Source Code
Latest 25 from a total of 122 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Claim Withdrawed... | 24497905 | 19 days ago | IN | 0 ETH | 0.00017754 | ||||
| Claim Withdrawed... | 24492097 | 19 days ago | IN | 0 ETH | 0.00008307 | ||||
| Withdraw | 24378476 | 35 days ago | IN | 0 ETH | 0.00022565 | ||||
| Withdraw | 24323331 | 43 days ago | IN | 0 ETH | 0.00024935 | ||||
| Claim Withdrawed... | 24291443 | 48 days ago | IN | 0 ETH | 0.00010194 | ||||
| Withdraw | 24190744 | 62 days ago | IN | 0 ETH | 0.00023365 | ||||
| Withdraw | 24173791 | 64 days ago | IN | 0 ETH | 0.00013051 | ||||
| Deposit | 24062022 | 80 days ago | IN | 0 ETH | 0.0004954 | ||||
| Claim Withdrawed... | 23939744 | 97 days ago | IN | 0 ETH | 0.00014256 | ||||
| Deposit | 23792948 | 117 days ago | IN | 0 ETH | 0.00063241 | ||||
| Withdraw | 23734625 | 125 days ago | IN | 0 ETH | 0.00040678 | ||||
| Claim Withdrawed... | 23700892 | 130 days ago | IN | 0 ETH | 0.00018077 | ||||
| Claim Withdrawed... | 23692512 | 131 days ago | IN | 0 ETH | 0.00015378 | ||||
| Claim Withdrawed... | 23691341 | 132 days ago | IN | 0 ETH | 0.00018382 | ||||
| Withdraw | 23678669 | 133 days ago | IN | 0 ETH | 0.0001317 | ||||
| Withdraw | 23675380 | 134 days ago | IN | 0 ETH | 0.00023254 | ||||
| Withdraw | 23618531 | 142 days ago | IN | 0 ETH | 0.00019723 | ||||
| Claim Withdrawed... | 23491747 | 159 days ago | IN | 0 ETH | 0.00013328 | ||||
| Deposit | 23490346 | 160 days ago | IN | 0 ETH | 0.00033014 | ||||
| Withdraw | 23490336 | 160 days ago | IN | 0 ETH | 0.00020476 | ||||
| Deposit | 23484708 | 160 days ago | IN | 0 ETH | 0.00033495 | ||||
| Cancel Deposit | 23419724 | 170 days ago | IN | 0 ETH | 0.00014562 | ||||
| Deposit | 23419657 | 170 days ago | IN | 0 ETH | 0.000329 | ||||
| Cancel Deposit | 23419142 | 170 days ago | IN | 0 ETH | 0.00015791 | ||||
| Deposit | 23418983 | 170 days ago | IN | 0 ETH | 0.00034639 |
Latest 2 internal transactions
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60806040 | 20941915 | 516 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20941915 | 516 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
CIVVault
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
/**
* @title Civ Vault
* @author Ren / Frank
*/
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "./interfaces/ICivFund.sol";
import "./CIV-VaultGetter.sol";
import "./CIV-VaultFactory.sol";
import "./dependencies/Ownable.sol";
contract CIVVault is Ownable, ReentrancyGuard {
using SafeERC20 for IERC20;
using SafeERC20 for ICivFundRT;
using Strings for uint;
/// @notice All Fees Base Amount
uint public constant feeBase = 10_000;
/// @notice Max entry Fee Amount
uint public constant maxEntryFee = 1_000;
/// @notice Max users for shares distribution
uint public maxUsersToDistribute;
/// @notice Number of strategies
uint public strategiesCounter;
/// @notice vault getter contract
ICivVaultGetter public vaultGetter;
/// @notice share factory contract
CIVFundShareFactory public fundShareFactory;
/// @notice mapping with info on each strategy
mapping(uint => StrategyInfo) private _strategyInfo;
/// @notice structure with epoch info
mapping(uint => mapping(uint => EpochInfo)) private _epochInfo;
/// @notice Info of each user that enters the fund
mapping(uint => mapping(address => UserInfo)) private _userInfo;
/// @notice Counter for the epochs of each strategy
mapping(uint => uint) private _epochCounter;
/// @notice Each Strategies epoch informations per address
mapping(uint => mapping(address => mapping(uint => UserInfoEpoch)))
private _userInfoEpoch;
/// @notice Mapping of depositors on a particular epoch
mapping(uint => mapping(uint => mapping(uint => address)))
private _depositors;
////////////////// EVENTS //////////////////
/// @notice Event emitted when user deposit fund to our vault or vault deposit fund to strategy
event Deposit(
address indexed user,
address receiver,
uint indexed id,
uint amount
);
/// @notice Event emitted when user request withdraw fund from our vault or vault withdraw fund to user
event Withdraw(address indexed user, uint indexed id, uint amount);
/// @notice Event emitted when owner sets new fee
event SetFee(
uint indexed id,
uint oldFee,
uint newFee,
uint oldDuration,
uint newDuration
);
/// @notice Event emitted when owner sets new entry fee
event SetEntryFee(uint indexed id, uint oldEntryFee, uint newEntryFee);
/// @notice Event emitted when owner sets new deposit duration
event SetEpochDuration(uint indexed id, uint oldDuration, uint newDuration);
/// @notice Event emitted when owner sets new treasury addresses
event SetWithdrawAddress(
uint indexed id,
address[] oldAddress,
address[] newAddress
);
/// @notice Event emitted when owner sets new invest address
event SetInvestAddress(
uint indexed id,
address oldAddress,
address newAddress
);
/// @notice Event emitted when send fee to our treasury
event SendFeeWithOwner(
uint indexed id,
address treasuryAddress,
uint feeAmount
);
/// @notice Event emitted when owner update new VPS
event UpdateVPS(uint indexed id, uint lastEpoch, uint VPS, uint netVPS);
/// @notice Event emitted when owner paused deposit
event SetPaused(uint indexed id, bool paused);
/// @notice Event emitted when owner set new Max & Min Deposit Amount
event SetLimits(
uint indexed id,
uint oldMaxAmount,
uint newMaxAmount,
uint oldMinAmount,
uint newMinAmount,
uint oldMaxUsers,
uint newMaxUsers
);
/// @notice Event emitted when user cancel pending deposit from vault
event CancelDeposit(address indexed user, uint indexed id, uint amount);
/// @notice Event emitted when user cancel withdraw request from vault
event CancelWithdraw(address indexed user, uint indexed id, uint amount);
/// @notice Event emitted when user claim Asset token for each epoch
event ClaimWithdrawedToken(
uint indexed id,
address user,
uint epoch,
uint assetAmount
);
event SharesDistributed(
uint indexed id,
uint epoch,
address indexed investor,
uint dueShares
);
event TransferFailed(
uint indexed strategyId,
uint indexed epoch,
address indexed investor,
uint dueShares
);
/// @notice Event emitted when user claim Asset token
event WithdrawedToken(
uint indexed id,
address indexed user,
uint assetAmount
);
/// @notice Event emitted when owner adds new strategy
event AddStrategy(
uint indexed id,
uint fee,
uint entryFee,
uint maxDeposit,
uint minDeposit,
bool paused,
address[] withdrawAddress,
address assetToken,
uint feeDuration
);
/// @notice Event emitted when strategy is initialized
event InitializeStrategy(uint indexed id);
////////////////// ERROR CODES //////////////////
/*
ERR_V.1 = "Strategy does not exist";
ERR_V.2 = "Deposit paused";
ERR_V.3 = "Treasury Address Length must be 2";
ERR_V.4 = "Burn failed";
ERR_V.5 = "Wait for rebalancing to complete";
ERR_V.6 = "First Treasury address cannot be null address";
ERR_V.7 = "Second Treasury address cannot be null address";
ERR_V.8 = "Minting failed";
ERR_V.9 = "Strategy already initialized";
ERR_V.10 = "No epochs exist";
ERR_V.11 = "Nothing to claim";
ERR_V.12 = "Insufficient contract balance";
ERR_V.13 = "Not enough amount to withdraw";
ERR_V.14 = "Strategy address cannot be null address";
ERR_V.15 = "No pending Fees to distribute";
ERR_V.16 = "Distribute all shares for previous epoch";
ERR_V.17 = "Epoch does not exist";
ERR_V.18 = "Epoch not yet expired";
ERR_V.19 = "Vault balance is not enough to pay fees";
ERR_V.20 = "Amount can't be 0";
ERR_V.21 = "Insufficient User balance";
ERR_V.22 = "No more users are allowed";
ERR_V.23 = "Deposit amount exceeds epoch limit";
ERR_V.24 = "Epoch expired";
ERR_V.25 = "Current balance not enough";
ERR_V.26 = "Not enough total withdrawals";
ERR_V.27 = "VPS not yet updated";
ERR_V.28 = "Already started distribution";
ERR_V.29 = "Not yet distributed";
ERR_V.30 = "Already distributed";
ERR_V.31 = "Fee duration not yet passed";
ERR_V.32 = "Withdraw Token cannot be deposit token";
ERR_V.33 = "Entry fee too high!";
*/
////////////////// MODIFIER //////////////////
modifier checkStrategyExistence(uint _id) {
require(strategiesCounter > _id, "ERR_V.1");
_;
}
modifier checkEpochExistence(uint _id) {
require(_epochCounter[_id] > 0, "ERR_V.10");
_;
}
////////////////// CONSTRUCTOR //////////////////
constructor() {
CivVaultGetter getterContract = new CivVaultGetter(address(this));
fundShareFactory = new CIVFundShareFactory();
vaultGetter = ICivVaultGetter(address(getterContract));
}
////////////////// INITIALIZATION //////////////////
/// @notice Add new strategy to our vault
/// @dev Only Owner can call this function
/// @param addStrategyParam Parameters for new strategy
function addStrategy(
AddStrategyParam memory addStrategyParam
) external virtual nonReentrant onlyOwner {
require(addStrategyParam._withdrawAddresses.length == 2, "ERR_V.3");
require(
addStrategyParam._withdrawAddresses[0] != address(0),
"ERR_V.6"
);
require(
addStrategyParam._withdrawAddresses[1] != address(0),
"ERR_V.7"
);
/// deploy new CIVFundShare contract
uint id = strategiesCounter;
// Generate unique name and symbol
string memory name = string(abi.encodePacked("CIVFundShare ", Strings.toString(id)));
string memory symbol = string(abi.encodePacked("CIVS", Strings.toString(id)));
// deploy new CIVFundShare contract with dynamic name and symbol
CIVFundShare fundRepresentToken = fundShareFactory.createCIVFundShare(
name,
symbol
);
_strategyInfo[id] = StrategyInfo({
assetToken: addStrategyParam._assetToken,
fundRepresentToken: ICivFundRT(address(fundRepresentToken)),
fee: addStrategyParam._fee,
entryFee: addStrategyParam._entryFee,
withdrawAddress: addStrategyParam._withdrawAddresses,
investAddress: addStrategyParam._investAddress,
initialized: false,
pendingFees: 0,
maxDeposit: addStrategyParam._maxDeposit,
maxUsers: addStrategyParam._maxUsers,
minDeposit: addStrategyParam._minAmount,
paused: addStrategyParam._paused,
epochDuration: addStrategyParam._epochDuration,
feeDuration: addStrategyParam._feeDuration,
lastFeeDistribution: 0,
lastProcessedEpoch: 0,
watermark: 0
});
strategiesCounter++;
emit AddStrategy(
id,
addStrategyParam._fee,
addStrategyParam._entryFee,
addStrategyParam._maxDeposit,
addStrategyParam._minAmount,
addStrategyParam._paused,
addStrategyParam._withdrawAddresses,
address(addStrategyParam._assetToken),
addStrategyParam._feeDuration
);
}
/// @notice Internal strategy initialization
/// @dev Internal function
/// @param _id strategy id
function _initializeStrategy(uint _id) internal {
_strategyInfo[_id].initialized = true;
vaultGetter.addTimeOracle(_id, _strategyInfo[_id].epochDuration);
_epochInfo[_id][_epochCounter[_id]] = EpochInfo({
totDepositors: 0,
totDepositedAssets: 0,
totWithdrawnShares: 0,
VPS: 0,
netVPS: 0,
newShares: 0,
currentWithdrawAssets: 0,
epochStartTime: block.timestamp,
entryFee: _strategyInfo[_id].entryFee,
totalFee: 0,
lastDepositorProcessed: 0,
duration: _strategyInfo[_id].epochDuration
});
_epochCounter[_id]++;
}
/// @notice Delayed strategy start
/// @dev Only Owner can call this function
/// @param _id strategy id
function initializeStrategy(
uint _id
) external onlyOwner checkStrategyExistence(_id) {
require(!_strategyInfo[_id].initialized, "ERR_V.9");
_initializeStrategy(_id);
emit InitializeStrategy(_id);
}
////////////////// SETTER //////////////////
/// @notice Sets new fee and new collecting fee duration
/// @dev Only Owner can call this function
/// @param _id Strategy Id
/// @param _newFee New Fee Percent
/// @param _newDuration New Collecting Fee Duration
function setFee(
uint _id,
uint _newFee,
uint _newDuration
) external onlyOwner checkStrategyExistence(_id) {
emit SetFee(
_id,
_strategyInfo[_id].fee,
_newFee,
_strategyInfo[_id].feeDuration,
_newDuration
);
_strategyInfo[_id].fee = _newFee;
_strategyInfo[_id].feeDuration = _newDuration;
}
/// @notice Sets new entry fee
/// @dev Only Owner can call this function
/// @param _id Strategy Id
/// @param _newEntryFee New Fee Percent
function setEntryFee(
uint _id,
uint _newEntryFee
) external onlyOwner checkStrategyExistence(_id) {
emit SetEntryFee(_id, _strategyInfo[_id].entryFee, _newEntryFee);
require(_newEntryFee <= maxEntryFee, "ERR_V.33");
_strategyInfo[_id].entryFee = _newEntryFee;
}
/// @notice Sets new deposit fund from vault to strategy duration
/// @dev Only Owner can call this function
/// @param _id Strategy Id
/// @param _newDuration New Duration for Deposit fund from vault to strategy
function setEpochDuration(
uint _id,
uint _newDuration
) external onlyOwner checkStrategyExistence(_id) {
emit SetEpochDuration(
_id,
_strategyInfo[_id].epochDuration,
_newDuration
);
vaultGetter.setEpochDuration(_id, _newDuration);
_strategyInfo[_id].epochDuration = _newDuration;
}
/// @notice Sets new treasury addresses to keep fee
/// @dev Only Owner can call this function
/// @param _id Strategy Id
/// @param _newAddress Address list to keep fee
function setWithdrawAddress(
uint _id,
address[] memory _newAddress
) external onlyOwner checkStrategyExistence(_id) {
require(_newAddress.length == 2, "ERR_V.3");
require(_newAddress[0] != address(0), "ERR_V.6");
require(_newAddress[1] != address(0), "ERR_V.7");
emit SetWithdrawAddress(
_id,
_strategyInfo[_id].withdrawAddress,
_newAddress
);
_strategyInfo[_id].withdrawAddress = _newAddress;
}
/// @notice Sets new invest address
/// @dev Only Owner can call this function
/// @param _id Strategy Id
/// @param _newAddress Address to invest funds into
function setInvestAddress(
uint _id,
address _newAddress
) external onlyOwner checkStrategyExistence(_id) {
require(_newAddress != address(0), "ERR_V.14");
emit SetInvestAddress(
_id,
_strategyInfo[_id].investAddress,
_newAddress
);
_strategyInfo[_id].investAddress = _newAddress;
}
/// @notice Set Pause or Unpause for deposit to vault
/// @dev Only Owner can change this status
/// @param _id Strategy Id
/// @param _paused paused or unpaused for deposit
function setPaused(
uint _id,
bool _paused
) external onlyOwner checkStrategyExistence(_id) {
emit SetPaused(_id, _paused);
_strategyInfo[_id].paused = _paused;
}
/// @notice Set limits on a given strategy
/// @dev Only Owner can change this status
/// @param _id Strategy Id
/// @param _newMaxDeposit New Max Deposit Amount
/// @param _newMinDeposit New Min Deposit Amount
/// @param _newMaxUsers New Max User Count
function setEpochLimits(
uint _id,
uint _newMaxDeposit,
uint _newMinDeposit,
uint _newMaxUsers
) external onlyOwner checkStrategyExistence(_id) {
emit SetLimits(
_id,
_strategyInfo[_id].maxDeposit,
_newMaxDeposit,
_strategyInfo[_id].minDeposit,
_newMinDeposit,
_strategyInfo[_id].maxUsers,
_newMaxUsers
);
_strategyInfo[_id].maxDeposit = _newMaxDeposit;
_strategyInfo[_id].minDeposit = _newMinDeposit;
_strategyInfo[_id].maxUsers = _newMaxUsers;
}
/// @notice Set the max number of users per distribution
/// @param _maxUsersToDistribute Max number of users to distribute shares to
function setMaxUsersToDistribute(
uint _maxUsersToDistribute
) external onlyOwner {
require(
_maxUsersToDistribute > 0 &&
_maxUsersToDistribute != maxUsersToDistribute,
"Invalid number of users"
);
maxUsersToDistribute = _maxUsersToDistribute;
}
////////////////// GETTER //////////////////
/**
* @dev Fetches the strategy information for a given strategy _id.
* @param _id The ID of the strategy to fetch the information for.
* @return strategy The StrategyInfo struct associated with the provided _id.
*/
function getStrategyInfo(
uint _id
)
external
view
checkStrategyExistence(_id)
returns (StrategyInfo memory strategy)
{
strategy = _strategyInfo[_id];
}
/**
* @dev Fetches the epoch information for a given strategy _id.
* @param _id The ID of the strategy to fetch the information for.
* @param _index The index of the epoch to fetch the information for.
* @return epoch The EpochInfo struct associated with the provided _id and _index.
*/
function getEpochInfo(
uint _id,
uint _index
)
external
view
checkStrategyExistence(_id)
checkEpochExistence(_id)
returns (EpochInfo memory epoch)
{
epoch = _epochInfo[_id][_index];
}
/**
* @dev Fetches the current epoch number for a given strategy _id.
* The current epoch is determined as the last index of the epochInfo mapping for the strategy.
* @param _id The _id of the strategy to fetch the current epoch for.
* @return The current epoch number for the given strategy _id.
*/
function getCurrentEpoch(
uint _id
)
public
view
checkStrategyExistence(_id)
checkEpochExistence(_id)
returns (uint)
{
return _epochCounter[_id] - 1;
}
/**
* @dev Fetches the user information for a given strategy _id.
* @param _id The _id of the strategy to fetch the information for.
* @param _user The address of the user to fetch the information for.
* @return user The UserInfo struct associated with the provided _id and _user.
*/
function getUserInfo(
uint _id,
address _user
) external view checkStrategyExistence(_id) returns (UserInfo memory user) {
user = _userInfo[_id][_user];
}
/**
* @dev Fetches the user information for a given strategy _id.
* @param _id The _id of the strategy to fetch the information for.
* @param _epoch The starting index to fetch the information for.
* @return users An array of addresses of unique depositors.
*/
function getDepositors(
uint _id,
uint _epoch
)
external
view
checkStrategyExistence(_id)
returns (address[] memory users)
{
// Initialize the return array with the size equal to the range between the start and end indices
users = new address[](_epochInfo[_id][_epoch].totDepositors);
// Loop through the mapping to populate the return array
for (uint i = 0; i < _epochInfo[_id][_epoch].totDepositors; i++) {
users[i] = _depositors[_id][_epoch][i];
}
}
/**
* @dev Fetches the deposit parameters for a given strategy _id.
* @param _id The _id of the strategy to fetch the information for.
* @param _user The address of the user to fetch the information for.
* @param _index The index of the deposit to fetch the information for.
* @return userEpochStruct The UserInfoEpoch struct associated with the provided _id, _user and _index.
*/
function getUserInfoEpoch(
uint _id,
address _user,
uint _index
)
external
view
checkStrategyExistence(_id)
returns (UserInfoEpoch memory userEpochStruct)
{
userEpochStruct = _userInfoEpoch[_id][_user][_index];
}
////////////////// UPDATE //////////////////
/**
* @dev Updates the current epoch information for the specified strategy
* @param _id The Strategy _id
*
* This function checks if the current epoch's duration has been met or exceeded.
* If true, it initializes a new epoch with its starting time as the current block timestamp.
* If false, no action is taken.
*
* Requirements:
* - The strategy must be initialized.
* - The current block timestamp must be equal to or greater than the start
* time of the current epoch plus the epoch's duration.
*/
function updateEpoch(uint _id) private checkEpochExistence(_id) {
uint currentEpoch = getCurrentEpoch(_id);
if (
block.timestamp >=
_epochInfo[_id][currentEpoch].epochStartTime +
_epochInfo[_id][currentEpoch].duration
) {
require(_epochInfo[_id][currentEpoch].VPS > 0, "ERR_V.5");
_epochInfo[_id][_epochCounter[_id]] = EpochInfo({
totDepositors: 0,
totDepositedAssets: 0,
totWithdrawnShares: 0,
VPS: 0,
netVPS: 0,
newShares: 0,
currentWithdrawAssets: 0,
epochStartTime: vaultGetter.getCurrentPeriod(_id),
entryFee: _strategyInfo[_id].entryFee,
totalFee: 0,
lastDepositorProcessed: 0,
duration: _strategyInfo[_id].epochDuration
});
_epochCounter[_id]++;
}
}
/// @notice Calculate fees to the treasury address and save it in the strategy mapping and returns net VPS
/**
* @dev Internal function
*/
/// @param _id Strategy _id
/// @param _newVPS new Net Asset Value
/// @return netVPS The new VPS after fees have been deducted
function takePerformanceFees(
uint _id,
uint _newVPS
) private returns (uint netVPS, uint actualFee) {
StrategyInfo storage strategy = _strategyInfo[_id];
uint sharesMultiplier = 10 ** strategy.fundRepresentToken.decimals();
uint totalSupplyShares = strategy.fundRepresentToken.totalSupply();
actualFee = 0;
netVPS = _newVPS;
if (strategy.watermark < _newVPS) {
if (strategy.fee > 0) {
actualFee =
((_newVPS - strategy.watermark) *
strategy.fee *
totalSupplyShares) /
feeBase /
sharesMultiplier;
if (actualFee > 0) {
strategy.pendingFees += actualFee;
// Calculate net VPS based on the actual fee
uint adjustedTotalValue = (_newVPS * totalSupplyShares) /
sharesMultiplier -
actualFee;
netVPS =
(adjustedTotalValue * sharesMultiplier) /
totalSupplyShares;
}
}
strategy.watermark = netVPS;
}
}
/**
* @dev Processes the fund associated with a particular strategy, handling deposits,
* minting, and burning of shares.
* @param _id The Strategy _id
* @param _newVPS New value per share (VPS) expressed in decimals (same as assetToken)
* - must be greater than 0
*
* This function performs the following actions:
* 1. Retrieves the current epoch and strategy info, as well as net VPS and performance Fees;
* 2. Calculate the new shares and current withdrawal based on new VPS;
* 3. Mints or burns shares depending on the new shares and total withdrawals.
* 4. Handles deposits, withdrawals and performance fees by transferring the Asset tokens.
*
* Requirements:
* - `_newVPS` must be greater than 0.
* - The necessary amount of Asset tokens must be present in the contract for deposits if required.
* - The necessary amount of Asset tokens must be present in the investAddress for withdrawals if required.
*/
function processFund(uint _id, uint _newVPS) private {
require(_newVPS > 0, "ERR_V.15");
uint performanceFees;
uint netVPS;
(netVPS, performanceFees) = takePerformanceFees(_id, _newVPS);
// Step 1
EpochInfo storage epoch = _epochInfo[_id][
_strategyInfo[_id].lastProcessedEpoch
];
StrategyInfo memory strategy = _strategyInfo[_id];
epoch.netVPS = netVPS;
uint sharesMultiplier = 10 ** strategy.fundRepresentToken.decimals();
// Step 2
uint newShares = (epoch.totDepositedAssets * sharesMultiplier) / netVPS;
uint currentWithdrawAssets = (netVPS * epoch.totWithdrawnShares) /
sharesMultiplier;
epoch.newShares = newShares;
epoch.currentWithdrawAssets = currentWithdrawAssets;
// Step 3
if (newShares > epoch.totWithdrawnShares) {
uint sharesToMint = newShares - epoch.totWithdrawnShares;
bool success = strategy.fundRepresentToken.mint(sharesToMint);
require(success, "ERR_V.8");
} else {
uint offSetShares = epoch.totWithdrawnShares - newShares;
if (offSetShares > 0) {
bool success = strategy.fundRepresentToken.burn(offSetShares);
require(success, "ERR_V.4");
}
}
// Step 4
if (
epoch.totDepositedAssets >= currentWithdrawAssets + performanceFees
) {
uint netDeposits = epoch.totDepositedAssets -
currentWithdrawAssets -
performanceFees;
if (netDeposits > 0) {
require(
strategy.assetToken.balanceOf(address(this)) >= netDeposits,
"ERR_V.12"
);
strategy.assetToken.safeTransfer(
strategy.investAddress,
netDeposits
);
emit Deposit(
address(this),
strategy.investAddress,
_id,
netDeposits
);
}
} else {
uint offSet = currentWithdrawAssets +
performanceFees -
epoch.totDepositedAssets;
require(
strategy.assetToken.balanceOf(strategy.investAddress) >= offSet,
"ERR_V.13"
);
require(
strategy.assetToken.allowance(strategy.investAddress, address(this)) >=
offSet,
"Insufficient allowance for withdraw"
);
strategy.assetToken.safeTransferFrom(
strategy.investAddress,
address(this),
offSet
);
}
// Transfer totalEntryFee to withdraw Addresses
if (epoch.totalFee > 0) {
require(
strategy.assetToken.balanceOf(address(this)) >= epoch.totalFee,
"ERR_V.19"
);
strategy.assetToken.safeTransfer(
strategy.withdrawAddress[0],
epoch.totalFee / 2
);
strategy.assetToken.safeTransfer(
strategy.withdrawAddress[1],
epoch.totalFee / 2
);
}
updateEpoch(_id);
emit UpdateVPS(_id, strategy.lastProcessedEpoch, _newVPS, netVPS);
}
/// @notice Sets new VPS of the strategy.
/**
* @dev Only Owner can call this function.
* Owner must transfer fund to our vault before calling this function
*/
/// @param _id Strategy _id
/// @param _newVPS New VPS value
function rebalancing(
uint _id,
uint _newVPS
) external nonReentrant onlyOwner checkStrategyExistence(_id) {
StrategyInfo storage strategy = _strategyInfo[_id];
require(strategy.investAddress != address(0), "ERR_V.14");
if (strategy.lastProcessedEpoch == 0) {
EpochInfo storage initEpoch = _epochInfo[_id][0];
if (initEpoch.VPS > 0) {
require(
initEpoch.lastDepositorProcessed == initEpoch.totDepositors,
"ERR_V.16"
);
require(_epochCounter[_id] > 1, "ERR_V.17");
strategy.lastProcessedEpoch++;
EpochInfo storage newEpoch = _epochInfo[_id][1];
require(
block.timestamp >=
newEpoch.epochStartTime + newEpoch.duration,
"ERR_V.18"
);
newEpoch.VPS = _newVPS;
} else {
require(
block.timestamp >=
initEpoch.epochStartTime + initEpoch.duration,
"ERR_V.18"
);
strategy.watermark = _newVPS;
initEpoch.VPS = _newVPS;
}
} else {
require(
_epochInfo[_id][strategy.lastProcessedEpoch]
.lastDepositorProcessed ==
_epochInfo[_id][strategy.lastProcessedEpoch].totDepositors,
"ERR_V.16"
);
strategy.lastProcessedEpoch++;
require(
_epochCounter[_id] > strategy.lastProcessedEpoch,
"ERR_V.17"
);
EpochInfo storage subsequentEpoch = _epochInfo[_id][
strategy.lastProcessedEpoch
];
require(
block.timestamp >=
subsequentEpoch.epochStartTime + subsequentEpoch.duration,
"ERR_V.18"
);
subsequentEpoch.VPS = _newVPS;
}
processFund(_id, _newVPS);
}
////////////////// MAIN //////////////////
/// @notice Users Deposit tokens to our vault
/**
* @dev Anyone can call this function if strategy is not paused.
* Users must approve deposit token before calling this function
* We mint represent token to users so that we can calculate each users deposit amount outside
*/
/// @param _id Strategy _id
/// @param _amount Token Amount to deposit
function deposit(
uint _id,
uint _amount
) external nonReentrant checkStrategyExistence(_id) {
require(_strategyInfo[_id].paused == false, "ERR_V.2");
StrategyInfo storage strategy = _strategyInfo[_id];
require(_amount > strategy.minDeposit, "ERR_V.20");
require(
strategy.assetToken.balanceOf(_msgSender()) >= _amount,
"ERR_V.21"
);
uint curEpoch = getCurrentEpoch(_id);
EpochInfo storage epoch = _epochInfo[_id][curEpoch];
require(
block.timestamp <= epoch.epochStartTime + epoch.duration,
"ERR_V.5"
);
UserInfoEpoch storage userEpoch = _userInfoEpoch[_id][_msgSender()][
curEpoch
];
require(
epoch.totDepositedAssets + _amount <= strategy.maxDeposit,
"ERR_V.23"
);
if (!userEpoch.hasDeposited) {
require(epoch.totDepositors + 1 <= strategy.maxUsers, "ERR_V.22");
_depositors[_id][curEpoch][epoch.totDepositors] = _msgSender();
userEpoch.depositIndex = epoch.totDepositors;
epoch.totDepositors++;
userEpoch.hasDeposited = true;
}
uint feeAmount = strategy.entryFee > 0 ? (_amount * strategy.entryFee) / feeBase : 0;
epoch.totalFee += feeAmount;
epoch.totDepositedAssets += (_amount - feeAmount);
require(
strategy.assetToken.allowance(_msgSender(), address(this)) >=
_amount,
"Insufficient allowance for deposit"
);
strategy.assetToken.safeTransferFrom(
_msgSender(),
address(this),
_amount
);
userEpoch.depositInfo += (_amount - feeAmount);
userEpoch.feePaid += feeAmount;
emit Deposit(_msgSender(), address(this), _id, _amount);
}
/// @notice Immediately withdraw current pending deposit amount
/// @param _id Strategy _id
function cancelDeposit(
uint _id
)
external
nonReentrant
checkStrategyExistence(_id)
checkEpochExistence(_id)
{
StrategyInfo storage strategy = _strategyInfo[_id];
uint curEpoch = getCurrentEpoch(_id);
EpochInfo storage epoch = _epochInfo[_id][curEpoch];
require(
block.timestamp < epoch.epochStartTime + epoch.duration,
"ERR_V.24"
);
UserInfoEpoch storage userEpoch = _userInfoEpoch[_id][_msgSender()][
curEpoch
];
uint amount = userEpoch.depositInfo + userEpoch.feePaid;
require(amount > 0, "ERR_V.20");
// Update state variables first
epoch.totDepositedAssets -= userEpoch.depositInfo;
epoch.totalFee -= userEpoch.feePaid;
// Reset user's deposit info
userEpoch.depositInfo = 0;
userEpoch.feePaid = 0;
// Handle depositors array update
if (epoch.totDepositors > 1) {
// Get the last depositor's address
address lastDepositor = _depositors[_id][curEpoch][
epoch.totDepositors - 1
];
// Replace the current user with the last depositor if they are not the last one
if (userEpoch.depositIndex != epoch.totDepositors - 1) {
_depositors[_id][curEpoch][
userEpoch.depositIndex
] = lastDepositor;
_userInfoEpoch[_id][lastDepositor][curEpoch]
.depositIndex = userEpoch.depositIndex;
}
// Clear the last depositor's slot
_depositors[_id][curEpoch][epoch.totDepositors - 1] = address(0);
} else {
// Clear the only depositor's slot if there's only one depositor
_depositors[_id][curEpoch][0] = address(0);
}
userEpoch.depositIndex = 0;
userEpoch.hasDeposited = false;
epoch.totDepositors--;
// Transfer the assets back to the user
strategy.assetToken.safeTransfer(_msgSender(), amount);
emit CancelDeposit(_msgSender(), _id, amount);
}
/// @notice Sends Withdraw Request to vault
/**
* @dev Withdraw amount user shares from vault
*/
/// @param _id Strategy _id
function withdraw(
uint _id,
uint _amount
)
external
nonReentrant
checkStrategyExistence(_id)
checkEpochExistence(_id)
{
require(_amount > 0, "ERR_V.20");
uint sharesBalance = _strategyInfo[_id].fundRepresentToken.balanceOf(
_msgSender()
);
require(sharesBalance >= _amount, "ERR_V.25");
uint curEpoch = getCurrentEpoch(_id);
require(
block.timestamp <=
_epochInfo[_id][curEpoch].epochStartTime +
_epochInfo[_id][curEpoch].duration,
"ERR_V.5"
);
UserInfoEpoch storage userEpoch = _userInfoEpoch[_id][_msgSender()][
curEpoch
];
UserInfo storage user = _userInfo[_id][_msgSender()];
if (user.lastEpoch > 0 && userEpoch.withdrawInfo == 0)
_claimWithdrawedTokens(_id, user.lastEpoch, _msgSender());
_epochInfo[_id][curEpoch].totWithdrawnShares += _amount;
userEpoch.withdrawInfo += _amount;
if (user.lastEpoch != curEpoch) user.lastEpoch = curEpoch;
require(
_strategyInfo[_id].fundRepresentToken.allowance(
_msgSender(),
address(this)
) >= _amount,
"Insufficient allowance for withdraw"
);
_strategyInfo[_id].fundRepresentToken.safeTransferFrom(
_msgSender(),
address(this),
_amount
);
emit Withdraw(_msgSender(), _id, _amount);
}
/// @notice Immediately claim current pending shares amount
/// @param _id Strategy _id
function cancelWithdraw(
uint _id
)
external
nonReentrant
checkStrategyExistence(_id)
checkEpochExistence(_id)
{
StrategyInfo storage strategy = _strategyInfo[_id];
uint curEpoch = getCurrentEpoch(_id);
EpochInfo storage epoch = _epochInfo[_id][curEpoch];
require(
block.timestamp < epoch.epochStartTime + epoch.duration,
"ERR_V.24"
);
UserInfoEpoch storage userEpoch = _userInfoEpoch[_id][_msgSender()][
curEpoch
];
UserInfo storage user = _userInfo[_id][_msgSender()];
uint amount = userEpoch.withdrawInfo;
require(amount > 0, "ERR_V.20");
userEpoch.withdrawInfo = 0;
user.lastEpoch = 0;
require(epoch.totWithdrawnShares >= amount, "ERR_V.26");
epoch.totWithdrawnShares -= amount;
strategy.fundRepresentToken.safeTransfer(
_msgSender(),
amount
);
emit CancelWithdraw(_msgSender(), _id, amount);
}
/// @notice Internal get withdraw tokens from vault for user
/**
* @dev Withdraw user funds from vault
*/
/// @param _id Strategy _id
/// @param _user Strategy _id
function _claimWithdrawedTokens(
uint _id,
uint _lastEpoch,
address _user
) internal {
EpochInfo storage epoch = _epochInfo[_id][_lastEpoch];
require(epoch.VPS > 0, "ERR_V.27");
uint withdrawInfo = _userInfoEpoch[_id][_user][_lastEpoch].withdrawInfo;
uint availableToClaim;
if (withdrawInfo > 0) {
uint dueWithdraw = (withdrawInfo * epoch.currentWithdrawAssets) /
epoch.totWithdrawnShares;
availableToClaim += dueWithdraw;
emit ClaimWithdrawedToken(_id, _user, _lastEpoch, dueWithdraw);
}
if (availableToClaim > 0) {
_strategyInfo[_id].assetToken.safeTransfer(
_user,
availableToClaim
);
}
emit WithdrawedToken(_id, _user, availableToClaim);
}
/// @notice Get withdraw tokens from vault
/**
* @dev Withdraw my fund from vault
*/
/// @param _id Strategy _id
function claimWithdrawedTokens(
uint _id
) external nonReentrant checkStrategyExistence(_id) {
UserInfo storage user = _userInfo[_id][_msgSender()];
require(user.lastEpoch > 0, "ERR_V.11");
_claimWithdrawedTokens(_id, user.lastEpoch, _msgSender());
user.lastEpoch = 0;
}
/// @notice Distribute shares to the epoch depositors
/// @dev Only Owner can call this function if deposit duration is passed.
/// @param _id Strategy _id
function processDeposits(
uint _id
) external nonReentrant onlyOwner checkStrategyExistence(_id) {
StrategyInfo memory strategy = _strategyInfo[_id];
EpochInfo memory epoch = _epochInfo[_id][strategy.lastProcessedEpoch];
require(epoch.VPS > 0, "ERR_V.27");
require(epoch.lastDepositorProcessed == 0, "ERR_V.28");
if (epoch.totDepositedAssets == 0) {
return;
}
_distributeShares(_id);
}
/**
* @dev Continues the process of distributing shares for a specific strategy, if possible.
* This function is only callable by the contract owner.
* @param _id The _id of the strategy for which to continue distributing shares.
*/
function continueDistributingShares(
uint _id
) external nonReentrant onlyOwner checkStrategyExistence(_id) {
// Check if there's anything to distribute
EpochInfo memory epoch = _epochInfo[_id][
_strategyInfo[_id].lastProcessedEpoch
];
require(epoch.VPS > 0, "ERR_V.27");
require(epoch.lastDepositorProcessed != 0, "ERR_V.29");
require(epoch.lastDepositorProcessed < epoch.totDepositors, "ERR_V.30");
_distributeShares(_id);
}
/**
* @dev Distributes the newly minted shares among the depositors of a specific strategy.
* The function processes depositors until maxUsersToDistribute is rechead if it is greater than 0.
* @param _id The _id of the strategy for which to distribute shares.
*/
function _distributeShares(uint _id) internal {
uint lastProcessedEpoch = _strategyInfo[_id].lastProcessedEpoch;
EpochInfo storage epoch = _epochInfo[_id][lastProcessedEpoch];
uint sharesToDistribute = epoch.newShares;
// Calculate loop limit
uint loopLimit = maxUsersToDistribute > 0
? maxUsersToDistribute + epoch.lastDepositorProcessed
: epoch.totDepositors;
if (loopLimit > epoch.totDepositors) {
loopLimit = epoch.totDepositors;
}
// Initialize a local counter for last processed index
uint lastProcessedIndex = epoch.lastDepositorProcessed;
// Process depositors and distribute shares proportionally
for (uint i = lastProcessedIndex; i < loopLimit; i++) {
address investor = _depositors[_id][lastProcessedEpoch][i];
uint depositInfo = _userInfoEpoch[_id][investor][lastProcessedEpoch]
.depositInfo;
uint dueShares = (sharesToDistribute * depositInfo) /
epoch.totDepositedAssets;
// Transfer shares if dueShares is greater than 0
if (dueShares > 0) {
require(
_strategyInfo[_id].fundRepresentToken.balanceOf(
address(this)
) >= dueShares,
"ERR_V.10"
);
_strategyInfo[_id].fundRepresentToken.safeTransfer(
investor,
dueShares
);
emit SharesDistributed(
_id,
lastProcessedEpoch,
investor,
dueShares
);
}
lastProcessedIndex = i + 1;
}
// Update the epoch's last depositor processed after the loop
epoch.lastDepositorProcessed = lastProcessedIndex;
}
/**
* @notice Distribute pending fees to the treasury addresses
* @dev Internal function
*/
/// @param _id Strategy _id
function sendPendingFees(
uint _id
) external nonReentrant onlyOwner checkStrategyExistence(_id) {
StrategyInfo storage strategy = _strategyInfo[_id];
require(
block.timestamp >=
strategy.lastFeeDistribution + strategy.feeDuration,
"ERR_V.31"
);
strategy.lastFeeDistribution = block.timestamp;
uint pendingFees = strategy.pendingFees;
require(pendingFees > 0, "ERR_V.15");
require(
strategy.assetToken.balanceOf(address(this)) >= pendingFees,
"ERR_V.19"
);
strategy.pendingFees = 0;
address addr0 = strategy.withdrawAddress[0];
address addr1 = strategy.withdrawAddress[1];
emit SendFeeWithOwner(_id, addr0, pendingFees / 2);
emit SendFeeWithOwner(_id, addr1, pendingFees / 2);
strategy.assetToken.safeTransfer(addr0, pendingFees / 2);
strategy.assetToken.safeTransfer(addr1, pendingFees / 2);
}
/// @notice Withdraw ERC-20 Token to the owner
/**
* @dev Only Owner can call this function
*/
/// @param _tokenContract ERC-20 Token address
function withdrawERC20(IERC20 _tokenContract) external onlyOwner {
for (uint i = 0; i < strategiesCounter; i++) {
require(_strategyInfo[i].assetToken != _tokenContract, "ERR_V.32");
}
_tokenContract.safeTransfer(
_msgSender(),
_tokenContract.balanceOf(address(this))
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../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;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
/// @title TimeOracle
/// @author Civilization
/// @notice This contract is used to track periods of time based on a given epoch duration
/// @dev The owner of the contract can change the epoch duration
contract TimeOracle {
address public owner; // Owner of the contract
uint public startTime; // Start time of the tracking
uint public epochDuration; // Duration of each period in seconds
uint public currentPeriod; // Current periods elapsed from the start
/// @notice Initializes the contract with a given epoch duration
/// @param _epochDuration Duration of each period in seconds
constructor(uint _epochDuration) {
owner = msg.sender; // Set the deployer as the owner
startTime = block.timestamp; // Initialization at deployment time
epochDuration = _epochDuration;
}
/// @notice Calculates the start time for current period
/// @return currentPeriodStartTime The start time for the current period
function getCurrentPeriod()
external
view
returns (uint currentPeriodStartTime)
{
require(
block.timestamp >= startTime,
"TimeOracle: Query before start time"
);
// Calculate how many periods have passed since the start
uint period = (block.timestamp - startTime) /
epochDuration;
// Calculate the start time for the current period
currentPeriodStartTime = startTime + period * epochDuration;
return currentPeriodStartTime;
}
/// @notice Allows the owner to set a new epoch duration
/// @param _newEpochDuration The new epoch duration in seconds
function setEpochDuration(uint _newEpochDuration) external {
require(
msg.sender == owner,
"TimeOracle: Only owner can change epochDuration"
);
// Calculate the current period before changing epochDuration
currentPeriod += (block.timestamp - startTime) / epochDuration;
// Update startTime to now
startTime = block.timestamp;
// Update epochDuration
epochDuration = _newEpochDuration;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./dependencies/Ownable.sol";
/// @custom:security-contact info@civfund.org
contract CIVFundShare is ERC20, Ownable {
constructor(
string memory name_,
string memory symbol_,
address owner_
) ERC20(name_, symbol_) {
_transferOwnership(owner_);
}
function mint(uint _amount) public onlyOwner returns (bool) {
_mint(_msgSender(), _amount);
return true;
}
function burn(uint _amount) public returns (bool) {
_burn(_msgSender(), _amount);
return true;
}
}
contract CIVFundShareFactory {
function createCIVFundShare(
string memory name_,
string memory symbol_
) public returns (CIVFundShare) {
CIVFundShare fundRepresentToken = new CIVFundShare(name_, symbol_, msg.sender);
return fundRepresentToken;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "./interfaces/ICivFund.sol";
import "./CIV-TimeOracle.sol";
////////////////// ERROR CODES //////////////////
/*
ERR_VG.1 = "Msg.sender is not the Vault";
ERR_VG.2 = "Nothing to withdraw";
ERR_VG.3 = "Wait for the previos epoch to settle before requesting withdraw";
*/
contract CivVaultGetter {
ICivVault public civVault;
/// @notice Each Strategy time Oracle
mapping(uint => TimeOracle) public timeOracle;
modifier onlyVault() {
require(msg.sender == address(civVault), "ERR_VG.1");
_;
}
constructor(address _civVaultAddress) {
civVault = ICivVault(_civVaultAddress);
}
/// @notice Deploy new Time Oracle for the strategy
/// @param _id Strategy Id
/// @param _epochDuration Epoch Duration
function addTimeOracle(uint _id, uint _epochDuration) external onlyVault {
timeOracle[_id] = new TimeOracle(_epochDuration);
}
/// @notice Set new epochDuration for Strategy
/// @dev Only the Getter can call this function from timeOracle
/// @param _id Strategy Id
/// @param _newEpochDuration new epochDuration
function setEpochDuration(uint _id, uint _newEpochDuration) public {
timeOracle[_id].setEpochDuration(_newEpochDuration);
}
/**
* @dev Get the current period for a Strategy
* @param _id The ID of the Strategy
* @return currentPeriodStartTime The end time for the current period
*/
function getCurrentPeriod(
uint _id
) external view returns (uint currentPeriodStartTime) {
return timeOracle[_id].getCurrentPeriod();
}
/**
* @dev Retrieves the current balance of the user's fund representative token, and liquidity strategy token in a specific strategy.
* @param _id The ID of the strategy from which to retrieve user balance information.
* @param _user The user EOA
* @return representTokenBalance The balance of the user's fund representative token in the given strategy.
* @return assetTokenBalance The balance of the user's liquidity strategy token in the given strategy.
* @return representTokenAddress The contract address of the fund representative token in the given strategy.
* @return assetTokenAddress The contract address of the liquidity strategy token in the given strategy.
*/
function getUserBalances(
uint _id,
address _user
)
external
view
returns (
uint representTokenBalance,
uint assetTokenBalance,
address representTokenAddress,
address assetTokenAddress
)
{
representTokenAddress = address(
civVault.getStrategyInfo(_id).fundRepresentToken
);
IERC20 representToken = IERC20(representTokenAddress);
representTokenBalance = representToken.balanceOf(_user);
assetTokenAddress = address(civVault.getStrategyInfo(_id).assetToken);
IERC20 assetToken = IERC20(assetTokenAddress);
assetTokenBalance = assetToken.balanceOf(_user);
return (
representTokenBalance,
assetTokenBalance,
representTokenAddress,
assetTokenAddress
);
}
/// @notice get unclaimed withdrawed tokens
/// @param _id Strategy Id
/// @param _user User EOA
/// @return unclaimedTokens Unclaimed tokens
function getUnclaimedTokens(
uint _id,
address _user
) public view returns (uint) {
uint lastEpoch = civVault.getUserInfo(_id, _user).lastEpoch;
require(lastEpoch > 0, "ERR_VG.2");
EpochInfo memory epoch = civVault.getEpochInfo(_id, lastEpoch);
require(epoch.VPS > 0, "ERR_VG.3");
uint withdrawInfo = civVault
.getUserInfoEpoch(_id, _user, lastEpoch)
.withdrawInfo;
return
(withdrawInfo * epoch.currentWithdrawAssets) /
epoch.totWithdrawnShares;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
// Modified
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/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 Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev 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: UNLICENSED
pragma solidity 0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
struct StrategyInfo {
// Info on each strategy
IERC20 assetToken; // Address of asset token e.g. USDT
ICivFundRT fundRepresentToken; // Fund Represent tokens for deposit in the strategy XCIV
uint fee; // Strategy Fee Amount
uint entryFee; // Strategy Entry Fee Amount
uint maxDeposit; // Strategy Max Deposit Amount per Epoch
uint maxUsers; // Strategy Max User per Epoch
uint minDeposit; // Strategy Min Deposit Amount
uint epochDuration; // Duration of an Epoch
uint feeDuration; // Fee withdraw period
uint lastFeeDistribution; // Last timestamp of distribution
uint lastProcessedEpoch; // Last Epoch Processed
uint watermark; // Fee watermark
uint pendingFees; // Pending fees that owner can withdraw
address[] withdrawAddress; // Strategy Withdraw Address
address investAddress; // Strategy Invest Address
bool initialized; // Is strategy initialized?
bool paused; // Flag that deposit is paused or not
}
struct EpochInfo {
uint totDepositors; // Current depositors of the epoch
uint totDepositedAssets; // Tot deposited asset in current epoch
uint totWithdrawnShares; // Tot withdrawn asset in current epoch
uint VPS; // VPS after rebalancing
uint netVPS; // Net VPS after rebalancing
uint newShares; // New shares after rebalancing
uint currentWithdrawAssets; // Withdrawn asset after rebalancing
uint epochStartTime; // Epoch start time from time oracle
uint entryFee; // Entry fee of the epoch
uint totalFee; // Total fee of the epoch
uint lastDepositorProcessed; // Last depositor that has recived shares
uint duration;
}
struct UserInfo {
uint lastEpoch; // Last withdraw epoch
}
struct UserInfoEpoch {
uint depositInfo;
uint feePaid;
uint withdrawInfo;
uint depositIndex;
bool hasDeposited;
}
struct AddStrategyParam {
IERC20 _assetToken;
uint _maxDeposit;
uint _maxUsers;
uint _minAmount;
uint _fee;
uint _entryFee;
uint _epochDuration;
uint _feeDuration;
address _investAddress;
address[] _withdrawAddresses;
bool _paused;
}
interface ICivVault {
function feeBase() external view returns (uint);
function getStrategyInfo(
uint _id
) external view returns (StrategyInfo memory);
function getEpochInfo(
uint _id,
uint _index
) external view returns (EpochInfo memory);
function getCurrentEpoch(uint _id) external view returns (uint);
function getUserInfo(
uint _id,
address _user
) external view returns (UserInfo memory);
function getUserInfoEpoch(
uint _id,
address _user,
uint _index
) external view returns (UserInfoEpoch memory);
}
interface ICivFundRT is IERC20 {
function decimals() external view returns (uint8);
function mint(uint _amount) external returns (bool);
function burn(uint _amount) external returns (bool);
}
interface ICivVaultGetter {
function getBalanceOfUser(uint, address) external view returns (uint);
function addTimeOracle(uint, uint) external;
function setEpochDuration(uint, uint) external;
function getCurrentPeriod(uint) external view returns (uint);
}
interface IERC20Extended is IERC20 {
function decimals() external view returns (uint);
function symbol() external view returns (string memory);
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "paris",
"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":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"entryFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxDeposit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minDeposit","type":"uint256"},{"indexed":false,"internalType":"bool","name":"paused","type":"bool"},{"indexed":false,"internalType":"address[]","name":"withdrawAddress","type":"address[]"},{"indexed":false,"internalType":"address","name":"assetToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"feeDuration","type":"uint256"}],"name":"AddStrategy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CancelDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CancelWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"}],"name":"ClaimWithdrawedToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"InitializeStrategy","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":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"treasuryAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"SendFeeWithOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldEntryFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newEntryFee","type":"uint256"}],"name":"SetEntryFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"SetEpochDuration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"SetFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"SetInvestAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldMaxAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMaxAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldMinAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMinAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldMaxUsers","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMaxUsers","type":"uint256"}],"name":"SetLimits","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"SetPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"oldAddress","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"newAddress","type":"address[]"}],"name":"SetWithdrawAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"investor","type":"address"},{"indexed":false,"internalType":"uint256","name":"dueShares","type":"uint256"}],"name":"SharesDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"strategyId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"investor","type":"address"},{"indexed":false,"internalType":"uint256","name":"dueShares","type":"uint256"}],"name":"TransferFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lastEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"VPS","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"netVPS","type":"uint256"}],"name":"UpdateVPS","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"}],"name":"WithdrawedToken","type":"event"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"_assetToken","type":"address"},{"internalType":"uint256","name":"_maxDeposit","type":"uint256"},{"internalType":"uint256","name":"_maxUsers","type":"uint256"},{"internalType":"uint256","name":"_minAmount","type":"uint256"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_entryFee","type":"uint256"},{"internalType":"uint256","name":"_epochDuration","type":"uint256"},{"internalType":"uint256","name":"_feeDuration","type":"uint256"},{"internalType":"address","name":"_investAddress","type":"address"},{"internalType":"address[]","name":"_withdrawAddresses","type":"address[]"},{"internalType":"bool","name":"_paused","type":"bool"}],"internalType":"struct AddStrategyParam","name":"addStrategyParam","type":"tuple"}],"name":"addStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"cancelDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"cancelWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"claimWithdrawedTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"continueDistributingShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeBase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundShareFactory","outputs":[{"internalType":"contract CIVFundShareFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getCurrentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"getDepositors","outputs":[{"internalType":"address[]","name":"users","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getEpochInfo","outputs":[{"components":[{"internalType":"uint256","name":"totDepositors","type":"uint256"},{"internalType":"uint256","name":"totDepositedAssets","type":"uint256"},{"internalType":"uint256","name":"totWithdrawnShares","type":"uint256"},{"internalType":"uint256","name":"VPS","type":"uint256"},{"internalType":"uint256","name":"netVPS","type":"uint256"},{"internalType":"uint256","name":"newShares","type":"uint256"},{"internalType":"uint256","name":"currentWithdrawAssets","type":"uint256"},{"internalType":"uint256","name":"epochStartTime","type":"uint256"},{"internalType":"uint256","name":"entryFee","type":"uint256"},{"internalType":"uint256","name":"totalFee","type":"uint256"},{"internalType":"uint256","name":"lastDepositorProcessed","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct EpochInfo","name":"epoch","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getStrategyInfo","outputs":[{"components":[{"internalType":"contract IERC20","name":"assetToken","type":"address"},{"internalType":"contract ICivFundRT","name":"fundRepresentToken","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"entryFee","type":"uint256"},{"internalType":"uint256","name":"maxDeposit","type":"uint256"},{"internalType":"uint256","name":"maxUsers","type":"uint256"},{"internalType":"uint256","name":"minDeposit","type":"uint256"},{"internalType":"uint256","name":"epochDuration","type":"uint256"},{"internalType":"uint256","name":"feeDuration","type":"uint256"},{"internalType":"uint256","name":"lastFeeDistribution","type":"uint256"},{"internalType":"uint256","name":"lastProcessedEpoch","type":"uint256"},{"internalType":"uint256","name":"watermark","type":"uint256"},{"internalType":"uint256","name":"pendingFees","type":"uint256"},{"internalType":"address[]","name":"withdrawAddress","type":"address[]"},{"internalType":"address","name":"investAddress","type":"address"},{"internalType":"bool","name":"initialized","type":"bool"},{"internalType":"bool","name":"paused","type":"bool"}],"internalType":"struct StrategyInfo","name":"strategy","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"getUserInfo","outputs":[{"components":[{"internalType":"uint256","name":"lastEpoch","type":"uint256"}],"internalType":"struct UserInfo","name":"user","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getUserInfoEpoch","outputs":[{"components":[{"internalType":"uint256","name":"depositInfo","type":"uint256"},{"internalType":"uint256","name":"feePaid","type":"uint256"},{"internalType":"uint256","name":"withdrawInfo","type":"uint256"},{"internalType":"uint256","name":"depositIndex","type":"uint256"},{"internalType":"bool","name":"hasDeposited","type":"bool"}],"internalType":"struct UserInfoEpoch","name":"userEpochStruct","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"initializeStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxEntryFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxUsersToDistribute","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"processDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_newVPS","type":"uint256"}],"name":"rebalancing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"sendPendingFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_newEntryFee","type":"uint256"}],"name":"setEntryFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_newDuration","type":"uint256"}],"name":"setEpochDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_newMaxDeposit","type":"uint256"},{"internalType":"uint256","name":"_newMinDeposit","type":"uint256"},{"internalType":"uint256","name":"_newMaxUsers","type":"uint256"}],"name":"setEpochLimits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_newFee","type":"uint256"},{"internalType":"uint256","name":"_newDuration","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"address","name":"_newAddress","type":"address"}],"name":"setInvestAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxUsersToDistribute","type":"uint256"}],"name":"setMaxUsersToDistribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"address[]","name":"_newAddress","type":"address[]"}],"name":"setWithdrawAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"strategiesCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vaultGetter","outputs":[{"internalType":"contract ICivVaultGetter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_tokenContract","type":"address"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60806040523480156200001157600080fd5b506200001d33620000c5565b600180556040516000903090620000349062000115565b6001600160a01b039091168152602001604051809103906000f08015801562000061573d6000803e3d6000fd5b509050604051620000729062000123565b604051809103906000f0801580156200008f573d6000803e3d6000fd5b50600580546001600160a01b039283166001600160a01b0319918216179091556004805493909216921691909117905562000131565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b610fe480620053a083390190565b610fb1806200638483390190565b61525f80620001416000396000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c80638da5cb5b1161011a578063b0b1f9e9116100ad578063daff97b51161007c578063daff97b51461051d578063db089e7914610530578063e2bbb15814610550578063f2fde38b14610563578063f4f3b2001461057657600080fd5b8063b0b1f9e9146104db578063c602e48e146104ee578063c6a1c131146104f7578063d88083061461050a57600080fd5b806398f3fc57116100e957806398f3fc57146104115780639f01f7ba14610424578063abdbbc5414610437578063ae51accb1461044057600080fd5b80638da5cb5b1461038d57806395ba65851461039e57806395e911a8146103f557806396bda284146103fe57600080fd5b80635408eec91161019d57806377278def1161016c57806377278def146103385780637fae6d4d1461034b578063813e62411461035e5780638550c9081461037157806386b2f7391461038457600080fd5b80635408eec9146102de5780635b65b9ab146102f15780635d7035c61461030457806372fada5c1461032557600080fd5b8063441a3e70116101d9578063441a3e701461029257806345a7f794146102a55780634aad6b22146102b85780634fc37bf7146102cb57600080fd5b80631069f3b51461020b5780631aa277a514610232578063282570f01461025d578063404f4e0d14610272575b600080fd5b61021e610219366004614857565b610589565b604051905181526020015b60405180910390f35b600454610245906001600160a01b031681565b6040516001600160a01b039091168152602001610229565b61027061026b366004614887565b6105f8565b005b610285610280366004614887565b6106e9565b60405161022991906148ed565b6102706102a0366004614887565b610803565b6102706102b3366004614900565b610b6b565b6102706102c6366004614932565b610c2b565b6102706102d9366004614a48565b610ee5565b6102706102ec366004614887565b61137f565b6102706102ff366004614b37565b61144a565b610317610312366004614932565b6114f4565b604051908152602001610229565b610270610333366004614932565b611568565b610270610346366004614932565b6118c6565b610270610359366004614932565b611931565b61027061036c366004614857565b6119c9565b61027061037f366004614887565b611ac2565b6103176103e881565b6000546001600160a01b0316610245565b6103b16103ac366004614b63565b611de1565b6040516102299190600060a0820190508251825260208301516020830152604083015160408301526060830151606083015260808301511515608083015292915050565b61031761271081565b61027061040c366004614932565b611ea6565b600554610245906001600160a01b031681565b610270610432366004614932565b611f5b565b61031760025481565b61045361044e366004614887565b61214f565b6040516102299190815181526020808301519082015260408083015190820152606080830151908201526080808301519082015260a0808301519082015260c0808301519082015260e0808301519082015261010080830151908201526101208083015190820152610140808301519082015261016091820151918101919091526101800190565b6102706104e9366004614932565b6122a3565b61031760035481565b610270610505366004614b9b565b61253e565b610270610518366004614932565b6126e9565b61027061052b366004614be2565b612877565b61054361053e366004614932565b61290c565b6040516102299190614c07565b61027061055e366004614887565b612b19565b610270610571366004614d2d565b612fd1565b610270610584366004614d2d565b613047565b6040805160208101909152600081528280600354116105c35760405162461bcd60e51b81526004016105ba90614d4a565b60405180910390fd5b505060009182526008602090815260408084206001600160a01b03909316845291815291819020815192830190915254815290565b610600613140565b8180600354116106225760405162461bcd60e51b81526004016105ba90614d4a565b60008381526006602090815260409182902060070154825190815290810184905284917fc8cebf046c3a3799bf1206980180c44983f298221ad03c0fe827ab5c5c0a398c910160405180910390a260048054604051630282570f60e41b8152918201859052602482018490526001600160a01b03169063282570f090604401600060405180830381600087803b1580156106bb57600080fd5b505af11580156106cf573d6000803e3d6000fd5b505050600093845250506006602052604090912060070155565b606082806003541161070d5760405162461bcd60e51b81526004016105ba90614d4a565b600084815260076020908152604080832086845290915290205467ffffffffffffffff81111561073f5761073f61494b565b604051908082528060200260200182016040528015610768578160200160208202803683370190505b50915060005b60008581526007602090815260408083208784529091529020548110156107fb576000858152600b60209081526040808320878452825280832084845290915290205483516001600160a01b03909116908490839081106107d1576107d1614d6b565b6001600160a01b0390921660209283029190910190910152806107f381614d97565b91505061076e565b505092915050565b61080b61319c565b81806003541161082d5760405162461bcd60e51b81526004016105ba90614d4a565b600083815260096020526040902054839061085a5760405162461bcd60e51b81526004016105ba90614db0565b6000831161087a5760405162461bcd60e51b81526004016105ba90614dd2565b6000848152600660205260408120600101546001600160a01b03166370a08231336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156108df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109039190614df4565b9050838110156109405760405162461bcd60e51b81526020600482015260086024820152674552525f562e323560c01b60448201526064016105ba565b600061094b866114f4565b6000878152600760208181526040808420858552909152909120600b81015491015491925061097991614e0d565b4211156109985760405162461bcd60e51b81526004016105ba90614e20565b6000868152600a602090815260408083203380855290835281842085855283528184208a85526008845282852091855292529091208054158015906109df57506002820154155b156109f25780546109f2908990336131c6565b600088815260076020908152604080832086845290915281206002018054899290610a1e908490614e0d565b9250508190555086826002016000828254610a399190614e0d565b909155505080548314610a4a578281555b60008881526006602052604090206001015487906001600160a01b031663dd62ed3e336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152306024820152604401602060405180830381865afa158015610ab7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610adb9190614df4565b1015610af95760405162461bcd60e51b81526004016105ba90614e41565b610b203360008a8152600660205260409020600101546001600160a01b031690308a613328565b604051878152889033907ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568906020015b60405180910390a3505050505050610b6760018055565b5050565b610b73613140565b838060035411610b955760405162461bcd60e51b81526004016105ba90614d4a565b60008581526006602081815260409283902060048101549281015460059091015484519384529183018890528284015260608201869052608082015260a08101849052905186917fe688636a9e39148b1edbd6665d6d4a56d8629d5047d44472c5d06bca67f90a4f919081900360c00190a250600093845260066020819052604090942060048101939093559282015560050155565b610c3361319c565b610c3b613140565b808060035411610c5d5760405162461bcd60e51b81526004016105ba90614d4a565b600082815260066020526040902060088101546009820154610c7f9190614e0d565b421015610cb95760405162461bcd60e51b81526020600482015260086024820152674552525f562e333160c01b60448201526064016105ba565b426009820155600c81015480610cfc5760405162461bcd60e51b81526020600482015260086024820152674552525f562e313560c01b60448201526064016105ba565b81546040516370a0823160e01b815230600482015282916001600160a01b0316906370a0823190602401602060405180830381865afa158015610d43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d679190614df4565b1015610da05760405162461bcd60e51b81526020600482015260086024820152674552525f562e313960c01b60448201526064016105ba565b6000600c8301819055600d830180548290610dbd57610dbd614d6b565b6000918252602082200154600d850180546001600160a01b039092169350906001908110610ded57610ded614d6b565b6000918252602090912001546001600160a01b03169050857f783ad58a3e77de8f48cbc5b7b85e67eda94545ca6ac909671c4d4461eedecc5683610e32600287614e84565b604080516001600160a01b03909316835260208301919091520160405180910390a2857f783ad58a3e77de8f48cbc5b7b85e67eda94545ca6ac909671c4d4461eedecc5682610e82600287614e84565b604080516001600160a01b03909316835260208301919091520160405180910390a2610ec582610eb3600286614e84565b86546001600160a01b0316919061338f565b610ed481610eb3600286614e84565b5050505050610ee260018055565b50565b610eed61319c565b610ef5613140565b80610120015151600214610f355760405162461bcd60e51b81526020600482015260076024820152664552525f562e3360c81b60448201526064016105ba565b60006001600160a01b0316816101200151600081518110610f5857610f58614d6b565b60200260200101516001600160a01b031603610fa05760405162461bcd60e51b815260206004820152600760248201526622a9292fab171b60c91b60448201526064016105ba565b60006001600160a01b0316816101200151600181518110610fc357610fc3614d6b565b60200260200101516001600160a01b03160361100b5760405162461bcd60e51b81526020600482015260076024820152664552525f562e3760c81b60448201526064016105ba565b6003546000611019826133c5565b6040516020016110299190614eca565b60405160208183030381529060405290506000611045836133c5565b6040516020016110559190614eff565b60408051601f198184030181529082905260055463766f97bb60e01b83529092506000916001600160a01b039091169063766f97bb9061109b9086908690600401614f57565b6020604051808303816000875af11580156110ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110de9190614f85565b905060405180610220016040528086600001516001600160a01b03168152602001826001600160a01b03168152602001866080015181526020018660a0015181526020018660200151815260200186604001518152602001866060015181526020018660c0015181526020018660e0015181526020016000815260200160008152602001600081526020016000815260200186610120015181526020018661010001516001600160a01b0316815260200160001515815260200186610140015115158152506006600086815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020155606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e082015181600701556101008201518160080155610120820151816009015561014082015181600a015561016082015181600b015561018082015181600c01556101a082015181600d0190805190602001906112a09291906147b8565b506101c0820151600e90910180546101e0840151610200909401511515600160a81b0260ff60a81b19941515600160a01b026001600160a81b03199092166001600160a01b039094169390931717929092161790556003805490600061130583614d97565b9190505550837f573c012a6af537230a7dfab893cb56e84491db0e7f7805b1553a2b8ca07a354f86608001518760a00151886020015189606001518a61014001518b61012001518c600001518d60e0015160405161136a989796959493929190614fa2565b60405180910390a250505050610ee260018055565b611387613140565b8180600354116113a95760405162461bcd60e51b81526004016105ba90614d4a565b60008381526006602090815260409182902060030154825190815290810184905284917f86d68dfe175a935769121f8412fc74b00e6ca0834585dc31e5562b0709f9c6c3910160405180910390a26103e88211156114345760405162461bcd60e51b81526020600482015260086024820152674552525f562e333360c01b60448201526064016105ba565b5060009182526006602052604090912060030155565b611452613140565b8280600354116114745760405162461bcd60e51b81526004016105ba90614d4a565b60008481526006602090815260409182902060028101546008909101548351918252918101869052918201526060810183905284907f7eb9abb1c530a0c5c9e6a557c080514b3d38d59cb1d2a0fc085405d67f991a029060800160405180910390a250600092835260066020526040909220600281019190915560080155565b60008180600354116115185760405162461bcd60e51b81526004016105ba90614d4a565b60008381526009602052604090205483906115455760405162461bcd60e51b81526004016105ba90614db0565b60008481526009602052604090205461156090600190614ff7565b949350505050565b61157061319c565b8080600354116115925760405162461bcd60e51b81526004016105ba90614d4a565b60008281526009602052604090205482906115bf5760405162461bcd60e51b81526004016105ba90614db0565b6000838152600660205260408120906115d7856114f4565b6000868152600760208181526040808420858552909152909120600b81015491810154929350916116089190614e0d565b42106116415760405162461bcd60e51b815260206004820152600860248201526711549497d58b8c8d60c21b60448201526064016105ba565b6000868152600a6020908152604080832033845282528083208584529091528120600181015481549192916116769190614e0d565b9050600081116116985760405162461bcd60e51b81526004016105ba90614dd2565b81546001840180546000906116ae908490614ff7565b909155505060018201546009840180546000906116cc908490614ff7565b909155505060008083556001808401919091558354111561180b576000888152600b6020908152604080832087845290915281208454829061171090600190614ff7565b815260208101919091526040016000205484546001600160a01b03909116915061173c90600190614ff7565b8360030154146117aa576000898152600b60209081526040808320888452825280832060038781018054865291845282852080546001600160a01b0319166001600160a01b03881690811790915591548e8652600a85528386209286529184528285208a8652909352922001555b6000898152600b602090815260408083208884529091528120855482906117d390600190614ff7565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055505061183b565b6000888152600b602090815260408083208784528252808320838052909152902080546001600160a01b03191690555b60006003830181905560048301805460ff19169055835490849061185e8361500a565b919050555061187f61186d3390565b86546001600160a01b0316908361338f565b604051818152889033907f4d03849d7cc7e6aa8812404182c6821dda42bc4a15e60a2055dee048bff349109060200160405180910390a350505050505050610ee260018055565b6118ce613140565b6000811180156118e057506002548114155b61192c5760405162461bcd60e51b815260206004820152601760248201527f496e76616c6964206e756d626572206f6620757365727300000000000000000060448201526064016105ba565b600255565b61193961319c565b80806003541161195b5760405162461bcd60e51b81526004016105ba90614d4a565b6000828152600860209081526040808320338452909152902080546119ad5760405162461bcd60e51b81526020600482015260086024820152674552525f562e313160c01b60448201526064016105ba565b80546119bb908490336131c6565b6000905550610ee260018055565b6119d1613140565b8180600354116119f35760405162461bcd60e51b81526004016105ba90614d4a565b6001600160a01b038216611a345760405162461bcd60e51b815260206004820152600860248201526711549497d58b8c4d60c21b60448201526064016105ba565b600083815260066020908152604091829020600e015482516001600160a01b0391821681529085169181019190915284917f94c8b3c75ba308c40e2ac10a7a6e9e5ff5c689a07cddb9a6b7a1227682e47283910160405180910390a250600091825260066020526040909120600e0180546001600160a01b0319166001600160a01b03909216919091179055565b611aca61319c565b611ad2613140565b818060035411611af45760405162461bcd60e51b81526004016105ba90614d4a565b6000838152600660205260409020600e8101546001600160a01b0316611b475760405162461bcd60e51b815260206004820152600860248201526711549497d58b8c4d60c21b60448201526064016105ba565b80600a0154600003611cb65760008481526007602090815260408083208380529091529020600381015415611c6e578054600a82015414611bb55760405162461bcd60e51b815260206004820152600860248201526722a9292fab17189b60c11b60448201526064016105ba565b600085815260096020526040902054600110611bfe5760405162461bcd60e51b81526020600482015260086024820152674552525f562e313760c01b60448201526064016105ba565b600a82018054906000611c1083614d97565b9091555050600085815260076020818152604080842060018552909152909120600b810154918101549091611c4491614e0d565b421015611c635760405162461bcd60e51b81526004016105ba90615021565b600301849055611cb0565b80600b01548160070154611c829190614e0d565b421015611ca15760405162461bcd60e51b81526004016105ba90615021565b600b8201849055600381018490555b50611dcc565b6000848152600760209081526040808320600a8581015485529252909120805491015414611d115760405162461bcd60e51b815260206004820152600860248201526722a9292fab17189b60c11b60448201526064016105ba565b600a81018054906000611d2383614d97565b9091555050600a81015460008581526009602052604090205411611d745760405162461bcd60e51b81526020600482015260086024820152674552525f562e313760c01b60448201526064016105ba565b6000848152600760208181526040808420600a8601548552909152909120600b810154918101549091611da691614e0d565b421015611dc55760405162461bcd60e51b81526004016105ba90615021565b6003018390555b611dd68484613458565b5050610b6760018055565b611e156040518060a00160405280600081526020016000815260200160008152602001600081526020016000151581525090565b838060035411611e375760405162461bcd60e51b81526004016105ba90614d4a565b50506000928352600a602090815260408085206001600160a01b03909416855292815282842091845290815291819020815160a08101835281548152600182015493810193909352600281015491830191909152600381015460608301526004015460ff161515608082015290565b611eae613140565b808060035411611ed05760405162461bcd60e51b81526004016105ba90614d4a565b6000828152600660205260409020600e0154600160a01b900460ff1615611f235760405162461bcd60e51b81526020600482015260076024820152664552525f562e3960c81b60448201526064016105ba565b611f2c82613cb9565b60405182907f74e534d9ee9babee8812594b75f57a2889a16ee1b6c28c2de11b5afffad02b8390600090a25050565b611f6361319c565b808060035411611f855760405162461bcd60e51b81526004016105ba90614d4a565b6000828152600960205260409020548290611fb25760405162461bcd60e51b81526004016105ba90614db0565b600083815260066020526040812090611fca856114f4565b6000868152600760208181526040808420858552909152909120600b8101549181015492935091611ffb9190614e0d565b42106120345760405162461bcd60e51b815260206004820152600860248201526711549497d58b8c8d60c21b60448201526064016105ba565b6000868152600a602090815260408083203380855290835281842086855283528184208a855260088452828520918552925290912060028201548061208b5760405162461bcd60e51b81526004016105ba90614dd2565b600060028085018290559083558401548111156120d55760405162461bcd60e51b815260206004820152600860248201526722a9292fab17191b60c11b60448201526064016105ba565b808460020160008282546120e99190614ff7565b90915550506001860154612107906001600160a01b0316338361338f565b604051818152899033907f39e2e01794006bc1f63835af5c05db790beca4bfb40de3f02cc3ddf22dccc0fb9060200160405180910390a35050505050505050610ee260018055565b6121b36040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8280600354116121d55760405162461bcd60e51b81526004016105ba90614d4a565b60008481526009602052604090205484906122025760405162461bcd60e51b81526004016105ba90614db0565b505050600091825260076020818152604080852093855292815292829020825161018081018452815481526001820154948101949094526002810154928401929092526003820154606084015260048201546080840152600582015460a0840152600682015460c084015281015460e083015260088101546101008301526009810154610120830152600a810154610140830152600b015461016082015290565b6122ab61319c565b6122b3613140565b8080600354116122d55760405162461bcd60e51b81526004016105ba90614d4a565b600082815260066020818152604080842081516102208101835281546001600160a01b039081168252600183015416818501526002820154818401526003820154606082015260048201546080820152600582015460a08201529381015460c0850152600781015460e085015260088101546101008501526009810154610120850152600a810154610140850152600b810154610160850152600c810154610180850152600d8101805483518186028101860190945280845291936101a086019392908301828280156123d157602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116123b3575b5050509183525050600e91909101546001600160a01b03811660208084019190915260ff600160a01b830481161515604080860191909152600160a81b909304161515606093840152600087815260078083528382206101408088015184529084529184902084516101808101865281548152600182015494810194909452600281015494840194909452600384015494830185905260048401546080840152600584015460a0840152600684015460c084015283015460e083015260088301546101008301526009830154610120830152600a83015490820152600b909101546101608201529192506124d75760405162461bcd60e51b81526004016105ba90615043565b610140810151156125155760405162461bcd60e51b815260206004820152600860248201526708aa4a4beac5c64760c31b60448201526064016105ba565b8060200151600003612528575050612534565b61253184613eb7565b50505b50610ee260018055565b612546613140565b8180600354116125685760405162461bcd60e51b81526004016105ba90614d4a565b81516002146125a35760405162461bcd60e51b81526020600482015260076024820152664552525f562e3360c81b60448201526064016105ba565b60006001600160a01b0316826000815181106125c1576125c1614d6b565b60200260200101516001600160a01b0316036126095760405162461bcd60e51b815260206004820152600760248201526622a9292fab171b60c91b60448201526064016105ba565b60006001600160a01b03168260018151811061262757612627614d6b565b60200260200101516001600160a01b03160361266f5760405162461bcd60e51b81526020600482015260076024820152664552525f562e3760c81b60448201526064016105ba565b827f13f42559ce1a76db34ac3c5b7141308c1e286e00168793042025be3e556bfc5560066000868152602001908152602001600020600d01846040516126b6929190615065565b60405180910390a2600083815260066020908152604090912083516126e392600d909201918501906147b8565b50505050565b6126f161319c565b6126f9613140565b80806003541161271b5760405162461bcd60e51b81526004016105ba90614d4a565b60008281526007602081815260408084206006808452828620600a908101548752918452948290208251610180810184528154815260018201549481019490945260028101549284019290925260038201546060840181905260048301546080850152600583015460a08501529482015460c08401529281015460e08301526008810154610100830152600981015461012083015291820154610140820152600b90910154610160820152906127e35760405162461bcd60e51b81526004016105ba90615043565b8061014001516000036128235760405162461bcd60e51b81526020600482015260086024820152674552525f562e323960c01b60448201526064016105ba565b8051610140820151106128635760405162461bcd60e51b815260206004820152600860248201526704552525f562e33360c41b60448201526064016105ba565b61286c83613eb7565b5050610ee260018055565b61287f613140565b8180600354116128a15760405162461bcd60e51b81526004016105ba90614d4a565b827f77f1fcfcce67dc392d64f842056d2ec06c80986c47c910f7e79c5b23a2738d74836040516128d5911515815260200190565b60405180910390a250600091825260066020526040909120600e018054911515600160a81b0260ff60a81b19909216919091179055565b6129b260405180610220016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016060815260200160006001600160a01b031681526020016000151581526020016000151581525090565b8180600354116129d45760405162461bcd60e51b81526004016105ba90614d4a565b60008381526006602081815260409283902083516102208101855281546001600160a01b039081168252600183015416818401526002820154818601526003820154606082015260048201546080820152600582015460a08201529281015460c0840152600781015460e084015260088101546101008401526009810154610120840152600a810154610140840152600b810154610160840152600c810154610180840152600d81018054855181850281018501909652808652939491936101a0860193830182828015612ad157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612ab3575b5050509183525050600e91909101546001600160a01b038116602083015260ff600160a01b8204811615156040840152600160a81b9091041615156060909101529392505050565b612b2161319c565b818060035411612b435760405162461bcd60e51b81526004016105ba90614d4a565b6000838152600660205260409020600e0154600160a81b900460ff1615612b965760405162461bcd60e51b815260206004820152600760248201526622a9292fab171960c91b60448201526064016105ba565b6000838152600660208190526040909120908101548311612bc95760405162461bcd60e51b81526004016105ba90614dd2565b805483906001600160a01b03166370a08231336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015612c20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c449190614df4565b1015612c7d5760405162461bcd60e51b81526020600482015260086024820152674552525f562e323160c01b60448201526064016105ba565b6000612c88856114f4565b6000868152600760208181526040808420858552909152909120600b8101549181015492935091612cb99190614e0d565b421115612cd85760405162461bcd60e51b81526004016105ba90614e20565b6000868152600a602090815260408083203384528252808320858452909152902060048401546001830154612d0e908890614e0d565b1115612d475760405162461bcd60e51b81526020600482015260086024820152674552525f562e323360c01b60448201526064016105ba565b600481015460ff16612dfa5760058401548254612d65906001614e0d565b1115612d9e5760405162461bcd60e51b815260206004820152600860248201526722a9292fab17191960c11b60448201526064016105ba565b6000878152600b60209081526040808320868452825280832085548452909152812080546001600160a01b03191633179055825460038301819055908390612de583614d97565b909155505060048101805460ff191660011790555b600080856003015411612e0e576000612e2b565b612710856003015488612e2191906150c3565b612e2b9190614e84565b905080836009016000828254612e419190614e0d565b90915550612e5190508188614ff7565b836001016000828254612e649190614e0d565b9091555050845487906001600160a01b031663dd62ed3e336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152306024820152604401602060405180830381865afa158015612ec6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eea9190614df4565b1015612f435760405162461bcd60e51b815260206004820152602260248201527f496e73756666696369656e7420616c6c6f77616e636520666f72206465706f736044820152611a5d60f21b60648201526084016105ba565b612f5a3386546001600160a01b031690308a613328565b612f648188614ff7565b826000016000828254612f779190614e0d565b9250508190555080826001016000828254612f929190614e0d565b90915550506040805130815260208101899052899133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79101610b50565b612fd9613140565b6001600160a01b03811661303e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016105ba565b610ee2816140ce565b61304f613140565b60005b6003548110156130c2576000818152600660205260409020546001600160a01b038084169116036130b05760405162461bcd60e51b815260206004820152600860248201526722a9292fab17199960c11b60448201526064016105ba565b806130ba81614d97565b915050613052565b50610ee2336040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa15801561310b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061312f9190614df4565b6001600160a01b038416919061338f565b6000546001600160a01b0316331461319a5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016105ba565b565b6002600154036131bf57604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b6000838152600760209081526040808320858452909152902060038101546132005760405162461bcd60e51b81526004016105ba90615043565b6000848152600a602090815260408083206001600160a01b038616845282528083208684529091528120600201549081156132b3576000836002015484600601548461324c91906150c3565b6132569190614e84565b90506132628183614e0d565b604080516001600160a01b03881681526020810189905290810183905290925087907f25d1255c398e637e957d1d04691db7d1b318bc88e20deda96f2c8714f8297e389060600160405180910390a2505b80156132dc576000868152600660205260409020546132dc906001600160a01b0316858361338f565b836001600160a01b0316867ffd8c040503c4beb434790f3a2e972438d06ece0dc342f58052f2f9fd385828778360405161331891815260200190565b60405180910390a3505050505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526126e39186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061411e565b6040516001600160a01b038381166024830152604482018390526133c091859182169063a9059cbb9060640161335d565b505050565b606060006133d283614181565b600101905060008167ffffffffffffffff8111156133f2576133f261494b565b6040519080825280601f01601f19166020018201604052801561341c576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461342657509392505050565b600081116134935760405162461bcd60e51b81526020600482015260086024820152674552525f562e313560c01b60448201526064016105ba565b6000806134a0848461425a565b60008681526007602081815260408084206006808452828620600a8101548088529285528387208d885282865284516102208101865282546001600160a01b039081168252600184015416818801526002830154818701526003830154606082015260048301546080820152600583015460a08201529282015460c08401529581015460e083015260088101546101008301526009810154610120830152610140820192909252600b820154610160820152600c820154610180820152600d82018054845181870281018701909552808552979a5097985093969495939490936101a08601938301828280156135bf57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116135a1575b5050509183525050600e91909101546001600160a01b0380821660208085019190915260ff600160a01b840481161515604080870191909152600160a81b909404161515606090940193909352600480870188905584840151835163313ce56760e01b8152935195965060009592169363313ce56793808301939192908290030181865afa158015613655573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061367991906150da565b61368490600a6151e1565b905060008482856001015461369991906150c3565b6136a39190614e84565b90506000828560020154876136b891906150c3565b6136c29190614e84565b600586018390556006860181905560028601549091508211156137aa5760008560020154836136f19190614ff7565b9050600085602001516001600160a01b031663a0712d68836040518263ffffffff1660e01b815260040161372791815260200190565b6020604051808303816000875af1158015613746573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061376a91906151f0565b9050806137a35760405162461bcd60e51b815260206004820152600760248201526608aa4a4beac5c760cb1b60448201526064016105ba565b5050613873565b60008286600201546137bc9190614ff7565b90508015613871576020850151604051630852cd8d60e31b8152600481018390526000916001600160a01b0316906342966c68906024016020604051808303816000875af1158015613812573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061383691906151f0565b90508061386f5760405162461bcd60e51b815260206004820152600760248201526611549497d58b8d60ca1b60448201526064016105ba565b505b505b61387d8782614e0d565b8560010154106139c25760008782876001015461389a9190614ff7565b6138a49190614ff7565b905080156139bc5784516040516370a0823160e01b815230600482015282916001600160a01b0316906370a0823190602401602060405180830381865afa1580156138f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139179190614df4565b10156139505760405162461bcd60e51b815260206004820152600860248201526722a9292fab17189960c11b60448201526064016105ba565b6101c0850151855161396e916001600160a01b03909116908361338f565b6101c0850151604080516001600160a01b039092168252602082018390528b9130917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a35b50613b49565b60018501546000906139d48984614e0d565b6139de9190614ff7565b85516101c08701516040516370a0823160e01b81526001600160a01b03918216600482015292935083929116906370a0823190602401602060405180830381865afa158015613a31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a559190614df4565b1015613a8e5760405162461bcd60e51b81526020600482015260086024820152674552525f562e313360c01b60448201526064016105ba565b84516101c0860151604051636eb1769f60e11b81526001600160a01b0391821660048201523060248201528392919091169063dd62ed3e90604401602060405180830381865afa158015613ae6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b0a9190614df4565b1015613b285760405162461bcd60e51b81526004016105ba90614e41565b6101c08501518551613b47916001600160a01b03909116903084613328565b505b600985015415613c5d57600985015484516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015613b9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bc39190614df4565b1015613bfc5760405162461bcd60e51b81526020600482015260086024820152674552525f562e313960c01b60448201526064016105ba565b613c42846101a00151600081518110613c1757613c17614d6b565b602002602001015160028760090154613c309190614e84565b86516001600160a01b0316919061338f565b613c5d846101a00151600181518110613c1757613c17614d6b565b613c6689614433565b61014084015160408051918252602082018a9052810187905289907f91f529843346f3c2fdcba5e8826d1a7f15ac1d049167589ccc580e07ab7e3a3f9060600160405180910390a2505050505050505050565b60008181526006602052604090819020600e8101805460ff60a01b1916600160a01b17905560048054600790920154925163ddaf8d0760e01b81526001600160a01b039092169263ddaf8d0792613d1c9286929101918252602082015260400190565b600060405180830381600087803b158015613d3657600080fd5b505af1158015613d4a573d6000803e3d6000fd5b5050505060405180610180016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001428152602001600660008481526020019081526020016000206003015481526020016000815260200160008152602001600660008481526020019081526020016000206007015481525060076000838152602001908152602001600020600060096000858152602001908152602001600020548152602001908152602001600020600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e082015181600701556101008201518160080155610120820151816009015561014082015181600a015561016082015181600b0155905050600960008281526020019081526020016000206000815480929190613eaf90614d97565b919050555050565b6000818152600660209081526040808320600a0154600783528184208185529092528220600581015460025492939192909190613ef5578254613f07565b82600a0154600254613f079190614e0d565b8354909150811115613f17575081545b600a830154805b828110156140be576000878152600b6020908152604080832089845282528083208484528252808320548a8452600a83528184206001600160a01b039091168085529083528184208a8552909252822054600188015491929091613f8283896150c3565b613f8c9190614e84565b9050801561409b5760008a815260066020526040908190206001015490516370a0823160e01b815230600482015282916001600160a01b0316906370a0823190602401602060405180830381865afa158015613fec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140109190614df4565b101561402e5760405162461bcd60e51b81526004016105ba90614db0565b60008a815260066020526040902060010154614054906001600160a01b0316848361338f565b604080518a8152602081018390526001600160a01b038516918c917f87dc6b8c12f17ba1cf9231ef9295a05eacf04b5c913d5488cdae799fc8e95081910160405180910390a35b6140a6846001614e0d565b945050505080806140b690614d97565b915050613f1e565b50600a9093019290925550505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006141336001600160a01b03841683614681565b9050805160001415801561415857508080602001905181019061415691906151f0565b155b156133c057604051635274afe760e01b81526001600160a01b03841660048201526024016105ba565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106141c05772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef810000000083106141ec576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061420a57662386f26fc10000830492506010015b6305f5e1008310614222576305f5e100830492506008015b612710831061423657612710830492506004015b60648310614248576064830492506002015b600a8310614254576001015b92915050565b60008281526006602090815260408083206001810154825163313ce56760e01b815292518594929385936001600160a01b039093169263313ce567926004808401938290030181865afa1580156142b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142d991906150da565b6142e490600a6151e1565b905060008260010160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561433d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143619190614df4565b9050600093508594508583600b0154101561442957600283015415614421578161271082856002015486600b01548a61439a9190614ff7565b6143a491906150c3565b6143ae91906150c3565b6143b89190614e84565b6143c29190614e84565b93508315614421578383600c0160008282546143de9190614e0d565b909155506000905084836143f2848a6150c3565b6143fc9190614e84565b6144069190614ff7565b90508161441384836150c3565b61441d9190614e84565b9550505b600b83018590555b5050509250929050565b60008181526009602052604090205481906144605760405162461bcd60e51b81526004016105ba90614db0565b600061446b836114f4565b6000848152600760208181526040808420858552909152909120600b81015491015491925061449991614e0d565b42106133c05760008381526007602090815260408083208484529091529020600301546144d85760405162461bcd60e51b81526004016105ba90614e20565b60405180610180016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600460009054906101000a90046001600160a01b03166001600160a01b03166322684db0866040518263ffffffff1660e01b815260040161455891815260200190565b602060405180830381865afa158015614575573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145999190614df4565b815260008581526006602081815260408084206003808201548488015282870186905260608088018790526007928301546080988901528b8752828552838720600980875285892080548a529187528589208b5181558b8801516001820155958b01516002870155918a01519285019290925596880151600484015560a0880151600584015560c08801519483019490945560e087015190820155610100860151600882015561012086015181860155610140860151600a82015561016090950151600b909501949094558682529190925280549161467783614d97565b9190505550505050565b606061468f83836000614696565b9392505050565b6060814710156146bb5760405163cd78605960e01b81523060048201526024016105ba565b600080856001600160a01b031684866040516146d7919061520d565b60006040518083038185875af1925050503d8060008114614714576040519150601f19603f3d011682016040523d82523d6000602084013e614719565b606091505b5091509150614729868383614733565b9695505050505050565b606082614748576147438261478f565b61468f565b815115801561475f57506001600160a01b0384163b155b1561478857604051639996b31560e01b81526001600160a01b03851660048201526024016105ba565b508061468f565b80511561479f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b82805482825590600052602060002090810192821561480d579160200282015b8281111561480d57825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906147d8565b5061481992915061481d565b5090565b5b80821115614819576000815560010161481e565b6001600160a01b0381168114610ee257600080fd5b803561485281614832565b919050565b6000806040838503121561486a57600080fd5b82359150602083013561487c81614832565b809150509250929050565b6000806040838503121561489a57600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b838110156148e25781516001600160a01b0316875295820195908201906001016148bd565b509495945050505050565b60208152600061468f60208301846148a9565b6000806000806080858703121561491657600080fd5b5050823594602084013594506040840135936060013592509050565b60006020828403121561494457600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b604051610160810167ffffffffffffffff811182821017156149855761498561494b565b60405290565b600082601f83011261499c57600080fd5b8135602067ffffffffffffffff808311156149b9576149b961494b565b8260051b604051601f19603f830116810181811084821117156149de576149de61494b565b6040529384528581018301938381019250878511156149fc57600080fd5b83870191505b84821015614a24578135614a1581614832565b83529183019190830190614a02565b979650505050505050565b8015158114610ee257600080fd5b803561485281614a2f565b600060208284031215614a5a57600080fd5b813567ffffffffffffffff80821115614a7257600080fd5b908301906101608286031215614a8757600080fd5b614a8f614961565b614a9883614847565b81526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c082015260e083013560e0820152610100614aee818501614847565b908201526101208381013583811115614b0657600080fd5b614b128882870161498b565b8284015250506101409150614b28828401614a3d565b91810191909152949350505050565b600080600060608486031215614b4c57600080fd5b505081359360208301359350604090920135919050565b600080600060608486031215614b7857600080fd5b833592506020840135614b8a81614832565b929592945050506040919091013590565b60008060408385031215614bae57600080fd5b82359150602083013567ffffffffffffffff811115614bcc57600080fd5b614bd88582860161498b565b9150509250929050565b60008060408385031215614bf557600080fd5b82359150602083013561487c81614a2f565b60208152614c216020820183516001600160a01b03169052565b60006020830151614c3d60408401826001600160a01b03169052565b506040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015160e083015260e08301516101008181850152808501519150506101208181850152808501519150506101408181850152808501519150506101608181850152808501519150506101808181850152808501519150506101a08181850152808501519150506102206101c08181860152614ce86102408601846148a9565b908601519092506101e0614d06868201836001600160a01b03169052565b8601519050610200614d1b8682018315159052565b90950151151593019290925250919050565b600060208284031215614d3f57600080fd5b813561468f81614832565b6020808252600790820152664552525f562e3160c81b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201614da957614da9614d81565b5060010190565b60208082526008908201526704552525f562e31360c41b604082015260600190565b60208082526008908201526704552525f562e32360c41b604082015260600190565b600060208284031215614e0657600080fd5b5051919050565b8082018082111561425457614254614d81565b6020808252600790820152664552525f562e3560c81b604082015260600190565b60208082526023908201527f496e73756666696369656e7420616c6c6f77616e636520666f7220776974686460408201526272617760e81b606082015260800190565b600082614ea157634e487b7160e01b600052601260045260246000fd5b500490565b60005b83811015614ec1578181015183820152602001614ea9565b50506000910152565b6c021a4ab233ab73229b430b9329609d1b815260008251614ef281600d850160208701614ea6565b91909101600d0192915050565b634349565360e01b815260008251614f1e816004850160208701614ea6565b9190910160040192915050565b60008151808452614f43816020860160208601614ea6565b601f01601f19169290920160200192915050565b604081526000614f6a6040830185614f2b565b8281036020840152614f7c8185614f2b565b95945050505050565b600060208284031215614f9757600080fd5b815161468f81614832565b60006101008a835289602084015288604084015287606084015286151560808401528060a0840152614fd6818401876148a9565b6001600160a01b039590951660c0840152505060e001529695505050505050565b8181038181111561425457614254614d81565b60008161501957615019614d81565b506000190190565b60208082526008908201526708aa4a4beac5c62760c31b604082015260600190565b6020808252600890820152674552525f562e323760c01b604082015260600190565b6000604082016040835280855480835260608501915086600052602092508260002060005b828110156150af5781546001600160a01b03168452928401926001918201910161508a565b5050508381038285015261472981866148a9565b808202811582820484141761425457614254614d81565b6000602082840312156150ec57600080fd5b815160ff8116811461468f57600080fd5b600181815b8085111561513857816000190482111561511e5761511e614d81565b8085161561512b57918102915b93841c9390800290615102565b509250929050565b60008261514f57506001614254565b8161515c57506000614254565b8160018114615172576002811461517c57615198565b6001915050614254565b60ff84111561518d5761518d614d81565b50506001821b614254565b5060208310610133831016604e8410600b84101617156151bb575081810a614254565b6151c583836150fd565b80600019048211156151d9576151d9614d81565b029392505050565b600061468f60ff841683615140565b60006020828403121561520257600080fd5b815161468f81614a2f565b6000825161521f818460208701614ea6565b919091019291505056fea264697066735822122006079d06cca09e01f473475bb5d8fe3e216b97681aa2237dbaf6cbc802b51f9164736f6c63430008140033608060405234801561001057600080fd5b50604051610fe4380380610fe483398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b610f51806100936000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806372ba25701161005b57806372ba2570146100e85780637fb917e11461012a578063ddaf8d071461013d578063e1048e741461015057600080fd5b806322684db014610082578063282570f0146100a85780635272cd90146100bd575b600080fd5b610095610090366004610707565b610179565b6040519081526020015b60405180910390f35b6100bb6100b6366004610720565b6101f5565b005b6000546100d0906001600160a01b031681565b6040516001600160a01b03909116815260200161009f565b6100fb6100f636600461075a565b610264565b6040805194855260208501939093526001600160a01b039182169284019290925216606082015260800161009f565b61009561013836600461075a565b61043c565b6100bb61014b366004610720565b610655565b6100d061015e366004610707565b6001602052600090815260409020546001600160a01b031681565b6000818152600160209081526040808320548151630430a36960e11b815291516001600160a01b039091169263086146d292600480820193918290030181865afa1580156101cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101ef919061078a565b92915050565b6000828152600160205260409081902054905163180126ff60e11b8152600481018390526001600160a01b03909116906330024dfe90602401600060405180830381600087803b15801561024857600080fd5b505af115801561025c573d6000803e3d6000fd5b505050505050565b6000805460405163db089e7960e01b8152600481018590528291829182916001600160a01b03169063db089e7990602401600060405180830381865afa1580156102b2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526102da91908101906108cb565b602001516040516370a0823160e01b81526001600160a01b03878116600483015291935083918216906370a0823190602401602060405180830381865afa158015610329573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061034d919061078a565b60005460405163db089e7960e01b8152600481018a90529196506001600160a01b03169063db089e7990602401600060405180830381865afa158015610397573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103bf91908101906108cb565b516040516370a0823160e01b81526001600160a01b03888116600483015291935083918216906370a0823190602401602060405180830381865afa15801561040b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061042f919061078a565b9450505092959194509250565b60008054604051631069f3b560e01b8152600481018590526001600160a01b03848116602483015283921690631069f3b590604401602060405180830381865afa15801561048e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b29190610a13565b519050806104f25760405162461bcd60e51b815260206004820152600860248201526722a9292fab23971960c11b60448201526064015b60405180910390fd5b6000805460405163ae51accb60e01b815260048101879052602481018490526001600160a01b039091169063ae51accb9060440161018060405180830381865afa158015610544573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105689190610a55565b905060008160600151116105a95760405162461bcd60e51b81526020600482015260086024820152674552525f56472e3360c01b60448201526064016104e9565b600080546040516395ba658560e01b8152600481018890526001600160a01b03878116602483015260448201869052909116906395ba65859060640160a060405180830381865afa158015610602573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106269190610af4565b60400151905081604001518260c00151826106419190610b68565b61064b9190610b8d565b9695505050505050565b6000546001600160a01b0316331461069a5760405162461bcd60e51b81526020600482015260086024820152674552525f56472e3160c01b60448201526064016104e9565b806040516106a7906106fa565b908152602001604051809103906000f0801580156106c9573d6000803e3d6000fd5b5060009283526001602052604090922080546001600160a01b0319166001600160a01b039093169290921790915550565b61036c80610bb083390190565b60006020828403121561071957600080fd5b5035919050565b6000806040838503121561073357600080fd5b50508035926020909101359150565b6001600160a01b038116811461075757600080fd5b50565b6000806040838503121561076d57600080fd5b82359150602083013561077f81610742565b809150509250929050565b60006020828403121561079c57600080fd5b5051919050565b634e487b7160e01b600052604160045260246000fd5b604051610220810167ffffffffffffffff811182821017156107dd576107dd6107a3565b60405290565b604051610180810167ffffffffffffffff811182821017156107dd576107dd6107a3565b805161081281610742565b919050565b600082601f83011261082857600080fd5b8151602067ffffffffffffffff80831115610845576108456107a3565b8260051b604051601f19603f8301168101818110848211171561086a5761086a6107a3565b60405293845285810183019383810192508785111561088857600080fd5b83870191505b848210156108b05781516108a181610742565b8352918301919083019061088e565b979650505050505050565b8051801515811461081257600080fd5b6000602082840312156108dd57600080fd5b815167ffffffffffffffff808211156108f557600080fd5b90830190610220828603121561090a57600080fd5b6109126107b9565b61091b83610807565b815261092960208401610807565b602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152506101208084015181830152506101408084015181830152506101608084015181830152506101808084015181830152506101a080840151838111156109ba57600080fd5b6109c688828701610817565b8284015250506101c091506109dc828401610807565b828201526101e091506109f08284016108bb565b828201526102009150610a048284016108bb565b91810191909152949350505050565b600060208284031215610a2557600080fd5b6040516020810181811067ffffffffffffffff82111715610a4857610a486107a3565b6040529151825250919050565b60006101808284031215610a6857600080fd5b610a706107e3565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152506101208084015181830152506101408084015181830152506101608084015181830152508091505092915050565b600060a08284031215610b0657600080fd5b60405160a0810181811067ffffffffffffffff82111715610b2957610b296107a3565b806040525082518152602083015160208201526040830151604082015260608301516060820152610b5c608084016108bb565b60808201529392505050565b80820281158282048414176101ef57634e487b7160e01b600052601160045260246000fd5b600082610baa57634e487b7160e01b600052601260045260246000fd5b50049056fe608060405234801561001057600080fd5b5060405161036c38038061036c83398101604081905261002f9161004d565b600080546001600160a01b0319163317905542600155600255610066565b60006020828403121561005f57600080fd5b5051919050565b6102f7806100756000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c80630604061814610067578063086146d21461008357806330024dfe1461008b5780634ff0876a146100a057806378e97925146100a95780638da5cb5b146100b2575b600080fd5b61007060035481565b6040519081526020015b60405180910390f35b6100706100dd565b61009e61009936600461022d565b610182565b005b61007060025481565b61007060015481565b6000546100c5906001600160a01b031681565b6040516001600160a01b03909116815260200161007a565b60006001544210156101425760405162461bcd60e51b815260206004820152602360248201527f54696d654f7261636c653a205175657279206265666f72652073746172742074604482015262696d6560e81b60648201526084015b60405180910390fd5b600060025460015442610155919061025c565b61015f9190610275565b90506002548161016f9190610297565b60015461017c91906102ae565b91505090565b6000546001600160a01b031633146101f45760405162461bcd60e51b815260206004820152602f60248201527f54696d654f7261636c653a204f6e6c79206f776e65722063616e206368616e6760448201526e329032b837b1b4223ab930ba34b7b760891b6064820152608401610139565b600254600154610204904261025c565b61020e9190610275565b6003600082825461021f91906102ae565b909155505042600155600255565b60006020828403121561023f57600080fd5b5035919050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561026f5761026f610246565b92915050565b60008261029257634e487b7160e01b600052601260045260246000fd5b500490565b808202811582820484141761026f5761026f610246565b8082018082111561026f5761026f61024656fea2646970667358221220f95a039b49b33af1b4c3790e1d9cad8ff69366674788fc08a3b96700377233f264736f6c63430008140033a26469706673582212201b1bd45d6c7a853952571e43c585f26b170267e0b1b8ce1f55fe926a41a7137b64736f6c63430008140033608060405234801561001057600080fd5b50610f91806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063766f97bb14610030575b600080fd5b61004361003e366004610152565b61005f565b6040516001600160a01b03909116815260200160405180910390f35b600080838333604051610071906100a2565b61007d939291906101fc565b604051809103906000f080158015610099573d6000803e3d6000fd5b50949350505050565b610d218061023b83390190565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126100d657600080fd5b813567ffffffffffffffff808211156100f1576100f16100af565b604051601f8301601f19908116603f01168101908282118183101715610119576101196100af565b8160405283815286602085880101111561013257600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806040838503121561016557600080fd5b823567ffffffffffffffff8082111561017d57600080fd5b610189868387016100c5565b9350602085013591508082111561019f57600080fd5b506101ac858286016100c5565b9150509250929050565b6000815180845260005b818110156101dc576020818501810151868301820152016101c0565b506000602082860101526020601f19601f83011685010191505092915050565b60608152600061020f60608301866101b6565b828103602084015261022181866101b6565b91505060018060a01b038316604083015294935050505056fe60806040523480156200001157600080fd5b5060405162000d2138038062000d2183398101604081905262000034916200019f565b82826003620000448382620002bb565b506004620000538282620002bb565b505050620000706200006a6200008460201b60201c565b62000088565b6200007b8162000088565b50505062000387565b3390565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200010257600080fd5b81516001600160401b03808211156200011f576200011f620000da565b604051601f8301601f19908116603f011681019082821181831017156200014a576200014a620000da565b816040528381526020925086838588010111156200016757600080fd5b600091505b838210156200018b57858201830151818301840152908201906200016c565b600093810190920192909252949350505050565b600080600060608486031215620001b557600080fd5b83516001600160401b0380821115620001cd57600080fd5b620001db87838801620000f0565b94506020860151915080821115620001f257600080fd5b506200020186828701620000f0565b604086015190935090506001600160a01b03811681146200022157600080fd5b809150509250925092565b600181811c908216806200024157607f821691505b6020821081036200026257634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620002b657600081815260208120601f850160051c81016020861015620002915750805b601f850160051c820191505b81811015620002b2578281556001016200029d565b5050505b505050565b81516001600160401b03811115620002d757620002d7620000da565b620002ef81620002e884546200022c565b8462000268565b602080601f8311600181146200032757600084156200030e5750858301515b600019600386901b1c1916600185901b178555620002b2565b600085815260208120601f198616915b82811015620003585788860151825594840194600190910190840162000337565b5085821015620003775787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b61098a80620003976000396000f3fe608060405234801561001057600080fd5b50600436106100cf5760003560e01c806370a082311161008c578063a0712d6811610066578063a0712d68146101a8578063a9059cbb146101bb578063dd62ed3e146101ce578063f2fde38b1461020757600080fd5b806370a082311461015c5780638da5cb5b1461018557806395d89b41146101a057600080fd5b806306fdde03146100d4578063095ea7b3146100f257806318160ddd1461011557806323b872dd14610127578063313ce5671461013a57806342966c6814610149575b600080fd5b6100dc61021c565b6040516100e991906107c0565b60405180910390f35b610105610100366004610825565b6102ae565b60405190151581526020016100e9565b6002545b6040519081526020016100e9565b61010561013536600461084f565b6102c8565b604051601281526020016100e9565b61010561015736600461088b565b6102ec565b61011961016a3660046108a4565b6001600160a01b031660009081526020819052604090205490565b6005546040516001600160a01b0390911681526020016100e9565b6100dc610300565b6101056101b636600461088b565b61030f565b6101056101c9366004610825565b610328565b6101196101dc3660046108c6565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b61021a6102153660046108a4565b610336565b005b60606003805461022b906108f9565b80601f0160208091040260200160405190810160405280929190818152602001828054610257906108f9565b80156102a45780601f10610279576101008083540402835291602001916102a4565b820191906000526020600020905b81548152906001019060200180831161028757829003601f168201915b5050505050905090565b6000336102bc8185856103b4565b60019150505b92915050565b6000336102d68582856103c6565b6102e1858585610444565b506001949350505050565b60006102f833836104a3565b506001919050565b60606004805461022b906108f9565b60006103196104dd565b6102f83383610539565b919050565b6000336102bc818585610444565b61033e6104dd565b6001600160a01b0381166103a85760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b6103b18161056f565b50565b6103c183838360016105c1565b505050565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461043e578181101561042f57604051637dc7a0d960e11b81526001600160a01b0384166004820152602481018290526044810183905260640161039f565b61043e848484840360006105c1565b50505050565b6001600160a01b03831661046e57604051634b637e8f60e11b81526000600482015260240161039f565b6001600160a01b0382166104985760405163ec442f0560e01b81526000600482015260240161039f565b6103c1838383610696565b6001600160a01b0382166104cd57604051634b637e8f60e11b81526000600482015260240161039f565b6104d982600083610696565b5050565b6005546001600160a01b031633146105375760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161039f565b565b6001600160a01b0382166105635760405163ec442f0560e01b81526000600482015260240161039f565b6104d960008383610696565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b0384166105eb5760405163e602df0560e01b81526000600482015260240161039f565b6001600160a01b03831661061557604051634a1406b160e11b81526000600482015260240161039f565b6001600160a01b038085166000908152600160209081526040808320938716835292905220829055801561043e57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161068891815260200190565b60405180910390a350505050565b6001600160a01b0383166106c15780600260008282546106b69190610933565b909155506107339050565b6001600160a01b038316600090815260208190526040902054818110156107145760405163391434e360e21b81526001600160a01b0385166004820152602481018290526044810183905260640161039f565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b03821661074f5760028054829003905561076e565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516107b391815260200190565b60405180910390a3505050565b600060208083528351808285015260005b818110156107ed578581018301518582016040015282016107d1565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461032357600080fd5b6000806040838503121561083857600080fd5b6108418361080e565b946020939093013593505050565b60008060006060848603121561086457600080fd5b61086d8461080e565b925061087b6020850161080e565b9150604084013590509250925092565b60006020828403121561089d57600080fd5b5035919050565b6000602082840312156108b657600080fd5b6108bf8261080e565b9392505050565b600080604083850312156108d957600080fd5b6108e28361080e565b91506108f06020840161080e565b90509250929050565b600181811c9082168061090d57607f821691505b60208210810361092d57634e487b7160e01b600052602260045260246000fd5b50919050565b808201808211156102c257634e487b7160e01b600052601160045260246000fdfea2646970667358221220bf237d2208ab261fdd43d7798cd306b007efde81be3f481b5eadd332dc2b55bb64736f6c63430008140033a2646970667358221220a3abc090003190af76aa3f1619914641d73175b693ac99f25699ecf94163583d64736f6c63430008140033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102065760003560e01c80638da5cb5b1161011a578063b0b1f9e9116100ad578063daff97b51161007c578063daff97b51461051d578063db089e7914610530578063e2bbb15814610550578063f2fde38b14610563578063f4f3b2001461057657600080fd5b8063b0b1f9e9146104db578063c602e48e146104ee578063c6a1c131146104f7578063d88083061461050a57600080fd5b806398f3fc57116100e957806398f3fc57146104115780639f01f7ba14610424578063abdbbc5414610437578063ae51accb1461044057600080fd5b80638da5cb5b1461038d57806395ba65851461039e57806395e911a8146103f557806396bda284146103fe57600080fd5b80635408eec91161019d57806377278def1161016c57806377278def146103385780637fae6d4d1461034b578063813e62411461035e5780638550c9081461037157806386b2f7391461038457600080fd5b80635408eec9146102de5780635b65b9ab146102f15780635d7035c61461030457806372fada5c1461032557600080fd5b8063441a3e70116101d9578063441a3e701461029257806345a7f794146102a55780634aad6b22146102b85780634fc37bf7146102cb57600080fd5b80631069f3b51461020b5780631aa277a514610232578063282570f01461025d578063404f4e0d14610272575b600080fd5b61021e610219366004614857565b610589565b604051905181526020015b60405180910390f35b600454610245906001600160a01b031681565b6040516001600160a01b039091168152602001610229565b61027061026b366004614887565b6105f8565b005b610285610280366004614887565b6106e9565b60405161022991906148ed565b6102706102a0366004614887565b610803565b6102706102b3366004614900565b610b6b565b6102706102c6366004614932565b610c2b565b6102706102d9366004614a48565b610ee5565b6102706102ec366004614887565b61137f565b6102706102ff366004614b37565b61144a565b610317610312366004614932565b6114f4565b604051908152602001610229565b610270610333366004614932565b611568565b610270610346366004614932565b6118c6565b610270610359366004614932565b611931565b61027061036c366004614857565b6119c9565b61027061037f366004614887565b611ac2565b6103176103e881565b6000546001600160a01b0316610245565b6103b16103ac366004614b63565b611de1565b6040516102299190600060a0820190508251825260208301516020830152604083015160408301526060830151606083015260808301511515608083015292915050565b61031761271081565b61027061040c366004614932565b611ea6565b600554610245906001600160a01b031681565b610270610432366004614932565b611f5b565b61031760025481565b61045361044e366004614887565b61214f565b6040516102299190815181526020808301519082015260408083015190820152606080830151908201526080808301519082015260a0808301519082015260c0808301519082015260e0808301519082015261010080830151908201526101208083015190820152610140808301519082015261016091820151918101919091526101800190565b6102706104e9366004614932565b6122a3565b61031760035481565b610270610505366004614b9b565b61253e565b610270610518366004614932565b6126e9565b61027061052b366004614be2565b612877565b61054361053e366004614932565b61290c565b6040516102299190614c07565b61027061055e366004614887565b612b19565b610270610571366004614d2d565b612fd1565b610270610584366004614d2d565b613047565b6040805160208101909152600081528280600354116105c35760405162461bcd60e51b81526004016105ba90614d4a565b60405180910390fd5b505060009182526008602090815260408084206001600160a01b03909316845291815291819020815192830190915254815290565b610600613140565b8180600354116106225760405162461bcd60e51b81526004016105ba90614d4a565b60008381526006602090815260409182902060070154825190815290810184905284917fc8cebf046c3a3799bf1206980180c44983f298221ad03c0fe827ab5c5c0a398c910160405180910390a260048054604051630282570f60e41b8152918201859052602482018490526001600160a01b03169063282570f090604401600060405180830381600087803b1580156106bb57600080fd5b505af11580156106cf573d6000803e3d6000fd5b505050600093845250506006602052604090912060070155565b606082806003541161070d5760405162461bcd60e51b81526004016105ba90614d4a565b600084815260076020908152604080832086845290915290205467ffffffffffffffff81111561073f5761073f61494b565b604051908082528060200260200182016040528015610768578160200160208202803683370190505b50915060005b60008581526007602090815260408083208784529091529020548110156107fb576000858152600b60209081526040808320878452825280832084845290915290205483516001600160a01b03909116908490839081106107d1576107d1614d6b565b6001600160a01b0390921660209283029190910190910152806107f381614d97565b91505061076e565b505092915050565b61080b61319c565b81806003541161082d5760405162461bcd60e51b81526004016105ba90614d4a565b600083815260096020526040902054839061085a5760405162461bcd60e51b81526004016105ba90614db0565b6000831161087a5760405162461bcd60e51b81526004016105ba90614dd2565b6000848152600660205260408120600101546001600160a01b03166370a08231336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156108df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109039190614df4565b9050838110156109405760405162461bcd60e51b81526020600482015260086024820152674552525f562e323560c01b60448201526064016105ba565b600061094b866114f4565b6000878152600760208181526040808420858552909152909120600b81015491015491925061097991614e0d565b4211156109985760405162461bcd60e51b81526004016105ba90614e20565b6000868152600a602090815260408083203380855290835281842085855283528184208a85526008845282852091855292529091208054158015906109df57506002820154155b156109f25780546109f2908990336131c6565b600088815260076020908152604080832086845290915281206002018054899290610a1e908490614e0d565b9250508190555086826002016000828254610a399190614e0d565b909155505080548314610a4a578281555b60008881526006602052604090206001015487906001600160a01b031663dd62ed3e336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152306024820152604401602060405180830381865afa158015610ab7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610adb9190614df4565b1015610af95760405162461bcd60e51b81526004016105ba90614e41565b610b203360008a8152600660205260409020600101546001600160a01b031690308a613328565b604051878152889033907ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568906020015b60405180910390a3505050505050610b6760018055565b5050565b610b73613140565b838060035411610b955760405162461bcd60e51b81526004016105ba90614d4a565b60008581526006602081815260409283902060048101549281015460059091015484519384529183018890528284015260608201869052608082015260a08101849052905186917fe688636a9e39148b1edbd6665d6d4a56d8629d5047d44472c5d06bca67f90a4f919081900360c00190a250600093845260066020819052604090942060048101939093559282015560050155565b610c3361319c565b610c3b613140565b808060035411610c5d5760405162461bcd60e51b81526004016105ba90614d4a565b600082815260066020526040902060088101546009820154610c7f9190614e0d565b421015610cb95760405162461bcd60e51b81526020600482015260086024820152674552525f562e333160c01b60448201526064016105ba565b426009820155600c81015480610cfc5760405162461bcd60e51b81526020600482015260086024820152674552525f562e313560c01b60448201526064016105ba565b81546040516370a0823160e01b815230600482015282916001600160a01b0316906370a0823190602401602060405180830381865afa158015610d43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d679190614df4565b1015610da05760405162461bcd60e51b81526020600482015260086024820152674552525f562e313960c01b60448201526064016105ba565b6000600c8301819055600d830180548290610dbd57610dbd614d6b565b6000918252602082200154600d850180546001600160a01b039092169350906001908110610ded57610ded614d6b565b6000918252602090912001546001600160a01b03169050857f783ad58a3e77de8f48cbc5b7b85e67eda94545ca6ac909671c4d4461eedecc5683610e32600287614e84565b604080516001600160a01b03909316835260208301919091520160405180910390a2857f783ad58a3e77de8f48cbc5b7b85e67eda94545ca6ac909671c4d4461eedecc5682610e82600287614e84565b604080516001600160a01b03909316835260208301919091520160405180910390a2610ec582610eb3600286614e84565b86546001600160a01b0316919061338f565b610ed481610eb3600286614e84565b5050505050610ee260018055565b50565b610eed61319c565b610ef5613140565b80610120015151600214610f355760405162461bcd60e51b81526020600482015260076024820152664552525f562e3360c81b60448201526064016105ba565b60006001600160a01b0316816101200151600081518110610f5857610f58614d6b565b60200260200101516001600160a01b031603610fa05760405162461bcd60e51b815260206004820152600760248201526622a9292fab171b60c91b60448201526064016105ba565b60006001600160a01b0316816101200151600181518110610fc357610fc3614d6b565b60200260200101516001600160a01b03160361100b5760405162461bcd60e51b81526020600482015260076024820152664552525f562e3760c81b60448201526064016105ba565b6003546000611019826133c5565b6040516020016110299190614eca565b60405160208183030381529060405290506000611045836133c5565b6040516020016110559190614eff565b60408051601f198184030181529082905260055463766f97bb60e01b83529092506000916001600160a01b039091169063766f97bb9061109b9086908690600401614f57565b6020604051808303816000875af11580156110ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110de9190614f85565b905060405180610220016040528086600001516001600160a01b03168152602001826001600160a01b03168152602001866080015181526020018660a0015181526020018660200151815260200186604001518152602001866060015181526020018660c0015181526020018660e0015181526020016000815260200160008152602001600081526020016000815260200186610120015181526020018661010001516001600160a01b0316815260200160001515815260200186610140015115158152506006600086815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020155606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e082015181600701556101008201518160080155610120820151816009015561014082015181600a015561016082015181600b015561018082015181600c01556101a082015181600d0190805190602001906112a09291906147b8565b506101c0820151600e90910180546101e0840151610200909401511515600160a81b0260ff60a81b19941515600160a01b026001600160a81b03199092166001600160a01b039094169390931717929092161790556003805490600061130583614d97565b9190505550837f573c012a6af537230a7dfab893cb56e84491db0e7f7805b1553a2b8ca07a354f86608001518760a00151886020015189606001518a61014001518b61012001518c600001518d60e0015160405161136a989796959493929190614fa2565b60405180910390a250505050610ee260018055565b611387613140565b8180600354116113a95760405162461bcd60e51b81526004016105ba90614d4a565b60008381526006602090815260409182902060030154825190815290810184905284917f86d68dfe175a935769121f8412fc74b00e6ca0834585dc31e5562b0709f9c6c3910160405180910390a26103e88211156114345760405162461bcd60e51b81526020600482015260086024820152674552525f562e333360c01b60448201526064016105ba565b5060009182526006602052604090912060030155565b611452613140565b8280600354116114745760405162461bcd60e51b81526004016105ba90614d4a565b60008481526006602090815260409182902060028101546008909101548351918252918101869052918201526060810183905284907f7eb9abb1c530a0c5c9e6a557c080514b3d38d59cb1d2a0fc085405d67f991a029060800160405180910390a250600092835260066020526040909220600281019190915560080155565b60008180600354116115185760405162461bcd60e51b81526004016105ba90614d4a565b60008381526009602052604090205483906115455760405162461bcd60e51b81526004016105ba90614db0565b60008481526009602052604090205461156090600190614ff7565b949350505050565b61157061319c565b8080600354116115925760405162461bcd60e51b81526004016105ba90614d4a565b60008281526009602052604090205482906115bf5760405162461bcd60e51b81526004016105ba90614db0565b6000838152600660205260408120906115d7856114f4565b6000868152600760208181526040808420858552909152909120600b81015491810154929350916116089190614e0d565b42106116415760405162461bcd60e51b815260206004820152600860248201526711549497d58b8c8d60c21b60448201526064016105ba565b6000868152600a6020908152604080832033845282528083208584529091528120600181015481549192916116769190614e0d565b9050600081116116985760405162461bcd60e51b81526004016105ba90614dd2565b81546001840180546000906116ae908490614ff7565b909155505060018201546009840180546000906116cc908490614ff7565b909155505060008083556001808401919091558354111561180b576000888152600b6020908152604080832087845290915281208454829061171090600190614ff7565b815260208101919091526040016000205484546001600160a01b03909116915061173c90600190614ff7565b8360030154146117aa576000898152600b60209081526040808320888452825280832060038781018054865291845282852080546001600160a01b0319166001600160a01b03881690811790915591548e8652600a85528386209286529184528285208a8652909352922001555b6000898152600b602090815260408083208884529091528120855482906117d390600190614ff7565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055505061183b565b6000888152600b602090815260408083208784528252808320838052909152902080546001600160a01b03191690555b60006003830181905560048301805460ff19169055835490849061185e8361500a565b919050555061187f61186d3390565b86546001600160a01b0316908361338f565b604051818152889033907f4d03849d7cc7e6aa8812404182c6821dda42bc4a15e60a2055dee048bff349109060200160405180910390a350505050505050610ee260018055565b6118ce613140565b6000811180156118e057506002548114155b61192c5760405162461bcd60e51b815260206004820152601760248201527f496e76616c6964206e756d626572206f6620757365727300000000000000000060448201526064016105ba565b600255565b61193961319c565b80806003541161195b5760405162461bcd60e51b81526004016105ba90614d4a565b6000828152600860209081526040808320338452909152902080546119ad5760405162461bcd60e51b81526020600482015260086024820152674552525f562e313160c01b60448201526064016105ba565b80546119bb908490336131c6565b6000905550610ee260018055565b6119d1613140565b8180600354116119f35760405162461bcd60e51b81526004016105ba90614d4a565b6001600160a01b038216611a345760405162461bcd60e51b815260206004820152600860248201526711549497d58b8c4d60c21b60448201526064016105ba565b600083815260066020908152604091829020600e015482516001600160a01b0391821681529085169181019190915284917f94c8b3c75ba308c40e2ac10a7a6e9e5ff5c689a07cddb9a6b7a1227682e47283910160405180910390a250600091825260066020526040909120600e0180546001600160a01b0319166001600160a01b03909216919091179055565b611aca61319c565b611ad2613140565b818060035411611af45760405162461bcd60e51b81526004016105ba90614d4a565b6000838152600660205260409020600e8101546001600160a01b0316611b475760405162461bcd60e51b815260206004820152600860248201526711549497d58b8c4d60c21b60448201526064016105ba565b80600a0154600003611cb65760008481526007602090815260408083208380529091529020600381015415611c6e578054600a82015414611bb55760405162461bcd60e51b815260206004820152600860248201526722a9292fab17189b60c11b60448201526064016105ba565b600085815260096020526040902054600110611bfe5760405162461bcd60e51b81526020600482015260086024820152674552525f562e313760c01b60448201526064016105ba565b600a82018054906000611c1083614d97565b9091555050600085815260076020818152604080842060018552909152909120600b810154918101549091611c4491614e0d565b421015611c635760405162461bcd60e51b81526004016105ba90615021565b600301849055611cb0565b80600b01548160070154611c829190614e0d565b421015611ca15760405162461bcd60e51b81526004016105ba90615021565b600b8201849055600381018490555b50611dcc565b6000848152600760209081526040808320600a8581015485529252909120805491015414611d115760405162461bcd60e51b815260206004820152600860248201526722a9292fab17189b60c11b60448201526064016105ba565b600a81018054906000611d2383614d97565b9091555050600a81015460008581526009602052604090205411611d745760405162461bcd60e51b81526020600482015260086024820152674552525f562e313760c01b60448201526064016105ba565b6000848152600760208181526040808420600a8601548552909152909120600b810154918101549091611da691614e0d565b421015611dc55760405162461bcd60e51b81526004016105ba90615021565b6003018390555b611dd68484613458565b5050610b6760018055565b611e156040518060a00160405280600081526020016000815260200160008152602001600081526020016000151581525090565b838060035411611e375760405162461bcd60e51b81526004016105ba90614d4a565b50506000928352600a602090815260408085206001600160a01b03909416855292815282842091845290815291819020815160a08101835281548152600182015493810193909352600281015491830191909152600381015460608301526004015460ff161515608082015290565b611eae613140565b808060035411611ed05760405162461bcd60e51b81526004016105ba90614d4a565b6000828152600660205260409020600e0154600160a01b900460ff1615611f235760405162461bcd60e51b81526020600482015260076024820152664552525f562e3960c81b60448201526064016105ba565b611f2c82613cb9565b60405182907f74e534d9ee9babee8812594b75f57a2889a16ee1b6c28c2de11b5afffad02b8390600090a25050565b611f6361319c565b808060035411611f855760405162461bcd60e51b81526004016105ba90614d4a565b6000828152600960205260409020548290611fb25760405162461bcd60e51b81526004016105ba90614db0565b600083815260066020526040812090611fca856114f4565b6000868152600760208181526040808420858552909152909120600b8101549181015492935091611ffb9190614e0d565b42106120345760405162461bcd60e51b815260206004820152600860248201526711549497d58b8c8d60c21b60448201526064016105ba565b6000868152600a602090815260408083203380855290835281842086855283528184208a855260088452828520918552925290912060028201548061208b5760405162461bcd60e51b81526004016105ba90614dd2565b600060028085018290559083558401548111156120d55760405162461bcd60e51b815260206004820152600860248201526722a9292fab17191b60c11b60448201526064016105ba565b808460020160008282546120e99190614ff7565b90915550506001860154612107906001600160a01b0316338361338f565b604051818152899033907f39e2e01794006bc1f63835af5c05db790beca4bfb40de3f02cc3ddf22dccc0fb9060200160405180910390a35050505050505050610ee260018055565b6121b36040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8280600354116121d55760405162461bcd60e51b81526004016105ba90614d4a565b60008481526009602052604090205484906122025760405162461bcd60e51b81526004016105ba90614db0565b505050600091825260076020818152604080852093855292815292829020825161018081018452815481526001820154948101949094526002810154928401929092526003820154606084015260048201546080840152600582015460a0840152600682015460c084015281015460e083015260088101546101008301526009810154610120830152600a810154610140830152600b015461016082015290565b6122ab61319c565b6122b3613140565b8080600354116122d55760405162461bcd60e51b81526004016105ba90614d4a565b600082815260066020818152604080842081516102208101835281546001600160a01b039081168252600183015416818501526002820154818401526003820154606082015260048201546080820152600582015460a08201529381015460c0850152600781015460e085015260088101546101008501526009810154610120850152600a810154610140850152600b810154610160850152600c810154610180850152600d8101805483518186028101860190945280845291936101a086019392908301828280156123d157602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116123b3575b5050509183525050600e91909101546001600160a01b03811660208084019190915260ff600160a01b830481161515604080860191909152600160a81b909304161515606093840152600087815260078083528382206101408088015184529084529184902084516101808101865281548152600182015494810194909452600281015494840194909452600384015494830185905260048401546080840152600584015460a0840152600684015460c084015283015460e083015260088301546101008301526009830154610120830152600a83015490820152600b909101546101608201529192506124d75760405162461bcd60e51b81526004016105ba90615043565b610140810151156125155760405162461bcd60e51b815260206004820152600860248201526708aa4a4beac5c64760c31b60448201526064016105ba565b8060200151600003612528575050612534565b61253184613eb7565b50505b50610ee260018055565b612546613140565b8180600354116125685760405162461bcd60e51b81526004016105ba90614d4a565b81516002146125a35760405162461bcd60e51b81526020600482015260076024820152664552525f562e3360c81b60448201526064016105ba565b60006001600160a01b0316826000815181106125c1576125c1614d6b565b60200260200101516001600160a01b0316036126095760405162461bcd60e51b815260206004820152600760248201526622a9292fab171b60c91b60448201526064016105ba565b60006001600160a01b03168260018151811061262757612627614d6b565b60200260200101516001600160a01b03160361266f5760405162461bcd60e51b81526020600482015260076024820152664552525f562e3760c81b60448201526064016105ba565b827f13f42559ce1a76db34ac3c5b7141308c1e286e00168793042025be3e556bfc5560066000868152602001908152602001600020600d01846040516126b6929190615065565b60405180910390a2600083815260066020908152604090912083516126e392600d909201918501906147b8565b50505050565b6126f161319c565b6126f9613140565b80806003541161271b5760405162461bcd60e51b81526004016105ba90614d4a565b60008281526007602081815260408084206006808452828620600a908101548752918452948290208251610180810184528154815260018201549481019490945260028101549284019290925260038201546060840181905260048301546080850152600583015460a08501529482015460c08401529281015460e08301526008810154610100830152600981015461012083015291820154610140820152600b90910154610160820152906127e35760405162461bcd60e51b81526004016105ba90615043565b8061014001516000036128235760405162461bcd60e51b81526020600482015260086024820152674552525f562e323960c01b60448201526064016105ba565b8051610140820151106128635760405162461bcd60e51b815260206004820152600860248201526704552525f562e33360c41b60448201526064016105ba565b61286c83613eb7565b5050610ee260018055565b61287f613140565b8180600354116128a15760405162461bcd60e51b81526004016105ba90614d4a565b827f77f1fcfcce67dc392d64f842056d2ec06c80986c47c910f7e79c5b23a2738d74836040516128d5911515815260200190565b60405180910390a250600091825260066020526040909120600e018054911515600160a81b0260ff60a81b19909216919091179055565b6129b260405180610220016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016060815260200160006001600160a01b031681526020016000151581526020016000151581525090565b8180600354116129d45760405162461bcd60e51b81526004016105ba90614d4a565b60008381526006602081815260409283902083516102208101855281546001600160a01b039081168252600183015416818401526002820154818601526003820154606082015260048201546080820152600582015460a08201529281015460c0840152600781015460e084015260088101546101008401526009810154610120840152600a810154610140840152600b810154610160840152600c810154610180840152600d81018054855181850281018501909652808652939491936101a0860193830182828015612ad157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612ab3575b5050509183525050600e91909101546001600160a01b038116602083015260ff600160a01b8204811615156040840152600160a81b9091041615156060909101529392505050565b612b2161319c565b818060035411612b435760405162461bcd60e51b81526004016105ba90614d4a565b6000838152600660205260409020600e0154600160a81b900460ff1615612b965760405162461bcd60e51b815260206004820152600760248201526622a9292fab171960c91b60448201526064016105ba565b6000838152600660208190526040909120908101548311612bc95760405162461bcd60e51b81526004016105ba90614dd2565b805483906001600160a01b03166370a08231336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015612c20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c449190614df4565b1015612c7d5760405162461bcd60e51b81526020600482015260086024820152674552525f562e323160c01b60448201526064016105ba565b6000612c88856114f4565b6000868152600760208181526040808420858552909152909120600b8101549181015492935091612cb99190614e0d565b421115612cd85760405162461bcd60e51b81526004016105ba90614e20565b6000868152600a602090815260408083203384528252808320858452909152902060048401546001830154612d0e908890614e0d565b1115612d475760405162461bcd60e51b81526020600482015260086024820152674552525f562e323360c01b60448201526064016105ba565b600481015460ff16612dfa5760058401548254612d65906001614e0d565b1115612d9e5760405162461bcd60e51b815260206004820152600860248201526722a9292fab17191960c11b60448201526064016105ba565b6000878152600b60209081526040808320868452825280832085548452909152812080546001600160a01b03191633179055825460038301819055908390612de583614d97565b909155505060048101805460ff191660011790555b600080856003015411612e0e576000612e2b565b612710856003015488612e2191906150c3565b612e2b9190614e84565b905080836009016000828254612e419190614e0d565b90915550612e5190508188614ff7565b836001016000828254612e649190614e0d565b9091555050845487906001600160a01b031663dd62ed3e336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152306024820152604401602060405180830381865afa158015612ec6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eea9190614df4565b1015612f435760405162461bcd60e51b815260206004820152602260248201527f496e73756666696369656e7420616c6c6f77616e636520666f72206465706f736044820152611a5d60f21b60648201526084016105ba565b612f5a3386546001600160a01b031690308a613328565b612f648188614ff7565b826000016000828254612f779190614e0d565b9250508190555080826001016000828254612f929190614e0d565b90915550506040805130815260208101899052899133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79101610b50565b612fd9613140565b6001600160a01b03811661303e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016105ba565b610ee2816140ce565b61304f613140565b60005b6003548110156130c2576000818152600660205260409020546001600160a01b038084169116036130b05760405162461bcd60e51b815260206004820152600860248201526722a9292fab17199960c11b60448201526064016105ba565b806130ba81614d97565b915050613052565b50610ee2336040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa15801561310b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061312f9190614df4565b6001600160a01b038416919061338f565b6000546001600160a01b0316331461319a5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016105ba565b565b6002600154036131bf57604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b6000838152600760209081526040808320858452909152902060038101546132005760405162461bcd60e51b81526004016105ba90615043565b6000848152600a602090815260408083206001600160a01b038616845282528083208684529091528120600201549081156132b3576000836002015484600601548461324c91906150c3565b6132569190614e84565b90506132628183614e0d565b604080516001600160a01b03881681526020810189905290810183905290925087907f25d1255c398e637e957d1d04691db7d1b318bc88e20deda96f2c8714f8297e389060600160405180910390a2505b80156132dc576000868152600660205260409020546132dc906001600160a01b0316858361338f565b836001600160a01b0316867ffd8c040503c4beb434790f3a2e972438d06ece0dc342f58052f2f9fd385828778360405161331891815260200190565b60405180910390a3505050505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526126e39186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061411e565b6040516001600160a01b038381166024830152604482018390526133c091859182169063a9059cbb9060640161335d565b505050565b606060006133d283614181565b600101905060008167ffffffffffffffff8111156133f2576133f261494b565b6040519080825280601f01601f19166020018201604052801561341c576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461342657509392505050565b600081116134935760405162461bcd60e51b81526020600482015260086024820152674552525f562e313560c01b60448201526064016105ba565b6000806134a0848461425a565b60008681526007602081815260408084206006808452828620600a8101548088529285528387208d885282865284516102208101865282546001600160a01b039081168252600184015416818801526002830154818701526003830154606082015260048301546080820152600583015460a08201529282015460c08401529581015460e083015260088101546101008301526009810154610120830152610140820192909252600b820154610160820152600c820154610180820152600d82018054845181870281018701909552808552979a5097985093969495939490936101a08601938301828280156135bf57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116135a1575b5050509183525050600e91909101546001600160a01b0380821660208085019190915260ff600160a01b840481161515604080870191909152600160a81b909404161515606090940193909352600480870188905584840151835163313ce56760e01b8152935195965060009592169363313ce56793808301939192908290030181865afa158015613655573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061367991906150da565b61368490600a6151e1565b905060008482856001015461369991906150c3565b6136a39190614e84565b90506000828560020154876136b891906150c3565b6136c29190614e84565b600586018390556006860181905560028601549091508211156137aa5760008560020154836136f19190614ff7565b9050600085602001516001600160a01b031663a0712d68836040518263ffffffff1660e01b815260040161372791815260200190565b6020604051808303816000875af1158015613746573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061376a91906151f0565b9050806137a35760405162461bcd60e51b815260206004820152600760248201526608aa4a4beac5c760cb1b60448201526064016105ba565b5050613873565b60008286600201546137bc9190614ff7565b90508015613871576020850151604051630852cd8d60e31b8152600481018390526000916001600160a01b0316906342966c68906024016020604051808303816000875af1158015613812573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061383691906151f0565b90508061386f5760405162461bcd60e51b815260206004820152600760248201526611549497d58b8d60ca1b60448201526064016105ba565b505b505b61387d8782614e0d565b8560010154106139c25760008782876001015461389a9190614ff7565b6138a49190614ff7565b905080156139bc5784516040516370a0823160e01b815230600482015282916001600160a01b0316906370a0823190602401602060405180830381865afa1580156138f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139179190614df4565b10156139505760405162461bcd60e51b815260206004820152600860248201526722a9292fab17189960c11b60448201526064016105ba565b6101c0850151855161396e916001600160a01b03909116908361338f565b6101c0850151604080516001600160a01b039092168252602082018390528b9130917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a35b50613b49565b60018501546000906139d48984614e0d565b6139de9190614ff7565b85516101c08701516040516370a0823160e01b81526001600160a01b03918216600482015292935083929116906370a0823190602401602060405180830381865afa158015613a31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a559190614df4565b1015613a8e5760405162461bcd60e51b81526020600482015260086024820152674552525f562e313360c01b60448201526064016105ba565b84516101c0860151604051636eb1769f60e11b81526001600160a01b0391821660048201523060248201528392919091169063dd62ed3e90604401602060405180830381865afa158015613ae6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b0a9190614df4565b1015613b285760405162461bcd60e51b81526004016105ba90614e41565b6101c08501518551613b47916001600160a01b03909116903084613328565b505b600985015415613c5d57600985015484516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015613b9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bc39190614df4565b1015613bfc5760405162461bcd60e51b81526020600482015260086024820152674552525f562e313960c01b60448201526064016105ba565b613c42846101a00151600081518110613c1757613c17614d6b565b602002602001015160028760090154613c309190614e84565b86516001600160a01b0316919061338f565b613c5d846101a00151600181518110613c1757613c17614d6b565b613c6689614433565b61014084015160408051918252602082018a9052810187905289907f91f529843346f3c2fdcba5e8826d1a7f15ac1d049167589ccc580e07ab7e3a3f9060600160405180910390a2505050505050505050565b60008181526006602052604090819020600e8101805460ff60a01b1916600160a01b17905560048054600790920154925163ddaf8d0760e01b81526001600160a01b039092169263ddaf8d0792613d1c9286929101918252602082015260400190565b600060405180830381600087803b158015613d3657600080fd5b505af1158015613d4a573d6000803e3d6000fd5b5050505060405180610180016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001428152602001600660008481526020019081526020016000206003015481526020016000815260200160008152602001600660008481526020019081526020016000206007015481525060076000838152602001908152602001600020600060096000858152602001908152602001600020548152602001908152602001600020600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e082015181600701556101008201518160080155610120820151816009015561014082015181600a015561016082015181600b0155905050600960008281526020019081526020016000206000815480929190613eaf90614d97565b919050555050565b6000818152600660209081526040808320600a0154600783528184208185529092528220600581015460025492939192909190613ef5578254613f07565b82600a0154600254613f079190614e0d565b8354909150811115613f17575081545b600a830154805b828110156140be576000878152600b6020908152604080832089845282528083208484528252808320548a8452600a83528184206001600160a01b039091168085529083528184208a8552909252822054600188015491929091613f8283896150c3565b613f8c9190614e84565b9050801561409b5760008a815260066020526040908190206001015490516370a0823160e01b815230600482015282916001600160a01b0316906370a0823190602401602060405180830381865afa158015613fec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140109190614df4565b101561402e5760405162461bcd60e51b81526004016105ba90614db0565b60008a815260066020526040902060010154614054906001600160a01b0316848361338f565b604080518a8152602081018390526001600160a01b038516918c917f87dc6b8c12f17ba1cf9231ef9295a05eacf04b5c913d5488cdae799fc8e95081910160405180910390a35b6140a6846001614e0d565b945050505080806140b690614d97565b915050613f1e565b50600a9093019290925550505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006141336001600160a01b03841683614681565b9050805160001415801561415857508080602001905181019061415691906151f0565b155b156133c057604051635274afe760e01b81526001600160a01b03841660048201526024016105ba565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106141c05772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef810000000083106141ec576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061420a57662386f26fc10000830492506010015b6305f5e1008310614222576305f5e100830492506008015b612710831061423657612710830492506004015b60648310614248576064830492506002015b600a8310614254576001015b92915050565b60008281526006602090815260408083206001810154825163313ce56760e01b815292518594929385936001600160a01b039093169263313ce567926004808401938290030181865afa1580156142b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142d991906150da565b6142e490600a6151e1565b905060008260010160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561433d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143619190614df4565b9050600093508594508583600b0154101561442957600283015415614421578161271082856002015486600b01548a61439a9190614ff7565b6143a491906150c3565b6143ae91906150c3565b6143b89190614e84565b6143c29190614e84565b93508315614421578383600c0160008282546143de9190614e0d565b909155506000905084836143f2848a6150c3565b6143fc9190614e84565b6144069190614ff7565b90508161441384836150c3565b61441d9190614e84565b9550505b600b83018590555b5050509250929050565b60008181526009602052604090205481906144605760405162461bcd60e51b81526004016105ba90614db0565b600061446b836114f4565b6000848152600760208181526040808420858552909152909120600b81015491015491925061449991614e0d565b42106133c05760008381526007602090815260408083208484529091529020600301546144d85760405162461bcd60e51b81526004016105ba90614e20565b60405180610180016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600460009054906101000a90046001600160a01b03166001600160a01b03166322684db0866040518263ffffffff1660e01b815260040161455891815260200190565b602060405180830381865afa158015614575573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145999190614df4565b815260008581526006602081815260408084206003808201548488015282870186905260608088018790526007928301546080988901528b8752828552838720600980875285892080548a529187528589208b5181558b8801516001820155958b01516002870155918a01519285019290925596880151600484015560a0880151600584015560c08801519483019490945560e087015190820155610100860151600882015561012086015181860155610140860151600a82015561016090950151600b909501949094558682529190925280549161467783614d97565b9190505550505050565b606061468f83836000614696565b9392505050565b6060814710156146bb5760405163cd78605960e01b81523060048201526024016105ba565b600080856001600160a01b031684866040516146d7919061520d565b60006040518083038185875af1925050503d8060008114614714576040519150601f19603f3d011682016040523d82523d6000602084013e614719565b606091505b5091509150614729868383614733565b9695505050505050565b606082614748576147438261478f565b61468f565b815115801561475f57506001600160a01b0384163b155b1561478857604051639996b31560e01b81526001600160a01b03851660048201526024016105ba565b508061468f565b80511561479f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b82805482825590600052602060002090810192821561480d579160200282015b8281111561480d57825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906147d8565b5061481992915061481d565b5090565b5b80821115614819576000815560010161481e565b6001600160a01b0381168114610ee257600080fd5b803561485281614832565b919050565b6000806040838503121561486a57600080fd5b82359150602083013561487c81614832565b809150509250929050565b6000806040838503121561489a57600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b838110156148e25781516001600160a01b0316875295820195908201906001016148bd565b509495945050505050565b60208152600061468f60208301846148a9565b6000806000806080858703121561491657600080fd5b5050823594602084013594506040840135936060013592509050565b60006020828403121561494457600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b604051610160810167ffffffffffffffff811182821017156149855761498561494b565b60405290565b600082601f83011261499c57600080fd5b8135602067ffffffffffffffff808311156149b9576149b961494b565b8260051b604051601f19603f830116810181811084821117156149de576149de61494b565b6040529384528581018301938381019250878511156149fc57600080fd5b83870191505b84821015614a24578135614a1581614832565b83529183019190830190614a02565b979650505050505050565b8015158114610ee257600080fd5b803561485281614a2f565b600060208284031215614a5a57600080fd5b813567ffffffffffffffff80821115614a7257600080fd5b908301906101608286031215614a8757600080fd5b614a8f614961565b614a9883614847565b81526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c082015260e083013560e0820152610100614aee818501614847565b908201526101208381013583811115614b0657600080fd5b614b128882870161498b565b8284015250506101409150614b28828401614a3d565b91810191909152949350505050565b600080600060608486031215614b4c57600080fd5b505081359360208301359350604090920135919050565b600080600060608486031215614b7857600080fd5b833592506020840135614b8a81614832565b929592945050506040919091013590565b60008060408385031215614bae57600080fd5b82359150602083013567ffffffffffffffff811115614bcc57600080fd5b614bd88582860161498b565b9150509250929050565b60008060408385031215614bf557600080fd5b82359150602083013561487c81614a2f565b60208152614c216020820183516001600160a01b03169052565b60006020830151614c3d60408401826001600160a01b03169052565b506040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015160e083015260e08301516101008181850152808501519150506101208181850152808501519150506101408181850152808501519150506101608181850152808501519150506101808181850152808501519150506101a08181850152808501519150506102206101c08181860152614ce86102408601846148a9565b908601519092506101e0614d06868201836001600160a01b03169052565b8601519050610200614d1b8682018315159052565b90950151151593019290925250919050565b600060208284031215614d3f57600080fd5b813561468f81614832565b6020808252600790820152664552525f562e3160c81b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201614da957614da9614d81565b5060010190565b60208082526008908201526704552525f562e31360c41b604082015260600190565b60208082526008908201526704552525f562e32360c41b604082015260600190565b600060208284031215614e0657600080fd5b5051919050565b8082018082111561425457614254614d81565b6020808252600790820152664552525f562e3560c81b604082015260600190565b60208082526023908201527f496e73756666696369656e7420616c6c6f77616e636520666f7220776974686460408201526272617760e81b606082015260800190565b600082614ea157634e487b7160e01b600052601260045260246000fd5b500490565b60005b83811015614ec1578181015183820152602001614ea9565b50506000910152565b6c021a4ab233ab73229b430b9329609d1b815260008251614ef281600d850160208701614ea6565b91909101600d0192915050565b634349565360e01b815260008251614f1e816004850160208701614ea6565b9190910160040192915050565b60008151808452614f43816020860160208601614ea6565b601f01601f19169290920160200192915050565b604081526000614f6a6040830185614f2b565b8281036020840152614f7c8185614f2b565b95945050505050565b600060208284031215614f9757600080fd5b815161468f81614832565b60006101008a835289602084015288604084015287606084015286151560808401528060a0840152614fd6818401876148a9565b6001600160a01b039590951660c0840152505060e001529695505050505050565b8181038181111561425457614254614d81565b60008161501957615019614d81565b506000190190565b60208082526008908201526708aa4a4beac5c62760c31b604082015260600190565b6020808252600890820152674552525f562e323760c01b604082015260600190565b6000604082016040835280855480835260608501915086600052602092508260002060005b828110156150af5781546001600160a01b03168452928401926001918201910161508a565b5050508381038285015261472981866148a9565b808202811582820484141761425457614254614d81565b6000602082840312156150ec57600080fd5b815160ff8116811461468f57600080fd5b600181815b8085111561513857816000190482111561511e5761511e614d81565b8085161561512b57918102915b93841c9390800290615102565b509250929050565b60008261514f57506001614254565b8161515c57506000614254565b8160018114615172576002811461517c57615198565b6001915050614254565b60ff84111561518d5761518d614d81565b50506001821b614254565b5060208310610133831016604e8410600b84101617156151bb575081810a614254565b6151c583836150fd565b80600019048211156151d9576151d9614d81565b029392505050565b600061468f60ff841683615140565b60006020828403121561520257600080fd5b815161468f81614a2f565b6000825161521f818460208701614ea6565b919091019291505056fea264697066735822122006079d06cca09e01f473475bb5d8fe3e216b97681aa2237dbaf6cbc802b51f9164736f6c63430008140033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$127.54
Net Worth in ETH
0.062021
Token Allocations
USDT
100.00%
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 100.00% | $1 | 127.5385 | $127.54 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.