More Info
Private Name Tags
ContractCreator
Latest 8 from a total of 8 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Unlock Early | 19392389 | 368 days ago | IN | 0 ETH | 0.02169163 | ||||
Unlock Early | 17442936 | 642 days ago | IN | 0 ETH | 0.00665501 | ||||
Unlock Early | 16856034 | 725 days ago | IN | 0 ETH | 0.01032077 | ||||
Unlock Early | 16827368 | 729 days ago | IN | 0 ETH | 0.01436219 | ||||
Unlock Early | 16812249 | 731 days ago | IN | 0 ETH | 0.00589428 | ||||
Unlock Early | 16207290 | 815 days ago | IN | 0 ETH | 0.0004745 | ||||
Unlock Early | 16207290 | 815 days ago | IN | 0 ETH | 0.00638987 | ||||
Unlock Early | 16176638 | 820 days ago | IN | 0 ETH | 0.00648424 |
Latest 2 internal transactions
Advanced mode:
Parent Transaction Hash | Method | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|---|
0x60e06040 | 16169904 | 821 days ago | Contract Creation | 0 ETH | |||
0x61016060 | 16169904 | 821 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
Lockup
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 20000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "contracts/interfaces/IERC721Transferable.sol"; import "contracts/interfaces/IStakingNFT.sol"; import "contracts/libraries/errors/LockupErrors.sol"; import "contracts/libraries/lockup/AccessControlled.sol"; import "contracts/utils/auth/ImmutableFactory.sol"; import "contracts/utils/auth/ImmutablePublicStaking.sol"; import "contracts/utils/auth/ImmutableALCA.sol"; import "contracts/utils/EthSafeTransfer.sol"; import "contracts/utils/ERC20SafeTransfer.sol"; import "contracts/BonusPool.sol"; import "contracts/RewardPool.sol"; /** * @notice This contract locks up publicStaking position for a certain period. The position is * transferred to this contract, and the original owner is entitled to collect profits, and unlock * the position. If the position was kept locked until the end of the locking period, the original * owner will be able to get the original position back, plus any profits gained by the position * (e.g from ALCB sale) + a bonus amount based on the amount of shares of the public staking * position. * * Original owner will be able to collect profits from the position normally during the locking * period. However, a certain percentage will be held by the contract and only distributed after the * locking period has finished and the user unlocks. * * Original owner will be able to unlock position (partially or fully) before the locking period has * finished. The owner will able to decide which will be the amount unlocked earlier (called * exitAmount). In case of full exit (exitAmount == positionShares), the owner will not get the * percentage of profits of that position that are held by this contract and he will not receive any * bonus amount. In case, of partial exit (exitAmount < positionShares), the owner will be loosing * only the profits + bonus relative to the exiting amount. * * * @dev deployed by the AliceNetFactory contract */ /// @custom:salt Lockup /// @custom:deploy-type deployCreateAndRegister /// @custom:deploy-group lockup /// @custom:deploy-group-index 0 contract Lockup is ImmutablePublicStaking, ImmutableALCA, ERC20SafeTransfer, EthSafeTransfer, ERC721Holder { enum State { PreLock, InLock, PostLock } uint256 public constant SCALING_FACTOR = 10 ** 18; uint256 public constant FRACTION_RESERVED = SCALING_FACTOR / 5; // rewardPool contract address address internal immutable _rewardPool; // bonusPool contract address address internal immutable _bonusPool; // block on which lock starts uint256 internal immutable _startBlock; // block on which lock ends uint256 internal immutable _endBlock; // Total Locked describes the total number of ALCA locked in this contract. // Since no accumulators are used this is tracked to allow proportionate // payouts. uint256 internal _totalSharesLocked; // _ownerOf tracks who is the owner of a tokenID locked in this contract // mapping(tokenID -> owner). mapping(uint256 => address) internal _ownerOf; // _tokenOf is the inverse of ownerOf and returns the owner given the tokenID // users are only allowed 1 position per account, mapping (owner -> tokenID). mapping(address => uint256) internal _tokenOf; // maps and index to a tokenID for iterable counting i.e (index -> tokenID). // Stop iterating when token id is zero. Must use tail insert to delete or else // pagination will end early. mapping(uint256 => uint256) internal _tokenIDs; // lookup index by ID (tokenID -> index). mapping(uint256 => uint256) internal _reverseTokenIDs; // tracks the number of tokenIDs this contract holds. uint256 internal _lenTokenIDs; // support mapping to keep track all the ethereum owed to user to be // redistributed in the postLock phase during safe mode. mapping(address => uint256) internal _rewardEth; // support mapping to keep track all the token owed to user to be // redistributed in the postLock phase during safe mode. mapping(address => uint256) internal _rewardTokens; // Flag to determine if we are in the postLock phase safe or unsafe, i.e if // users are allowed to withdrawal or not. All profits need to be collect by all // positions before setting the safe mode. bool public payoutSafe; // offset for pagination when collecting the profits in the postLock unsafe // phase. Many people may call aggregateProfits until all rewards has been // collected. uint256 internal _tokenIDOffset; event EarlyExit(address to_, uint256 tokenID_); event NewLockup(address from_, uint256 tokenID_); modifier onlyPreLock() { if (_getState() != State.PreLock) { revert LockupErrors.PreLockStateRequired(); } _; } modifier excludePreLock() { if (_getState() == State.PreLock) { revert LockupErrors.PreLockStateNotAllowed(); } _; } modifier onlyPostLock() { if (_getState() != State.PostLock) { revert LockupErrors.PostLockStateRequired(); } _; } modifier excludePostLock() { if (_getState() == State.PostLock) { revert LockupErrors.PostLockStateNotAllowed(); } _; } modifier onlyPayoutSafe() { if (!payoutSafe) { revert LockupErrors.PayoutUnsafe(); } _; } modifier onlyPayoutUnSafe() { if (payoutSafe) { revert LockupErrors.PayoutSafe(); } _; } modifier onlyInLock() { if (_getState() != State.InLock) { revert LockupErrors.InLockStateRequired(); } _; } constructor( uint256 enrollmentPeriod_, uint256 lockDuration_, uint256 totalBonusAmount_ ) ImmutableFactory(msg.sender) ImmutablePublicStaking() ImmutableALCA() { RewardPool rewardPool = new RewardPool( _alcaAddress(), _factoryAddress(), totalBonusAmount_ ); _rewardPool = address(rewardPool); _bonusPool = rewardPool.getBonusPoolAddress(); _startBlock = block.number + enrollmentPeriod_; _endBlock = _startBlock + lockDuration_; } /// @dev only publicStaking and rewardPool are allowed to send ether to this contract receive() external payable { if (msg.sender != _publicStakingAddress() && msg.sender != _rewardPool) { revert LockupErrors.AddressNotAllowedToSendEther(); } } /// @notice callback function called by the ERC721.safeTransfer. On safe transfer of /// publicStaking positions to this contract, it will be performing checks and in case everything /// is fine, that position will be locked in name of the original owner that performed the /// transfer /// @dev publicStaking positions can only be safe transferred to this contract on PreLock phase /// (enrollment phase) /// @param from_ original owner of the publicStaking Position. The position will locked for this /// address /// @param tokenID_ The publicStaking tokenID that will be locked up function onERC721Received( address, address from_, uint256 tokenID_, bytes memory ) public override onlyPreLock returns (bytes4) { if (msg.sender != _publicStakingAddress()) { revert LockupErrors.OnlyStakingNFTAllowed(); } _lockFromTransfer(tokenID_, from_); return this.onERC721Received.selector; } /// @notice transfer and locks a pre-approved publicStaking position to this contract /// @dev can only be called at PreLock phase (enrollment phase) /// @param tokenID_ The publicStaking tokenID that will be locked up function lockFromApproval(uint256 tokenID_) public { // msg.sender already approved transfer, so contract can safeTransfer to itself; by doing // this onERC721Received is called as part of the chain of transfer methods hence the checks // run from within onERC721Received IERC721Transferable(_publicStakingAddress()).safeTransferFrom( msg.sender, address(this), tokenID_ ); } /// @notice locks a position that was already transferred to this contract without using /// safeTransfer. WARNING: SHOULD ONLY BE USED FROM SMART CONTRACT THAT TRANSFERS A POSITION AND /// CALL THIS METHOD RIGHT IN SEQUENCE /// @dev can only be called at PreLock phase (enrollment phase) /// @param tokenID_ The publicStaking tokenID that will be locked up /// @param tokenOwner_ The address that will be used as the user entitled to that position function lockFromTransfer(uint256 tokenID_, address tokenOwner_) public onlyPreLock { _lockFromTransfer(tokenID_, tokenOwner_); } /// @notice collects all profits from a position locked up by this contract. Only a certain /// amount of the profits will be sent, the rest will held by the contract and released at the /// final unlock. /// @dev can only be called if the PostLock phase has not began /// @dev can only be called by position's entitled owner /// @return payoutEth the amount of eth that was sent to user /// @return payoutToken the amount of ALCA that was sent to user function collectAllProfits() public excludePostLock returns (uint256 payoutEth, uint256 payoutToken) { return _collectAllProfits(_payableSender(), _validateAndGetTokenId()); } /// @notice function to partially or fully unlock a locked position. The entitled owner will /// able to decide which will be the amount unlocked earlier (exitValue_). In case of full exit /// (exitValue_ == positionShares), the owner will not get the percentage of profits of that /// position that are held by this contract and he will not receive any bonus amount. In case, of /// partial exit (exitValue_< positionShares), the owner will be loosing only the profits + bonus /// relative to the exiting amount. The owner may choose via stakeExit_ boolean if the ALCA will be /// sent a new publicStaking position or as ALCA directly to his address. /// @dev can only be called if the PostLock phase has not began /// @dev can only be called by position's entitled owner /// @param exitValue_ The amount in which the user wants to unlock earlier /// @param stakeExit_ Flag to decide the ALCA will be sent directly or staked as new /// publicStaking position /// @return payoutEth the amount of eth that was sent to user discounting the reserved amount /// @return payoutToken the amount of ALCA discounting the reserved amount that was sent or /// staked as new position to the user function unlockEarly( uint256 exitValue_, bool stakeExit_ ) public excludePostLock returns (uint256 payoutEth, uint256 payoutToken) { uint256 tokenID = _validateAndGetTokenId(); // get the number of shares and check validity uint256 shares = _getNumShares(tokenID); if (exitValue_ > shares) { revert LockupErrors.InsufficientBalanceForEarlyExit(exitValue_, shares); } // burn the existing position (payoutEth, payoutToken) = IStakingNFT(_publicStakingAddress()).burn(tokenID); // separating alca reward from alca shares payoutToken -= shares; // blank old record _ownerOf[tokenID] = address(0); // create placeholder uint256 newTokenID; // find shares delta and mint new position uint256 remainingShares = shares - exitValue_; if (remainingShares > 0) { // approve the transfer of ALCA in order to mint the publicStaking position IERC20(_alcaAddress()).approve(_publicStakingAddress(), remainingShares); // burn profits contain staked position... so sub it out newTokenID = IStakingNFT(_publicStakingAddress()).mint(remainingShares); // set new records _ownerOf[newTokenID] = msg.sender; _replaceTokenID(tokenID, newTokenID); } else { _removeTokenID(tokenID); } // safe because newTokenId is zero if shares == exitValue _tokenOf[msg.sender] = newTokenID; _totalSharesLocked -= exitValue_; (payoutEth, payoutToken) = _distributeAllProfits( _payableSender(), payoutEth, payoutToken, exitValue_, stakeExit_ ); emit EarlyExit(msg.sender, tokenID); } /// @notice aggregateProfits iterate alls locked positions and collect their profits before /// allowing withdraws/unlocks. This step is necessary to make sure that the correct reserved /// amount is in the rewardPool before allowing unlocks. This function will not send any ether or /// ALCA to users, since this can be very dangerous (specially on a loop). Instead all the /// assets that are not sent to the rewardPool are held in the lockup contract, and the right /// balance is stored per position owner. All the value will be send to the owner address at the /// call of the `{unlock()}` function. This function can only be called after the locking period /// has finished. Anyone can call this function. function aggregateProfits() public onlyPayoutUnSafe onlyPostLock { // get some gas cost tracking setup uint256 gasStart = gasleft(); uint256 gasLoop; // start index where we left off plus one uint256 i = _tokenIDOffset + 1; // for loop that will exit when one of following is true the gas remaining is less than 5x // the estimated per iteration cost or the iterator is done for (; ; i++) { (uint256 tokenID, bool ok) = _getTokenIDAtIndex(i); if (!ok) { // if we get here, iteration of array is done and we can move on with life and set // payoutSafe since all payouts have been recorded payoutSafe = true; // burn the bonus Position and send the bonus to the rewardPool contract BonusPool(payable(_bonusPool)).terminate(); break; } address payable acct = _getOwnerOf(tokenID); _collectAllProfits(acct, tokenID); uint256 gasRem = gasleft(); if (gasLoop == 0) { // record gas iteration estimate if not done gasLoop = gasStart - gasRem; // give 5x multi on it to ensure even an overpriced element by 2x the normal // cost will still pass gasLoop = 5 * gasLoop; // accounts for state writes on exit gasLoop = gasLoop + 10000; } else if (gasRem <= gasLoop) { // if we are below cutoff break break; } } _tokenIDOffset = i; } /// @notice unlocks a locked position and collect all kind of profits (bonus shares, held /// rewards etc). Can only be called after the locking period has finished and {aggregateProfits} /// has been executed for positions. Can only be called by the user entitled to a position /// (address that locked a position). This function can only be called after the locking period /// has finished and {aggregateProfits()} has been executed for all locked positions. /// @param to_ destination address were the profits, shares will be sent /// @param stakeExit_ boolean flag indicating if the ALCA should be returned directly or staked /// into a new publicStaking position. /// @return payoutEth the ether amount deposited to an address after unlock /// @return payoutToken the ALCA amount staked or sent to an address after unlock function unlock( address to_, bool stakeExit_ ) public onlyPostLock onlyPayoutSafe returns (uint256 payoutEth, uint256 payoutToken) { uint256 tokenID = _validateAndGetTokenId(); uint256 shares = _getNumShares(tokenID); bool isLastPosition = _lenTokenIDs == 1; (payoutEth, payoutToken) = _burnLockedPosition(tokenID, msg.sender); (uint256 accumulatedRewardEth, uint256 accumulatedRewardToken) = RewardPool(_rewardPool) .payout(_totalSharesLocked, shares, isLastPosition); payoutEth += accumulatedRewardEth; payoutToken += accumulatedRewardToken; (uint256 aggregatedEth, uint256 aggregatedToken) = _withdrawalAggregatedAmount(msg.sender); payoutEth += aggregatedEth; payoutToken += aggregatedToken; _transferEthAndTokensWithReStake(to_, payoutEth, payoutToken, stakeExit_); } /// @notice gets the address that is entitled to unlock/collect profits for a position. I.e the /// address that locked this position into this contract. /// @param tokenID_ the position Id to retrieve the owner /// @return the owner address of a position. Returns 0 if a position is not locked into this /// contract function ownerOf(uint256 tokenID_) public view returns (address payable) { return _getOwnerOf(tokenID_); } /// @notice gets the positionID that an address is entitled to unlock/collect profits. I.e /// position that an address locked into this contract. /// @param acct_ address to retrieve a position (tokenID) /// @return the position ID (tokenID) of the position that the address locked into this /// contract. If an address doesn't possess any locked position in this contract, this function /// returns 0 function tokenOf(address acct_) public view returns (uint256) { return _getTokenOf(acct_); } /// @notice gets the total number of positions locked into this contract. Can be used with /// {getIndexByTokenId} and {getPositionByIndex} to get all publicStaking positions held by this /// contract. /// @return the total number of positions locked into this contract function getCurrentNumberOfLockedPositions() public view returns (uint256) { return _lenTokenIDs; } /// @notice gets the position referenced by an index in the enumerable mapping implemented by /// this contract. Can be used {getIndexByTokenId} to get all positions IDs locked by this /// contract. /// @param index_ the index to get the positionID /// @return the tokenId referenced by an index the enumerable mapping (indexes start at 1). If /// the index doesn't exists this function returns 0 function getPositionByIndex(uint256 index_) public view returns (uint256) { return _tokenIDs[index_]; } /// @notice gets the index of a position in the enumerable mapping implemented by this contract. /// Can be used {getPositionByIndex} to get all positions IDs locked by this contract. /// @param tokenID_ the position ID to get index for /// @return the index of a position in the enumerable mapping (indexes start at 1). If the /// tokenID is not locked into this contract this function returns 0 function getIndexByTokenId(uint256 tokenID_) public view returns (uint256) { return _reverseTokenIDs[tokenID_]; } /// @notice gets the ethereum block where the locking period will start. This block is also /// when the enrollment period will finish. I.e after this block we don't allow new positions to /// be locked. /// @return the ethereum block where the locking period will start function getLockupStartBlock() public view returns (uint256) { return _startBlock; } /// @notice gets the ethereum block where the locking period will end. After this block /// aggregateProfit has to be called to enable the unlock period. /// @return the ethereum block where the locking period will end function getLockupEndBlock() public view returns (uint256) { return _endBlock; } /// @notice gets the ether and ALCA balance owed to a user after aggregateProfit has been /// called. This funds are send after final unlock. /// @return user ether balance held by this contract /// @return user ALCA balance held by this contract function getTemporaryRewardBalance(address user_) public view returns (uint256, uint256) { return _getTemporaryRewardBalance(user_); } /// @notice gets the RewardPool contract address /// @return the reward pool contract address function getRewardPoolAddress() public view returns (address) { return _rewardPool; } /// @notice gets the bonusPool contract address /// @return the bonusPool contract address function getBonusPoolAddress() public view returns (address) { return _bonusPool; } /// @notice gets the current amount of ALCA that is locked in this contract, after all early exits /// @return the amount of ALCA that is currently locked in this contract function getTotalSharesLocked() public view returns (uint256) { return _totalSharesLocked; } /// @notice gets the current state of the lockup (preLock, InLock, PostLock) /// @return the current state of the lockup contract function getState() public view returns (State) { return _getState(); } /// @notice estimate the (liquid) income that can be collected from locked positions via /// {collectAllProfits} /// @dev this functions deducts the reserved amount that is sent to rewardPool contract function estimateProfits( uint256 tokenID_ ) public view returns (uint256 payoutEth, uint256 payoutToken) { // check if the position owned by this contract _verifyLockedPosition(tokenID_); (payoutEth, payoutToken) = IStakingNFT(_publicStakingAddress()).estimateAllProfits( tokenID_ ); (uint256 reserveEth, uint256 reserveToken) = _computeReservedAmount(payoutEth, payoutToken); payoutEth -= reserveEth; payoutToken -= reserveToken; } /// @notice function to estimate the final amount of ALCA and ether that a locked /// position will receive at the end of the locking period. Depending on the preciseEstimation_ flag this function can be an imprecise approximation, /// the real amount can differ especially as user's collect profits in the middle of the locking /// period. Passing preciseEstimation_ as true will give a precise estimate since all profits are aggregated in a loop, /// hence is optional as it can be expensive if called as part of a smart contract transaction that alters state. After the locking /// period has finished and aggregateProfits has been executed for all locked positions the estimate will also be accurate. /// @dev this function is just an approximation when preciseEstimation_ is false, the real amount can differ! /// @param tokenID_ The token to check for the final profits. /// @param preciseEstimation_ whether to use the precise estimation or the approximation (precise is expensive due to looping so use wisely) /// @return positionShares_ the positions ALCA shares /// @return payoutEth_ the ether amount that the position will receive as profit /// @return payoutToken_ the ALCA amount that the position will receive as profit function estimateFinalBonusWithProfits( uint256 tokenID_, bool preciseEstimation_ ) public view returns (uint256 positionShares_, uint256 payoutEth_, uint256 payoutToken_) { // check if the position owned by this contract _verifyLockedPosition(tokenID_); positionShares_ = _getNumShares(tokenID_); uint256 currentSharesLocked = _totalSharesLocked; // get the bonus amount + any profit from the bonus staked position (payoutEth_, payoutToken_) = BonusPool(payable(_bonusPool)).estimateBonusAmountWithReward( currentSharesLocked, positionShares_ ); // get the cumulative rewards held in the rewardPool so far. In the case that // aggregateProfits has not been ran, the amount returned by this call may not be precise, // since only some users may have been collected until this point, in which case // preciseEstimation_ can be passed as true to get a precise estimate. (uint256 rewardEthProfit, uint256 rewardTokenProfit) = RewardPool(_rewardPool) .estimateRewards(currentSharesLocked, positionShares_); payoutEth_ += rewardEthProfit; payoutToken_ += rewardTokenProfit; uint256 reservedEth; uint256 reservedToken; // if aggregateProfits has been called (indicated by the payoutSafe flag), this calculation is not needed if (preciseEstimation_ && !payoutSafe) { // get this positions share based on all user profits aggregated (NOTE: precise but expensive due to the loop) (reservedEth, reservedToken) = _estimateUserAggregatedProfits( positionShares_, currentSharesLocked ); } else { // get any future profit that will be held in the rewardPool for this position (uint256 positionEthProfit, uint256 positionTokenProfit) = IStakingNFT( _publicStakingAddress() ).estimateAllProfits(tokenID_); (reservedEth, reservedToken) = _computeReservedAmount( positionEthProfit, positionTokenProfit ); } payoutEth_ += reservedEth; payoutToken_ += reservedToken; // get any eth and token held by this contract as result of the call to the aggregateProfit // function (uint256 aggregatedEth, uint256 aggregatedTokens) = _getTemporaryRewardBalance( _getOwnerOf(tokenID_) ); payoutEth_ += aggregatedEth; payoutToken_ += aggregatedTokens; } /// @notice return the percentage amount that is held from the locked positions /// @dev this value is scaled by 100. Therefore the values are from 0-100% /// @return the percentage amount that is held from the locked positions function getReservedPercentage() public pure returns (uint256) { return (100 * FRACTION_RESERVED) / SCALING_FACTOR; } /// @notice gets the fraction of the amount that is reserved to reward pool /// @return the calculated reserved amount function getReservedAmount(uint256 amount_) public pure returns (uint256) { return (amount_ * FRACTION_RESERVED) / SCALING_FACTOR; } function _lockFromTransfer(uint256 tokenID_, address tokenOwner_) internal { _validateEntry(tokenID_, tokenOwner_); _checkTokenTransfer(tokenID_); _lock(tokenID_, tokenOwner_); } function _lock(uint256 tokenID_, address tokenOwner_) internal { uint256 shares = _verifyPositionAndGetShares(tokenID_); _totalSharesLocked += shares; _tokenOf[tokenOwner_] = tokenID_; _ownerOf[tokenID_] = tokenOwner_; _newTokenID(tokenID_); emit NewLockup(tokenOwner_, tokenID_); } function _burnLockedPosition( uint256 tokenID_, address tokenOwner_ ) internal returns (uint256 payoutEth, uint256 payoutToken) { // burn the old position (payoutEth, payoutToken) = IStakingNFT(_publicStakingAddress()).burn(tokenID_); //delete tokenID_ from iterable tokenID mapping _removeTokenID(tokenID_); delete (_tokenOf[tokenOwner_]); delete (_ownerOf[tokenID_]); } function _withdrawalAggregatedAmount( address account_ ) internal returns (uint256 payoutEth, uint256 payoutToken) { // case of we are sending out final pay based on request just pay all payoutEth = _rewardEth[account_]; payoutToken = _rewardTokens[account_]; _rewardEth[account_] = 0; _rewardTokens[account_] = 0; } function _collectAllProfits( address payable acct_, uint256 tokenID_ ) internal returns (uint256 payoutEth, uint256 payoutToken) { (payoutEth, payoutToken) = IStakingNFT(_publicStakingAddress()).collectAllProfits(tokenID_); return _distributeAllProfits(acct_, payoutEth, payoutToken, 0, false); } function _distributeAllProfits( address payable acct_, uint256 payoutEth_, uint256 payoutToken_, uint256 additionalTokens, bool stakeExit_ ) internal returns (uint256 userPayoutEth, uint256 userPayoutToken) { State state = _getState(); bool localPayoutSafe = payoutSafe; userPayoutEth = payoutEth_; userPayoutToken = payoutToken_; (uint256 reservedEth, uint256 reservedToken) = _computeReservedAmount( payoutEth_, payoutToken_ ); userPayoutEth -= reservedEth; userPayoutToken -= reservedToken; // send tokens to reward pool _depositFundsInRewardPool(reservedEth, reservedToken); // in case this is being called by {aggregateProfits()} we don't send any asset to the // users, we just store the owed amounts on state if (!localPayoutSafe && state == State.PostLock) { // we should not send here and should instead track to local mapping as // otherwise a single bad user could block exit operations for all other users // by making the send to their account fail via a contract _rewardEth[acct_] += userPayoutEth; _rewardTokens[acct_] += userPayoutToken; return (userPayoutEth, userPayoutToken); } // adding any additional token that should be sent to the user (e.g shares from // burned position on early exit) userPayoutToken += additionalTokens; _transferEthAndTokensWithReStake(acct_, userPayoutEth, userPayoutToken, stakeExit_); return (userPayoutEth, userPayoutToken); } function _transferEthAndTokensWithReStake( address to_, uint256 payoutEth_, uint256 payoutToken_, bool stakeExit_ ) internal { if (stakeExit_) { IERC20(_alcaAddress()).approve(_publicStakingAddress(), payoutToken_); IStakingNFT(_publicStakingAddress()).mintTo(to_, payoutToken_, 0); } else { _safeTransferERC20(IERC20Transferable(_alcaAddress()), to_, payoutToken_); } _safeTransferEth(to_, payoutEth_); } function _newTokenID(uint256 tokenID_) internal { uint256 index = _lenTokenIDs + 1; _tokenIDs[index] = tokenID_; _reverseTokenIDs[tokenID_] = index; _lenTokenIDs = index; } function _replaceTokenID(uint256 oldID_, uint256 newID_) internal { uint256 index = _reverseTokenIDs[oldID_]; _reverseTokenIDs[oldID_] = 0; _tokenIDs[index] = newID_; _reverseTokenIDs[newID_] = index; } function _removeTokenID(uint256 tokenID_) internal { uint256 initialLen = _lenTokenIDs; if (initialLen == 0) { return; } if (initialLen == 1) { uint256 index = _reverseTokenIDs[tokenID_]; _reverseTokenIDs[tokenID_] = 0; _tokenIDs[index] = 0; _lenTokenIDs = 0; return; } // pop the tail uint256 tailTokenID = _tokenIDs[initialLen]; _tokenIDs[initialLen] = 0; _lenTokenIDs = initialLen - 1; if (tailTokenID == tokenID_) { // element was tail, so we are done _reverseTokenIDs[tailTokenID] = 0; return; } // use swap logic to re-insert tail over other position _replaceTokenID(tokenID_, tailTokenID); } function _depositFundsInRewardPool(uint256 reservedEth_, uint256 reservedToken_) internal { _safeTransferERC20(IERC20Transferable(_alcaAddress()), _rewardPool, reservedToken_); RewardPool(_rewardPool).deposit{value: reservedEth_}(reservedToken_); } function _getNumShares(uint256 tokenID_) internal view returns (uint256 shares) { (shares, , , , ) = IStakingNFT(_publicStakingAddress()).getPosition(tokenID_); } function _estimateTotalAggregatedProfits() internal view returns (uint256 payoutEth, uint256 payoutToken) { for (uint256 i = 1; i <= _lenTokenIDs; i++) { (uint256 tokenID, ) = _getTokenIDAtIndex(i); (uint256 stakingProfitEth, uint256 stakingProfitToken) = IStakingNFT( _publicStakingAddress() ).estimateAllProfits(tokenID); (uint256 reserveEth, uint256 reserveToken) = _computeReservedAmount( stakingProfitEth, stakingProfitToken ); payoutEth += reserveEth; payoutToken += reserveToken; } } function _estimateUserAggregatedProfits( uint256 userShares_, uint256 totalShares_ ) internal view returns (uint256 payoutEth, uint256 payoutToken) { (payoutEth, payoutToken) = _estimateTotalAggregatedProfits(); payoutEth = (payoutEth * userShares_) / totalShares_; payoutToken = (payoutToken * userShares_) / totalShares_; } function _payableSender() internal view returns (address payable) { return payable(msg.sender); } function _getTokenIDAtIndex(uint256 index_) internal view returns (uint256 tokenID, bool ok) { tokenID = _tokenIDs[index_]; return (tokenID, tokenID > 0); } function _checkTokenTransfer(uint256 tokenID_) internal view { if (IERC721(_publicStakingAddress()).ownerOf(tokenID_) != address(this)) { revert LockupErrors.ContractDoesNotOwnTokenID(tokenID_); } } function _validateEntry(uint256 tokenID_, address sender_) internal view { if (_getOwnerOf(tokenID_) != address(0)) { revert LockupErrors.TokenIDAlreadyClaimed(tokenID_); } if (_getTokenOf(sender_) != 0) { revert LockupErrors.AddressAlreadyLockedUp(); } } function _validateAndGetTokenId() internal view returns (uint256) { // get tokenID of caller uint256 tokenID = _getTokenOf(msg.sender); if (tokenID == 0) { revert LockupErrors.UserHasNoPosition(); } return tokenID; } function _verifyLockedPosition(uint256 tokenID_) internal view { if (_getOwnerOf(tokenID_) == address(0)) { revert LockupErrors.TokenIDNotLocked(tokenID_); } } // Gets the shares of position and checks if a position exists and if we can collect the // profits after the _endBlock. function _verifyPositionAndGetShares(uint256 tokenId_) internal view returns (uint256) { // get position fails if the position doesn't exists! (uint256 shares, , uint256 withdrawFreeAfter, , ) = IStakingNFT(_publicStakingAddress()) .getPosition(tokenId_); if (withdrawFreeAfter >= _endBlock) { revert LockupErrors.InvalidPositionWithdrawPeriod(withdrawFreeAfter, _endBlock); } return shares; } function _getState() internal view returns (State) { if (block.number < _startBlock) { return State.PreLock; } if (block.number < _endBlock) { return State.InLock; } return State.PostLock; } function _getOwnerOf(uint256 tokenID_) internal view returns (address payable) { return payable(_ownerOf[tokenID_]); } function _getTokenOf(address acct_) internal view returns (uint256) { return _tokenOf[acct_]; } function _getTemporaryRewardBalance(address user_) internal view returns (uint256, uint256) { return (_rewardEth[user_], _rewardTokens[user_]); } function _computeReservedAmount( uint256 payoutEth_, uint256 payoutToken_ ) internal pure returns (uint256 reservedEth, uint256 reservedToken) { reservedEth = (payoutEth_ * FRACTION_RESERVED) / SCALING_FACTOR; reservedToken = (payoutToken_ * FRACTION_RESERVED) / SCALING_FACTOR; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/utils/ERC721Holder.sol) pragma solidity ^0.8.0; import "../IERC721Receiver.sol"; /** * @dev Implementation of the {IERC721Receiver} interface. * * Accepts all token transfers. * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}. */ contract ERC721Holder is IERC721Receiver { /** * @dev See {IERC721Receiver-onERC721Received}. * * Always returns `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address, address, uint256, bytes memory ) public virtual override returns (bytes4) { return this.onERC721Received.selector; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; import "contracts/utils/auth/ImmutableFactory.sol"; import "contracts/utils/auth/ImmutableALCA.sol"; import "contracts/utils/auth/ImmutablePublicStaking.sol"; import "contracts/utils/auth/ImmutableFoundation.sol"; import "contracts/utils/EthSafeTransfer.sol"; import "contracts/utils/ERC20SafeTransfer.sol"; import "contracts/utils/MagicEthTransfer.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "contracts/interfaces/IStakingNFT.sol"; import "contracts/libraries/errors/LockupErrors.sol"; import "contracts/libraries/lockup/AccessControlled.sol"; import "contracts/RewardPool.sol"; import "contracts/Lockup.sol"; /** * @notice This contract holds all ALCA that is held in escrow for lockup * bonuses. All ALCA is hold into a single staked position that is owned * locally. * @dev deployed by the RewardPool contract */ contract BonusPool is ImmutableALCA, ImmutablePublicStaking, ImmutableFoundation, ERC20SafeTransfer, EthSafeTransfer, ERC721Holder, AccessControlled, MagicEthTransfer { uint256 internal immutable _totalBonusAmount; address internal immutable _lockupContract; address internal immutable _rewardPool; // tokenID of the position created to hold the amount that will be redistributed as bonus uint256 internal _tokenID; event BonusPositionCreated(uint256 tokenID); constructor( address aliceNetFactory_, address lockupContract_, address rewardPool_, uint256 totalBonusAmount_ ) ImmutableFactory(aliceNetFactory_) ImmutableALCA() ImmutablePublicStaking() ImmutableFoundation() { _totalBonusAmount = totalBonusAmount_; _lockupContract = lockupContract_; _rewardPool = rewardPool_; } receive() external payable { if (msg.sender != _publicStakingAddress()) { revert LockupErrors.AddressNotAllowedToSendEther(); } } /// @notice function that creates/mint a publicStaking position with an amount that will be /// redistributed as bonus at the end of the lockup period. The amount of ALCA has to be /// transferred before calling this function. /// @dev can be only called by the AliceNet factory function createBonusStakedPosition() public onlyFactory { if (_tokenID != 0) { revert LockupErrors.BonusTokenAlreadyCreated(); } IERC20 alca = IERC20(_alcaAddress()); //get the total balance of ALCA owned by bonus pool as stake amount uint256 _stakeAmount = alca.balanceOf(address(this)); if (_stakeAmount < _totalBonusAmount) { revert LockupErrors.NotEnoughALCAToStake(_stakeAmount, _totalBonusAmount); } // approve the staking contract to transfer the ALCA alca.approve(_publicStakingAddress(), _totalBonusAmount); uint256 tokenID = IStakingNFT(_publicStakingAddress()).mint(_totalBonusAmount); _tokenID = tokenID; emit BonusPositionCreated(_tokenID); } /// @notice Burns that bonus staked position, and send the bonus amount of shares + profits to /// the rewardPool contract, so users can collect. function terminate() public onlyLockup { if (_tokenID == 0) { revert LockupErrors.BonusTokenNotCreated(); } // burn the nft to collect all profits. IStakingNFT(_publicStakingAddress()).burn(_tokenID); // restarting the _tokenID _tokenID = 0; // send the total balance of ALCA to the rewardPool contract uint256 alcaBalance = IERC20(_alcaAddress()).balanceOf(address(this)); _safeTransferERC20( IERC20Transferable(_alcaAddress()), _getRewardPoolAddress(), alcaBalance ); // send also all the balance of ether uint256 ethBalance = address(this).balance; RewardPool(_getRewardPoolAddress()).deposit{value: ethBalance}(alcaBalance); } /// @notice gets the lockup contract address /// @return the lockup contract address function getLockupContractAddress() public view returns (address) { return _getLockupContractAddress(); } /// @notice gets the rewardPool contract address /// @return the rewardPool contract address function getRewardPoolAddress() public view returns (address) { return _getRewardPoolAddress(); } /// @notice gets the tokenID of the publicStaking position that has the whole bonus amount /// @return the tokenID of the publicStaking position that has the whole bonus amount function getBonusStakedPosition() public view returns (uint256) { return _tokenID; } /// @notice gets the total amount of ALCA that was staked initially in the publicStaking position /// @return the total amount of ALCA that was staked initially in the publicStaking position function getTotalBonusAmount() public view returns (uint256) { return _totalBonusAmount; } /// @notice estimates a user's bonus amount + bonus position profits. /// @param currentSharesLocked_ The current number of shares locked in the lockup contract /// @param userShares_ The amount of shares that a user locked-up. /// @return bonusRewardEth the estimated amount ether profits for a user /// @return bonusRewardToken the estimated amount ALCA profits for a user function estimateBonusAmountWithReward( uint256 currentSharesLocked_, uint256 userShares_ ) public view returns (uint256 bonusRewardEth, uint256 bonusRewardToken) { if (_tokenID == 0) { return (0, 0); } (uint256 estimatedPayoutEth, uint256 estimatedPayoutToken) = IStakingNFT( _publicStakingAddress() ).estimateAllProfits(_tokenID); (uint256 shares, , , , ) = IStakingNFT(_publicStakingAddress()).getPosition(_tokenID); estimatedPayoutToken += shares; // compute what will be the amount that a user will receive from the amount that will be // sent to the reward contract. bonusRewardEth = (estimatedPayoutEth * userShares_) / currentSharesLocked_; bonusRewardToken = (estimatedPayoutToken * userShares_) / currentSharesLocked_; } function _getLockupContractAddress() internal view override returns (address) { return _lockupContract; } function _getBonusPoolAddress() internal view override returns (address) { return address(this); } function _getRewardPoolAddress() internal view override returns (address) { return _rewardPool; } }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; interface IAliceNetFactory { function lookup(bytes32 salt_) external view returns (address); }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; interface IERC20Transferable { function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); function transfer(address recipient, uint256 amount) external returns (bool); function approve(address spender, uint256 amount) external returns (bool); function balanceOf(address account) external view returns (uint256); }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; interface IERC721Transferable { function safeTransferFrom(address from, address to, uint256 tokenId) external; }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; interface IMagicEthTransfer { function depositEth(uint8 magic_) external payable; }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; interface IStakingNFT { function skimExcessEth(address to_) external returns (uint256 excess); function skimExcessToken(address to_) external returns (uint256 excess); function depositToken(uint8 magic_, uint256 amount_) external; function depositEth(uint8 magic_) external payable; function lockPosition( address caller_, uint256 tokenID_, uint256 lockDuration_ ) external returns (uint256); function lockOwnPosition(uint256 tokenID_, uint256 lockDuration_) external returns (uint256); function lockWithdraw(uint256 tokenID_, uint256 lockDuration_) external returns (uint256); function mint(uint256 amount_) external returns (uint256 tokenID); function mintTo( address to_, uint256 amount_, uint256 lockDuration_ ) external returns (uint256 tokenID); function burn(uint256 tokenID_) external returns (uint256 payoutEth, uint256 payoutALCA); function burnTo( address to_, uint256 tokenID_ ) external returns (uint256 payoutEth, uint256 payoutALCA); function collectEth(uint256 tokenID_) external returns (uint256 payout); function collectToken(uint256 tokenID_) external returns (uint256 payout); function collectAllProfits( uint256 tokenID_ ) external returns (uint256 payoutToken, uint256 payoutEth); function collectEthTo(address to_, uint256 tokenID_) external returns (uint256 payout); function collectTokenTo(address to_, uint256 tokenID_) external returns (uint256 payout); function collectAllProfitsTo( address to_, uint256 tokenID_ ) external returns (uint256 payoutToken, uint256 payoutEth); function getPosition( uint256 tokenID_ ) external view returns ( uint256 shares, uint256 freeAfter, uint256 withdrawFreeAfter, uint256 accumulatorEth, uint256 accumulatorToken ); function getTotalShares() external view returns (uint256); function getTotalReserveEth() external view returns (uint256); function getTotalReserveALCA() external view returns (uint256); function estimateEthCollection(uint256 tokenID_) external view returns (uint256 payout); function estimateTokenCollection(uint256 tokenID_) external view returns (uint256 payout); function estimateAllProfits( uint256 tokenID_ ) external view returns (uint256 payoutEth, uint256 payoutToken); function estimateExcessToken() external view returns (uint256 excess); function estimateExcessEth() external view returns (uint256 excess); function getEthAccumulator() external view returns (uint256 accumulator, uint256 slush); function getTokenAccumulator() external view returns (uint256 accumulator, uint256 slush); function getLatestMintedPositionID() external view returns (uint256); function getAccumulatorScaleFactor() external pure returns (uint256); function getMaxMintLock() external pure returns (uint256); function getMaxGovernanceLock() external pure returns (uint256); }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; library ERC20SafeTransferErrors { error CannotCallContractMethodsOnZeroAddress(); error Erc20TransferFailed(address erc20Address, address from, address to, uint256 amount); }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; library ETHSafeTransferErrors { error CannotTransferToZeroAddress(); error EthTransferFailed(address from, address to, uint256 amount); }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; library LockupErrors { error AddressNotAllowedToSendEther(); error OnlyStakingNFTAllowed(); error ContractDoesNotOwnTokenID(uint256 tokenID_); error AddressAlreadyLockedUp(); error TokenIDAlreadyClaimed(uint256 tokenID_); error InsufficientBalanceForEarlyExit(uint256 exitValue, uint256 currentBalance); error UserHasNoPosition(); error PreLockStateRequired(); error PreLockStateNotAllowed(); error PostLockStateNotAllowed(); error PostLockStateRequired(); error PayoutUnsafe(); error PayoutSafe(); error TokenIDNotLocked(uint256 tokenID_); error InvalidPositionWithdrawPeriod(uint256 withdrawFreeAfter, uint256 endBlock); error InLockStateRequired(); error BonusTokenNotCreated(); error BonusTokenAlreadyCreated(); error NotEnoughALCAToStake(uint256 currentBalance, uint256 expectedAmount); error InvalidTotalSharesValue(); }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; library MagicValueErrors { error BadMagic(uint256 magic); }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; abstract contract AccessControlled { error CallerNotLockup(); error CallerNotLockupOrBonus(); modifier onlyLockup() { if (msg.sender != _getLockupContractAddress()) { revert CallerNotLockup(); } _; } modifier onlyLockupOrBonus() { // must protect increment of token balance if ( msg.sender != _getLockupContractAddress() && msg.sender != address(_getBonusPoolAddress()) ) { revert CallerNotLockupOrBonus(); } _; } function _getLockupContractAddress() internal view virtual returns (address); function _getBonusPoolAddress() internal view virtual returns (address); function _getRewardPoolAddress() internal view virtual returns (address); }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "contracts/interfaces/IStakingNFT.sol"; import "contracts/libraries/lockup/AccessControlled.sol"; import "contracts/libraries/errors/LockupErrors.sol"; import "contracts/BonusPool.sol"; import "contracts/utils/EthSafeTransfer.sol"; import "contracts/utils/ERC20SafeTransfer.sol"; /** * @notice RewardPool holds all ether and ALCA that is part of reserved amount * of rewards on base positions. * @dev deployed by the lockup contract */ contract RewardPool is AccessControlled, EthSafeTransfer, ERC20SafeTransfer { address internal immutable _alca; address internal immutable _lockupContract; address internal immutable _bonusPool; uint256 internal _ethReserve; uint256 internal _tokenReserve; constructor(address alca_, address aliceNetFactory_, uint256 totalBonusAmount_) { _bonusPool = address( new BonusPool(aliceNetFactory_, msg.sender, address(this), totalBonusAmount_) ); _lockupContract = msg.sender; _alca = alca_; } /// @notice function that receives ether and updates the token and ether reservers. The ALCA /// tokens has to be sent prior the call to this function. /// @dev can only be called by the bonusPool or lockup contracts /// @param numTokens_ number of ALCA tokens transferred to this contract before the call to this /// function function deposit(uint256 numTokens_) public payable onlyLockupOrBonus { _tokenReserve += numTokens_; _ethReserve += msg.value; } /// @notice function to pay a user after the lockup period. If a user is the last exiting the /// lockup it will receive any remainders kept by this contract by integer division errors. /// @dev only can be called by the lockup contract /// @param totalShares_ the total shares at the end of the lockup period /// @param userShares_ the user shares /// @param isLastPosition_ if the user is the last position exiting from the lockup contract function payout( uint256 totalShares_, uint256 userShares_, bool isLastPosition_ ) public onlyLockup returns (uint256 proportionalEth, uint256 proportionalTokens) { if (totalShares_ == 0 || userShares_ > totalShares_) { revert LockupErrors.InvalidTotalSharesValue(); } // last position gets any remainder left on this contract if (isLastPosition_) { proportionalEth = address(this).balance; proportionalTokens = IERC20(_alca).balanceOf(address(this)); } else { (proportionalEth, proportionalTokens) = _computeProportions(totalShares_, userShares_); } _safeTransferERC20(IERC20Transferable(_alca), _lockupContract, proportionalTokens); _safeTransferEth(payable(_lockupContract), proportionalEth); } /// @notice gets the bonusPool contract address /// @return the bonusPool contract address function getBonusPoolAddress() public view returns (address) { return _getBonusPoolAddress(); } /// @notice gets the lockup contract address /// @return the lockup contract address function getLockupContractAddress() public view returns (address) { return _getLockupContractAddress(); } /// @notice get the ALCA reserve kept by this contract /// @return the ALCA reserve kept by this contract function getTokenReserve() public view returns (uint256) { return _tokenReserve; } /// @notice get the ether reserve kept by this contract /// @return the ether reserve kept by this contract function getEthReserve() public view returns (uint256) { return _ethReserve; } /// @notice estimates the final amount that a user will receive from the assets hold by this /// contract after end of the lockup period. /// @param totalShares_ total number of shares locked by the lockup contract /// @param userShares_ the user's shares /// @return proportionalEth The ether that a user will receive at the end of the lockup period /// @return proportionalTokens The ALCA that a user will receive at the end of the lockup period function estimateRewards( uint256 totalShares_, uint256 userShares_ ) public view returns (uint256 proportionalEth, uint256 proportionalTokens) { if (totalShares_ == 0 || userShares_ > totalShares_) { revert LockupErrors.InvalidTotalSharesValue(); } return _computeProportions(totalShares_, userShares_); } function _computeProportions( uint256 totalShares_, uint256 userShares_ ) internal view returns (uint256 proportionalEth, uint256 proportionalTokens) { proportionalEth = (_ethReserve * userShares_) / totalShares_; proportionalTokens = (_tokenReserve * userShares_) / totalShares_; } function _getLockupContractAddress() internal view override returns (address) { return _lockupContract; } function _getBonusPoolAddress() internal view override returns (address) { return _bonusPool; } function _getRewardPoolAddress() internal view override returns (address) { return address(this); } }
// This file is auto-generated by hardhat generate-immutable-auth-contract task. DO NOT EDIT. // SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; import "contracts/utils/DeterministicAddress.sol"; import "contracts/utils/auth/ImmutableFactory.sol"; import "contracts/interfaces/IAliceNetFactory.sol"; abstract contract ImmutableALCA is ImmutableFactory { address private immutable _alca; error OnlyALCA(address sender, address expected); modifier onlyALCA() { if (msg.sender != _alca) { revert OnlyALCA(msg.sender, _alca); } _; } constructor() { _alca = IAliceNetFactory(_factoryAddress()).lookup(_saltForALCA()); } function _alcaAddress() internal view returns (address) { return _alca; } function _saltForALCA() internal pure returns (bytes32) { return 0x414c434100000000000000000000000000000000000000000000000000000000; } }
// This file is auto-generated by hardhat generate-immutable-auth-contract task. DO NOT EDIT. // SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; import "contracts/utils/DeterministicAddress.sol"; abstract contract ImmutableFactory is DeterministicAddress { address private immutable _factory; error OnlyFactory(address sender, address expected); modifier onlyFactory() { if (msg.sender != _factory) { revert OnlyFactory(msg.sender, _factory); } _; } constructor(address factory_) { _factory = factory_; } function _factoryAddress() internal view returns (address) { return _factory; } }
// This file is auto-generated by hardhat generate-immutable-auth-contract task. DO NOT EDIT. // SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; import "contracts/utils/DeterministicAddress.sol"; import "contracts/utils/auth/ImmutableFactory.sol"; abstract contract ImmutableFoundation is ImmutableFactory { address private immutable _foundation; error OnlyFoundation(address sender, address expected); modifier onlyFoundation() { if (msg.sender != _foundation) { revert OnlyFoundation(msg.sender, _foundation); } _; } constructor() { _foundation = getMetamorphicContractAddress( 0x466f756e646174696f6e00000000000000000000000000000000000000000000, _factoryAddress() ); } function _foundationAddress() internal view returns (address) { return _foundation; } function _saltForFoundation() internal pure returns (bytes32) { return 0x466f756e646174696f6e00000000000000000000000000000000000000000000; } }
// This file is auto-generated by hardhat generate-immutable-auth-contract task. DO NOT EDIT. // SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; import "contracts/utils/DeterministicAddress.sol"; import "contracts/utils/auth/ImmutableFactory.sol"; abstract contract ImmutablePublicStaking is ImmutableFactory { address private immutable _publicStaking; error OnlyPublicStaking(address sender, address expected); modifier onlyPublicStaking() { if (msg.sender != _publicStaking) { revert OnlyPublicStaking(msg.sender, _publicStaking); } _; } constructor() { _publicStaking = getMetamorphicContractAddress( 0x5075626c69635374616b696e6700000000000000000000000000000000000000, _factoryAddress() ); } function _publicStakingAddress() internal view returns (address) { return _publicStaking; } function _saltForPublicStaking() internal pure returns (bytes32) { return 0x5075626c69635374616b696e6700000000000000000000000000000000000000; } }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; abstract contract DeterministicAddress { function getMetamorphicContractAddress( bytes32 _salt, address _factory ) public pure returns (address) { // byte code for metamorphic contract // 6020363636335afa1536363636515af43d36363e3d36f3 bytes32 metamorphicContractBytecodeHash_ = 0x1c0bf703a3415cada9785e89e9d70314c3111ae7d8e04f33bb42eb1d264088be; return address( uint160( uint256( keccak256( abi.encodePacked( hex"ff", _factory, _salt, metamorphicContractBytecodeHash_ ) ) ) ) ); } }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; import "contracts/interfaces/IERC20Transferable.sol"; import "contracts/libraries/errors/ERC20SafeTransferErrors.sol"; abstract contract ERC20SafeTransfer { // _safeTransferFromERC20 performs a transferFrom call against an erc20 contract in a safe manner // by reverting on failure // this function will return without performing a call or reverting // if amount_ is zero function _safeTransferFromERC20( IERC20Transferable contract_, address sender_, uint256 amount_ ) internal { if (amount_ == 0) { return; } if (address(contract_) == address(0x0)) { revert ERC20SafeTransferErrors.CannotCallContractMethodsOnZeroAddress(); } bool success = contract_.transferFrom(sender_, address(this), amount_); if (!success) { revert ERC20SafeTransferErrors.Erc20TransferFailed( address(contract_), sender_, address(this), amount_ ); } } // _safeTransferERC20 performs a transfer call against an erc20 contract in a safe manner // by reverting on failure // this function will return without performing a call or reverting // if amount_ is zero function _safeTransferERC20( IERC20Transferable contract_, address to_, uint256 amount_ ) internal { if (amount_ == 0) { return; } if (address(contract_) == address(0x0)) { revert ERC20SafeTransferErrors.CannotCallContractMethodsOnZeroAddress(); } bool success = contract_.transfer(to_, amount_); if (!success) { revert ERC20SafeTransferErrors.Erc20TransferFailed( address(contract_), address(this), to_, amount_ ); } } }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; import "contracts/libraries/errors/ETHSafeTransferErrors.sol"; abstract contract EthSafeTransfer { /// @notice _safeTransferEth performs a transfer of Eth using the call /// method / this function is resistant to breaking gas price changes and / /// performs call in a safe manner by reverting on failure. / this function /// will return without performing a call or reverting, / if amount_ is zero function _safeTransferEth(address to_, uint256 amount_) internal { if (amount_ == 0) { return; } if (to_ == address(0)) { revert ETHSafeTransferErrors.CannotTransferToZeroAddress(); } address payable caller = payable(to_); (bool success, ) = caller.call{value: amount_}(""); if (!success) { revert ETHSafeTransferErrors.EthTransferFailed(address(this), to_, amount_); } } }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; import "contracts/utils/MagicValue.sol"; import "contracts/interfaces/IMagicEthTransfer.sol"; abstract contract MagicEthTransfer is MagicValue { function _safeTransferEthWithMagic(IMagicEthTransfer to_, uint256 amount_) internal { to_.depositEth{value: amount_}(_getMagic()); } }
// SPDX-License-Identifier: MIT-open-group pragma solidity ^0.8.16; import "contracts/libraries/errors/MagicValueErrors.sol"; abstract contract MagicValue { // _MAGIC_VALUE is a constant that may be used to prevent // a user from calling a dangerous method without significant // effort or ( hopefully ) reading the code to understand the risk uint8 internal constant _MAGIC_VALUE = 42; modifier checkMagic(uint8 magic_) { if (magic_ != _getMagic()) { revert MagicValueErrors.BadMagic(magic_); } _; } // _getMagic returns the magic constant function _getMagic() internal pure returns (uint8) { return _MAGIC_VALUE; } }
{ "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 20000 }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"uint256","name":"enrollmentPeriod_","type":"uint256"},{"internalType":"uint256","name":"lockDuration_","type":"uint256"},{"internalType":"uint256","name":"totalBonusAmount_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressAlreadyLockedUp","type":"error"},{"inputs":[],"name":"AddressNotAllowedToSendEther","type":"error"},{"inputs":[],"name":"CannotCallContractMethodsOnZeroAddress","type":"error"},{"inputs":[],"name":"CannotTransferToZeroAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenID_","type":"uint256"}],"name":"ContractDoesNotOwnTokenID","type":"error"},{"inputs":[{"internalType":"address","name":"erc20Address","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Erc20TransferFailed","type":"error"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EthTransferFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"exitValue","type":"uint256"},{"internalType":"uint256","name":"currentBalance","type":"uint256"}],"name":"InsufficientBalanceForEarlyExit","type":"error"},{"inputs":[{"internalType":"uint256","name":"withdrawFreeAfter","type":"uint256"},{"internalType":"uint256","name":"endBlock","type":"uint256"}],"name":"InvalidPositionWithdrawPeriod","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"expected","type":"address"}],"name":"OnlyALCA","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"expected","type":"address"}],"name":"OnlyFactory","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"expected","type":"address"}],"name":"OnlyPublicStaking","type":"error"},{"inputs":[],"name":"OnlyStakingNFTAllowed","type":"error"},{"inputs":[],"name":"PayoutSafe","type":"error"},{"inputs":[],"name":"PayoutUnsafe","type":"error"},{"inputs":[],"name":"PostLockStateNotAllowed","type":"error"},{"inputs":[],"name":"PostLockStateRequired","type":"error"},{"inputs":[],"name":"PreLockStateRequired","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenID_","type":"uint256"}],"name":"TokenIDAlreadyClaimed","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenID_","type":"uint256"}],"name":"TokenIDNotLocked","type":"error"},{"inputs":[],"name":"UserHasNoPosition","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to_","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenID_","type":"uint256"}],"name":"EarlyExit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from_","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenID_","type":"uint256"}],"name":"NewLockup","type":"event"},{"inputs":[],"name":"FRACTION_RESERVED","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SCALING_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"aggregateProfits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectAllProfits","outputs":[{"internalType":"uint256","name":"payoutEth","type":"uint256"},{"internalType":"uint256","name":"payoutToken","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenID_","type":"uint256"},{"internalType":"bool","name":"preciseEstimation_","type":"bool"}],"name":"estimateFinalBonusWithProfits","outputs":[{"internalType":"uint256","name":"positionShares_","type":"uint256"},{"internalType":"uint256","name":"payoutEth_","type":"uint256"},{"internalType":"uint256","name":"payoutToken_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenID_","type":"uint256"}],"name":"estimateProfits","outputs":[{"internalType":"uint256","name":"payoutEth","type":"uint256"},{"internalType":"uint256","name":"payoutToken","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBonusPoolAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentNumberOfLockedPositions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenID_","type":"uint256"}],"name":"getIndexByTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLockupEndBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLockupStartBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_salt","type":"bytes32"},{"internalType":"address","name":"_factory","type":"address"}],"name":"getMetamorphicContractAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"index_","type":"uint256"}],"name":"getPositionByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"getReservedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getReservedPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getRewardPoolAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getState","outputs":[{"internalType":"enum Lockup.State","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"}],"name":"getTemporaryRewardBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalSharesLocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenID_","type":"uint256"}],"name":"lockFromApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenID_","type":"uint256"},{"internalType":"address","name":"tokenOwner_","type":"address"}],"name":"lockFromTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"from_","type":"address"},{"internalType":"uint256","name":"tokenID_","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenID_","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"payoutSafe","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"acct_","type":"address"}],"name":"tokenOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to_","type":"address"},{"internalType":"bool","name":"stakeExit_","type":"bool"}],"name":"unlock","outputs":[{"internalType":"uint256","name":"payoutEth","type":"uint256"},{"internalType":"uint256","name":"payoutToken","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"exitValue_","type":"uint256"},{"internalType":"bool","name":"stakeExit_","type":"bool"}],"name":"unlockEarly","outputs":[{"internalType":"uint256","name":"payoutEth","type":"uint256"},{"internalType":"uint256","name":"payoutToken","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6101606040523480156200001257600080fd5b5060405162004fb938038062004fb983398101604081905262000035916200029e565b336080526200005e6c5075626c69635374616b696e6760981b6200005860805190565b620001fb565b6001600160a01b0390811660a05260805160405163f39ec1f760e01b815263414c434160e01b600482015291169063f39ec1f790602401602060405180830381865afa158015620000b3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000d99190620002cd565b6001600160a01b031660c0526000620000f160c05190565b60805183604051620001039062000290565b6001600160a01b0393841681529290911660208301526040820152606001604051809103906000f0801580156200013e573d6000803e3d6000fd5b509050806001600160a01b031660e0816001600160a01b031681525050806001600160a01b031663b5bd13206040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200019a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001c09190620002cd565b6001600160a01b031661010052620001d98443620002ff565b610120819052620001ec908490620002ff565b61014052506200032192505050565b6040517fff0000000000000000000000000000000000000000000000000000000000000060208201526001600160601b0319606083901b166021820152603581018390527f1c0bf703a3415cada9785e89e9d70314c3111ae7d8e04f33bb42eb1d264088be605582018190526000916075016040516020818303038152906040528051906020012060001c9150505b92915050565b611ee080620030d983390190565b600080600060608486031215620002b457600080fd5b8351925060208401519150604084015190509250925092565b600060208284031215620002e057600080fd5b81516001600160a01b0381168114620002f857600080fd5b9392505050565b808201808211156200028a57634e487b7160e01b600052601160045260246000fd5b60805160a05160c05160e051610100516101205161014051612c9562000444600039600081816103b5015281816117860152818161275601526127aa0152600081816107430152611758015260008181610610015281816111c9015261148f01526000818161020e0152818161067001528181610ef70152818161154e01528181611fb6015261200a01526000818161094501528181611bd601528181611d960152611f950152600081816101cd0152818161087e0152818161098101528181610a7b01528181610ca401528181610d400152818161139b0152818161165f0152818161184e01528181611af301528181611c1201528181611cca01528181611e1501528181612183015281816125f801526126b3015260005050612c956000f3fe6080604052600436106101b05760003560e01c80637fc4e4b1116100ec578063bf0f727c1161008a578063e07d2c4e11610064578063e07d2c4e146106cf578063e1eeec3e146106e4578063ed190bb014610734578063ef4cadc51461076757600080fd5b8063bf0f727c14610634578063cf6c533014610661578063db7898471461069457600080fd5b8063a0e91ed2116100c6578063a0e91ed2146105b7578063a593fc78146105cc578063b30c7d7f146105e1578063b5bd13201461060157600080fd5b80637fc4e4b1146104aa5780638653a465146104bf5780638905564c146105a257600080fd5b80631dd8abd0116101595780633bd769e5116101335780633bd769e51461040357806342ec38e21461041857806344ff7dc3146104385780636352211e1461046557600080fd5b80631dd8abd0146103865780631fe52cb2146103a657806339350ccc146103e357600080fd5b8063150b7a021161018a578063150b7a02146102f35780631865c57d14610344578063187980f11461036657600080fd5b8063089a47821461026f5780630d40bccb146102a957806314567f37146102d357600080fd5b3661026a573373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161480159061023157503373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614155b15610268576040517f525e9a0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561027b57600080fd5b5061028f61028a366004612824565b610783565b604080519283526020830191909152015b60405180910390f35b3480156102b557600080fd5b506008546102c39060ff1681565b60405190151581526020016102a0565b3480156102df57600080fd5b506102686102ee366004612876565b610bd9565b3480156102ff57600080fd5b5061031361030e3660046128ca565b610c39565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016102a0565b34801561035057600080fd5b50610359610d2f565b6040516102a091906129f7565b34801561037257600080fd5b50610268610381366004612a38565b610d3e565b34801561039257600080fd5b5061028f6103a1366004612a51565b610df0565b3480156103b257600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040519081526020016102a0565b3480156103ef57600080fd5b506103d56103fe366004612a38565b610fee565b34801561040f57600080fd5b506103d561101e565b34801561042457600080fd5b506103d5610433366004612a7f565b611049565b34801561044457600080fd5b506103d5610453366004612a38565b60009081526003602052604090205490565b34801561047157600080fd5b50610485610480366004612a38565b611074565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102a0565b3480156104b657600080fd5b5061026861109e565b3480156104cb57600080fd5b506104856104da366004612876565b6040517fff0000000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b166021820152603581018390527f1c0bf703a3415cada9785e89e9d70314c3111ae7d8e04f33bb42eb1d264088be60558201819052600091607501604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120949350505050565b3480156105ae57600080fd5b506005546103d5565b3480156105c357600080fd5b5061028f6112c7565b3480156105d857600080fd5b506103d5611335565b3480156105ed57600080fd5b5061028f6105fc366004612a38565b61134b565b34801561060d57600080fd5b507f0000000000000000000000000000000000000000000000000000000000000000610485565b34801561064057600080fd5b506103d561064f366004612a38565b60009081526004602052604090205490565b34801561066d57600080fd5b507f0000000000000000000000000000000000000000000000000000000000000000610485565b3480156106a057600080fd5b506106b46106af366004612824565b61143b565b604080519384526020840192909252908201526060016102a0565b3480156106db57600080fd5b506000546103d5565b3480156106f057600080fd5b5061028f6106ff366004612a7f565b73ffffffffffffffffffffffffffffffffffffffff166000908152600660209081526040808320546007909252909120549091565b34801561074057600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006103d5565b34801561077357600080fd5b506103d5670de0b6b3a764000081565b6000806002610790611754565b60028111156107a1576107a16129c8565b036107d8576040517fab2d593500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107e26117b8565b905060006107ef82611807565b90508086111561083a576040517fe33b0e3400000000000000000000000000000000000000000000000000000000815260048101879052602481018290526044015b60405180910390fd5b6040517f42966c680000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906342966c689060240160408051808303816000875af11580156108c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ea9190612aa3565b90945092506108f98184612af6565b600083815260016020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690559093508061093b8884612af6565b90508015610b4f577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663095ea7b37f00000000000000000000000000000000000000000000000000000000000000006040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602481018490526044016020604051808303816000875af1158015610a12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a369190612b09565b506040517fa0712d680000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063a0712d68906024016020604051808303816000875af1158015610ac4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ae89190612b26565b600081815260016020908152604080832080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790558783526004808352818420805490859055808552600384528285208690558585529252909120559150610b58565b610b58846118c4565b33600090815260026020526040812083905580548991908190610b7c908490612af6565b90915550610b8f90503387878b8b611974565b60408051338152602081018890529298509096507fd48329b60fb879cd966b0702df731363e4402bac1b144c525e02dd02b9873485910160405180910390a1505050509250929050565b6000610be3611754565b6002811115610bf457610bf46129c8565b14610c2b576040517fb12988f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c358282611a8d565b5050565b600080610c44611754565b6002811115610c5557610c556129c8565b14610c8c576040517fb12988f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610cfb576040517f4b07173500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d058385611a8d565b507f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6000610d39611754565b905090565b7f00000000000000000000000000000000000000000000000000000000000000006040517f42842e0e0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810183905273ffffffffffffffffffffffffffffffffffffffff91909116906342842e0e90606401600060405180830381600087803b158015610dd557600080fd5b505af1158015610de9573d6000803e3d6000fd5b5050505050565b6000806002610dfd611754565b6002811115610e0e57610e0e6129c8565b14610e45576040517f10ca2bad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60085460ff16610e81576040517f6d49151100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610e8b6117b8565b90506000610e9882611807565b600554909150600114610eab8333611aaa565b600080546040517fa77ead1600000000000000000000000000000000000000000000000000000000815260048101919091526024810186905284151560448201529297509095509081907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a77ead169060640160408051808303816000875af1158015610f54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f789190612aa3565b9092509050610f878288612b3f565b9650610f938187612b3f565b3360009081526006602090815260408083208054600790935290832080549184905592909255919750610fc6828a612b3f565b9850610fd28189612b3f565b9750610fe08b8a8a8d611bce565b505050505050509250929050565b6000670de0b6b3a7640000611004600582612b52565b61100e9084612b8d565b6110189190612b52565b92915050565b6000670de0b6b3a7640000611034600582612b52565b61103f906064612b8d565b610d399190612b52565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260026020526040812054611018565b60008181526001602052604081205473ffffffffffffffffffffffffffffffffffffffff16611018565b60085460ff16156110db576040517f0e672def00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60026110e5611754565b60028111156110f6576110f66129c8565b1461112d576040517f10ca2bad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005a905060008060095460016111449190612b3f565b90505b6000818152600360205260409020548015158061122f57600880547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055604080517f0c08bf88000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691630c08bf8891600480830192600092919082900301818387803b15801561121057600080fd5b505af1158015611224573d6000803e3d6000fd5b5050505050506112c0565b60008281526001602052604090205473ffffffffffffffffffffffffffffffffffffffff1661125e8184611dcc565b505060005a905085600003611299576112778188612af6565b9550611284866005612b8d565b955061129286612710612b3f565b95506112a9565b8581116112a957505050506112c0565b5050505080806112b890612bca565b915050611147565b6009555050565b60008060026112d4611754565b60028111156112e5576112e56129c8565b0361131c576040517fab2d593500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61132d336113286117b8565b611dcc565b915091509091565b6113486005670de0b6b3a7640000612b52565b81565b60008061135783611e9f565b6040517f4fc8638f0000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634fc8638f906024016040805180830381865afa1580156113e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114059190612aa3565b90925090506000806114178484611f00565b90925090506114268285612af6565b93506114328184612af6565b92505050915091565b600080600061144985611e9f565b61145285611807565b6000546040517f1aee09a00000000000000000000000000000000000000000000000000000000081526004810182905260248101839052919450907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690631aee09a0906044016040805180830381865afa1580156114ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061150e9190612aa3565b6040517f60df82970000000000000000000000000000000000000000000000000000000081526004810184905260248101879052919450925060009081907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906360df8297906044016040805180830381865afa1580156115a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115cd9190612aa3565b90925090506115dc8286612b3f565b94506115e88185612b3f565b93506000808880156115fd575060085460ff16155b156116165761160c8886611f5e565b90925090506116df565b6040517f4fc8638f000000000000000000000000000000000000000000000000000000008152600481018b9052600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634fc8638f906024016040805180830381865afa1580156116a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116c99190612aa3565b915091506116d78282611f00565b909450925050505b6116e98288612b3f565b96506116f58187612b3f565b95506000806117296106ff8d60009081526001602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b9092509050611738828a612b3f565b98506117448189612b3f565b9750505050505050509250925092565b60007f00000000000000000000000000000000000000000000000000000000000000004310156117845750600090565b7f00000000000000000000000000000000000000000000000000000000000000004310156117b25750600190565b50600290565b3360009081526002602052604081205480600003611802576040517f8efe8f0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b919050565b6040517feb02c3010000000000000000000000000000000000000000000000000000000081526004810182905260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063eb02c3019060240160a060405180830381865afa158015611895573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118b99190612c02565b509295945050505050565b60055460008190036118d4575050565b8060010361190557506000908152600460209081526040808320805490849055835260039091528120819055600555565b60008181526003602052604081208054919055611923600183612af6565b600555828103611940576000908152600460205260408120555050565b6000838152600460208181526040808420805490859055808552600383528185208690558585529290915290912055505050565b6000806000611981611754565b60085488945087935090915060ff1660008061199d8686611f00565b90925090506119ac8287612af6565b95506119b88186612af6565b94506119c48282611f90565b821580156119e3575060028460028111156119e1576119e16129c8565b145b15611a665773ffffffffffffffffffffffffffffffffffffffff8b1660009081526006602052604081208054889290611a1d908490612b3f565b909155505073ffffffffffffffffffffffffffffffffffffffff8b1660009081526007602052604081208054879290611a57908490612b3f565b90915550611a83945050505050565b611a708886612b3f565b9450611a7e8b87878a611bce565b505050505b9550959350505050565b611a978282612081565b611aa08261213d565b610c35828261223e565b6040517f42966c6800000000000000000000000000000000000000000000000000000000815260048101839052600090819073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906342966c689060240160408051808303816000875af1158015611b3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b5f9190612aa3565b9092509050611b6d846118c4565b73ffffffffffffffffffffffffffffffffffffffff9092166000908152600260209081526040808320839055948252600190529290922080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690559091565b8015611d91577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663095ea7b37f00000000000000000000000000000000000000000000000000000000000000006040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602481018590526044016020604051808303816000875af1158015611ca3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc79190612b09565b507f00000000000000000000000000000000000000000000000000000000000000006040517f2baf2acb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015260248201859052600060448301529190911690632baf2acb906064016020604051808303816000875af1158015611d67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d8b9190612b26565b50611dbc565b611dbc7f0000000000000000000000000000000000000000000000000000000000000000858461231c565b611dc68484612479565b50505050565b6040517f0df4b2dd00000000000000000000000000000000000000000000000000000000815260048101829052600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690630df4b2dd9060240160408051808303816000875af1158015611e5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e819190612aa3565b9092509050611e94848383600080611974565b915091509250929050565b60008181526001602052604090205473ffffffffffffffffffffffffffffffffffffffff16611efd576040517ff85aa73400000000000000000000000000000000000000000000000000000000815260048101829052602401610831565b50565b600080670de0b6b3a7640000611f17600582612b52565b611f219086612b8d565b611f2b9190612b52565b9150670de0b6b3a7640000611f41600582612b52565b611f4b9085612b8d565b611f559190612b52565b90509250929050565b600080611f69612591565b909250905082611f798584612b8d565b611f839190612b52565b915082611f4b8583612b8d565b611fdb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008361231c565b6040517fb6b55f25000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b6b55f259084906024016000604051808303818588803b15801561206457600080fd5b505af1158015612078573d6000803e3d6000fd5b50505050505050565b60008281526001602052604090205473ffffffffffffffffffffffffffffffffffffffff16156120e0576040517fd9edd04700000000000000000000000000000000000000000000000000000000815260048101839052602401610831565b73ffffffffffffffffffffffffffffffffffffffff811660009081526002602052604090205415610c35576040517fa616504400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f6352211e00000000000000000000000000000000000000000000000000000000815260048101829052309073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690636352211e90602401602060405180830381865afa1580156121ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121ee9190612c42565b73ffffffffffffffffffffffffffffffffffffffff1614611efd576040517f45131cda00000000000000000000000000000000000000000000000000000000815260048101829052602401610831565b6000612249836126ad565b90508060008082825461225c9190612b3f565b909155505073ffffffffffffffffffffffffffffffffffffffff821660008181526002602090815260408083208790558683526001909152902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690911790556122c8836127dd565b6040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018590527f737fabc26fafc74f523485e7fe4e50fc50cf8e93822e60768c66970d8a19a534910160405180910390a1505050565b8060000361232957505050565b73ffffffffffffffffffffffffffffffffffffffff8316612376576040517f2518928d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390526000919085169063a9059cbb906044016020604051808303816000875af11580156123ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124139190612b09565b905080611dc6576040517ff0798c5100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff80861660048301523060248301528416604482015260648101839052608401610831565b80600003612485575050565b73ffffffffffffffffffffffffffffffffffffffff82166124d2576040517f6f5ec9ea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051829060009073ffffffffffffffffffffffffffffffffffffffff83169084908381818185875af1925050503d806000811461252c576040519150601f19603f3d011682016040523d82523d6000602084013e612531565b606091505b5050905080611dc6576040517fb250066300000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8516602482015260448101849052606401610831565b60008060015b60055481116126a8576000818152600360205260408082205490517f4fc8638f00000000000000000000000000000000000000000000000000000000815260048101829052909190819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634fc8638f906024016040805180830381865afa15801561263e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126629190612aa3565b915091506000806126738484611f00565b90925090506126828289612b3f565b975061268e8188612b3f565b9650505050505080806126a090612bca565b915050612597565b509091565b600080807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663eb02c301856040518263ffffffff1660e01b815260040161270c91815260200190565b60a060405180830381865afa158015612729573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061274d9190612c02565b505092505091507f000000000000000000000000000000000000000000000000000000000000000081106127d6576040517ffddbcf09000000000000000000000000000000000000000000000000000000008152600481018290527f00000000000000000000000000000000000000000000000000000000000000006024820152604401610831565b5092915050565b600060055460016127ee9190612b3f565b6000818152600360209081526040808320869055948252600490529290922082905550600555565b8015158114611efd57600080fd5b6000806040838503121561283757600080fd5b82359150602083013561284981612816565b809150509250929050565b73ffffffffffffffffffffffffffffffffffffffff81168114611efd57600080fd5b6000806040838503121561288957600080fd5b82359150602083013561284981612854565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600080608085870312156128e057600080fd5b84356128eb81612854565b935060208501356128fb81612854565b925060408501359150606085013567ffffffffffffffff8082111561291f57600080fd5b818701915087601f83011261293357600080fd5b8135818111156129455761294561289b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561298b5761298b61289b565b816040528281528a60208487010111156129a457600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6020810160038310612a32577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91905290565b600060208284031215612a4a57600080fd5b5035919050565b60008060408385031215612a6457600080fd5b8235612a6f81612854565b9150602083013561284981612816565b600060208284031215612a9157600080fd5b8135612a9c81612854565b9392505050565b60008060408385031215612ab657600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561101857611018612ac7565b600060208284031215612b1b57600080fd5b8151612a9c81612816565b600060208284031215612b3857600080fd5b5051919050565b8082018082111561101857611018612ac7565b600082612b88577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612bc557612bc5612ac7565b500290565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612bfb57612bfb612ac7565b5060010190565b600080600080600060a08688031215612c1a57600080fd5b5050835160208501516040860151606087015160809097015192989197509594509092509050565b600060208284031215612c5457600080fd5b8151612a9c8161285456fea26469706673582212206070eda80c6dea6bc16e0a4f72b7debb9564c6a3833fc46c824241182e9846f664736f6c6343000810003360e060405234801561001057600080fd5b50604051611ee0380380611ee083398101604081905261002f916100cd565b8133308360405161003f906100a4565b6001600160a01b0394851681529284166020840152921660408201526060810191909152608001604051809103906000f080158015610082573d6000803e3d6000fd5b506001600160a01b0390811660c0523360a05292909216608052506101099050565b61142880610ab883390190565b80516001600160a01b03811681146100c857600080fd5b919050565b6000806000606084860312156100e257600080fd5b6100eb846100b1565b92506100f9602085016100b1565b9150604084015190509250925092565b60805160a05160c0516109576101616000396000818160fd015261045301526000818161016601528181610215015281816103a3015281816103cd01526104120152600081816102e6015261038201526109576000f3fe6080604052600436106100705760003560e01c8063b5bd13201161004e578063b5bd1320146100ee578063b6b55f2514610142578063cfbb72e014610157578063f1d9f88b1461018a57600080fd5b806360df8297146100755780638a9118c0146100af578063a77ead16146100ce575b600080fd5b34801561008157600080fd5b5061009561009036600461079f565b61019f565b604080519283526020830191909152015b60405180910390f35b3480156100bb57600080fd5b506000545b6040519081526020016100a6565b3480156100da57600080fd5b506100956100e93660046107d2565b6101fa565b3480156100fa57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100a6565b61015561015036600461080b565b6103fa565b005b34801561016357600080fd5b507f000000000000000000000000000000000000000000000000000000000000000061011d565b34801561019657600080fd5b506001546100c0565b6000808315806101ae57508383115b156101e5576040517fa96b2ea000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101ef84846104df565b915091509250929050565b6000803373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461026c576040517f52177e1400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84158061027857508484115b156102af576040517fa96b2ea000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b821561036d576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201524792507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610342573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103669190610824565b905061037d565b61037785856104df565b90925090505b6103c87f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008361051f565b6103f27f000000000000000000000000000000000000000000000000000000000000000083610687565b935093915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161480159061047657503373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614155b156104ad576040517f5771207d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600160008282546104bf919061086c565b92505081905550346000808282546104d7919061086c565b909155505050565b60008083836000546104f19190610885565b6104fb91906108c2565b9150838360015461050c9190610885565b61051691906108c2565b90509250929050565b8060000361052c57505050565b73ffffffffffffffffffffffffffffffffffffffff8316610579576040517f2518928d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390526000919085169063a9059cbb906044016020604051808303816000875af11580156105f2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061691906108fd565b905080610681576040517ff0798c5100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808616600483015230602483015284166044820152606481018390526084015b60405180910390fd5b50505050565b80600003610693575050565b73ffffffffffffffffffffffffffffffffffffffff82166106e0576040517f6f5ec9ea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051829060009073ffffffffffffffffffffffffffffffffffffffff83169084908381818185875af1925050503d806000811461073a576040519150601f19603f3d011682016040523d82523d6000602084013e61073f565b606091505b5050905080610681576040517fb250066300000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8516602482015260448101849052606401610678565b600080604083850312156107b257600080fd5b50508035926020909101359150565b80151581146107cf57600080fd5b50565b6000806000606084860312156107e757600080fd5b83359250602084013591506040840135610800816107c1565b809150509250925092565b60006020828403121561081d57600080fd5b5035919050565b60006020828403121561083657600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561087f5761087f61083d565b92915050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156108bd576108bd61083d565b500290565b6000826108f8577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60006020828403121561090f57600080fd5b815161091a816107c1565b939250505056fea26469706673582212208c1f8fc3d6fb83a7bf6522ed04ad4f00d9c49e37805e38d28ee22e53f7f80c5064736f6c634300081000336101606040523480156200001257600080fd5b5060405162001428380380620014288339810160408190526200003591620001d5565b6001600160a01b038416608081905260405163f39ec1f760e01b815263414c434160e01b600482015263f39ec1f790602401602060405180830381865afa15801562000085573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ab919062000227565b6001600160a01b031660a052620001686c5075626c69635374616b696e6760981b620000d660805190565b6040517fff0000000000000000000000000000000000000000000000000000000000000060208201526001600160601b0319606083901b166021820152603581018390527f1c0bf703a3415cada9785e89e9d70314c3111ae7d8e04f33bb42eb1d264088be6055820181905260009160750160408051601f198184030181529190528051602090910120949350505050565b6001600160a01b031660c05262000190692337bab73230ba34b7b760b11b620000d660805190565b6001600160a01b0390811660e05261010091909152918216610120521661014052506200024c565b80516001600160a01b0381168114620001d057600080fd5b919050565b60008060008060808587031215620001ec57600080fd5b620001f785620001b8565b93506200020760208601620001b8565b92506200021760408601620001b8565b6060959095015193969295505050565b6000602082840312156200023a57600080fd5b6200024582620001b8565b9392505050565b60805160a05160c05160e05161010051610120516101405161111c6200030c6000396000818161027f0152818161052a01526105950152600081816102b201526103030152600081816102420152818161094d015281816109a201528181610a560152610b0c0152600050506000818160b7015281816103de01528181610614015281816106ba015281816109ec0152610ac50152600081816104530152818161050901526108960152600081816107be0152610826015261111c6000f3fe60806040526004361061009a5760003560e01c80638653a46511610069578063cf6c53301161004e578063cf6c533014610270578063cfbb72e0146102a3578063eff6d617146102d657600080fd5b80638653a465146101ee57806391e8e20f1461023357600080fd5b80630c08bf8814610115578063150b7a021461012a5780631aee09a0146101a4578063646279cd146101d957600080fd5b36610110573373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461010e576040517f525e9a0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561012157600080fd5b5061010e6102eb565b34801561013657600080fd5b5061016e610145366004610e3e565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020015b60405180910390f35b3480156101b057600080fd5b506101c46101bf366004610f38565b6105f7565b6040805192835260208301919091520161019b565b3480156101e557600080fd5b5061010e6107a6565b3480156101fa57600080fd5b5061020e610209366004610f5a565b610bd6565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161019b565b34801561023f57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405190815260200161019b565b34801561027c57600080fd5b507f000000000000000000000000000000000000000000000000000000000000000061020e565b3480156102af57600080fd5b507f000000000000000000000000000000000000000000000000000000000000000061020e565b3480156102e257600080fd5b50600054610262565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461035a576040517f52177e1400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600054600003610396576040517fca2d357200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000546040517f42966c68000000000000000000000000000000000000000000000000000000008152600481019190915273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906342966c689060240160408051808303816000875af1158015610426573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061044a9190610f86565b505060008080557f00000000000000000000000000000000000000000000000000000000000000006040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa1580156104de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105029190610faa565b905061054f7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083610c83565b6040517fb6b55f2500000000000000000000000000000000000000000000000000000000815260048101829052479073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063b6b55f259083906024016000604051808303818588803b1580156105da57600080fd5b505af11580156105ee573d6000803e3d6000fd5b50505050505050565b60008060005460000361060f5750600090508061079f565b6000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16634fc8638f6000546040518263ffffffff1660e01b815260040161066f91815260200190565b6040805180830381865afa15801561068b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106af9190610f86565b9150915060006106dc7f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff1663eb02c3016000546040518263ffffffff1660e01b815260040161071891815260200190565b60a060405180830381865afa158015610735573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107599190610fc3565b505050509050808261076b9190611032565b9150866107788785611045565b6107829190611082565b94508661078f8784611045565b6107999190611082565b93505050505b9250929050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610858576040517f0f634fbe00000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660248201526044015b60405180910390fd5b60005415610892576040517f0f12873500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60007f00000000000000000000000000000000000000000000000000000000000000006040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290915060009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610925573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109499190610faa565b90507f00000000000000000000000000000000000000000000000000000000000000008110156109ce576040517f3bd7532b000000000000000000000000000000000000000000000000000000008152600481018290527f0000000000000000000000000000000000000000000000000000000000000000602482015260440161084f565b73ffffffffffffffffffffffffffffffffffffffff821663095ea7b37f00000000000000000000000000000000000000000000000000000000000000006040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000060248201526044016020604051808303816000875af1158015610a9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ac091906110bd565b5060007f00000000000000000000000000000000000000000000000000000000000000006040517fa0712d680000000000000000000000000000000000000000000000000000000081527f0000000000000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff919091169063a0712d68906024016020604051808303816000875af1158015610b72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b969190610faa565b60008190556040518181529091507fda02726d9c28425343ec682e51934569d02e0c9143e4b3d11c0019d29c4e73609060200160405180910390a1505050565b6040517fff0000000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b166021820152603581018390527f1c0bf703a3415cada9785e89e9d70314c3111ae7d8e04f33bb42eb1d264088be605582018190526000916075016040516020818303038152906040528051906020012060001c9150505b92915050565b80600003610c9057505050565b73ffffffffffffffffffffffffffffffffffffffff8316610cdd576040517f2518928d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390526000919085169063a9059cbb906044016020604051808303816000875af1158015610d56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7a91906110bd565b905080610de0576040517ff0798c5100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8086166004830152306024830152841660448201526064810183905260840161084f565b50505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610e0a57600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060008060808587031215610e5457600080fd5b610e5d85610de6565b9350610e6b60208601610de6565b925060408501359150606085013567ffffffffffffffff80821115610e8f57600080fd5b818701915087601f830112610ea357600080fd5b813581811115610eb557610eb5610e0f565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715610efb57610efb610e0f565b816040528281528a6020848701011115610f1457600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b60008060408385031215610f4b57600080fd5b50508035926020909101359150565b60008060408385031215610f6d57600080fd5b82359150610f7d60208401610de6565b90509250929050565b60008060408385031215610f9957600080fd5b505080516020909101519092909150565b600060208284031215610fbc57600080fd5b5051919050565b600080600080600060a08688031215610fdb57600080fd5b5050835160208501516040860151606087015160809097015192989197509594509092509050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610c7d57610c7d611003565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561107d5761107d611003565b500290565b6000826110b8577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000602082840312156110cf57600080fd5b815180151581146110df57600080fd5b939250505056fea264697066735822122024852d43de685ef3d02e2a26099bb32d4c64c63dea50f8f4845af92b190d955464736f6c63430008100033000000000000000000000000000000000000000000000000000000000009e34000000000000000000000000000000000000000000000000000000000002819a000000000000000000000000000000000000000000000be951906eba2aa800000
Deployed Bytecode
0x6080604052600436106101b05760003560e01c80637fc4e4b1116100ec578063bf0f727c1161008a578063e07d2c4e11610064578063e07d2c4e146106cf578063e1eeec3e146106e4578063ed190bb014610734578063ef4cadc51461076757600080fd5b8063bf0f727c14610634578063cf6c533014610661578063db7898471461069457600080fd5b8063a0e91ed2116100c6578063a0e91ed2146105b7578063a593fc78146105cc578063b30c7d7f146105e1578063b5bd13201461060157600080fd5b80637fc4e4b1146104aa5780638653a465146104bf5780638905564c146105a257600080fd5b80631dd8abd0116101595780633bd769e5116101335780633bd769e51461040357806342ec38e21461041857806344ff7dc3146104385780636352211e1461046557600080fd5b80631dd8abd0146103865780631fe52cb2146103a657806339350ccc146103e357600080fd5b8063150b7a021161018a578063150b7a02146102f35780631865c57d14610344578063187980f11461036657600080fd5b8063089a47821461026f5780630d40bccb146102a957806314567f37146102d357600080fd5b3661026a573373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f09161480159061023157503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000744387e5abcde8831f31df35480c848754e866631614155b15610268576040517f525e9a0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561027b57600080fd5b5061028f61028a366004612824565b610783565b604080519283526020830191909152015b60405180910390f35b3480156102b557600080fd5b506008546102c39060ff1681565b60405190151581526020016102a0565b3480156102df57600080fd5b506102686102ee366004612876565b610bd9565b3480156102ff57600080fd5b5061031361030e3660046128ca565b610c39565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016102a0565b34801561035057600080fd5b50610359610d2f565b6040516102a091906129f7565b34801561037257600080fd5b50610268610381366004612a38565b610d3e565b34801561039257600080fd5b5061028f6103a1366004612a51565b610df0565b3480156103b257600080fd5b507f000000000000000000000000000000000000000000000000000000000128b8905b6040519081526020016102a0565b3480156103ef57600080fd5b506103d56103fe366004612a38565b610fee565b34801561040f57600080fd5b506103d561101e565b34801561042457600080fd5b506103d5610433366004612a7f565b611049565b34801561044457600080fd5b506103d5610453366004612a38565b60009081526003602052604090205490565b34801561047157600080fd5b50610485610480366004612a38565b611074565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102a0565b3480156104b657600080fd5b5061026861109e565b3480156104cb57600080fd5b506104856104da366004612876565b6040517fff0000000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b166021820152603581018390527f1c0bf703a3415cada9785e89e9d70314c3111ae7d8e04f33bb42eb1d264088be60558201819052600091607501604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120949350505050565b3480156105ae57600080fd5b506005546103d5565b3480156105c357600080fd5b5061028f6112c7565b3480156105d857600080fd5b506103d5611335565b3480156105ed57600080fd5b5061028f6105fc366004612a38565b61134b565b34801561060d57600080fd5b507f000000000000000000000000046e9274572b3e42d3665179d5189c6c4e13cf4f610485565b34801561064057600080fd5b506103d561064f366004612a38565b60009081526004602052604090205490565b34801561066d57600080fd5b507f000000000000000000000000744387e5abcde8831f31df35480c848754e86663610485565b3480156106a057600080fd5b506106b46106af366004612824565b61143b565b604080519384526020840192909252908201526060016102a0565b3480156106db57600080fd5b506000546103d5565b3480156106f057600080fd5b5061028f6106ff366004612a7f565b73ffffffffffffffffffffffffffffffffffffffff166000908152600660209081526040808320546007909252909120549091565b34801561074057600080fd5b507f0000000000000000000000000000000000000000000000000000000001009ef06103d5565b34801561077357600080fd5b506103d5670de0b6b3a764000081565b6000806002610790611754565b60028111156107a1576107a16129c8565b036107d8576040517fab2d593500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107e26117b8565b905060006107ef82611807565b90508086111561083a576040517fe33b0e3400000000000000000000000000000000000000000000000000000000815260048101879052602481018290526044015b60405180910390fd5b6040517f42966c680000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f0916906342966c689060240160408051808303816000875af11580156108c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ea9190612aa3565b90945092506108f98184612af6565b600083815260016020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690559093508061093b8884612af6565b90508015610b4f577f000000000000000000000000bb556b0ee2cbd89ed95ddea881477723a3aa8f8b73ffffffffffffffffffffffffffffffffffffffff1663095ea7b37f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f096040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602481018490526044016020604051808303816000875af1158015610a12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a369190612b09565b506040517fa0712d680000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f09169063a0712d68906024016020604051808303816000875af1158015610ac4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ae89190612b26565b600081815260016020908152604080832080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790558783526004808352818420805490859055808552600384528285208690558585529252909120559150610b58565b610b58846118c4565b33600090815260026020526040812083905580548991908190610b7c908490612af6565b90915550610b8f90503387878b8b611974565b60408051338152602081018890529298509096507fd48329b60fb879cd966b0702df731363e4402bac1b144c525e02dd02b9873485910160405180910390a1505050509250929050565b6000610be3611754565b6002811115610bf457610bf46129c8565b14610c2b576040517fb12988f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c358282611a8d565b5050565b600080610c44611754565b6002811115610c5557610c556129c8565b14610c8c576040517fb12988f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f091614610cfb576040517f4b07173500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d058385611a8d565b507f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6000610d39611754565b905090565b7f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f096040517f42842e0e0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810183905273ffffffffffffffffffffffffffffffffffffffff91909116906342842e0e90606401600060405180830381600087803b158015610dd557600080fd5b505af1158015610de9573d6000803e3d6000fd5b5050505050565b6000806002610dfd611754565b6002811115610e0e57610e0e6129c8565b14610e45576040517f10ca2bad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60085460ff16610e81576040517f6d49151100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610e8b6117b8565b90506000610e9882611807565b600554909150600114610eab8333611aaa565b600080546040517fa77ead1600000000000000000000000000000000000000000000000000000000815260048101919091526024810186905284151560448201529297509095509081907f000000000000000000000000744387e5abcde8831f31df35480c848754e8666373ffffffffffffffffffffffffffffffffffffffff169063a77ead169060640160408051808303816000875af1158015610f54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f789190612aa3565b9092509050610f878288612b3f565b9650610f938187612b3f565b3360009081526006602090815260408083208054600790935290832080549184905592909255919750610fc6828a612b3f565b9850610fd28189612b3f565b9750610fe08b8a8a8d611bce565b505050505050509250929050565b6000670de0b6b3a7640000611004600582612b52565b61100e9084612b8d565b6110189190612b52565b92915050565b6000670de0b6b3a7640000611034600582612b52565b61103f906064612b8d565b610d399190612b52565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260026020526040812054611018565b60008181526001602052604081205473ffffffffffffffffffffffffffffffffffffffff16611018565b60085460ff16156110db576040517f0e672def00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60026110e5611754565b60028111156110f6576110f66129c8565b1461112d576040517f10ca2bad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005a905060008060095460016111449190612b3f565b90505b6000818152600360205260409020548015158061122f57600880547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055604080517f0c08bf88000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000046e9274572b3e42d3665179d5189c6c4e13cf4f1691630c08bf8891600480830192600092919082900301818387803b15801561121057600080fd5b505af1158015611224573d6000803e3d6000fd5b5050505050506112c0565b60008281526001602052604090205473ffffffffffffffffffffffffffffffffffffffff1661125e8184611dcc565b505060005a905085600003611299576112778188612af6565b9550611284866005612b8d565b955061129286612710612b3f565b95506112a9565b8581116112a957505050506112c0565b5050505080806112b890612bca565b915050611147565b6009555050565b60008060026112d4611754565b60028111156112e5576112e56129c8565b0361131c576040517fab2d593500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61132d336113286117b8565b611dcc565b915091509091565b6113486005670de0b6b3a7640000612b52565b81565b60008061135783611e9f565b6040517f4fc8638f0000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f091690634fc8638f906024016040805180830381865afa1580156113e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114059190612aa3565b90925090506000806114178484611f00565b90925090506114268285612af6565b93506114328184612af6565b92505050915091565b600080600061144985611e9f565b61145285611807565b6000546040517f1aee09a00000000000000000000000000000000000000000000000000000000081526004810182905260248101839052919450907f000000000000000000000000046e9274572b3e42d3665179d5189c6c4e13cf4f73ffffffffffffffffffffffffffffffffffffffff1690631aee09a0906044016040805180830381865afa1580156114ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061150e9190612aa3565b6040517f60df82970000000000000000000000000000000000000000000000000000000081526004810184905260248101879052919450925060009081907f000000000000000000000000744387e5abcde8831f31df35480c848754e8666373ffffffffffffffffffffffffffffffffffffffff16906360df8297906044016040805180830381865afa1580156115a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115cd9190612aa3565b90925090506115dc8286612b3f565b94506115e88185612b3f565b93506000808880156115fd575060085460ff16155b156116165761160c8886611f5e565b90925090506116df565b6040517f4fc8638f000000000000000000000000000000000000000000000000000000008152600481018b9052600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f091690634fc8638f906024016040805180830381865afa1580156116a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116c99190612aa3565b915091506116d78282611f00565b909450925050505b6116e98288612b3f565b96506116f58187612b3f565b95506000806117296106ff8d60009081526001602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b9092509050611738828a612b3f565b98506117448189612b3f565b9750505050505050509250925092565b60007f0000000000000000000000000000000000000000000000000000000001009ef04310156117845750600090565b7f000000000000000000000000000000000000000000000000000000000128b8904310156117b25750600190565b50600290565b3360009081526002602052604081205480600003611802576040517f8efe8f0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b919050565b6040517feb02c3010000000000000000000000000000000000000000000000000000000081526004810182905260009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f09169063eb02c3019060240160a060405180830381865afa158015611895573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118b99190612c02565b509295945050505050565b60055460008190036118d4575050565b8060010361190557506000908152600460209081526040808320805490849055835260039091528120819055600555565b60008181526003602052604081208054919055611923600183612af6565b600555828103611940576000908152600460205260408120555050565b6000838152600460208181526040808420805490859055808552600383528185208690558585529290915290912055505050565b6000806000611981611754565b60085488945087935090915060ff1660008061199d8686611f00565b90925090506119ac8287612af6565b95506119b88186612af6565b94506119c48282611f90565b821580156119e3575060028460028111156119e1576119e16129c8565b145b15611a665773ffffffffffffffffffffffffffffffffffffffff8b1660009081526006602052604081208054889290611a1d908490612b3f565b909155505073ffffffffffffffffffffffffffffffffffffffff8b1660009081526007602052604081208054879290611a57908490612b3f565b90915550611a83945050505050565b611a708886612b3f565b9450611a7e8b87878a611bce565b505050505b9550959350505050565b611a978282612081565b611aa08261213d565b610c35828261223e565b6040517f42966c6800000000000000000000000000000000000000000000000000000000815260048101839052600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f0916906342966c689060240160408051808303816000875af1158015611b3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b5f9190612aa3565b9092509050611b6d846118c4565b73ffffffffffffffffffffffffffffffffffffffff9092166000908152600260209081526040808320839055948252600190529290922080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690559091565b8015611d91577f000000000000000000000000bb556b0ee2cbd89ed95ddea881477723a3aa8f8b73ffffffffffffffffffffffffffffffffffffffff1663095ea7b37f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f096040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602481018590526044016020604051808303816000875af1158015611ca3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc79190612b09565b507f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f096040517f2baf2acb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015260248201859052600060448301529190911690632baf2acb906064016020604051808303816000875af1158015611d67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d8b9190612b26565b50611dbc565b611dbc7f000000000000000000000000bb556b0ee2cbd89ed95ddea881477723a3aa8f8b858461231c565b611dc68484612479565b50505050565b6040517f0df4b2dd00000000000000000000000000000000000000000000000000000000815260048101829052600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f091690630df4b2dd9060240160408051808303816000875af1158015611e5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e819190612aa3565b9092509050611e94848383600080611974565b915091509250929050565b60008181526001602052604090205473ffffffffffffffffffffffffffffffffffffffff16611efd576040517ff85aa73400000000000000000000000000000000000000000000000000000000815260048101829052602401610831565b50565b600080670de0b6b3a7640000611f17600582612b52565b611f219086612b8d565b611f2b9190612b52565b9150670de0b6b3a7640000611f41600582612b52565b611f4b9085612b8d565b611f559190612b52565b90509250929050565b600080611f69612591565b909250905082611f798584612b8d565b611f839190612b52565b915082611f4b8583612b8d565b611fdb7f000000000000000000000000bb556b0ee2cbd89ed95ddea881477723a3aa8f8b7f000000000000000000000000744387e5abcde8831f31df35480c848754e866638361231c565b6040517fb6b55f25000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000744387e5abcde8831f31df35480c848754e8666373ffffffffffffffffffffffffffffffffffffffff169063b6b55f259084906024016000604051808303818588803b15801561206457600080fd5b505af1158015612078573d6000803e3d6000fd5b50505050505050565b60008281526001602052604090205473ffffffffffffffffffffffffffffffffffffffff16156120e0576040517fd9edd04700000000000000000000000000000000000000000000000000000000815260048101839052602401610831565b73ffffffffffffffffffffffffffffffffffffffff811660009081526002602052604090205415610c35576040517fa616504400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f6352211e00000000000000000000000000000000000000000000000000000000815260048101829052309073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f091690636352211e90602401602060405180830381865afa1580156121ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121ee9190612c42565b73ffffffffffffffffffffffffffffffffffffffff1614611efd576040517f45131cda00000000000000000000000000000000000000000000000000000000815260048101829052602401610831565b6000612249836126ad565b90508060008082825461225c9190612b3f565b909155505073ffffffffffffffffffffffffffffffffffffffff821660008181526002602090815260408083208790558683526001909152902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690911790556122c8836127dd565b6040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018590527f737fabc26fafc74f523485e7fe4e50fc50cf8e93822e60768c66970d8a19a534910160405180910390a1505050565b8060000361232957505050565b73ffffffffffffffffffffffffffffffffffffffff8316612376576040517f2518928d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390526000919085169063a9059cbb906044016020604051808303816000875af11580156123ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124139190612b09565b905080611dc6576040517ff0798c5100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff80861660048301523060248301528416604482015260648101839052608401610831565b80600003612485575050565b73ffffffffffffffffffffffffffffffffffffffff82166124d2576040517f6f5ec9ea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051829060009073ffffffffffffffffffffffffffffffffffffffff83169084908381818185875af1925050503d806000811461252c576040519150601f19603f3d011682016040523d82523d6000602084013e612531565b606091505b5050905080611dc6576040517fb250066300000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8516602482015260448101849052606401610831565b60008060015b60055481116126a8576000818152600360205260408082205490517f4fc8638f00000000000000000000000000000000000000000000000000000000815260048101829052909190819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f091690634fc8638f906024016040805180830381865afa15801561263e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126629190612aa3565b915091506000806126738484611f00565b90925090506126828289612b3f565b975061268e8188612b3f565b9650505050505080806126a090612bca565b915050612597565b509091565b600080807f00000000000000000000000065683990415a669a7ecbd877240818ee458d0f0973ffffffffffffffffffffffffffffffffffffffff1663eb02c301856040518263ffffffff1660e01b815260040161270c91815260200190565b60a060405180830381865afa158015612729573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061274d9190612c02565b505092505091507f000000000000000000000000000000000000000000000000000000000128b89081106127d6576040517ffddbcf09000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000000000000000000000000000000000000128b8906024820152604401610831565b5092915050565b600060055460016127ee9190612b3f565b6000818152600360209081526040808320869055948252600490529290922082905550600555565b8015158114611efd57600080fd5b6000806040838503121561283757600080fd5b82359150602083013561284981612816565b809150509250929050565b73ffffffffffffffffffffffffffffffffffffffff81168114611efd57600080fd5b6000806040838503121561288957600080fd5b82359150602083013561284981612854565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600080608085870312156128e057600080fd5b84356128eb81612854565b935060208501356128fb81612854565b925060408501359150606085013567ffffffffffffffff8082111561291f57600080fd5b818701915087601f83011261293357600080fd5b8135818111156129455761294561289b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561298b5761298b61289b565b816040528281528a60208487010111156129a457600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6020810160038310612a32577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91905290565b600060208284031215612a4a57600080fd5b5035919050565b60008060408385031215612a6457600080fd5b8235612a6f81612854565b9150602083013561284981612816565b600060208284031215612a9157600080fd5b8135612a9c81612854565b9392505050565b60008060408385031215612ab657600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561101857611018612ac7565b600060208284031215612b1b57600080fd5b8151612a9c81612816565b600060208284031215612b3857600080fd5b5051919050565b8082018082111561101857611018612ac7565b600082612b88577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612bc557612bc5612ac7565b500290565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612bfb57612bfb612ac7565b5060010190565b600080600080600060a08688031215612c1a57600080fd5b5050835160208501516040860151606087015160809097015192989197509594509092509050565b600060208284031215612c5457600080fd5b8151612a9c8161285456fea26469706673582212206070eda80c6dea6bc16e0a4f72b7debb9564c6a3833fc46c824241182e9846f664736f6c63430008100033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000009e34000000000000000000000000000000000000000000000000000000000002819a000000000000000000000000000000000000000000000be951906eba2aa800000
-----Decoded View---------------
Arg [0] : enrollmentPeriod_ (uint256): 648000
Arg [1] : lockDuration_ (uint256): 2628000
Arg [2] : totalBonusAmount_ (uint256): 900000000000000000000000
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000000000000000000000000000009e340
Arg [1] : 00000000000000000000000000000000000000000000000000000000002819a0
Arg [2] : 00000000000000000000000000000000000000000000be951906eba2aa800000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 35 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.