Latest 25 from a total of 90,008 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Withdraw All | 24536478 | 2 days ago | IN | 0 ETH | 0.00049356 | ||||
| Withdraw All | 24480579 | 9 days ago | IN | 0 ETH | 0.00023714 | ||||
| Withdraw All | 24458001 | 13 days ago | IN | 0 ETH | 0.00046921 | ||||
| Withdraw All | 24434843 | 16 days ago | IN | 0 ETH | 0.00009238 | ||||
| Withdraw All | 24433335 | 16 days ago | IN | 0 ETH | 0.0000194 | ||||
| Withdraw All | 24419394 | 18 days ago | IN | 0 ETH | 0.00001419 | ||||
| Withdraw All | 24412225 | 19 days ago | IN | 0 ETH | 0.00000986 | ||||
| Withdraw All | 24380636 | 23 days ago | IN | 0 ETH | 0.0000254 | ||||
| Withdraw All | 24375166 | 24 days ago | IN | 0 ETH | 0.000448 | ||||
| Withdraw All | 24373448 | 24 days ago | IN | 0 ETH | 0.00002782 | ||||
| Withdraw All | 24370778 | 25 days ago | IN | 0 ETH | 0.00003315 | ||||
| Withdraw All | 24331631 | 30 days ago | IN | 0 ETH | 0.00047149 | ||||
| Withdraw All | 24321038 | 32 days ago | IN | 0 ETH | 0.00048305 | ||||
| Withdraw All | 24307392 | 34 days ago | IN | 0 ETH | 0.00046721 | ||||
| Withdraw All | 24300921 | 35 days ago | IN | 0 ETH | 0.00047121 | ||||
| Withdraw All | 24283570 | 37 days ago | IN | 0 ETH | 0.00049614 | ||||
| Withdraw All | 24259085 | 40 days ago | IN | 0 ETH | 0.00021426 | ||||
| Withdraw All | 24238055 | 43 days ago | IN | 0 ETH | 0.00045825 | ||||
| Withdraw All | 24192722 | 50 days ago | IN | 0 ETH | 0.00046789 | ||||
| Withdraw All | 24187329 | 50 days ago | IN | 0 ETH | 0.00001002 | ||||
| Deposit | 24175222 | 52 days ago | IN | 0 ETH | 0.00015549 | ||||
| Withdraw All | 24156283 | 55 days ago | IN | 0 ETH | 0.00049963 | ||||
| Withdraw All | 24143211 | 57 days ago | IN | 0 ETH | 0.00001089 | ||||
| Withdraw All | 24136906 | 57 days ago | IN | 0 ETH | 0.0004662 | ||||
| Withdraw All | 24117564 | 60 days ago | IN | 0 ETH | 0.00046055 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
AggregatorFeeSharingWithUniswapV3
Compiler Version
v0.8.7+commit.e28d00a7
Optimization Enabled:
Yes with 888888 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ISwapRouter} from "./uniswap-interfaces/ISwapRouter.sol";
import {FeeSharingSystem} from "./FeeSharingSystem.sol";
/**
* @title AggregatorFeeSharingWithUniswapV3
* @notice It sells WETH to LOOKS using Uniswap V3.
* @dev Prime shares represent the number of shares in the FeeSharingSystem. When not specified, shares represent secondary shares in this contract.
*/
contract AggregatorFeeSharingWithUniswapV3 is Ownable, Pausable, ReentrancyGuard {
using SafeERC20 for IERC20;
// Maximum buffer between 2 harvests (in blocks)
uint256 public constant MAXIMUM_HARVEST_BUFFER_BLOCKS = 6500;
// FeeSharingSystem (handles the distribution of WETH for LOOKS stakers)
FeeSharingSystem public immutable feeSharingSystem;
// Router of Uniswap v3
ISwapRouter public immutable uniswapRouter;
// Minimum deposit in LOOKS (it is derived from the FeeSharingSystem)
uint256 public immutable MINIMUM_DEPOSIT_LOOKS;
// LooksRare Token (LOOKS)
IERC20 public immutable looksRareToken;
// Reward token (WETH)
IERC20 public immutable rewardToken;
// Whether harvest and WETH selling is triggered automatically at user action
bool public canHarvest;
// Trading fee on Uniswap v3 (e.g., 3000 ---> 0.3%)
uint24 public tradingFeeUniswapV3;
// Buffer between two harvests (in blocks)
uint256 public harvestBufferBlocks;
// Last user action block
uint256 public lastHarvestBlock;
// Maximum price of LOOKS (in WETH) multiplied 1e18 (e.g., 0.0004 ETH --> 4e14)
uint256 public maxPriceLOOKSInWETH;
// Threshold amount (in rewardToken)
uint256 public thresholdAmount;
// Total number of shares outstanding
uint256 public totalShares;
// Keeps track of number of user shares
mapping(address => uint256) public userInfo;
event ConversionToLOOKS(uint256 amountSold, uint256 amountReceived);
event Deposit(address indexed user, uint256 amount);
event FailedConversion();
event HarvestStart();
event HarvestStop();
event NewHarvestBufferBlocks(uint256 harvestBufferBlocks);
event NewMaximumPriceLOOKSInWETH(uint256 maxPriceLOOKSInWETH);
event NewThresholdAmount(uint256 thresholdAmount);
event NewTradingFeeUniswapV3(uint24 tradingFeeUniswapV3);
event Withdraw(address indexed user, uint256 amount);
/**
* @notice Constructor
* @param _feeSharingSystem address of the fee sharing system contract
* @param _uniswapRouter address of the Uniswap v3 router
*/
constructor(address _feeSharingSystem, address _uniswapRouter) {
address looksRareTokenAddress = address(FeeSharingSystem(_feeSharingSystem).looksRareToken());
address rewardTokenAddress = address(FeeSharingSystem(_feeSharingSystem).rewardToken());
looksRareToken = IERC20(looksRareTokenAddress);
rewardToken = IERC20(rewardTokenAddress);
feeSharingSystem = FeeSharingSystem(_feeSharingSystem);
uniswapRouter = ISwapRouter(_uniswapRouter);
IERC20(looksRareTokenAddress).approve(_feeSharingSystem, type(uint256).max);
IERC20(rewardTokenAddress).approve(_uniswapRouter, type(uint256).max);
tradingFeeUniswapV3 = 3000;
MINIMUM_DEPOSIT_LOOKS = FeeSharingSystem(_feeSharingSystem).PRECISION_FACTOR();
}
/**
* @notice Deposit LOOKS tokens
* @param amount amount to deposit (in LOOKS)
* @dev There is a limit of 1 LOOKS per deposit to prevent potential manipulation of the shares
*/
function deposit(uint256 amount) external nonReentrant whenNotPaused {
require(amount >= MINIMUM_DEPOSIT_LOOKS, "Deposit: Amount must be >= 1 LOOKS");
if (block.number > (lastHarvestBlock + harvestBufferBlocks) && canHarvest && totalShares != 0) {
_harvestAndSellAndCompound();
}
// Transfer LOOKS tokens to this address
looksRareToken.safeTransferFrom(msg.sender, address(this), amount);
// Fetch the total number of LOOKS staked by this contract
uint256 totalAmountStaked = feeSharingSystem.calculateSharesValueInLOOKS(address(this));
uint256 currentShares = totalShares == 0 ? amount : (amount * totalShares) / totalAmountStaked;
require(currentShares != 0, "Deposit: Fail");
// Adjust number of shares for user/total
userInfo[msg.sender] += currentShares;
totalShares += currentShares;
// Deposit to FeeSharingSystem contract
feeSharingSystem.deposit(amount, false);
emit Deposit(msg.sender, amount);
}
/**
* @notice Redeem shares for LOOKS tokens
* @param shares number of shares to redeem
*/
function withdraw(uint256 shares) external nonReentrant {
require(
(shares > 0) && (shares <= userInfo[msg.sender]),
"Withdraw: Shares equal to 0 or larger than user shares"
);
_withdraw(shares);
}
/**
* @notice Withdraw all shares of sender
*/
function withdrawAll() external nonReentrant {
require(userInfo[msg.sender] > 0, "Withdraw: Shares equal to 0");
_withdraw(userInfo[msg.sender]);
}
/**
* @notice Harvest pending WETH, sell them to LOOKS, and deposit LOOKS (if possible)
* @dev Only callable by owner.
*/
function harvestAndSellAndCompound() external nonReentrant onlyOwner {
require(totalShares != 0, "Harvest: No share");
require(block.number != lastHarvestBlock, "Harvest: Already done");
_harvestAndSellAndCompound();
}
/**
* @notice Adjust allowance if necessary
* @dev Only callable by owner.
*/
function checkAndAdjustLOOKSTokenAllowanceIfRequired() external onlyOwner {
looksRareToken.approve(address(feeSharingSystem), type(uint256).max);
}
/**
* @notice Adjust allowance if necessary
* @dev Only callable by owner.
*/
function checkAndAdjustRewardTokenAllowanceIfRequired() external onlyOwner {
rewardToken.approve(address(uniswapRouter), type(uint256).max);
}
/**
* @notice Update harvest buffer block
* @param _newHarvestBufferBlocks buffer in blocks between two harvest operations
* @dev Only callable by owner.
*/
function updateHarvestBufferBlocks(uint256 _newHarvestBufferBlocks) external onlyOwner {
require(
_newHarvestBufferBlocks <= MAXIMUM_HARVEST_BUFFER_BLOCKS,
"Owner: Must be below MAXIMUM_HARVEST_BUFFER_BLOCKS"
);
harvestBufferBlocks = _newHarvestBufferBlocks;
emit NewHarvestBufferBlocks(_newHarvestBufferBlocks);
}
/**
* @notice Start automatic harvest/selling transactions
* @dev Only callable by owner
*/
function startHarvest() external onlyOwner {
canHarvest = true;
emit HarvestStart();
}
/**
* @notice Stop automatic harvest transactions
* @dev Only callable by owner
*/
function stopHarvest() external onlyOwner {
canHarvest = false;
emit HarvestStop();
}
/**
* @notice Update maximum price of LOOKS in WETH
* @param _newMaxPriceLOOKSInWETH new maximum price of LOOKS in WETH times 1e18
* @dev Only callable by owner
*/
function updateMaxPriceOfLOOKSInWETH(uint256 _newMaxPriceLOOKSInWETH) external onlyOwner {
maxPriceLOOKSInWETH = _newMaxPriceLOOKSInWETH;
emit NewMaximumPriceLOOKSInWETH(_newMaxPriceLOOKSInWETH);
}
/**
* @notice Adjust trading fee for Uniswap v3
* @param _newTradingFeeUniswapV3 new tradingFeeUniswapV3
* @dev Only callable by owner. Can only be 10,000 (1%), 3000 (0.3%), or 500 (0.05%).
*/
function updateTradingFeeUniswapV3(uint24 _newTradingFeeUniswapV3) external onlyOwner {
require(
_newTradingFeeUniswapV3 == 10000 || _newTradingFeeUniswapV3 == 3000 || _newTradingFeeUniswapV3 == 500,
"Owner: Fee invalid"
);
tradingFeeUniswapV3 = _newTradingFeeUniswapV3;
emit NewTradingFeeUniswapV3(_newTradingFeeUniswapV3);
}
/**
* @notice Adjust threshold amount for periodic Uniswap v3 WETH --> LOOKS conversion
* @param _newThresholdAmount new threshold amount (in WETH)
* @dev Only callable by owner
*/
function updateThresholdAmount(uint256 _newThresholdAmount) external onlyOwner {
thresholdAmount = _newThresholdAmount;
emit NewThresholdAmount(_newThresholdAmount);
}
/**
* @notice Pause
* @dev Only callable by owner
*/
function pause() external onlyOwner whenNotPaused {
_pause();
}
/**
* @notice Unpause
* @dev Only callable by owner
*/
function unpause() external onlyOwner whenPaused {
_unpause();
}
/**
* @notice Calculate price of one share (in LOOKS token)
* Share price is expressed times 1e18
*/
function calculateSharePriceInLOOKS() external view returns (uint256) {
uint256 totalAmountStakedWithAggregator = feeSharingSystem.calculateSharesValueInLOOKS(address(this));
return
totalShares == 0
? MINIMUM_DEPOSIT_LOOKS
: (totalAmountStakedWithAggregator * MINIMUM_DEPOSIT_LOOKS) / (totalShares);
}
/**
* @notice Calculate price of one share (in prime share)
* Share price is expressed times 1e18
*/
function calculateSharePriceInPrimeShare() external view returns (uint256) {
(uint256 totalNumberPrimeShares, , ) = feeSharingSystem.userInfo(address(this));
return
totalShares == 0 ? MINIMUM_DEPOSIT_LOOKS : (totalNumberPrimeShares * MINIMUM_DEPOSIT_LOOKS) / totalShares;
}
/**
* @notice Calculate shares value of a user (in LOOKS)
* @param user address of the user
*/
function calculateSharesValueInLOOKS(address user) external view returns (uint256) {
uint256 totalAmountStakedWithAggregator = feeSharingSystem.calculateSharesValueInLOOKS(address(this));
return totalShares == 0 ? 0 : (totalAmountStakedWithAggregator * userInfo[user]) / totalShares;
}
/**
* @notice Harvest pending WETH, sell them to LOOKS, and deposit LOOKS (if possible)
*/
function _harvestAndSellAndCompound() internal {
// Try/catch to prevent revertions if nothing to harvest
try feeSharingSystem.harvest() {} catch {}
uint256 amountToSell = rewardToken.balanceOf(address(this));
if (amountToSell >= thresholdAmount) {
bool isExecuted = _sellRewardTokenToLOOKS(amountToSell);
if (isExecuted) {
uint256 adjustedAmount = looksRareToken.balanceOf(address(this));
if (adjustedAmount >= MINIMUM_DEPOSIT_LOOKS) {
feeSharingSystem.deposit(adjustedAmount, false);
}
}
}
// Adjust last harvest block
lastHarvestBlock = block.number;
}
/**
* @notice Sell WETH to LOOKS
* @param _amount amount of rewardToken to convert (WETH)
* @return whether the transaction went through
*/
function _sellRewardTokenToLOOKS(uint256 _amount) internal returns (bool) {
uint256 amountOutMinimum = maxPriceLOOKSInWETH != 0 ? (_amount * 1e18) / maxPriceLOOKSInWETH : 0;
// Set the order parameters
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams(
address(rewardToken), // tokenIn
address(looksRareToken), // tokenOut
tradingFeeUniswapV3, // fee
address(this), // recipient
block.timestamp, // deadline
_amount, // amountIn
amountOutMinimum, // amountOutMinimum
0 // sqrtPriceLimitX96
);
// Swap on Uniswap V3
try uniswapRouter.exactInputSingle(params) returns (uint256 amountOut) {
emit ConversionToLOOKS(_amount, amountOut);
return true;
} catch {
emit FailedConversion();
return false;
}
}
/**
* @notice Withdraw shares
* @param _shares number of shares to redeem
* @dev The difference between the two snapshots of LOOKS balances is used to know how many tokens to transfer to user.
*/
function _withdraw(uint256 _shares) internal {
if (block.number > (lastHarvestBlock + harvestBufferBlocks) && canHarvest) {
_harvestAndSellAndCompound();
}
// Take snapshot of current LOOKS balance
uint256 previousBalanceLOOKS = looksRareToken.balanceOf(address(this));
// Fetch total number of prime shares
(uint256 totalNumberPrimeShares, , ) = feeSharingSystem.userInfo(address(this));
// Calculate number of prime shares to redeem based on existing shares (from this contract)
uint256 currentNumberPrimeShares = (totalNumberPrimeShares * _shares) / totalShares;
// Adjust number of shares for user/total
userInfo[msg.sender] -= _shares;
totalShares -= _shares;
// Withdraw amount equivalent in prime shares
feeSharingSystem.withdraw(currentNumberPrimeShares, false);
// Calculate the difference between the current balance of LOOKS with the previous snapshot
uint256 amountToTransfer = looksRareToken.balanceOf(address(this)) - previousBalanceLOOKS;
// Transfer the LOOKS amount back to user
looksRareToken.safeTransfer(msg.sender, amountToTransfer);
emit Withdraw(msg.sender, amountToTransfer);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract 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 Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
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);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
require(!paused(), "Pausable: paused");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
require(paused(), "Pausable: not paused");
_;
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
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));
}
}
/**
* @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");
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import "./IUniswapV3SwapCallback.sol";
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {TokenDistributor} from "./TokenDistributor.sol";
/**
* @title FeeSharingSystem
* @notice It handles the distribution of fees using
* WETH along with the auto-compounding of LOOKS.
*/
contract FeeSharingSystem is ReentrancyGuard, Ownable {
using SafeERC20 for IERC20;
struct UserInfo {
uint256 shares; // shares of token staked
uint256 userRewardPerTokenPaid; // user reward per token paid
uint256 rewards; // pending rewards
}
// Precision factor for calculating rewards and exchange rate
uint256 public constant PRECISION_FACTOR = 10**18;
IERC20 public immutable looksRareToken;
IERC20 public immutable rewardToken;
TokenDistributor public immutable tokenDistributor;
// Reward rate (block)
uint256 public currentRewardPerBlock;
// Last reward adjustment block number
uint256 public lastRewardAdjustment;
// Last update block for rewards
uint256 public lastUpdateBlock;
// Current end block for the current reward period
uint256 public periodEndBlock;
// Reward per token stored
uint256 public rewardPerTokenStored;
// Total existing shares
uint256 public totalShares;
mapping(address => UserInfo) public userInfo;
event Deposit(address indexed user, uint256 amount, uint256 harvestedAmount);
event Harvest(address indexed user, uint256 harvestedAmount);
event NewRewardPeriod(uint256 numberBlocks, uint256 rewardPerBlock, uint256 reward);
event Withdraw(address indexed user, uint256 amount, uint256 harvestedAmount);
/**
* @notice Constructor
* @param _looksRareToken address of the token staked (LOOKS)
* @param _rewardToken address of the reward token
* @param _tokenDistributor address of the token distributor contract
*/
constructor(
address _looksRareToken,
address _rewardToken,
address _tokenDistributor
) {
rewardToken = IERC20(_rewardToken);
looksRareToken = IERC20(_looksRareToken);
tokenDistributor = TokenDistributor(_tokenDistributor);
}
/**
* @notice Deposit staked tokens (and collect reward tokens if requested)
* @param amount amount to deposit (in LOOKS)
* @param claimRewardToken whether to claim reward tokens
* @dev There is a limit of 1 LOOKS per deposit to prevent potential manipulation of current shares
*/
function deposit(uint256 amount, bool claimRewardToken) external nonReentrant {
require(amount >= PRECISION_FACTOR, "Deposit: Amount must be >= 1 LOOKS");
// Auto compounds for everyone
tokenDistributor.harvestAndCompound();
// Update reward for user
_updateReward(msg.sender);
// Retrieve total amount staked by this contract
(uint256 totalAmountStaked, ) = tokenDistributor.userInfo(address(this));
// Transfer LOOKS tokens to this address
looksRareToken.safeTransferFrom(msg.sender, address(this), amount);
uint256 currentShares;
// Calculate the number of shares to issue for the user
if (totalShares != 0) {
currentShares = (amount * totalShares) / totalAmountStaked;
// This is a sanity check to prevent deposit for 0 shares
require(currentShares != 0, "Deposit: Fail");
} else {
currentShares = amount;
}
// Adjust internal shares
userInfo[msg.sender].shares += currentShares;
totalShares += currentShares;
uint256 pendingRewards;
if (claimRewardToken) {
// Fetch pending rewards
pendingRewards = userInfo[msg.sender].rewards;
if (pendingRewards > 0) {
userInfo[msg.sender].rewards = 0;
rewardToken.safeTransfer(msg.sender, pendingRewards);
}
}
// Verify LOOKS token allowance and adjust if necessary
_checkAndAdjustLOOKSTokenAllowanceIfRequired(amount, address(tokenDistributor));
// Deposit user amount in the token distributor contract
tokenDistributor.deposit(amount);
emit Deposit(msg.sender, amount, pendingRewards);
}
/**
* @notice Harvest reward tokens that are pending
*/
function harvest() external nonReentrant {
// Auto compounds for everyone
tokenDistributor.harvestAndCompound();
// Update reward for user
_updateReward(msg.sender);
// Retrieve pending rewards
uint256 pendingRewards = userInfo[msg.sender].rewards;
// If pending rewards are null, revert
require(pendingRewards > 0, "Harvest: Pending rewards must be > 0");
// Adjust user rewards and transfer
userInfo[msg.sender].rewards = 0;
// Transfer reward token to sender
rewardToken.safeTransfer(msg.sender, pendingRewards);
emit Harvest(msg.sender, pendingRewards);
}
/**
* @notice Withdraw staked tokens (and collect reward tokens if requested)
* @param shares shares to withdraw
* @param claimRewardToken whether to claim reward tokens
*/
function withdraw(uint256 shares, bool claimRewardToken) external nonReentrant {
require(
(shares > 0) && (shares <= userInfo[msg.sender].shares),
"Withdraw: Shares equal to 0 or larger than user shares"
);
_withdraw(shares, claimRewardToken);
}
/**
* @notice Withdraw all staked tokens (and collect reward tokens if requested)
* @param claimRewardToken whether to claim reward tokens
*/
function withdrawAll(bool claimRewardToken) external nonReentrant {
_withdraw(userInfo[msg.sender].shares, claimRewardToken);
}
/**
* @notice Update the reward per block (in rewardToken)
* @dev Only callable by owner. Owner is meant to be another smart contract.
*/
function updateRewards(uint256 reward, uint256 rewardDurationInBlocks) external onlyOwner {
// Adjust the current reward per block
if (block.number >= periodEndBlock) {
currentRewardPerBlock = reward / rewardDurationInBlocks;
} else {
currentRewardPerBlock =
(reward + ((periodEndBlock - block.number) * currentRewardPerBlock)) /
rewardDurationInBlocks;
}
lastUpdateBlock = block.number;
periodEndBlock = block.number + rewardDurationInBlocks;
emit NewRewardPeriod(rewardDurationInBlocks, currentRewardPerBlock, reward);
}
/**
* @notice Calculate pending rewards (WETH) for a user
* @param user address of the user
*/
function calculatePendingRewards(address user) external view returns (uint256) {
return _calculatePendingRewards(user);
}
/**
* @notice Calculate value of LOOKS for a user given a number of shares owned
* @param user address of the user
*/
function calculateSharesValueInLOOKS(address user) external view returns (uint256) {
// Retrieve amount staked
(uint256 totalAmountStaked, ) = tokenDistributor.userInfo(address(this));
// Adjust for pending rewards
totalAmountStaked += tokenDistributor.calculatePendingRewards(address(this));
// Return user pro-rata of total shares
return userInfo[user].shares == 0 ? 0 : (totalAmountStaked * userInfo[user].shares) / totalShares;
}
/**
* @notice Calculate price of one share (in LOOKS token)
* Share price is expressed times 1e18
*/
function calculateSharePriceInLOOKS() external view returns (uint256) {
(uint256 totalAmountStaked, ) = tokenDistributor.userInfo(address(this));
// Adjust for pending rewards
totalAmountStaked += tokenDistributor.calculatePendingRewards(address(this));
return totalShares == 0 ? PRECISION_FACTOR : (totalAmountStaked * PRECISION_FACTOR) / (totalShares);
}
/**
* @notice Return last block where trading rewards were distributed
*/
function lastRewardBlock() external view returns (uint256) {
return _lastRewardBlock();
}
/**
* @notice Calculate pending rewards for a user
* @param user address of the user
*/
function _calculatePendingRewards(address user) internal view returns (uint256) {
return
((userInfo[user].shares * (_rewardPerToken() - (userInfo[user].userRewardPerTokenPaid))) /
PRECISION_FACTOR) + userInfo[user].rewards;
}
/**
* @notice Check current allowance and adjust if necessary
* @param _amount amount to transfer
* @param _to token to transfer
*/
function _checkAndAdjustLOOKSTokenAllowanceIfRequired(uint256 _amount, address _to) internal {
if (looksRareToken.allowance(address(this), _to) < _amount) {
looksRareToken.approve(_to, type(uint256).max);
}
}
/**
* @notice Return last block where rewards must be distributed
*/
function _lastRewardBlock() internal view returns (uint256) {
return block.number < periodEndBlock ? block.number : periodEndBlock;
}
/**
* @notice Return reward per token
*/
function _rewardPerToken() internal view returns (uint256) {
if (totalShares == 0) {
return rewardPerTokenStored;
}
return
rewardPerTokenStored +
((_lastRewardBlock() - lastUpdateBlock) * (currentRewardPerBlock * PRECISION_FACTOR)) /
totalShares;
}
/**
* @notice Update reward for a user account
* @param _user address of the user
*/
function _updateReward(address _user) internal {
if (block.number != lastUpdateBlock) {
rewardPerTokenStored = _rewardPerToken();
lastUpdateBlock = _lastRewardBlock();
}
userInfo[_user].rewards = _calculatePendingRewards(_user);
userInfo[_user].userRewardPerTokenPaid = rewardPerTokenStored;
}
/**
* @notice Withdraw staked tokens (and collect reward tokens if requested)
* @param shares shares to withdraw
* @param claimRewardToken whether to claim reward tokens
*/
function _withdraw(uint256 shares, bool claimRewardToken) internal {
// Auto compounds for everyone
tokenDistributor.harvestAndCompound();
// Update reward for user
_updateReward(msg.sender);
// Retrieve total amount staked and calculated current amount (in LOOKS)
(uint256 totalAmountStaked, ) = tokenDistributor.userInfo(address(this));
uint256 currentAmount = (totalAmountStaked * shares) / totalShares;
userInfo[msg.sender].shares -= shares;
totalShares -= shares;
// Withdraw amount equivalent in shares
tokenDistributor.withdraw(currentAmount);
uint256 pendingRewards;
if (claimRewardToken) {
// Fetch pending rewards
pendingRewards = userInfo[msg.sender].rewards;
if (pendingRewards > 0) {
userInfo[msg.sender].rewards = 0;
rewardToken.safeTransfer(msg.sender, pendingRewards);
}
}
// Transfer LOOKS tokens to sender
looksRareToken.safeTransfer(msg.sender, currentAmount);
emit Withdraw(msg.sender, currentAmount, pendingRewards);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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 `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, 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 `sender` to `recipient` 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 sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
pragma solidity ^0.8.0;
/**
* @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
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason 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 {
// 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
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
/// @dev In the implementation you must pay the pool tokens owed for the swap.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ILooksRareToken} from "../interfaces/ILooksRareToken.sol";
/**
* @title TokenDistributor
* @notice It handles the distribution of LOOKS token.
* It auto-adjusts block rewards over a set number of periods.
*/
contract TokenDistributor is ReentrancyGuard {
using SafeERC20 for IERC20;
using SafeERC20 for ILooksRareToken;
struct StakingPeriod {
uint256 rewardPerBlockForStaking;
uint256 rewardPerBlockForOthers;
uint256 periodLengthInBlock;
}
struct UserInfo {
uint256 amount; // Amount of staked tokens provided by user
uint256 rewardDebt; // Reward debt
}
// Precision factor for calculating rewards
uint256 public constant PRECISION_FACTOR = 10**12;
ILooksRareToken public immutable looksRareToken;
address public immutable tokenSplitter;
// Number of reward periods
uint256 public immutable NUMBER_PERIODS;
// Block number when rewards start
uint256 public immutable START_BLOCK;
// Accumulated tokens per share
uint256 public accTokenPerShare;
// Current phase for rewards
uint256 public currentPhase;
// Block number when rewards end
uint256 public endBlock;
// Block number of the last update
uint256 public lastRewardBlock;
// Tokens distributed per block for other purposes (team + treasury + trading rewards)
uint256 public rewardPerBlockForOthers;
// Tokens distributed per block for staking
uint256 public rewardPerBlockForStaking;
// Total amount staked
uint256 public totalAmountStaked;
mapping(uint256 => StakingPeriod) public stakingPeriod;
mapping(address => UserInfo) public userInfo;
event Compound(address indexed user, uint256 harvestedAmount);
event Deposit(address indexed user, uint256 amount, uint256 harvestedAmount);
event NewRewardsPerBlock(
uint256 indexed currentPhase,
uint256 startBlock,
uint256 rewardPerBlockForStaking,
uint256 rewardPerBlockForOthers
);
event Withdraw(address indexed user, uint256 amount, uint256 harvestedAmount);
/**
* @notice Constructor
* @param _looksRareToken LOOKS token address
* @param _tokenSplitter token splitter contract address (for team and trading rewards)
* @param _startBlock start block for reward program
* @param _rewardsPerBlockForStaking array of rewards per block for staking
* @param _rewardsPerBlockForOthers array of rewards per block for other purposes (team + treasury + trading rewards)
* @param _periodLengthesInBlocks array of period lengthes
* @param _numberPeriods number of periods with different rewards/lengthes (e.g., if 3 changes --> 4 periods)
*/
constructor(
address _looksRareToken,
address _tokenSplitter,
uint256 _startBlock,
uint256[] memory _rewardsPerBlockForStaking,
uint256[] memory _rewardsPerBlockForOthers,
uint256[] memory _periodLengthesInBlocks,
uint256 _numberPeriods
) {
require(
(_periodLengthesInBlocks.length == _numberPeriods) &&
(_rewardsPerBlockForStaking.length == _numberPeriods) &&
(_rewardsPerBlockForStaking.length == _numberPeriods),
"Distributor: Lengthes must match numberPeriods"
);
// 1. Operational checks for supply
uint256 nonCirculatingSupply = ILooksRareToken(_looksRareToken).SUPPLY_CAP() -
ILooksRareToken(_looksRareToken).totalSupply();
uint256 amountTokensToBeMinted;
for (uint256 i = 0; i < _numberPeriods; i++) {
amountTokensToBeMinted +=
(_rewardsPerBlockForStaking[i] * _periodLengthesInBlocks[i]) +
(_rewardsPerBlockForOthers[i] * _periodLengthesInBlocks[i]);
stakingPeriod[i] = StakingPeriod({
rewardPerBlockForStaking: _rewardsPerBlockForStaking[i],
rewardPerBlockForOthers: _rewardsPerBlockForOthers[i],
periodLengthInBlock: _periodLengthesInBlocks[i]
});
}
require(amountTokensToBeMinted == nonCirculatingSupply, "Distributor: Wrong reward parameters");
// 2. Store values
looksRareToken = ILooksRareToken(_looksRareToken);
tokenSplitter = _tokenSplitter;
rewardPerBlockForStaking = _rewardsPerBlockForStaking[0];
rewardPerBlockForOthers = _rewardsPerBlockForOthers[0];
START_BLOCK = _startBlock;
endBlock = _startBlock + _periodLengthesInBlocks[0];
NUMBER_PERIODS = _numberPeriods;
// Set the lastRewardBlock as the startBlock
lastRewardBlock = _startBlock;
}
/**
* @notice Deposit staked tokens and compounds pending rewards
* @param amount amount to deposit (in LOOKS)
*/
function deposit(uint256 amount) external nonReentrant {
require(amount > 0, "Deposit: Amount must be > 0");
// Update pool information
_updatePool();
// Transfer LOOKS tokens to this contract
looksRareToken.safeTransferFrom(msg.sender, address(this), amount);
uint256 pendingRewards;
// If not new deposit, calculate pending rewards (for auto-compounding)
if (userInfo[msg.sender].amount > 0) {
pendingRewards =
((userInfo[msg.sender].amount * accTokenPerShare) / PRECISION_FACTOR) -
userInfo[msg.sender].rewardDebt;
}
// Adjust user information
userInfo[msg.sender].amount += (amount + pendingRewards);
userInfo[msg.sender].rewardDebt = (userInfo[msg.sender].amount * accTokenPerShare) / PRECISION_FACTOR;
// Increase totalAmountStaked
totalAmountStaked += (amount + pendingRewards);
emit Deposit(msg.sender, amount, pendingRewards);
}
/**
* @notice Compound based on pending rewards
*/
function harvestAndCompound() external nonReentrant {
// Update pool information
_updatePool();
// Calculate pending rewards
uint256 pendingRewards = ((userInfo[msg.sender].amount * accTokenPerShare) / PRECISION_FACTOR) -
userInfo[msg.sender].rewardDebt;
// Return if no pending rewards
if (pendingRewards == 0) {
// It doesn't throw revertion (to help with the fee-sharing auto-compounding contract)
return;
}
// Adjust user amount for pending rewards
userInfo[msg.sender].amount += pendingRewards;
// Adjust totalAmountStaked
totalAmountStaked += pendingRewards;
// Recalculate reward debt based on new user amount
userInfo[msg.sender].rewardDebt = (userInfo[msg.sender].amount * accTokenPerShare) / PRECISION_FACTOR;
emit Compound(msg.sender, pendingRewards);
}
/**
* @notice Update pool rewards
*/
function updatePool() external nonReentrant {
_updatePool();
}
/**
* @notice Withdraw staked tokens and compound pending rewards
* @param amount amount to withdraw
*/
function withdraw(uint256 amount) external nonReentrant {
require(
(userInfo[msg.sender].amount >= amount) && (amount > 0),
"Withdraw: Amount must be > 0 or lower than user balance"
);
// Update pool
_updatePool();
// Calculate pending rewards
uint256 pendingRewards = ((userInfo[msg.sender].amount * accTokenPerShare) / PRECISION_FACTOR) -
userInfo[msg.sender].rewardDebt;
// Adjust user information
userInfo[msg.sender].amount = userInfo[msg.sender].amount + pendingRewards - amount;
userInfo[msg.sender].rewardDebt = (userInfo[msg.sender].amount * accTokenPerShare) / PRECISION_FACTOR;
// Adjust total amount staked
totalAmountStaked = totalAmountStaked + pendingRewards - amount;
// Transfer LOOKS tokens to the sender
looksRareToken.safeTransfer(msg.sender, amount);
emit Withdraw(msg.sender, amount, pendingRewards);
}
/**
* @notice Withdraw all staked tokens and collect tokens
*/
function withdrawAll() external nonReentrant {
require(userInfo[msg.sender].amount > 0, "Withdraw: Amount must be > 0");
// Update pool
_updatePool();
// Calculate pending rewards and amount to transfer (to the sender)
uint256 pendingRewards = ((userInfo[msg.sender].amount * accTokenPerShare) / PRECISION_FACTOR) -
userInfo[msg.sender].rewardDebt;
uint256 amountToTransfer = userInfo[msg.sender].amount + pendingRewards;
// Adjust total amount staked
totalAmountStaked = totalAmountStaked - userInfo[msg.sender].amount;
// Adjust user information
userInfo[msg.sender].amount = 0;
userInfo[msg.sender].rewardDebt = 0;
// Transfer LOOKS tokens to the sender
looksRareToken.safeTransfer(msg.sender, amountToTransfer);
emit Withdraw(msg.sender, amountToTransfer, pendingRewards);
}
/**
* @notice Calculate pending rewards for a user
* @param user address of the user
* @return Pending rewards
*/
function calculatePendingRewards(address user) external view returns (uint256) {
if ((block.number > lastRewardBlock) && (totalAmountStaked != 0)) {
uint256 multiplier = _getMultiplier(lastRewardBlock, block.number);
uint256 tokenRewardForStaking = multiplier * rewardPerBlockForStaking;
uint256 adjustedEndBlock = endBlock;
uint256 adjustedCurrentPhase = currentPhase;
// Check whether to adjust multipliers and reward per block
while ((block.number > adjustedEndBlock) && (adjustedCurrentPhase < (NUMBER_PERIODS - 1))) {
// Update current phase
adjustedCurrentPhase++;
// Update rewards per block
uint256 adjustedRewardPerBlockForStaking = stakingPeriod[adjustedCurrentPhase].rewardPerBlockForStaking;
// Calculate adjusted block number
uint256 previousEndBlock = adjustedEndBlock;
// Update end block
adjustedEndBlock = previousEndBlock + stakingPeriod[adjustedCurrentPhase].periodLengthInBlock;
// Calculate new multiplier
uint256 newMultiplier = (block.number <= adjustedEndBlock)
? (block.number - previousEndBlock)
: stakingPeriod[adjustedCurrentPhase].periodLengthInBlock;
// Adjust token rewards for staking
tokenRewardForStaking += (newMultiplier * adjustedRewardPerBlockForStaking);
}
uint256 adjustedTokenPerShare = accTokenPerShare +
(tokenRewardForStaking * PRECISION_FACTOR) /
totalAmountStaked;
return (userInfo[user].amount * adjustedTokenPerShare) / PRECISION_FACTOR - userInfo[user].rewardDebt;
} else {
return (userInfo[user].amount * accTokenPerShare) / PRECISION_FACTOR - userInfo[user].rewardDebt;
}
}
/**
* @notice Update reward variables of the pool
*/
function _updatePool() internal {
if (block.number <= lastRewardBlock) {
return;
}
if (totalAmountStaked == 0) {
lastRewardBlock = block.number;
return;
}
// Calculate multiplier
uint256 multiplier = _getMultiplier(lastRewardBlock, block.number);
// Calculate rewards for staking and others
uint256 tokenRewardForStaking = multiplier * rewardPerBlockForStaking;
uint256 tokenRewardForOthers = multiplier * rewardPerBlockForOthers;
// Check whether to adjust multipliers and reward per block
while ((block.number > endBlock) && (currentPhase < (NUMBER_PERIODS - 1))) {
// Update rewards per block
_updateRewardsPerBlock(endBlock);
uint256 previousEndBlock = endBlock;
// Adjust the end block
endBlock += stakingPeriod[currentPhase].periodLengthInBlock;
// Adjust multiplier to cover the missing periods with other lower inflation schedule
uint256 newMultiplier = _getMultiplier(previousEndBlock, block.number);
// Adjust token rewards
tokenRewardForStaking += (newMultiplier * rewardPerBlockForStaking);
tokenRewardForOthers += (newMultiplier * rewardPerBlockForOthers);
}
// Mint tokens only if token rewards for staking are not null
if (tokenRewardForStaking > 0) {
// It allows protection against potential issues to prevent funds from being locked
bool mintStatus = looksRareToken.mint(address(this), tokenRewardForStaking);
if (mintStatus) {
accTokenPerShare = accTokenPerShare + ((tokenRewardForStaking * PRECISION_FACTOR) / totalAmountStaked);
}
looksRareToken.mint(tokenSplitter, tokenRewardForOthers);
}
// Update last reward block only if it wasn't updated after or at the end block
if (lastRewardBlock <= endBlock) {
lastRewardBlock = block.number;
}
}
/**
* @notice Update rewards per block
* @dev Rewards are halved by 2 (for staking + others)
*/
function _updateRewardsPerBlock(uint256 _newStartBlock) internal {
// Update current phase
currentPhase++;
// Update rewards per block
rewardPerBlockForStaking = stakingPeriod[currentPhase].rewardPerBlockForStaking;
rewardPerBlockForOthers = stakingPeriod[currentPhase].rewardPerBlockForOthers;
emit NewRewardsPerBlock(currentPhase, _newStartBlock, rewardPerBlockForStaking, rewardPerBlockForOthers);
}
/**
* @notice Return reward multiplier over the given "from" to "to" block.
* @param from block to start calculating reward
* @param to block to finish calculating reward
* @return the multiplier for the period
*/
function _getMultiplier(uint256 from, uint256 to) internal view returns (uint256) {
if (to <= endBlock) {
return to - from;
} else if (from >= endBlock) {
return 0;
} else {
return endBlock - from;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface ILooksRareToken is IERC20 {
function SUPPLY_CAP() external view returns (uint256);
function mint(address account, uint256 amount) external returns (bool);
}{
"optimizer": {
"enabled": true,
"runs": 888888
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_feeSharingSystem","type":"address"},{"internalType":"address","name":"_uniswapRouter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amountSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountReceived","type":"uint256"}],"name":"ConversionToLOOKS","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[],"name":"FailedConversion","type":"event"},{"anonymous":false,"inputs":[],"name":"HarvestStart","type":"event"},{"anonymous":false,"inputs":[],"name":"HarvestStop","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"harvestBufferBlocks","type":"uint256"}],"name":"NewHarvestBufferBlocks","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxPriceLOOKSInWETH","type":"uint256"}],"name":"NewMaximumPriceLOOKSInWETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"thresholdAmount","type":"uint256"}],"name":"NewThresholdAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint24","name":"tradingFeeUniswapV3","type":"uint24"}],"name":"NewTradingFeeUniswapV3","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"MAXIMUM_HARVEST_BUFFER_BLOCKS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINIMUM_DEPOSIT_LOOKS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"calculateSharePriceInLOOKS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"calculateSharePriceInPrimeShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"calculateSharesValueInLOOKS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canHarvest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkAndAdjustLOOKSTokenAllowanceIfRequired","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkAndAdjustRewardTokenAllowanceIfRequired","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeSharingSystem","outputs":[{"internalType":"contract FeeSharingSystem","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvestAndSellAndCompound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"harvestBufferBlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastHarvestBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"looksRareToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxPriceLOOKSInWETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startHarvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stopHarvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"thresholdAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tradingFeeUniswapV3","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapRouter","outputs":[{"internalType":"contract ISwapRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newHarvestBufferBlocks","type":"uint256"}],"name":"updateHarvestBufferBlocks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newMaxPriceLOOKSInWETH","type":"uint256"}],"name":"updateMaxPriceOfLOOKSInWETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newThresholdAmount","type":"uint256"}],"name":"updateThresholdAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"_newTradingFeeUniswapV3","type":"uint24"}],"name":"updateTradingFeeUniswapV3","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101206040523480156200001257600080fd5b506040516200351638038062003516833981016040819052620000359162000371565b620000403362000321565b6000805460ff60a01b191681556001805560408051631b6dcfd960e11b815290516001600160a01b038516916336db9fb2916004808301926020929190829003018186803b1580156200009257600080fd5b505afa158015620000a7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000cd9190620003db565b90506000836001600160a01b031663f7c618c16040518163ffffffff1660e01b815260040160206040518083038186803b1580156200010b57600080fd5b505afa15801562000120573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001469190620003db565b6001600160601b0319606084811b821660e05282811b82166101005286811b821660805285901b1660a05260405163095ea7b360e01b81526001600160a01b03808716600483015260001960248301529192509083169063095ea7b390604401602060405180830381600087803b158015620001c157600080fd5b505af1158015620001d6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001fc9190620003b0565b5060405163095ea7b360e01b81526001600160a01b038481166004830152600019602483015282169063095ea7b390604401602060405180830381600087803b1580156200024957600080fd5b505af11580156200025e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002849190620003b0565b506002805463ffffff001916620bb8001790556040805163ccd34cd560e01b815290516001600160a01b0386169163ccd34cd5916004808301926020929190829003018186803b158015620002d857600080fd5b505afa158015620002ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003139190620003fb565b60c052506200042e92505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600080604083850312156200038557600080fd5b8251620003928162000415565b6020840151909250620003a58162000415565b809150509250929050565b600060208284031215620003c357600080fd5b81518015158114620003d457600080fd5b9392505050565b600060208284031215620003ee57600080fd5b8151620003d48162000415565b6000602082840312156200040e57600080fd5b5051919050565b6001600160a01b03811681146200042b57600080fd5b50565b60805160601c60a05160601c60c05160e05160601c6101005160601c612fda6200053c600039600081816104e501528181611d4b01528181611e2501526128040152600081816102f8015281816115580152818161190501528181611f03015281816120cc0152818161236e0152818161240e01526128290152600081816104870152818161087f015281816108b501528181610d34015281816114560152611f9601526000818161038801528181611cfd01526128f70152600081816103af015281816107ef01528181610ca6015281816111b7015281816115b10152818161174a015281816118b701528181611d7c01528181611ff1015281816121a401526122b50152612fda6000f3fe608060405234801561001057600080fd5b50600436106102415760003560e01c80638456cb5911610145578063b8984c86116100bd578063ea09c2a11161008c578063f7b0055311610071578063f7b00553146104d8578063f7c618c1146104e0578063fca3f55b1461050757600080fd5b8063ea09c2a1146104bc578063f2fde38b146104c557600080fd5b8063b8984c8614610452578063d7f8e43f1461045a578063d84e9ebb14610482578063db200bfa146104a957600080fd5b80639a59514111610114578063b10aa43a116100f9578063b10aa43a1461042e578063b3e7860814610436578063b6b55f251461043f57600080fd5b80639a59514114610408578063ab5e32af1461041b57600080fd5b80638456cb59146103d1578063853828b6146103d95780638b2aa597146103e15780638da5cb5b146103ea57600080fd5b80632e1a7d4d116101d85780635c975abb116101a7578063715018a61161018c578063715018a61461037b578063735de9f7146103835780637f085fd1146103aa57600080fd5b80635c975abb146103505780636de26e381461037357600080fd5b80632e1a7d4d146102e057806336db9fb2146102f35780633a98ef391461033f5780633f4ba83a1461034857600080fd5b8063130180de11610214578063130180de1461029b57806314b74d9a146102ae5780631959a002146102b757806328f4dbb6146102d757600080fd5b8063056f7a0f1461024657806305d26905146102505780630a738779146102635780631064ac151461027e575b600080fd5b61024e61050f565b005b61024e61025e366004612dd5565b6106ea565b61026b6107a7565b6040519081526020015b60405180910390f35b60025461028b9060ff1681565b6040519015158152602001610275565b61024e6102a9366004612dd5565b6108db565b61026b60045481565b61026b6102c5366004612d58565b60086020526000908152604090205481565b61026b60065481565b61024e6102ee366004612dd5565b610a23565b61031a7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610275565b61026b60075481565b61024e610b4f565b60005474010000000000000000000000000000000000000000900460ff1661028b565b61026b610c5e565b61024e610d59565b61031a7f000000000000000000000000000000000000000000000000000000000000000081565b61031a7f000000000000000000000000000000000000000000000000000000000000000081565b61024e610de4565b61024e610ef2565b61026b61196481565b60005473ffffffffffffffffffffffffffffffffffffffff1661031a565b61024e610416366004612db0565b610ff3565b61026b610429366004612d58565b61116f565b61024e611289565b61026b60035481565b61024e61044d366004612dd5565b61135d565b61024e6117f9565b60025461046e90610100900462ffffff1681565b60405162ffffff9091168152602001610275565b61026b7f000000000000000000000000000000000000000000000000000000000000000081565b61024e6104b7366004612dd5565b611985565b61026b60055481565b61024e6104d3366004612d58565b611a3b565b61024e611b68565b61031a7f000000000000000000000000000000000000000000000000000000000000000081565b61024e611c3f565b60026001541415610581576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b600260015560005473ffffffffffffffffffffffffffffffffffffffff163314610607576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b600754610670576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f486172766573743a204e6f2073686172650000000000000000000000000000006044820152606401610578565b6004544314156106dc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f486172766573743a20416c726561647920646f6e6500000000000000000000006044820152606401610578565b6106e4611d7a565b60018055565b60005473ffffffffffffffffffffffffffffffffffffffff16331461076b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b60058190556040518181527fbb7d853f3ca3c528bd0d8658c87586e2aa72e909bf52350a2febebc3d86e6e92906020015b60405180910390a150565b6040517f1959a002000000000000000000000000000000000000000000000000000000008152306004820152600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690631959a0029060240160606040518083038186803b15801561083157600080fd5b505afa158015610845573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108699190612e07565b505090506007546000146108b3576007546108a47f000000000000000000000000000000000000000000000000000000000000000083612ef5565b6108ae9190612eba565b6108d5565b7f00000000000000000000000000000000000000000000000000000000000000005b91505090565b60005473ffffffffffffffffffffffffffffffffffffffff16331461095c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b6119648111156109ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f776e65723a204d7573742062652062656c6f77204d4158494d554d5f48415260448201527f564553545f4255464645525f424c4f434b5300000000000000000000000000006064820152608401610578565b60038190556040518181527f6d7e6751f19ef9993001812d2c797cbadcd5d28801195fc8865e1f89210388789060200161079c565b60026001541415610a90576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610578565b60026001558015801590610ab35750336000908152600860205260409020548111155b610b3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f57697468647261773a2053686172657320657175616c20746f2030206f72206c60448201527f6172676572207468616e207573657220736861726573000000000000000000006064820152608401610578565b610b488161206e565b5060018055565b60005473ffffffffffffffffffffffffffffffffffffffff163314610bd0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b60005474010000000000000000000000000000000000000000900460ff16610c54576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610578565b610c5c612471565b565b6040517fab5e32af000000000000000000000000000000000000000000000000000000008152306004820152600090819073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063ab5e32af9060240160206040518083038186803b158015610ce857600080fd5b505afa158015610cfc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d209190612dee565b90506007546000146108b3576007546108a47f000000000000000000000000000000000000000000000000000000000000000083612ef5565b60005473ffffffffffffffffffffffffffffffffffffffff163314610dda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b610c5c600061256a565b60005473ffffffffffffffffffffffffffffffffffffffff163314610e65576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b60005474010000000000000000000000000000000000000000900460ff1615610eea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610578565b610c5c6125df565b60026001541415610f5f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610578565b600260015533600090815260086020526040902054610fda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f57697468647261773a2053686172657320657175616c20746f203000000000006044820152606401610578565b336000908152600860205260409020546106e49061206e565b60005473ffffffffffffffffffffffffffffffffffffffff163314611074576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b8062ffffff16612710148061108f57508062ffffff16610bb8145b806110a057508062ffffff166101f4145b611106576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f4f776e65723a2046656520696e76616c696400000000000000000000000000006044820152606401610578565b600280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ff1661010062ffffff8416908102919091179091556040519081527f6ad74f3d67c0aaacc1d39a227db4945469415b7fbe576e134a5d6d5dbf0d889a9060200161079c565b6040517fab5e32af000000000000000000000000000000000000000000000000000000008152306004820152600090819073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063ab5e32af9060240160206040518083038186803b1580156111f957600080fd5b505afa15801561120d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112319190612dee565b905060075460001461127f5760075473ffffffffffffffffffffffffffffffffffffffff84166000908152600860205260409020546112709083612ef5565b61127a9190612eba565b611282565b60005b9392505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461130a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b600280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040517fac2c97b84646af77f8f38b20c67f88e77b613bb18dcff8dada6d85fc46bbb68590600090a1565b600260015414156113ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610578565b600260015560005474010000000000000000000000000000000000000000900460ff1615611454576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610578565b7f0000000000000000000000000000000000000000000000000000000000000000811015611504576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f4465706f7369743a20416d6f756e74206d757374206265203e3d2031204c4f4f60448201527f4b530000000000000000000000000000000000000000000000000000000000006064820152608401610578565b6003546004546115149190612ea2565b43118015611524575060025460ff165b8015611531575060075415155b1561153e5761153e611d7a565b61158073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163330846126cb565b6040517fab5e32af0000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063ab5e32af9060240160206040518083038186803b15801561160857600080fd5b505afa15801561161c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116409190612dee565b9050600060075460001461166c57816007548461165d9190612ef5565b6116679190612eba565b61166e565b825b9050806116d7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f4465706f7369743a204661696c000000000000000000000000000000000000006044820152606401610578565b33600090815260086020526040812080548392906116f6908490612ea2565b92505081905550806007600082825461170f9190612ea2565b90915550506040517f9a40832100000000000000000000000000000000000000000000000000000000815260048101849052600060248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639a40832190604401600060405180830381600087803b1580156117a357600080fd5b505af11580156117b7573d6000803e3d6000fd5b50506040518581523392507fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c915060200160405180910390a250506001805550565b60005473ffffffffffffffffffffffffffffffffffffffff16331461187a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044015b602060405180830381600087803b15801561194a57600080fd5b505af115801561195e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119829190612d8e565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314611a06576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b60068190556040518181527f0c5f835c1112970802d2e3848cc0541d14975686d176cfc3439b7ac0a9ee28d09060200161079c565b60005473ffffffffffffffffffffffffffffffffffffffff163314611abc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b73ffffffffffffffffffffffffffffffffffffffff8116611b5f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610578565b6119828161256a565b60005473ffffffffffffffffffffffffffffffffffffffff163314611be9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b600280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040517fd1051920bdffd26b2c190ffd77161a6611db762e9d0b4c2b87681f6a45af75ec90600090a1565b60005473ffffffffffffffffffffffffffffffffffffffff163314611cc0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b390604401611930565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16634641257d6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611de257600080fd5b505af1925050508015611df3575060015b506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b158015611e7c57600080fd5b505afa158015611e90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb49190612dee565b90506006548110612067576000611eca826127ad565b90508015612065576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b158015611f5a57600080fd5b505afa158015611f6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f929190612dee565b90507f00000000000000000000000000000000000000000000000000000000000000008110612063576040517f9a40832100000000000000000000000000000000000000000000000000000000815260048101829052600060248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639a40832190604401600060405180830381600087803b15801561204a57600080fd5b505af115801561205e573d6000803e3d6000fd5b505050505b505b505b5043600455565b60035460045461207e9190612ea2565b4311801561208e575060025460ff165b1561209b5761209b611d7a565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b15801561212357600080fd5b505afa158015612137573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061215b9190612dee565b6040517f1959a00200000000000000000000000000000000000000000000000000000000815230600482015290915060009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690631959a0029060240160606040518083038186803b1580156121e657600080fd5b505afa1580156121fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221e9190612e07565b50509050600060075484836122339190612ef5565b61223d9190612eba565b33600090815260086020526040812080549293508692909190612261908490612f32565b92505081905550836007600082825461227a9190612f32565b90915550506040517f38d0743600000000000000000000000000000000000000000000000000000000815260048101829052600060248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906338d0743690604401600060405180830381600087803b15801561230e57600080fd5b505af1158015612322573d6000803e3d6000fd5b50506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000925085915073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906370a082319060240160206040518083038186803b1580156123b057600080fd5b505afa1580156123c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123e89190612dee565b6123f29190612f32565b905061243573ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163383612a07565b60405181815233907f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243649060200160405180910390a25050505050565b60005474010000000000000000000000000000000000000000900460ff166124f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610578565b600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60005474010000000000000000000000000000000000000000900460ff1615612664576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610578565b600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586125403390565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526127a79085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612a62565b50505050565b600080600554600014156127c25760006127e1565b6005546127d784670de0b6b3a7640000612ef5565b6127e19190612eba565b60408051610100808201835273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811683527f000000000000000000000000000000000000000000000000000000000000000081166020840190815260025462ffffff93900483168486019081523060608601908152426080870190815260a087018c815260c088018a8152600060e08a0190815299517f414bf38900000000000000000000000000000000000000000000000000000000815289518816600482015295518716602487015293519096166044850152905184166064840152516084830152925160a4820152915160c48301529251831660e4820152929350917f00000000000000000000000000000000000000000000000000000000000000009091169063414bf3899061010401602060405180830381600087803b15801561293e57600080fd5b505af192505050801561298c575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261298991810190612dee565b60015b6129c3576040517faae67a130b4659edc2a47c783b84fd0693bf47710bfcf2ab37958b563676274190600090a15060009392505050565b60408051868152602081018390527fb69d773dde0af0af79dc63e6051ba81c5d8f46dfbd0c6f32e5537e97c2abf155910160405180910390a1506001949350505050565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052612a5d9084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401612725565b505050565b6000612ac4826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16612b6e9092919063ffffffff16565b805190915015612a5d5780806020019051810190612ae29190612d8e565b612a5d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610578565b6060612b7d8484600085612b85565b949350505050565b606082471015612c17576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610578565b843b612c7f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610578565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051612ca89190612e35565b60006040518083038185875af1925050503d8060008114612ce5576040519150601f19603f3d011682016040523d82523d6000602084013e612cea565b606091505b5091509150612cfa828286612d05565b979650505050505050565b60608315612d14575081611282565b825115612d245782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105789190612e51565b600060208284031215612d6a57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461128257600080fd5b600060208284031215612da057600080fd5b8151801515811461128257600080fd5b600060208284031215612dc257600080fd5b813562ffffff8116811461128257600080fd5b600060208284031215612de757600080fd5b5035919050565b600060208284031215612e0057600080fd5b5051919050565b600080600060608486031215612e1c57600080fd5b8351925060208401519150604084015190509250925092565b60008251612e47818460208701612f49565b9190910192915050565b6020815260008251806020840152612e70816040850160208701612f49565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60008219821115612eb557612eb5612f75565b500190565b600082612ef0577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612f2d57612f2d612f75565b500290565b600082821015612f4457612f44612f75565b500390565b60005b83811015612f64578181015183820152602001612f4c565b838111156127a75750506000910152565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fdfea26469706673582212202c86b54cea867ae71653e1a0b4e948d0d86ef0ab52e0c856e29cee6c1ac8b11a64736f6c63430008070033000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102415760003560e01c80638456cb5911610145578063b8984c86116100bd578063ea09c2a11161008c578063f7b0055311610071578063f7b00553146104d8578063f7c618c1146104e0578063fca3f55b1461050757600080fd5b8063ea09c2a1146104bc578063f2fde38b146104c557600080fd5b8063b8984c8614610452578063d7f8e43f1461045a578063d84e9ebb14610482578063db200bfa146104a957600080fd5b80639a59514111610114578063b10aa43a116100f9578063b10aa43a1461042e578063b3e7860814610436578063b6b55f251461043f57600080fd5b80639a59514114610408578063ab5e32af1461041b57600080fd5b80638456cb59146103d1578063853828b6146103d95780638b2aa597146103e15780638da5cb5b146103ea57600080fd5b80632e1a7d4d116101d85780635c975abb116101a7578063715018a61161018c578063715018a61461037b578063735de9f7146103835780637f085fd1146103aa57600080fd5b80635c975abb146103505780636de26e381461037357600080fd5b80632e1a7d4d146102e057806336db9fb2146102f35780633a98ef391461033f5780633f4ba83a1461034857600080fd5b8063130180de11610214578063130180de1461029b57806314b74d9a146102ae5780631959a002146102b757806328f4dbb6146102d757600080fd5b8063056f7a0f1461024657806305d26905146102505780630a738779146102635780631064ac151461027e575b600080fd5b61024e61050f565b005b61024e61025e366004612dd5565b6106ea565b61026b6107a7565b6040519081526020015b60405180910390f35b60025461028b9060ff1681565b6040519015158152602001610275565b61024e6102a9366004612dd5565b6108db565b61026b60045481565b61026b6102c5366004612d58565b60086020526000908152604090205481565b61026b60065481565b61024e6102ee366004612dd5565b610a23565b61031a7f000000000000000000000000f4d2888d29d722226fafa5d9b24f9164c092421e81565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610275565b61026b60075481565b61024e610b4f565b60005474010000000000000000000000000000000000000000900460ff1661028b565b61026b610c5e565b61024e610d59565b61031a7f000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156481565b61031a7f000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce81565b61024e610de4565b61024e610ef2565b61026b61196481565b60005473ffffffffffffffffffffffffffffffffffffffff1661031a565b61024e610416366004612db0565b610ff3565b61026b610429366004612d58565b61116f565b61024e611289565b61026b60035481565b61024e61044d366004612dd5565b61135d565b61024e6117f9565b60025461046e90610100900462ffffff1681565b60405162ffffff9091168152602001610275565b61026b7f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b61024e6104b7366004612dd5565b611985565b61026b60055481565b61024e6104d3366004612d58565b611a3b565b61024e611b68565b61031a7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b61024e611c3f565b60026001541415610581576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b600260015560005473ffffffffffffffffffffffffffffffffffffffff163314610607576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b600754610670576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f486172766573743a204e6f2073686172650000000000000000000000000000006044820152606401610578565b6004544314156106dc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f486172766573743a20416c726561647920646f6e6500000000000000000000006044820152606401610578565b6106e4611d7a565b60018055565b60005473ffffffffffffffffffffffffffffffffffffffff16331461076b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b60058190556040518181527fbb7d853f3ca3c528bd0d8658c87586e2aa72e909bf52350a2febebc3d86e6e92906020015b60405180910390a150565b6040517f1959a002000000000000000000000000000000000000000000000000000000008152306004820152600090819073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce1690631959a0029060240160606040518083038186803b15801561083157600080fd5b505afa158015610845573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108699190612e07565b505090506007546000146108b3576007546108a47f0000000000000000000000000000000000000000000000000de0b6b3a764000083612ef5565b6108ae9190612eba565b6108d5565b7f0000000000000000000000000000000000000000000000000de0b6b3a76400005b91505090565b60005473ffffffffffffffffffffffffffffffffffffffff16331461095c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b6119648111156109ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f776e65723a204d7573742062652062656c6f77204d4158494d554d5f48415260448201527f564553545f4255464645525f424c4f434b5300000000000000000000000000006064820152608401610578565b60038190556040518181527f6d7e6751f19ef9993001812d2c797cbadcd5d28801195fc8865e1f89210388789060200161079c565b60026001541415610a90576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610578565b60026001558015801590610ab35750336000908152600860205260409020548111155b610b3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f57697468647261773a2053686172657320657175616c20746f2030206f72206c60448201527f6172676572207468616e207573657220736861726573000000000000000000006064820152608401610578565b610b488161206e565b5060018055565b60005473ffffffffffffffffffffffffffffffffffffffff163314610bd0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b60005474010000000000000000000000000000000000000000900460ff16610c54576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610578565b610c5c612471565b565b6040517fab5e32af000000000000000000000000000000000000000000000000000000008152306004820152600090819073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce169063ab5e32af9060240160206040518083038186803b158015610ce857600080fd5b505afa158015610cfc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d209190612dee565b90506007546000146108b3576007546108a47f0000000000000000000000000000000000000000000000000de0b6b3a764000083612ef5565b60005473ffffffffffffffffffffffffffffffffffffffff163314610dda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b610c5c600061256a565b60005473ffffffffffffffffffffffffffffffffffffffff163314610e65576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b60005474010000000000000000000000000000000000000000900460ff1615610eea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610578565b610c5c6125df565b60026001541415610f5f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610578565b600260015533600090815260086020526040902054610fda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f57697468647261773a2053686172657320657175616c20746f203000000000006044820152606401610578565b336000908152600860205260409020546106e49061206e565b60005473ffffffffffffffffffffffffffffffffffffffff163314611074576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b8062ffffff16612710148061108f57508062ffffff16610bb8145b806110a057508062ffffff166101f4145b611106576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f4f776e65723a2046656520696e76616c696400000000000000000000000000006044820152606401610578565b600280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ff1661010062ffffff8416908102919091179091556040519081527f6ad74f3d67c0aaacc1d39a227db4945469415b7fbe576e134a5d6d5dbf0d889a9060200161079c565b6040517fab5e32af000000000000000000000000000000000000000000000000000000008152306004820152600090819073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce169063ab5e32af9060240160206040518083038186803b1580156111f957600080fd5b505afa15801561120d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112319190612dee565b905060075460001461127f5760075473ffffffffffffffffffffffffffffffffffffffff84166000908152600860205260409020546112709083612ef5565b61127a9190612eba565b611282565b60005b9392505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461130a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b600280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040517fac2c97b84646af77f8f38b20c67f88e77b613bb18dcff8dada6d85fc46bbb68590600090a1565b600260015414156113ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610578565b600260015560005474010000000000000000000000000000000000000000900460ff1615611454576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610578565b7f0000000000000000000000000000000000000000000000000de0b6b3a7640000811015611504576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f4465706f7369743a20416d6f756e74206d757374206265203e3d2031204c4f4f60448201527f4b530000000000000000000000000000000000000000000000000000000000006064820152608401610578565b6003546004546115149190612ea2565b43118015611524575060025460ff165b8015611531575060075415155b1561153e5761153e611d7a565b61158073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f4d2888d29d722226fafa5d9b24f9164c092421e163330846126cb565b6040517fab5e32af0000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce73ffffffffffffffffffffffffffffffffffffffff169063ab5e32af9060240160206040518083038186803b15801561160857600080fd5b505afa15801561161c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116409190612dee565b9050600060075460001461166c57816007548461165d9190612ef5565b6116679190612eba565b61166e565b825b9050806116d7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f4465706f7369743a204661696c000000000000000000000000000000000000006044820152606401610578565b33600090815260086020526040812080548392906116f6908490612ea2565b92505081905550806007600082825461170f9190612ea2565b90915550506040517f9a40832100000000000000000000000000000000000000000000000000000000815260048101849052600060248201527f000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce73ffffffffffffffffffffffffffffffffffffffff1690639a40832190604401600060405180830381600087803b1580156117a357600080fd5b505af11580156117b7573d6000803e3d6000fd5b50506040518581523392507fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c915060200160405180910390a250506001805550565b60005473ffffffffffffffffffffffffffffffffffffffff16331461187a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce811660048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248301527f000000000000000000000000f4d2888d29d722226fafa5d9b24f9164c092421e169063095ea7b3906044015b602060405180830381600087803b15801561194a57600080fd5b505af115801561195e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119829190612d8e565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314611a06576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b60068190556040518181527f0c5f835c1112970802d2e3848cc0541d14975686d176cfc3439b7ac0a9ee28d09060200161079c565b60005473ffffffffffffffffffffffffffffffffffffffff163314611abc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b73ffffffffffffffffffffffffffffffffffffffff8116611b5f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610578565b6119828161256a565b60005473ffffffffffffffffffffffffffffffffffffffff163314611be9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b600280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040517fd1051920bdffd26b2c190ffd77161a6611db762e9d0b4c2b87681f6a45af75ec90600090a1565b60005473ffffffffffffffffffffffffffffffffffffffff163314611cc0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610578565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564811660048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248301527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169063095ea7b390604401611930565b7f000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce73ffffffffffffffffffffffffffffffffffffffff16634641257d6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611de257600080fd5b505af1925050508015611df3575060015b506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b158015611e7c57600080fd5b505afa158015611e90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb49190612dee565b90506006548110612067576000611eca826127ad565b90508015612065576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000f4d2888d29d722226fafa5d9b24f9164c092421e73ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b158015611f5a57600080fd5b505afa158015611f6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f929190612dee565b90507f0000000000000000000000000000000000000000000000000de0b6b3a76400008110612063576040517f9a40832100000000000000000000000000000000000000000000000000000000815260048101829052600060248201527f000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce73ffffffffffffffffffffffffffffffffffffffff1690639a40832190604401600060405180830381600087803b15801561204a57600080fd5b505af115801561205e573d6000803e3d6000fd5b505050505b505b505b5043600455565b60035460045461207e9190612ea2565b4311801561208e575060025460ff165b1561209b5761209b611d7a565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000f4d2888d29d722226fafa5d9b24f9164c092421e73ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b15801561212357600080fd5b505afa158015612137573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061215b9190612dee565b6040517f1959a00200000000000000000000000000000000000000000000000000000000815230600482015290915060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce1690631959a0029060240160606040518083038186803b1580156121e657600080fd5b505afa1580156121fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221e9190612e07565b50509050600060075484836122339190612ef5565b61223d9190612eba565b33600090815260086020526040812080549293508692909190612261908490612f32565b92505081905550836007600082825461227a9190612f32565b90915550506040517f38d0743600000000000000000000000000000000000000000000000000000000815260048101829052600060248201527f000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce73ffffffffffffffffffffffffffffffffffffffff16906338d0743690604401600060405180830381600087803b15801561230e57600080fd5b505af1158015612322573d6000803e3d6000fd5b50506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000925085915073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f4d2888d29d722226fafa5d9b24f9164c092421e16906370a082319060240160206040518083038186803b1580156123b057600080fd5b505afa1580156123c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123e89190612dee565b6123f29190612f32565b905061243573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f4d2888d29d722226fafa5d9b24f9164c092421e163383612a07565b60405181815233907f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243649060200160405180910390a25050505050565b60005474010000000000000000000000000000000000000000900460ff166124f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610578565b600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60005474010000000000000000000000000000000000000000900460ff1615612664576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610578565b600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586125403390565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526127a79085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612a62565b50505050565b600080600554600014156127c25760006127e1565b6005546127d784670de0b6b3a7640000612ef5565b6127e19190612eba565b60408051610100808201835273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2811683527f000000000000000000000000f4d2888d29d722226fafa5d9b24f9164c092421e81166020840190815260025462ffffff93900483168486019081523060608601908152426080870190815260a087018c815260c088018a8152600060e08a0190815299517f414bf38900000000000000000000000000000000000000000000000000000000815289518816600482015295518716602487015293519096166044850152905184166064840152516084830152925160a4820152915160c48301529251831660e4820152929350917f000000000000000000000000e592427a0aece92de3edee1f18e0157c058615649091169063414bf3899061010401602060405180830381600087803b15801561293e57600080fd5b505af192505050801561298c575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261298991810190612dee565b60015b6129c3576040517faae67a130b4659edc2a47c783b84fd0693bf47710bfcf2ab37958b563676274190600090a15060009392505050565b60408051868152602081018390527fb69d773dde0af0af79dc63e6051ba81c5d8f46dfbd0c6f32e5537e97c2abf155910160405180910390a1506001949350505050565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052612a5d9084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401612725565b505050565b6000612ac4826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16612b6e9092919063ffffffff16565b805190915015612a5d5780806020019051810190612ae29190612d8e565b612a5d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610578565b6060612b7d8484600085612b85565b949350505050565b606082471015612c17576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610578565b843b612c7f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610578565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051612ca89190612e35565b60006040518083038185875af1925050503d8060008114612ce5576040519150601f19603f3d011682016040523d82523d6000602084013e612cea565b606091505b5091509150612cfa828286612d05565b979650505050505050565b60608315612d14575081611282565b825115612d245782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105789190612e51565b600060208284031215612d6a57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461128257600080fd5b600060208284031215612da057600080fd5b8151801515811461128257600080fd5b600060208284031215612dc257600080fd5b813562ffffff8116811461128257600080fd5b600060208284031215612de757600080fd5b5035919050565b600060208284031215612e0057600080fd5b5051919050565b600080600060608486031215612e1c57600080fd5b8351925060208401519150604084015190509250925092565b60008251612e47818460208701612f49565b9190910192915050565b6020815260008251806020840152612e70816040850160208701612f49565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60008219821115612eb557612eb5612f75565b500190565b600082612ef0577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612f2d57612f2d612f75565b500290565b600082821015612f4457612f44612f75565b500390565b60005b83811015612f64578181015183820152602001612f4c565b838111156127a75750506000910152565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fdfea26469706673582212202c86b54cea867ae71653e1a0b4e948d0d86ef0ab52e0c856e29cee6c1ac8b11a64736f6c63430008070033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564
-----Decoded View---------------
Arg [0] : _feeSharingSystem (address): 0xBcD7254A1D759EFA08eC7c3291B2E85c5dCC12ce
Arg [1] : _uniswapRouter (address): 0xE592427A0AEce92De3Edee1F18E0157C05861564
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000bcd7254a1d759efa08ec7c3291b2e85c5dcc12ce
Arg [1] : 000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564
Loading...
Loading
Loading...
Loading
Net Worth in USD
$251.55
Net Worth in ETH
0.130477
Token Allocations
WETH
100.00%
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 100.00% | $1,927.91 | 0.1305 | $251.55 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.