Contract Overview
Balance:
0 ETH
ETH Value:
$0.00
My Name Tag:
Not Available
Txn Hash | Method |
Block
|
From
|
To
|
Value | [Txn Fee] | |||
---|---|---|---|---|---|---|---|---|---|
0x55ce4a236c66495d298074f610a3bdeb0716f7e8a7c5a8b2fbf0ab41b03b04d2 | 0x60806040 | 18520736 | 613 days 44 mins ago | 0x60e788ee9a094c402b6cec03e3fce0ef90944b87 | IN | Create: BattleflyFounderVaultV08 | 0 ETH | 0.00338742167 ETH |
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
Contract Name:
BattleflyFounderVaultV08
Compiler Version
v0.8.4+commit.c7e474f2
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol"; import "./interfaces/IMasterOfCoin.sol"; import "./interfaces/ILegionMetadataStore.sol"; contract AtlasMine is Initializable, AccessControlEnumerableUpgradeable, ERC1155HolderUpgradeable { using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; using SafeERC20Upgradeable for IERC20Upgradeable; using SafeCastUpgradeable for uint256; using SafeCastUpgradeable for int256; enum Lock { twoWeeks, oneMonth, threeMonths, sixMonths, twelveMonths } struct UserInfo { uint256 originalDepositAmount; uint256 depositAmount; uint256 lpAmount; uint256 lockedUntil; uint256 vestingLastUpdate; int256 rewardDebt; Lock lock; } bytes32 public constant ATLAS_MINE_ADMIN_ROLE = keccak256("ATLAS_MINE_ADMIN_ROLE"); uint256 public constant DAY = 1 days; uint256 public constant ONE_WEEK = 7 days; uint256 public constant TWO_WEEKS = ONE_WEEK * 2; uint256 public constant ONE_MONTH = 30 days; uint256 public constant THREE_MONTHS = ONE_MONTH * 3; uint256 public constant SIX_MONTHS = ONE_MONTH * 6; uint256 public constant TWELVE_MONTHS = 365 days; uint256 public constant ONE = 1e18; // Magic token addr IERC20Upgradeable public magic; IMasterOfCoin public masterOfCoin; bool public unlockAll; uint256 public totalRewardsEarned; uint256 public totalUndistributedRewards; uint256 public accMagicPerShare; uint256 public totalLpToken; uint256 public magicTotalDeposits; uint256 public utilizationOverride; EnumerableSetUpgradeable.AddressSet private excludedAddresses; address public legionMetadataStore; address public treasure; address public legion; // user => staked 1/1 mapping(address => bool) public isLegion1_1Staked; uint256[][] public legionBoostMatrix; /// @notice user => depositId => UserInfo mapping(address => mapping(uint256 => UserInfo)) public userInfo; /// @notice user => depositId[] mapping(address => EnumerableSetUpgradeable.UintSet) private allUserDepositIds; /// @notice user => deposit index mapping(address => uint256) public currentId; // user => tokenIds mapping(address => EnumerableSetUpgradeable.UintSet) private legionStaked; // user => tokenId => amount mapping(address => mapping(uint256 => uint256)) public treasureStaked; // user => total amount staked mapping(address => uint256) public treasureStakedAmount; // user => boost mapping(address => uint256) public boosts; event Staked(address nft, uint256 tokenId, uint256 amount, uint256 currentBoost); event Unstaked(address nft, uint256 tokenId, uint256 amount, uint256 currentBoost); event Deposit(address indexed user, uint256 indexed index, uint256 amount, Lock lock); event Withdraw(address indexed user, uint256 indexed index, uint256 amount); event UndistributedRewardsWithdraw(address indexed to, uint256 amount); event Harvest(address indexed user, uint256 indexed index, uint256 amount); event LogUpdateRewards( uint256 distributedRewards, uint256 undistributedRewards, uint256 lpSupply, uint256 accMagicPerShare ); event UtilizationRate(uint256 util); modifier updateRewards() { uint256 lpSupply = totalLpToken; if (lpSupply > 0) { (uint256 distributedRewards, uint256 undistributedRewards) = getRealMagicReward( masterOfCoin.requestRewards() ); totalRewardsEarned += distributedRewards; totalUndistributedRewards += undistributedRewards; accMagicPerShare += (distributedRewards * ONE) / lpSupply; emit LogUpdateRewards(distributedRewards, undistributedRewards, lpSupply, accMagicPerShare); } uint256 util = utilization(); emit UtilizationRate(util); _; } function init(address _magic, address _masterOfCoin) external initializer { magic = IERC20Upgradeable(_magic); masterOfCoin = IMasterOfCoin(_masterOfCoin); _setRoleAdmin(ATLAS_MINE_ADMIN_ROLE, ATLAS_MINE_ADMIN_ROLE); _grantRole(ATLAS_MINE_ADMIN_ROLE, msg.sender); // array follows values from ILegionMetadataStore.LegionGeneration and ILegionMetadataStore.LegionRarity legionBoostMatrix = [ // GENESIS // LEGENDARY,RARE,SPECIAL,UNCOMMON,COMMON,RECRUIT [uint256(600e16), uint256(200e16), uint256(75e16), uint256(100e16), uint256(50e16), uint256(0)], // AUXILIARY // LEGENDARY,RARE,SPECIAL,UNCOMMON,COMMON,RECRUIT [uint256(0), uint256(25e16), uint256(0), uint256(10e16), uint256(5e16), uint256(0)], // RECRUIT // LEGENDARY,RARE,SPECIAL,UNCOMMON,COMMON,RECRUIT [uint256(0), uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)] ]; __AccessControlEnumerable_init(); __ERC1155Holder_init(); } function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155ReceiverUpgradeable, AccessControlEnumerableUpgradeable) returns (bool) { return super.supportsInterface(interfaceId); } function getStakedLegions(address _user) external view virtual returns (uint256[] memory) { return legionStaked[_user].values(); } function getUserBoost(address _user) external view virtual returns (uint256) { return boosts[_user]; } function getLegionBoostMatrix() external view virtual returns (uint256[][] memory) { return legionBoostMatrix; } function getLegionBoost(uint256 _legionGeneration, uint256 _legionRarity) public view virtual returns (uint256) { if ( _legionGeneration < legionBoostMatrix.length && _legionRarity < legionBoostMatrix[_legionGeneration].length ) { return legionBoostMatrix[_legionGeneration][_legionRarity]; } return 0; } function utilization() public view virtual returns (uint256 util) { if (utilizationOverride > 0) return utilizationOverride; uint256 circulatingSupply = magic.totalSupply(); uint256 len = excludedAddresses.length(); for (uint256 i = 0; i < len; i++) { circulatingSupply -= magic.balanceOf(excludedAddresses.at(i)); } uint256 rewardsAmount = magic.balanceOf(address(this)) - magicTotalDeposits; circulatingSupply -= rewardsAmount; if (circulatingSupply != 0) { util = (magicTotalDeposits * ONE) / circulatingSupply; } } function getRealMagicReward(uint256 _magicReward) public view virtual returns (uint256 distributedRewards, uint256 undistributedRewards) { //Disabled for testing /* uint256 util = utilization(); if (util < 3e17) { distributedRewards = 0; } else if (util < 4e17) { // >30% // 50% distributedRewards = _magicReward * 5 / 10; } else if (util < 5e17) { // >40% // 60% distributedRewards = _magicReward * 6 / 10; } else if (util < 6e17) { // >50% // 80% distributedRewards = _magicReward * 8 / 10; } else { // >60% // 100% distributedRewards = _magicReward; } undistributedRewards = _magicReward - distributedRewards; */ distributedRewards = _magicReward; undistributedRewards = 0; } function getAllUserDepositIds(address _user) public view virtual returns (uint256[] memory) { return allUserDepositIds[_user].values(); } function getExcludedAddresses() public view virtual returns (address[] memory) { return excludedAddresses.values(); } function getLockBoost(Lock _lock) public pure virtual returns (uint256 boost, uint256 timelock) { if (_lock == Lock.twoWeeks) { // 10% return (10e16, TWO_WEEKS); } else if (_lock == Lock.oneMonth) { // 25% return (25e16, ONE_MONTH); } else if (_lock == Lock.threeMonths) { // 80% return (80e16, THREE_MONTHS); } else if (_lock == Lock.sixMonths) { // 180% return (180e16, SIX_MONTHS); } else if (_lock == Lock.twelveMonths) { // 400% return (400e16, TWELVE_MONTHS); } else { revert("Invalid lock value"); } } function getVestingTime(Lock _lock) public pure virtual returns (uint256 vestingTime) { if (_lock == Lock.twoWeeks) { vestingTime = 0; } else if (_lock == Lock.oneMonth) { vestingTime = 7 days; } else if (_lock == Lock.threeMonths) { vestingTime = 14 days; } else if (_lock == Lock.sixMonths) { vestingTime = 30 days; } else if (_lock == Lock.twelveMonths) { vestingTime = 45 days; } } function calcualteVestedPrincipal(address _user, uint256 _depositId) public view virtual returns (uint256 amount) { UserInfo storage user = userInfo[_user][_depositId]; Lock _lock = user.lock; uint256 vestingEnd = user.lockedUntil + getVestingTime(_lock); uint256 vestingBegin = user.lockedUntil; if (block.timestamp >= vestingEnd || unlockAll) { amount = user.originalDepositAmount; } else if (block.timestamp > user.vestingLastUpdate) { amount = (user.originalDepositAmount * (block.timestamp - user.vestingLastUpdate)) / (vestingEnd - vestingBegin); } } function pendingRewardsPosition(address _user, uint256 _depositId) public view virtual returns (uint256 pending) { UserInfo storage user = userInfo[_user][_depositId]; uint256 _accMagicPerShare = accMagicPerShare; uint256 lpSupply = totalLpToken; (uint256 distributedRewards, ) = getRealMagicReward(masterOfCoin.getPendingRewards(address(this))); _accMagicPerShare += (distributedRewards * ONE) / lpSupply; pending = (((user.lpAmount * _accMagicPerShare) / ONE).toInt256() - user.rewardDebt).toUint256(); } function pendingRewardsAll(address _user) external view virtual returns (uint256 pending) { uint256 len = allUserDepositIds[_user].length(); for (uint256 i = 0; i < len; i++) { uint256 depositId = allUserDepositIds[_user].at(i); pending += pendingRewardsPosition(_user, depositId); } } function deposit(uint256 _amount, Lock _lock) public virtual updateRewards { (UserInfo storage user, uint256 depositId) = _addDeposit(msg.sender); (uint256 lockBoost, uint256 timelock) = getLockBoost(_lock); uint256 nftBoost = boosts[msg.sender]; uint256 lpAmount = _amount + (_amount * (lockBoost + nftBoost)) / ONE; magicTotalDeposits += _amount; totalLpToken += lpAmount; user.originalDepositAmount = _amount; user.depositAmount = _amount; user.lpAmount = lpAmount; user.lockedUntil = block.timestamp + timelock; user.vestingLastUpdate = user.lockedUntil; user.rewardDebt = ((lpAmount * accMagicPerShare) / ONE).toInt256(); user.lock = _lock; magic.safeTransferFrom(msg.sender, address(this), _amount); emit Deposit(msg.sender, depositId, _amount, _lock); } function withdrawPosition(uint256 _depositId, uint256 _amount) public virtual updateRewards returns (bool) { UserInfo storage user = userInfo[msg.sender][_depositId]; uint256 depositAmount = user.depositAmount; if (depositAmount == 0) return false; if (_amount > depositAmount) { _amount = depositAmount; } // anyone can withdraw if kill swith was used if (!unlockAll) { require(block.timestamp >= user.lockedUntil, "Position is still locked"); uint256 vestedAmount = _vestedPrincipal(msg.sender, _depositId); if (_amount > vestedAmount) { _amount = vestedAmount; } } // Effects uint256 ratio = (_amount * ONE) / depositAmount; uint256 lpAmount = (user.lpAmount * ratio) / ONE; totalLpToken -= lpAmount; magicTotalDeposits -= _amount; user.depositAmount -= _amount; user.lpAmount -= lpAmount; user.rewardDebt -= ((lpAmount * accMagicPerShare) / ONE).toInt256(); // Interactions magic.safeTransfer(msg.sender, _amount); emit Withdraw(msg.sender, _depositId, _amount); return true; } function withdrawAll() public virtual { uint256[] memory depositIds = allUserDepositIds[msg.sender].values(); for (uint256 i = 0; i < depositIds.length; i++) { withdrawPosition(depositIds[i], type(uint256).max); } } function harvestPosition(uint256 _depositId) public virtual updateRewards { UserInfo storage user = userInfo[msg.sender][_depositId]; int256 accumulatedMagic = ((user.lpAmount * accMagicPerShare) / ONE).toInt256(); uint256 _pendingMagic = (accumulatedMagic - user.rewardDebt).toUint256(); // Effects user.rewardDebt = accumulatedMagic; if (user.depositAmount == 0 && user.lpAmount == 0) { _removeDeposit(msg.sender, _depositId); } // Interactions if (_pendingMagic != 0) { magic.safeTransfer(msg.sender, _pendingMagic); } emit Harvest(msg.sender, _depositId, _pendingMagic); require(magic.balanceOf(address(this)) >= magicTotalDeposits, "Run on banks"); } function harvestAll() public virtual { uint256[] memory depositIds = allUserDepositIds[msg.sender].values(); for (uint256 i = 0; i < depositIds.length; i++) { harvestPosition(depositIds[i]); } } function withdrawAndHarvestPosition(uint256 _depositId, uint256 _amount) public virtual { withdrawPosition(_depositId, _amount); harvestPosition(_depositId); } function withdrawAndHarvestAll() public virtual { uint256[] memory depositIds = allUserDepositIds[msg.sender].values(); for (uint256 i = 0; i < depositIds.length; i++) { withdrawAndHarvestPosition(depositIds[i], type(uint256).max); } } function stakeTreasure(uint256 _tokenId, uint256 _amount) external virtual updateRewards { require(treasure != address(0), "Cannot stake Treasure"); require(_amount > 0, "Amount is 0"); treasureStaked[msg.sender][_tokenId] += _amount; treasureStakedAmount[msg.sender] += _amount; require(treasureStakedAmount[msg.sender] <= 20, "Max 20 treasures per wallet"); uint256 boost = getNftBoost(treasure, _tokenId, _amount); boosts[msg.sender] += boost; _recalculateLpAmount(msg.sender); IERC1155Upgradeable(treasure).safeTransferFrom(msg.sender, address(this), _tokenId, _amount, bytes("")); emit Staked(treasure, _tokenId, _amount, boosts[msg.sender]); } function unstakeTreasure(uint256 _tokenId, uint256 _amount) external virtual updateRewards { require(treasure != address(0), "Cannot stake Treasure"); require(_amount > 0, "Amount is 0"); require(treasureStaked[msg.sender][_tokenId] >= _amount, "Withdraw amount too big"); treasureStaked[msg.sender][_tokenId] -= _amount; treasureStakedAmount[msg.sender] -= _amount; uint256 boost = getNftBoost(treasure, _tokenId, _amount); boosts[msg.sender] -= boost; _recalculateLpAmount(msg.sender); IERC1155Upgradeable(treasure).safeTransferFrom(address(this), msg.sender, _tokenId, _amount, bytes("")); emit Unstaked(treasure, _tokenId, _amount, boosts[msg.sender]); } function stakeLegion(uint256 _tokenId) external virtual updateRewards { require(legion != address(0), "Cannot stake Legion"); require(legionStaked[msg.sender].add(_tokenId), "NFT already staked"); require(legionStaked[msg.sender].length() <= 3, "Max 3 legions per wallet"); if (isLegion1_1(_tokenId)) { require(!isLegion1_1Staked[msg.sender], "Max 1 1/1 legion per wallet"); isLegion1_1Staked[msg.sender] = true; } uint256 boost = getNftBoost(legion, _tokenId, 1); boosts[msg.sender] += boost; _recalculateLpAmount(msg.sender); IERC721Upgradeable(legion).transferFrom(msg.sender, address(this), _tokenId); emit Staked(legion, _tokenId, 1, boosts[msg.sender]); } function unstakeLegion(uint256 _tokenId) external virtual updateRewards { require(legionStaked[msg.sender].remove(_tokenId), "NFT is not staked"); if (isLegion1_1(_tokenId)) { isLegion1_1Staked[msg.sender] = false; } uint256 boost = getNftBoost(legion, _tokenId, 1); boosts[msg.sender] -= boost; _recalculateLpAmount(msg.sender); IERC721Upgradeable(legion).transferFrom(address(this), msg.sender, _tokenId); emit Unstaked(legion, _tokenId, 1, boosts[msg.sender]); } function isLegion1_1(uint256 _tokenId) public view virtual returns (bool) { try ILegionMetadataStore(legionMetadataStore).metadataForLegion(_tokenId) returns ( ILegionMetadataStore.LegionMetadata memory metadata ) { return metadata.legionGeneration == ILegionMetadataStore.LegionGeneration.GENESIS && metadata.legionRarity == ILegionMetadataStore.LegionRarity.LEGENDARY; } catch Error( string memory /*reason*/ ) { return false; } catch Panic(uint256) { return false; } catch ( bytes memory /*lowLevelData*/ ) { return false; } } function getNftBoost( address _nft, uint256 _tokenId, uint256 _amount ) public view virtual returns (uint256) { if (_nft == treasure) { return getTreasureBoost(_tokenId, _amount); } else if (_nft == legion) { try ILegionMetadataStore(legionMetadataStore).metadataForLegion(_tokenId) returns ( ILegionMetadataStore.LegionMetadata memory metadata ) { return getLegionBoost(uint256(metadata.legionGeneration), uint256(metadata.legionRarity)); } catch Error( string memory /*reason*/ ) { return 0; } catch Panic(uint256) { return 0; } catch ( bytes memory /*lowLevelData*/ ) { return 0; } } return 0; } function _recalculateLpAmount(address _user) internal virtual { uint256 nftBoost = boosts[_user]; uint256[] memory depositIds = allUserDepositIds[_user].values(); for (uint256 i = 0; i < depositIds.length; i++) { uint256 depositId = depositIds[i]; UserInfo storage user = userInfo[_user][depositId]; (uint256 lockBoost, ) = getLockBoost(user.lock); uint256 _amount = user.depositAmount; uint256 newlLpAmount = _amount + (_amount * (lockBoost + nftBoost)) / ONE; uint256 oldLpAmount = user.lpAmount; if (newlLpAmount > oldLpAmount) { uint256 lpDiff = newlLpAmount - oldLpAmount; user.rewardDebt += ((lpDiff * accMagicPerShare) / ONE).toInt256(); totalLpToken += lpDiff; user.lpAmount += lpDiff; } else if (newlLpAmount < oldLpAmount) { uint256 lpDiff = oldLpAmount - newlLpAmount; user.rewardDebt -= ((lpDiff * accMagicPerShare) / ONE).toInt256(); totalLpToken -= lpDiff; user.lpAmount -= lpDiff; } } } function addExcludedAddress(address _exclude) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) updateRewards { require(excludedAddresses.add(_exclude), "Address already excluded"); } function removeExcludedAddress(address _excluded) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) updateRewards { require(excludedAddresses.remove(_excluded), "Address is not excluded"); } function setUtilizationOverride(uint256 _utilizationOverride) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) updateRewards { utilizationOverride = _utilizationOverride; } function setMagicToken(address _magic) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) { magic = IERC20Upgradeable(_magic); } function setTreasure(address _treasure) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) { treasure = _treasure; } function setLegion(address _legion) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) { legion = _legion; } function setLegionMetadataStore(address _legionMetadataStore) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) { legionMetadataStore = _legionMetadataStore; } function setLegionBoostMatrix(uint256[][] memory _legionBoostMatrix) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) { legionBoostMatrix = _legionBoostMatrix; } /// @notice EMERGENCY ONLY function toggleUnlockAll() external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) updateRewards { unlockAll = unlockAll ? false : true; } function withdrawUndistributedRewards(address _to) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) updateRewards { uint256 _totalUndistributedRewards = totalUndistributedRewards; totalUndistributedRewards = 0; magic.safeTransfer(_to, _totalUndistributedRewards); emit UndistributedRewardsWithdraw(_to, _totalUndistributedRewards); } function getTreasureBoost(uint256 _tokenId, uint256 _amount) public pure virtual returns (uint256 boost) { if (_tokenId == 39) { // Ancient Relic 8% boost = 75e15; } else if (_tokenId == 46) { // Bag of Rare Mushrooms 6.2% boost = 62e15; } else if (_tokenId == 47) { // Bait for Monsters 7.3% boost = 73e15; } else if (_tokenId == 48) { // Beetle-wing 0.8% boost = 8e15; } else if (_tokenId == 49) { // Blue Rupee 1.5% boost = 15e15; } else if (_tokenId == 51) { // Bottomless Elixir 7.6% boost = 76e15; } else if (_tokenId == 52) { // Cap of Invisibility 7.6% boost = 76e15; } else if (_tokenId == 53) { // Carriage 6.1% boost = 61e15; } else if (_tokenId == 54) { // Castle 7.3% boost = 71e15; } else if (_tokenId == 68) { // Common Bead 5.6% boost = 56e15; } else if (_tokenId == 69) { // Common Feather 3.4% boost = 34e15; } else if (_tokenId == 71) { // Common Relic 2.2% boost = 22e15; } else if (_tokenId == 72) { // Cow 5.8% boost = 58e15; } else if (_tokenId == 73) { // Diamond 0.8% boost = 8e15; } else if (_tokenId == 74) { // Divine Hourglass 6.3% boost = 63e15; } else if (_tokenId == 75) { // Divine Mask 5.7% boost = 57e15; } else if (_tokenId == 76) { // Donkey 1.2% boost = 12e15; } else if (_tokenId == 77) { // Dragon Tail 0.8% boost = 8e15; } else if (_tokenId == 79) { // Emerald 0.8% boost = 8e15; } else if (_tokenId == 82) { // Favor from the Gods 5.6% boost = 56e15; } else if (_tokenId == 91) { // Framed Butterfly 5.8% boost = 58e15; } else if (_tokenId == 92) { // Gold Coin 0.8% boost = 8e15; } else if (_tokenId == 93) { // Grain 3.2% boost = 32e15; } else if (_tokenId == 94) { // Green Rupee 3.3% boost = 33e15; } else if (_tokenId == 95) { // Grin 15.7% boost = 157e15; } else if (_tokenId == 96) { // Half-Penny 0.8% boost = 8e15; } else if (_tokenId == 97) { // Honeycomb 15.8% boost = 158e15; } else if (_tokenId == 98) { // Immovable Stone 7.2% boost = 72e15; } else if (_tokenId == 99) { // Ivory Breastpin 6.4% boost = 64e15; } else if (_tokenId == 100) { // Jar of Fairies 5.3% boost = 53e15; } else if (_tokenId == 103) { // Lumber 3% boost = 30e15; } else if (_tokenId == 104) { // Military Stipend 6.2% boost = 62e15; } else if (_tokenId == 105) { // Mollusk Shell 6.7% boost = 67e15; } else if (_tokenId == 114) { // Ox 1.6% boost = 16e15; } else if (_tokenId == 115) { // Pearl 0.8% boost = 8e15; } else if (_tokenId == 116) { // Pot of Gold 5.8% boost = 58e15; } else if (_tokenId == 117) { // Quarter-Penny 0.8% boost = 8e15; } else if (_tokenId == 132) { // Red Feather 6.4% boost = 64e15; } else if (_tokenId == 133) { // Red Rupee 0.8% boost = 8e15; } else if (_tokenId == 141) { // Score of Ivory 6% boost = 60e15; } else if (_tokenId == 151) { // Silver Coin 0.8% boost = 8e15; } else if (_tokenId == 152) { // Small Bird 6% boost = 60e15; } else if (_tokenId == 153) { // Snow White Feather 6.4% boost = 64e15; } else if (_tokenId == 161) { // Thread of Divine Silk 7.3% boost = 73e15; } else if (_tokenId == 162) { // Unbreakable Pocketwatch 5.9% boost = 59e15; } else if (_tokenId == 164) { // Witches Broom 5.1% boost = 51e15; } boost = boost * _amount; } function _vestedPrincipal(address _user, uint256 _depositId) internal virtual returns (uint256 amount) { amount = calcualteVestedPrincipal(_user, _depositId); UserInfo storage user = userInfo[_user][_depositId]; user.vestingLastUpdate = block.timestamp; } function _addDeposit(address _user) internal virtual returns (UserInfo storage user, uint256 newDepositId) { // start depositId from 1 newDepositId = ++currentId[_user]; allUserDepositIds[_user].add(newDepositId); user = userInfo[_user][newDepositId]; } function _removeDeposit(address _user, uint256 _depositId) internal virtual { require(allUserDepositIds[_user].remove(_depositId), "depositId !exists"); } }
// 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 IERC20Upgradeable { /** * @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.6.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165Upgradeable.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721Upgradeable is IERC165Upgradeable { /** * @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 be 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: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * 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 v4.4.1 (token/ERC1155/IERC1155.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165Upgradeable.sol"; /** * @dev Required interface of an ERC1155 compliant contract, as defined in the * https://eips.ethereum.org/EIPS/eip-1155[EIP]. * * _Available since v3.1._ */ interface IERC1155Upgradeable is IERC165Upgradeable { /** * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`. */ event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); /** * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /** * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to * `approved`. */ event ApprovalForAll(address indexed account, address indexed operator, bool approved); /** * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. * * If an {URI} event was emitted for `id`, the standard * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value * returned by {IERC1155MetadataURI-uri}. */ event URI(string value, uint256 indexed id); /** * @dev Returns the amount of tokens of token type `id` owned by `account`. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); /** * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, * * Emits an {ApprovalForAll} event. * * Requirements: * * - `operator` cannot be the caller. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. * * See {setApprovalForAll}. */ function isApprovedForAll(address account, address operator) external view returns (bool); /** * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}. * - `from` must have a balance of tokens of type `id` of at least `amount`. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes calldata data ) external; /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. * * Emits a {TransferBatch} event. * * Requirements: * * - `ids` and `amounts` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (utils/structs/EnumerableSet.sol) pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. */ library EnumerableSetUpgradeable { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { return _values(set._inner); } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol) pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCastUpgradeable { /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); return uint224(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits. */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128) { require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits"); return int128(value); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64) { require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits"); return int64(value); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32) { require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits"); return int32(value); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16) { require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits"); return int16(value); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits. * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8) { require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits"); return int8(value); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; import "../../../utils/AddressUpgradeable.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20Upgradeable { using AddressUpgradeable for address; function safeTransfer( IERC20Upgradeable token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20Upgradeable token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20Upgradeable token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. */ modifier initializer() { bool isTopLevelCall = _setInitializedVersion(1); if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original * initialization step. This is essential to configure modules that are added through upgrades and that require * initialization. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. */ modifier reinitializer(uint8 version) { bool isTopLevelCall = _setInitializedVersion(version); if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(version); } } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. */ function _disableInitializers() internal virtual { _setInitializedVersion(type(uint8).max); } function _setInitializedVersion(uint8 version) private returns (bool) { // If the contract is initializing we ignore whether _initialized is set in order to support multiple // inheritance patterns, but we only do this in the context of a constructor, and for the lowest level // of initializers, because in other contexts the contract may have been reentered. if (_initializing) { require( version == 1 && !AddressUpgradeable.isContract(address(this)), "Initializable: contract is already initialized" ); return false; } else { require(_initialized < version, "Initializable: contract is already initialized"); _initialized = version; return true; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) pragma solidity ^0.8.0; import "./IAccessControlEnumerableUpgradeable.sol"; import "./AccessControlUpgradeable.sol"; import "../utils/structs/EnumerableSetUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Extension of {AccessControl} that allows enumerating the members of each role. */ abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerableUpgradeable, AccessControlUpgradeable { function __AccessControlEnumerable_init() internal onlyInitializing { } function __AccessControlEnumerable_init_unchained() internal onlyInitializing { } using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; mapping(bytes32 => EnumerableSetUpgradeable.AddressSet) private _roleMembers; /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControlEnumerableUpgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns one of the accounts that have `role`. `index` must be a * value between 0 and {getRoleMemberCount}, non-inclusive. * * Role bearers are not sorted in any particular way, and their ordering may * change at any point. * * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure * you perform all queries on the same block. See the following * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] * for more information. */ function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { return _roleMembers[role].at(index); } /** * @dev Returns the number of accounts that have `role`. Can be used * together with {getRoleMember} to enumerate all bearers of a role. */ function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { return _roleMembers[role].length(); } /** * @dev Overload {_grantRole} to track enumerable memberships */ function _grantRole(bytes32 role, address account) internal virtual override { super._grantRole(role, account); _roleMembers[role].add(account); } /** * @dev Overload {_revokeRole} to track enumerable memberships */ function _revokeRole(bytes32 role, address account) internal virtual override { super._revokeRole(role, account); _roleMembers[role].remove(account); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol) pragma solidity ^0.8.0; import "./ERC1155ReceiverUpgradeable.sol"; import "../../../proxy/utils/Initializable.sol"; /** * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens. * * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be * stuck. * * @dev _Available since v3.1._ */ contract ERC1155HolderUpgradeable is Initializable, ERC1155ReceiverUpgradeable { function __ERC1155Holder_init() internal onlyInitializing { } function __ERC1155Holder_init_unchained() internal onlyInitializing { } function onERC1155Received( address, address, uint256, uint256, bytes memory ) public virtual override returns (bytes4) { return this.onERC1155Received.selector; } function onERC1155BatchReceived( address, address, uint256[] memory, uint256[] memory, bytes memory ) public virtual override returns (bytes4) { return this.onERC1155BatchReceived.selector; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity >0.8.0; interface IMasterOfCoin { function requestRewards() external returns (uint256 rewardsPaid); function getPendingRewards(address _stream) external view returns (uint256 pendingRewards); function setWithdrawStamp() external; function setStaticAmount(bool set) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ILegionMetadataStore { // As this will likely change in the future, this should not be used to store state, but rather // as parameters and return values from functions. struct LegionMetadata { LegionGeneration legionGeneration; LegionClass legionClass; LegionRarity legionRarity; uint8 questLevel; uint8 craftLevel; uint8[6] constellationRanks; uint256 oldId; } enum Constellation { FIRE, EARTH, WIND, WATER, LIGHT, DARK } enum LegionRarity { LEGENDARY, RARE, SPECIAL, UNCOMMON, COMMON, RECRUIT } enum LegionClass { RECRUIT, SIEGE, FIGHTER, ASSASSIN, RANGED, SPELLCASTER, RIVERMAN, NUMERAIRE, ALL_CLASS, ORIGIN } enum LegionGeneration { GENESIS, AUXILIARY, RECRUIT } // Sets the intial metadata for a token id. // Admin only. function setInitialMetadataForLegion( address _owner, uint256 _tokenId, LegionGeneration _generation, LegionClass _class, LegionRarity _rarity, uint256 _oldId ) external; // Increases the quest level by one. It is up to the calling contract to regulate the max quest level. No validation. // Admin only. function increaseQuestLevel(uint256 _tokenId) external; // Increases the craft level by one. It is up to the calling contract to regulate the max craft level. No validation. // Admin only. function increaseCraftLevel(uint256 _tokenId) external; // Increases the rank of the given constellation to the given number. It is up to the calling contract to regulate the max constellation rank. No validation. // Admin only. function increaseConstellationRank( uint256 _tokenId, Constellation _constellation, uint8 _to ) external; // Returns the metadata for the given legion. function metadataForLegion(uint256 _tokenId) external view returns (LegionMetadata memory); // Returns the tokenUri for the given token. function tokenURI(uint256 _tokenId) external view returns (string memory); }
// 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 IERC165Upgradeable { /** * @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 // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) pragma solidity ^0.8.0; import "./IAccessControlUpgradeable.sol"; /** * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. */ interface IAccessControlEnumerableUpgradeable is IAccessControlUpgradeable { /** * @dev Returns one of the accounts that have `role`. `index` must be a * value between 0 and {getRoleMemberCount}, non-inclusive. * * Role bearers are not sorted in any particular way, and their ordering may * change at any point. * * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure * you perform all queries on the same block. See the following * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] * for more information. */ function getRoleMember(bytes32 role, uint256 index) external view returns (address); /** * @dev Returns the number of accounts that have `role`. Can be used * together with {getRoleMember} to enumerate all bearers of a role. */ function getRoleMemberCount(bytes32 role) external view returns (uint256); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (access/AccessControl.sol) pragma solidity ^0.8.0; import "./IAccessControlUpgradeable.sol"; import "../utils/ContextUpgradeable.sol"; import "../utils/StringsUpgradeable.sol"; import "../utils/introspection/ERC165Upgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ``` * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ``` * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. */ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable { function __AccessControl_init() internal onlyInitializing { } function __AccessControl_init_unchained() internal onlyInitializing { } struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `_msgSender()` is missing `role`. * Overriding this function changes the behavior of the {onlyRole} modifier. * * Format of the revert message is described in {_checkRole}. * * _Available since v4.6._ */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", StringsUpgradeable.toHexString(uint160(account), 20), " is missing role ", StringsUpgradeable.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), "AccessControl: can only renounce roles for self"); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}. */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. */ function _grantRole(bytes32 role, address account) internal virtual { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. */ function _revokeRole(bytes32 role, address account) internal virtual { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControlUpgradeable { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) pragma solidity ^0.8.0; /** * @dev String operations. */ library StringsUpgradeable { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165Upgradeable.sol"; import "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable { function __ERC165_init() internal onlyInitializing { } function __ERC165_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165Upgradeable).interfaceId; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol) pragma solidity ^0.8.0; import "../IERC1155ReceiverUpgradeable.sol"; import "../../../utils/introspection/ERC165Upgradeable.sol"; import "../../../proxy/utils/Initializable.sol"; /** * @dev _Available since v3.1._ */ abstract contract ERC1155ReceiverUpgradeable is Initializable, ERC165Upgradeable, IERC1155ReceiverUpgradeable { function __ERC1155Receiver_init() internal onlyInitializing { } function __ERC1155Receiver_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) { return interfaceId == type(IERC1155ReceiverUpgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165Upgradeable.sol"; /** * @dev _Available since v3.1._ */ interface IERC1155ReceiverUpgradeable is IERC165Upgradeable { /** * @dev Handles the receipt of a single ERC1155 token type. This function is * called at the end of a `safeTransferFrom` after the balance has been updated. * * NOTE: To accept the transfer, this must return * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` * (i.e. 0xf23a6e61, or its own function selector). * * @param operator The address which initiated the transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param id The ID of the token being transferred * @param value The amount of tokens being transferred * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4); /** * @dev Handles the receipt of a multiple ERC1155 token types. This function * is called at the end of a `safeBatchTransferFrom` after the balances have * been updated. * * NOTE: To accept the transfer(s), this must return * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` * (i.e. 0xbc197c81, or its own function selector). * * @param operator The address which initiated the batch transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param ids An array containing ids of each token being transferred (order and length must match values array) * @param values An array containing amounts of each token being transferred (order and length must match ids array) * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4); }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "../interfaces/ILegionMetadataStore.sol"; contract LegionMetadataStore is Initializable, ILegionMetadataStore { event LegionQuestLevelUp(uint256 indexed _tokenId, uint8 _questLevel); event LegionCraftLevelUp(uint256 indexed _tokenId, uint8 _craftLevel); event LegionConstellationRankUp(uint256 indexed _tokenId, Constellation indexed _constellation, uint8 _rank); event LegionCreated( address indexed _owner, uint256 indexed _tokenId, LegionGeneration _generation, LegionClass _class, LegionRarity _rarity ); mapping(uint256 => LegionGeneration) internal idToGeneration; mapping(uint256 => LegionClass) internal idToClass; mapping(uint256 => LegionRarity) internal idToRarity; mapping(uint256 => uint256) internal idToOldId; mapping(uint256 => uint8) internal idToQuestLevel; mapping(uint256 => uint8) internal idToCraftLevel; mapping(uint256 => uint8[6]) internal idToConstellationRanks; mapping(LegionGeneration => mapping(LegionClass => mapping(LegionRarity => mapping(uint256 => string)))) internal _genToClassToRarityToOldIdToUri; function initialize() external initializer {} function setInitialMetadataForLegion( address _owner, uint256 _tokenId, LegionGeneration _generation, LegionClass _class, LegionRarity _rarity, uint256 _oldId ) external override { idToGeneration[_tokenId] = _generation; idToClass[_tokenId] = _class; idToRarity[_tokenId] = _rarity; idToOldId[_tokenId] = _oldId; // Initial quest/craft level is 1. idToQuestLevel[_tokenId] = 1; idToCraftLevel[_tokenId] = 1; emit LegionCreated(_owner, _tokenId, _generation, _class, _rarity); } function increaseQuestLevel(uint256 _tokenId) external override { idToQuestLevel[_tokenId]++; emit LegionQuestLevelUp(_tokenId, idToQuestLevel[_tokenId]); } function increaseCraftLevel(uint256 _tokenId) external override { idToCraftLevel[_tokenId]++; emit LegionCraftLevelUp(_tokenId, idToCraftLevel[_tokenId]); } function increaseConstellationRank( uint256 _tokenId, Constellation _constellation, uint8 _to ) external override { idToConstellationRanks[_tokenId][uint256(_constellation)] = _to; emit LegionConstellationRankUp(_tokenId, _constellation, _to); } function metadataForLegion(uint256 _tokenId) external view override returns (LegionMetadata memory) { return LegionMetadata( idToGeneration[_tokenId], idToClass[_tokenId], idToRarity[_tokenId], idToQuestLevel[_tokenId], idToCraftLevel[_tokenId], idToConstellationRanks[_tokenId], idToOldId[_tokenId] ); } function tokenURI(uint256 _tokenId) external view override returns (string memory) { return _genToClassToRarityToOldIdToUri[idToGeneration[_tokenId]][idToClass[_tokenId]][idToRarity[_tokenId]][ idToOldId[_tokenId] ]; } function setTokenUriForGenClassRarityOldId( LegionGeneration _gen, LegionClass _class, LegionRarity _rarity, uint256 _oldId, string calldata _uri ) external { _genToClassToRarityToOldIdToUri[_gen][_class][_rarity][_oldId] = _uri; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/cryptography/MerkleProofUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "../BattleflyFounderVaultV08.sol"; import "../interfaces/IBattleflyAtlasStakerV02.sol"; import "../interfaces/vaults/IBattleflyTreasuryFlywheelVault.sol"; import "../interfaces/IBattlefly.sol"; import "../interfaces/IAtlasMine.sol"; import "../interfaces/IBattleflyFounderVault.sol"; contract BattleflyTreasuryFlywheelVault is IBattleflyTreasuryFlywheelVault, Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable, PausableUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; /** * @dev Immutable states */ IERC20Upgradeable public MAGIC; IBattleflyAtlasStakerV02 public ATLAS_STAKER; IBattleflyFounderVault public FOUNDER_VAULT_V2; address public BATTLEFLY_BOT; uint256 public V2_VAULT_PERCENTAGE; uint256 public TREASURY_PERCENTAGE; uint256 public DENOMINATOR; IAtlasMine.Lock public TREASURY_LOCK; EnumerableSetUpgradeable.UintSet depositIds; uint256 public pendingDeposits; uint256 public activeRestakeDepositId; uint256 public pendingTreasuryAmountToStake; /** * @dev User stake data * { depositId } => { User stake data } */ mapping(uint256 => UserStake) public userStakes; function initialize( address _magic, address _atlasStaker, address _battleflyFounderVaultV2, address _battleflyBot ) external initializer { __Ownable_init(); __ReentrancyGuard_init(); __Pausable_init(); require(_magic != address(0), "BattleflyTreasuryFlywheelVault: invalid address"); require(_atlasStaker != address(0), "BattleflyTreasuryFlywheelVault: invalid address"); require(_battleflyFounderVaultV2 != address(0), "BattleflyTreasuryFlywheelVault: invalid address"); require(_battleflyBot != address(0), "BattleflyTreasuryFlywheelVault: invalid address"); MAGIC = IERC20Upgradeable(_magic); ATLAS_STAKER = IBattleflyAtlasStakerV02(_atlasStaker); FOUNDER_VAULT_V2 = IBattleflyFounderVault(_battleflyFounderVaultV2); BATTLEFLY_BOT = _battleflyBot; V2_VAULT_PERCENTAGE = 5000; TREASURY_PERCENTAGE = 95000; DENOMINATOR = 100000; TREASURY_LOCK = IAtlasMine.Lock.twoWeeks; MAGIC.approve(address(ATLAS_STAKER), 2**256 - 1); } /** * @dev Deposit funds to AtlasStaker */ function deposit(uint128 _amount) external override nonReentrant onlyOwner returns (uint256 atlasStakerDepositId) { MAGIC.safeTransferFrom(msg.sender, address(this), _amount); atlasStakerDepositId = _deposit(uint256(_amount)); } /** * @dev Withdraw staked funds from AtlasStaker */ function withdraw(uint256[] memory _depositIds, address user) public override nonReentrant onlyOwner returns (uint256 amount) { for (uint256 i = 0; i < _depositIds.length; i++) { amount += _withdraw(_depositIds[i], user); } } /** * @dev Withdraw all from AtlasStaker. This is only possible when the retention period of 14 epochs has passed. * The retention period is started when a withdrawal for the stake is requested. */ function withdrawAll(address user) public override nonReentrant onlyOwner returns (uint256 amount) { uint256[] memory ids = depositIds.values(); require(ids.length > 0, "BattleflyTreasuryFlywheelVault: No deposited funds"); for (uint256 i = 0; i < ids.length; i++) { if (ATLAS_STAKER.canWithdraw(ids[i])) { amount += _withdraw(ids[i], user); } } } /** * @dev Request a withdrawal from AtlasStaker. This works with a retention period of 14 epochs. * Once the retention period has passed, the stake can be withdrawn. */ function requestWithdrawal(uint256[] memory _depositIds) public override onlyOwner { for (uint256 i = 0; i < _depositIds.length; i++) { ATLAS_STAKER.requestWithdrawal(_depositIds[i]); emit RequestWithdrawal(_depositIds[i]); } } /** * @dev Claim emission from AtlasStaker */ function claim(uint256 _depositId, address user) public override nonReentrant onlyOwner returns (uint256 emission) { emission = _claim(_depositId, user); } /** * @dev Claim all emissions from AtlasStaker */ function claimAll(address user) external override nonReentrant onlyOwner returns (uint256 amount) { uint256[] memory ids = depositIds.values(); require(ids.length > 0, "BattleflyTreasuryFlywheelVault: No deposited funds"); for (uint256 i = 0; i < ids.length; i++) { amount += _claim(ids[i], user); } } /** * @dev Claim all emissions from AtlasStaker, send percentage to V2 Vault and restake. */ function claimAllAndRestake() external override nonReentrant onlyBattleflyBot returns (uint256 amount) { uint256[] memory ids = depositIds.values(); for (uint256 i = 0; i < ids.length; i++) { if (getClaimableEmission(ids[i]) > 0) { amount += _claim(ids[i], address(this)); } } amount = amount + pendingDeposits; uint256 v2VaultAmount = (amount * V2_VAULT_PERCENTAGE) / DENOMINATOR; uint256 treasuryAmount = (amount * TREASURY_PERCENTAGE) / DENOMINATOR; if (v2VaultAmount > 0) { MAGIC.safeApprove(address(FOUNDER_VAULT_V2), v2VaultAmount); FOUNDER_VAULT_V2.topupTodayEmission(v2VaultAmount); } pendingTreasuryAmountToStake += treasuryAmount; if (activeRestakeDepositId == 0 && pendingTreasuryAmountToStake > 0) { activeRestakeDepositId = _deposit(pendingTreasuryAmountToStake); pendingTreasuryAmountToStake = 0; } else if (activeRestakeDepositId != 0 && canWithdraw(activeRestakeDepositId)) { uint256 withdrawn = _withdraw(activeRestakeDepositId, address(this)); uint256 toDeposit = withdrawn + pendingTreasuryAmountToStake; activeRestakeDepositId = _deposit(toDeposit); pendingTreasuryAmountToStake = 0; } else if (activeRestakeDepositId != 0 && canRequestWithdrawal(activeRestakeDepositId)) { ATLAS_STAKER.requestWithdrawal(activeRestakeDepositId); } pendingDeposits = 0; } function topupMagic(uint256 amount) public override nonReentrant { require(amount > 0); MAGIC.safeTransferFrom(msg.sender, address(this), amount); pendingDeposits += amount; emit TopupMagic(msg.sender, amount); } // ================ INTERNAL ================ /** * @dev Withdraw a stake from AtlasStaker (Only possible when the retention period has passed) */ function _withdraw(uint256 _depositId, address user) internal returns (uint256 amount) { require(ATLAS_STAKER.canWithdraw(_depositId), "BattleflyTreasuryFlywheelVault: stake not yet unlocked"); amount = ATLAS_STAKER.withdraw(_depositId); MAGIC.safeTransfer(user, amount); depositIds.remove(_depositId); delete userStakes[_depositId]; emit WithdrawPosition(_depositId, amount); } /** * @dev Claim emission from AtlasStaker */ function _claim(uint256 _depositId, address user) internal returns (uint256 emission) { emission = ATLAS_STAKER.claim(_depositId); MAGIC.safeTransfer(user, emission); emit ClaimEmission(_depositId, emission); } function _deposit(uint256 _amount) internal returns (uint256 atlasStakerDepositId) { atlasStakerDepositId = ATLAS_STAKER.deposit(_amount, TREASURY_LOCK); IBattleflyAtlasStakerV02.VaultStake memory vaultStake = ATLAS_STAKER.getVaultStake(atlasStakerDepositId); UserStake storage userStake = userStakes[atlasStakerDepositId]; userStake.amount = _amount; userStake.lockAt = vaultStake.lockAt; userStake.owner = address(this); userStake.lock = TREASURY_LOCK; depositIds.add(atlasStakerDepositId); emit NewUserStake(atlasStakerDepositId, _amount, vaultStake.unlockAt, address(this), TREASURY_LOCK); } // ================== VIEW ================== /** * @dev Get allowed lock periods from AtlasStaker */ function getAllowedLocks() public view override returns (IAtlasMine.Lock[] memory) { return ATLAS_STAKER.getAllowedLocks(); } /** * @dev Get claimed emission */ function getClaimableEmission(uint256 _depositId) public view override returns (uint256 emission) { (emission, ) = ATLAS_STAKER.getClaimableEmission(_depositId); } /** * @dev Check if a vaultStake is eligible for requesting a withdrawal. * This is 14 epochs before the end of the initial lock period. */ function canRequestWithdrawal(uint256 _depositId) public view override returns (bool requestable) { return ATLAS_STAKER.canRequestWithdrawal(_depositId); } /** * @dev Check if a vaultStake is eligible for a withdrawal * This is when the retention period has passed */ function canWithdraw(uint256 _depositId) public view override returns (bool withdrawable) { return ATLAS_STAKER.canWithdraw(_depositId); } /** * @dev Check the epoch in which the initial lock period of the vaultStake expires. * This is at the end of the lock period */ function initialUnlock(uint256 _depositId) public view override returns (uint64 epoch) { return ATLAS_STAKER.getVaultStake(_depositId).unlockAt; } /** * @dev Check the epoch in which the retention period of the vaultStake expires. * This is 14 epochs after the withdrawal request has taken place */ function retentionUnlock(uint256 _depositId) public view override returns (uint64 epoch) { return ATLAS_STAKER.getVaultStake(_depositId).retentionUnlock; } /** * @dev Get the currently active epoch */ function getCurrentEpoch() public view override returns (uint64 epoch) { return ATLAS_STAKER.currentEpoch(); } /** * @dev Get the deposit ids */ function getDepositIds() public view override returns (uint256[] memory ids) { ids = depositIds.values(); } /** * @dev Return the name of the vault */ function getName() public pure override returns (string memory) { return "Treasury Flywheel Vault"; } // ================== MODIFIERS ================== modifier onlyBattleflyBot() { require(msg.sender == BATTLEFLY_BOT, "BattleflyTreasuryFlywheelVault: caller is not a battlefly bot"); _; } // ================== EVENTS ================== event NewUserStake(uint256 depositId, uint256 amount, uint256 unlockAt, address owner, IAtlasMine.Lock lock); event UpdateUserStake(uint256 depositId, uint256 amount, uint256 unlockAt, address owner, IAtlasMine.Lock lock); event ClaimEmission(uint256 depositId, uint256 emission); event WithdrawPosition(uint256 depositId, uint256 amount); event RequestWithdrawal(uint256 depositId); event TopupMagic(address sender, uint256 amount); event AddedUser(address vault); event RemovedUser(address vault); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal onlyInitializing { __Ownable_init_unchained(); } function __Ownable_init_unchained() internal onlyInitializing { _transferOwnership(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract PausableUpgradeable is Initializable, ContextUpgradeable { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ function __Pausable_init() internal onlyInitializing { __Pausable_init_unchained(); } function __Pausable_init_unchained() internal onlyInitializing { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.0; /** * @dev These functions deal with verification of Merkle Trees proofs. * * The proofs can be generated using the JavaScript library * https://github.com/miguelmota/merkletreejs[merkletreejs]. * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled. * * See `test/utils/cryptography/MerkleProof.test.js` for some examples. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the merkle tree could be reinterpreted as a leaf value. */ library MerkleProofUpgradeable { /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify( bytes32[] memory proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. * * _Available since v4.4._ */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { bytes32 proofElement = proof[i]; if (computedHash <= proofElement) { // Hash(current computed hash + current element of the proof) computedHash = _efficientHash(computedHash, proofElement); } else { // Hash(current element of the proof + current computed hash) computedHash = _efficientHash(proofElement, computedHash); } } return computedHash; } function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/IBattleflyAtlasStaker.sol"; import "./interfaces/IBattleflyAtlasStakerV02.sol"; import "./interfaces/IAtlasMine.sol"; import "./interfaces/ISpecialNFT.sol"; import "./interfaces/IBattleflyFounderVault.sol"; import "./interfaces/IBattleflyFlywheelVault.sol"; import "./interfaces/vaults/IBattleflyFoundersFlywheelVault.sol"; import "./interfaces/vaults/IBattleflyTreasuryFlywheelVault.sol"; contract BattleflyFounderVaultV08 is Initializable, OwnableUpgradeable, ERC1155HolderUpgradeable, ERC721HolderUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; using AddressUpgradeable for address; using SafeCastUpgradeable for uint256; using SafeCastUpgradeable for int256; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; // ============================================ STATE ============================================== struct FounderStake { uint256 amount; uint256 stakeTimestamp; address owner; uint256 lastClaimedDay; } struct DailyFounderEmission { uint256 totalEmission; uint256 totalFounders; } // ============= Global Immutable State ============== /// @notice MAGIC token /// @dev functionally immutable IERC20Upgradeable public magic; ISpecialNFT public founderNFT; uint256 public founderTypeID; // ---- !!! Not Used Anymore !!! ---- IBattleflyAtlasStaker public BattleflyStaker; uint256 public startTimestamp; // ============= Global mutable State ============== uint256 totalEmission; uint256 claimedEmission; uint256 pendingFounderEmission; mapping(address => EnumerableSetUpgradeable.UintSet) private FounderStakeOfOwner; uint256 lastStakeTimestamp; mapping(uint256 => FounderStake) public FounderStakes; uint256 lastStakeId; mapping(address => bool) private adminAccess; uint256 public DaysSinceStart; mapping(uint256 => DailyFounderEmission) public DailyFounderEmissions; uint256 withdrawnOldFounder; uint256 unupdatedStakeIdFrom; uint256 public stakeBackPercent; uint256 public treasuryPercent; uint256 public v2VaultPercent; IBattleflyFounderVault battleflyFounderVaultV2; // ---- !!! Not Used Anymore !!! ---- IBattleflyFlywheelVault battleflyFlywheelVault; // ============= Constant ============== address public constant TREASURY_WALLET = 0xF5411006eEfD66c213d2fd2033a1d340458B7226; uint256 public constant PERCENT_DENOMINATOR = 10000; IAtlasMine.Lock public constant DEFAULT_STAKE_BACK_LOCK = IAtlasMine.Lock.twoWeeks; mapping(uint256 => bool) public claimedPastEmission; uint256 public pastEmissionPerFounder; mapping(uint256 => uint256) public stakeIdOfFounder; mapping(uint256 => EnumerableSetUpgradeable.UintSet) stakingFounderOfStakeId; // ============================================ EVENTS ============================================== event ClaimDailyEmission( uint256 dayTotalEmission, uint256 totalFounderEmission, uint256 totalFounders, uint256 stakeBackAmount, uint256 treasuryAmount, uint256 v2VaultAmount ); event Claim(address user, uint256 stakeId, uint256 amount); event Withdraw(address user, uint256 stakeId, uint256 founderId); event Stake(address user, uint256 stakeId, uint256[] founderNFTIDs); event TopupMagicToStaker(address user, uint256 amount, IAtlasMine.Lock lock); event TopupTodayEmission(address user, uint256 amount); event ClaimPastEmission(address user, uint256 amount, uint256[] tokenIds); // Upgrade Atlas Staker Start bool public claimingIsPaused; EnumerableSetUpgradeable.UintSet depositIds; // ---- !!! New Versions !!! ---- IBattleflyAtlasStakerV02 public BattleflyStakerV2; IBattleflyFoundersFlywheelVault public BattleflyFoundersFlywheelVault; IBattleflyTreasuryFlywheelVault public TREASURY_VAULT; uint256 public activeDepositId; uint256 public activeRestakeDepositId; uint256 public pendingStakeBackAmount; address public BattleflyBot; event WithdrawalFromStaker(uint256 depositId); event RequestWithdrawalFromStaker(uint256 depositId); // Upgrade Atlas Staker End // ============================================ INITIALIZE ============================================== function initialize( address _magicAddress, address _BattleflyStakerAddress, uint256 _founderTypeID, address _founderNFTAddress, uint256 _startTimestamp, address _battleflyFounderVaultV2Address, uint256 _stakeBackPercent, uint256 _treasuryPercent, uint256 _v2VaultPercent ) external initializer { __ERC1155Holder_init(); __ERC721Holder_init(); __Ownable_init(); __ReentrancyGuard_init(); magic = IERC20Upgradeable(_magicAddress); BattleflyStaker = IBattleflyAtlasStaker(_BattleflyStakerAddress); founderNFT = (ISpecialNFT(_founderNFTAddress)); founderTypeID = _founderTypeID; lastStakeTimestamp = block.timestamp; lastStakeId = 0; startTimestamp = _startTimestamp; DaysSinceStart = 0; stakeBackPercent = _stakeBackPercent; treasuryPercent = _treasuryPercent; v2VaultPercent = _v2VaultPercent; if (_battleflyFounderVaultV2Address == address(0)) battleflyFounderVaultV2 = IBattleflyFounderVault(address(this)); else battleflyFounderVaultV2 = IBattleflyFounderVault(_battleflyFounderVaultV2Address); require(stakeBackPercent + treasuryPercent + v2VaultPercent <= PERCENT_DENOMINATOR); // Approve the AtlasStaker contract to spend the magic magic.safeApprove(address(BattleflyStaker), 2**256 - 1); } // ============================================ USER OPERATIONS ============================================== /** * @dev Claim past emissions for all owned founders tokens */ function claimPastEmission() external { require(pastEmissionPerFounder != 0, "No past founder emission to claim"); uint256[] memory tokenIds = getPastEmissionClaimableTokens(msg.sender); require(tokenIds.length > 0, "No tokens to claim"); for (uint256 i = 0; i < tokenIds.length; i++) { claimedPastEmission[tokenIds[i]] = true; } magic.safeTransfer(msg.sender, pastEmissionPerFounder * tokenIds.length); emit ClaimPastEmission(msg.sender, pastEmissionPerFounder * tokenIds.length, tokenIds); } /** * @dev get all tokens eligible for cliaming past emissions */ function getPastEmissionClaimableTokens(address user) public view returns (uint256[] memory) { uint256 balance = founderNFT.balanceOf(user); uint256[] memory tokenIds = new uint256[](balance); uint256 countClaimable = 0; for (uint256 i = 0; i < balance; i++) { uint256 tokenId = founderNFT.tokenOfOwnerByIndex(user, i); uint256 tokenType = founderNFT.getSpecialNFTType(tokenId); if (tokenType == founderTypeID && claimedPastEmission[tokenId] == false) { tokenIds[countClaimable] = tokenId; countClaimable++; } } (, uint256[][] memory stakeTokens) = stakesOf(user); uint256 countClaimableStaked = 0; uint256 balanceStaked = 0; for (uint256 i = 0; i < stakeTokens.length; i++) { balanceStaked += stakeTokens[i].length; } uint256[] memory stakingTokenIds = new uint256[](balanceStaked); for (uint256 i = 0; i < stakeTokens.length; i++) { uint256[] memory stakeTokenIds = stakeTokens[i]; for (uint256 j = 0; j < stakeTokenIds.length; j++) { uint256 tokenId = stakeTokenIds[j]; uint256 tokenType = founderNFT.getSpecialNFTType(tokenId); if (tokenType == founderTypeID && claimedPastEmission[tokenId] == false) { stakingTokenIds[countClaimableStaked] = tokenId; countClaimableStaked++; } } } uint256[] memory result = new uint256[](countClaimable + countClaimableStaked); for (uint256 i = 0; i < countClaimable; i++) { result[i] = tokenIds[i]; } for (uint256 i = countClaimable; i < countClaimable + countClaimableStaked; i++) { result[i] = stakingTokenIds[i - countClaimable]; } return result; } /** * @dev set founder tokens that can claim past emissions */ function setTokenClaimedPastEmission(uint256[] memory tokenIds, bool isClaimed) external onlyOwner { for (uint256 i = 0; i < tokenIds.length; i++) { claimedPastEmission[tokenIds[i]] = isClaimed; } } /** * @dev set the amount of past emissions per founder token */ function setPastEmission(uint256 amount) external onlyOwner { pastEmissionPerFounder = amount; } /** * @dev returns the stake objects and the corresponding founder tokens in the stakes of a specific owner */ function stakesOf(address owner) public view returns (FounderStake[] memory, uint256[][] memory) { FounderStake[] memory stakes = new FounderStake[](FounderStakeOfOwner[owner].length()); uint256[][] memory _founderIDsOfStake = new uint256[][](FounderStakeOfOwner[owner].length()); for (uint256 i = 0; i < FounderStakeOfOwner[owner].length(); i++) { stakes[i] = FounderStakes[FounderStakeOfOwner[owner].at(i)]; _founderIDsOfStake[i] = stakingFounderOfStakeId[FounderStakeOfOwner[owner].at(i)].values(); } return (stakes, _founderIDsOfStake); } function isOwner(address owner, uint256 tokenId) public view returns (bool) { (, uint256[][] memory tokensPerStake) = stakesOf(owner); for (uint256 i = 0; i < tokensPerStake.length; i++) { for (uint256 j = 0; j < tokensPerStake[i].length; j++) { if (tokensPerStake[i][j] == tokenId) { return true; } } } return false; } /** * @dev Returns the founder tokens balance of an owner */ function balanceOf(address owner) external view returns (uint256 balance) { require(owner != address(0), "ERC721: balance query for the zero address"); uint256 balanceOfUser = 0; uint256 founderStakeCount = FounderStakeOfOwner[owner].length(); for (uint256 i = 0; i < founderStakeCount; i++) { balanceOfUser += FounderStakes[FounderStakeOfOwner[owner].at(i)].amount; } return balanceOfUser; } /** * @dev Stake a list of founder tokens */ function stakeFounderNFT(uint256[] memory ids) external { require(ids.length != 0, "Must provide at least one founder NFT ID"); for (uint256 i = 0; i < ids.length; i++) { require(founderNFT.getSpecialNFTType(ids[i]) == founderTypeID, "Not valid founder NFT"); founderNFT.safeTransferFrom(msg.sender, address(this), ids[i]); } uint256 currentDay = DaysSinceStart; lastStakeId++; FounderStakes[lastStakeId] = ( FounderStake({ amount: ids.length, stakeTimestamp: block.timestamp, owner: msg.sender, lastClaimedDay: currentDay }) ); for (uint256 i = 0; i < ids.length; i++) { stakeIdOfFounder[ids[i]] = lastStakeId; stakingFounderOfStakeId[lastStakeId].add(ids[i]); } FounderStakeOfOwner[msg.sender].add(lastStakeId); emit Stake(msg.sender, lastStakeId, ids); } /** * @dev Indicates if claiming is paused */ function isPaused() external view returns (bool) { return claimingIsPaused; } /** * @dev Claim all emissions for the founder tokens owned by the sender */ function claimAll() external nonReentrant { if (claimingIsPaused) { revert("Claiming is currently paused, please try again later"); } uint256 totalReward = 0; for (uint256 i = 0; i < FounderStakeOfOwner[msg.sender].length(); i++) { totalReward += _claimByStakeId(FounderStakeOfOwner[msg.sender].at(i)); } require(totalReward > 0, "No reward to claim"); } /** * @dev Withdraw all founder tokens of the sender */ function withdrawAll() external nonReentrant { require(FounderStakeOfOwner[msg.sender].length() > 0, "No STAKE to withdraw"); uint256 totalWithdraw = 0; uint256[] memory stakeIds = FounderStakeOfOwner[msg.sender].values(); for (uint256 i = 0; i < stakeIds.length; i++) { uint256 stakeId = stakeIds[i]; _claimByStakeId(stakeId); totalWithdraw += FounderStakes[stakeId].amount; _withdrawByStakeId(stakeId); } _checkStakingAmount(totalWithdraw); } function _withdrawByStakeId(uint256 stakeId) internal { FounderStake storage stake = FounderStakes[stakeId]; _claimByStakeId(stakeId); for (uint256 i = 0; i < stakingFounderOfStakeId[stakeId].length(); i++) { founderNFT.safeTransferFrom(address(this), stake.owner, stakingFounderOfStakeId[stakeId].at(i)); emit Withdraw(stake.owner, stakeId, stakingFounderOfStakeId[stakeId].at(i)); } if (stake.stakeTimestamp < (startTimestamp + (DaysSinceStart) * 24 hours - 24 hours)) { withdrawnOldFounder += stakingFounderOfStakeId[stakeId].length(); } FounderStakeOfOwner[stake.owner].remove(stakeId); delete FounderStakes[stakeId]; delete stakingFounderOfStakeId[stakeId]; } function _claimByStakeId(uint256 stakeId) internal returns (uint256) { require(stakeId != 0, "No stake to claim"); FounderStake storage stake = FounderStakes[stakeId]; uint256 totalReward = _getClaimableEmissionOf(stakeId); claimedEmission += totalReward; stake.lastClaimedDay = DaysSinceStart; magic.safeTransfer(stake.owner, totalReward); emit Claim(stake.owner, stakeId, totalReward); return totalReward; } /** * @dev Withdraw a list of founder tokens */ function withdraw(uint256[] memory founderIds) external nonReentrant { require(founderIds.length > 0, "No Founder to withdraw"); uint256 totalWithdraw = 0; for (uint256 i = 0; i < founderIds.length; i++) { uint256 stakeId = stakeIdOfFounder[founderIds[i]]; require(FounderStakes[stakeId].owner == msg.sender, "Not your stake"); _claimBeforeWithdraw(founderIds[i]); _withdraw(founderIds[i]); totalWithdraw++; } _checkStakingAmount(totalWithdraw); } function _checkStakingAmount(uint256 totalWithdraw) internal view { uint256 stakeableAmountPerFounder = founderTypeID == 150 ? BattleflyFoundersFlywheelVault.STAKING_LIMIT_V1() : BattleflyFoundersFlywheelVault.STAKING_LIMIT_V2(); uint256 currentlyRemaining = BattleflyFoundersFlywheelVault.remainingStakeableAmount(msg.sender); uint256 currentlyStaked = BattleflyFoundersFlywheelVault.getStakedAmount(msg.sender); uint256 remainingAfterSubstraction = (stakeableAmountPerFounder * totalWithdraw) <= currentlyRemaining ? currentlyRemaining - stakeableAmountPerFounder * totalWithdraw : 0; require(currentlyStaked <= remainingAfterSubstraction, "Pls withdraw from FlywheelVault first"); } function _claimBeforeWithdraw(uint256 founderId) internal returns (uint256) { uint256 stakeId = stakeIdOfFounder[founderId]; FounderStake storage stake = FounderStakes[stakeId]; uint256 founderReward = _getClaimableEmissionOf(stakeId) / stake.amount; claimedEmission += founderReward; magic.safeTransfer(stake.owner, founderReward); emit Claim(stake.owner, stakeId, founderReward); return founderReward; } /** * @dev Get the emissions claimable by a certain user */ function getClaimableEmissionOf(address user) public view returns (uint256) { uint256 totalReward = 0; for (uint256 i = 0; i < FounderStakeOfOwner[user].length(); i++) { totalReward += _getClaimableEmissionOf(FounderStakeOfOwner[user].at(i)); } return totalReward; } function _getClaimableEmissionOf(uint256 stakeId) internal view returns (uint256) { uint256 totalReward = 0; FounderStake memory stake = FounderStakes[stakeId]; if (stake.lastClaimedDay == DaysSinceStart) return 0; for (uint256 j = stake.lastClaimedDay + 1; j <= DaysSinceStart; j++) { if (DailyFounderEmissions[j].totalFounders == 0 || stake.amount == 0) continue; totalReward += (DailyFounderEmissions[j].totalEmission / DailyFounderEmissions[j].totalFounders) * stake.amount; } return totalReward; } function _withdraw(uint256 founderId) internal { uint256 stakeId = stakeIdOfFounder[founderId]; FounderStake storage stake = FounderStakes[stakeId]; founderNFT.safeTransferFrom(address(this), stake.owner, founderId); stake.amount--; delete stakeIdOfFounder[founderId]; stakingFounderOfStakeId[stakeId].remove(founderId); if (stake.stakeTimestamp < (startTimestamp + (DaysSinceStart) * 24 hours - 24 hours)) { withdrawnOldFounder += 1; } if (stake.amount == 0) { FounderStakeOfOwner[stake.owner].remove(stakeId); delete FounderStakes[stakeId]; } emit Withdraw(stake.owner, stakeId, founderId); } function _depositToStaker(uint256 amount, IAtlasMine.Lock lock) internal returns (uint256 depositId) { depositId = BattleflyStakerV2.deposit(amount, lock); depositIds.add(depositId); } function _updateTotalStakingFounders(uint256 currentDay) private returns (uint256) { uint256 result = DailyFounderEmissions[DaysSinceStart].totalFounders - withdrawnOldFounder; withdrawnOldFounder = 0; uint256 to = startTimestamp + currentDay * 24 hours; uint256 i = unupdatedStakeIdFrom; for (; i <= lastStakeId; i++) { if (FounderStakes[i].stakeTimestamp == 0) { continue; } if (FounderStakes[i].stakeTimestamp > to) { break; } result += FounderStakes[i].amount; } unupdatedStakeIdFrom = i; return result; } function _claimAllFromStaker() private returns (uint256 amount) { uint256[] memory ids = depositIds.values(); for (uint256 i = 0; i < ids.length; i++) { (uint256 pending, ) = BattleflyStakerV2.getClaimableEmission(ids[i]); if (pending > 0) { amount += BattleflyStakerV2.claim(ids[i]); } } } function _stakeBack(uint256 stakeBackAmount) internal { pendingStakeBackAmount += stakeBackAmount; if (activeRestakeDepositId == 0 && pendingStakeBackAmount > 0) { activeRestakeDepositId = _depositToStaker(pendingStakeBackAmount, DEFAULT_STAKE_BACK_LOCK); pendingStakeBackAmount = 0; } else if (activeRestakeDepositId > 0 && BattleflyStakerV2.canWithdraw(activeRestakeDepositId)) { uint256 withdrawn = BattleflyStakerV2.withdraw(activeRestakeDepositId); depositIds.remove(activeRestakeDepositId); uint256 toDeposit = withdrawn + pendingStakeBackAmount; activeRestakeDepositId = _depositToStaker(toDeposit, DEFAULT_STAKE_BACK_LOCK); depositIds.add(activeRestakeDepositId); pendingStakeBackAmount = 0; } else if (activeRestakeDepositId > 0 && BattleflyStakerV2.canRequestWithdrawal(activeRestakeDepositId)) { BattleflyStakerV2.requestWithdrawal(activeRestakeDepositId); } pendingFounderEmission = 0; } /** * @dev Return the name of the vault */ function getName() public view returns (string memory) { if (founderTypeID == 150) { return "V1 Stakers Vault"; } else { return "V2 Stakers Vault"; } } // ============================================ ADMIN OPERATIONS ============================================== /** * @dev Topup magic directly to the atlas staker */ function topupMagicToStaker(uint256 amount, IAtlasMine.Lock lock) external onlyAdminAccess { require(amount > 0); magic.safeTransferFrom(msg.sender, address(this), amount); _depositToStaker(amount, lock); emit TopupMagicToStaker(msg.sender, amount, lock); } /** * @dev Topup magic to be staked in the daily emission batch */ function topupTodayEmission(uint256 amount) external onlyAdminAccess { require(amount > 0); magic.safeTransferFrom(msg.sender, address(this), amount); pendingFounderEmission += amount; emit TopupTodayEmission(msg.sender, amount); } // to support initial staking period, only to be run after staking period is over function setFounderStakesToStart() public onlyAdminAccess nonReentrant { uint256 length = lastStakeId; for (uint256 i = 0; i <= length; i++) { FounderStakes[i].stakeTimestamp = startTimestamp; FounderStakes[i].lastClaimedDay = 0; } } /** * @dev Update the claimed founder emission for a certain day */ function updateClaimedFounderEmission(uint256 amount, uint256 currentDay) external onlyAdminAccess { DaysSinceStart = currentDay; uint256 todayTotalFounderNFTs = _updateTotalStakingFounders(currentDay); DailyFounderEmissions[DaysSinceStart] = DailyFounderEmission({ totalEmission: amount, totalFounders: todayTotalFounderNFTs }); } /** * @dev Get the current day */ function getCurrentDay() public view onlyAdminAccess returns (uint256) { return DaysSinceStart; } /** * @dev Get the daily founder emission for a specific day */ function getDailyFounderEmission(uint256 currentDay) public view onlyAdminAccess returns (uint256[2] memory) { return [DailyFounderEmissions[currentDay].totalEmission, DailyFounderEmissions[currentDay].totalFounders]; } /** * @dev set the start timestamp */ function setStartTimestamp(uint256 newTimestamp) public onlyAdminAccess { startTimestamp = newTimestamp; } /** * @dev Simulate a claim for a specific token id */ function simulateClaim(uint256 tokenId) public view onlyAdminAccess returns (uint256) { uint256 stakeId = stakeIdOfFounder[tokenId]; return _getClaimableEmissionOf(stakeId); } /** * @dev Pause or unpause claiming */ function pauseClaim(bool doPause) external onlyAdminAccess { claimingIsPaused = doPause; } /** * @dev Reduce the total emission */ function reduceTotalEmission(uint256 amount) external onlyAdminAccess { totalEmission -= amount; } /** * @dev Increase the total emission */ function increaseTotalEmission(uint256 amount) external onlyAdminAccess { totalEmission += amount; } /** * @dev Recalculate the total amount of founders to be included for every day starting from a specific day */ function recalculateTotalFounders(uint256 dayToStart) external onlyAdminAccess { uint256 base = DailyFounderEmissions[dayToStart].totalFounders; for (uint256 index = dayToStart + 1; index <= DaysSinceStart; index++) { DailyFounderEmission storage daily = DailyFounderEmissions[index]; daily.totalFounders += base; } } /** * @dev Claim daily emissions from AtlasStaker and distribute over founder token stakers */ function claimDailyEmission() public onlyBattleflyBot nonReentrant { uint256 currentDay = DaysSinceStart + 1; uint256 todayTotalEmission = _claimAllFromStaker(); uint256 todayTotalFounderNFTs = _updateTotalStakingFounders(currentDay); uint256 stakeBackAmount; uint256 v2VaultAmount; uint256 treasuryAmount; uint256 founderEmission; if (todayTotalEmission != 0) { stakeBackAmount = ((todayTotalEmission * stakeBackPercent) / PERCENT_DENOMINATOR); _stakeBack(stakeBackAmount + pendingFounderEmission); v2VaultAmount = (todayTotalEmission * v2VaultPercent) / PERCENT_DENOMINATOR; if (v2VaultAmount != 0) battleflyFounderVaultV2.topupTodayEmission(v2VaultAmount); treasuryAmount = (todayTotalEmission * treasuryPercent) / PERCENT_DENOMINATOR; if (treasuryAmount != 0) { magic.approve(address(TREASURY_VAULT), treasuryAmount); TREASURY_VAULT.topupMagic(treasuryAmount); } founderEmission += todayTotalEmission - stakeBackAmount - v2VaultAmount - treasuryAmount; } else if (pendingFounderEmission > 0) { _stakeBack(pendingFounderEmission); } else { _stakeBack(0); } totalEmission += founderEmission; DaysSinceStart = currentDay; DailyFounderEmissions[DaysSinceStart] = DailyFounderEmission({ totalEmission: founderEmission, totalFounders: todayTotalFounderNFTs }); emit ClaimDailyEmission( todayTotalEmission, founderEmission, todayTotalFounderNFTs, stakeBackAmount, treasuryAmount, v2VaultAmount ); } /** * @dev Withdraw all withdrawable deposit ids from the vault in the Atlas Staker */ function withdrawAllFromStaker() external onlyAdminAccess { uint256[] memory ids = depositIds.values(); withdrawFromStaker(ids); } function withdrawFromStaker(uint256[] memory ids) public onlyAdminAccess { claimDailyEmission(); require(ids.length > 0, "BattleflyFlywheelVault: No deposited funds"); for (uint256 i = 0; i < ids.length; i++) { if (BattleflyStakerV2.canWithdraw(ids[i])) { BattleflyStakerV2.withdraw(ids[i]); depositIds.remove(ids[i]); emit WithdrawalFromStaker(ids[i]); } } } /** * @dev Request a withdrawal from Atlas Staker for all claimable deposit ids */ function requestWithdrawAllFromStaker() external onlyAdminAccess { uint256[] memory ids = depositIds.values(); requestWithdrawFromStaker(ids); } /** * @dev Request a withdrawal from Atlas Staker for specific deposit ids */ function requestWithdrawFromStaker(uint256[] memory ids) public onlyAdminAccess { for (uint256 i = 0; i < ids.length; i++) { if (BattleflyStakerV2.canRequestWithdrawal(ids[i])) { BattleflyStakerV2.requestWithdrawal(ids[i]); emit RequestWithdrawalFromStaker(ids[i]); } } } /** * @dev Withdraw a specific magic amount from the vault and send it to a receiver */ function withdrawFromVault(address receiver, uint256 amount) external onlyAdminAccess { magic.safeTransfer(receiver, amount); } /** * @dev Set the daily founder emissions for a specific day */ function setDailyFounderEmissions( uint256 day, uint256 amount, uint256 stakers ) external onlyAdminAccess { DailyFounderEmissions[day] = DailyFounderEmission(amount, stakers); } /** * @dev Set the treasury vault address */ function setTreasuryVault(address _treasuryAddress) external onlyAdminAccess { require(_treasuryAddress != address(0)); TREASURY_VAULT = IBattleflyTreasuryFlywheelVault(_treasuryAddress); } //Must be called right after init /** * @dev Set the flywheel vault address */ function setFlywheelVault(address vault) external onlyOwner { require(vault != address(0)); BattleflyFoundersFlywheelVault = IBattleflyFoundersFlywheelVault(vault); } //Must be called right after init /** * @dev Set the battlefly bot address */ function setBattleflyBot(address _battleflyBot) external onlyOwner { require(_battleflyBot != address(0)); BattleflyBot = _battleflyBot; } //Must be called right after init /** * @dev Set the battlefly staker address */ function setBattleflyStaker(address staker) external onlyOwner { require(staker != address(0)); BattleflyStakerV2 = IBattleflyAtlasStakerV02(staker); // Approve the AtlasStaker contract to spend the magic magic.approve(address(BattleflyStakerV2), 2**256 - 1); } //Must be called right after init /** * @dev Set the founder vault address */ function setFounderVaultV2(address founderVault) external onlyOwner { require(founderVault != address(0)); battleflyFounderVaultV2 = IBattleflyFounderVault(founderVault); // Approve the FounderVault contract to spend the magic magic.approve(address(battleflyFounderVaultV2), 2**256 - 1); } //Must be called right after init /** * @dev Set the distribution percentages */ function setPercentages( uint256 _stakeBackPercent, uint256 _treasuryPercent, uint256 _v2VaultPercent ) external onlyOwner { require(_stakeBackPercent + _treasuryPercent + _v2VaultPercent <= PERCENT_DENOMINATOR); stakeBackPercent = _stakeBackPercent; treasuryPercent = _treasuryPercent; v2VaultPercent = _v2VaultPercent; } /** * @dev Set admin access for a specific user */ function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; } modifier onlyAdminAccess() { require(adminAccess[_msgSender()] || _msgSender() == owner(), "Require admin access"); _; } modifier onlyBattleflyBot() { require(msg.sender == BattleflyBot, "Require battlefly bot"); _; } }
// SPDX-License-Identifier: MIT pragma solidity >0.8.0; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "./IAtlasMine.sol"; interface IBattleflyAtlasStakerV02 { struct Vault { uint16 fee; uint16 claimRate; bool enabled; } struct VaultStake { uint64 lockAt; uint64 unlockAt; uint64 retentionUnlock; uint256 amount; uint256 paidEmission; address vault; IAtlasMine.Lock lock; } function MAGIC() external returns (IERC20Upgradeable); function deposit(uint256, IAtlasMine.Lock) external returns (uint256); function withdraw(uint256) external returns (uint256); function claim(uint256) external returns (uint256); function requestWithdrawal(uint256) external returns (uint64); function currentDepositId() external view returns (uint256); function getAllowedLocks() external view returns (IAtlasMine.Lock[] memory); function getVaultStake(uint256) external view returns (VaultStake memory); function getClaimableEmission(uint256) external view returns (uint256, uint256); function canWithdraw(uint256 _depositId) external view returns (bool withdrawable); function canRequestWithdrawal(uint256 _depositId) external view returns (bool requestable); function currentEpoch() external view returns (uint64 epoch); function getLockPeriod(IAtlasMine.Lock) external view returns (uint64 epoch); function setPause(bool _paused) external; function depositIdsOfVault(address vault) external view returns (uint256[] memory depositIds); // ========== Events ========== event AddedSuperAdmin(address who); event RemovedSuperAdmin(address who); event AddedVault(address indexed vault, uint16 fee, uint16 claimRate); event RemovedVault(address indexed vault); event StakedTreasure(address staker, uint256 tokenId, uint256 amount); event UnstakedTreasure(address staker, uint256 tokenId, uint256 amount); event StakedLegion(address staker, uint256 tokenId); event UnstakedLegion(address staker, uint256 tokenId); event SetTreasury(address treasury); event SetBattleflyBot(address bot); event NewDeposit(address indexed vault, uint256 amount, uint256 unlockedAt, uint256 indexed depositId); event WithdrawPosition(address indexed vault, uint256 amount, uint256 indexed depositId); event ClaimEmission(address indexed vault, uint256 amount, uint256 indexed depositId); event RequestWithdrawal(address indexed vault, uint64 withdrawalEpoch, uint256 indexed depositId); event DepositedAllToMine(uint256 amount); event SetPause(bool paused); }
// SPDX-License-Identifier: MIT pragma solidity >0.8.0; import "../IAtlasMine.sol"; interface IBattleflyTreasuryFlywheelVault { struct UserStake { uint64 lockAt; uint256 amount; address owner; IAtlasMine.Lock lock; } function deposit(uint128 _amount) external returns (uint256 atlasStakerDepositId); function withdraw(uint256[] memory _depositIds, address user) external returns (uint256 amount); function withdrawAll(address user) external returns (uint256 amount); function requestWithdrawal(uint256[] memory _depositIds) external; function claim(uint256 _depositId, address user) external returns (uint256 emission); function claimAll(address user) external returns (uint256 amount); function claimAllAndRestake() external returns (uint256 amount); function topupMagic(uint256 amount) external; function getAllowedLocks() external view returns (IAtlasMine.Lock[] memory); function getClaimableEmission(uint256) external view returns (uint256); function canRequestWithdrawal(uint256 _depositId) external view returns (bool requestable); function canWithdraw(uint256 _depositId) external view returns (bool withdrawable); function initialUnlock(uint256 _depositId) external view returns (uint64 epoch); function retentionUnlock(uint256 _depositId) external view returns (uint64 epoch); function getCurrentEpoch() external view returns (uint64 epoch); function getDepositIds() external view returns (uint256[] memory ids); function getName() external pure returns (string memory); }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol"; interface IBattlefly is IERC721EnumerableUpgradeable { function mintBattlefly(address receiver, uint256 battleflyType) external returns (uint256); function mintBattleflies( address receiver, uint256 _battleflyType, uint256 amount ) external returns (uint256[] memory); function getBattleflyType(uint256) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity >0.8.0; interface IAtlasMine { enum Lock { twoWeeks, oneMonth, threeMonths, sixMonths, twelveMonths } struct UserInfo { uint256 originalDepositAmount; uint256 depositAmount; uint256 lpAmount; uint256 lockedUntil; uint256 vestingLastUpdate; int256 rewardDebt; Lock lock; } function treasure() external view returns (address); function legion() external view returns (address); function unlockAll() external view returns (bool); function boosts(address user) external view returns (uint256); function userInfo(address user, uint256 depositId) external view returns ( uint256 originalDepositAmount, uint256 depositAmount, uint256 lpAmount, uint256 lockedUntil, uint256 vestingLastUpdate, int256 rewardDebt, Lock lock ); function getLockBoost(Lock _lock) external pure returns (uint256 boost, uint256 timelock); function getVestingTime(Lock _lock) external pure returns (uint256 vestingTime); function stakeTreasure(uint256 _tokenId, uint256 _amount) external; function unstakeTreasure(uint256 _tokenId, uint256 _amount) external; function stakeLegion(uint256 _tokenId) external; function unstakeLegion(uint256 _tokenId) external; function withdrawPosition(uint256 _depositId, uint256 _amount) external returns (bool); function withdrawAll() external; function pendingRewardsAll(address _user) external view returns (uint256 pending); function deposit(uint256 _amount, Lock _lock) external; function harvestAll() external; function harvestPosition(uint256 _depositId) external; function currentId(address _user) external view returns (uint256); function pendingRewardsPosition(address _user, uint256 _depositId) external view returns (uint256); function getAllUserDepositIds(address) external view returns (uint256[] memory); }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; import "./IAtlasMine.sol"; interface IBattleflyFounderVault { struct FounderStake { uint256 amount; uint256 stakeTimestamp; address owner; uint256[] founderNFTIDs; uint256 lastClaimedDay; } function topupTodayEmission(uint256 amount) external; function topupMagicToStaker(uint256 amount, IAtlasMine.Lock lock) external; function depositToStaker(uint256 amount, IAtlasMine.Lock lock) external; function stakesOf(address owner) external view returns (FounderStake[] memory); function isOwner(address owner, uint256 tokenId) external view returns (bool); function balanceOf(address owner) external view returns (uint256 balance); function getName() external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/utils/ERC721Holder.sol) pragma solidity ^0.8.0; import "../IERC721ReceiverUpgradeable.sol"; import "../../../proxy/utils/Initializable.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 ERC721HolderUpgradeable is Initializable, IERC721ReceiverUpgradeable { function __ERC721Holder_init() internal onlyInitializing { } function __ERC721Holder_init_unchained() internal onlyInitializing { } /** * @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; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity >0.8.0; import "./IAtlasMine.sol"; interface IBattleflyAtlasStaker { // ============= Events ============== event VaultDeposit( address indexed vault, uint256 indexed depositId, uint256 amount, uint256 unlockAt, IAtlasMine.Lock lock ); event VaultWithdraw(address indexed vault, uint256 indexed depositId, uint256 amount, uint256 reward); event VaultClaim(address indexed vault, uint256 indexed depositId, uint256 reward); event MineStake(uint256 currentDepositId, uint256 unlockTime); event MineHarvest(uint256 earned, uint256 feeEarned, uint256 feeRefunded); event StakeNFT(address indexed vault, address indexed nft, uint256 tokenId, uint256 amount, uint256 currentBoost); event UnstakeNFT(address indexed vault, address indexed nft, uint256 tokenId, uint256 amount, uint256 currentBoost); event StakingPauseToggle(bool paused); event WithdrawFeesToTreasury(uint256 amount); event SetFeeWhitelistVault(address vault, bool isSet); event SetBattleflyVault(address vault, bool isSet); // ================= Data Types ================== struct Stake { uint256 amount; uint256 unlockAt; uint256 depositId; } struct VaultStake { uint256 amount; uint256 unlockAt; int256 rewardDebt; IAtlasMine.Lock lock; } struct VaultOwner { uint256 share; int256 rewardDebt; address owner; uint256 unclaimedReward; } // =============== View Functions ================ function getVaultStake(address vault, uint256 depositId) external returns (VaultStake memory); // function vaultTotalStake(address vault) external returns (uint256); function pendingRewards(address vault, uint256 depositId) external view returns (uint256); function pendingRewardsAll(address vault) external returns (uint256); function totalMagic() external returns (uint256); // function totalPendingStake() external returns (uint256); function totalWithdrawableMagic() external returns (uint256); // ============= Staking Operations ============== function deposit(uint256 _amount, IAtlasMine.Lock lock) external returns (uint256); function withdraw(uint256 depositId) external; function withdrawAll() external; function claim(uint256 depositId) external returns (uint256); function claimAll() external returns (uint256); // function withdrawEmergency() external; function stakeScheduled() external; // ============= Owner Operations ============== function unstakeAllFromMine() external; function unstakeToTarget(uint256 target) external; // function emergencyUnstakeAllFromMine() external; function setBoostAdmin(address _hoard, bool isSet) external; function approveNFTs() external; // function revokeNFTApprovals() external; // function setMinimumStakingWait(uint256 wait) external; function toggleSchedulePause(bool paused) external; }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol"; import "./IMod.sol"; interface ISpecialNFT is IERC721EnumerableUpgradeable { function mintSpecialNFT(address receiver, uint256 specialNFTType) external returns (uint256); function mintSpecialNFTs( address receiver, uint256 _specialNFTType, uint256 amount ) external returns (uint256[] memory); function getSpecialNFTType(uint256 tokenId) external view returns (uint256); }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; interface IBattleflyFlywheelVault { function getStakeAmount(address user) external view returns (uint256, uint256); function stakeableAmountPerV1() external view returns (uint256); function stakeableAmountPerV2() external view returns (uint256); function stakeableAmountPerFounder(address vault) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity >0.8.0; import "../IAtlasMine.sol"; interface IBattleflyFoundersFlywheelVault { struct UserStake { uint64 lockAt; uint256 amount; address owner; IAtlasMine.Lock lock; } function deposit(uint128, IAtlasMine.Lock) external returns (uint256); function withdraw(uint256[] memory _depositIds) external returns (uint256); function withdrawAll() external returns (uint256); function requestWithdrawal(uint256[] memory _depositIds) external; function claim(uint256) external returns (uint256); function claimAll() external returns (uint256); function getAllowedLocks() external view returns (IAtlasMine.Lock[] memory); function getClaimableEmission(uint256) external view returns (uint256); function canRequestWithdrawal(uint256 _depositId) external view returns (bool requestable); function canWithdraw(uint256 _depositId) external view returns (bool withdrawable); function initialUnlock(uint256 _depositId) external view returns (uint64 epoch); function retentionUnlock(uint256 _depositId) external view returns (uint64 epoch); function getCurrentEpoch() external view returns (uint64 epoch); function remainingStakeableAmount(address user) external view returns (uint256 remaining); function getStakedAmount(address user) external view returns (uint256 amount); function getDepositIdsOfUser(address user) external view returns (uint256[] memory depositIds); function getName() external pure returns (string memory); function STAKING_LIMIT_V1() external view returns (uint256); function STAKING_LIMIT_V2() external view returns (uint256); }
// 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 IERC721ReceiverUpgradeable { /** * @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 (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol) pragma solidity ^0.8.0; import "../IERC721Upgradeable.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721EnumerableUpgradeable is IERC721Upgradeable { /** * @dev Returns the total amount of tokens stored by the contract. */ function totalSupply() external view returns (uint256); /** * @dev Returns a token ID owned by `owner` at a given `index` of its token list. * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. */ function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); /** * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. * Use along with {totalSupply} to enumerate all tokens. */ function tokenByIndex(uint256 index) external view returns (uint256); }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol"; interface IMod is IERC721EnumerableUpgradeable { function mintMod(address receiver) external returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/cryptography/MerkleProofUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "../BattleflyFounderVaultV08.sol"; import "../interfaces/IBattleflyAtlasStakerV02.sol"; import "../interfaces/vaults/IBattleflyFoundersFlywheelVault.sol"; import "../interfaces/IBattlefly.sol"; import "../interfaces/IAtlasMine.sol"; import "../interfaces/IBattleflyFounderVault.sol"; contract BattleflyFoundersFlywheelVault is IBattleflyFoundersFlywheelVault, Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable, PausableUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; /** * @dev Immutable states */ IERC20Upgradeable public MAGIC; IBattleflyAtlasStakerV02 public ATLAS_STAKER; IBattleflyFounderVault public FOUNDER_VAULT_V1; IBattleflyFounderVault public FOUNDER_VAULT_V2; uint256 public override STAKING_LIMIT_V1; uint256 public override STAKING_LIMIT_V2; /** * @dev User stake data * { depositId } => { User stake data } */ mapping(uint256 => UserStake) public userStakes; /** * @dev User's depositIds * { user } => { depositIds } */ mapping(address => EnumerableSetUpgradeable.UintSet) private depositIdByUser; /** * @dev Whitelisted users * { user } => { is whitelisted } */ mapping(address => bool) public whitelistedUsers; function initialize( address _magic, address _atlasStaker, address _battleflyFounderVaultV1, address _battleflyFounderVaultV2 ) external initializer { __Ownable_init(); __ReentrancyGuard_init(); __Pausable_init(); require(_magic != address(0), "BattleflyFlywheelVault: invalid address"); require(_atlasStaker != address(0), "BattleflyFlywheelVault: invalid address"); require(_battleflyFounderVaultV1 != address(0), "BattleflyFlywheelVault: invalid address"); require(_battleflyFounderVaultV2 != address(0), "BattleflyFlywheelVault: invalid address"); MAGIC = IERC20Upgradeable(_magic); ATLAS_STAKER = IBattleflyAtlasStakerV02(_atlasStaker); FOUNDER_VAULT_V1 = IBattleflyFounderVault(_battleflyFounderVaultV1); FOUNDER_VAULT_V2 = IBattleflyFounderVault(_battleflyFounderVaultV2); STAKING_LIMIT_V1 = 20000e18; STAKING_LIMIT_V2 = 10000e18; } /** * @dev Deposit funds to AtlasStaker */ function deposit(uint128 _amount, IAtlasMine.Lock _lock) external override nonReentrant onlyMembers returns (uint256 atlasStakerDepositId) { if (!whitelistedUsers[msg.sender]) { require( remainingStakeableAmount(msg.sender) >= _amount, "BattleflyFlywheelVault: amount exceeds stakeable amount" ); } MAGIC.safeTransferFrom(msg.sender, address(this), _amount); MAGIC.safeApprove(address(ATLAS_STAKER), _amount); atlasStakerDepositId = ATLAS_STAKER.deposit(uint256(_amount), _lock); IBattleflyAtlasStakerV02.VaultStake memory vaultStake = ATLAS_STAKER.getVaultStake(atlasStakerDepositId); UserStake storage userStake = userStakes[atlasStakerDepositId]; userStake.amount = _amount; userStake.lockAt = vaultStake.lockAt; userStake.owner = msg.sender; userStake.lock = _lock; depositIdByUser[msg.sender].add(atlasStakerDepositId); emit NewUserStake(atlasStakerDepositId, _amount, vaultStake.unlockAt, msg.sender, _lock); } /** * @dev Withdraw staked funds from AtlasStaker */ function withdraw(uint256[] memory _depositIds) public override nonReentrant returns (uint256 amount) { for (uint256 i = 0; i < _depositIds.length; i++) { amount += _withdraw(_depositIds[i]); } } /** * @dev Withdraw all from AtlasStaker. This is only possible when the retention period of 14 epochs has passed. * The retention period is started when a withdrawal for the stake is requested. */ function withdrawAll() public override nonReentrant returns (uint256 amount) { uint256[] memory depositIds = depositIdByUser[msg.sender].values(); require(depositIds.length > 0, "BattleflyFlywheelVault: No deposited funds"); for (uint256 i = 0; i < depositIds.length; i++) { if (ATLAS_STAKER.canWithdraw(depositIds[i])) { amount += _withdraw(depositIds[i]); } } } /** * @dev Request a withdrawal from AtlasStaker. This works with a retention period of 14 epochs. * Once the retention period has passed, the stake can be withdrawn. */ function requestWithdrawal(uint256[] memory _depositIds) public override { for (uint256 i = 0; i < _depositIds.length; i++) { UserStake memory userStake = userStakes[_depositIds[i]]; require(userStake.owner == msg.sender, "BattleflyFlywheelVault: caller is not the owner"); ATLAS_STAKER.requestWithdrawal(_depositIds[i]); emit RequestWithdrawal(_depositIds[i]); } } /** * @dev Claim emission from AtlasStaker */ function claim(uint256 _depositId) public override nonReentrant returns (uint256 emission) { emission = _claim(_depositId); } /** * @dev Claim all emissions from AtlasStaker */ function claimAll() external override nonReentrant returns (uint256 amount) { uint256[] memory depositIds = depositIdByUser[msg.sender].values(); require(depositIds.length > 0, "BattleflyFlywheelVault: No deposited funds"); for (uint256 i = 0; i < depositIds.length; i++) { amount += _claim(depositIds[i]); } } /** * @dev Whitelist user */ function whitelistUser(address _who) public onlyOwner { require(!whitelistedUsers[_who], "BattlefalyWheelVault: Already whitelisted"); whitelistedUsers[_who] = true; emit AddedUser(_who); } /** * @dev Whitelist users */ function whitelistUsers(address[] memory _users) external onlyOwner { for (uint256 i = 0; i < _users.length; i++) { whitelistUser(_users[i]); } } /** * @dev Remove user from whitelist */ function removeUser(address _who) public onlyOwner { require(whitelistedUsers[_who], "BattlefalyWheelVault: Not whitelisted yet"); whitelistedUsers[_who] = false; emit RemovedUser(_who); } /** * @dev Remove users from whitelist */ function removeUsers(address[] memory _users) external onlyOwner { for (uint256 i = 0; i < _users.length; i++) { removeUser(_users[i]); } } // ================ INTERNAL ================ /** * @dev Withdraw a stake from AtlasStaker (Only possible when the retention period has passed) */ function _withdraw(uint256 _depositId) internal returns (uint256 amount) { UserStake memory userStake = userStakes[_depositId]; require(userStake.owner == msg.sender, "BattleflyFlywheelVault: caller is not the owner"); require(ATLAS_STAKER.canWithdraw(_depositId), "BattleflyFlywheelVault: stake not yet unlocked"); amount = ATLAS_STAKER.withdraw(_depositId); MAGIC.safeTransfer(msg.sender, amount); depositIdByUser[msg.sender].remove(_depositId); delete userStakes[_depositId]; emit WithdrawPosition(_depositId, amount); } /** * @dev Claim emission from AtlasStaker */ function _claim(uint256 _depositId) internal returns (uint256 emission) { UserStake memory userStake = userStakes[_depositId]; require(userStake.owner == msg.sender, "BattleflyFlywheelVault: caller is not the owner"); emission = ATLAS_STAKER.claim(_depositId); MAGIC.safeTransfer(msg.sender, emission); emit ClaimEmission(_depositId, emission); } // ================== VIEW ================== /** * @dev Get allowed lock periods from AtlasStaker */ function getAllowedLocks() public view override returns (IAtlasMine.Lock[] memory) { return ATLAS_STAKER.getAllowedLocks(); } /** * @dev Get claimed emission */ function getClaimableEmission(uint256 _depositId) public view override returns (uint256 emission) { (emission, ) = ATLAS_STAKER.getClaimableEmission(_depositId); } /** * @dev Check if a vaultStake is eligible for requesting a withdrawal. * This is 14 epochs before the end of the initial lock period. */ function canRequestWithdrawal(uint256 _depositId) public view override returns (bool requestable) { return ATLAS_STAKER.canRequestWithdrawal(_depositId); } /** * @dev Check if a vaultStake is eligible for a withdrawal * This is when the retention period has passed */ function canWithdraw(uint256 _depositId) public view override returns (bool withdrawable) { return ATLAS_STAKER.canWithdraw(_depositId); } /** * @dev Check the epoch in which the initial lock period of the vaultStake expires. * This is at the end of the lock period */ function initialUnlock(uint256 _depositId) public view override returns (uint64 epoch) { return ATLAS_STAKER.getVaultStake(_depositId).unlockAt; } /** * @dev Check the epoch in which the retention period of the vaultStake expires. * This is 14 epochs after the withdrawal request has taken place */ function retentionUnlock(uint256 _depositId) public view override returns (uint64 epoch) { return ATLAS_STAKER.getVaultStake(_depositId).retentionUnlock; } /** * @dev Get the currently active epoch */ function getCurrentEpoch() public view override returns (uint64 epoch) { return ATLAS_STAKER.currentEpoch(); } /** * @dev Get the remaining stakeable MAGIC amount. */ function remainingStakeableAmount(address user) public view override returns (uint256 remaining) { uint256 v1Amount = FOUNDER_VAULT_V1.balanceOf(user); uint256 v2Amount = FOUNDER_VAULT_V2.balanceOf(user); uint256 eligible = (v1Amount * STAKING_LIMIT_V1) + (v2Amount * STAKING_LIMIT_V2); uint256 staked = getStakedAmount(user); remaining = eligible >= staked ? eligible - staked : 0; } /** * @dev Get the staked amount of a particular user. */ function getStakedAmount(address user) public view override returns (uint256 amount) { uint256[] memory depositIds = depositIdByUser[user].values(); for (uint256 i = 0; i < depositIds.length; i++) { amount += userStakes[depositIds[i]].amount; } } /** * @dev Get the deposit ids of a user. */ function getDepositIdsOfUser(address user) public view override returns (uint256[] memory depositIds) { depositIds = depositIdByUser[user].values(); } /** * @dev Return the name of the vault */ function getName() public pure override returns (string memory) { return "Founders Flywheel Vault"; } // ================== MODIFIERS ================== modifier onlyMembers() { if (!whitelistedUsers[msg.sender]) { require( FOUNDER_VAULT_V1.balanceOf(msg.sender) + FOUNDER_VAULT_V2.balanceOf(msg.sender) > 0, "BattleflyWheelVault: caller has no staked Founder NFTs" ); } _; } // ================== EVENTS ================== event NewUserStake(uint256 depositId, uint256 amount, uint256 unlockAt, address owner, IAtlasMine.Lock lock); event UpdateUserStake(uint256 depositId, uint256 amount, uint256 unlockAt, address owner, IAtlasMine.Lock lock); event ClaimEmission(uint256 depositId, uint256 emission); event WithdrawPosition(uint256 depositId, uint256 amount); event RequestWithdrawal(uint256 depositId); event AddedUser(address vault); event RemovedUser(address vault); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "../interfaces/IVault.sol"; import "../interfaces/IAtlasMine.sol"; import "../interfaces/IBattleflyAtlasStakerV02.sol"; import "../interfaces/ITestERC20.sol"; contract VaultMock is IVault { IBattleflyAtlasStakerV02 public STAKER; constructor(address _staker, address _magic) { STAKER = IBattleflyAtlasStakerV02(_staker); ITestERC20(_magic).approve(_staker, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); ITestERC20(_magic).mint(100000e18, address(this)); } function deposit(uint128 _amount, IAtlasMine.Lock lock) public { STAKER.deposit(_amount, lock); } function withdraw(uint256 depositId) public { STAKER.withdraw(depositId); } function requestWithdrawal(uint256 depositId) public { STAKER.requestWithdrawal(depositId); } function claim(uint256 depositId) public { STAKER.claim(depositId); } function isAutoCompounded(uint256) public pure override returns (bool) { return false; } function updatePosition(uint256) public override {} }
// SPDX-License-Identifier: MIT pragma solidity >0.8.0; interface IVault { function isAutoCompounded(uint256) external view returns (bool); function updatePosition(uint256) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface ITestERC20 is IERC20 { function mint(uint256 amount, address receiver) external; }
// 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 pragma solidity ^0.8.2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../interfaces/IMasterOfCoin.sol"; import "../interfaces/ITestERC20.sol"; contract MasterOfCoinMock is IMasterOfCoin { ITestERC20 public immutable magic; uint256 public previousWithdrawStamp; bool public staticAmount; constructor(address magic_) { magic = ITestERC20(magic_); previousWithdrawStamp = block.timestamp; staticAmount = true; } function requestRewards() external override returns (uint256 rewardsPaid) { if (staticAmount) { magic.mint(500 ether, msg.sender); return 500 ether; } uint256 secondsPassed = block.timestamp - previousWithdrawStamp; uint256 rewards = secondsPassed * 11574074e8; previousWithdrawStamp = block.timestamp; magic.mint(rewards, msg.sender); return rewards; } function getPendingRewards(address) external view override returns (uint256 pendingRewards) { if (staticAmount) { return 500 ether; } uint256 secondsPassed = block.timestamp - previousWithdrawStamp; uint256 rewards = secondsPassed * 11574074e8; return rewards; } function setWithdrawStamp() external override { previousWithdrawStamp = block.timestamp; } function setStaticAmount(bool set) external override { staticAmount = set; } }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IMagicToken is IERC20 {}
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * The default value of {decimals} is 18. To select a different value for * {decimals} you should overload it. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless this function is * overridden; * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom( address from, address to, uint256 amount ) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `sender` to `recipient`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer( address from, address to, uint256 amount ) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; } _balances[to] += amount; emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; _balances[account] += amount; emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; } _totalSupply -= amount; emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance( address owner, address spender, uint256 amount ) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; contract TestERC20 is ERC20 { using SafeERC20Upgradeable for IERC20Upgradeable; constructor() ERC20("TestERC20", "MAGIC") {} function mint(uint256 amount, address receiver) public { _mint(receiver, amount); } }
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "./interfaces/IBattlefly.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; import "./interfaces/IBattleflyGame.sol"; contract RevealStaking is ERC721HolderUpgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable { using SafeMathUpgradeable for uint256; using AddressUpgradeable for address; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; using SafeERC20Upgradeable for IERC20Upgradeable; mapping(address => bool) private adminAccess; IBattlefly private BattleflyContract; IERC20Upgradeable private MagicToken; uint256 public MagicAmountPerBattlefly; IBattleflyGame private BattleflyGame; mapping(address => EnumerableSetUpgradeable.UintSet) private StakingBattlefliesOfOwner; mapping(uint256 => address) public OwnerOfStakingBattlefly; mapping(uint256 => uint256) public MagicAmountOfStakingBattlefly; mapping(uint256 => bool) public NectarClaimed; uint8 constant COCOON_STAGE = 0; uint8 constant BATTLEFLY_STAGE = 1; uint8 constant NECTAR_ID = 0; event SetAdminAccess(address indexed user, bool access); event BulkStakeBattlefly(uint256[] tokenIds, address indexed user, uint256 totalMagicAmount); event BulkUnstakeBattlefly(uint256[] tokenIds, address indexed user, uint256 totalMagicAmount); function initialize( address batteflyGameContractAddress, address battleflyContractAddress, address magicTokenAddress, uint256 _MagicAmountPerBattlefly ) public initializer { __ERC721Holder_init(); __Ownable_init(); __ReentrancyGuard_init(); BattleflyContract = IBattlefly(battleflyContractAddress); MagicToken = IERC20Upgradeable(magicTokenAddress); MagicAmountPerBattlefly = _MagicAmountPerBattlefly; BattleflyGame = IBattleflyGame(batteflyGameContractAddress); } // ADMIN function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; emit SetAdminAccess(user, access); } //USER function stakingBattlefliesOfOwner(address user) external view returns (uint256[] memory) { return StakingBattlefliesOfOwner[user].values(); } function bulkStakeBattlefly(uint256[] memory tokenIds) external nonReentrant { for (uint256 i = 0; i < tokenIds.length; i++) { uint256 tokenId = tokenIds[i]; OwnerOfStakingBattlefly[tokenId] = _msgSender(); MagicAmountOfStakingBattlefly[tokenId] = MagicAmountPerBattlefly; StakingBattlefliesOfOwner[_msgSender()].add(tokenId); BattleflyContract.safeTransferFrom(_msgSender(), address(this), tokenId); } uint256 totalMagicAmount = MagicAmountPerBattlefly.mul(tokenIds.length); MagicToken.safeTransferFrom(_msgSender(), address(this), totalMagicAmount); emit BulkStakeBattlefly(tokenIds, _msgSender(), totalMagicAmount); } function bulkUnstakeBattlefly( uint256[] memory tokenIds, uint256[] memory battleflyStages, uint8 v, bytes32 r, bytes32 s ) external nonReentrant { uint256 totalMagicAmount = 0; uint256 totalNectar = 0; address receiver = _msgSender(); bytes32 payloadHash = keccak256(abi.encodePacked(tokenIds, battleflyStages)); bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash)); (address admin, ECDSAUpgradeable.RecoverError result) = ECDSAUpgradeable.tryRecover(messageHash, v, r, s); require(result == ECDSAUpgradeable.RecoverError.NoError && adminAccess[admin], "Require admin access"); for (uint256 i = 0; i < tokenIds.length; i++) { uint256 tokenId = tokenIds[i]; require(OwnerOfStakingBattlefly[tokenId] == _msgSender(), "Require Staking Battlefly owner access"); OwnerOfStakingBattlefly[tokenId] = address(0); totalMagicAmount = totalMagicAmount.add(MagicAmountOfStakingBattlefly[tokenId]); MagicAmountOfStakingBattlefly[tokenId] = 0; StakingBattlefliesOfOwner[_msgSender()].remove(tokenId); if (battleflyStages[i] == BATTLEFLY_STAGE && NectarClaimed[tokenId] == false) { NectarClaimed[tokenId] = true; totalNectar = totalNectar.add(10); } BattleflyContract.safeTransferFrom(address(this), receiver, tokenId); } if (totalMagicAmount != 0) MagicToken.safeTransfer(receiver, totalMagicAmount); if (totalNectar != 0) BattleflyGame.mintItems(NECTAR_ID, receiver, totalNectar); emit BulkUnstakeBattlefly(tokenIds, receiver, totalMagicAmount); } /** * @dev Returns the number of staking tokens in ``owner``'s account. */ function balanceOf(address owner) public view returns (uint256) { require(owner != address(0), "ERC721: balance query for the zero address"); return StakingBattlefliesOfOwner[owner].length(); } /** * @dev Returns the owner of the `tokenId` staking token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner) { return OwnerOfStakingBattlefly[tokenId]; } //modifier modifier onlyAdminAccess() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) pragma solidity ^0.8.0; // CAUTION // This version of SafeMath should only be used with Solidity 0.8 or later, // because it relies on the compiler's built in overflow checks. /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler * now has built in overflow checking. */ library SafeMathUpgradeable { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../StringsUpgradeable.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSAUpgradeable { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } else if (error == RecoverError.InvalidSignatureV) { revert("ECDSA: invalid signature 'v' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { // Check the signature length // - case 65: r,s,v signature (standard) // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else if (signature.length == 64) { bytes32 r; bytes32 vs; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) vs := mload(add(signature, 0x40)) } return tryRecover(hash, r, vs); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } if (v != 27 && v != 28) { return (address(0), RecoverError.InvalidSignatureV); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", StringsUpgradeable.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; interface IBattleflyGame { function mintBattlefly(address receiver, uint256 battleflyType) external returns (uint256); function mintSpecialNFT(address receiver, uint256 specialNFTType) external returns (uint256); function mintBattleflies( address receiver, uint256 battleflyType, uint256 amount ) external returns (uint256[] memory); function mintSpecialNFTs( address receiver, uint256 specialNFTType, uint256 amount ) external returns (uint256[] memory); function mintItems( uint256 itemId, address receiver, uint256 amount ) external; }
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "./interfaces/IBattleflyGame.sol"; import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; // OZ: MerkleProof contract FounderGenesisV2Sale is OwnableUpgradeable { using SafeMathUpgradeable for uint256; using AddressUpgradeable for address; mapping(address => bool) private adminAccess; IBattleflyGame Game; uint256 FounderGenesisV2TokenType; uint256 public totalSellAmount; uint256 public soldAmount; uint256 public timeStart; uint256 public price; address public fundAddress; uint256 public discountPrice; uint256 public discountSoldAmount; uint256 public discountTimeStart; bytes32 public merkleRoot; mapping(bytes32 => bool) public ticketUsed; mapping(address => uint256) public mintedOfUser; event MintFounderGenesisV2( address indexed to, uint256 price, uint256 indexed specialNFTType, uint256 tokenId, address fundAddress, bytes32 indexed ticket ); function initialize(address battleflyGameContractAddress) public initializer { __Ownable_init(); Game = IBattleflyGame(battleflyGameContractAddress); FounderGenesisV2TokenType = 151; totalSellAmount = 0; } function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; } function setMerkleRoot(bytes32 _merkleRoot) external onlyAdminAccess { merkleRoot = _merkleRoot; } function mintFounderGenesisV2WithDiscount( uint256 amount, uint256 allocation, bytes32[] calldata proof ) external payable { address to = _msgSender(); require(discountSoldAmount + soldAmount + amount <= totalSellAmount, "Sold out"); require(msg.value == discountPrice.mul(amount), "Not enough ETH"); require(block.timestamp >= discountTimeStart, "Not time yet"); require(amount + mintedOfUser[to] <= allocation, "Not enough allocation"); bytes32 leaf = keccak256(abi.encodePacked(to, allocation)); bool isValidLeaf = MerkleProof.verify(proof, merkleRoot, leaf); require(isValidLeaf, "Not in merkle"); mintedOfUser[to] += amount; discountSoldAmount += amount; uint256[] memory tokenIds = Game.mintSpecialNFTs(to, FounderGenesisV2TokenType, amount); if (fundAddress != address(0)) { (bool success, ) = payable(fundAddress).call{ value: address(this).balance }(""); require(success, "Failed to send Ether"); } for (uint256 i = 0; i < amount; i++) { emit MintFounderGenesisV2(to, discountPrice, FounderGenesisV2TokenType, tokenIds[i], fundAddress, ""); } } function setDiscountSaleInfo(uint256 _timeStart, uint256 _price) external onlyAdminAccess { discountTimeStart = _timeStart; discountPrice = _price; } function setSaleInfo( uint256 _totalSellAmount, uint256 _timeStart, uint256 _price ) external onlyAdminAccess { totalSellAmount = _totalSellAmount; timeStart = _timeStart; price = _price; } // function setfundAddress(address _fundAddress) external onlyAdminAccess { // fundAddress = _fundAddress; // } function mintFounderGenesisV2(uint256 amount) external payable { address to = _msgSender(); require(discountSoldAmount + soldAmount + amount <= totalSellAmount, "Sold out"); require(msg.value == price.mul(amount), "Not enough ETH"); require(block.timestamp >= timeStart, "Not time yet"); soldAmount += amount; uint256[] memory tokenIds = Game.mintSpecialNFTs(to, FounderGenesisV2TokenType, amount); if (fundAddress != address(0)) { (bool success, ) = payable(fundAddress).call{ value: address(this).balance }(""); require(success, "Failed to send Ether"); } for (uint256 i = 0; i < amount; i++) { emit MintFounderGenesisV2(to, price, FounderGenesisV2TokenType, tokenIds[i], fundAddress, ""); } } // function withdraw(uint256 amount) external onlyAdminAccess { // require(amount <= address(this).balance, "Not enough balance"); // (bool success, ) = payable(fundAddress).call{value: amount}(""); // require(success, "Failed to send Ether"); // } modifier onlyAdminAccess() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.0; /** * @dev These functions deal with verification of Merkle Trees proofs. * * The proofs can be generated using the JavaScript library * https://github.com/miguelmota/merkletreejs[merkletreejs]. * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled. * * See `test/utils/cryptography/MerkleProof.test.js` for some examples. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the merkle tree could be reinterpreted as a leaf value. */ library MerkleProof { /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify( bytes32[] memory proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. * * _Available since v4.4._ */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { bytes32 proofElement = proof[i]; if (computedHash <= proofElement) { // Hash(current computed hash + current element of the proof) computedHash = _efficientHash(computedHash, proofElement); } else { // Hash(current element of the proof + current computed hash) computedHash = _efficientHash(proofElement, computedHash); } } return computedHash; } function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "./interfaces/IBattleflyGame.sol"; import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; // OZ: MerkleProof contract BattleflyWhitelist is OwnableUpgradeable { using SafeMathUpgradeable for uint256; using AddressUpgradeable for address; mapping(address => mapping(uint256 => uint256)) public hasClaimedSpecialNFT; mapping(address => mapping(uint256 => uint256)) public hasClaimedBattlefly; bytes32 public merkleRootBattlefly; bytes32 public merkleRootSpecialNFT; mapping(address => bool) private adminAccess; IBattleflyGame Game; uint256 StartTime; uint256 EndTime; event ClaimBattlefly(address indexed to, uint256 amount, uint256 indexed battleflyType); event ClaimSpecialNFT(address indexed to, uint256 amount, uint256 indexed specialNFTType); function initialize(address battleflyGameContractAddress) public initializer { __Ownable_init(); Game = IBattleflyGame(battleflyGameContractAddress); } function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; } function setMerkleRootBattlefly(bytes32 merkleRoot) external onlyAdminAccess { merkleRootBattlefly = merkleRoot; } function setMerkleRootSpecialNFT(bytes32 merkleRoot) external onlyAdminAccess { merkleRootSpecialNFT = merkleRoot; } function setHasClaimedBattlefly( address user, uint256 battleflyType, uint256 value ) external onlyAdminAccess { hasClaimedBattlefly[user][battleflyType] = value; } function setHasClaimedSpecialNFT( address user, uint256 specialNFTType, uint256 value ) external onlyAdminAccess { hasClaimedSpecialNFT[user][specialNFTType] = value; } function setMintingTime(uint256 start, uint256 end) external onlyAdminAccess { StartTime = start; EndTime = end; } function claimBattlefly( uint256 allocatedAmount, uint256 mintingAmount, uint256 battleflyType, bytes32[] calldata proof ) external { address to = _msgSender(); if (StartTime != 0) { require(block.timestamp >= StartTime, "Not start yet"); } if (EndTime != 0) { require(block.timestamp <= EndTime, "Already finished"); } require(hasClaimedBattlefly[to][battleflyType] + mintingAmount <= allocatedAmount, "Not enough allocation"); bytes32 leaf = keccak256(abi.encodePacked(to, allocatedAmount, battleflyType)); bool isValidLeaf = MerkleProof.verify(proof, merkleRootBattlefly, leaf); require(isValidLeaf, "Not in merkle"); hasClaimedBattlefly[to][battleflyType] += mintingAmount; for (uint256 i = 0; i < mintingAmount; i++) { Game.mintBattlefly(to, battleflyType); } emit ClaimBattlefly(to, mintingAmount, battleflyType); } function claimSpecialNFT( uint256 allocatedAmount, uint256 mintingAmount, uint256 specialNFTType, bytes32[] calldata proof ) external { address to = _msgSender(); require(hasClaimedSpecialNFT[to][specialNFTType] + mintingAmount <= allocatedAmount, "Not enough allocation"); bytes32 leaf = keccak256(abi.encodePacked(to, allocatedAmount, specialNFTType)); bool isValidLeaf = MerkleProof.verify(proof, merkleRootSpecialNFT, leaf); require(isValidLeaf, "Not in merkle"); hasClaimedSpecialNFT[to][specialNFTType] += mintingAmount; for (uint256 i = 0; i < mintingAmount; i++) { Game.mintSpecialNFT(to, specialNFTType); } emit ClaimSpecialNFT(to, mintingAmount, specialNFTType); } modifier onlyAdminAccess() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/IAtlasMine.sol"; import "./interfaces/IBattleflyAtlasStaker.sol"; contract MockAtlasStaker is IBattleflyAtlasStaker, Initializable, OwnableUpgradeable, ERC1155HolderUpgradeable, ERC721HolderUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; using AddressUpgradeable for address; using SafeCastUpgradeable for uint256; using SafeCastUpgradeable for int256; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; // ============================================ STATE ============================================== // ============= Global Immutable State ============== /// @notice MAGIC token /// @dev functionally immutable IERC20Upgradeable public magic; /// @notice The IAtlasMine /// @dev functionally immutable IAtlasMine public mine; // ============= Global Staking State ============== uint256 public constant ONE = 1e30; /// @notice Whether new stakes will get staked on the contract as scheduled. For emergencies bool public schedulePaused; /// @notice The total amount of staked token uint256 public totalStaked; /// @notice The total amount of share uint256 public totalShare; /// @notice All stakes currently active Stake[] public stakes; /// @notice Deposit ID of last stake. Also tracked in atlas mine uint256 public lastDepositId; /// @notice Rewards accumulated per share uint256 public accRewardsPerShare; // ============= Vault Staking State ============== mapping(address => bool) public battleflyVaults; /// @notice Each vault stake, keyed by vault contract address => deposit ID mapping(address => mapping(uint256 => VaultStake)) public vaultStake; /// @notice All deposit IDs fro a vault, enumerated mapping(address => EnumerableSetUpgradeable.UintSet) private allVaultDepositIds; /// @notice The current ID of the vault's last deposited stake mapping(address => uint256) public currentId; // ============= NFT Boosting State ============== /// @notice Holder of treasures and legions mapping(uint256 => bool) public legionsStaked; mapping(uint256 => uint256) public treasuresStaked; // ============= Operator State ============== IAtlasMine.Lock[] public allowedLocks; /// @notice Fee to contract operator. Only assessed on rewards. uint256 public fee; /// @notice Amount of fees reserved for withdrawal by the operator. uint256 public feeReserve; /// @notice Max fee the owner can ever take - 10% uint256 public constant MAX_FEE = 1000; uint256 public constant FEE_DENOMINATOR = 10000; mapping(address => mapping(uint256 => int256)) refundedFeeDebts; uint256 accRefundedFeePerShare; uint256 totalWhitelistedFeeShare; EnumerableSetUpgradeable.AddressSet whitelistedFeeVaults; mapping(address => bool) public superAdmins; /// @notice deposited but unstaked uint256 public unstakedDeposits; mapping(IAtlasMine.Lock => uint256) public unstakedDepositsByLock; address public constant TREASURY_WALLET = 0xF5411006eEfD66c213d2fd2033a1d340458B7226; /// @notice Intra-tx buffer for pending payouts uint256 public tokenBuffer; // =========================================== // ============== Post Upgrade =============== // =========================================== // ========================================== INITIALIZER =========================================== /** * @param _magic The MAGIC token address. * @param _mine The IAtlasMine contract. * Maps to a timelock for IAtlasMine deposits. */ function initialize( IERC20Upgradeable _magic, IAtlasMine _mine, IAtlasMine.Lock[] memory _allowedLocks ) external initializer { __ERC1155Holder_init(); __ERC721Holder_init(); __Ownable_init(); __ReentrancyGuard_init(); magic = _magic; mine = _mine; allowedLocks = _allowedLocks; fee = 1000; // Approve the mine magic.safeApprove(address(mine), 2**256 - 1); // approveNFTs(); } // ======================================== VAULT OPERATIONS ======================================== /** * @notice Make a new deposit into the Staker. The Staker will collect * the tokens, to be later staked in atlas mine by the owner, * according to the stake/unlock schedule. * @dev Specified amount of token must be approved by the caller. * * @param _amount The amount of tokens to deposit. */ function deposit(uint256 _amount, IAtlasMine.Lock lock) public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) { require(!schedulePaused, "new staking paused"); _updateRewards(); // Collect tokens uint256 newDepositId = _deposit(_amount, msg.sender, lock); magic.safeTransferFrom(msg.sender, address(this), _amount); return (newDepositId); } function _deposit( uint256 _amount, address _vault, IAtlasMine.Lock lock ) internal returns (uint256) { require(_amount > 0, "Deposit amount 0"); bool validLock = false; for (uint256 i = 0; i < allowedLocks.length; i++) { if (allowedLocks[i] == lock) { validLock = true; break; } } require(validLock, "Lock time not allowed"); // Add vault stake uint256 newDepositId = ++currentId[_vault]; allVaultDepositIds[_vault].add(newDepositId); VaultStake storage s = vaultStake[_vault][newDepositId]; s.amount = _amount; (uint256 boost, uint256 lockTime) = getLockBoost(lock); uint256 share = (_amount * (100e16 + boost)) / 100e16; uint256 vestingTime = mine.getVestingTime(lock); s.unlockAt = block.timestamp + lockTime + vestingTime + 1 days; s.rewardDebt = ((share * accRewardsPerShare) / ONE).toInt256(); s.lock = lock; // Update global accounting totalStaked += _amount; totalShare += share; if (whitelistedFeeVaults.contains(_vault)) { totalWhitelistedFeeShare += share; refundedFeeDebts[_vault][newDepositId] = ((share * accRefundedFeePerShare) / ONE).toInt256(); } // MAGIC tokens sit in contract. Added to pending stakes unstakedDeposits += _amount; unstakedDepositsByLock[lock] += _amount; emit VaultDeposit(_vault, newDepositId, _amount, s.unlockAt, s.lock); return newDepositId; } /** * @notice Withdraw a deposit from the Staker contract. Calculates * pro rata share of accumulated MAGIC and distributes any * earned rewards in addition to original deposit. * There must be enough unlocked tokens to withdraw. * * @param depositId The ID of the deposit to withdraw from. * */ function withdraw(uint256 depositId) public virtual override onlyBattleflyVaultOrOwner nonReentrant { // Distribute tokens _updateRewards(); VaultStake storage s = vaultStake[msg.sender][depositId]; require(s.amount > 0, "No deposit"); require(block.timestamp >= s.unlockAt, "Deposit locked"); uint256 payout = _withdraw(s, depositId); magic.safeTransfer(msg.sender, payout); } /** * @notice Withdraw all eligible deposits from the staker contract. * Will skip any deposits not yet unlocked. Will also * distribute rewards for all stakes via 'withdraw'. * */ function withdrawAll() public virtual override onlyBattleflyVaultOrOwner nonReentrant { // Distribute tokens _updateRewards(); uint256[] memory depositIds = allVaultDepositIds[msg.sender].values(); for (uint256 i = 0; i < depositIds.length; i++) { VaultStake storage s = vaultStake[msg.sender][depositIds[i]]; if (s.amount > 0 && s.unlockAt > 0 && s.unlockAt <= block.timestamp) { tokenBuffer += _withdraw(s, depositIds[i]); } } magic.safeTransfer(msg.sender, tokenBuffer); tokenBuffer = 0; } /** * @dev Logic for withdrawing a deposit. Calculates pro rata share of * accumulated MAGIC and dsitributed any earned rewards in addition * to original deposit. * * @dev An _amount argument larger than the total deposit amount will * withdraw the entire deposit. * * @param s The VaultStake struct to withdraw from. * @param depositId The ID of the deposit to withdraw from (for event). */ function _withdraw(VaultStake storage s, uint256 depositId) internal returns (uint256 payout) { uint256 _amount = s.amount; // Unstake if we need to to ensure we can withdraw (uint256 boost, ) = getLockBoost(s.lock); uint256 share = (_amount * (100e16 + boost)) / 100e16; int256 accumulatedRewards = ((share * accRewardsPerShare) / ONE).toInt256(); if (whitelistedFeeVaults.contains(msg.sender)) { accumulatedRewards += ((share * accRefundedFeePerShare) / ONE).toInt256(); accumulatedRewards -= refundedFeeDebts[msg.sender][depositId]; totalWhitelistedFeeShare -= share; refundedFeeDebts[msg.sender][depositId] = 0; } uint256 reward = (accumulatedRewards - s.rewardDebt).toUint256(); payout = _amount + reward; // // Update vault accounting // s.amount -= _amount; // s.rewardDebt = 0; ///comment Archethect: Consider deleting the VaultStake object for gas optimization. s.unlockAt and s.lock can be zeroed as well. delete vaultStake[msg.sender][depositId]; // Update global accounting totalStaked -= _amount; totalShare -= share; // If we need to unstake, unstake until we have enough if (payout > _totalUsableMagic()) { _unstakeToTarget(payout - _totalUsableMagic()); } emit VaultWithdraw(msg.sender, depositId, _amount, reward); } /** * @notice Claim rewards without unstaking. Will fail if there * are not enough tokens in the contract to claim rewards. * Does not attempt to unstake. * * @param depositId The ID of the deposit to claim rewards from. * */ function claim(uint256 depositId) public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) { _updateRewards(); VaultStake storage s = vaultStake[msg.sender][depositId]; require(s.amount > 0, "No deposit"); uint256 reward = _claim(s, depositId); magic.safeTransfer(msg.sender, reward); return reward; } /** * @notice Claim all possible rewards from the staker contract. * Will apply to both locked and unlocked deposits. * */ function claimAll() public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) { return 0; } /** * @notice Claim all possible rewards from the staker contract then restake. * Will apply to both locked and unlocked deposits. * */ function claimAllAndRestake(IAtlasMine.Lock lock) public onlyBattleflyVaultOrOwner nonReentrant returns (uint256) { _updateRewards(); uint256[] memory depositIds = allVaultDepositIds[msg.sender].values(); uint256 totalReward = 0; for (uint256 i = 0; i < depositIds.length; i++) { VaultStake storage s = vaultStake[msg.sender][depositIds[i]]; uint256 reward = _claim(s, depositIds[i]); tokenBuffer += reward; } _deposit(tokenBuffer, msg.sender, lock); tokenBuffer = 0; return totalReward; } /** * @dev Logic for claiming rewards on a deposit. Calculates pro rata share of * accumulated MAGIC and dsitributed any earned rewards in addition * to original deposit. * * @param s The VaultStake struct to claim from. * @param depositId The ID of the deposit to claim from (for event). */ function _claim(VaultStake storage s, uint256 depositId) internal returns (uint256) { // Update accounting (uint256 boost, ) = getLockBoost(s.lock); uint256 share = (s.amount * (100e16 + boost)) / 100e16; int256 accumulatedRewards = ((share * accRewardsPerShare) / ONE).toInt256(); uint256 reward = (accumulatedRewards - s.rewardDebt).toUint256(); if (whitelistedFeeVaults.contains(msg.sender)) { int256 accumulatedRefundedFee = ((share * accRefundedFeePerShare) / ONE).toInt256(); reward += accumulatedRefundedFee.toUint256(); reward -= refundedFeeDebts[msg.sender][depositId].toUint256(); refundedFeeDebts[msg.sender][depositId] = accumulatedRefundedFee; } s.rewardDebt = accumulatedRewards; // Unstake if we need to to ensure we can withdraw if (reward > _totalUsableMagic()) { _unstakeToTarget(reward - _totalUsableMagic()); } require(reward <= _totalUsableMagic(), "Not enough rewards to claim"); emit VaultClaim(msg.sender, depositId, reward); return reward; } // ======================================= SUPER ADMIN OPERATIONS ======================================== /** * @notice Stake a Treasure owned by the superAdmin into the Atlas Mine. * Staked treasures will boost all vault deposits. * @dev Any treasure must be approved for withdrawal by the caller. * * @param _tokenId The tokenId of the specified treasure. * @param _amount The amount of treasures to stake. */ function stakeTreasure(uint256 _tokenId, uint256 _amount) external onlySuperAdminOrOwner { address treasureAddr = mine.treasure(); require(IERC1155Upgradeable(treasureAddr).balanceOf(msg.sender, _tokenId) >= _amount, "Not enough treasures"); treasuresStaked[_tokenId] += _amount; // First withdraw and approve IERC1155Upgradeable(treasureAddr).safeTransferFrom(msg.sender, address(this), _tokenId, _amount, bytes("")); mine.stakeTreasure(_tokenId, _amount); uint256 boost = mine.boosts(address(this)); emit StakeNFT(msg.sender, treasureAddr, _tokenId, _amount, boost); } /** * @notice Unstake a Treasure from the Atlas Mine adn transfer to receiver. * * @param _receiver The receiver . * @param _tokenId The tokenId of the specified treasure. * @param _amount The amount of treasures to stake. */ function unstakeTreasure( address _receiver, uint256 _tokenId, uint256 _amount ) external onlySuperAdminOrOwner { require(treasuresStaked[_tokenId] >= _amount, "Not enough treasures"); treasuresStaked[_tokenId] -= _amount; address treasureAddr = mine.treasure(); mine.unstakeTreasure(_tokenId, _amount); IERC1155Upgradeable(treasureAddr).safeTransferFrom(address(this), _receiver, _tokenId, _amount, bytes("")); uint256 boost = mine.boosts(address(this)); emit UnstakeNFT(_receiver, treasureAddr, _tokenId, _amount, boost); } /** * @notice Stake a Legion owned by the superAdmin into the Atlas Mine. * Staked legions will boost all vault deposits. * @dev Any legion be approved for withdrawal by the caller. * * @param _tokenId The tokenId of the specified legion. */ function stakeLegion(uint256 _tokenId) external onlySuperAdminOrOwner { address legionAddr = mine.legion(); require(IERC721Upgradeable(legionAddr).ownerOf(_tokenId) == msg.sender, "Not owner of legion"); legionsStaked[_tokenId] = true; IERC721Upgradeable(legionAddr).safeTransferFrom(msg.sender, address(this), _tokenId); mine.stakeLegion(_tokenId); uint256 boost = mine.boosts(address(this)); emit StakeNFT(msg.sender, legionAddr, _tokenId, 1, boost); } /** * @notice Unstake a Legion from the Atlas Mine and return it to the superAdmin. * * @param _tokenId The tokenId of the specified legion. */ function unstakeLegion(address _receiver, uint256 _tokenId) external onlySuperAdminOrOwner { require(legionsStaked[_tokenId], "No legion"); address legionAddr = mine.legion(); delete legionsStaked[_tokenId]; mine.unstakeLegion(_tokenId); // Distribute to superAdmin IERC721Upgradeable(legionAddr).safeTransferFrom(address(this), _receiver, _tokenId); uint256 boost = mine.boosts(address(this)); emit UnstakeNFT(_receiver, legionAddr, _tokenId, 1, boost); } /** * @notice Stake any pending stakes before the current day. Callable * by anybody. Any pending stakes will unlock according * to the time this method is called, and the contract's defined * lock time. */ function stakeScheduled() external virtual override onlySuperAdminOrOwner { for (uint256 i = 0; i < allowedLocks.length; i++) { IAtlasMine.Lock lock = allowedLocks[i]; _stakeInMine(unstakedDepositsByLock[lock], lock); unstakedDepositsByLock[lock] = 0; } unstakedDeposits = 0; } /** * @notice Unstake everything eligible for unstaking from Atlas Mine. * Callable by owner. Should only be used in case of emergency * or migration to a new contract, or if there is a need to service * an unexpectedly large amount of withdrawals. * * If unlockAll is set to true in the Atlas Mine, this can withdraw * all stake. */ function unstakeAllFromMine() external override onlySuperAdminOrOwner { // Unstake everything eligible _updateRewards(); for (uint256 i = 0; i < stakes.length; i++) { Stake memory s = stakes[i]; if (s.unlockAt > block.timestamp) { continue; } // Withdraw position - auto-harvest mine.withdrawPosition(s.depositId, s.amount); } // Only check for removal after, so we don't mutate while looping _removeZeroStakes(); } /** * @notice Let owner unstake a specified amount as needed to make sure the contract is funded. * Can be used to facilitate expected future withdrawals. * * @param target The amount of tokens to reclaim from the mine. */ function unstakeToTarget(uint256 target) external override onlySuperAdminOrOwner { _updateRewards(); _unstakeToTarget(target); } /** * @notice Withdraw any accumulated reward fees to the treasury */ function withdrawFeesToTreasury() external virtual onlySuperAdminOrOwner { uint256 amount = feeReserve; feeReserve = 0; magic.safeTransfer(TREASURY_WALLET, amount); emit WithdrawFeesToTreasury(amount); } function stakeBackFeeTreasury(IAtlasMine.Lock lock) external virtual onlySuperAdminOrOwner { uint256 amount = feeReserve; feeReserve = 0; emit WithdrawFeesToTreasury(amount); // magic.safeTransfer(TREASURY_WALLET, amount); _deposit(amount, TREASURY_WALLET, lock); } /** * @notice Whitelist vault from fees. * * @param _vault Vault address. * @param isSet Whether to enable or disable the vault whitelist. */ function setFeeWhitelistVault(address _vault, bool isSet) external onlyOwner { require(_vault != address(0), "Invalid Vault"); if (isSet) { whitelistedFeeVaults.add(_vault); totalWhitelistedFeeShare += totalShareOf(_vault); } else { whitelistedFeeVaults.remove(_vault); totalWhitelistedFeeShare -= totalShareOf(_vault); } emit SetFeeWhitelistVault(_vault, isSet); } // ======================================= OWNER OPERATIONS ======================================= function setBattleflyVault(address _vaultAddress, bool isSet) external onlyOwner { require(_vaultAddress != address(0), "Invalid vault"); if (isSet) { require(battleflyVaults[_vaultAddress] == false, "Vault already set"); battleflyVaults[_vaultAddress] = isSet; } else { require(allVaultDepositIds[_vaultAddress].length() == 0, "Vault is still active"); delete battleflyVaults[_vaultAddress]; } emit SetBattleflyVault(_vaultAddress, isSet); } /** * @notice Change the designated superAdmin, the address where treasures and * legions are held. Staked NFTs can only be * withdrawn to the current superAdmin address, regardless of which * address the superAdmin was set to when it was staked. * * @param _superAdmin The new superAdmin address. * @param isSet Whether to enable or disable the superAdmin address. */ function setBoostAdmin(address _superAdmin, bool isSet) external override onlyOwner { require(_superAdmin != address(0), "Invalid superAdmin"); superAdmins[_superAdmin] = isSet; } /** * @notice Change the designated super admin, who manage the fee reverse * * @param _superAdmin The new superAdmin address. * @param isSet Whether to enable or disable the super admin address. */ function setSuperAdmin(address _superAdmin, bool isSet) external onlyOwner { require(_superAdmin != address(0), "Invalid address"); superAdmins[_superAdmin] = isSet; } /** * @notice Approve treasures and legions for withdrawal from the atlas mine. * Called on startup, and should be called again in case contract * addresses for treasures and legions ever change. * */ function approveNFTs() public override onlyOwner { address treasureAddr = mine.treasure(); IERC1155Upgradeable(treasureAddr).setApprovalForAll(address(mine), true); address legionAddr = mine.legion(); IERC1155Upgradeable(legionAddr).setApprovalForAll(address(mine), true); } /** * @notice EMERGENCY ONLY - toggle pausing new scheduled stakes. * If on, vaults can deposit, but stakes won't go to Atlas Mine. * Can be used in case of Atlas Mine issues or forced migration * to new contract. */ function toggleSchedulePause(bool paused) external virtual override onlyOwner { schedulePaused = paused; emit StakingPauseToggle(paused); } // ======================================== VIEW FUNCTIONS ========================================= function getLockBoost(IAtlasMine.Lock _lock) public pure virtual returns (uint256 boost, uint256 timelock) { if (_lock == IAtlasMine.Lock.twoWeeks) { // 10% return (10e16, 14 days); } else if (_lock == IAtlasMine.Lock.oneMonth) { // 25% return (25e16, 30 days); } else if (_lock == IAtlasMine.Lock.threeMonths) { // 80% return (80e16, 3 * 30 days); } else if (_lock == IAtlasMine.Lock.sixMonths) { // 180% return (180e16, 6 * 30 days); } else if (_lock == IAtlasMine.Lock.twelveMonths) { // 400% return (400e16, 365 days); } else { revert("Invalid lock value"); } } /** * @notice Returns all magic either unstaked, staked, or pending rewards in Atlas Mine. * Best proxy for TVL. * * @return total The total amount of MAGIC in the staker. */ function totalMagic() external view override returns (uint256) { return _totalControlledMagic() + mine.pendingRewardsAll(address(this)); } /** * @notice Returns all magic that has been deposited, but not staked, and is eligible * to be staked (deposit time < current day). * * @return total The total amount of MAGIC available to stake. */ // removed, will read the unstakedDeposits directly // function totalPendingStake() external view override returns (uint256) { // return unstakedDeposits; // } /** * @notice Returns all magic that has been deposited, but not staked, and is eligible * to be staked (deposit time < current day). * * @return total The total amount of MAGIC that can be withdrawn. */ function totalWithdrawableMagic() external view override returns (uint256) { uint256 totalPendingRewards; // IAtlasMine attempts to divide by 0 if there are no deposits try mine.pendingRewardsAll(address(this)) returns (uint256 _pending) { totalPendingRewards = _pending; } catch Panic(uint256) { totalPendingRewards = 0; } // uint256 vestedPrincipal; // for (uint256 i = 0; i < stakes.length; i++) { // vestedPrincipal += mine.calcualteVestedPrincipal(address(this), stakes[i].depositId); // } return _totalUsableMagic() + totalPendingRewards; } /** * @notice Returns the details of a vault stake. * * @return vaultStake The details of a vault stake. */ function getVaultStake(address vault, uint256 depositId) external view override returns (VaultStake memory) { return vaultStake[vault][depositId]; } /** * @notice Returns the total amount staked by a vault. * * @return totalStake The total amount of MAGIC staked by a vault. */ // we will read it from the subgraph // function vaultTotalStake(address vault) external view override returns (uint256 totalStake) { // uint256[] memory depositIds = allVaultDepositIds[vault].values(); // for (uint256 i = 0; i < depositIds.length; i++) { // VaultStake storage s = vaultStake[vault][depositIds[i]]; // totalStake += s.amount; // } // } /** * @notice Returns the pending, claimable rewards for a deposit. * @dev This does not update rewards, so out of date if rewards not recently updated. * Needed to maintain 'view' function type. * * @param vault The vault to check rewards for. * @param depositId The specific deposit to check rewards for. * * @return reward The total amount of MAGIC reward pending. */ function pendingRewards(address vault, uint256 depositId) public view override returns (uint256 reward) { if (totalShare == 0) { return 0; } VaultStake storage s = vaultStake[vault][depositId]; (uint256 boost, ) = getLockBoost(s.lock); uint256 share = (s.amount * (100e16 + boost)) / 100e16; uint256 unupdatedReward = mine.pendingRewardsAll(address(this)); (uint256 founderReward, , uint256 feeRefund) = _calculateHarvestRewardFee(unupdatedReward); uint256 realAccRewardsPerShare = accRewardsPerShare + (founderReward * ONE) / totalShare; uint256 accumulatedRewards = (share * realAccRewardsPerShare) / ONE; if (whitelistedFeeVaults.contains(vault) && totalWhitelistedFeeShare > 0) { uint256 realAccRefundedFeePerShare = accRefundedFeePerShare + (feeRefund * ONE) / totalWhitelistedFeeShare; uint256 accumulatedRefundedFee = (share * realAccRefundedFeePerShare) / ONE; accumulatedRewards = accumulatedRewards + accumulatedRefundedFee; accumulatedRewards -= refundedFeeDebts[vault][depositId].toUint256(); } reward = accumulatedRewards - s.rewardDebt.toUint256(); } /** * @notice Returns the pending, claimable rewards for all of a vault's deposits. * @dev This does not update rewards, so out of date if rewards not recently updated. * Needed to maintain 'view' function type. * * @param vault The vault to check rewards for. * * @return reward The total amount of MAGIC reward pending. */ function pendingRewardsAll(address vault) external view override returns (uint256 reward) { uint256[] memory depositIds = allVaultDepositIds[vault].values(); for (uint256 i = 0; i < depositIds.length; i++) { reward += pendingRewards(vault, depositIds[i]); } } /** * @notice Returns the total Share of a vault. * * @param vault The vault to check rewards for. * * @return _totalShare The total share of a vault. */ function totalShareOf(address vault) public view returns (uint256 _totalShare) { uint256[] memory depositIds = allVaultDepositIds[vault].values(); for (uint256 i = 0; i < depositIds.length; i++) { (uint256 boost, ) = getLockBoost(vaultStake[vault][depositIds[i]].lock); uint256 share = (vaultStake[vault][depositIds[i]].amount * (100e16 + boost)) / 100e16; _totalShare += share; } } // ============================================ HELPERS ============================================ /** * @dev Stake tokens held by staker in the Atlas Mine, according to * the predefined lock value. Schedules for staking will be managed by a queue. * * @param _amount Number of tokens to stake */ function _stakeInMine(uint256 _amount, IAtlasMine.Lock lock) internal { require(_amount <= _totalUsableMagic(), "Not enough funds"); uint256 depositId = ++lastDepositId; (, uint256 lockTime) = getLockBoost(lock); uint256 vestingPeriod = mine.getVestingTime(lock); uint256 unlockAt = block.timestamp + lockTime + vestingPeriod; stakes.push(Stake({ amount: _amount, unlockAt: unlockAt, depositId: depositId })); mine.deposit(_amount, lock); } /** * @dev Unstakes until we have enough unstaked tokens to meet a specific target. * Used to make sure we can service withdrawals. * * @param target The amount of tokens we want to have unstaked. */ function _unstakeToTarget(uint256 target) internal { uint256 unstaked = 0; for (uint256 i = 0; i < stakes.length; i++) { Stake memory s = stakes[i]; if (s.unlockAt > block.timestamp && !mine.unlockAll()) { // This stake is not unlocked - stop looking continue; } // Withdraw position - auto-harvest uint256 preclaimBalance = _totalUsableMagic(); uint256 targetLeft = target - unstaked; uint256 amount = targetLeft > s.amount ? s.amount : targetLeft; // Do not harvest rewards - if this is running, we've already // harvested in the same fn call mine.withdrawPosition(s.depositId, amount); uint256 postclaimBalance = _totalUsableMagic(); // Increment amount unstaked unstaked += postclaimBalance - preclaimBalance; if (unstaked >= target) { // We unstaked enough break; } } require(unstaked >= target, "Cannot unstake enough"); require(_totalUsableMagic() >= target, "Not enough in contract after unstaking"); // Only check for removal after, so we don't mutate while looping _removeZeroStakes(); } /** * @dev Harvest rewards from the IAtlasMine and send them back to * this contract. * * @return earned The amount of rewards earned for depositors, minus the fee. * @return feeEearned The amount of fees earned for the contract operator. */ function _harvestMine() internal returns (uint256, uint256) { uint256 preclaimBalance = magic.balanceOf(address(this)); try mine.harvestAll() { uint256 postclaimBalance = magic.balanceOf(address(this)); uint256 earned = postclaimBalance - preclaimBalance; // Reserve the 'fee' amount of what is earned (, uint256 feeEarned, uint256 feeRefunded) = _calculateHarvestRewardFee(earned); feeReserve += feeEarned - feeRefunded; emit MineHarvest(earned - feeEarned, feeEarned - feeRefunded, feeRefunded); return (earned - feeEarned, feeRefunded); } catch { // Failed because of reward debt calculation - should be 0 return (0, 0); } } function _calculateHarvestRewardFee(uint256 earned) internal view returns ( uint256, uint256, uint256 ) { uint256 feeEarned = (earned * fee) / FEE_DENOMINATOR; uint256 accFeePerShare = (feeEarned * ONE) / totalShare; uint256 feeRefunded = (accFeePerShare * totalWhitelistedFeeShare) / ONE; return (earned - feeEarned, feeEarned, feeRefunded); } /** * @dev Harvest rewards from the mine so that stakers can claim. * Recalculate how many rewards are distributed to each share. */ function _updateRewards() internal { if (totalStaked == 0 || totalShare == 0) return; (uint256 newRewards, uint256 feeRefunded) = _harvestMine(); accRewardsPerShare += (newRewards * ONE) / totalShare; if (totalWhitelistedFeeShare > 0) accRefundedFeePerShare += (feeRefunded * ONE) / totalWhitelistedFeeShare; } /** * @dev After mutating a stake (by withdrawing fully or partially), * get updated data from the staking contract, and update the stake amounts * * @param stakeIndex The index of the stake in the Stakes storage array. * * @return amount The current, updated amount of the stake. */ function _updateStakeDepositAmount(uint256 stakeIndex) internal returns (uint256) { Stake storage s = stakes[stakeIndex]; (, uint256 depositAmount, , , , , ) = mine.userInfo(address(this), s.depositId); s.amount = depositAmount; return s.amount; } /** * @dev Find stakes with zero deposit amount and remove them from tracking. * Uses recursion to stop from mutating an array we are currently looping over. * If a zero stake is found, it is removed, and the function is restarted, * such that it is always working from a 'clean' array. * */ function _removeZeroStakes() internal { bool shouldRecurse = stakes.length > 0; for (uint256 i = 0; i < stakes.length; i++) { _updateStakeDepositAmount(i); Stake storage s = stakes[i]; if (s.amount == 0) { _removeStake(i); // Stop looping and start again - we will skip // out of the look and recurse break; } if (i == stakes.length - 1) { // We didn't remove anything, so stop recursing shouldRecurse = false; } } if (shouldRecurse) { _removeZeroStakes(); } } /** * @dev Calculate total amount of MAGIC usable by the contract. * 'Usable' means available for either withdrawal or re-staking. * Counts unstaked magic less fee reserve. * * @return amount The amount of usable MAGIC. */ function _totalUsableMagic() internal view returns (uint256) { // Current magic held in contract uint256 unstaked = magic.balanceOf(address(this)); return unstaked - tokenBuffer - feeReserve; } /** * @dev Calculate total amount of MAGIC under control of the contract. * Counts staked and unstaked MAGIC. Does _not_ count accumulated * but unclaimed rewards. * * @return amount The total amount of MAGIC under control of the contract. */ function _totalControlledMagic() internal view returns (uint256) { // Current magic staked in mine uint256 staked = 0; for (uint256 i = 0; i < stakes.length; i++) { staked += stakes[i].amount; } return staked + _totalUsableMagic(); } /** * @dev Remove a tracked stake from any position in the stakes array. * Used when a stake is no longer relevant i.e. fully withdrawn. * Mutates the Stakes array in storage. * * @param index The index of the stake to remove. */ function _removeStake(uint256 index) internal { if (index >= stakes.length) return; for (uint256 i = index; i < stakes.length - 1; i++) { stakes[i] = stakes[i + 1]; } delete stakes[stakes.length - 1]; stakes.pop(); } modifier onlySuperAdminOrOwner() { require(msg.sender == owner() || superAdmins[msg.sender], "Not Super Admin"); _; } modifier onlyBattleflyVaultOrOwner() { require(msg.sender == owner() || battleflyVaults[msg.sender], "Not BattleflyVault"); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/IBattleflyAtlasStaker.sol"; import "./interfaces/IAtlasMine.sol"; import "./interfaces/ISpecialNFT.sol"; import "./interfaces/IBattleflyVault.sol"; contract BattleflyVault is Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; using AddressUpgradeable for address; using SafeCastUpgradeable for uint256; using SafeCastUpgradeable for int256; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; // ============================================ STATE ============================================== struct UserStake { uint256 amount; uint256 unlockAt; uint256 withdrawAt; IAtlasMine.Lock lock; uint256 battleflyStakerDepositId; address owner; } // ============= Global Immutable State ============== IERC20Upgradeable public magic; IBattleflyAtlasStaker public BattleflyStaker; // ============= Global Staking State ============== mapping(uint256 => UserStake) public userStakes; mapping(address => EnumerableSetUpgradeable.UintSet) private stakesOfOwner; uint256 nextStakeId; uint256 public totalStaked; // ============= Global Admin ============== mapping(address => bool) private adminAccess; // ============================================ EVENT ============================================== event Claim(address indexed user, uint256 stakeId, uint256 amount); event Stake(address indexed user, uint256 stakeId, uint256 amount, IAtlasMine.Lock lock); event Withdraw(address indexed user, uint256 stakeId, uint256 amount); event SetFee(uint256 oldFee, uint256 newFee, uint256 denominator); event WithdrawFee(address receiver, uint256 amount); event SetAdminAccess(address indexed user, bool access); // ============================================ INITIALIZE ============================================== function initialize(address _magicAddress, address _BattleflyStakerAddress) external initializer { __Ownable_init(); __ReentrancyGuard_init(); magic = IERC20Upgradeable(_magicAddress); BattleflyStaker = IBattleflyAtlasStaker(_BattleflyStakerAddress); nextStakeId = 0; // Approve the AtlasStaker contract to spend the magic magic.safeApprove(address(BattleflyStaker), 2**256 - 1); } // ============================================ USER FUNCTIONS ============================================== function stake(uint256 amount, IAtlasMine.Lock lock) external { magic.safeTransferFrom(msg.sender, address(this), amount); _stake(amount, lock); } function _stake(uint256 amount, IAtlasMine.Lock lock) internal returns (uint256) { require(amount > 0, "Amount must be greater than 0"); uint256 battleflyStakerDepositId = BattleflyStaker.deposit(amount, lock); IBattleflyAtlasStaker.VaultStake memory vaultStake = BattleflyStaker.getVaultStake( address(this), battleflyStakerDepositId ); UserStake storage s = userStakes[nextStakeId]; s.amount = amount; s.unlockAt = vaultStake.unlockAt; s.lock = lock; s.battleflyStakerDepositId = battleflyStakerDepositId; s.owner = msg.sender; stakesOfOwner[msg.sender].add(nextStakeId); emit Stake(msg.sender, nextStakeId, amount, lock); nextStakeId++; totalStaked += amount; return nextStakeId - 1; } function claimAll() public nonReentrant { uint256 totalReward = 0; for (uint256 i = 0; i < stakesOfOwner[msg.sender].length(); i++) { uint256 stakeId = stakesOfOwner[msg.sender].at(i); if (_getStakeClaimableAmount(stakeId) > 0) { totalReward += _claim(stakeId); } } require(totalReward > 0, "No rewards to claim"); magic.safeTransfer(msg.sender, totalReward); } function claimAllAndRestake(IAtlasMine.Lock lock) external { uint256 totalReward = 0; for (uint256 i = 0; i < stakesOfOwner[msg.sender].length(); i++) { uint256 stakeId = stakesOfOwner[msg.sender].at(i); if (_getStakeClaimableAmount(stakeId) > 0) { totalReward += _claim(stakeId); } } require(totalReward > 0, "No rewards to claim"); _stake(totalReward, lock); } function claim(uint256[] memory stakeIds) external nonReentrant { uint256 totalReward = 0; for (uint256 i = 0; i < stakeIds.length; i++) { UserStake storage s = userStakes[stakeIds[i]]; require(s.owner == msg.sender, "Only the owner can claim"); if (_getStakeClaimableAmount(stakeIds[i]) > 0) { totalReward += _claim(stakeIds[i]); } } require(totalReward > 0, "No rewards to claim"); magic.safeTransfer(msg.sender, totalReward); } function _claim(uint256 stakeId) internal returns (uint256) { UserStake memory s = userStakes[stakeId]; uint256 claimedAmount = BattleflyStaker.claim(s.battleflyStakerDepositId); emit Claim(s.owner, stakeId, claimedAmount); return claimedAmount; } function _getStakeClaimableAmount(uint256 stakeId) internal view returns (uint256) { UserStake memory s = userStakes[stakeId]; uint256 claimAmount = BattleflyStaker.pendingRewards(address(this), s.battleflyStakerDepositId); return claimAmount; } function getUserClaimableAmount(address user) external view returns (uint256) { uint256 total = 0; for (uint256 i = 0; i < stakesOfOwner[user].length(); i++) { uint256 stakeId = stakesOfOwner[user].at(i); total += _getStakeClaimableAmount(stakeId); } return total; } function getUserStakes(address user) external view returns (UserStake[] memory) { UserStake[] memory stakes = new UserStake[](stakesOfOwner[user].length()); for (uint256 i = 0; i < stakesOfOwner[user].length(); i++) { uint256 stakeId = stakesOfOwner[user].at(i); stakes[i] = userStakes[stakeId]; } return stakes; } function withdrawAll() external nonReentrant { require(stakesOfOwner[msg.sender].length() > 0, "No stakes to withdraw"); uint256 receiveAmount; for (uint256 i = 0; i < stakesOfOwner[msg.sender].length(); i++) { uint256 stakeId = stakesOfOwner[msg.sender].at(i); if (userStakes[stakeId].unlockAt < block.timestamp) { receiveAmount += _withdraw(stakeId); } } require(receiveAmount > 0, "No stakes to withdraw"); magic.safeTransfer(msg.sender, receiveAmount); } function withdraw(uint256[] memory stakeIds) external nonReentrant { uint256 receiveAmount; for (uint256 i = 0; i < stakeIds.length; i++) { UserStake storage s = userStakes[stakeIds[i]]; require(s.owner == msg.sender, "Only the owner can withdraw"); receiveAmount += _withdraw(stakeIds[i]); } require(receiveAmount > 0, "No stakes to withdraw"); magic.safeTransfer(msg.sender, receiveAmount); } function _withdraw(uint256 stakeId) internal returns (uint256 withdrawAmount) { UserStake storage s = userStakes[stakeId]; withdrawAmount = s.amount; require(s.unlockAt < block.timestamp, "Cannot withdraw before the lock time"); uint256 claimableAmount = _getStakeClaimableAmount(stakeId); if (claimableAmount > 0) { withdrawAmount += _claim(stakeId); } BattleflyStaker.withdraw(s.battleflyStakerDepositId); totalStaked -= s.amount; stakesOfOwner[msg.sender].remove(stakeId); s.withdrawAt = block.timestamp; emit Withdraw(msg.sender, stakeId, withdrawAmount); } // ============================================ OWNER FUNCTIONS ============================================== function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; emit SetAdminAccess(user, access); } // ============================================ MODIFIER ============================================== modifier onlyAdminAccessOrOwner() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; interface IBattleflyVault {}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/IBattleflyAtlasStaker.sol"; import "./interfaces/IAtlasMine.sol"; import "./interfaces/ISpecialNFT.sol"; import "./interfaces/IBattleflyVault.sol"; import "./interfaces/IBattleflyFounderVault.sol"; contract BattleflyFlywheelVault is IBattleflyVault, Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; using AddressUpgradeable for address; using SafeCastUpgradeable for uint256; using SafeCastUpgradeable for int256; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; // ============================================ STATE ============================================== struct UserStake { uint256 amount; uint256 unlockAt; uint256 withdrawAt; uint256 battleflyStakerDepositId; address owner; IAtlasMine.Lock lock; } // ============= Global Immutable State ============== IERC20Upgradeable public magic; IBattleflyAtlasStaker public BattleflyStaker; // ============= Global Staking State ============== mapping(uint256 => UserStake) public userStakes; mapping(address => EnumerableSetUpgradeable.UintSet) private stakesOfOwner; uint256 public nextStakeId; uint256 public totalStaked; uint256 public totalFee; uint256 public totalFeeWithdrawn; uint256 public FEE; uint256 public FEE_DENOMINATOR; // ============= Global Admin ============== mapping(address => bool) private adminAccess; address public TREASURY_WALLET; IBattleflyFounderVault public founderVaultV1; IBattleflyFounderVault public founderVaultV2; uint256 public stakeableAmountPerV1; uint256 public stakeableAmountPerV2; // ============================================ EVENT ============================================== event Claim(address indexed user, uint256 stakeId, uint256 amount, uint256 fee); event Stake(address indexed user, uint256 stakeId, uint256 amount, IAtlasMine.Lock lock); event Withdraw(address indexed user, uint256 stakeId, uint256 amount); event SetFee(uint256 oldFee, uint256 newFee, uint256 denominator); event WithdrawFee(address receiver, uint256 amount); event SetAdminAccess(address indexed user, bool access); // ============================================ INITIALIZE ============================================== function initialize( address _magicAddress, address _BattleflyStakerAddress, uint256 _fee, uint256 _feeDominator, address _founderVaultV1Address, address _founderVaultV2Address, uint256 _stakeableAmountPerV1, uint256 _stakeableAmountPerV2 ) external initializer { __Ownable_init(); __ReentrancyGuard_init(); magic = IERC20Upgradeable(_magicAddress); BattleflyStaker = IBattleflyAtlasStaker(_BattleflyStakerAddress); nextStakeId = 0; FEE_DENOMINATOR = _feeDominator; founderVaultV1 = IBattleflyFounderVault(_founderVaultV1Address); founderVaultV2 = IBattleflyFounderVault(_founderVaultV2Address); stakeableAmountPerV1 = _stakeableAmountPerV1; stakeableAmountPerV2 = _stakeableAmountPerV2; TREASURY_WALLET = 0xF5411006eEfD66c213d2fd2033a1d340458B7226; // Approve the AtlasStaker contract to spend the magic magic.safeApprove(address(BattleflyStaker), 2**256 - 1); _setFee(_fee); } // ============================================ USER FUNCTIONS ============================================== function stake(uint256 amount, IAtlasMine.Lock lock) external { magic.safeTransferFrom(msg.sender, address(this), amount); _stake(amount, lock); } function _stake(uint256 amount, IAtlasMine.Lock lock) internal returns (uint256) { require(amount > 0, "Amount must be greater than 0"); uint256 battleflyStakerDepositId = BattleflyStaker.deposit(amount, lock); IBattleflyAtlasStaker.VaultStake memory vaultStake = BattleflyStaker.getVaultStake( address(this), battleflyStakerDepositId ); UserStake storage s = userStakes[nextStakeId]; s.amount = amount; s.unlockAt = vaultStake.unlockAt; s.battleflyStakerDepositId = battleflyStakerDepositId; s.owner = msg.sender; s.lock = lock; stakesOfOwner[msg.sender].add(nextStakeId); emit Stake(msg.sender, nextStakeId, amount, lock); nextStakeId++; totalStaked += amount; return nextStakeId - 1; } function getStakeAmount(address user) public view returns (uint256, uint256) { uint256 totalStakingV1; uint256 totalStakingV2; IBattleflyFounderVault.FounderStake[] memory v1Stakes = founderVaultV1.stakesOf(user); IBattleflyFounderVault.FounderStake[] memory v2Stakes = founderVaultV2.stakesOf(user); for (uint256 i = 0; i < v1Stakes.length; i++) { totalStakingV1 += v1Stakes[i].amount; } for (uint256 i = 0; i < v2Stakes.length; i++) { totalStakingV2 += v2Stakes[i].amount; } uint256 totalUserStaking = 0; for (uint256 i = 0; i < stakesOfOwner[user].length(); i++) { uint256 stakeId = stakesOfOwner[user].at(i); totalUserStaking += userStakes[stakeId].amount; } uint256 stakedAmount = totalStakingV1 * stakeableAmountPerV1 + totalStakingV2 * stakeableAmountPerV2; return (stakedAmount, totalUserStaking); } function claimAll() public nonReentrant { uint256 totalReward = 0; for (uint256 i = 0; i < stakesOfOwner[msg.sender].length(); i++) { uint256 stakeId = stakesOfOwner[msg.sender].at(i); if (_getStakeClaimableAmount(stakeId) > 0) { totalReward += _claim(stakeId); } } require(totalReward > 0, "No rewards to claim"); magic.safeTransfer(msg.sender, totalReward); } function claimAllAndRestake(IAtlasMine.Lock lock) external { uint256 totalReward = 0; for (uint256 i = 0; i < stakesOfOwner[msg.sender].length(); i++) { uint256 stakeId = stakesOfOwner[msg.sender].at(i); if (_getStakeClaimableAmount(stakeId) > 0) { totalReward += _claim(stakeId); } } require(totalReward > 0, "No rewards to claim"); _stake(totalReward, lock); } function claim(uint256[] memory stakeIds) external nonReentrant { uint256 totalReward = 0; for (uint256 i = 0; i < stakeIds.length; i++) { UserStake storage s = userStakes[stakeIds[i]]; require(s.owner == msg.sender, "Only the owner can claim"); if (_getStakeClaimableAmount(stakeIds[i]) > 0) { totalReward += _claim(stakeIds[i]); } } require(totalReward > 0, "No rewards to claim"); magic.safeTransfer(msg.sender, totalReward); } function _claim(uint256 stakeId) internal returns (uint256) { UserStake memory s = userStakes[stakeId]; uint256 claimedAmount = BattleflyStaker.claim(s.battleflyStakerDepositId); uint256 fee = (claimedAmount * FEE) / FEE_DENOMINATOR; uint256 userClaimAmount = claimedAmount - fee; totalFee += fee; emit Claim(s.owner, stakeId, userClaimAmount, fee); return userClaimAmount; } function _getStakeClaimableAmount(uint256 stakeId) internal view returns (uint256) { UserStake memory s = userStakes[stakeId]; uint256 claimAmount = BattleflyStaker.pendingRewards(address(this), s.battleflyStakerDepositId); uint256 fee = (claimAmount * FEE) / FEE_DENOMINATOR; uint256 userClaimAmount = claimAmount - fee; return userClaimAmount; } function getUserClaimableAmount(address user) external view returns (uint256) { uint256 total = 0; for (uint256 i = 0; i < stakesOfOwner[user].length(); i++) { uint256 stakeId = stakesOfOwner[user].at(i); total += _getStakeClaimableAmount(stakeId); } return total; } function getUserStakes(address user) external view returns (UserStake[] memory) { UserStake[] memory stakes = new UserStake[](stakesOfOwner[user].length()); for (uint256 i = 0; i < stakesOfOwner[user].length(); i++) { uint256 stakeId = stakesOfOwner[user].at(i); stakes[i] = userStakes[stakeId]; } return stakes; } function withdrawAll() external nonReentrant { require(stakesOfOwner[msg.sender].length() > 0, "No stakes to withdraw"); uint256 receiveAmount; for (uint256 i = 0; i < stakesOfOwner[msg.sender].length(); i++) { uint256 stakeId = stakesOfOwner[msg.sender].at(i); if (userStakes[stakeId].unlockAt < block.timestamp) { receiveAmount += _withdraw(stakeId); } } require(receiveAmount > 0, "No stakes to withdraw"); magic.safeTransfer(msg.sender, receiveAmount); } function withdraw(uint256[] memory stakeIds) external nonReentrant { uint256 receiveAmount; for (uint256 i = 0; i < stakeIds.length; i++) { UserStake storage s = userStakes[stakeIds[i]]; require(s.owner == msg.sender, "Only the owner can withdraw"); receiveAmount += _withdraw(stakeIds[i]); } require(receiveAmount > 0, "No stakes to withdraw"); magic.safeTransfer(msg.sender, receiveAmount); } function _withdraw(uint256 stakeId) internal returns (uint256 withdrawAmount) { UserStake storage s = userStakes[stakeId]; withdrawAmount = s.amount; require(s.unlockAt < block.timestamp, "Cannot withdraw before the lock time"); uint256 claimableAmount = _getStakeClaimableAmount(stakeId); if (claimableAmount > 0) { withdrawAmount += _claim(stakeId); } BattleflyStaker.withdraw(s.battleflyStakerDepositId); totalStaked -= s.amount; stakesOfOwner[msg.sender].remove(stakeId); s.withdrawAt = block.timestamp; emit Withdraw(msg.sender, stakeId, withdrawAmount); } function stakeableAmountPerFounder(address vault) external view returns (uint256) { if (vault == address(founderVaultV1)) { return stakeableAmountPerV1; } if (vault == address(founderVaultV2)) { return stakeableAmountPerV2; } return 0; } // ============================================ ADMIN FUNCTIONS ============================================== function setFee(uint256 _fee) external onlyAdminAccessOrOwner { _setFee(_fee); require(totalStaked == 0, "Fee can only be updated without any stakers"); emit SetFee(FEE, _fee, FEE_DENOMINATOR); } function _setFee(uint256 _fee) private { require(_fee < FEE_DENOMINATOR, "Fee must be less than the fee dominator"); FEE = _fee; } function setTreasuryWallet(address _treasuryWallet) external onlyAdminAccessOrOwner { TREASURY_WALLET = _treasuryWallet; } function withdrawFeeToTreasury() external onlyAdminAccessOrOwner { uint256 amount = totalFee - totalFeeWithdrawn; require(amount > 0, "No fee to withdraw"); totalFeeWithdrawn += amount; magic.safeTransfer(TREASURY_WALLET, amount); emit WithdrawFee(TREASURY_WALLET, amount); } // ============================================ OWNER FUNCTIONS ============================================== function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; emit SetAdminAccess(user, access); } // ============================================ MODIFIER ============================================== modifier onlyAdminAccessOrOwner() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } }
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.2; import "./interfaces/ISpecialNFT.sol"; contract SpecialNFTRouter { ISpecialNFT token; constructor(address _tokenAddress) { token = ISpecialNFT(_tokenAddress); } function getAllBalance(address owner) external view returns (uint256[] memory, uint256[] memory) { uint256 balance = token.balanceOf(owner); uint256[] memory tokenIds = new uint256[](balance); uint256[] memory tokenTypes = new uint256[](balance); for (uint256 i = 0; i < balance; i++) { tokenIds[i] = token.tokenOfOwnerByIndex(owner, i); tokenTypes[i] = token.getSpecialNFTType(tokenIds[i]); } return (tokenIds, tokenTypes); } function getBalanceOf(address owner, uint256 typeId) external view returns (uint256[] memory) { uint256 balance = token.balanceOf(owner); uint256[] memory tokenIds = new uint256[](balance); uint256 count = 0; for (uint256 i = 0; i < balance; i++) { uint256 tokenId = token.tokenOfOwnerByIndex(owner, i); uint256 tokenType = token.getSpecialNFTType(tokenId); if (tokenType == typeId) { tokenIds[count] = tokenId; count++; } } uint256[] memory result = new uint256[](count); for (uint256 i = 0; i < count; i++) { result[i] = tokenIds[i]; } return (result); } }
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "./interfaces/IHyperdome.sol"; import "./interfaces/IBattlefly.sol"; import "./interfaces/ISpecialNFT.sol"; import "./interfaces/IMod.sol"; import "./interfaces/IScrapToken.sol"; import "./interfaces/IItem.sol"; contract BattleflyGame is OwnableUpgradeable, ERC1155HolderUpgradeable, ERC721HolderUpgradeable { using SafeMathUpgradeable for uint256; using AddressUpgradeable for address; using SafeERC20Upgradeable for IERC20Upgradeable; mapping(address => bool) private adminAccess; IHyperdome private HyperdomeContract; //removed IBattlefly private BattleflyContract; ISpecialNFT private SpecialNFTContract; IMod private ModContract; // removed IERC20Upgradeable private MagicToken; IScrapToken private ScrapToken; // removed IItem private ItemContract; mapping(uint256 => bool) public ProcessedTransactions; uint8 constant TRANSACTION_TYPE_WITHDRAW_BATTLEFLY = 0; uint8 constant TRANSACTION_TYPE_WITHDRAW_MAGIC = 1; uint8 constant TRANSACTION_TYPE_WITHDRAW_ITEM = 2; event DepositHyperdome(uint256 indexed tokenId, address indexed user, uint256 timestamp); event WithdrawHyperdome(uint256 indexed tokenId, address indexed receiver, uint256 timestamp); event DepositBattlefly(uint256 indexed tokenId, address indexed user, uint256 timestamp); event WithdrawBattlefly(uint256 indexed tokenId, address indexed receiver, uint256 timestamp); event DepositItems(uint256 indexed tokenId, address indexed user, uint256 amount, uint256 timestamp); event WithdrawItems(uint256 indexed tokenId, address indexed receiver, uint256 amount, uint256 timestamp); event DepositMagic(uint256 amount, address indexed user, uint256 timestamp); event WithdrawMagic(uint256 amount, address indexed receiver, uint256 timestamp); event MintHyperdome(address indexed receiver, uint256 tokenId); event MintBattlefly(address indexed receiver, uint256 tokenId, uint256 battleflyType); event MintBattleflies(address[] receivers, uint256[] tokenIds, uint256[] battleflyTypes); event MintSpecialNFT(address indexed receiver, uint256 tokenId, uint256 specialNFTType); event MintSpecialNFTs(address[] receivers, uint256[] tokenIds, uint256[] specialNFTTypes); event MintMod(address indexed receiver, uint256 tokenId); event SetAdminAccess(address indexed user, bool access); event MintItems(uint256 indexed itemId, address indexed receiver, uint256 amount); function initialize( address hyperdomeContractAddress, address battleflyContractAddress, address specialNFTContractAddress, address modContractAddress, address magicTokenAddress, address scrapTokenAddress ) public initializer { __Ownable_init(); HyperdomeContract = IHyperdome(hyperdomeContractAddress); BattleflyContract = IBattlefly(battleflyContractAddress); SpecialNFTContract = ISpecialNFT(specialNFTContractAddress); ModContract = IMod(modContractAddress); MagicToken = IERC20Upgradeable(magicTokenAddress); ScrapToken = IScrapToken(scrapTokenAddress); } function initializeUpgrade(address itemContractAddress) external onlyOwner { ItemContract = IItem(itemContractAddress); } function setMagic(address magicAddress) external onlyOwner { MagicToken = IERC20Upgradeable(magicAddress); } function getBattlefliesOfOwner(address user) external view returns (uint256[] memory) { uint256 balance = BattleflyContract.balanceOf(user); uint256[] memory tokenIds = new uint256[](balance); for (uint256 i = 0; i < balance; i++) { tokenIds[i] = BattleflyContract.tokenOfOwnerByIndex(user, i); } return tokenIds; } function getSpecialNFTsOfOwner(address user) external view returns (uint256[] memory) { uint256 balance = SpecialNFTContract.balanceOf(user); uint256[] memory tokenIds = new uint256[](balance); for (uint256 i = 0; i < balance; i++) { tokenIds[i] = SpecialNFTContract.tokenOfOwnerByIndex(user, i); } return tokenIds; } // ADMIN function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; emit SetAdminAccess(user, access); } function mintSpecialNFT(address receiver, uint256 specialNFTType) external onlyAdminAccess returns (uint256) { uint256 tokenId = SpecialNFTContract.mintSpecialNFT(receiver, specialNFTType); emit MintSpecialNFT(receiver, tokenId, specialNFTType); return tokenId; } function mintSpecialNFTs( address receiver, uint256 specialNFTType, uint256 amount ) external onlyAdminAccess returns (uint256[] memory) { uint256[] memory tokenIds = SpecialNFTContract.mintSpecialNFTs(receiver, specialNFTType, amount); for (uint256 i = 0; i < amount; i++) { emit MintSpecialNFT(receiver, tokenIds[i], specialNFTType); } return tokenIds; } function mintItems( uint256 itemId, address receiver, uint256 amount ) external onlyAdminAccess { ItemContract.mintItems(itemId, receiver, amount, ""); emit MintItems(itemId, receiver, amount); } function mintBattlefly(address receiver, uint256 battleflyType) external onlyAdminAccess returns (uint256) { uint256 tokenId = BattleflyContract.mintBattlefly(receiver, battleflyType); emit MintBattlefly(receiver, tokenId, battleflyType); return tokenId; } function mintBattleflies( address receiver, uint256 battleflyType, uint256 amount ) external onlyAdminAccess returns (uint256[] memory) { uint256[] memory tokenIds = BattleflyContract.mintBattleflies(receiver, battleflyType, amount); for (uint256 i = 0; i < amount; i++) { emit MintBattlefly(receiver, tokenIds[i], battleflyType); } return tokenIds; } // Battlefly function bulkDepositBattlefly(uint256[] memory tokenIds) external { for (uint256 i = 0; i < tokenIds.length; i++) { depositBattlefly(tokenIds[i]); } } function depositBattlefly(uint256 tokenId) public { BattleflyContract.safeTransferFrom(_msgSender(), address(this), tokenId); emit DepositBattlefly(tokenId, _msgSender(), block.timestamp); } function withdrawBattlefly(uint256 tokenId, address receiver) external onlyAdminAccess { BattleflyContract.safeTransferFrom(address(this), receiver, tokenId); emit WithdrawBattlefly(tokenId, receiver, block.timestamp); } function claimWithdrawBattleflies( uint256[] memory tokenIds, uint256 transactionId, uint8 v, bytes32 r, bytes32 s ) external { require(ProcessedTransactions[transactionId] == false, "Transaction have been processed"); ProcessedTransactions[transactionId] = true; bytes32 payloadHash = keccak256( abi.encodePacked(_msgSender(), tokenIds, transactionId, TRANSACTION_TYPE_WITHDRAW_BATTLEFLY) ); bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash)); (address admin, ECDSAUpgradeable.RecoverError result) = ECDSAUpgradeable.tryRecover(messageHash, v, r, s); require(result == ECDSAUpgradeable.RecoverError.NoError && adminAccess[admin], "Require admin access"); for (uint256 i = 0; i < tokenIds.length; i++) { BattleflyContract.safeTransferFrom(address(this), _msgSender(), tokenIds[i]); emit WithdrawBattlefly(tokenIds[i], _msgSender(), block.timestamp); } } //Magic token function depositMagic(uint256 amount) external { MagicToken.safeTransferFrom(_msgSender(), address(this), amount); emit DepositMagic(amount, _msgSender(), block.timestamp); } function withdrawMagic(uint256 amount, address receiver) external onlyAdminAccess { MagicToken.safeTransfer(receiver, amount); emit WithdrawMagic(amount, receiver, block.timestamp); } function claimWithdrawMagic( uint256 amount, uint256 transactionId, uint8 v, bytes32 r, bytes32 s ) external { require(ProcessedTransactions[transactionId] == false, "Transaction have been processed"); ProcessedTransactions[transactionId] = true; bytes32 payloadHash = keccak256( abi.encodePacked(_msgSender(), amount, transactionId, TRANSACTION_TYPE_WITHDRAW_MAGIC) ); bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash)); (address admin, ECDSAUpgradeable.RecoverError result) = ECDSAUpgradeable.tryRecover(messageHash, v, r, s); require(result == ECDSAUpgradeable.RecoverError.NoError && adminAccess[admin], "Require admin access"); MagicToken.safeTransfer(_msgSender(), amount); emit WithdrawMagic(amount, _msgSender(), block.timestamp); } //Item function depositItems(uint256 itemId, uint256 amount) external { ItemContract.safeTransferFrom(_msgSender(), address(this), itemId, amount, ""); emit DepositItems(itemId, _msgSender(), amount, block.timestamp); } function withdrawItems( uint256 itemId, uint256 amount, address receiver ) external onlyAdminAccess { ItemContract.safeTransferFrom(address(this), receiver, itemId, amount, ""); emit WithdrawItems(itemId, receiver, amount, block.timestamp); } function claimWithdrawItems( uint256 itemId, uint256 amount, uint256 transactionId, uint8 v, bytes32 r, bytes32 s ) external { require(ProcessedTransactions[transactionId] == false, "Transaction have been processed"); ProcessedTransactions[transactionId] = true; bytes32 payloadHash = keccak256( abi.encodePacked(_msgSender(), itemId, amount, transactionId, TRANSACTION_TYPE_WITHDRAW_ITEM) ); bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash)); (address admin, ECDSAUpgradeable.RecoverError result) = ECDSAUpgradeable.tryRecover(messageHash, v, r, s); require(result == ECDSAUpgradeable.RecoverError.NoError && adminAccess[admin], "Require admin access"); ItemContract.safeTransferFrom(address(this), _msgSender(), itemId, amount, ""); emit WithdrawItems(itemId, _msgSender(), amount, block.timestamp); } //modifier modifier onlyAdminAccess() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol"; import "./IBattlefly.sol"; interface IHyperdome is IERC721EnumerableUpgradeable { function mintHyperdome(address receiver) external returns (uint256); }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IScrapToken is IERC20 { function mint(uint256 amount, address receiver) external returns (uint256); }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; interface IItem is IERC1155Upgradeable { function mintItems( uint256 itemId, address receiver, uint256 amount, bytes memory data ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/IBattleflyAtlasStaker.sol"; import "./interfaces/IAtlasMine.sol"; import "./interfaces/ISpecialNFT.sol"; import "./interfaces/IBattleflyFounderVault.sol"; import "./interfaces/IBattleflyFlywheelVault.sol"; contract BattleflyFounderVault is Initializable, OwnableUpgradeable, ERC1155HolderUpgradeable, ERC721HolderUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; using AddressUpgradeable for address; using SafeCastUpgradeable for uint256; using SafeCastUpgradeable for int256; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; // ============================================ STATE ============================================== struct FounderStake { uint256 amount; uint256 stakeTimestamp; address owner; uint256 lastClaimedDay; } struct DailyFounderEmission { uint256 totalEmission; uint256 totalFounders; } // ============= Global Immutable State ============== /// @notice MAGIC token /// @dev functionally immutable IERC20Upgradeable public magic; ISpecialNFT public founderNFT; uint256 public founderTypeID; IBattleflyAtlasStaker public BattleflyStaker; uint256 public startTimestamp; // ============= Global mutable State ============== uint256 totalEmission; uint256 claimedEmission; uint256 pendingFounderEmission; mapping(address => EnumerableSetUpgradeable.UintSet) private FounderStakeOfOwner; uint256 lastStakeTimestamp; mapping(uint256 => FounderStake) public FounderStakes; uint256 lastStakeId; mapping(address => bool) private adminAccess; uint256 public DaysSinceStart; mapping(uint256 => DailyFounderEmission) public DailyFounderEmissions; uint256 withdrawnOldFounder; uint256 unupdatedStakeIdFrom; uint256 public stakeBackPercent; uint256 public treasuryPercent; uint256 public v2VaultPercent; IBattleflyFounderVault battleflyFounderVaultV2; IBattleflyFlywheelVault battleflyFlywheelVault; // ============= Constant ============== address public constant TREASURY_WALLET = 0xF5411006eEfD66c213d2fd2033a1d340458B7226; uint256 public constant PERCENT_DENOMINATOR = 10000; IAtlasMine.Lock public constant DEFAULT_STAKE_BACK_LOCK = IAtlasMine.Lock.twoWeeks; mapping(uint256 => bool) public claimedPastEmission; uint256 public pastEmissionPerFounder; mapping(uint256 => uint256) public stakeIdOfFounder; mapping(uint256 => EnumerableSetUpgradeable.UintSet) stakingFounderOfStakeId; // ============================================ EVENTS ============================================== event ClaimDailyEmission( uint256 dayTotalEmission, uint256 totalFounderEmission, uint256 totalFounders, uint256 stakeBackAmount, uint256 treasuryAmount, uint256 v2VaultAmount ); event Claim(address user, uint256 stakeId, uint256 amount); event Withdraw(address user, uint256 stakeId, uint256 founderId); event Stake(address user, uint256 stakeId, uint256[] founderNFTIDs); event TopupMagicToStaker(address user, uint256 amount, IAtlasMine.Lock lock); event TopupTodayEmission(address user, uint256 amount); event ClaimPastEmission(address user, uint256 amount, uint256[] tokenIds); // ============================================ INITIALIZE ============================================== function initialize( address _magicAddress, address _BattleflyStakerAddress, uint256 _founderTypeID, address _founderNFTAddress, uint256 _startTimestamp, address _battleflyFounderVaultV2Address, uint256 _stakeBackPercent, uint256 _treasuryPercent, uint256 _v2VaultPercent ) external initializer { __ERC1155Holder_init(); __ERC721Holder_init(); __Ownable_init(); __ReentrancyGuard_init(); magic = IERC20Upgradeable(_magicAddress); BattleflyStaker = IBattleflyAtlasStaker(_BattleflyStakerAddress); founderNFT = (ISpecialNFT(_founderNFTAddress)); founderTypeID = _founderTypeID; lastStakeTimestamp = block.timestamp; lastStakeId = 0; startTimestamp = _startTimestamp; DaysSinceStart = 0; stakeBackPercent = _stakeBackPercent; treasuryPercent = _treasuryPercent; v2VaultPercent = _v2VaultPercent; if (_battleflyFounderVaultV2Address == address(0)) battleflyFounderVaultV2 = IBattleflyFounderVault(address(this)); else battleflyFounderVaultV2 = IBattleflyFounderVault(_battleflyFounderVaultV2Address); require(stakeBackPercent + treasuryPercent + v2VaultPercent < PERCENT_DENOMINATOR); // Approve the AtlasStaker contract to spend the magic magic.safeApprove(address(BattleflyStaker), 2**256 - 1); } // ============================================ USER OPERATIONS ============================================== function claimPastEmission() external { require(pastEmissionPerFounder != 0, "No past founder emission to claim"); uint256[] memory tokenIds = getPastEmissionClaimableTokens(msg.sender); require(tokenIds.length > 0, "No tokens to claim"); for (uint256 i = 0; i < tokenIds.length; i++) { claimedPastEmission[tokenIds[i]] = true; } magic.safeTransfer(msg.sender, pastEmissionPerFounder * tokenIds.length); emit ClaimPastEmission(msg.sender, pastEmissionPerFounder * tokenIds.length, tokenIds); } function getPastEmissionClaimableTokens(address user) public view returns (uint256[] memory) { uint256 balance = founderNFT.balanceOf(user); uint256[] memory tokenIds = new uint256[](balance); uint256 countClaimable = 0; for (uint256 i = 0; i < balance; i++) { uint256 tokenId = founderNFT.tokenOfOwnerByIndex(user, i); uint256 tokenType = founderNFT.getSpecialNFTType(tokenId); if (tokenType == founderTypeID && claimedPastEmission[tokenId] == false) { tokenIds[countClaimable] = tokenId; countClaimable++; } } (, uint256[][] memory stakeTokens) = stakesOf(user); uint256 countClaimableStaked = 0; uint256 balanceStaked = 0; for (uint256 i = 0; i < stakeTokens.length; i++) { balanceStaked += stakeTokens[i].length; } uint256[] memory stakingTokenIds = new uint256[](balanceStaked); for (uint256 i = 0; i < stakeTokens.length; i++) { uint256[] memory stakeTokenIds = stakeTokens[i]; for (uint256 j = 0; j < stakeTokenIds.length; j++) { uint256 tokenId = stakeTokenIds[j]; uint256 tokenType = founderNFT.getSpecialNFTType(tokenId); if (tokenType == founderTypeID && claimedPastEmission[tokenId] == false) { stakingTokenIds[countClaimableStaked] = tokenId; countClaimableStaked++; } } } uint256[] memory result = new uint256[](countClaimable + countClaimableStaked); for (uint256 i = 0; i < countClaimable; i++) { result[i] = tokenIds[i]; } for (uint256 i = countClaimable; i < countClaimable + countClaimableStaked; i++) { result[i] = stakingTokenIds[i - countClaimable]; } return result; } function setTokenClaimedPastEmission(uint256[] memory tokenIds, bool isClaimed) external onlyOwner { for (uint256 i = 0; i < tokenIds.length; i++) { claimedPastEmission[tokenIds[i]] = isClaimed; } } function setPastEmission(uint256 amount) external onlyOwner { pastEmissionPerFounder = amount; } function stakesOf(address owner) public view returns (FounderStake[] memory, uint256[][] memory) { FounderStake[] memory stakes = new FounderStake[](FounderStakeOfOwner[owner].length()); uint256[][] memory _founderIDsOfStake = new uint256[][](FounderStakeOfOwner[owner].length()); for (uint256 i = 0; i < FounderStakeOfOwner[owner].length(); i++) { stakes[i] = FounderStakes[FounderStakeOfOwner[owner].at(i)]; _founderIDsOfStake[i] = stakingFounderOfStakeId[FounderStakeOfOwner[owner].at(i)].values(); } return (stakes, _founderIDsOfStake); } function stakeFounderNFT(uint256[] memory ids) external { require(ids.length != 0, "Must provide at least one founder NFT ID"); for (uint256 i = 0; i < ids.length; i++) { require(founderNFT.getSpecialNFTType(ids[i]) == founderTypeID, "Not valid founder NFT"); founderNFT.safeTransferFrom(msg.sender, address(this), ids[i]); } uint256 currentDay = (block.timestamp - startTimestamp) / 24 hours; lastStakeId++; FounderStakes[lastStakeId] = ( FounderStake({ amount: ids.length, stakeTimestamp: block.timestamp, owner: msg.sender, lastClaimedDay: currentDay }) ); for (uint256 i = 0; i < ids.length; i++) { stakeIdOfFounder[ids[i]] = lastStakeId; stakingFounderOfStakeId[lastStakeId].add(ids[i]); } FounderStakeOfOwner[msg.sender].add(lastStakeId); emit Stake(msg.sender, lastStakeId, ids); } function claimAll() external nonReentrant { uint256 totalReward = 0; for (uint256 i = 0; i < FounderStakeOfOwner[msg.sender].length(); i++) { totalReward += _claimByStakeId(FounderStakeOfOwner[msg.sender].at(i)); } require(totalReward > 0, "No reward to claim"); } function withdrawAll() external nonReentrant { require(FounderStakeOfOwner[msg.sender].length() > 0, "No STAKE to withdraw"); uint256 totalWithdraw = 0; for (uint256 i = 0; i < FounderStakeOfOwner[msg.sender].length(); i++) { uint256 stakeId = FounderStakeOfOwner[msg.sender].at(i); _claimByStakeId(stakeId); totalWithdraw += FounderStakes[stakeId].amount; _withdrawByStakeId(stakeId); } (uint256 stakeableAmount, uint256 stakingAmount) = battleflyFlywheelVault.getStakeAmount(msg.sender); uint256 stakeableAmountPerFounder = battleflyFlywheelVault.stakeableAmountPerFounder(address(this)); require( stakingAmount <= stakeableAmount - stakeableAmountPerFounder * totalWithdraw, "Pls withdraw FlywheelVault first" ); } function _withdrawByStakeId(uint256 stakeId) internal { FounderStake storage stake = FounderStakes[stakeId]; _claimByStakeId(stakeId); for (uint256 i = 0; i < stakingFounderOfStakeId[stakeId].length(); i++) { founderNFT.safeTransferFrom(address(this), stake.owner, stakingFounderOfStakeId[stakeId].at(i)); emit Withdraw(stake.owner, stakeId, stakingFounderOfStakeId[stakeId].at(i)); } if (stake.stakeTimestamp < (startTimestamp + (DaysSinceStart) * 24 hours - 24 hours)) { withdrawnOldFounder += stakingFounderOfStakeId[stakeId].length(); } FounderStakeOfOwner[stake.owner].remove(stakeId); delete FounderStakes[stakeId]; delete stakingFounderOfStakeId[stakeId]; } function _claimByStakeId(uint256 stakeId) internal returns (uint256) { require(stakeId != 0, "No stake to claim"); FounderStake storage stake = FounderStakes[stakeId]; uint256 totalReward = _getClaimableEmissionOf(stakeId); claimedEmission += totalReward; stake.lastClaimedDay = DaysSinceStart; magic.safeTransfer(stake.owner, totalReward); emit Claim(stake.owner, stakeId, totalReward); return totalReward; } function withdraw(uint256[] memory founderIds) external nonReentrant { require(founderIds.length > 0, "No Founder to withdraw"); uint256 totalWithdraw = 0; for (uint256 i = 0; i < founderIds.length; i++) { uint256 stakeId = stakeIdOfFounder[founderIds[i]]; require(FounderStakes[stakeId].owner == msg.sender, "Not your stake"); _claimBeforeWithdraw(founderIds[i]); _withdraw(founderIds[i]); totalWithdraw++; } // (uint256 stakeableAmount, uint256 stakingAmount) = battleflyFlywheelVault.getStakeAmount(msg.sender); // uint256 stakeableAmountPerFounder = battleflyFlywheelVault.stakeableAmountPerFounder(address(this)); // require(stakingAmount <= stakeableAmount - stakeableAmountPerFounder * totalWithdraw, "Pls withdraw FlywheelVault first"); } function _claimBeforeWithdraw(uint256 founderId) internal returns (uint256) { uint256 stakeId = stakeIdOfFounder[founderId]; FounderStake storage stake = FounderStakes[stakeId]; uint256 founderReward = _getClaimableEmissionOf(stakeId) / stake.amount; claimedEmission += founderReward; magic.safeTransfer(stake.owner, founderReward); emit Claim(stake.owner, stakeId, founderReward); return founderReward; } function getClaimableEmissionOf(address user) public view returns (uint256) { uint256 totalReward = 0; for (uint256 i = 0; i < FounderStakeOfOwner[user].length(); i++) { totalReward += _getClaimableEmissionOf(FounderStakeOfOwner[user].at(i)); } return totalReward; } function _getClaimableEmissionOf(uint256 stakeId) internal view returns (uint256) { uint256 totalReward = 0; FounderStake memory stake = FounderStakes[stakeId]; if (stake.lastClaimedDay == DaysSinceStart) return 0; for (uint256 j = stake.lastClaimedDay + 1; j <= DaysSinceStart; j++) { if (DailyFounderEmissions[j].totalFounders == 0 || stake.amount == 0) continue; totalReward += (DailyFounderEmissions[j].totalEmission / DailyFounderEmissions[j].totalFounders) * stake.amount; } return totalReward; } function _withdraw(uint256 founderId) internal { uint256 stakeId = stakeIdOfFounder[founderId]; FounderStake storage stake = FounderStakes[stakeId]; // _claim(founderId); founderNFT.safeTransferFrom(address(this), stake.owner, founderId); stake.amount--; delete stakeIdOfFounder[founderId]; stakingFounderOfStakeId[stakeId].remove(founderId); if (stake.stakeTimestamp < (startTimestamp + (DaysSinceStart) * 24 hours - 24 hours)) { withdrawnOldFounder += 1; } if (stake.amount == 0) { FounderStakeOfOwner[stake.owner].remove(stakeId); delete FounderStakes[stakeId]; } emit Withdraw(stake.owner, stakeId, founderId); } // ============================================ ADMIN OPERATIONS ============================================== function topupMagicToStaker(uint256 amount, IAtlasMine.Lock lock) external onlyAdminAccess { require(amount > 0); magic.safeTransferFrom(msg.sender, address(this), amount); _depositToStaker(amount, lock); emit TopupMagicToStaker(msg.sender, amount, lock); } function topupTodayEmission(uint256 amount) external onlyAdminAccess { require(amount > 0); magic.safeTransferFrom(msg.sender, address(this), amount); pendingFounderEmission += amount; emit TopupTodayEmission(msg.sender, amount); } function depositToStaker(uint256 amount, IAtlasMine.Lock lock) external onlyAdminAccess { require(magic.balanceOf(address(this)) >= amount); require(amount > 0); _depositToStaker(amount, lock); } function _depositToStaker(uint256 amount, IAtlasMine.Lock lock) internal { BattleflyStaker.deposit(amount, lock); } function claimDailyEmission() public onlyAdminAccess nonReentrant { uint256 currentDay = (block.timestamp - startTimestamp) / 24 hours; require(currentDay > DaysSinceStart, "Cant claim again for today"); uint256 todayTotalEmission = BattleflyStaker.claimAll(); uint256 todayTotalFounderNFTs = _updateTotalStakingFounders(currentDay); uint256 stakeBackAmount; uint256 v2VaultAmount; uint256 treasuryAmount; uint256 founderEmission; if (todayTotalEmission != 0) { stakeBackAmount = (todayTotalEmission * stakeBackPercent) / PERCENT_DENOMINATOR; if (stakeBackAmount != 0) _depositToStaker(stakeBackAmount, DEFAULT_STAKE_BACK_LOCK); v2VaultAmount = (todayTotalEmission * v2VaultPercent) / PERCENT_DENOMINATOR; if (v2VaultAmount != 0) battleflyFounderVaultV2.topupMagicToStaker(v2VaultAmount, DEFAULT_STAKE_BACK_LOCK); treasuryAmount = (todayTotalEmission * treasuryPercent) / PERCENT_DENOMINATOR; if (treasuryAmount != 0) magic.safeTransfer(TREASURY_WALLET, treasuryAmount); founderEmission += todayTotalEmission - stakeBackAmount - v2VaultAmount - treasuryAmount; } if (pendingFounderEmission > 0) { founderEmission += pendingFounderEmission; pendingFounderEmission = 0; } totalEmission += founderEmission; DaysSinceStart = currentDay; DailyFounderEmissions[DaysSinceStart] = DailyFounderEmission({ totalEmission: founderEmission, totalFounders: todayTotalFounderNFTs }); emit ClaimDailyEmission( todayTotalEmission, founderEmission, todayTotalFounderNFTs, stakeBackAmount, treasuryAmount, v2VaultAmount ); } function withdrawAllFromStaker() external onlyAdminAccess { claimDailyEmission(); BattleflyStaker.withdrawAll(); } function withdrawFromVault(address receiver, uint256 amount) external onlyAdminAccess { magic.safeTransfer(receiver, amount); } function _updateTotalStakingFounders(uint256 currentDay) private returns (uint256) { uint256 result = DailyFounderEmissions[DaysSinceStart].totalFounders - withdrawnOldFounder; withdrawnOldFounder = 0; uint256 to = startTimestamp + currentDay * 24 hours; uint256 i = unupdatedStakeIdFrom; for (; i <= lastStakeId; i++) { if (FounderStakes[i].stakeTimestamp == 0) { continue; } if (FounderStakes[i].stakeTimestamp > to) { break; } result += FounderStakes[i].amount; } unupdatedStakeIdFrom = i; return result; } //Must be called right after init function setFlywheelVault(address vault) external onlyOwner { require(vault != address(0)); battleflyFlywheelVault = IBattleflyFlywheelVault(vault); } function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; } modifier onlyAdminAccess() { require(adminAccess[_msgSender()] || _msgSender() == owner(), "Require admin access"); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/IAtlasMine.sol"; import "./interfaces/IBattleflyAtlasStakerV02.sol"; import "./interfaces/IVault.sol"; import "./libraries/BattleflyAtlasStakerUtils.sol"; import "./interfaces/vaults/IBattleflyTreasuryFlywheelVault.sol"; /// @custom:oz-upgrades-unsafe-allow external-library-linking contract BattleflyAtlasStakerV02 is IBattleflyAtlasStakerV02, Initializable, OwnableUpgradeable, ERC1155HolderUpgradeable, ERC721HolderUpgradeable, ReentrancyGuardUpgradeable { using AddressUpgradeable for address; using SafeCastUpgradeable for uint256; using SafeCastUpgradeable for int256; using SafeERC20Upgradeable for IERC20Upgradeable; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; // ========== CONSTANTS ========== uint96 public constant FEE_DENOMINATOR = 10000; uint256 public constant ONE = 1e28; address public BATTLEFLY_BOT; IERC20Upgradeable public override MAGIC; IAtlasMine public ATLAS_MINE; IBattleflyTreasuryFlywheelVault public TREASURY_VAULT; IAtlasMine.Lock[] public LOCKS; IAtlasMine.Lock[] public allowedLocks; // ========== Operator States ========== uint256 public override currentDepositId; uint64 public override currentEpoch; uint256 public pausedUntil; uint256 public nextExecution; /** * @dev active positionIds in AtlasMine for this contract. * */ EnumerableSetUpgradeable.UintSet private activePositionIds; /** * @dev Total emissions harvested from Atlas Mine for a particular lock period and epoch * { lock } => { epoch } => { emissions } */ mapping(IAtlasMine.Lock => mapping(uint64 => uint256)) private totalEmissionsAtEpochForLock; /** * @dev Total amount of magic staked in Atlas Mine for a particular lock period and epoch * { lock } => { epoch } => { magic } */ mapping(IAtlasMine.Lock => mapping(uint64 => uint256)) private totalStakedAtEpochForLock; /** * @dev Total amount of emissions per share in magic for a particular lock period and epoch * { lock } => { epoch } => { emissionsPerShare } */ mapping(IAtlasMine.Lock => mapping(uint64 => uint256)) private totalPerShareAtEpochForLock; /** * @dev Total amount of unstaked Magic at a particular epoch * { epoch } => { unstaked magic } */ mapping(uint64 => uint256) private unstakeAmountAtEpoch; /** * @dev Legion ERC721 NFT stakers data * { tokenId } => { depositor } */ mapping(uint256 => address) public legionStakers; /** * @dev TREASURE ERC1155 NFT stakers data * { tokenId } => { depositor } => { deposit amount } */ mapping(uint256 => mapping(address => uint256)) public treasureStakers; /** * @dev Vaultstakes per depositId * { depositId } => { VaultStake } */ mapping(uint256 => VaultStake) public vaultStakes; /** * @dev Vaults' all deposits * { address } => { depositId } */ mapping(address => EnumerableSetUpgradeable.UintSet) private depositIdByVault; /** * @dev Magic amount that is not staked to AtlasMine * { Lock } => { unstaked amount } */ mapping(IAtlasMine.Lock => uint256) public unstakedAmount; // ========== Access Control States ========== mapping(address => bool) public superAdmins; /** * @dev Whitelisted vaults * { vault address } => { Vault } */ mapping(address => Vault) public vaults; function initialize( address _magic, address _atlasMine, address _treasury, address _battleflyBot, IAtlasMine.Lock[] memory _allowedLocks ) external initializer { __ERC1155Holder_init(); __ERC721Holder_init(); __Ownable_init(); __ReentrancyGuard_init(); require(_magic != address(0), "BattleflyAtlasStaker: invalid address"); require(_atlasMine != address(0), "BattleflyAtlasStaker: invalid address"); MAGIC = IERC20Upgradeable(_magic); ATLAS_MINE = IAtlasMine(_atlasMine); superAdmins[msg.sender] = true; LOCKS = [ IAtlasMine.Lock.twoWeeks, IAtlasMine.Lock.oneMonth, IAtlasMine.Lock.threeMonths, IAtlasMine.Lock.sixMonths, IAtlasMine.Lock.twelveMonths ]; nextExecution = block.timestamp; setTreasury(_treasury); setBattleflyBot(_battleflyBot); setAllowedLocks(_allowedLocks); approveLegion(true); approveTreasure(true); } // ============================== Vault Operations ============================== /** * @dev deposit an amount of MAGIC in the AtlasStaker for a particular lock period */ function deposit(uint256 _amount, IAtlasMine.Lock _lock) external override onlyWhitelistedVaults nonReentrant whenNotPaused onlyAvailableLock(_lock) returns (uint256) { require(_amount > 0, "BattflyAtlasStaker: cannot deposit 0"); // Transfer MAGIC from Vault MAGIC.safeTransferFrom(msg.sender, address(this), _amount); uint256 newDepositId = ++currentDepositId; _deposit(newDepositId, _amount, _lock); return newDepositId; } /** * @dev withdraw a vaultstake from the AtlasStaker with a specific depositId */ function withdraw(uint256 _depositId) public override nonReentrant whenNotPaused returns (uint256 amount) { VaultStake memory vaultStake = vaultStakes[_depositId]; require(vaultStake.vault == msg.sender, "BattleflyAtlasStaker: caller is not a correct vault"); // withdraw can only happen if the retention period has passed. require(canWithdraw(_depositId), "BattleflyAtlasStaker: position is locked"); amount = vaultStake.amount; // Withdraw MAGIC to user MAGIC.safeTransfer(msg.sender, amount); // claim remaining emissions (uint256 emission, ) = getClaimableEmission(_depositId); if (emission > 0) { amount += _claim(_depositId); } // Reset vault stake data delete vaultStakes[_depositId]; depositIdByVault[msg.sender].remove(_depositId); emit WithdrawPosition(msg.sender, amount, _depositId); } /** * @dev Claim emissions from a vaultstake in the AtlasStaker with a specific depositId */ function claim(uint256 _depositId) public override nonReentrant whenNotPaused returns (uint256 amount) { amount = _claim(_depositId); } /** * @dev Request a withdrawal for a specific depositId. This is required because the vaultStake will be restaked in blocks of 2 weeks after the unlock period has passed. * This function is used to notify the AtlasStaker that it should not restake the vaultStake on the next iteration and the initial stake becomes unlocked. */ function requestWithdrawal(uint256 _depositId) public override nonReentrant whenNotPaused returns (uint64) { VaultStake storage vaultStake = vaultStakes[_depositId]; require(vaultStake.vault == msg.sender, "BattleflyAtlasStaker: caller is not a correct vault"); require(vaultStake.retentionUnlock == 0, "BattleflyAtlasStaker: withdrawal already requested"); // Every epoch is 1 day; We can start requesting for a withdrawal 14 days before the unlock period. require(currentEpoch >= (vaultStake.unlockAt - 14), "BattleflyAtlasStaker: position not yet unlockable"); // We set the retention period before the withdrawal can happen to the nearest epoch in the future uint64 retentionUnlock = currentEpoch < vaultStake.unlockAt ? vaultStake.unlockAt : currentEpoch + (14 - ((currentEpoch - vaultStake.unlockAt) % 14)); vaultStake.retentionUnlock = retentionUnlock - 1 == currentEpoch ? retentionUnlock + 14 : retentionUnlock; unstakeAmountAtEpoch[vaultStake.retentionUnlock - 1] += vaultStake.amount; emit RequestWithdrawal(msg.sender, vaultStake.retentionUnlock, _depositId); return vaultStake.retentionUnlock; } // ============================== Super Admin Operations ============================== /** * @dev Execute the daily cron job to deposit funds to AtlasMine & claim emissions from AtlasMine * The Battlefly CRON BOT will use this function to execute deposit/claim. */ function executeAll() external onlyBattleflyBot { require(block.timestamp >= nextExecution, "BattleflyAtlasStaker: Executed less than 24h ago"); // set to 24 hours - 5 minutes to take blockchain tx delays into account. nextExecution = block.timestamp + 86100; // Harvest all positions from AtlasMine _executeHarvestAll(); // Withdraw all positions from AtlasMine _executeWithdrawAll(); // Possibly correct the amount to be deposited due to users withdrawing their stake. _correctForUserWithdrawals(); // Stake all funds to AtlasMine _executeDepositAll(); } /** * @dev Approve TREASURE ERC1155 NFT transfer to deposit into AtlasMine contract */ function approveTreasure(bool _approve) public onlySuperAdmin { getTREASURE().setApprovalForAll(address(ATLAS_MINE), _approve); } /** * @dev Approve LEGION ERC721 NFT transfer to deposit into AtlasMine contract */ function approveLegion(bool _approve) public onlySuperAdmin { getLEGION().setApprovalForAll(address(ATLAS_MINE), _approve); } /** * @dev Stake TREASURE ERC1155 NFT */ function stakeTreasure(uint256 _tokenId, uint256 _amount) external onlySuperAdmin nonReentrant { require(_amount > 0, "BattleflyAtlasStaker: Invalid TREASURE amount"); // Caller's balance check already implemented in _safeTransferFrom() in ERC1155Upgradeable contract getTREASURE().safeTransferFrom(msg.sender, address(this), _tokenId, _amount, ""); treasureStakers[_tokenId][msg.sender] += _amount; // Token Approval is already done in constructor ATLAS_MINE.stakeTreasure(_tokenId, _amount); emit StakedTreasure(msg.sender, _tokenId, _amount); } /** * @dev Unstake TREASURE ERC1155 NFT */ function unstakeTreasure(uint256 _tokenId, uint256 _amount) external onlySuperAdmin nonReentrant { require(_amount > 0, "BattleflyAtlasStaker: Invalid TREASURE amount"); require(treasureStakers[_tokenId][msg.sender] >= _amount, "BattleflyAtlasStaker: Invalid TREASURE amount"); // Unstake TREASURE from AtlasMine ATLAS_MINE.unstakeTreasure(_tokenId, _amount); // Transfer TREASURE to the staker getTREASURE().safeTransferFrom(address(this), msg.sender, _tokenId, _amount, ""); treasureStakers[_tokenId][msg.sender] -= _amount; emit UnstakedTreasure(msg.sender, _tokenId, _amount); } /** * @dev Stake LEGION ERC721 NFT */ function stakeLegion(uint256 _tokenId) external onlySuperAdmin nonReentrant { // TokenId ownership validation is already implemented in safeTransferFrom function getLEGION().safeTransferFrom(msg.sender, address(this), _tokenId, ""); legionStakers[_tokenId] = msg.sender; // Token Approval is already done in constructor ATLAS_MINE.stakeLegion(_tokenId); emit StakedLegion(msg.sender, _tokenId); } /** * @dev Unstake LEGION ERC721 NFT */ function unstakeLegion(uint256 _tokenId) external onlySuperAdmin nonReentrant { require(legionStakers[_tokenId] == msg.sender, "BattleflyAtlasStaker: Invalid staker"); // Unstake LEGION from AtlasMine ATLAS_MINE.unstakeLegion(_tokenId); // Transfer LEGION to the staker getLEGION().safeTransferFrom(address(this), msg.sender, _tokenId, ""); legionStakers[_tokenId] = address(0); emit UnstakedLegion(msg.sender, _tokenId); } // ============================== Owner Operations ============================== /** * @dev Add super admin permission */ function addSuperAdmin(address _admin) public onlyOwner { require(!superAdmins[_admin], "BattleflyAtlasStaker: admin already exists"); superAdmins[_admin] = true; emit AddedSuperAdmin(_admin); } /** * @dev Batch adding super admin permission */ function addSuperAdmins(address[] calldata _admins) external onlyOwner { for (uint256 i = 0; i < _admins.length; i++) { addSuperAdmin(_admins[i]); } } /** * @dev Remove super admin permission */ function removeSuperAdmin(address _admin) public onlyOwner { require(superAdmins[_admin], "BattleflyAtlasStaker: admin does not exist"); superAdmins[_admin] = false; emit RemovedSuperAdmin(_admin); } /** * @dev Batch removing super admin permission */ function removeSuperAdmins(address[] calldata _admins) external onlyOwner { for (uint256 i = 0; i < _admins.length; i++) { removeSuperAdmin(_admins[i]); } } /** * @dev Add vault address */ function addVault(address _vault, Vault calldata _vaultData) public onlyOwner { require(!vaults[_vault].enabled, "BattleflyAtlasStaker: vault is already added"); require(_vaultData.fee + _vaultData.claimRate == FEE_DENOMINATOR, "BattleflyAtlasStaker: invalid vault info"); Vault storage vault = vaults[_vault]; vault.fee = _vaultData.fee; vault.claimRate = _vaultData.claimRate; vault.enabled = true; emit AddedVault(_vault, vault.fee, vault.claimRate); } /** * @dev Remove vault address */ function removeVault(address _vault) public onlyOwner { Vault storage vault = vaults[_vault]; require(vault.enabled, "BattleflyAtlasStaker: vault does not exist"); vault.enabled = false; emit RemovedVault(_vault); } /** * @dev Set allowed locks */ function setAllowedLocks(IAtlasMine.Lock[] memory _locks) public onlyOwner { allowedLocks = _locks; } /** * @dev Set treasury wallet address */ function setTreasury(address _treasury) public onlyOwner { require(_treasury != address(0), "BattleflyAtlasStaker: invalid address"); TREASURY_VAULT = IBattleflyTreasuryFlywheelVault(_treasury); emit SetTreasury(_treasury); } /** * @dev Set daily bot address */ function setBattleflyBot(address _battleflyBot) public onlyOwner { require(_battleflyBot != address(0), "BattleflyAtlasStaker: invalid address"); BATTLEFLY_BOT = _battleflyBot; emit SetBattleflyBot(_battleflyBot); } function setPause(bool _paused) external override onlyOwner { pausedUntil = _paused ? block.timestamp + 48 hours : 0; emit SetPause(_paused); } // ============================== VIEW ============================== /** * @dev Validate the lock period */ function isValidLock(IAtlasMine.Lock _lock) public view returns (bool) { for (uint256 i = 0; i < allowedLocks.length; i++) { if (allowedLocks[i] == _lock) { return true; } } return false; } /** * @dev Get AtlasMine TREASURE ERC1155 NFT address */ function getTREASURE() public view returns (IERC1155Upgradeable) { return IERC1155Upgradeable(ATLAS_MINE.treasure()); } /** * @dev Get AtlasMine LEGION ERC721 NFT address */ function getLEGION() public view returns (IERC721Upgradeable) { return IERC721Upgradeable(ATLAS_MINE.legion()); } /** * @dev Get Unstaked MAGIC amount */ function getUnstakedAmount() public view returns (uint256 amount) { IAtlasMine.Lock[] memory locks = LOCKS; for (uint256 i = 0; i < locks.length; i++) { amount += unstakedAmount[locks[i]]; } } /** * @dev Get claimable MAGIC emission. * Emissions are: * Emissions from normal lock period + * Emissions from retention period - * Already received emissions */ function getClaimableEmission(uint256 _depositId) public view override returns (uint256 emission, uint256 fee) { VaultStake memory vaultStake = vaultStakes[_depositId]; if (currentEpoch > 0) { uint64 retentionLock = vaultStake.retentionUnlock == 0 ? currentEpoch + 1 : vaultStake.retentionUnlock; uint64 x = vaultStake.retentionUnlock == 0 || vaultStake.retentionUnlock != vaultStake.unlockAt ? 0 : 1; emission = _getEmissionsForPeriod(vaultStake.amount, vaultStake.lockAt, vaultStake.unlockAt - x, vaultStake.lock) + _getEmissionsForPeriod(vaultStake.amount, vaultStake.unlockAt, retentionLock, vaultStake.lock) - vaultStake.paidEmission; } Vault memory vault = vaults[vaultStake.vault]; fee = (emission * vault.fee) / FEE_DENOMINATOR; emission -= fee; } /** * @dev Get staked amount */ function getDepositedAmount(uint256[] memory _depositIds) public view returns (uint256 amount) { for (uint256 i = 0; i < _depositIds.length; i++) { amount += vaultStakes[_depositIds[i]].amount; } } /** * @dev Get allowed locks */ function getAllowedLocks() public view override returns (IAtlasMine.Lock[] memory) { return allowedLocks; } /** * @dev Get vault staked data */ function getVaultStake(uint256 _depositId) public view override returns (VaultStake memory) { return vaultStakes[_depositId]; } /** * @dev Gets the lock period in epochs */ function getLockPeriod(IAtlasMine.Lock _lock) external view override returns (uint64 epoch) { return BattleflyAtlasStakerUtils.getLockPeriod(_lock, ATLAS_MINE) / 1 days; } /** * @dev Check if a vaultStake can be withdrawn */ function canWithdraw(uint256 _depositId) public view override returns (bool withdrawable) { VaultStake memory vaultStake = vaultStakes[_depositId]; withdrawable = (vaultStake.retentionUnlock > 0) && (vaultStake.retentionUnlock <= currentEpoch); } /** * @dev Check if a vaultStake can request a withdrawal */ function canRequestWithdrawal(uint256 _depositId) public view override returns (bool requestable) { VaultStake memory vaultStake = vaultStakes[_depositId]; requestable = (vaultStake.retentionUnlock == 0) && (currentEpoch >= (vaultStake.unlockAt - 14)); } /** * @dev Get the depositIds of a user */ function depositIdsOfVault(address vault) public view override returns (uint256[] memory depositIds) { return depositIdByVault[vault].values(); } // ============================== Internal ============================== /** * @dev recalculate and update the emissions per share per lock period and per epoch * 1 share is 1 wei of Magic. */ function _updateEmissionsForEpoch() private returns (uint256 totalEmission) { uint256[] memory positionIds = activePositionIds.values(); // Total emissions of the current epoch are at least as much as the total emissions of the previous epoch for (uint256 i = 0; i < LOCKS.length; i++) { totalEmissionsAtEpochForLock[LOCKS[i]][currentEpoch] = currentEpoch > 0 ? totalEmissionsAtEpochForLock[LOCKS[i]][currentEpoch - 1] : 0; } // Calculate the total amount of pending emissions and the total deposited amount for each lock period in the current epoch for (uint256 j = 0; j < positionIds.length; j++) { uint256 pendingRewards = ATLAS_MINE.pendingRewardsPosition(address(this), positionIds[j]); (, uint256 currentAmount, , , , , IAtlasMine.Lock _lock) = ATLAS_MINE.userInfo( address(this), positionIds[j] ); totalEmission += pendingRewards; totalEmissionsAtEpochForLock[_lock][currentEpoch] += pendingRewards; totalStakedAtEpochForLock[_lock][currentEpoch] += currentAmount; } // Calculate the accrued emissions per share by (totalEmission * 1e18) / totalStaked // Set the total emissions to the accrued emissions of the current epoch + the previous epochs for (uint256 k = 0; k < LOCKS.length; k++) { uint256 totalStaked = totalStakedAtEpochForLock[LOCKS[k]][currentEpoch]; if (totalStaked > 0) { uint256 accruedRewardsPerShare = (totalEmission * ONE) / totalStaked; totalPerShareAtEpochForLock[LOCKS[k]][currentEpoch] = currentEpoch > 0 ? totalPerShareAtEpochForLock[LOCKS[k]][currentEpoch - 1] + accruedRewardsPerShare : accruedRewardsPerShare; } else { totalPerShareAtEpochForLock[LOCKS[k]][currentEpoch] = currentEpoch > 0 ? totalPerShareAtEpochForLock[LOCKS[k]][currentEpoch - 1] : 0; } } } /** * @dev get the total amount of emissions of a certain period between two epochs. */ function _getEmissionsForPeriod( uint256 amount, uint64 startEpoch, uint64 stopEpoch, IAtlasMine.Lock lock ) private view returns (uint256 emissions) { if (stopEpoch >= startEpoch && currentEpoch >= startEpoch) { uint256 totalEmissions = (amount * totalPerShareAtEpochForLock[lock][currentEpoch - 1]); uint256 emissionsTillExclusion = (amount * totalPerShareAtEpochForLock[lock][stopEpoch - 1]); uint256 emissionsTillInclusion = (amount * totalPerShareAtEpochForLock[lock][startEpoch - 1]); uint256 emissionsFromExclusion = emissionsTillExclusion > 0 ? (totalEmissions - emissionsTillExclusion) : 0; emissions = (totalEmissions - emissionsFromExclusion - emissionsTillInclusion) / ONE; } } /** * @dev Deposit MAGIC to AtlasMine */ function _deposit( uint256 _depositId, uint256 _amount, IAtlasMine.Lock _lock ) private returns (uint256) { // We only deposit to AtlasMine in the next epoch. We can unlock after the lock period has passed. uint64 lockAt = currentEpoch + 1; uint64 unlockAt = currentEpoch + 1 + (BattleflyAtlasStakerUtils.getLockPeriod(_lock, ATLAS_MINE) / 1 days); vaultStakes[_depositId] = VaultStake(lockAt, unlockAt, 0, _amount, 0, msg.sender, _lock); // Updated unstaked MAGIC amount unstakedAmount[_lock] += _amount; depositIdByVault[msg.sender].add(_depositId); emit NewDeposit(msg.sender, _amount, unlockAt, _depositId); return unlockAt; } /** * @dev Claim emissions for a depositId */ function _claim(uint256 _depositId) internal returns (uint256) { VaultStake storage vaultStake = vaultStakes[_depositId]; require(vaultStake.vault == msg.sender, "BattleflyAtlasStaker: caller is not a correct vault"); (uint256 emission, uint256 fee) = getClaimableEmission(_depositId); if (emission > 0) { MAGIC.safeTransfer(msg.sender, emission); if (fee > 0) { MAGIC.approve(address(TREASURY_VAULT), fee); TREASURY_VAULT.topupMagic(fee); } uint256 amount = emission + fee; vaultStake.paidEmission += amount; emit ClaimEmission(msg.sender, emission, _depositId); } return emission; } /** * @dev Execute the daily cron job to harvest all emissions from AtlasMine */ function _executeHarvestAll() internal { uint256 pendingHarvest = _updateEmissionsForEpoch(); uint256 preHarvest = MAGIC.balanceOf(address(this)); for (uint64 i = 0; i < activePositionIds.length(); i++) { ATLAS_MINE.harvestPosition(activePositionIds.at(i)); } uint256 harvested = MAGIC.balanceOf(address(this)) - preHarvest; require(pendingHarvest == harvested, "BattleflyAtlasStaker: pending harvest and actual harvest are not equal"); // Increment the epoch currentEpoch++; } /** * @dev Possibly correct the amount to be deposited due to users withdrawing their stake. */ function _correctForUserWithdrawals() internal { if (unstakedAmount[IAtlasMine.Lock.twoWeeks] >= unstakeAmountAtEpoch[currentEpoch]) { unstakedAmount[IAtlasMine.Lock.twoWeeks] -= unstakeAmountAtEpoch[currentEpoch]; } else { //If not enough withdrawals available from current epoch, request more from the next epoch unstakeAmountAtEpoch[currentEpoch + 1] += (unstakeAmountAtEpoch[currentEpoch] - unstakedAmount[IAtlasMine.Lock.twoWeeks]); unstakedAmount[IAtlasMine.Lock.twoWeeks] = 0; } } /** * @dev Execute the daily cron job to deposit all to AtlasMine */ function _executeDepositAll() internal { uint256 unstaked; for (uint256 i = 0; i < LOCKS.length; i++) { uint256 amount = unstakedAmount[LOCKS[i]]; if (amount > 0) { unstaked += amount; MAGIC.safeApprove(address(ATLAS_MINE), amount); ATLAS_MINE.deposit(amount, LOCKS[i]); activePositionIds.add(ATLAS_MINE.currentId(address(this))); unstakedAmount[LOCKS[i]] = 0; } } emit DepositedAllToMine(unstaked); } /** * @dev Execute the daily cron job to withdraw all positions from AtlasMine */ function _executeWithdrawAll() internal { uint256[] memory depositIds = activePositionIds.values(); for (uint256 i = 0; i < depositIds.length; i++) { (uint256 amount, , , uint256 lockedUntil, , , IAtlasMine.Lock lock) = ATLAS_MINE.userInfo( address(this), depositIds[i] ); uint256 totalLockedPeriod = lockedUntil + ATLAS_MINE.getVestingTime(lock); // If the position is available to withdraw if (totalLockedPeriod <= block.timestamp) { ATLAS_MINE.withdrawPosition(depositIds[i], type(uint256).max); activePositionIds.remove(depositIds[i]); // Directly register for restaking, unless a withdrawal is requested (we correct this in _correctForUserWithdrawals()) unstakedAmount[IAtlasMine.Lock.twoWeeks] += uint256(amount); } } } // ============================== Modifiers ============================== modifier onlySuperAdmin() { require(superAdmins[msg.sender], "BattleflyAtlasStaker: caller is not a super admin"); _; } modifier onlyWhitelistedVaults() { require(vaults[msg.sender].enabled, "BattleflyAtlasStaker: caller is not whitelisted"); _; } modifier onlyAvailableLock(IAtlasMine.Lock _lock) { require(isValidLock(_lock), "BattleflyAtlasStaker: invalid lock period"); _; } modifier onlyBattleflyBot() { require(msg.sender == BATTLEFLY_BOT, "BattleflyAtlasStaker: caller is not a battlefly bot"); _; } modifier whenNotPaused() { require(block.timestamp > pausedUntil, "BattleflyAtlasStaker: contract paused"); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/IAtlasMine.sol"; library BattleflyAtlasStakerUtils { /** * @dev Get lock period * Need to consider about adding 1 more day to lock period regarding the daily cron job */ function getLockPeriod(IAtlasMine.Lock _lock, IAtlasMine ATLAS_MINE) external pure returns (uint64) { if (_lock == IAtlasMine.Lock.twoWeeks) { return 14 days + 1 days + uint64(ATLAS_MINE.getVestingTime(_lock)); } if (_lock == IAtlasMine.Lock.oneMonth) { return 30 days + 1 days + uint64(ATLAS_MINE.getVestingTime(_lock)); } if (_lock == IAtlasMine.Lock.threeMonths) { return 90 days + 1 days + uint64(ATLAS_MINE.getVestingTime(_lock)); } if (_lock == IAtlasMine.Lock.sixMonths) { return 180 days + 1 days + uint64(ATLAS_MINE.getVestingTime(_lock)); } if (_lock == IAtlasMine.Lock.twelveMonths) { return 365 days + 1 days + uint64(ATLAS_MINE.getVestingTime(_lock)); } revert("BattleflyAtlasStaker: Invalid Lock"); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/cryptography/MerkleProofUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "./BattleflyFounderVaultV08.sol"; import "./interfaces/IBattleflyAtlasStakerV02.sol"; import "./interfaces/IBattleflyFlywheelVaultV02.sol"; import "./interfaces/IAtlasMine.sol"; contract BattleflyFlywheelVaultV02 is IBattleflyFlywheelVaultV02, Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; /** * @dev Immutable states */ IERC20Upgradeable public MAGIC; IBattleflyAtlasStakerV02 public ATLAS_STAKER; string public name; /** * @dev User stake data * { depositId } => { User stake data } */ mapping(uint256 => UserStake) public userStakes; /** * @dev User's depositIds * { user } => { depositIds } */ mapping(address => EnumerableSetUpgradeable.UintSet) private depositIdByUser; /** * @dev Whitelisted users * { user } => { is whitelisted } */ mapping(address => bool) public whitelistedUsers; function initialize(address _atlasStaker, string memory _name) external initializer { __Ownable_init(); __ReentrancyGuard_init(); require(_atlasStaker != address(0), "BattleflyFlywheelVault: invalid address"); require(bytes(_name).length > 0, "BattleflyFlywheelVault: invalid name"); ATLAS_STAKER = IBattleflyAtlasStakerV02(_atlasStaker); MAGIC = ATLAS_STAKER.MAGIC(); name = _name; } /** * @dev Deposit funds to AtlasStaker */ function deposit(uint128 _amount, IAtlasMine.Lock _lock) external override nonReentrant onlyMembers returns (uint256 atlasStakerDepositId) { MAGIC.safeTransferFrom(msg.sender, address(this), _amount); MAGIC.safeApprove(address(ATLAS_STAKER), _amount); atlasStakerDepositId = ATLAS_STAKER.deposit(uint256(_amount), _lock); IBattleflyAtlasStakerV02.VaultStake memory vaultStake = ATLAS_STAKER.getVaultStake(atlasStakerDepositId); UserStake storage userStake = userStakes[atlasStakerDepositId]; userStake.amount = _amount; userStake.lockAt = vaultStake.lockAt; userStake.owner = msg.sender; userStake.lock = _lock; depositIdByUser[msg.sender].add(atlasStakerDepositId); emit NewUserStake(atlasStakerDepositId, _amount, vaultStake.unlockAt, msg.sender, _lock); } /** * @dev Withdraw staked funds from AtlasStaker */ function withdraw(uint256[] calldata _depositIds) public override nonReentrant returns (uint256 amount) { for (uint256 i = 0; i < _depositIds.length; i++) { amount += _withdraw(_depositIds[i]); } } /** * @dev Withdraw all from AtlasStaker. This is only possible when the retention period of 14 epochs has passed. * The retention period is started when a withdrawal for the stake is requested. */ function withdrawAll() public override nonReentrant returns (uint256 amount) { uint256[] memory depositIds = depositIdByUser[msg.sender].values(); require(depositIds.length > 0, "BattleflyFlywheelVault: No deposited funds"); for (uint256 i = 0; i < depositIds.length; i++) { if (ATLAS_STAKER.canWithdraw(depositIds[i])) { amount += _withdraw(depositIds[i]); } } } /** * @dev Request a withdrawal from AtlasStaker. This works with a retention period of 14 epochs. * Once the retention period has passed, the stake can be withdrawn. */ function requestWithdrawal(uint256[] calldata _depositIds) public override { for (uint256 i = 0; i < _depositIds.length; i++) { UserStake memory userStake = userStakes[_depositIds[i]]; require(userStake.owner == msg.sender, "BattleflyFlywheelVault: caller is not the owner"); ATLAS_STAKER.requestWithdrawal(_depositIds[i]); emit RequestWithdrawal(_depositIds[i]); } } /** * @dev Claim emission from AtlasStaker */ function claim(uint256 _depositId) public override nonReentrant returns (uint256 emission) { emission = _claim(_depositId); } /** * @dev Claim all emissions from AtlasStaker */ function claimAll() external override nonReentrant returns (uint256 amount) { uint256[] memory depositIds = depositIdByUser[msg.sender].values(); require(depositIds.length > 0, "BattleflyFlywheelVault: No deposited funds"); for (uint256 i = 0; i < depositIds.length; i++) { amount += _claim(depositIds[i]); } } /** * @dev Whitelist user */ function whitelistUser(address _who) public onlyOwner { require(!whitelistedUsers[_who], "BattlefalyWheelVault: Already whitelisted"); whitelistedUsers[_who] = true; emit AddedUser(_who); } /** * @dev Whitelist users */ function whitelistUsers(address[] calldata _users) external onlyOwner { for (uint256 i = 0; i < _users.length; i++) { whitelistUser(_users[i]); } } /** * @dev Remove user from whitelist */ function removeUser(address _who) public onlyOwner { require(whitelistedUsers[_who], "BattlefalyWheelVault: Not whitelisted yet"); whitelistedUsers[_who] = false; emit RemovedUser(_who); } /** * @dev Remove users from whitelist */ function removeUsers(address[] calldata _users) external onlyOwner { for (uint256 i = 0; i < _users.length; i++) { removeUser(_users[i]); } } /** * @dev Set the name of the vault */ function setName(string memory _name) public onlyOwner { name = _name; } // ================ INTERNAL ================ /** * @dev Withdraw a stake from AtlasStaker (Only possible when the retention period has passed) */ function _withdraw(uint256 _depositId) internal returns (uint256 amount) { UserStake memory userStake = userStakes[_depositId]; require(userStake.owner == msg.sender, "BattleflyFlywheelVault: caller is not the owner"); require(ATLAS_STAKER.canWithdraw(_depositId), "BattleflyFlywheelVault: stake not yet unlocked"); amount = ATLAS_STAKER.withdraw(_depositId); MAGIC.safeTransfer(msg.sender, amount); depositIdByUser[msg.sender].remove(_depositId); delete userStakes[_depositId]; emit WithdrawPosition(_depositId, amount); } /** * @dev Claim emission from AtlasStaker */ function _claim(uint256 _depositId) internal returns (uint256 emission) { UserStake memory userStake = userStakes[_depositId]; require(userStake.owner == msg.sender, "BattleflyFlywheelVault: caller is not the owner"); emission = ATLAS_STAKER.claim(_depositId); MAGIC.safeTransfer(msg.sender, emission); emit ClaimEmission(_depositId, emission); } // ================== VIEW ================== /** * @dev Get allowed lock periods from AtlasStaker */ function getAllowedLocks() public view override returns (IAtlasMine.Lock[] memory) { return ATLAS_STAKER.getAllowedLocks(); } /** * @dev Get claimed emission */ function getClaimableEmission(uint256 _depositId) public view override returns (uint256 emission) { (emission, ) = ATLAS_STAKER.getClaimableEmission(_depositId); } /** * @dev Check if a vaultStake is eligible for requesting a withdrawal. * This is 14 epochs before the end of the initial lock period. */ function canRequestWithdrawal(uint256 _depositId) public view override returns (bool requestable) { return ATLAS_STAKER.canRequestWithdrawal(_depositId); } /** * @dev Check if a vaultStake is eligible for a withdrawal * This is when the retention period has passed */ function canWithdraw(uint256 _depositId) public view override returns (bool withdrawable) { return ATLAS_STAKER.canWithdraw(_depositId); } /** * @dev Check the epoch in which the initial lock period of the vaultStake expires. * This is at the end of the lock period */ function initialUnlock(uint256 _depositId) public view override returns (uint64 epoch) { return ATLAS_STAKER.getVaultStake(_depositId).unlockAt; } /** * @dev Check the epoch in which the retention period of the vaultStake expires. * This is 14 epochs after the withdrawal request has taken place */ function retentionUnlock(uint256 _depositId) public view override returns (uint64 epoch) { return ATLAS_STAKER.getVaultStake(_depositId).retentionUnlock; } /** * @dev Get the currently active epoch */ function getCurrentEpoch() public view override returns (uint64 epoch) { return ATLAS_STAKER.currentEpoch(); } /** * @dev Get the depositIds of a user */ function depositIdsOfUser(address user) public view override returns (uint256[] memory depositIds) { return depositIdByUser[user].values(); } /** * @dev Return the name of the vault */ function getName() public view override returns (string memory) { return name; } // ================== MODIFIERS ================== modifier onlyMembers() { require(whitelistedUsers[msg.sender], "BattleflyWheelVault: caller is not a whitelisted member"); _; } }
// SPDX-License-Identifier: MIT pragma solidity >0.8.0; import "./IAtlasMine.sol"; interface IBattleflyFlywheelVaultV02 { struct UserStake { uint64 lockAt; uint256 amount; address owner; IAtlasMine.Lock lock; } function deposit(uint128, IAtlasMine.Lock) external returns (uint256); function withdraw(uint256[] calldata _depositIds) external returns (uint256); function withdrawAll() external returns (uint256); function requestWithdrawal(uint256[] calldata _depositIds) external; function claim(uint256) external returns (uint256); function claimAll() external returns (uint256); function getAllowedLocks() external view returns (IAtlasMine.Lock[] memory); function getClaimableEmission(uint256) external view returns (uint256); function canRequestWithdrawal(uint256 _depositId) external view returns (bool requestable); function canWithdraw(uint256 _depositId) external view returns (bool withdrawable); function initialUnlock(uint256 _depositId) external view returns (uint64 epoch); function retentionUnlock(uint256 _depositId) external view returns (uint64 epoch); function getCurrentEpoch() external view returns (uint64 epoch); function depositIdsOfUser(address user) external view returns (uint256[] memory depositIds); function getName() external view returns (string memory); // ================== EVENTS ================== event NewUserStake( uint256 indexed depositId, uint256 amount, uint256 unlockAt, address indexed owner, IAtlasMine.Lock lock ); event UpdateUserStake( uint256 indexed depositId, uint256 amount, uint256 unlockAt, address indexed owner, IAtlasMine.Lock lock ); event ClaimEmission(uint256 indexed depositId, uint256 emission); event WithdrawPosition(uint256 indexed depositId, uint256 amount); event RequestWithdrawal(uint256 indexed depositId); event AddedUser(address indexed vault); event RemovedUser(address indexed vault); }
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; contract HyperdomeContract is ERC721Upgradeable, ERC721EnumerableUpgradeable, OwnableUpgradeable { using SafeMathUpgradeable for uint256; using AddressUpgradeable for address; using CountersUpgradeable for CountersUpgradeable.Counter; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; CountersUpgradeable.Counter private _tokenIdCounter; mapping(address => bool) private adminAccess; function initialize() public initializer { __ERC721_init("HyperdomeLand", "Hyperdome"); __Ownable_init(); } function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; } function mintHyperdome(address receiver) external onlyAdminAccess returns (uint256) { uint256 nextTokenId = _getNextTokenId(); _mint(receiver, nextTokenId); return nextTokenId; } function _getNextTokenId() private view returns (uint256) { return (_tokenIdCounter.current()); } function _mint(address to, uint256 tokenId) internal override(ERC721Upgradeable) { super._mint(to, tokenId); _tokenIdCounter.increment(); } function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) { super._beforeTokenTransfer(from, to, tokenId); } function supportsInterface(bytes4 interfaceId) public view override(ERC721Upgradeable, ERC721EnumerableUpgradeable) returns (bool) { return super.supportsInterface(interfaceId); } modifier onlyAdminAccess() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol) pragma solidity ^0.8.0; import "../ERC721Upgradeable.sol"; import "./IERC721EnumerableUpgradeable.sol"; import "../../../proxy/utils/Initializable.sol"; /** * @dev This implements an optional extension of {ERC721} defined in the EIP that adds * enumerability of all the token ids in the contract as well as all token ids owned by each * account. */ abstract contract ERC721EnumerableUpgradeable is Initializable, ERC721Upgradeable, IERC721EnumerableUpgradeable { function __ERC721Enumerable_init() internal onlyInitializing { } function __ERC721Enumerable_init_unchained() internal onlyInitializing { } // Mapping from owner to list of owned token IDs mapping(address => mapping(uint256 => uint256)) private _ownedTokens; // Mapping from token ID to index of the owner tokens list mapping(uint256 => uint256) private _ownedTokensIndex; // Array with all token ids, used for enumeration uint256[] private _allTokens; // Mapping from token id to position in the allTokens array mapping(uint256 => uint256) private _allTokensIndex; /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC721Upgradeable) returns (bool) { return interfaceId == type(IERC721EnumerableUpgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. */ function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) { require(index < ERC721Upgradeable.balanceOf(owner), "ERC721Enumerable: owner index out of bounds"); return _ownedTokens[owner][index]; } /** * @dev See {IERC721Enumerable-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _allTokens.length; } /** * @dev See {IERC721Enumerable-tokenByIndex}. */ function tokenByIndex(uint256 index) public view virtual override returns (uint256) { require(index < ERC721EnumerableUpgradeable.totalSupply(), "ERC721Enumerable: global index out of bounds"); return _allTokens[index]; } /** * @dev Hook that is called before any token transfer. This includes minting * and burning. * * Calling conditions: * * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be * transferred to `to`. * - When `from` is zero, `tokenId` will be minted for `to`. * - When `to` is zero, ``from``'s `tokenId` will be burned. * - `from` cannot be the zero address. * - `to` cannot be the zero address. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal virtual override { super._beforeTokenTransfer(from, to, tokenId); if (from == address(0)) { _addTokenToAllTokensEnumeration(tokenId); } else if (from != to) { _removeTokenFromOwnerEnumeration(from, tokenId); } if (to == address(0)) { _removeTokenFromAllTokensEnumeration(tokenId); } else if (to != from) { _addTokenToOwnerEnumeration(to, tokenId); } } /** * @dev Private function to add a token to this extension's ownership-tracking data structures. * @param to address representing the new owner of the given token ID * @param tokenId uint256 ID of the token to be added to the tokens list of the given address */ function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { uint256 length = ERC721Upgradeable.balanceOf(to); _ownedTokens[to][length] = tokenId; _ownedTokensIndex[tokenId] = length; } /** * @dev Private function to add a token to this extension's token tracking data structures. * @param tokenId uint256 ID of the token to be added to the tokens list */ function _addTokenToAllTokensEnumeration(uint256 tokenId) private { _allTokensIndex[tokenId] = _allTokens.length; _allTokens.push(tokenId); } /** * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for * gas optimizations e.g. when performing a transfer operation (avoiding double writes). * This has O(1) time complexity, but alters the order of the _ownedTokens array. * @param from address representing the previous owner of the given token ID * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address */ function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). uint256 lastTokenIndex = ERC721Upgradeable.balanceOf(from) - 1; uint256 tokenIndex = _ownedTokensIndex[tokenId]; // When the token to delete is the last token, the swap operation is unnecessary if (tokenIndex != lastTokenIndex) { uint256 lastTokenId = _ownedTokens[from][lastTokenIndex]; _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index } // This also deletes the contents at the last position of the array delete _ownedTokensIndex[tokenId]; delete _ownedTokens[from][lastTokenIndex]; } /** * @dev Private function to remove a token from this extension's token tracking data structures. * This has O(1) time complexity, but alters the order of the _allTokens array. * @param tokenId uint256 ID of the token to be removed from the tokens list */ function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). uint256 lastTokenIndex = _allTokens.length - 1; uint256 tokenIndex = _allTokensIndex[tokenId]; // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding // an 'if' statement (like in _removeTokenFromOwnerEnumeration) uint256 lastTokenId = _allTokens[lastTokenIndex]; _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index // This also deletes the contents at the last position of the array delete _allTokensIndex[tokenId]; _allTokens.pop(); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[46] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) pragma solidity ^0.8.0; /** * @title Counters * @author Matt Condon (@shrugs) * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number * of elements in a mapping, issuing ERC721 ids, or counting request ids. * * Include with `using Counters for Counters.Counter;` */ library CountersUpgradeable { struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { unchecked { counter._value += 1; } } function decrement(Counter storage counter) internal { uint256 value = counter._value; require(value > 0, "Counter: decrement overflow"); unchecked { counter._value = value - 1; } } function reset(Counter storage counter) internal { counter._value = 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/ERC721.sol) pragma solidity ^0.8.0; import "./IERC721Upgradeable.sol"; import "./IERC721ReceiverUpgradeable.sol"; import "./extensions/IERC721MetadataUpgradeable.sol"; import "../../utils/AddressUpgradeable.sol"; import "../../utils/ContextUpgradeable.sol"; import "../../utils/StringsUpgradeable.sol"; import "../../utils/introspection/ERC165Upgradeable.sol"; import "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including * the Metadata extension, but not including the Enumerable extension, which is available separately as * {ERC721Enumerable}. */ contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable { using AddressUpgradeable for address; using StringsUpgradeable for uint256; // Token name string private _name; // Token symbol string private _symbol; // Mapping from token ID to owner address mapping(uint256 => address) private _owners; // Mapping owner address to token count mapping(address => uint256) private _balances; // Mapping from token ID to approved address mapping(uint256 => address) private _tokenApprovals; // Mapping from owner to operator approvals mapping(address => mapping(address => bool)) private _operatorApprovals; /** * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. */ function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing { __ERC721_init_unchained(name_, symbol_); } function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing { _name = name_; _symbol = symbol_; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) { return interfaceId == type(IERC721Upgradeable).interfaceId || interfaceId == type(IERC721MetadataUpgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC721-balanceOf}. */ function balanceOf(address owner) public view virtual override returns (uint256) { require(owner != address(0), "ERC721: balance query for the zero address"); return _balances[owner]; } /** * @dev See {IERC721-ownerOf}. */ function ownerOf(uint256 tokenId) public view virtual override returns (address) { address owner = _owners[tokenId]; require(owner != address(0), "ERC721: owner query for nonexistent token"); return owner; } /** * @dev See {IERC721Metadata-name}. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev See {IERC721Metadata-symbol}. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); string memory baseURI = _baseURI(); return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; } /** * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each * token will be the concatenation of the `baseURI` and the `tokenId`. Empty * by default, can be overridden in child contracts. */ function _baseURI() internal view virtual returns (string memory) { return ""; } /** * @dev See {IERC721-approve}. */ function approve(address to, uint256 tokenId) public virtual override { address owner = ERC721Upgradeable.ownerOf(tokenId); require(to != owner, "ERC721: approval to current owner"); require( _msgSender() == owner || isApprovedForAll(owner, _msgSender()), "ERC721: approve caller is not owner nor approved for all" ); _approve(to, tokenId); } /** * @dev See {IERC721-getApproved}. */ function getApproved(uint256 tokenId) public view virtual override returns (address) { require(_exists(tokenId), "ERC721: approved query for nonexistent token"); return _tokenApprovals[tokenId]; } /** * @dev See {IERC721-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual override { _setApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC721-isApprovedForAll}. */ function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { return _operatorApprovals[owner][operator]; } /** * @dev See {IERC721-transferFrom}. */ function transferFrom( address from, address to, uint256 tokenId ) public virtual override { //solhint-disable-next-line max-line-length require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _transfer(from, to, tokenId); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 tokenId ) public virtual override { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes memory _data ) public virtual override { require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _safeTransfer(from, to, tokenId, _data); } /** * @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. * * `_data` is additional data, it has no specified format and it is sent in call to `to`. * * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. * implement alternative mechanisms to perform token transfer, such as signature-based. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeTransfer( address from, address to, uint256 tokenId, bytes memory _data ) internal virtual { _transfer(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } /** * @dev Returns whether `tokenId` exists. * * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. * * Tokens start existing when they are minted (`_mint`), * and stop existing when they are burned (`_burn`). */ function _exists(uint256 tokenId) internal view virtual returns (bool) { return _owners[tokenId] != address(0); } /** * @dev Returns whether `spender` is allowed to manage `tokenId`. * * Requirements: * * - `tokenId` must exist. */ function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { require(_exists(tokenId), "ERC721: operator query for nonexistent token"); address owner = ERC721Upgradeable.ownerOf(tokenId); return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender); } /** * @dev Safely mints `tokenId` and transfers it to `to`. * * Requirements: * * - `tokenId` must not exist. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeMint(address to, uint256 tokenId) internal virtual { _safeMint(to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeMint( address to, uint256 tokenId, bytes memory _data ) internal virtual { _mint(to, tokenId); require( _checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer" ); } /** * @dev Mints `tokenId` and transfers it to `to`. * * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible * * Requirements: * * - `tokenId` must not exist. * - `to` cannot be the zero address. * * Emits a {Transfer} event. */ function _mint(address to, uint256 tokenId) internal virtual { require(to != address(0), "ERC721: mint to the zero address"); require(!_exists(tokenId), "ERC721: token already minted"); _beforeTokenTransfer(address(0), to, tokenId); _balances[to] += 1; _owners[tokenId] = to; emit Transfer(address(0), to, tokenId); _afterTokenTransfer(address(0), to, tokenId); } /** * @dev Destroys `tokenId`. * The approval is cleared when the token is burned. * * Requirements: * * - `tokenId` must exist. * * Emits a {Transfer} event. */ function _burn(uint256 tokenId) internal virtual { address owner = ERC721Upgradeable.ownerOf(tokenId); _beforeTokenTransfer(owner, address(0), tokenId); // Clear approvals _approve(address(0), tokenId); _balances[owner] -= 1; delete _owners[tokenId]; emit Transfer(owner, address(0), tokenId); _afterTokenTransfer(owner, address(0), tokenId); } /** * @dev Transfers `tokenId` from `from` to `to`. * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. * * Requirements: * * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * * Emits a {Transfer} event. */ function _transfer( address from, address to, uint256 tokenId ) internal virtual { require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); require(to != address(0), "ERC721: transfer to the zero address"); _beforeTokenTransfer(from, to, tokenId); // Clear approvals from the previous owner _approve(address(0), tokenId); _balances[from] -= 1; _balances[to] += 1; _owners[tokenId] = to; emit Transfer(from, to, tokenId); _afterTokenTransfer(from, to, tokenId); } /** * @dev Approve `to` to operate on `tokenId` * * Emits a {Approval} event. */ function _approve(address to, uint256 tokenId) internal virtual { _tokenApprovals[tokenId] = to; emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId); } /** * @dev Approve `operator` to operate on all of `owner` tokens * * Emits a {ApprovalForAll} event. */ function _setApprovalForAll( address owner, address operator, bool approved ) internal virtual { require(owner != operator, "ERC721: approve to caller"); _operatorApprovals[owner][operator] = approved; emit ApprovalForAll(owner, operator, approved); } /** * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. * The call is not executed if the target address is not a contract. * * @param from address representing the previous owner of the given token ID * @param to target address that will receive the tokens * @param tokenId uint256 ID of the token to be transferred * @param _data bytes optional data to send along with the call * @return bool whether the call correctly returned the expected magic value */ function _checkOnERC721Received( address from, address to, uint256 tokenId, bytes memory _data ) private returns (bool) { if (to.isContract()) { try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) { return retval == IERC721ReceiverUpgradeable.onERC721Received.selector; } catch (bytes memory reason) { if (reason.length == 0) { revert("ERC721: transfer to non ERC721Receiver implementer"); } else { assembly { revert(add(32, reason), mload(reason)) } } } } else { return true; } } /** * @dev Hook that is called before any token transfer. This includes minting * and burning. * * Calling conditions: * * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be * transferred to `to`. * - When `from` is zero, `tokenId` will be minted for `to`. * - When `to` is zero, ``from``'s `tokenId` will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 tokenId ) internal virtual {} /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[44] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol) pragma solidity ^0.8.0; import "../IERC721Upgradeable.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721MetadataUpgradeable is IERC721Upgradeable { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; contract SpecialNFTContract is ERC721EnumerableUpgradeable, OwnableUpgradeable { using SafeMathUpgradeable for uint256; using AddressUpgradeable for address; using CountersUpgradeable for CountersUpgradeable.Counter; using StringsUpgradeable for uint256; mapping(address => bool) private adminAccess; mapping(uint256 => uint256) private specialNFTTypes; CountersUpgradeable.Counter private _tokenIdCounter; event SetAdminAccess(address indexed user, bool access); function initialize() public initializer { __ERC721Enumerable_init(); __ERC721_init("Battlefly Special NFTs", "BattleflySNFT"); __Ownable_init(); } function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; emit SetAdminAccess(user, access); } function mintSpecialNFTs( address receiver, uint256 _specialNFTType, uint256 amount ) external onlyAdminAccess returns (uint256[] memory) { uint256[] memory tokenIds = new uint256[](amount); for (uint256 i = 0; i < amount; i++) { uint256 nextTokenId = _getNextTokenId(); _mint(receiver, nextTokenId); specialNFTTypes[nextTokenId] = _specialNFTType; tokenIds[i] = nextTokenId; } return tokenIds; } function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); return string(abi.encodePacked("https://api.battlefly.game/specials/", tokenId.toString(), "/metadata")); } function mintSpecialNFT(address receiver, uint256 specialNFTType) external onlyAdminAccess returns (uint256) { uint256 nextTokenId = _getNextTokenId(); _mint(receiver, nextTokenId); specialNFTTypes[nextTokenId] = specialNFTType; return nextTokenId; } function getSpecialNFTType(uint256 tokenId) external view returns (uint256) { return specialNFTTypes[tokenId]; } function _getNextTokenId() private view returns (uint256) { return (_tokenIdCounter.current() + 1); } function _mint(address to, uint256 tokenId) internal override(ERC721Upgradeable) { super._mint(to, tokenId); _tokenIdCounter.increment(); } modifier onlyAdminAccess() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } }
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; contract ModContract is ERC721EnumerableUpgradeable, OwnableUpgradeable { using SafeMathUpgradeable for uint256; using AddressUpgradeable for address; using CountersUpgradeable for CountersUpgradeable.Counter; mapping(address => bool) private adminAccess; CountersUpgradeable.Counter private _tokenIdCounter; mapping(uint256 => Mod) private mods; struct Mod { uint256 modId; uint256 item; uint256 mountType; } function initialize() public initializer { __ERC721Enumerable_init(); __ERC721_init("Mod", "Mod"); __Ownable_init(); } function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; } function mintMod(address receiver, Mod memory mod) external onlyAdminAccess returns (uint256) { uint256 nextTokenId = _getNextTokenId(); _mint(receiver, nextTokenId); mod.modId = nextTokenId; mods[nextTokenId] = mod; return nextTokenId; } function _getNextTokenId() private view returns (uint256) { return (_tokenIdCounter.current() + 1); } function _mint(address to, uint256 tokenId) internal override(ERC721Upgradeable) { super._mint(to, tokenId); _tokenIdCounter.increment(); } modifier onlyAdminAccess() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal override(ERC721EnumerableUpgradeable) { super._beforeTokenTransfer(from, to, tokenId); } function supportsInterface(bytes4 interfaceId) public view override(ERC721EnumerableUpgradeable) returns (bool) { return super.supportsInterface(interfaceId); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/ERC1155.sol) pragma solidity ^0.8.0; import "./IERC1155Upgradeable.sol"; import "./IERC1155ReceiverUpgradeable.sol"; import "./extensions/IERC1155MetadataURIUpgradeable.sol"; import "../../utils/AddressUpgradeable.sol"; import "../../utils/ContextUpgradeable.sol"; import "../../utils/introspection/ERC165Upgradeable.sol"; import "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the basic standard multi-token. * See https://eips.ethereum.org/EIPS/eip-1155 * Originally based on code by Enjin: https://github.com/enjin/erc-1155 * * _Available since v3.1._ */ contract ERC1155Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC1155Upgradeable, IERC1155MetadataURIUpgradeable { using AddressUpgradeable for address; // Mapping from token ID to account balances mapping(uint256 => mapping(address => uint256)) private _balances; // Mapping from account to operator approvals mapping(address => mapping(address => bool)) private _operatorApprovals; // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json string private _uri; /** * @dev See {_setURI}. */ function __ERC1155_init(string memory uri_) internal onlyInitializing { __ERC1155_init_unchained(uri_); } function __ERC1155_init_unchained(string memory uri_) internal onlyInitializing { _setURI(uri_); } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) { return interfaceId == type(IERC1155Upgradeable).interfaceId || interfaceId == type(IERC1155MetadataURIUpgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC1155MetadataURI-uri}. * * This implementation returns the same URI for *all* token types. It relies * on the token type ID substitution mechanism * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. * * Clients calling this function must replace the `\{id\}` substring with the * actual token type ID. */ function uri(uint256) public view virtual override returns (string memory) { return _uri; } /** * @dev See {IERC1155-balanceOf}. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { require(account != address(0), "ERC1155: balance query for the zero address"); return _balances[id][account]; } /** * @dev See {IERC1155-balanceOfBatch}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch(address[] memory accounts, uint256[] memory ids) public view virtual override returns (uint256[] memory) { require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch"); uint256[] memory batchBalances = new uint256[](accounts.length); for (uint256 i = 0; i < accounts.length; ++i) { batchBalances[i] = balanceOf(accounts[i], ids[i]); } return batchBalances; } /** * @dev See {IERC1155-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual override { _setApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC1155-isApprovedForAll}. */ function isApprovedForAll(address account, address operator) public view virtual override returns (bool) { return _operatorApprovals[account][operator]; } /** * @dev See {IERC1155-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), "ERC1155: caller is not owner nor approved" ); _safeTransferFrom(from, to, id, amount, data); } /** * @dev See {IERC1155-safeBatchTransferFrom}. */ function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), "ERC1155: transfer caller is not owner nor approved" ); _safeBatchTransferFrom(from, to, ids, amounts, data); } /** * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - `from` must have a balance of tokens of type `id` of at least `amount`. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function _safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) internal virtual { require(to != address(0), "ERC1155: transfer to the zero address"); address operator = _msgSender(); uint256[] memory ids = _asSingletonArray(id); uint256[] memory amounts = _asSingletonArray(amount); _beforeTokenTransfer(operator, from, to, ids, amounts, data); uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); unchecked { _balances[id][from] = fromBalance - amount; } _balances[id][to] += amount; emit TransferSingle(operator, from, to, id, amount); _afterTokenTransfer(operator, from, to, ids, amounts, data); _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}. * * Emits a {TransferBatch} event. * * Requirements: * * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function _safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual { require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); require(to != address(0), "ERC1155: transfer to the zero address"); address operator = _msgSender(); _beforeTokenTransfer(operator, from, to, ids, amounts, data); for (uint256 i = 0; i < ids.length; ++i) { uint256 id = ids[i]; uint256 amount = amounts[i]; uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); unchecked { _balances[id][from] = fromBalance - amount; } _balances[id][to] += amount; } emit TransferBatch(operator, from, to, ids, amounts); _afterTokenTransfer(operator, from, to, ids, amounts, data); _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data); } /** * @dev Sets a new URI for all token types, by relying on the token type ID * substitution mechanism * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. * * By this mechanism, any occurrence of the `\{id\}` substring in either the * URI or any of the amounts in the JSON file at said URI will be replaced by * clients with the token type ID. * * For example, the `https://token-cdn-domain/\{id\}.json` URI would be * interpreted by clients as * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` * for token type ID 0x4cce0. * * See {uri}. * * Because these URIs cannot be meaningfully represented by the {URI} event, * this function emits no events. */ function _setURI(string memory newuri) internal virtual { _uri = newuri; } /** * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function _mint( address to, uint256 id, uint256 amount, bytes memory data ) internal virtual { require(to != address(0), "ERC1155: mint to the zero address"); address operator = _msgSender(); uint256[] memory ids = _asSingletonArray(id); uint256[] memory amounts = _asSingletonArray(amount); _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); _balances[id][to] += amount; emit TransferSingle(operator, address(0), to, id, amount); _afterTokenTransfer(operator, address(0), to, ids, amounts, data); _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}. * * Requirements: * * - `ids` and `amounts` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function _mintBatch( address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual { require(to != address(0), "ERC1155: mint to the zero address"); require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); address operator = _msgSender(); _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); for (uint256 i = 0; i < ids.length; i++) { _balances[ids[i]][to] += amounts[i]; } emit TransferBatch(operator, address(0), to, ids, amounts); _afterTokenTransfer(operator, address(0), to, ids, amounts, data); _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data); } /** * @dev Destroys `amount` tokens of token type `id` from `from` * * Requirements: * * - `from` cannot be the zero address. * - `from` must have at least `amount` tokens of token type `id`. */ function _burn( address from, uint256 id, uint256 amount ) internal virtual { require(from != address(0), "ERC1155: burn from the zero address"); address operator = _msgSender(); uint256[] memory ids = _asSingletonArray(id); uint256[] memory amounts = _asSingletonArray(amount); _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); unchecked { _balances[id][from] = fromBalance - amount; } emit TransferSingle(operator, from, address(0), id, amount); _afterTokenTransfer(operator, from, address(0), ids, amounts, ""); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}. * * Requirements: * * - `ids` and `amounts` must have the same length. */ function _burnBatch( address from, uint256[] memory ids, uint256[] memory amounts ) internal virtual { require(from != address(0), "ERC1155: burn from the zero address"); require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); address operator = _msgSender(); _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); for (uint256 i = 0; i < ids.length; i++) { uint256 id = ids[i]; uint256 amount = amounts[i]; uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); unchecked { _balances[id][from] = fromBalance - amount; } } emit TransferBatch(operator, from, address(0), ids, amounts); _afterTokenTransfer(operator, from, address(0), ids, amounts, ""); } /** * @dev Approve `operator` to operate on all of `owner` tokens * * Emits a {ApprovalForAll} event. */ function _setApprovalForAll( address owner, address operator, bool approved ) internal virtual { require(owner != operator, "ERC1155: setting approval status for self"); _operatorApprovals[owner][operator] = approved; emit ApprovalForAll(owner, operator, approved); } /** * @dev Hook that is called before any token transfer. This includes minting * and burning, as well as batched variants. * * The same hook is called on both single and batched variants. For single * transfers, the length of the `id` and `amount` arrays will be 1. * * Calling conditions (for each `id` and `amount` pair): * * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens * of token type `id` will be transferred to `to`. * - When `from` is zero, `amount` tokens of token type `id` will be minted * for `to`. * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` * will be burned. * - `from` and `to` are never both zero. * - `ids` and `amounts` have the same, non-zero length. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual {} /** * @dev Hook that is called after any token transfer. This includes minting * and burning, as well as batched variants. * * The same hook is called on both single and batched variants. For single * transfers, the length of the `id` and `amount` arrays will be 1. * * Calling conditions (for each `id` and `amount` pair): * * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens * of token type `id` will be transferred to `to`. * - When `from` is zero, `amount` tokens of token type `id` will be minted * for `to`. * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` * will be burned. * - `from` and `to` are never both zero. * - `ids` and `amounts` have the same, non-zero length. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual {} function _doSafeTransferAcceptanceCheck( address operator, address from, address to, uint256 id, uint256 amount, bytes memory data ) private { if (to.isContract()) { try IERC1155ReceiverUpgradeable(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { if (response != IERC1155ReceiverUpgradeable.onERC1155Received.selector) { revert("ERC1155: ERC1155Receiver rejected tokens"); } } catch Error(string memory reason) { revert(reason); } catch { revert("ERC1155: transfer to non ERC1155Receiver implementer"); } } } function _doSafeBatchTransferAcceptanceCheck( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) private { if (to.isContract()) { try IERC1155ReceiverUpgradeable(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( bytes4 response ) { if (response != IERC1155ReceiverUpgradeable.onERC1155BatchReceived.selector) { revert("ERC1155: ERC1155Receiver rejected tokens"); } } catch Error(string memory reason) { revert(reason); } catch { revert("ERC1155: transfer to non ERC1155Receiver implementer"); } } } function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) { uint256[] memory array = new uint256[](1); array[0] = element; return array; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[47] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol) pragma solidity ^0.8.0; import "../IERC1155Upgradeable.sol"; /** * @dev Interface of the optional ERC1155MetadataExtension interface, as defined * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP]. * * _Available since v3.1._ */ interface IERC1155MetadataURIUpgradeable is IERC1155Upgradeable { /** * @dev Returns the URI for token type `id`. * * If the `\{id\}` substring is present in the URI, it must be replaced by * clients with the actual token type ID. */ function uri(uint256 id) external view returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; contract ItemContract is OwnableUpgradeable, ERC1155Upgradeable { using SafeMathUpgradeable for uint256; mapping(address => bool) private adminAccess; event SetAdminAccess(address indexed user, bool access); string _contractURI; function initialize() public initializer { __ERC1155_init(""); __Ownable_init(); } function name() public view virtual returns (string memory) { return "Battlefly Items"; } function symbol() public view virtual returns (string memory) { return "Battlefly ITEM"; } function contractURI() public view virtual returns (string memory) { return _contractURI; } function mintItems( uint256 itemId, address receiver, uint256 amount, bytes memory data ) external onlyAdminAccess { _mint(receiver, itemId, amount, data); } function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; emit SetAdminAccess(user, access); } function setContractURI(string memory contractURI_) external onlyOwner { _contractURI = contractURI_; } function setURI(string memory uri_) external onlyOwner { _setURI(uri_); } modifier onlyAdminAccess() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } }
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "./interfaces/IBattleflyGame.sol"; import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; // OZ: MerkleProof import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract BattleflyPublicMint is OwnableUpgradeable { using SafeMathUpgradeable for uint256; using AddressUpgradeable for address; mapping(address => bool) public HasMinted; bytes32 public merkleRootBattlefly; mapping(address => bool) private adminAccess; IBattleflyGame Game; uint256 public StartTime; uint256 public EndTime; uint256 public BattleflyType; uint256 public MinMagicAmountHolder; ERC20 MagicToken; mapping(bytes32 => bool) public HasMintedTicket; event PublicMintBattlefly(address indexed to, uint256 battleflyType, uint256 battleflyId, bytes32 indexed ticket); function initialize(address battleflyGameContractAddress, address magicTokenAddress) public initializer { __Ownable_init(); Game = IBattleflyGame(battleflyGameContractAddress); MagicToken = ERC20(magicTokenAddress); } function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; } function setMerkleRootBattlefly(bytes32 merkleRoot) external onlyAdminAccess { merkleRootBattlefly = merkleRoot; } function setHasMinted(address user, bool value) external onlyAdminAccess { HasMinted[user] = value; } function setMinting( uint256 start, uint256 end, uint256 minMagicAmountHolder, uint256 battleflyType ) external onlyAdminAccess { StartTime = start; EndTime = end; MinMagicAmountHolder = minMagicAmountHolder; BattleflyType = battleflyType; } function mintBattlefly(bytes32 ticket, bytes32[] calldata proof) external { address to = _msgSender(); require(block.timestamp >= StartTime, "Not start yet"); require(block.timestamp <= EndTime, "Already finished"); require(HasMinted[to] == false, "Already minted"); require(HasMintedTicket[ticket] == false, "Already minted - ticket"); if (MinMagicAmountHolder != 0) require(MagicToken.balanceOf(_msgSender()) >= MinMagicAmountHolder, "You must hold an amount of Magic"); bytes32 leaf = keccak256(abi.encodePacked(ticket)); bool isValidLeaf = MerkleProof.verify(proof, merkleRootBattlefly, leaf); require(isValidLeaf, "Not in merkle"); HasMinted[to] = true; HasMintedTicket[ticket] = true; uint256 battleflyId = Game.mintBattlefly(to, BattleflyType); emit PublicMintBattlefly(to, BattleflyType, battleflyId, ticket); } modifier onlyAdminAccess() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } }
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol"; contract BattleflyContract is ERC721EnumerableUpgradeable, OwnableUpgradeable { using SafeMathUpgradeable for uint256; using AddressUpgradeable for address; using CountersUpgradeable for CountersUpgradeable.Counter; using StringsUpgradeable for uint256; mapping(address => bool) private adminAccess; mapping(uint256 => uint256) private battleflyTypes; CountersUpgradeable.Counter private _tokenIdCounter; event SetAdminAccess(address indexed user, bool access); function initialize() public initializer { __ERC721Enumerable_init(); __ERC721_init("Battlefly", "Battlefly"); __Ownable_init(); } function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); return string(abi.encodePacked("https://api.battlefly.game/battleflies/", tokenId.toString(), "/metadata")); } function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; emit SetAdminAccess(user, access); } function mintBattlefly(address receiver, uint256 battleflyType) external onlyAdminAccess returns (uint256) { uint256 nextTokenId = _getNextTokenId(); battleflyTypes[nextTokenId] = battleflyType; _mint(receiver, nextTokenId); return nextTokenId; } function mintBattleflies( address receiver, uint256 _battleflyType, uint256 amount ) external onlyAdminAccess returns (uint256[] memory) { uint256[] memory tokenIds = new uint256[](amount); for (uint256 i = 0; i < amount; i++) { uint256 nextTokenId = _getNextTokenId(); battleflyTypes[nextTokenId] = _battleflyType; tokenIds[i] = nextTokenId; _mint(receiver, nextTokenId); } return tokenIds; } function getBattleflyType(uint256 tokenId) external view returns (uint256) { return battleflyTypes[tokenId]; } function _getNextTokenId() private view returns (uint256) { return (_tokenIdCounter.current() + 1); } function _mint(address to, uint256 tokenId) internal override(ERC721Upgradeable) { super._mint(to, tokenId); _tokenIdCounter.increment(); } modifier onlyAdminAccess() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/IAtlasMine.sol"; import "./interfaces/IBattleflyAtlasStaker.sol"; import "hardhat/console.sol"; contract BattleflyAtlasStakerV01 is IBattleflyAtlasStaker, Initializable, OwnableUpgradeable, ERC1155HolderUpgradeable, ERC721HolderUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; using AddressUpgradeable for address; using SafeCastUpgradeable for uint256; using SafeCastUpgradeable for int256; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; // ============================================ STATE ============================================== // ============= Global Immutable State ============== /// @notice MAGIC token /// @dev functionally immutable IERC20Upgradeable public magic; /// @notice The IAtlasMine /// @dev functionally immutable IAtlasMine public mine; // ============= Global Staking State ============== uint256 public constant ONE = 1e30; /// @notice Whether new stakes will get staked on the contract as scheduled. For emergencies bool public schedulePaused; /// @notice The total amount of staked token uint256 public totalStaked; /// @notice The total amount of share uint256 public totalShare; /// @notice All stakes currently active Stake[] public stakes; /// @notice Deposit ID of last stake. Also tracked in atlas mine uint256 public lastDepositId; /// @notice Rewards accumulated per share uint256 public accRewardsPerShare; // ============= Vault Staking State ============== mapping(address => bool) public battleflyVaults; /// @notice Each vault stake, keyed by vault contract address => deposit ID mapping(address => mapping(uint256 => VaultStake)) public vaultStake; /// @notice All deposit IDs fro a vault, enumerated mapping(address => EnumerableSetUpgradeable.UintSet) private allVaultDepositIds; /// @notice The current ID of the vault's last deposited stake mapping(address => uint256) public currentId; // ============= NFT Boosting State ============== /// @notice Holder of treasures and legions mapping(uint256 => bool) public legionsStaked; mapping(uint256 => uint256) public treasuresStaked; // ============= Operator State ============== IAtlasMine.Lock[] public allowedLocks; /// @notice Fee to contract operator. Only assessed on rewards. uint256 public fee; /// @notice Amount of fees reserved for withdrawal by the operator. uint256 public feeReserve; /// @notice Max fee the owner can ever take - 10% uint256 public constant MAX_FEE = 1000; uint256 public constant FEE_DENOMINATOR = 10000; mapping(address => mapping(uint256 => int256)) refundedFeeDebts; uint256 accRefundedFeePerShare; uint256 totalWhitelistedFeeShare; EnumerableSetUpgradeable.AddressSet whitelistedFeeVaults; mapping(address => bool) public superAdmins; /// @notice deposited but unstaked uint256 public unstakedDeposits; mapping(IAtlasMine.Lock => uint256) public unstakedDepositsByLock; address public constant TREASURY_WALLET = 0xF5411006eEfD66c213d2fd2033a1d340458B7226; /// @notice Intra-tx buffer for pending payouts uint256 public tokenBuffer; // =========================================== // ============== Post Upgrade =============== // =========================================== // ========================================== INITIALIZER =========================================== /** * @param _magic The MAGIC token address. * @param _mine The IAtlasMine contract. * Maps to a timelock for IAtlasMine deposits. */ function initialize( IERC20Upgradeable _magic, IAtlasMine _mine, IAtlasMine.Lock[] memory _allowedLocks ) external initializer { __ERC1155Holder_init(); __ERC721Holder_init(); __Ownable_init(); __ReentrancyGuard_init(); magic = _magic; mine = _mine; allowedLocks = _allowedLocks; fee = 1000; // Approve the mine magic.safeApprove(address(mine), 2**256 - 1); } // ======================================== VAULT OPERATIONS ======================================== /** * @notice Make a new deposit into the Staker. The Staker will collect * the tokens, to be later staked in atlas mine by the owner, * according to the stake/unlock schedule. * @dev Specified amount of token must be approved by the caller. * * @param _amount The amount of tokens to deposit. */ function deposit(uint256 _amount, IAtlasMine.Lock lock) public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) { require(!schedulePaused, "new staking paused"); _updateRewards(); // Collect tokens uint256 newDepositId = _deposit(_amount, msg.sender, lock); magic.safeTransferFrom(msg.sender, address(this), _amount); return (newDepositId); } function _deposit( uint256 _amount, address _vault, IAtlasMine.Lock lock ) internal returns (uint256) { require(_amount > 0, "Deposit amount 0"); bool validLock = false; for (uint256 i = 0; i < allowedLocks.length; i++) { if (allowedLocks[i] == lock) { validLock = true; break; } } require(validLock, "Lock time not allowed"); // Add vault stake uint256 newDepositId = ++currentId[_vault]; allVaultDepositIds[_vault].add(newDepositId); VaultStake storage s = vaultStake[_vault][newDepositId]; s.amount = _amount; (uint256 boost, uint256 lockTime) = getLockBoost(lock); uint256 share = (_amount * (100e16 + boost)) / 100e16; uint256 vestingTime = mine.getVestingTime(lock); s.unlockAt = block.timestamp + lockTime + vestingTime + 1 days; s.rewardDebt = ((share * accRewardsPerShare) / ONE).toInt256(); s.lock = lock; // Update global accounting totalStaked += _amount; totalShare += share; if (whitelistedFeeVaults.contains(_vault)) { totalWhitelistedFeeShare += share; refundedFeeDebts[_vault][newDepositId] = ((share * accRefundedFeePerShare) / ONE).toInt256(); } // MAGIC tokens sit in contract. Added to pending stakes unstakedDeposits += _amount; unstakedDepositsByLock[lock] += _amount; emit VaultDeposit(_vault, newDepositId, _amount, s.unlockAt, s.lock); return newDepositId; } /** * @notice Withdraw a deposit from the Staker contract. Calculates * pro rata share of accumulated MAGIC and distributes any * earned rewards in addition to original deposit. * There must be enough unlocked tokens to withdraw. * * @param depositId The ID of the deposit to withdraw from. * */ function withdraw(uint256 depositId) public virtual override onlyBattleflyVaultOrOwner nonReentrant { // Distribute tokens _updateRewards(); VaultStake storage s = vaultStake[msg.sender][depositId]; require(s.amount > 0, "No deposit"); require(block.timestamp >= s.unlockAt, "Deposit locked"); uint256 payout = _withdraw(s, depositId); magic.safeTransfer(msg.sender, payout); } /** * @notice Withdraw all eligible deposits from the staker contract. * Will skip any deposits not yet unlocked. Will also * distribute rewards for all stakes via 'withdraw'. * */ function withdrawAll() public virtual override onlyBattleflyVaultOrOwner nonReentrant { // Distribute tokens _updateRewards(); uint256[] memory depositIds = allVaultDepositIds[msg.sender].values(); for (uint256 i = 0; i < depositIds.length; i++) { VaultStake storage s = vaultStake[msg.sender][depositIds[i]]; if (s.amount > 0 && s.unlockAt > 0 && s.unlockAt <= block.timestamp) { tokenBuffer += _withdraw(s, depositIds[i]); } } magic.safeTransfer(msg.sender, tokenBuffer); tokenBuffer = 0; } /** * @dev Logic for withdrawing a deposit. Calculates pro rata share of * accumulated MAGIC and dsitributed any earned rewards in addition * to original deposit. * * @dev An _amount argument larger than the total deposit amount will * withdraw the entire deposit. * * @param s The VaultStake struct to withdraw from. * @param depositId The ID of the deposit to withdraw from (for event). */ function _withdraw(VaultStake storage s, uint256 depositId) internal returns (uint256 payout) { uint256 _amount = s.amount; // Unstake if we need to to ensure we can withdraw (uint256 boost, ) = getLockBoost(s.lock); uint256 share = (_amount * (100e16 + boost)) / 100e16; int256 accumulatedRewards = ((share * accRewardsPerShare) / ONE).toInt256(); if (whitelistedFeeVaults.contains(msg.sender)) { accumulatedRewards += ((share * accRefundedFeePerShare) / ONE).toInt256(); accumulatedRewards -= refundedFeeDebts[msg.sender][depositId]; totalWhitelistedFeeShare -= share; refundedFeeDebts[msg.sender][depositId] = 0; } uint256 reward = (accumulatedRewards - s.rewardDebt).toUint256(); payout = _amount + reward; delete vaultStake[msg.sender][depositId]; // Update global accounting totalStaked -= _amount; totalShare -= share; // If we need to unstake, unstake until we have enough if (payout > _totalUsableMagic()) { _unstakeToTarget(payout - _totalUsableMagic()); } emit VaultWithdraw(msg.sender, depositId, _amount, reward); } /** * @notice Claim rewards without unstaking. Will fail if there * are not enough tokens in the contract to claim rewards. * Does not attempt to unstake. * * @param depositId The ID of the deposit to claim rewards from. * */ function claim(uint256 depositId) public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) { _updateRewards(); VaultStake storage s = vaultStake[msg.sender][depositId]; require(s.amount > 0, "No deposit"); uint256 reward = _claim(s, depositId); magic.safeTransfer(msg.sender, reward); return reward; } /** * @notice Claim all possible rewards from the staker contract. * Will apply to both locked and unlocked deposits. * */ function claimAll() public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) { _updateRewards(); uint256[] memory depositIds = allVaultDepositIds[msg.sender].values(); uint256 totalReward = 0; for (uint256 i = 0; i < depositIds.length; i++) { VaultStake storage s = vaultStake[msg.sender][depositIds[i]]; uint256 reward = _claim(s, depositIds[i]); totalReward += reward; } magic.safeTransfer(msg.sender, totalReward); return totalReward; } /** * @notice Claim all possible rewards from the staker contract then restake. * Will apply to both locked and unlocked deposits. * */ function claimAllAndRestake(IAtlasMine.Lock lock) public onlyBattleflyVaultOrOwner nonReentrant returns (uint256) { _updateRewards(); uint256[] memory depositIds = allVaultDepositIds[msg.sender].values(); uint256 totalReward = 0; for (uint256 i = 0; i < depositIds.length; i++) { VaultStake storage s = vaultStake[msg.sender][depositIds[i]]; uint256 reward = _claim(s, depositIds[i]); totalReward += reward; } _deposit(totalReward, msg.sender, lock); return totalReward; } /** * @dev Logic for claiming rewards on a deposit. Calculates pro rata share of * accumulated MAGIC and dsitributed any earned rewards in addition * to original deposit. * * @param s The VaultStake struct to claim from. * @param depositId The ID of the deposit to claim from (for event). */ function _claim(VaultStake storage s, uint256 depositId) internal returns (uint256) { // Update accounting (uint256 boost, ) = getLockBoost(s.lock); uint256 share = (s.amount * (100e16 + boost)) / 100e16; int256 accumulatedRewards = ((share * accRewardsPerShare) / ONE).toInt256(); uint256 reward = (accumulatedRewards - s.rewardDebt).toUint256(); if (whitelistedFeeVaults.contains(msg.sender)) { int256 accumulatedRefundedFee = ((share * accRefundedFeePerShare) / ONE).toInt256(); reward += accumulatedRefundedFee.toUint256(); reward -= refundedFeeDebts[msg.sender][depositId].toUint256(); refundedFeeDebts[msg.sender][depositId] = accumulatedRefundedFee; } s.rewardDebt = accumulatedRewards; // Unstake if we need to to ensure we can withdraw if (reward > _totalUsableMagic()) { _unstakeToTarget(reward - _totalUsableMagic()); } require(reward <= _totalUsableMagic(), "Not enough rewards to claim"); emit VaultClaim(msg.sender, depositId, reward); return reward; } // ======================================= SUPER ADMIN OPERATIONS ======================================== /** * @notice Stake a Treasure owned by the superAdmin into the Atlas Mine. * Staked treasures will boost all vault deposits. * @dev Any treasure must be approved for withdrawal by the caller. * * @param _tokenId The tokenId of the specified treasure. * @param _amount The amount of treasures to stake. */ function stakeTreasure(uint256 _tokenId, uint256 _amount) external onlySuperAdminOrOwner { address treasureAddr = mine.treasure(); require(IERC1155Upgradeable(treasureAddr).balanceOf(msg.sender, _tokenId) >= _amount, "Not enough treasures"); treasuresStaked[_tokenId] += _amount; // First withdraw and approve IERC1155Upgradeable(treasureAddr).safeTransferFrom(msg.sender, address(this), _tokenId, _amount, bytes("")); mine.stakeTreasure(_tokenId, _amount); uint256 boost = mine.boosts(address(this)); emit StakeNFT(msg.sender, treasureAddr, _tokenId, _amount, boost); } /** * @notice Unstake a Treasure from the Atlas Mine adn transfer to receiver. * * @param _receiver The receiver . * @param _tokenId The tokenId of the specified treasure. * @param _amount The amount of treasures to stake. */ function unstakeTreasure( address _receiver, uint256 _tokenId, uint256 _amount ) external onlySuperAdminOrOwner { require(treasuresStaked[_tokenId] >= _amount, "Not enough treasures"); treasuresStaked[_tokenId] -= _amount; address treasureAddr = mine.treasure(); mine.unstakeTreasure(_tokenId, _amount); IERC1155Upgradeable(treasureAddr).safeTransferFrom(address(this), _receiver, _tokenId, _amount, bytes("")); uint256 boost = mine.boosts(address(this)); emit UnstakeNFT(_receiver, treasureAddr, _tokenId, _amount, boost); } /** * @notice Stake a Legion owned by the superAdmin into the Atlas Mine. * Staked legions will boost all vault deposits. * @dev Any legion be approved for withdrawal by the caller. * * @param _tokenId The tokenId of the specified legion. */ function stakeLegion(uint256 _tokenId) external onlySuperAdminOrOwner { address legionAddr = mine.legion(); require(IERC721Upgradeable(legionAddr).ownerOf(_tokenId) == msg.sender, "Not owner of legion"); legionsStaked[_tokenId] = true; IERC721Upgradeable(legionAddr).safeTransferFrom(msg.sender, address(this), _tokenId); mine.stakeLegion(_tokenId); uint256 boost = mine.boosts(address(this)); emit StakeNFT(msg.sender, legionAddr, _tokenId, 1, boost); } /** * @notice Unstake a Legion from the Atlas Mine and return it to the superAdmin. * * @param _tokenId The tokenId of the specified legion. */ function unstakeLegion(address _receiver, uint256 _tokenId) external onlySuperAdminOrOwner { require(legionsStaked[_tokenId], "No legion"); address legionAddr = mine.legion(); delete legionsStaked[_tokenId]; mine.unstakeLegion(_tokenId); // Distribute to superAdmin IERC721Upgradeable(legionAddr).safeTransferFrom(address(this), _receiver, _tokenId); uint256 boost = mine.boosts(address(this)); emit UnstakeNFT(_receiver, legionAddr, _tokenId, 1, boost); } /** * @notice Stake any pending stakes before the current day. Callable * by anybody. Any pending stakes will unlock according * to the time this method is called, and the contract's defined * lock time. */ function stakeScheduled() external virtual override onlySuperAdminOrOwner { for (uint256 i = 0; i < allowedLocks.length; i++) { IAtlasMine.Lock lock = allowedLocks[i]; _stakeInMine(unstakedDepositsByLock[lock], lock); unstakedDepositsByLock[lock] = 0; } unstakedDeposits = 0; } /** * @notice Unstake everything eligible for unstaking from Atlas Mine. * Callable by owner. Should only be used in case of emergency * or migration to a new contract, or if there is a need to service * an unexpectedly large amount of withdrawals. * * If unlockAll is set to true in the Atlas Mine, this can withdraw * all stake. */ function unstakeAllFromMine() external override onlySuperAdminOrOwner { // Unstake everything eligible _updateRewards(); for (uint256 i = 0; i < stakes.length; i++) { Stake memory s = stakes[i]; if (s.unlockAt > block.timestamp) { continue; } // Withdraw position - auto-harvest mine.withdrawPosition(s.depositId, s.amount); } // Only check for removal after, so we don't mutate while looping _removeZeroStakes(); } /** * @notice Let owner unstake a specified amount as needed to make sure the contract is funded. * Can be used to facilitate expected future withdrawals. * * @param target The amount of tokens to reclaim from the mine. */ function unstakeToTarget(uint256 target) external override onlySuperAdminOrOwner { _updateRewards(); _unstakeToTarget(target); } /** * @notice Withdraw any accumulated reward fees to the treasury */ function withdrawFeesToTreasury() external virtual onlySuperAdminOrOwner { uint256 amount = feeReserve; feeReserve = 0; magic.safeTransfer(TREASURY_WALLET, amount); emit WithdrawFeesToTreasury(amount); } function stakeBackFeeTreasury(IAtlasMine.Lock lock) external virtual onlySuperAdminOrOwner { uint256 amount = feeReserve; feeReserve = 0; emit WithdrawFeesToTreasury(amount); _deposit(amount, TREASURY_WALLET, lock); } /** * @notice Whitelist vault from fees. * * @param _vault Vault address. * @param isSet Whether to enable or disable the vault whitelist. */ function setFeeWhitelistVault(address _vault, bool isSet) external onlyOwner { require(_vault != address(0), "Invalid Vault"); if (isSet) { whitelistedFeeVaults.add(_vault); totalWhitelistedFeeShare += totalShareOf(_vault); } else { whitelistedFeeVaults.remove(_vault); totalWhitelistedFeeShare -= totalShareOf(_vault); } emit SetFeeWhitelistVault(_vault, isSet); } // ======================================= OWNER OPERATIONS ======================================= function setBattleflyVault(address _vaultAddress, bool isSet) external onlyOwner { require(_vaultAddress != address(0), "Invalid vault"); if (isSet) { require(battleflyVaults[_vaultAddress] == false, "Vault already set"); battleflyVaults[_vaultAddress] = isSet; } else { require(allVaultDepositIds[_vaultAddress].length() == 0, "Vault is still active"); delete battleflyVaults[_vaultAddress]; } emit SetBattleflyVault(_vaultAddress, isSet); } /** * @notice Change the designated superAdmin, the address where treasures and * legions are held. Staked NFTs can only be * withdrawn to the current superAdmin address, regardless of which * address the superAdmin was set to when it was staked. * * @param _superAdmin The new superAdmin address. * @param isSet Whether to enable or disable the superAdmin address. */ function setBoostAdmin(address _superAdmin, bool isSet) external override onlyOwner { require(_superAdmin != address(0), "Invalid superAdmin"); superAdmins[_superAdmin] = isSet; } /** * @notice Change the designated super admin, who manage the fee reverse * * @param _superAdmin The new superAdmin address. * @param isSet Whether to enable or disable the super admin address. */ function setSuperAdmin(address _superAdmin, bool isSet) external onlyOwner { require(_superAdmin != address(0), "Invalid address"); superAdmins[_superAdmin] = isSet; } /** * @notice Approve treasures and legions for withdrawal from the atlas mine. * Called on startup, and should be called again in case contract * addresses for treasures and legions ever change. * */ function approveNFTs() public override onlyOwner { address treasureAddr = mine.treasure(); IERC1155Upgradeable(treasureAddr).setApprovalForAll(address(mine), true); address legionAddr = mine.legion(); IERC1155Upgradeable(legionAddr).setApprovalForAll(address(mine), true); } /** * @notice EMERGENCY ONLY - toggle pausing new scheduled stakes. * If on, vaults can deposit, but stakes won't go to Atlas Mine. * Can be used in case of Atlas Mine issues or forced migration * to new contract. */ function toggleSchedulePause(bool paused) external virtual override onlyOwner { schedulePaused = paused; emit StakingPauseToggle(paused); } // ======================================== VIEW FUNCTIONS ========================================= function getLockBoost(IAtlasMine.Lock _lock) public pure virtual returns (uint256 boost, uint256 timelock) { if (_lock == IAtlasMine.Lock.twoWeeks) { // 10% return (10e16, 14 days); } else if (_lock == IAtlasMine.Lock.oneMonth) { // 25% return (25e16, 30 days); } else if (_lock == IAtlasMine.Lock.threeMonths) { // 80% return (80e16, 13 weeks); } else if (_lock == IAtlasMine.Lock.sixMonths) { // 180% return (180e16, 23 weeks); } else if (_lock == IAtlasMine.Lock.twelveMonths) { // 400% return (400e16, 365 days); } else { revert("Invalid lock value"); } } /** * @notice Returns all magic either unstaked, staked, or pending rewards in Atlas Mine. * Best proxy for TVL. * * @return total The total amount of MAGIC in the staker. */ function totalMagic() external view override returns (uint256) { return _totalControlledMagic() + mine.pendingRewardsAll(address(this)); } /** * @notice Returns all magic that has been deposited, but not staked, and is eligible * to be staked (deposit time < current day). * * @return total The total amount of MAGIC that can be withdrawn. */ function totalWithdrawableMagic() external view override returns (uint256) { uint256 totalPendingRewards; // IAtlasMine attempts to divide by 0 if there are no deposits try mine.pendingRewardsAll(address(this)) returns (uint256 _pending) { totalPendingRewards = _pending; } catch Panic(uint256) { totalPendingRewards = 0; } return _totalUsableMagic() + totalPendingRewards; } /** * @notice Returns the details of a vault stake. * * @return vaultStake The details of a vault stake. */ function getVaultStake(address vault, uint256 depositId) external view override returns (VaultStake memory) { return vaultStake[vault][depositId]; } /** * @notice Returns the pending, claimable rewards for a deposit. * @dev This does not update rewards, so out of date if rewards not recently updated. * Needed to maintain 'view' function type. * * @param vault The vault to check rewards for. * @param depositId The specific deposit to check rewards for. * * @return reward The total amount of MAGIC reward pending. */ function pendingRewards(address vault, uint256 depositId) public view override returns (uint256 reward) { if (totalShare == 0) { return 0; } VaultStake storage s = vaultStake[vault][depositId]; (uint256 boost, ) = getLockBoost(s.lock); uint256 share = (s.amount * (100e16 + boost)) / 100e16; uint256 unupdatedReward = mine.pendingRewardsAll(address(this)); (uint256 founderReward, , uint256 feeRefund) = _calculateHarvestRewardFee(unupdatedReward); uint256 realAccRewardsPerShare = accRewardsPerShare + (founderReward * ONE) / totalShare; uint256 accumulatedRewards = (share * realAccRewardsPerShare) / ONE; if (whitelistedFeeVaults.contains(vault) && totalWhitelistedFeeShare > 0) { uint256 realAccRefundedFeePerShare = accRefundedFeePerShare + (feeRefund * ONE) / totalWhitelistedFeeShare; uint256 accumulatedRefundedFee = (share * realAccRefundedFeePerShare) / ONE; accumulatedRewards = accumulatedRewards + accumulatedRefundedFee; accumulatedRewards -= refundedFeeDebts[vault][depositId].toUint256(); } reward = accumulatedRewards - s.rewardDebt.toUint256(); } /** * @notice Returns the pending, claimable rewards for all of a vault's deposits. * @dev This does not update rewards, so out of date if rewards not recently updated. * Needed to maintain 'view' function type. * * @param vault The vault to check rewards for. * * @return reward The total amount of MAGIC reward pending. */ function pendingRewardsAll(address vault) external view override returns (uint256 reward) { uint256[] memory depositIds = allVaultDepositIds[vault].values(); for (uint256 i = 0; i < depositIds.length; i++) { reward += pendingRewards(vault, depositIds[i]); } } /** * @notice Returns the total Share of a vault. * * @param vault The vault to check rewards for. * * @return _totalShare The total share of a vault. */ function totalShareOf(address vault) public view returns (uint256 _totalShare) { uint256[] memory depositIds = allVaultDepositIds[vault].values(); for (uint256 i = 0; i < depositIds.length; i++) { (uint256 boost, ) = getLockBoost(vaultStake[vault][depositIds[i]].lock); uint256 share = (vaultStake[vault][depositIds[i]].amount * (100e16 + boost)) / 100e16; _totalShare += share; } } // ============================================ HELPERS ============================================ /** * @dev Stake tokens held by staker in the Atlas Mine, according to * the predefined lock value. Schedules for staking will be managed by a queue. * * @param _amount Number of tokens to stake */ function _stakeInMine(uint256 _amount, IAtlasMine.Lock lock) internal { require(_amount <= _totalUsableMagic(), "Not enough funds"); uint256 depositId = ++lastDepositId; (, uint256 lockTime) = getLockBoost(lock); uint256 vestingPeriod = mine.getVestingTime(lock); uint256 unlockAt = block.timestamp + lockTime + vestingPeriod; stakes.push(Stake({ amount: _amount, unlockAt: unlockAt, depositId: depositId })); mine.deposit(_amount, lock); } /** * @dev Unstakes until we have enough unstaked tokens to meet a specific target. * Used to make sure we can service withdrawals. * * @param target The amount of tokens we want to have unstaked. */ function _unstakeToTarget(uint256 target) internal { uint256 unstaked = 0; for (uint256 i = 0; i < stakes.length; i++) { Stake memory s = stakes[i]; if (s.unlockAt > block.timestamp && !mine.unlockAll()) { // This stake is not unlocked - stop looking continue; } // Withdraw position - auto-harvest uint256 preclaimBalance = _totalUsableMagic(); uint256 targetLeft = target - unstaked; uint256 amount = targetLeft > s.amount ? s.amount : targetLeft; // Do not harvest rewards - if this is running, we've already // harvested in the same fn call mine.withdrawPosition(s.depositId, amount); uint256 postclaimBalance = _totalUsableMagic(); // Increment amount unstaked unstaked += postclaimBalance - preclaimBalance; if (unstaked >= target) { // We unstaked enough break; } } require(unstaked >= target, "Cannot unstake enough"); require(_totalUsableMagic() >= target, "Not enough in contract after unstaking"); // Only check for removal after, so we don't mutate while looping _removeZeroStakes(); } /** * @dev Harvest rewards from the IAtlasMine and send them back to * this contract. * * @return earned The amount of rewards earned for depositors, minus the fee. * @return feeEearned The amount of fees earned for the contract operator. */ function _harvestMine() internal returns (uint256, uint256) { uint256 preclaimBalance = magic.balanceOf(address(this)); try mine.harvestAll() { uint256 postclaimBalance = magic.balanceOf(address(this)); uint256 earned = postclaimBalance - preclaimBalance; // Reserve the 'fee' amount of what is earned (, uint256 feeEarned, uint256 feeRefunded) = _calculateHarvestRewardFee(earned); feeReserve += feeEarned - feeRefunded; emit MineHarvest(earned - feeEarned, feeEarned - feeRefunded, feeRefunded); return (earned - feeEarned, feeRefunded); } catch { // Failed because of reward debt calculation - should be 0 return (0, 0); } } function _calculateHarvestRewardFee(uint256 earned) internal view returns ( uint256, uint256, uint256 ) { uint256 feeEarned = (earned * fee) / FEE_DENOMINATOR; uint256 accFeePerShare = (feeEarned * ONE) / totalShare; uint256 feeRefunded = (accFeePerShare * totalWhitelistedFeeShare) / ONE; return (earned - feeEarned, feeEarned, feeRefunded); } /** * @dev Harvest rewards from the mine so that stakers can claim. * Recalculate how many rewards are distributed to each share. */ function _updateRewards() internal { if (totalStaked == 0 || totalShare == 0) return; (uint256 newRewards, uint256 feeRefunded) = _harvestMine(); accRewardsPerShare += (newRewards * ONE) / totalShare; if (totalWhitelistedFeeShare > 0) accRefundedFeePerShare += (feeRefunded * ONE) / totalWhitelistedFeeShare; } /** * @dev After mutating a stake (by withdrawing fully or partially), * get updated data from the staking contract, and update the stake amounts * * @param stakeIndex The index of the stake in the Stakes storage array. * * @return amount The current, updated amount of the stake. */ function _updateStakeDepositAmount(uint256 stakeIndex) internal returns (uint256) { Stake storage s = stakes[stakeIndex]; (, uint256 depositAmount, , , , , ) = mine.userInfo(address(this), s.depositId); s.amount = depositAmount; return s.amount; } /** * @dev Find stakes with zero deposit amount and remove them from tracking. * Uses recursion to stop from mutating an array we are currently looping over. * If a zero stake is found, it is removed, and the function is restarted, * such that it is always working from a 'clean' array. * */ function _removeZeroStakes() internal { bool shouldRecurse = stakes.length > 0; for (uint256 i = 0; i < stakes.length; i++) { _updateStakeDepositAmount(i); Stake storage s = stakes[i]; if (s.amount == 0) { _removeStake(i); // Stop looping and start again - we will skip // out of the look and recurse break; } if (i == stakes.length - 1) { // We didn't remove anything, so stop recursing shouldRecurse = false; } } if (shouldRecurse) { _removeZeroStakes(); } } /** * @dev Calculate total amount of MAGIC usable by the contract. * 'Usable' means available for either withdrawal or re-staking. * Counts unstaked magic less fee reserve. * * @return amount The amount of usable MAGIC. */ function _totalUsableMagic() internal view returns (uint256) { // Current magic held in contract uint256 unstaked = magic.balanceOf(address(this)); return unstaked - tokenBuffer - feeReserve; } /** * @dev Calculate total amount of MAGIC under control of the contract. * Counts staked and unstaked MAGIC. Does _not_ count accumulated * but unclaimed rewards. * * @return amount The total amount of MAGIC under control of the contract. */ function _totalControlledMagic() internal view returns (uint256) { // Current magic staked in mine uint256 staked = 0; for (uint256 i = 0; i < stakes.length; i++) { staked += stakes[i].amount; } return staked + _totalUsableMagic(); } /** * @dev Remove a tracked stake from any position in the stakes array. * Used when a stake is no longer relevant i.e. fully withdrawn. * Mutates the Stakes array in storage. * * @param index The index of the stake to remove. */ function _removeStake(uint256 index) internal { if (index >= stakes.length) return; for (uint256 i = index; i < stakes.length - 1; i++) { stakes[i] = stakes[i + 1]; } delete stakes[stakes.length - 1]; stakes.pop(); } modifier onlySuperAdminOrOwner() { require(msg.sender == owner() || superAdmins[msg.sender], "Not Super Admin"); _; } modifier onlyBattleflyVaultOrOwner() { require(msg.sender == owner() || battleflyVaults[msg.sender], "Not BattleflyVault"); _; } }
// SPDX-License-Identifier: MIT pragma solidity >= 0.4.22 <0.9.0; library console { address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); function _sendLogPayload(bytes memory payload) private view { uint256 payloadLength = payload.length; address consoleAddress = CONSOLE_ADDRESS; assembly { let payloadStart := add(payload, 32) let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) } } function log() internal view { _sendLogPayload(abi.encodeWithSignature("log()")); } function logInt(int p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(int)", p0)); } function logUint(uint p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); } function logString(string memory p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function logBool(bool p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function logAddress(address p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function logBytes(bytes memory p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); } function logBytes1(bytes1 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); } function logBytes2(bytes2 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); } function logBytes3(bytes3 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); } function logBytes4(bytes4 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); } function logBytes5(bytes5 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); } function logBytes6(bytes6 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); } function logBytes7(bytes7 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); } function logBytes8(bytes8 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); } function logBytes9(bytes9 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); } function logBytes10(bytes10 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); } function logBytes11(bytes11 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); } function logBytes12(bytes12 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); } function logBytes13(bytes13 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); } function logBytes14(bytes14 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); } function logBytes15(bytes15 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); } function logBytes16(bytes16 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); } function logBytes17(bytes17 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); } function logBytes18(bytes18 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); } function logBytes19(bytes19 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); } function logBytes20(bytes20 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); } function logBytes21(bytes21 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); } function logBytes22(bytes22 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); } function logBytes23(bytes23 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); } function logBytes24(bytes24 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); } function logBytes25(bytes25 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); } function logBytes26(bytes26 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); } function logBytes27(bytes27 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); } function logBytes28(bytes28 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); } function logBytes29(bytes29 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); } function logBytes30(bytes30 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); } function logBytes31(bytes31 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); } function logBytes32(bytes32 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); } function log(uint p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); } function log(string memory p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function log(bool p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function log(address p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function log(uint p0, uint p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1)); } function log(uint p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1)); } function log(uint p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1)); } function log(uint p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1)); } function log(string memory p0, uint p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1)); } function log(string memory p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); } function log(string memory p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); } function log(string memory p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); } function log(bool p0, uint p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1)); } function log(bool p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); } function log(bool p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); } function log(bool p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); } function log(address p0, uint p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1)); } function log(address p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); } function log(address p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); } function log(address p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); } function log(uint p0, uint p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); } function log(uint p0, uint p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); } function log(uint p0, uint p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); } function log(uint p0, uint p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); } function log(uint p0, string memory p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); } function log(uint p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); } function log(uint p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); } function log(uint p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); } function log(uint p0, bool p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); } function log(uint p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); } function log(uint p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); } function log(uint p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); } function log(uint p0, address p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); } function log(uint p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); } function log(uint p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); } function log(uint p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); } function log(string memory p0, uint p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); } function log(string memory p0, uint p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); } function log(string memory p0, uint p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); } function log(string memory p0, uint p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); } function log(string memory p0, string memory p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); } function log(string memory p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); } function log(string memory p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); } function log(string memory p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); } function log(string memory p0, bool p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); } function log(string memory p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); } function log(string memory p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); } function log(string memory p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); } function log(string memory p0, address p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); } function log(string memory p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); } function log(string memory p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); } function log(string memory p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); } function log(bool p0, uint p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); } function log(bool p0, uint p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); } function log(bool p0, uint p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); } function log(bool p0, uint p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); } function log(bool p0, string memory p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); } function log(bool p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); } function log(bool p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); } function log(bool p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); } function log(bool p0, bool p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); } function log(bool p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); } function log(bool p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); } function log(bool p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); } function log(bool p0, address p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); } function log(bool p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); } function log(bool p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); } function log(bool p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); } function log(address p0, uint p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); } function log(address p0, uint p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); } function log(address p0, uint p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); } function log(address p0, uint p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); } function log(address p0, string memory p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); } function log(address p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); } function log(address p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); } function log(address p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); } function log(address p0, bool p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); } function log(address p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); } function log(address p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); } function log(address p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); } function log(address p0, address p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); } function log(address p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); } function log(address p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); } function log(address p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); } function log(uint p0, uint p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); } function log(uint p0, uint p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); } function log(uint p0, uint p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); } function log(uint p0, uint p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); } function log(uint p0, uint p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); } function log(uint p0, uint p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); } function log(uint p0, uint p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); } function log(uint p0, uint p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); } function log(uint p0, uint p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); } function log(uint p0, uint p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); } function log(uint p0, uint p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); } function log(uint p0, uint p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); } function log(uint p0, uint p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); } function log(uint p0, uint p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); } function log(uint p0, uint p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); } function log(uint p0, uint p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3)); } function log(uint p0, bool p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); } function log(uint p0, bool p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); } function log(uint p0, bool p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); } function log(uint p0, bool p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); } function log(uint p0, bool p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); } function log(uint p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); } function log(uint p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); } function log(uint p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); } function log(uint p0, bool p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); } function log(uint p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); } function log(uint p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); } function log(uint p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); } function log(uint p0, bool p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); } function log(uint p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); } function log(uint p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); } function log(uint p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); } function log(uint p0, address p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); } function log(uint p0, address p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); } function log(uint p0, address p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); } function log(uint p0, address p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); } function log(uint p0, address p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); } function log(uint p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); } function log(uint p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); } function log(uint p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3)); } function log(uint p0, address p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); } function log(uint p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); } function log(uint p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); } function log(uint p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); } function log(uint p0, address p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); } function log(uint p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3)); } function log(uint p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); } function log(uint p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); } function log(bool p0, uint p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); } function log(bool p0, uint p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); } function log(bool p0, uint p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); } function log(bool p0, uint p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); } function log(bool p0, uint p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); } function log(bool p0, uint p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); } function log(bool p0, uint p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); } function log(bool p0, uint p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); } function log(bool p0, uint p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); } function log(bool p0, uint p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); } function log(bool p0, uint p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, uint p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); } function log(bool p0, uint p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); } function log(bool p0, uint p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); } function log(bool p0, uint p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); } function log(bool p0, uint p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); } function log(address p0, uint p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); } function log(address p0, uint p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); } function log(address p0, uint p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); } function log(address p0, uint p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); } function log(address p0, uint p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); } function log(address p0, uint p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); } function log(address p0, uint p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); } function log(address p0, uint p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3)); } function log(address p0, uint p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); } function log(address p0, uint p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); } function log(address p0, uint p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); } function log(address p0, uint p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); } function log(address p0, uint p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); } function log(address p0, uint p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3)); } function log(address p0, uint p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); } function log(address p0, uint p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); } function log(address p0, address p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); } function log(address p0, address p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3)); } function log(address p0, address p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "./interfaces/IBattleflyFounderVault.sol"; import "./interfaces/IBattleflyStaker.sol"; import "./interfaces/IBattleflyComic.sol"; contract BattleflyComic is ERC1155Upgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable, ERC1155SupplyUpgradeable, IBattleflyComic { using SafeERC20Upgradeable for IERC20Upgradeable; uint256 public currentComicId; IBattleflyFounderVault public FounderVaultV1; IBattleflyFounderVault public FounderVaultV2; IBattleflyStaker public BattleflyStaker; IERC20Upgradeable public Magic; mapping(uint256 => Comic) public comicIdToComic; mapping(uint256 => mapping(uint256 => bool)) public usedTokens; mapping(uint256 => mapping(address => uint256)) public paidMints; mapping(address => bool) public admins; function initialize( address _magic, address _founderVaultV1, address _founderVaultV2, address _battleflyStake ) external initializer { __ERC1155_init(""); __Ownable_init(); __ReentrancyGuard_init(); __ERC1155Supply_init(); require(_magic != address(0), "BattleflyComic: invalid address"); require(_founderVaultV1 != address(0), "BattleflyComic: invalid address"); require(_founderVaultV2 != address(0), "BattleflyComic: invalid address"); require(_battleflyStake != address(0), "BattleflyComic: invalid address"); admins[msg.sender] = true; Magic = IERC20Upgradeable(_magic); FounderVaultV1 = IBattleflyFounderVault(_founderVaultV1); FounderVaultV2 = IBattleflyFounderVault(_founderVaultV2); BattleflyStaker = IBattleflyStaker(_battleflyStake); } // ---------------- Public methods ----------------- // /** * @dev Mint comic(s) with staked founders tokens. */ function mintFounders(uint256[] memory tokenIds, uint256 id) public override nonReentrant { require(comicIdToComic[id].active, "BattleflyComic: This comic cannot be minted as it is currently paused"); require( comicIdToComic[id].mintType == 1, "BattleflyComic: This comic cannot be minted by using founders tokens" ); require( comicIdToComic[id].maxMints == 0 || totalSupply(id) + tokenIds.length <= comicIdToComic[id].maxMints, "BattleflyComic: Max amount of mints reached for this comic" ); for (uint256 i = 0; i < tokenIds.length; i++) { require( FounderVaultV1.isOwner(msg.sender, tokenIds[i]) || FounderVaultV2.isOwner(msg.sender, tokenIds[i]), "BattleflyComic: Founders token not staked by minter" ); require(!usedTokens[id][tokenIds[i]], "BattleflyComic: Founders token cannot be used twice for minting"); } _mint(msg.sender, id, tokenIds.length, ""); emit MintComicWithFounder(msg.sender, id, tokenIds); for (uint256 i = 0; i < tokenIds.length; i++) { usedTokens[id][tokenIds[i]] = true; } } /** * @dev Mint comic(s) with staked battlefly tokens. */ function mintBattlefly(uint256[] memory tokenIds, uint256 id) public override nonReentrant { require(comicIdToComic[id].active, "BattleflyComic: This comic cannot be minted as it is currently paused"); require( comicIdToComic[id].mintType == 2, "BattleflyComic: This comic cannot be minted by using battlefly tokens" ); require( comicIdToComic[id].maxMints == 0 || totalSupply(id) + tokenIds.length <= comicIdToComic[id].maxMints, "BattleflyComic: Max amount of mints reached for this comic" ); for (uint256 i = 0; i < tokenIds.length; i++) { require( BattleflyStaker.ownerOf(tokenIds[i]) == msg.sender, "BattleflyComic: Battlefly token not staked by minter" ); require(!usedTokens[id][tokenIds[i]], "BattleflyComic: Battlefly token cannot be used twice for minting"); } _mint(msg.sender, id, tokenIds.length, ""); emit MintComicWithBattlefly(msg.sender, id, tokenIds); for (uint256 i = 0; i < tokenIds.length; i++) { usedTokens[id][tokenIds[i]] = true; } } /** * @dev Mint comic(s) by paying Magic. */ function mintPaid(uint256 amount, uint256 id) public override nonReentrant { require(comicIdToComic[id].active, "BattleflyComic: This comic cannot be minted as it is currently paused"); require( comicIdToComic[id].mintType == 1 || comicIdToComic[id].mintType == 2, "BattleflyComic: This comic cannot be minted as paid mint" ); require( comicIdToComic[id].maxMints == 0 || totalSupply(id) + amount <= comicIdToComic[id].maxMints, "BattleflyComic: Max amount of mints reached for this comic" ); require( comicIdToComic[id].maxPaidMintsPerWallet == 0 || paidMints[id][msg.sender] + amount <= comicIdToComic[id].maxPaidMintsPerWallet, "BattleflyComic: Max mints per address for this comic reached" ); require( Magic.balanceOf(msg.sender) >= (amount * comicIdToComic[id].priceInWei), "BattleflyComic: Not enough MAGIC in wallet" ); Magic.transferFrom(msg.sender, address(this), amount * comicIdToComic[id].priceInWei); _mint(msg.sender, id, amount, ""); emit MintComicWithPayment(msg.sender, id, amount); paidMints[id][msg.sender] = paidMints[id][msg.sender] + amount; } /** * @dev Mint comic(s) by burning other comics. */ function burn( uint256 burnId, uint256 amount, uint256 mintId ) public override nonReentrant { require(comicIdToComic[mintId].active, "BattleflyComic: This comic cannot be minted as it is currently paused"); require(comicIdToComic[mintId].mintType == 3, "BattleflyComic: This comic cannot be used for burning"); require(comicIdToComic[burnId].burnableIn == mintId, "BattleflyComic: This comic cannot be used for burning"); require( balanceOf(msg.sender, burnId) >= comicIdToComic[burnId].burnAmount * amount, "BattleflyComic: Not enough comics in wallet to burn" ); safeTransferFrom( msg.sender, 0x000000000000000000000000000000000000dEaD, burnId, comicIdToComic[burnId].burnAmount * amount, "" ); _mint(msg.sender, mintId, amount, ""); emit MintComicByBurning(msg.sender, burnId, amount, mintId); } // ---------------- Admin methods ----------------- // /** * @dev Add a new comic cover */ function addComic(Comic memory comic) public onlyAdmin { currentComicId++; Comic memory newComic = Comic( currentComicId, comic.active, comic.mintType, comic.priceInWei, comic.burnableIn, comic.burnAmount, comic.maxPaidMintsPerWallet, comic.maxMints, comic.name, comic.uri ); comicIdToComic[currentComicId] = newComic; emit NewComicAdded( currentComicId, comic.active, comic.mintType, comic.priceInWei, comic.burnableIn, comic.burnAmount, comic.maxPaidMintsPerWallet, comic.maxMints, comic.name, comic.uri ); } /** * @dev Mint comic(s) and send them to the treasury address. */ function mintTreasury( uint256 amount, uint256 id, address treasury ) public onlyAdmin nonReentrant { require(comicIdToComic[id].active, "BattleflyComic: This comic cannot be minted as it is currently paused"); require(comicIdToComic[id].mintType == 4, "BattleflyComic: This comic cannot be minted as treasury mint"); require( comicIdToComic[id].maxMints == 0 || totalSupply(id) + amount <= comicIdToComic[id].maxMints, "BattleflyComic: Max amount of mints reached for this comic" ); _mint(treasury, id, amount, ""); emit MintComicWithTreasury(treasury, id, amount); } /** * @dev Withdraw Magic */ function withdrawMagic(uint256 amount, address receiver) public onlyAdmin { Magic.transfer(receiver, amount); } /** * @dev Update a comic URI */ function updateURI(uint256 _comicId, string memory _newUri) public override onlyAdmin { comicIdToComic[_comicId].uri = _newUri; emit UpdateComicURI(_comicId, _newUri); } /** * @dev Activate or deactivate the comic */ function activateComic(uint256 _comicId, bool _activate) public onlyAdmin { comicIdToComic[_comicId].active = _activate; emit ComicActivated(_comicId, _activate); } /** * @dev Update comic. */ function updateComic(uint256 _comicId, Comic memory _comic) public onlyAdmin { require(_comicId > 0 && _comicId <= currentComicId, "BattleflyComic: Invalid comic id"); _comic.id = _comicId; comicIdToComic[_comicId] = _comic; emit ComicUpdated( _comicId, _comic.active, _comic.mintType, _comic.priceInWei, _comic.burnableIn, _comic.burnAmount, _comic.maxPaidMintsPerWallet, _comic.maxMints, _comic.name, _comic.uri ); } /** * @dev Batch adding admin permission */ function addAdmins(address[] calldata _admins) external onlyOwner { for (uint256 i = 0; i < _admins.length; i++) { admins[_admins[i]] = true; } } /** * @dev Batch removing admin permission */ function removeAdmins(address[] calldata _admins) external onlyOwner { for (uint256 i = 0; i < _admins.length; i++) { admins[_admins[i]] = false; } } // ---------------- View methods ----------------- // /** * @dev et the URI of a comic. */ function uri(uint256 _comicId) public view virtual override(ERC1155Upgradeable, IBattleflyComic) returns (string memory) { return comicIdToComic[_comicId].uri; } // ---------------- Internal methods ----------------- // function _beforeTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal override(ERC1155Upgradeable, ERC1155SupplyUpgradeable) { super._beforeTokenTransfer(operator, from, to, ids, amounts, data); } modifier onlyAdmin() { require(admins[msg.sender], "BattleflyComic: caller is not an admin"); _; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/extensions/ERC1155Supply.sol) pragma solidity ^0.8.0; import "../ERC1155Upgradeable.sol"; import "../../../proxy/utils/Initializable.sol"; /** * @dev Extension of ERC1155 that adds tracking of total supply per id. * * Useful for scenarios where Fungible and Non-fungible tokens have to be * clearly identified. Note: While a totalSupply of 1 might mean the * corresponding is an NFT, there is no guarantees that no other token with the * same id are not going to be minted. */ abstract contract ERC1155SupplyUpgradeable is Initializable, ERC1155Upgradeable { function __ERC1155Supply_init() internal onlyInitializing { } function __ERC1155Supply_init_unchained() internal onlyInitializing { } mapping(uint256 => uint256) private _totalSupply; /** * @dev Total amount of tokens in with a given id. */ function totalSupply(uint256 id) public view virtual returns (uint256) { return _totalSupply[id]; } /** * @dev Indicates whether any token exist with a given id, or not. */ function exists(uint256 id) public view virtual returns (bool) { return ERC1155SupplyUpgradeable.totalSupply(id) > 0; } /** * @dev See {ERC1155-_beforeTokenTransfer}. */ function _beforeTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual override { super._beforeTokenTransfer(operator, from, to, ids, amounts, data); if (from == address(0)) { for (uint256 i = 0; i < ids.length; ++i) { _totalSupply[ids[i]] += amounts[i]; } } if (to == address(0)) { for (uint256 i = 0; i < ids.length; ++i) { uint256 id = ids[i]; uint256 amount = amounts[i]; uint256 supply = _totalSupply[id]; require(supply >= amount, "ERC1155: burn amount exceeds totalSupply"); unchecked { _totalSupply[id] = supply - amount; } } } } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; interface IBattleflyStaker { function stakingBattlefliesOfOwner(address user) external view returns (uint256[] memory); function bulkStakeBattlefly(uint256[] memory tokenIds) external; function bulkUnstakeBattlefly( uint256[] memory tokenIds, uint256[] memory battleflyStages, uint8 v, bytes32 r, bytes32 s ) external; function balanceOf(address owner) external view returns (uint256); function ownerOf(uint256 tokenId) external view returns (address owner); }
//SPDX-License-Identifier: Unlicense pragma solidity >=0.6.0; interface IBattleflyComic { struct Comic { uint256 id; bool active; // 1 = V1/V2 staked + buyable, 2 = battlefly staked + buyable, 3 = burn result, 4 only mintable by treasury uint256 mintType; uint256 priceInWei; uint256 burnableIn; uint256 burnAmount; uint256 maxPaidMintsPerWallet; uint256 maxMints; string name; string uri; } function uri(uint256 _comicId) external view returns (string memory); function updateURI(uint256 _comicId, string memory _newUri) external; function mintFounders(uint256[] memory tokenIds, uint256 id) external; function mintBattlefly(uint256[] memory tokenIds, uint256 id) external; function mintPaid(uint256 amount, uint256 id) external; function burn( uint256 burnId, uint256 amount, uint256 mintId ) external; event MintComicWithFounder(address indexed sender, uint256 indexed comicId, uint256[] usedFounderIds); event MintComicWithBattlefly(address indexed sender, uint256 indexed comicId, uint256[] usedBattleflyIds); event MintComicWithPayment(address indexed sender, uint256 indexed comicId, uint256 amount); event MintComicByBurning( address indexed sender, uint256 indexed comicToBeBurnt, uint256 amount, uint256 indexed comicId ); event MintComicWithTreasury(address indexed treasury, uint256 indexed comicId, uint256 amount); event NewComicAdded( uint256 indexed comicId, bool active, uint256 mintType, uint256 priceInWei, uint256 burnableIn, uint256 burnAmount, uint256 maxPaidMintsPerWallet, uint256 maxMints, string name, string uri ); event UpdateComicURI(uint256 indexed comicId, string newUri); event ComicActivated(uint256 indexed comicId, bool activated); event ComicUpdated( uint256 comicId, bool active, uint256 mintType, uint256 priceInWei, uint256 burnableIn, uint256 burnAmount, uint256 maxPaidMintsPerWallet, uint256 maxMints, string name, string uri ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/IAtlasMine.sol"; import "./interfaces/IBattleflyAtlasStaker.sol"; contract BattleflyAtlasStaker is IBattleflyAtlasStaker, Initializable, OwnableUpgradeable, ERC1155HolderUpgradeable, ERC721HolderUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; using AddressUpgradeable for address; using SafeCastUpgradeable for uint256; using SafeCastUpgradeable for int256; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; // ============================================ STATE ============================================== // ============= Global Immutable State ============== /// @notice MAGIC token /// @dev functionally immutable IERC20Upgradeable public magic; /// @notice The IAtlasMine /// @dev functionally immutable IAtlasMine public mine; // ============= Global Staking State ============== uint256 public constant ONE = 1e30; /// @notice Whether new stakes will get staked on the contract as scheduled. For emergencies bool public schedulePaused; /// @notice The total amount of staked token uint256 public totalStaked; /// @notice The total amount of share uint256 public totalShare; /// @notice All stakes currently active Stake[] public stakes; /// @notice Deposit ID of last stake. Also tracked in atlas mine uint256 public lastDepositId; /// @notice Rewards accumulated per share uint256 public accRewardsPerShare; // ============= Vault Staking State ============== mapping(address => bool) public battleflyVaults; /// @notice Each vault stake, keyed by vault contract address => deposit ID mapping(address => mapping(uint256 => VaultStake)) public vaultStake; /// @notice All deposit IDs fro a vault, enumerated mapping(address => EnumerableSetUpgradeable.UintSet) private allVaultDepositIds; /// @notice The current ID of the vault's last deposited stake mapping(address => uint256) public currentId; // ============= NFT Boosting State ============== /// @notice Holder of treasures and legions mapping(uint256 => bool) public legionsStaked; mapping(uint256 => uint256) public treasuresStaked; // ============= Operator State ============== IAtlasMine.Lock[] public allowedLocks; /// @notice Fee to contract operator. Only assessed on rewards. uint256 public fee; /// @notice Amount of fees reserved for withdrawal by the operator. uint256 public feeReserve; /// @notice Max fee the owner can ever take - 10% uint256 public constant MAX_FEE = 1000; uint256 public constant FEE_DENOMINATOR = 10000; mapping(address => mapping(uint256 => int256)) refundedFeeDebts; uint256 accRefundedFeePerShare; uint256 totalWhitelistedFeeShare; EnumerableSetUpgradeable.AddressSet whitelistedFeeVaults; mapping(address => bool) public superAdmins; /// @notice deposited but unstaked uint256 public unstakedDeposits; mapping(IAtlasMine.Lock => uint256) public unstakedDepositsByLock; address public constant TREASURY_WALLET = 0xF5411006eEfD66c213d2fd2033a1d340458B7226; /// @notice Intra-tx buffer for pending payouts uint256 public tokenBuffer; // =========================================== // ============== Post Upgrade =============== // =========================================== // ========================================== INITIALIZER =========================================== /** * @param _magic The MAGIC token address. * @param _mine The IAtlasMine contract. * Maps to a timelock for IAtlasMine deposits. */ function initialize( IERC20Upgradeable _magic, IAtlasMine _mine, IAtlasMine.Lock[] memory _allowedLocks ) external initializer { __ERC1155Holder_init(); __ERC721Holder_init(); __Ownable_init(); __ReentrancyGuard_init(); magic = _magic; mine = _mine; allowedLocks = _allowedLocks; fee = 1000; // Approve the mine magic.safeApprove(address(mine), 2**256 - 1); // approveNFTs(); } // ======================================== VAULT OPERATIONS ======================================== /** * @notice Make a new deposit into the Staker. The Staker will collect * the tokens, to be later staked in atlas mine by the owner, * according to the stake/unlock schedule. * @dev Specified amount of token must be approved by the caller. * * @param _amount The amount of tokens to deposit. */ function deposit(uint256 _amount, IAtlasMine.Lock lock) public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) { require(!schedulePaused, "new staking paused"); _updateRewards(); // Collect tokens uint256 newDepositId = _deposit(_amount, msg.sender, lock); magic.safeTransferFrom(msg.sender, address(this), _amount); return (newDepositId); } function _deposit( uint256 _amount, address _vault, IAtlasMine.Lock lock ) internal returns (uint256) { require(_amount > 0, "Deposit amount 0"); bool validLock = false; for (uint256 i = 0; i < allowedLocks.length; i++) { if (allowedLocks[i] == lock) { validLock = true; break; } } require(validLock, "Lock time not allowed"); // Add vault stake uint256 newDepositId = ++currentId[_vault]; allVaultDepositIds[_vault].add(newDepositId); VaultStake storage s = vaultStake[_vault][newDepositId]; s.amount = _amount; (uint256 boost, uint256 lockTime) = getLockBoost(lock); uint256 share = (_amount * (100e16 + boost)) / 100e16; uint256 vestingTime = mine.getVestingTime(lock); s.unlockAt = block.timestamp + lockTime + vestingTime + 1 days; s.rewardDebt = ((share * accRewardsPerShare) / ONE).toInt256(); s.lock = lock; // Update global accounting totalStaked += _amount; totalShare += share; if (whitelistedFeeVaults.contains(_vault)) { totalWhitelistedFeeShare += share; refundedFeeDebts[_vault][newDepositId] = ((share * accRefundedFeePerShare) / ONE).toInt256(); } // MAGIC tokens sit in contract. Added to pending stakes unstakedDeposits += _amount; unstakedDepositsByLock[lock] += _amount; emit VaultDeposit(_vault, newDepositId, _amount, s.unlockAt, s.lock); return newDepositId; } /** * @notice Withdraw a deposit from the Staker contract. Calculates * pro rata share of accumulated MAGIC and distributes any * earned rewards in addition to original deposit. * There must be enough unlocked tokens to withdraw. * * @param depositId The ID of the deposit to withdraw from. * */ function withdraw(uint256 depositId) public virtual override onlyBattleflyVaultOrOwner nonReentrant { // Distribute tokens _updateRewards(); VaultStake storage s = vaultStake[msg.sender][depositId]; require(s.amount > 0, "No deposit"); require(block.timestamp >= s.unlockAt, "Deposit locked"); uint256 payout = _withdraw(s, depositId); magic.safeTransfer(msg.sender, payout); } /** * @notice Withdraw all eligible deposits from the staker contract. * Will skip any deposits not yet unlocked. Will also * distribute rewards for all stakes via 'withdraw'. * */ function withdrawAll() public virtual override onlyBattleflyVaultOrOwner nonReentrant { // Distribute tokens _updateRewards(); uint256[] memory depositIds = allVaultDepositIds[msg.sender].values(); for (uint256 i = 0; i < depositIds.length; i++) { VaultStake storage s = vaultStake[msg.sender][depositIds[i]]; if (s.amount > 0 && s.unlockAt > 0 && s.unlockAt <= block.timestamp) { tokenBuffer += _withdraw(s, depositIds[i]); } } magic.safeTransfer(msg.sender, tokenBuffer); tokenBuffer = 0; } /** * @dev Logic for withdrawing a deposit. Calculates pro rata share of * accumulated MAGIC and dsitributed any earned rewards in addition * to original deposit. * * @dev An _amount argument larger than the total deposit amount will * withdraw the entire deposit. * * @param s The VaultStake struct to withdraw from. * @param depositId The ID of the deposit to withdraw from (for event). */ function _withdraw(VaultStake storage s, uint256 depositId) internal returns (uint256 payout) { uint256 _amount = s.amount; // Unstake if we need to to ensure we can withdraw (uint256 boost, ) = getLockBoost(s.lock); uint256 share = (_amount * (100e16 + boost)) / 100e16; int256 accumulatedRewards = ((share * accRewardsPerShare) / ONE).toInt256(); if (whitelistedFeeVaults.contains(msg.sender)) { accumulatedRewards += ((share * accRefundedFeePerShare) / ONE).toInt256(); accumulatedRewards -= refundedFeeDebts[msg.sender][depositId]; totalWhitelistedFeeShare -= share; refundedFeeDebts[msg.sender][depositId] = 0; } uint256 reward = (accumulatedRewards - s.rewardDebt).toUint256(); payout = _amount + reward; // // Update vault accounting // s.amount -= _amount; // s.rewardDebt = 0; ///comment Archethect: Consider deleting the VaultStake object for gas optimization. s.unlockAt and s.lock can be zeroed as well. delete vaultStake[msg.sender][depositId]; // Update global accounting totalStaked -= _amount; totalShare -= share; // If we need to unstake, unstake until we have enough if (payout > _totalUsableMagic()) { _unstakeToTarget(payout - _totalUsableMagic()); } emit VaultWithdraw(msg.sender, depositId, _amount, reward); } /** * @notice Claim rewards without unstaking. Will fail if there * are not enough tokens in the contract to claim rewards. * Does not attempt to unstake. * * @param depositId The ID of the deposit to claim rewards from. * */ function claim(uint256 depositId) public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) { _updateRewards(); VaultStake storage s = vaultStake[msg.sender][depositId]; require(s.amount > 0, "No deposit"); uint256 reward = _claim(s, depositId); magic.safeTransfer(msg.sender, reward); return reward; } /** * @notice Claim all possible rewards from the staker contract. * Will apply to both locked and unlocked deposits. * */ function claimAll() public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) { _updateRewards(); uint256[] memory depositIds = allVaultDepositIds[msg.sender].values(); uint256 totalReward = 0; for (uint256 i = 0; i < depositIds.length; i++) { VaultStake storage s = vaultStake[msg.sender][depositIds[i]]; uint256 reward = _claim(s, depositIds[i]); tokenBuffer += reward; } magic.safeTransfer(msg.sender, tokenBuffer); totalReward = tokenBuffer; tokenBuffer = 0; return totalReward; } /** * @notice Claim all possible rewards from the staker contract then restake. * Will apply to both locked and unlocked deposits. * */ function claimAllAndRestake(IAtlasMine.Lock lock) public onlyBattleflyVaultOrOwner nonReentrant returns (uint256) { _updateRewards(); uint256[] memory depositIds = allVaultDepositIds[msg.sender].values(); uint256 totalReward = 0; for (uint256 i = 0; i < depositIds.length; i++) { VaultStake storage s = vaultStake[msg.sender][depositIds[i]]; uint256 reward = _claim(s, depositIds[i]); tokenBuffer += reward; } _deposit(tokenBuffer, msg.sender, lock); tokenBuffer = 0; return totalReward; } /** * @dev Logic for claiming rewards on a deposit. Calculates pro rata share of * accumulated MAGIC and dsitributed any earned rewards in addition * to original deposit. * * @param s The VaultStake struct to claim from. * @param depositId The ID of the deposit to claim from (for event). */ function _claim(VaultStake storage s, uint256 depositId) internal returns (uint256) { // Update accounting (uint256 boost, ) = getLockBoost(s.lock); uint256 share = (s.amount * (100e16 + boost)) / 100e16; int256 accumulatedRewards = ((share * accRewardsPerShare) / ONE).toInt256(); uint256 reward = (accumulatedRewards - s.rewardDebt).toUint256(); if (whitelistedFeeVaults.contains(msg.sender)) { int256 accumulatedRefundedFee = ((share * accRefundedFeePerShare) / ONE).toInt256(); reward += accumulatedRefundedFee.toUint256(); reward -= refundedFeeDebts[msg.sender][depositId].toUint256(); refundedFeeDebts[msg.sender][depositId] = accumulatedRefundedFee; } s.rewardDebt = accumulatedRewards; // Unstake if we need to to ensure we can withdraw if (reward > _totalUsableMagic()) { _unstakeToTarget(reward - _totalUsableMagic()); } require(reward <= _totalUsableMagic(), "Not enough rewards to claim"); emit VaultClaim(msg.sender, depositId, reward); return reward; } // ======================================= SUPER ADMIN OPERATIONS ======================================== /** * @notice Stake a Treasure owned by the superAdmin into the Atlas Mine. * Staked treasures will boost all vault deposits. * @dev Any treasure must be approved for withdrawal by the caller. * * @param _tokenId The tokenId of the specified treasure. * @param _amount The amount of treasures to stake. */ function stakeTreasure(uint256 _tokenId, uint256 _amount) external onlySuperAdminOrOwner { address treasureAddr = mine.treasure(); require(IERC1155Upgradeable(treasureAddr).balanceOf(msg.sender, _tokenId) >= _amount, "Not enough treasures"); treasuresStaked[_tokenId] += _amount; // First withdraw and approve IERC1155Upgradeable(treasureAddr).safeTransferFrom(msg.sender, address(this), _tokenId, _amount, bytes("")); mine.stakeTreasure(_tokenId, _amount); uint256 boost = mine.boosts(address(this)); emit StakeNFT(msg.sender, treasureAddr, _tokenId, _amount, boost); } /** * @notice Unstake a Treasure from the Atlas Mine adn transfer to receiver. * * @param _receiver The receiver . * @param _tokenId The tokenId of the specified treasure. * @param _amount The amount of treasures to stake. */ function unstakeTreasure( address _receiver, uint256 _tokenId, uint256 _amount ) external onlySuperAdminOrOwner { require(treasuresStaked[_tokenId] >= _amount, "Not enough treasures"); treasuresStaked[_tokenId] -= _amount; address treasureAddr = mine.treasure(); mine.unstakeTreasure(_tokenId, _amount); IERC1155Upgradeable(treasureAddr).safeTransferFrom(address(this), _receiver, _tokenId, _amount, bytes("")); uint256 boost = mine.boosts(address(this)); emit UnstakeNFT(_receiver, treasureAddr, _tokenId, _amount, boost); } /** * @notice Stake a Legion owned by the superAdmin into the Atlas Mine. * Staked legions will boost all vault deposits. * @dev Any legion be approved for withdrawal by the caller. * * @param _tokenId The tokenId of the specified legion. */ function stakeLegion(uint256 _tokenId) external onlySuperAdminOrOwner { address legionAddr = mine.legion(); require(IERC721Upgradeable(legionAddr).ownerOf(_tokenId) == msg.sender, "Not owner of legion"); legionsStaked[_tokenId] = true; IERC721Upgradeable(legionAddr).safeTransferFrom(msg.sender, address(this), _tokenId); mine.stakeLegion(_tokenId); uint256 boost = mine.boosts(address(this)); emit StakeNFT(msg.sender, legionAddr, _tokenId, 1, boost); } /** * @notice Unstake a Legion from the Atlas Mine and return it to the superAdmin. * * @param _tokenId The tokenId of the specified legion. */ function unstakeLegion(address _receiver, uint256 _tokenId) external onlySuperAdminOrOwner { require(legionsStaked[_tokenId], "No legion"); address legionAddr = mine.legion(); delete legionsStaked[_tokenId]; mine.unstakeLegion(_tokenId); // Distribute to superAdmin IERC721Upgradeable(legionAddr).safeTransferFrom(address(this), _receiver, _tokenId); uint256 boost = mine.boosts(address(this)); emit UnstakeNFT(_receiver, legionAddr, _tokenId, 1, boost); } /** * @notice Stake any pending stakes before the current day. Callable * by anybody. Any pending stakes will unlock according * to the time this method is called, and the contract's defined * lock time. */ function stakeScheduled() external virtual override onlySuperAdminOrOwner { for (uint256 i = 0; i < allowedLocks.length; i++) { IAtlasMine.Lock lock = allowedLocks[i]; _stakeInMine(unstakedDepositsByLock[lock], lock); unstakedDepositsByLock[lock] = 0; } unstakedDeposits = 0; } /** * @notice Unstake everything eligible for unstaking from Atlas Mine. * Callable by owner. Should only be used in case of emergency * or migration to a new contract, or if there is a need to service * an unexpectedly large amount of withdrawals. * * If unlockAll is set to true in the Atlas Mine, this can withdraw * all stake. */ function unstakeAllFromMine() external override onlySuperAdminOrOwner { // Unstake everything eligible _updateRewards(); for (uint256 i = 0; i < stakes.length; i++) { Stake memory s = stakes[i]; if (s.unlockAt > block.timestamp) { continue; } // Withdraw position - auto-harvest mine.withdrawPosition(s.depositId, s.amount); } // Only check for removal after, so we don't mutate while looping _removeZeroStakes(); } /** * @notice Let owner unstake a specified amount as needed to make sure the contract is funded. * Can be used to facilitate expected future withdrawals. * * @param target The amount of tokens to reclaim from the mine. */ function unstakeToTarget(uint256 target) external override onlySuperAdminOrOwner { _updateRewards(); _unstakeToTarget(target); } /** * @notice Withdraw any accumulated reward fees to the treasury */ function withdrawFeesToTreasury() external virtual onlySuperAdminOrOwner { uint256 amount = feeReserve; feeReserve = 0; magic.safeTransfer(TREASURY_WALLET, amount); emit WithdrawFeesToTreasury(amount); } function stakeBackFeeTreasury(IAtlasMine.Lock lock) external virtual onlySuperAdminOrOwner { uint256 amount = feeReserve; feeReserve = 0; emit WithdrawFeesToTreasury(amount); // magic.safeTransfer(TREASURY_WALLET, amount); _deposit(amount, TREASURY_WALLET, lock); } /** * @notice Whitelist vault from fees. * * @param _vault Vault address. * @param isSet Whether to enable or disable the vault whitelist. */ function setFeeWhitelistVault(address _vault, bool isSet) external onlyOwner { require(_vault != address(0), "Invalid Vault"); if (isSet) { whitelistedFeeVaults.add(_vault); totalWhitelistedFeeShare += totalShareOf(_vault); } else { whitelistedFeeVaults.remove(_vault); totalWhitelistedFeeShare -= totalShareOf(_vault); } emit SetFeeWhitelistVault(_vault, isSet); } // ======================================= OWNER OPERATIONS ======================================= function setBattleflyVault(address _vaultAddress, bool isSet) external onlyOwner { require(_vaultAddress != address(0), "Invalid vault"); if (isSet) { require(battleflyVaults[_vaultAddress] == false, "Vault already set"); battleflyVaults[_vaultAddress] = isSet; } else { require(allVaultDepositIds[_vaultAddress].length() == 0, "Vault is still active"); delete battleflyVaults[_vaultAddress]; } emit SetBattleflyVault(_vaultAddress, isSet); } /** * @notice Change the designated superAdmin, the address where treasures and * legions are held. Staked NFTs can only be * withdrawn to the current superAdmin address, regardless of which * address the superAdmin was set to when it was staked. * * @param _superAdmin The new superAdmin address. * @param isSet Whether to enable or disable the superAdmin address. */ function setBoostAdmin(address _superAdmin, bool isSet) external override onlyOwner { require(_superAdmin != address(0), "Invalid superAdmin"); superAdmins[_superAdmin] = isSet; } /** * @notice Change the designated super admin, who manage the fee reverse * * @param _superAdmin The new superAdmin address. * @param isSet Whether to enable or disable the super admin address. */ function setSuperAdmin(address _superAdmin, bool isSet) external onlyOwner { require(_superAdmin != address(0), "Invalid address"); superAdmins[_superAdmin] = isSet; } /** * @notice Approve treasures and legions for withdrawal from the atlas mine. * Called on startup, and should be called again in case contract * addresses for treasures and legions ever change. * */ function approveNFTs() public override onlyOwner { address treasureAddr = mine.treasure(); IERC1155Upgradeable(treasureAddr).setApprovalForAll(address(mine), true); address legionAddr = mine.legion(); IERC1155Upgradeable(legionAddr).setApprovalForAll(address(mine), true); } /** * @notice EMERGENCY ONLY - toggle pausing new scheduled stakes. * If on, vaults can deposit, but stakes won't go to Atlas Mine. * Can be used in case of Atlas Mine issues or forced migration * to new contract. */ function toggleSchedulePause(bool paused) external virtual override onlyOwner { schedulePaused = paused; emit StakingPauseToggle(paused); } // ======================================== VIEW FUNCTIONS ========================================= function getLockBoost(IAtlasMine.Lock _lock) public pure virtual returns (uint256 boost, uint256 timelock) { if (_lock == IAtlasMine.Lock.twoWeeks) { // 10% return (10e16, 14 days); } else if (_lock == IAtlasMine.Lock.oneMonth) { // 25% return (25e16, 30 days); } else if (_lock == IAtlasMine.Lock.threeMonths) { // 80% return (80e16, 13 weeks); } else if (_lock == IAtlasMine.Lock.sixMonths) { // 180% return (180e16, 26 weeks); } else if (_lock == IAtlasMine.Lock.twelveMonths) { // 400% return (400e16, 365 days); } else { revert("Invalid lock value"); } } /** * @notice Returns all magic either unstaked, staked, or pending rewards in Atlas Mine. * Best proxy for TVL. * * @return total The total amount of MAGIC in the staker. */ function totalMagic() external view override returns (uint256) { return _totalControlledMagic() + mine.pendingRewardsAll(address(this)); } /** * @notice Returns all magic that has been deposited, but not staked, and is eligible * to be staked (deposit time < current day). * * @return total The total amount of MAGIC that can be withdrawn. */ function totalWithdrawableMagic() external view override returns (uint256) { uint256 totalPendingRewards; // IAtlasMine attempts to divide by 0 if there are no deposits try mine.pendingRewardsAll(address(this)) returns (uint256 _pending) { totalPendingRewards = _pending; } catch Panic(uint256) { totalPendingRewards = 0; } return _totalUsableMagic() + totalPendingRewards; } /** * @notice Returns the details of a vault stake. * * @return vaultStake The details of a vault stake. */ function getVaultStake(address vault, uint256 depositId) external view override returns (VaultStake memory) { return vaultStake[vault][depositId]; } /** * @notice Returns the pending, claimable rewards for a deposit. * @dev This does not update rewards, so out of date if rewards not recently updated. * Needed to maintain 'view' function type. * * @param vault The vault to check rewards for. * @param depositId The specific deposit to check rewards for. * * @return reward The total amount of MAGIC reward pending. */ function pendingRewards(address vault, uint256 depositId) public view override returns (uint256 reward) { if (totalShare == 0) { return 0; } VaultStake storage s = vaultStake[vault][depositId]; (uint256 boost, ) = getLockBoost(s.lock); uint256 share = (s.amount * (100e16 + boost)) / 100e16; uint256 unupdatedReward = mine.pendingRewardsAll(address(this)); (uint256 founderReward, , uint256 feeRefund) = _calculateHarvestRewardFee(unupdatedReward); uint256 realAccRewardsPerShare = accRewardsPerShare + (founderReward * ONE) / totalShare; uint256 accumulatedRewards = (share * realAccRewardsPerShare) / ONE; if (whitelistedFeeVaults.contains(vault) && totalWhitelistedFeeShare > 0) { uint256 realAccRefundedFeePerShare = accRefundedFeePerShare + (feeRefund * ONE) / totalWhitelistedFeeShare; uint256 accumulatedRefundedFee = (share * realAccRefundedFeePerShare) / ONE; accumulatedRewards = accumulatedRewards + accumulatedRefundedFee; accumulatedRewards -= refundedFeeDebts[vault][depositId].toUint256(); } reward = accumulatedRewards - s.rewardDebt.toUint256(); } /** * @notice Returns the pending, claimable rewards for all of a vault's deposits. * @dev This does not update rewards, so out of date if rewards not recently updated. * Needed to maintain 'view' function type. * * @param vault The vault to check rewards for. * * @return reward The total amount of MAGIC reward pending. */ function pendingRewardsAll(address vault) external view override returns (uint256 reward) { uint256[] memory depositIds = allVaultDepositIds[vault].values(); for (uint256 i = 0; i < depositIds.length; i++) { reward += pendingRewards(vault, depositIds[i]); } } /** * @notice Returns the total Share of a vault. * * @param vault The vault to check rewards for. * * @return _totalShare The total share of a vault. */ function totalShareOf(address vault) public view returns (uint256 _totalShare) { uint256[] memory depositIds = allVaultDepositIds[vault].values(); for (uint256 i = 0; i < depositIds.length; i++) { (uint256 boost, ) = getLockBoost(vaultStake[vault][depositIds[i]].lock); uint256 share = (vaultStake[vault][depositIds[i]].amount * (100e16 + boost)) / 100e16; _totalShare += share; } } // ============================================ HELPERS ============================================ /** * @dev Stake tokens held by staker in the Atlas Mine, according to * the predefined lock value. Schedules for staking will be managed by a queue. * * @param _amount Number of tokens to stake */ function _stakeInMine(uint256 _amount, IAtlasMine.Lock lock) internal { require(_amount <= _totalUsableMagic(), "Not enough funds"); uint256 depositId = ++lastDepositId; (, uint256 lockTime) = getLockBoost(lock); uint256 vestingPeriod = mine.getVestingTime(lock); uint256 unlockAt = block.timestamp + lockTime + vestingPeriod; stakes.push(Stake({ amount: _amount, unlockAt: unlockAt, depositId: depositId })); mine.deposit(_amount, lock); } /** * @dev Unstakes until we have enough unstaked tokens to meet a specific target. * Used to make sure we can service withdrawals. * * @param target The amount of tokens we want to have unstaked. */ function _unstakeToTarget(uint256 target) internal { uint256 unstaked = 0; for (uint256 i = 0; i < stakes.length; i++) { Stake memory s = stakes[i]; if (s.unlockAt > block.timestamp && !mine.unlockAll()) { // This stake is not unlocked - stop looking continue; } // Withdraw position - auto-harvest uint256 preclaimBalance = _totalUsableMagic(); uint256 targetLeft = target - unstaked; uint256 amount = targetLeft > s.amount ? s.amount : targetLeft; // Do not harvest rewards - if this is running, we've already // harvested in the same fn call mine.withdrawPosition(s.depositId, amount); uint256 postclaimBalance = _totalUsableMagic(); // Increment amount unstaked unstaked += postclaimBalance - preclaimBalance; if (unstaked >= target) { // We unstaked enough break; } } require(unstaked >= target, "Cannot unstake enough"); require(_totalUsableMagic() >= target, "Not enough in contract after unstaking"); // Only check for removal after, so we don't mutate while looping _removeZeroStakes(); } /** * @dev Harvest rewards from the IAtlasMine and send them back to * this contract. * * @return earned The amount of rewards earned for depositors, minus the fee. * @return feeEearned The amount of fees earned for the contract operator. */ function _harvestMine() internal returns (uint256, uint256) { uint256 preclaimBalance = magic.balanceOf(address(this)); try mine.harvestAll() { uint256 postclaimBalance = magic.balanceOf(address(this)); uint256 earned = postclaimBalance - preclaimBalance; // Reserve the 'fee' amount of what is earned (, uint256 feeEarned, uint256 feeRefunded) = _calculateHarvestRewardFee(earned); feeReserve += feeEarned - feeRefunded; emit MineHarvest(earned - feeEarned, feeEarned - feeRefunded, feeRefunded); return (earned - feeEarned, feeRefunded); } catch { // Failed because of reward debt calculation - should be 0 return (0, 0); } } function _calculateHarvestRewardFee(uint256 earned) internal view returns ( uint256, uint256, uint256 ) { uint256 feeEarned = (earned * fee) / FEE_DENOMINATOR; uint256 accFeePerShare = (feeEarned * ONE) / totalShare; uint256 feeRefunded = (accFeePerShare * totalWhitelistedFeeShare) / ONE; return (earned - feeEarned, feeEarned, feeRefunded); } /** * @dev Harvest rewards from the mine so that stakers can claim. * Recalculate how many rewards are distributed to each share. */ function _updateRewards() internal { if (totalStaked == 0 || totalShare == 0) return; (uint256 newRewards, uint256 feeRefunded) = _harvestMine(); accRewardsPerShare += (newRewards * ONE) / totalShare; if (totalWhitelistedFeeShare > 0) accRefundedFeePerShare += (feeRefunded * ONE) / totalWhitelistedFeeShare; } /** * @dev After mutating a stake (by withdrawing fully or partially), * get updated data from the staking contract, and update the stake amounts * * @param stakeIndex The index of the stake in the Stakes storage array. * * @return amount The current, updated amount of the stake. */ function _updateStakeDepositAmount(uint256 stakeIndex) internal returns (uint256) { Stake storage s = stakes[stakeIndex]; (, uint256 depositAmount, , , , , ) = mine.userInfo(address(this), s.depositId); s.amount = depositAmount; return s.amount; } /** * @dev Find stakes with zero deposit amount and remove them from tracking. * Uses recursion to stop from mutating an array we are currently looping over. * If a zero stake is found, it is removed, and the function is restarted, * such that it is always working from a 'clean' array. * */ function _removeZeroStakes() internal { bool shouldRecurse = stakes.length > 0; for (uint256 i = 0; i < stakes.length; i++) { _updateStakeDepositAmount(i); Stake storage s = stakes[i]; if (s.amount == 0) { _removeStake(i); // Stop looping and start again - we will skip // out of the look and recurse break; } if (i == stakes.length - 1) { // We didn't remove anything, so stop recursing shouldRecurse = false; } } if (shouldRecurse) { _removeZeroStakes(); } } /** * @dev Calculate total amount of MAGIC usable by the contract. * 'Usable' means available for either withdrawal or re-staking. * Counts unstaked magic less fee reserve. * * @return amount The amount of usable MAGIC. */ function _totalUsableMagic() internal view returns (uint256) { // Current magic held in contract uint256 unstaked = magic.balanceOf(address(this)); return unstaked - tokenBuffer - feeReserve; } /** * @dev Calculate total amount of MAGIC under control of the contract. * Counts staked and unstaked MAGIC. Does _not_ count accumulated * but unclaimed rewards. * * @return amount The total amount of MAGIC under control of the contract. */ function _totalControlledMagic() internal view returns (uint256) { // Current magic staked in mine uint256 staked = 0; for (uint256 i = 0; i < stakes.length; i++) { staked += stakes[i].amount; } return staked + _totalUsableMagic(); } /** * @dev Remove a tracked stake from any position in the stakes array. * Used when a stake is no longer relevant i.e. fully withdrawn. * Mutates the Stakes array in storage. * * @param index The index of the stake to remove. */ function _removeStake(uint256 index) internal { if (index >= stakes.length) return; for (uint256 i = index; i < stakes.length - 1; i++) { stakes[i] = stakes[i + 1]; } delete stakes[stakes.length - 1]; stakes.pop(); } modifier onlySuperAdminOrOwner() { require(msg.sender == owner() || superAdmins[msg.sender], "Not Super Admin"); _; } modifier onlyBattleflyVaultOrOwner() { require(msg.sender == owner() || battleflyVaults[msg.sender], "Not BattleflyVault"); _; } }
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.2; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract MagicTokenContract is ERC20, Ownable { mapping(address => bool) private adminAccess; constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {} function setAdminAccess(address user, bool access) external onlyOwner { adminAccess[user] = access; } function mint(uint256 amount, address receiver) external onlyAdminAccess { _mint(receiver, amount); } modifier onlyAdminAccess() { require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access"); _; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.2; import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Context.sol"; contract ERC721BulkTransfer is Context { mapping(address => bool) private adminAccess; constructor() {} function bulkTransferERC721( uint256[] memory tokenIds, address[] memory receivers, address tokenAddress ) external { IERC721 token = IERC721(tokenAddress); for (uint256 i = 0; i < tokenIds.length; i++) { token.transferFrom(_msgSender(), receivers[i], tokenIds[i]); } } function transferWithAmount( address receiver, uint256 amount, address tokenAddress ) external { IERC721Enumerable token = IERC721Enumerable(tokenAddress); uint256 balance = token.balanceOf(_msgSender()); require(amount <= balance, "Not enough balance"); for (uint256 i = 0; i < amount; i++) { token.transferFrom(_msgSender(), receiver, token.tokenOfOwnerByIndex(_msgSender(), 0)); } } function bulkTransferWithAmount( address[] memory receivers, uint256[] memory amounts, address tokenAddress ) external { IERC721Enumerable token = IERC721Enumerable(tokenAddress); require(receivers.length == amounts.length, "Wrong input"); for (uint256 i = 0; i < receivers.length; i++) { for (uint256 j = 0; j < amounts[i]; j++) { token.transferFrom(_msgSender(), receivers[i], token.tokenOfOwnerByIndex(_msgSender(), 0)); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol) pragma solidity ^0.8.0; import "../IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Enumerable is IERC721 { /** * @dev Returns the total amount of tokens stored by the contract. */ function totalSupply() external view returns (uint256); /** * @dev Returns a token ID owned by `owner` at a given `index` of its token list. * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. */ function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); /** * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. * Use along with {totalSupply} to enumerate all tokens. */ function tokenByIndex(uint256 index) external view returns (uint256); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.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 be 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: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * 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 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 // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/ERC721.sol) pragma solidity ^0.8.0; import "./IERC721.sol"; import "./IERC721Receiver.sol"; import "./extensions/IERC721Metadata.sol"; import "../../utils/Address.sol"; import "../../utils/Context.sol"; import "../../utils/Strings.sol"; import "../../utils/introspection/ERC165.sol"; /** * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including * the Metadata extension, but not including the Enumerable extension, which is available separately as * {ERC721Enumerable}. */ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata { using Address for address; using Strings for uint256; // Token name string private _name; // Token symbol string private _symbol; // Mapping from token ID to owner address mapping(uint256 => address) private _owners; // Mapping owner address to token count mapping(address => uint256) private _balances; // Mapping from token ID to approved address mapping(uint256 => address) private _tokenApprovals; // Mapping from owner to operator approvals mapping(address => mapping(address => bool)) private _operatorApprovals; /** * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC721Metadata).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC721-balanceOf}. */ function balanceOf(address owner) public view virtual override returns (uint256) { require(owner != address(0), "ERC721: balance query for the zero address"); return _balances[owner]; } /** * @dev See {IERC721-ownerOf}. */ function ownerOf(uint256 tokenId) public view virtual override returns (address) { address owner = _owners[tokenId]; require(owner != address(0), "ERC721: owner query for nonexistent token"); return owner; } /** * @dev See {IERC721Metadata-name}. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev See {IERC721Metadata-symbol}. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); string memory baseURI = _baseURI(); return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; } /** * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each * token will be the concatenation of the `baseURI` and the `tokenId`. Empty * by default, can be overridden in child contracts. */ function _baseURI() internal view virtual returns (string memory) { return ""; } /** * @dev See {IERC721-approve}. */ function approve(address to, uint256 tokenId) public virtual override { address owner = ERC721.ownerOf(tokenId); require(to != owner, "ERC721: approval to current owner"); require( _msgSender() == owner || isApprovedForAll(owner, _msgSender()), "ERC721: approve caller is not owner nor approved for all" ); _approve(to, tokenId); } /** * @dev See {IERC721-getApproved}. */ function getApproved(uint256 tokenId) public view virtual override returns (address) { require(_exists(tokenId), "ERC721: approved query for nonexistent token"); return _tokenApprovals[tokenId]; } /** * @dev See {IERC721-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual override { _setApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC721-isApprovedForAll}. */ function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { return _operatorApprovals[owner][operator]; } /** * @dev See {IERC721-transferFrom}. */ function transferFrom( address from, address to, uint256 tokenId ) public virtual override { //solhint-disable-next-line max-line-length require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _transfer(from, to, tokenId); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 tokenId ) public virtual override { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes memory _data ) public virtual override { require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _safeTransfer(from, to, tokenId, _data); } /** * @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. * * `_data` is additional data, it has no specified format and it is sent in call to `to`. * * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. * implement alternative mechanisms to perform token transfer, such as signature-based. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeTransfer( address from, address to, uint256 tokenId, bytes memory _data ) internal virtual { _transfer(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } /** * @dev Returns whether `tokenId` exists. * * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. * * Tokens start existing when they are minted (`_mint`), * and stop existing when they are burned (`_burn`). */ function _exists(uint256 tokenId) internal view virtual returns (bool) { return _owners[tokenId] != address(0); } /** * @dev Returns whether `spender` is allowed to manage `tokenId`. * * Requirements: * * - `tokenId` must exist. */ function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { require(_exists(tokenId), "ERC721: operator query for nonexistent token"); address owner = ERC721.ownerOf(tokenId); return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender); } /** * @dev Safely mints `tokenId` and transfers it to `to`. * * Requirements: * * - `tokenId` must not exist. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeMint(address to, uint256 tokenId) internal virtual { _safeMint(to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeMint( address to, uint256 tokenId, bytes memory _data ) internal virtual { _mint(to, tokenId); require( _checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer" ); } /** * @dev Mints `tokenId` and transfers it to `to`. * * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible * * Requirements: * * - `tokenId` must not exist. * - `to` cannot be the zero address. * * Emits a {Transfer} event. */ function _mint(address to, uint256 tokenId) internal virtual { require(to != address(0), "ERC721: mint to the zero address"); require(!_exists(tokenId), "ERC721: token already minted"); _beforeTokenTransfer(address(0), to, tokenId); _balances[to] += 1; _owners[tokenId] = to; emit Transfer(address(0), to, tokenId); _afterTokenTransfer(address(0), to, tokenId); } /** * @dev Destroys `tokenId`. * The approval is cleared when the token is burned. * * Requirements: * * - `tokenId` must exist. * * Emits a {Transfer} event. */ function _burn(uint256 tokenId) internal virtual { address owner = ERC721.ownerOf(tokenId); _beforeTokenTransfer(owner, address(0), tokenId); // Clear approvals _approve(address(0), tokenId); _balances[owner] -= 1; delete _owners[tokenId]; emit Transfer(owner, address(0), tokenId); _afterTokenTransfer(owner, address(0), tokenId); } /** * @dev Transfers `tokenId` from `from` to `to`. * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. * * Requirements: * * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * * Emits a {Transfer} event. */ function _transfer( address from, address to, uint256 tokenId ) internal virtual { require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); require(to != address(0), "ERC721: transfer to the zero address"); _beforeTokenTransfer(from, to, tokenId); // Clear approvals from the previous owner _approve(address(0), tokenId); _balances[from] -= 1; _balances[to] += 1; _owners[tokenId] = to; emit Transfer(from, to, tokenId); _afterTokenTransfer(from, to, tokenId); } /** * @dev Approve `to` to operate on `tokenId` * * Emits a {Approval} event. */ function _approve(address to, uint256 tokenId) internal virtual { _tokenApprovals[tokenId] = to; emit Approval(ERC721.ownerOf(tokenId), to, tokenId); } /** * @dev Approve `operator` to operate on all of `owner` tokens * * Emits a {ApprovalForAll} event. */ function _setApprovalForAll( address owner, address operator, bool approved ) internal virtual { require(owner != operator, "ERC721: approve to caller"); _operatorApprovals[owner][operator] = approved; emit ApprovalForAll(owner, operator, approved); } /** * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. * The call is not executed if the target address is not a contract. * * @param from address representing the previous owner of the given token ID * @param to target address that will receive the tokens * @param tokenId uint256 ID of the token to be transferred * @param _data bytes optional data to send along with the call * @return bool whether the call correctly returned the expected magic value */ function _checkOnERC721Received( address from, address to, uint256 tokenId, bytes memory _data ) private returns (bool) { if (to.isContract()) { try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) { return retval == IERC721Receiver.onERC721Received.selector; } catch (bytes memory reason) { if (reason.length == 0) { revert("ERC721: transfer to non ERC721Receiver implementer"); } else { assembly { revert(add(32, reason), mload(reason)) } } } } else { return true; } } /** * @dev Hook that is called before any token transfer. This includes minting * and burning. * * Calling conditions: * * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be * transferred to `to`. * - When `from` is zero, `tokenId` will be minted for `to`. * - When `to` is zero, ``from``'s `tokenId` will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 tokenId ) internal virtual {} }
// 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/extensions/IERC721Metadata.sol) pragma solidity ^0.8.0; import "../IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Metadata is IERC721 { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) pragma solidity ^0.8.0; /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev _Available since v3.1._ */ interface IERC1155Receiver is IERC165 { /** * @dev Handles the receipt of a single ERC1155 token type. This function is * called at the end of a `safeTransferFrom` after the balance has been updated. * * NOTE: To accept the transfer, this must return * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` * (i.e. 0xf23a6e61, or its own function selector). * * @param operator The address which initiated the transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param id The ID of the token being transferred * @param value The amount of tokens being transferred * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4); /** * @dev Handles the receipt of a multiple ERC1155 token types. This function * is called at the end of a `safeBatchTransferFrom` after the balances have * been updated. * * NOTE: To accept the transfer(s), this must return * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` * (i.e. 0xbc197c81, or its own function selector). * * @param operator The address which initiated the batch transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param ids An array containing ids of each token being transferred (order and length must match values array) * @param values An array containing amounts of each token being transferred (order and length must match ids array) * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/ERC1155.sol) pragma solidity ^0.8.0; import "./IERC1155.sol"; import "./IERC1155Receiver.sol"; import "./extensions/IERC1155MetadataURI.sol"; import "../../utils/Address.sol"; import "../../utils/Context.sol"; import "../../utils/introspection/ERC165.sol"; /** * @dev Implementation of the basic standard multi-token. * See https://eips.ethereum.org/EIPS/eip-1155 * Originally based on code by Enjin: https://github.com/enjin/erc-1155 * * _Available since v3.1._ */ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { using Address for address; // Mapping from token ID to account balances mapping(uint256 => mapping(address => uint256)) private _balances; // Mapping from account to operator approvals mapping(address => mapping(address => bool)) private _operatorApprovals; // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json string private _uri; /** * @dev See {_setURI}. */ constructor(string memory uri_) { _setURI(uri_); } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IERC1155).interfaceId || interfaceId == type(IERC1155MetadataURI).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC1155MetadataURI-uri}. * * This implementation returns the same URI for *all* token types. It relies * on the token type ID substitution mechanism * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. * * Clients calling this function must replace the `\{id\}` substring with the * actual token type ID. */ function uri(uint256) public view virtual override returns (string memory) { return _uri; } /** * @dev See {IERC1155-balanceOf}. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { require(account != address(0), "ERC1155: balance query for the zero address"); return _balances[id][account]; } /** * @dev See {IERC1155-balanceOfBatch}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch(address[] memory accounts, uint256[] memory ids) public view virtual override returns (uint256[] memory) { require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch"); uint256[] memory batchBalances = new uint256[](accounts.length); for (uint256 i = 0; i < accounts.length; ++i) { batchBalances[i] = balanceOf(accounts[i], ids[i]); } return batchBalances; } /** * @dev See {IERC1155-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual override { _setApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC1155-isApprovedForAll}. */ function isApprovedForAll(address account, address operator) public view virtual override returns (bool) { return _operatorApprovals[account][operator]; } /** * @dev See {IERC1155-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), "ERC1155: caller is not owner nor approved" ); _safeTransferFrom(from, to, id, amount, data); } /** * @dev See {IERC1155-safeBatchTransferFrom}. */ function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), "ERC1155: transfer caller is not owner nor approved" ); _safeBatchTransferFrom(from, to, ids, amounts, data); } /** * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - `from` must have a balance of tokens of type `id` of at least `amount`. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function _safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) internal virtual { require(to != address(0), "ERC1155: transfer to the zero address"); address operator = _msgSender(); uint256[] memory ids = _asSingletonArray(id); uint256[] memory amounts = _asSingletonArray(amount); _beforeTokenTransfer(operator, from, to, ids, amounts, data); uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); unchecked { _balances[id][from] = fromBalance - amount; } _balances[id][to] += amount; emit TransferSingle(operator, from, to, id, amount); _afterTokenTransfer(operator, from, to, ids, amounts, data); _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}. * * Emits a {TransferBatch} event. * * Requirements: * * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function _safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual { require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); require(to != address(0), "ERC1155: transfer to the zero address"); address operator = _msgSender(); _beforeTokenTransfer(operator, from, to, ids, amounts, data); for (uint256 i = 0; i < ids.length; ++i) { uint256 id = ids[i]; uint256 amount = amounts[i]; uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); unchecked { _balances[id][from] = fromBalance - amount; } _balances[id][to] += amount; } emit TransferBatch(operator, from, to, ids, amounts); _afterTokenTransfer(operator, from, to, ids, amounts, data); _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data); } /** * @dev Sets a new URI for all token types, by relying on the token type ID * substitution mechanism * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. * * By this mechanism, any occurrence of the `\{id\}` substring in either the * URI or any of the amounts in the JSON file at said URI will be replaced by * clients with the token type ID. * * For example, the `https://token-cdn-domain/\{id\}.json` URI would be * interpreted by clients as * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` * for token type ID 0x4cce0. * * See {uri}. * * Because these URIs cannot be meaningfully represented by the {URI} event, * this function emits no events. */ function _setURI(string memory newuri) internal virtual { _uri = newuri; } /** * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function _mint( address to, uint256 id, uint256 amount, bytes memory data ) internal virtual { require(to != address(0), "ERC1155: mint to the zero address"); address operator = _msgSender(); uint256[] memory ids = _asSingletonArray(id); uint256[] memory amounts = _asSingletonArray(amount); _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); _balances[id][to] += amount; emit TransferSingle(operator, address(0), to, id, amount); _afterTokenTransfer(operator, address(0), to, ids, amounts, data); _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}. * * Requirements: * * - `ids` and `amounts` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function _mintBatch( address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual { require(to != address(0), "ERC1155: mint to the zero address"); require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); address operator = _msgSender(); _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); for (uint256 i = 0; i < ids.length; i++) { _balances[ids[i]][to] += amounts[i]; } emit TransferBatch(operator, address(0), to, ids, amounts); _afterTokenTransfer(operator, address(0), to, ids, amounts, data); _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data); } /** * @dev Destroys `amount` tokens of token type `id` from `from` * * Requirements: * * - `from` cannot be the zero address. * - `from` must have at least `amount` tokens of token type `id`. */ function _burn( address from, uint256 id, uint256 amount ) internal virtual { require(from != address(0), "ERC1155: burn from the zero address"); address operator = _msgSender(); uint256[] memory ids = _asSingletonArray(id); uint256[] memory amounts = _asSingletonArray(amount); _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); unchecked { _balances[id][from] = fromBalance - amount; } emit TransferSingle(operator, from, address(0), id, amount); _afterTokenTransfer(operator, from, address(0), ids, amounts, ""); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}. * * Requirements: * * - `ids` and `amounts` must have the same length. */ function _burnBatch( address from, uint256[] memory ids, uint256[] memory amounts ) internal virtual { require(from != address(0), "ERC1155: burn from the zero address"); require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); address operator = _msgSender(); _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); for (uint256 i = 0; i < ids.length; i++) { uint256 id = ids[i]; uint256 amount = amounts[i]; uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); unchecked { _balances[id][from] = fromBalance - amount; } } emit TransferBatch(operator, from, address(0), ids, amounts); _afterTokenTransfer(operator, from, address(0), ids, amounts, ""); } /** * @dev Approve `operator` to operate on all of `owner` tokens * * Emits a {ApprovalForAll} event. */ function _setApprovalForAll( address owner, address operator, bool approved ) internal virtual { require(owner != operator, "ERC1155: setting approval status for self"); _operatorApprovals[owner][operator] = approved; emit ApprovalForAll(owner, operator, approved); } /** * @dev Hook that is called before any token transfer. This includes minting * and burning, as well as batched variants. * * The same hook is called on both single and batched variants. For single * transfers, the length of the `id` and `amount` arrays will be 1. * * Calling conditions (for each `id` and `amount` pair): * * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens * of token type `id` will be transferred to `to`. * - When `from` is zero, `amount` tokens of token type `id` will be minted * for `to`. * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` * will be burned. * - `from` and `to` are never both zero. * - `ids` and `amounts` have the same, non-zero length. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual {} /** * @dev Hook that is called after any token transfer. This includes minting * and burning, as well as batched variants. * * The same hook is called on both single and batched variants. For single * transfers, the length of the `id` and `amount` arrays will be 1. * * Calling conditions (for each `id` and `amount` pair): * * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens * of token type `id` will be transferred to `to`. * - When `from` is zero, `amount` tokens of token type `id` will be minted * for `to`. * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` * will be burned. * - `from` and `to` are never both zero. * - `ids` and `amounts` have the same, non-zero length. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual {} function _doSafeTransferAcceptanceCheck( address operator, address from, address to, uint256 id, uint256 amount, bytes memory data ) private { if (to.isContract()) { try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { if (response != IERC1155Receiver.onERC1155Received.selector) { revert("ERC1155: ERC1155Receiver rejected tokens"); } } catch Error(string memory reason) { revert(reason); } catch { revert("ERC1155: transfer to non ERC1155Receiver implementer"); } } } function _doSafeBatchTransferAcceptanceCheck( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) private { if (to.isContract()) { try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( bytes4 response ) { if (response != IERC1155Receiver.onERC1155BatchReceived.selector) { revert("ERC1155: ERC1155Receiver rejected tokens"); } } catch Error(string memory reason) { revert(reason); } catch { revert("ERC1155: transfer to non ERC1155Receiver implementer"); } } } function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) { uint256[] memory array = new uint256[](1); array[0] = element; return array; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC1155 compliant contract, as defined in the * https://eips.ethereum.org/EIPS/eip-1155[EIP]. * * _Available since v3.1._ */ interface IERC1155 is IERC165 { /** * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`. */ event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); /** * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /** * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to * `approved`. */ event ApprovalForAll(address indexed account, address indexed operator, bool approved); /** * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. * * If an {URI} event was emitted for `id`, the standard * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value * returned by {IERC1155MetadataURI-uri}. */ event URI(string value, uint256 indexed id); /** * @dev Returns the amount of tokens of token type `id` owned by `account`. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); /** * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, * * Emits an {ApprovalForAll} event. * * Requirements: * * - `operator` cannot be the caller. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. * * See {setApprovalForAll}. */ function isApprovedForAll(address account, address operator) external view returns (bool); /** * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}. * - `from` must have a balance of tokens of type `id` of at least `amount`. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes calldata data ) external; /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. * * Emits a {TransferBatch} event. * * Requirements: * * - `ids` and `amounts` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol) pragma solidity ^0.8.0; import "../IERC1155.sol"; /** * @dev Interface of the optional ERC1155MetadataExtension interface, as defined * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP]. * * _Available since v3.1._ */ interface IERC1155MetadataURI is IERC1155 { /** * @dev Returns the URI for token type `id`. * * If the `\{id\}` substring is present in the URI, it must be replaced by * clients with the actual token type ID. */ function uri(uint256 id) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol) pragma solidity ^0.8.2; import "../beacon/IBeacon.sol"; import "../../interfaces/draft-IERC1822.sol"; import "../../utils/Address.sol"; import "../../utils/StorageSlot.sol"; /** * @dev This abstract contract provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. * * _Available since v4.1._ * * @custom:oz-upgrades-unsafe-allow delegatecall */ abstract contract ERC1967Upgrade { // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Emitted when the implementation is upgraded. */ event Upgraded(address indexed implementation); /** * @dev Returns the current implementation address. */ function _getImplementation() internal view returns (address) { return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; } /** * @dev Stores a new address in the EIP1967 implementation slot. */ function _setImplementation(address newImplementation) private { require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; } /** * @dev Perform implementation upgrade * * Emits an {Upgraded} event. */ function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Perform implementation upgrade with additional setup call. * * Emits an {Upgraded} event. */ function _upgradeToAndCall( address newImplementation, bytes memory data, bool forceCall ) internal { _upgradeTo(newImplementation); if (data.length > 0 || forceCall) { Address.functionDelegateCall(newImplementation, data); } } /** * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. * * Emits an {Upgraded} event. */ function _upgradeToAndCallUUPS( address newImplementation, bytes memory data, bool forceCall ) internal { // Upgrades from old implementations will perform a rollback test. This test requires the new // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing // this special case will break upgrade paths from old UUPS implementation to new ones. if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) { _setImplementation(newImplementation); } else { try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID"); } catch { revert("ERC1967Upgrade: new implementation is not UUPS"); } _upgradeToAndCall(newImplementation, data, forceCall); } } /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Emitted when the admin account has changed. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Returns the current admin. */ function _getAdmin() internal view returns (address) { return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; } /** * @dev Stores a new address in the EIP1967 admin slot. */ function _setAdmin(address newAdmin) private { require(newAdmin != address(0), "ERC1967: new admin is the zero address"); StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; } /** * @dev Changes the admin of the proxy. * * Emits an {AdminChanged} event. */ function _changeAdmin(address newAdmin) internal { emit AdminChanged(_getAdmin(), newAdmin); _setAdmin(newAdmin); } /** * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. */ bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; /** * @dev Emitted when the beacon is upgraded. */ event BeaconUpgraded(address indexed beacon); /** * @dev Returns the current beacon. */ function _getBeacon() internal view returns (address) { return StorageSlot.getAddressSlot(_BEACON_SLOT).value; } /** * @dev Stores a new beacon in the EIP1967 beacon slot. */ function _setBeacon(address newBeacon) private { require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); require( Address.isContract(IBeacon(newBeacon).implementation()), "ERC1967: beacon implementation is not a contract" ); StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; } /** * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). * * Emits a {BeaconUpgraded} event. */ function _upgradeBeaconToAndCall( address newBeacon, bytes memory data, bool forceCall ) internal { _setBeacon(newBeacon); emit BeaconUpgraded(newBeacon); if (data.length > 0 || forceCall) { Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol) pragma solidity ^0.8.0; /** * @dev This is the interface that {BeaconProxy} expects of its beacon. */ interface IBeacon { /** * @dev Must return an address that can be used as a delegate call target. * * {BeaconProxy} will check that this address is a contract. */ function implementation() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol) pragma solidity ^0.8.0; /** * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified * proxy whose upgrades are fully controlled by the current implementation. */ interface IERC1822Proxiable { /** * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation * address. * * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this * function revert if invoked through a proxy. */ function proxiableUUID() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol) pragma solidity ^0.8.0; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC1967 implementation slot: * ``` * contract ERC1967 { * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._ */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { assembly { r.slot := slot } } /** * @dev Returns an `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { assembly { r.slot := slot } } /** * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { assembly { r.slot := slot } } /** * @dev Returns an `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { assembly { r.slot := slot } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol) pragma solidity ^0.8.0; import "../Proxy.sol"; import "./ERC1967Upgrade.sol"; /** * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an * implementation address that can be changed. This address is stored in storage in the location specified by * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the * implementation behind the proxy. */ contract ERC1967Proxy is Proxy, ERC1967Upgrade { /** * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. * * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded * function call, and allows initializating the storage of the proxy like a Solidity constructor. */ constructor(address _logic, bytes memory _data) payable { assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); _upgradeToAndCall(_logic, _data, false); } /** * @dev Returns the current implementation address. */ function _implementation() internal view virtual override returns (address impl) { return ERC1967Upgrade._getImplementation(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol) pragma solidity ^0.8.0; /** * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to * be specified by overriding the virtual {_implementation} function. * * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a * different contract through the {_delegate} function. * * The success and return data of the delegated call will be returned back to the caller of the proxy. */ abstract contract Proxy { /** * @dev Delegates the current call to `implementation`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _delegate(address implementation) internal virtual { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } /** * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function * and {_fallback} should delegate. */ function _implementation() internal view virtual returns (address); /** * @dev Delegates the current call to the address returned by `_implementation()`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _fallback() internal virtual { _beforeFallback(); _delegate(_implementation()); } /** * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other * function in the contract matches the call data. */ fallback() external payable virtual { _fallback(); } /** * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data * is empty. */ receive() external payable virtual { _fallback(); } /** * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` * call, or as part of the Solidity `fallback` or `receive` functions. * * If overridden should call `super._beforeFallback()`. */ function _beforeFallback() internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol) pragma solidity ^0.8.0; import "../ERC1967/ERC1967Proxy.sol"; /** * @dev This contract implements a proxy that is upgradeable by an admin. * * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector * clashing], which can potentially be used in an attack, this contract uses the * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two * things that go hand in hand: * * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if * that call matches one of the admin functions exposed by the proxy itself. * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the * implementation. If the admin tries to call a function on the implementation it will fail with an error that says * "admin cannot fallback to proxy target". * * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due * to sudden errors when trying to call a function from the proxy implementation. * * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way, * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy. */ contract TransparentUpgradeableProxy is ERC1967Proxy { /** * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}. */ constructor( address _logic, address admin_, bytes memory _data ) payable ERC1967Proxy(_logic, _data) { assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); _changeAdmin(admin_); } /** * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin. */ modifier ifAdmin() { if (msg.sender == _getAdmin()) { _; } else { _fallback(); } } /** * @dev Returns the current admin. * * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function admin() external ifAdmin returns (address admin_) { admin_ = _getAdmin(); } /** * @dev Returns the current implementation. * * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` */ function implementation() external ifAdmin returns (address implementation_) { implementation_ = _implementation(); } /** * @dev Changes the admin of the proxy. * * Emits an {AdminChanged} event. * * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}. */ function changeAdmin(address newAdmin) external virtual ifAdmin { _changeAdmin(newAdmin); } /** * @dev Upgrade the implementation of the proxy. * * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}. */ function upgradeTo(address newImplementation) external ifAdmin { _upgradeToAndCall(newImplementation, bytes(""), false); } /** * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the * proxied contract. * * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}. */ function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin { _upgradeToAndCall(newImplementation, data, true); } /** * @dev Returns the current admin. */ function _admin() internal view virtual returns (address) { return _getAdmin(); } /** * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}. */ function _beforeFallback() internal virtual override { require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target"); super._beforeFallback(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; /** * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}. */ contract ProxyAdmin is Ownable { /** * @dev Returns the current implementation of `proxy`. * * Requirements: * * - This contract must be the admin of `proxy`. */ function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("implementation()")) == 0x5c60da1b (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b"); require(success); return abi.decode(returndata, (address)); } /** * @dev Returns the current admin of `proxy`. * * Requirements: * * - This contract must be the admin of `proxy`. */ function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("admin()")) == 0xf851a440 (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440"); require(success); return abi.decode(returndata, (address)); } /** * @dev Changes the admin of `proxy` to `newAdmin`. * * Requirements: * * - This contract must be the current admin of `proxy`. */ function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner { proxy.changeAdmin(newAdmin); } /** * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}. * * Requirements: * * - This contract must be the admin of `proxy`. */ function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner { proxy.upgradeTo(implementation); } /** * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See * {TransparentUpgradeableProxy-upgradeToAndCall}. * * Requirements: * * - This contract must be the admin of `proxy`. */ function upgradeAndCall( TransparentUpgradeableProxy proxy, address implementation, bytes memory data ) public payable virtual onlyOwner { proxy.upgradeToAndCall{ value: msg.value }(implementation, data); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; interface ITestERC1155 is IERC1155 { function mint( uint256 amount, uint256 id, address receiver ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "../interfaces/ITestERC1155.sol"; import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; contract TestERC1155 is ERC1155, ITestERC1155 { constructor() ERC1155("TestToken ERC1155") {} function mint( uint256 amount, uint256 id, address receiver ) public override { _mint(receiver, id, amount, ""); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol) pragma solidity ^0.8.0; import "../ERC721.sol"; import "./IERC721Enumerable.sol"; /** * @dev This implements an optional extension of {ERC721} defined in the EIP that adds * enumerability of all the token ids in the contract as well as all token ids owned by each * account. */ abstract contract ERC721Enumerable is ERC721, IERC721Enumerable { // Mapping from owner to list of owned token IDs mapping(address => mapping(uint256 => uint256)) private _ownedTokens; // Mapping from token ID to index of the owner tokens list mapping(uint256 => uint256) private _ownedTokensIndex; // Array with all token ids, used for enumeration uint256[] private _allTokens; // Mapping from token id to position in the allTokens array mapping(uint256 => uint256) private _allTokensIndex; /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. */ function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) { require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds"); return _ownedTokens[owner][index]; } /** * @dev See {IERC721Enumerable-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _allTokens.length; } /** * @dev See {IERC721Enumerable-tokenByIndex}. */ function tokenByIndex(uint256 index) public view virtual override returns (uint256) { require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds"); return _allTokens[index]; } /** * @dev Hook that is called before any token transfer. This includes minting * and burning. * * Calling conditions: * * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be * transferred to `to`. * - When `from` is zero, `tokenId` will be minted for `to`. * - When `to` is zero, ``from``'s `tokenId` will be burned. * - `from` cannot be the zero address. * - `to` cannot be the zero address. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal virtual override { super._beforeTokenTransfer(from, to, tokenId); if (from == address(0)) { _addTokenToAllTokensEnumeration(tokenId); } else if (from != to) { _removeTokenFromOwnerEnumeration(from, tokenId); } if (to == address(0)) { _removeTokenFromAllTokensEnumeration(tokenId); } else if (to != from) { _addTokenToOwnerEnumeration(to, tokenId); } } /** * @dev Private function to add a token to this extension's ownership-tracking data structures. * @param to address representing the new owner of the given token ID * @param tokenId uint256 ID of the token to be added to the tokens list of the given address */ function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { uint256 length = ERC721.balanceOf(to); _ownedTokens[to][length] = tokenId; _ownedTokensIndex[tokenId] = length; } /** * @dev Private function to add a token to this extension's token tracking data structures. * @param tokenId uint256 ID of the token to be added to the tokens list */ function _addTokenToAllTokensEnumeration(uint256 tokenId) private { _allTokensIndex[tokenId] = _allTokens.length; _allTokens.push(tokenId); } /** * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for * gas optimizations e.g. when performing a transfer operation (avoiding double writes). * This has O(1) time complexity, but alters the order of the _ownedTokens array. * @param from address representing the previous owner of the given token ID * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address */ function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). uint256 lastTokenIndex = ERC721.balanceOf(from) - 1; uint256 tokenIndex = _ownedTokensIndex[tokenId]; // When the token to delete is the last token, the swap operation is unnecessary if (tokenIndex != lastTokenIndex) { uint256 lastTokenId = _ownedTokens[from][lastTokenIndex]; _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index } // This also deletes the contents at the last position of the array delete _ownedTokensIndex[tokenId]; delete _ownedTokens[from][lastTokenIndex]; } /** * @dev Private function to remove a token from this extension's token tracking data structures. * This has O(1) time complexity, but alters the order of the _allTokens array. * @param tokenId uint256 ID of the token to be removed from the tokens list */ function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). uint256 lastTokenIndex = _allTokens.length - 1; uint256 tokenIndex = _allTokensIndex[tokenId]; // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding // an 'if' statement (like in _removeTokenFromOwnerEnumeration) uint256 lastTokenId = _allTokens[lastTokenIndex]; _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index // This also deletes the contents at the last position of the array delete _allTokensIndex[tokenId]; _allTokens.pop(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; contract TestERC721 is ERC721Enumerable { uint256 public tokenId; constructor() ERC721("TestToken ERC721", "TTERC721") { tokenId = 1; } function mint(address receiver) public { _safeMint(receiver, tokenId); tokenId++; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; interface ITestERC721 is IERC721Enumerable { function mint(address receiver) external; }
{ "optimizer": { "enabled": true, "runs": 100 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "metadata": { "useLiteralContent": true } }
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakeId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"dayTotalEmission","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalFounderEmission","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalFounders","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stakeBackAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasuryAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"v2VaultAmount","type":"uint256"}],"name":"ClaimDailyEmission","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"ClaimPastEmission","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"RequestWithdrawalFromStaker","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakeId","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"founderNFTIDs","type":"uint256[]"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"enum IAtlasMine.Lock","name":"lock","type":"uint8"}],"name":"TopupMagicToStaker","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TopupTodayEmission","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakeId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"founderId","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"WithdrawalFromStaker","type":"event"},{"inputs":[],"name":"BattleflyBot","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BattleflyFoundersFlywheelVault","outputs":[{"internalType":"contract IBattleflyFoundersFlywheelVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BattleflyStaker","outputs":[{"internalType":"contract IBattleflyAtlasStaker","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BattleflyStakerV2","outputs":[{"internalType":"contract IBattleflyAtlasStakerV02","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_STAKE_BACK_LOCK","outputs":[{"internalType":"enum IAtlasMine.Lock","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"DailyFounderEmissions","outputs":[{"internalType":"uint256","name":"totalEmission","type":"uint256"},{"internalType":"uint256","name":"totalFounders","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DaysSinceStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"FounderStakes","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"stakeTimestamp","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"lastClaimedDay","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TREASURY_VAULT","outputs":[{"internalType":"contract IBattleflyTreasuryFlywheelVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TREASURY_WALLET","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activeDepositId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activeRestakeDepositId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimDailyEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimPastEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"claimedPastEmission","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimingIsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"founderNFT","outputs":[{"internalType":"contract ISpecialNFT","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"founderTypeID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getClaimableEmissionOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentDay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"currentDay","type":"uint256"}],"name":"getDailyFounderEmission","outputs":[{"internalType":"uint256[2]","name":"","type":"uint256[2]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getPastEmissionClaimableTokens","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"increaseTotalEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_magicAddress","type":"address"},{"internalType":"address","name":"_BattleflyStakerAddress","type":"address"},{"internalType":"uint256","name":"_founderTypeID","type":"uint256"},{"internalType":"address","name":"_founderNFTAddress","type":"address"},{"internalType":"uint256","name":"_startTimestamp","type":"uint256"},{"internalType":"address","name":"_battleflyFounderVaultV2Address","type":"address"},{"internalType":"uint256","name":"_stakeBackPercent","type":"uint256"},{"internalType":"uint256","name":"_treasuryPercent","type":"uint256"},{"internalType":"uint256","name":"_v2VaultPercent","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"magic","outputs":[{"internalType":"contract IERC20Upgradeable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pastEmissionPerFounder","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"doPause","type":"bool"}],"name":"pauseClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pendingStakeBackAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"dayToStart","type":"uint256"}],"name":"recalculateTotalFounders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"reduceTotalEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestWithdrawAllFromStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"requestWithdrawFromStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"access","type":"bool"}],"name":"setAdminAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_battleflyBot","type":"address"}],"name":"setBattleflyBot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"setBattleflyStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"day","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"stakers","type":"uint256"}],"name":"setDailyFounderEmissions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"setFlywheelVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setFounderStakesToStart","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"founderVault","type":"address"}],"name":"setFounderVaultV2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setPastEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_stakeBackPercent","type":"uint256"},{"internalType":"uint256","name":"_treasuryPercent","type":"uint256"},{"internalType":"uint256","name":"_v2VaultPercent","type":"uint256"}],"name":"setPercentages","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newTimestamp","type":"uint256"}],"name":"setStartTimestamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"bool","name":"isClaimed","type":"bool"}],"name":"setTokenClaimedPastEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_treasuryAddress","type":"address"}],"name":"setTreasuryVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"simulateClaim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakeBackPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"stakeFounderNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakeIdOfFounder","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"stakesOf","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"stakeTimestamp","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"lastClaimedDay","type":"uint256"}],"internalType":"struct BattleflyFounderVaultV08.FounderStake[]","name":"","type":"tuple[]"},{"internalType":"uint256[][]","name":"","type":"uint256[][]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"enum IAtlasMine.Lock","name":"lock","type":"uint8"}],"name":"topupMagicToStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"topupTodayEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasuryPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"currentDay","type":"uint256"}],"name":"updateClaimedFounderEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"v2VaultPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"founderIds","type":"uint256[]"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAllFromStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"withdrawFromStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawFromVault","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b506157dc80620000216000396000f3fe608060405234801561001057600080fd5b50600436106103c35760003560e01c80639212567c11610206578063be70ba311161012b578063e327a6af116100c3578063f23a6e6111610087578063f23a6e611461092f578063f2fde38b1461094e578063f65a0b3e14610961578063fc68866114610974578063fe41d0561461098757600080fd5b8063e327a6af146108e2578063e6fd48bc146108f5578063e9d33b8b146108ff578063ea0f57e114610912578063f01e30351461092557600080fd5b8063be70ba31146107dc578063c0ce0bad146107ef578063c3a7ab0514610813578063c44bef751461081d578063c579007714610830578063d1058e5914610843578063d11dea5a1461084b578063d3e210e31461085e578063d5f80c34146108cf57600080fd5b8063a8ad1ca81161019e578063a8ad1ca8146106ff578063a9d3ce9914610720578063aad8750b14610733578063ac6b640a14610746578063afe89b4f14610783578063b187bd2614610796578063b25c6eaf146107a2578063b84c0b6a146107aa578063bc197c81146107bd57600080fd5b80639212567c14610670578063935b7aca1461067857806396e381541461068c578063983d95ce146106a05780639888cc83146106b35780639e6c2959146106c65780639f51563b146106cf578063a073801f146106d9578063a4ade971146106ec57600080fd5b80633d0fc343116102ec57806365ccc5b01161028457806365ccc5b0146105f6578063705aefc31461060b57806370a0823114610615578063715018a6146106285780637765968714610630578063791253b5146106435780637e1464331461064d578063853828b6146106605780638da5cb5b1461066857600080fd5b80633d0fc343146105425780633e6968b6146105555780633fc276cd1461055d5780634295d22d1461057d5780634719f251146105905780634def312d1461059a5780635daf3146146105a8578063610a8aec146105c357806363a454e0146105e357600080fd5b806317d7de7c1161035f57806317d7de7c146104ad57806318b46e9e146104c25780631d093859146104cc57806321116f4e146104d457806321ec4db9146104e757806332bd72e5146104ef57806333b69c4c146104f95780633819a9931461051a5780633a2a3bf21461052e57600080fd5b806301ffc9a7146103c857806304ef9d58146103f0578063076e298214610408578063090cdd1f1461041d5780630a7b4b2e1461043e5780630d854646146104525780630e82867b14610466578063150b7a021461047957806316c44182146104a5575b600080fd5b6103db6103d6366004615190565b61099a565b60405190151581526020015b60405180910390f35b6103fa6101715481565b6040519081526020016103e7565b61041b6104163660046151b8565b6109d1565b005b61017d54610431906001600160a01b031681565b6040516103e7919061531d565b61017e54610431906001600160a01b031681565b61015f54610431906001600160a01b031681565b61041b610474366004614e79565b610a9f565b61048c610487366004614fbc565b610b81565b6040516001600160e01b031990911681526020016103e7565b61041b610b92565b6104b5610c00565b6040516103e791906154f6565b6103fa6101705481565b61041b610c64565b61041b6104e23660046150b9565b610d2c565b61041b610d97565b6103fa61017f5481565b61050c610507366004614e79565b610f2d565b6040516103e79291906153e3565b61016254610431906001600160a01b031681565b61017c54610431906001600160a01b031681565b61041b610550366004615114565b6111c6565b6103fa611267565b61057061056b3660046151b8565b6112c2565b6040516103e791906154a4565b61041b61058b3660046150e2565b611352565b6103fa6101725481565b610179546103db9060ff1681565b61043173f5411006eefd66c213d2fd2033a1d340458b722681565b6105d66105d1366004614e79565b6116f4565b6040516103e791906154d5565b61041b6105f13660046150e2565b611cf6565b6105fe600081565b6040516103e791906154e8565b6103fa6101615481565b6103fa610623366004614e79565b611f2c565b61041b612028565b61041b61063e3660046151b8565b612063565b6103fa61016c5481565b61041b61065b3660046151b8565b612098565b61041b61215b565b610431612283565b61041b612292565b61016054610431906001600160a01b031681565b61018254610431906001600160a01b031681565b61041b6106ae3660046150e2565b612613565b61041b6106c1366004615158565b6127bd565b6103fa61271081565b6103fa6101815481565b61041b6106e7366004614e79565b612824565b61041b6106fa36600461520f565b6128b0565b6103fa61070d3660046151b8565b6101776020526000908152604090205481565b61041b61072e366004615253565b61294a565b61041b6107413660046151b8565b6129ad565b61076e6107543660046151b8565b61016d602052600090815260409020805460019091015482565b604080519283526020830191909152016103e7565b61041b610791366004614f38565b612a1b565b6101795460ff166103db565b61041b612ba8565b61041b6107b83660046151e8565b612c13565b61048c6107cb366004614e93565b63bc197c8160e01b95945050505050565b61041b6107ea366004614e79565b612cca565b6103db6107fd3660046151b8565b6101756020526000908152604090205460ff1681565b6103fa6101765481565b61041b61082b3660046151b8565b612d2f565b6103fa61083e366004614e79565b612d88565b61041b612e07565b61041b610859366004615253565b612f56565b6108a061086c3660046151b8565b610169602052600090815260409020805460018201546002830154600390930154919290916001600160a01b039091169084565b6040516103e7949392919093845260208401929092526001600160a01b03166040830152606082015260800190565b61041b6108dd366004614e79565b612fd8565b6103db6108f03660046150b9565b613061565b6103fa6101635481565b61041b61090d3660046151b8565b613141565b61041b6109203660046150e2565b6131a7565b6103fa6101805481565b61048c61093d366004615021565b63f23a6e6160e01b95945050505050565b61041b61095c366004614e79565b613486565b61041b61096f366004615083565b613523565b6103fa6109823660046151b8565b61357e565b61041b610995366004614e79565b6135f1565b60006001600160e01b03198216630271189760e51b14806109cb57506301ffc9a760e01b6001600160e01b03198316145b92915050565b33600090815261016b602052604090205460ff1680610a0857506109f3612283565b6001600160a01b0316336001600160a01b0316145b610a2d5760405162461bcd60e51b8152600401610a2490615529565b60405180910390fd5b600081815261016d602052604081206001908101549190610a4f9084906156a0565b90505b61016c548111610a9a57600081815261016d6020526040812060018101805491928592610a809084906156a0565b90915550829150610a92905081615751565b915050610a52565b505050565b33610aa8612283565b6001600160a01b031614610ace5760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b038116610ae157600080fd5b61017380546001600160a01b0319166001600160a01b0383811691821790925561015f5460405163095ea7b360e01b815292169163095ea7b391610b2b9160001990600401615355565b602060405180830381600087803b158015610b4557600080fd5b505af1158015610b59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7d9190615174565b5050565b630a85bd0160e11b5b949350505050565b33600090815261016b602052604090205460ff1680610bc95750610bb4612283565b6001600160a01b0316336001600160a01b0316145b610be55760405162461bcd60e51b8152600401610a2490615529565b6000610bf261017a613656565b9050610bfd81611cf6565b50565b60606101615460961415610c39575060408051808201909152601081526f158c4814dd185ad95c9cc815985d5b1d60821b602082015290565b5060408051808201909152601081526f158c8814dd185ad95c9cc815985d5b1d60821b602082015290565b33600090815261016b602052604090205460ff1680610c9b5750610c86612283565b6001600160a01b0316336001600160a01b0316145b610cb75760405162461bcd60e51b8152600401610a2490615529565b600261012d541415610cdb5760405162461bcd60e51b8152600401610a2490615625565b600261012d5561016a5460005b818111610d225761016354600082815261016960205260408120600181019290925560039091015580610d1a81615751565b915050610ce8565b5050600161012d55565b33600090815261016b602052604090205460ff1680610d635750610d4e612283565b6001600160a01b0316336001600160a01b0316145b610d7f5760405162461bcd60e51b8152600401610a2490615529565b61015f54610b7d906001600160a01b03168383613663565b61017654610df15760405162461bcd60e51b815260206004820152602160248201527f4e6f207061737420666f756e64657220656d697373696f6e20746f20636c61696044820152606d60f81b6064820152608401610a24565b6000610dfc336116f4565b90506000815111610e445760405162461bcd60e51b81526020600482015260126024820152714e6f20746f6b656e7320746f20636c61696d60701b6044820152606401610a24565b60005b8151811015610eb75760016101756000848481518110610e7757634e487b7160e01b600052603260045260246000fd5b6020026020010151815260200190815260200160002060006101000a81548160ff0219169083151502179055508080610eaf90615751565b915050610e47565b50610ee033825161017654610ecc91906156d8565b61015f546001600160a01b03169190613663565b7f93f2234b87606ba2c9f5e61b5e95b7ef9d74b574120ae0be68c57fe636e6ef4733825161017654610f1291906156d8565b83604051610f229392919061536e565b60405180910390a150565b6001600160a01b0381166000908152610167602052604081206060918291610f54906136b9565b6001600160401b03811115610f7957634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610fde57816020015b610fcb6040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b815260200190600190039081610f975790505b506001600160a01b03851660009081526101676020526040812091925090611005906136b9565b6001600160401b0381111561102a57634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561105d57816020015b60608152602001906001900390816110485790505b50905060005b6001600160a01b038616600090815261016760205260409020611085906136b9565b8110156111bb576001600160a01b03861660009081526101676020526040812061016991906110b490846136c3565b8152602080820192909252604090810160002081516080810183528154815260018201549381019390935260028101546001600160a01b031691830191909152600301546060820152835184908390811061111f57634e487b7160e01b600052603260045260246000fd5b602002602001018190525061117d610178600061116a8461016760008c6001600160a01b03166001600160a01b031681526020019081526020016000206136c390919063ffffffff16565b8152602001908152602001600020613656565b82828151811061119d57634e487b7160e01b600052603260045260246000fd5b602002602001018190525080806111b390615751565b915050611063565b509094909350915050565b336111cf612283565b6001600160a01b0316146111f55760405162461bcd60e51b8152600401610a24906155a5565b60005b8251811015610a9a5781610175600085848151811061122757634e487b7160e01b600052603260045260246000fd5b6020026020010151815260200190815260200160002060006101000a81548160ff021916908315150217905550808061125f90615751565b9150506111f8565b33600090815261016b602052604081205460ff168061129e5750611289612283565b6001600160a01b0316336001600160a01b0316145b6112ba5760405162461bcd60e51b8152600401610a2490615529565b5061016c5490565b6112ca614d26565b33600090815261016b602052604090205460ff168061130157506112ec612283565b6001600160a01b0316336001600160a01b0316145b61131d5760405162461bcd60e51b8152600401610a2490615529565b50604080518082018252600083815261016d60208181529382208054845291859052835260010154918101919091525b919050565b80516113b15760405162461bcd60e51b815260206004820152602860248201527f4d7573742070726f76696465206174206c65617374206f6e6520666f756e64656044820152671c8813919508125160c21b6064820152608401610a24565b60005b815181101561155b57610161546101605483516001600160a01b039091169063a2d9dd12908590859081106113f957634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b815260040161141f91815260200190565b60206040518083038186803b15801561143757600080fd5b505afa15801561144b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146f91906151d0565b146114b45760405162461bcd60e51b8152602060048201526015602482015274139bdd081d985b1a5908199bdd5b99195c88139195605a1b6044820152606401610a24565b6101605482516001600160a01b03909116906342842e0e90339030908690869081106114f057634e487b7160e01b600052603260045260246000fd5b60200260200101516040518463ffffffff1660e01b815260040161151693929190615331565b600060405180830381600087803b15801561153057600080fd5b505af1158015611544573d6000803e3d6000fd5b50505050808061155390615751565b9150506113b4565b5061016c5461016a805490600061157183615751565b90915550506040805160808101825283518152426020808301918252338385019081526060840186815261016a546000908152610169909352948220935184559151600184015590516002830180546001600160a01b0319166001600160a01b0390921691909117905591516003909101555b82518110156116935761016a54610177600085848151811061161657634e487b7160e01b600052603260045260246000fd5b602002602001015181526020019081526020016000208190555061168083828151811061165357634e487b7160e01b600052603260045260246000fd5b6020026020010151610178600061016a5481526020019081526020016000206136cf90919063ffffffff16565b508061168b81615751565b9150506115e4565b5061016a54336000908152610167602052604090206116b1916136cf565b507f55331f3fa9ca07b25728aacdb3badf1eacd93d82f1dfe128cc633a137232f9663361016a54846040516116e89392919061536e565b60405180910390a15050565b610160546040516370a0823160e01b81526060916000916001600160a01b03909116906370a082319061172b90869060040161531d565b60206040518083038186803b15801561174357600080fd5b505afa158015611757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177b91906151d0565b90506000816001600160401b038111156117a557634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156117ce578160200160208202803683370190505b5090506000805b8381101561195c5761016054604051632f745c5960e01b81526000916001600160a01b031690632f745c5990611811908a908690600401615355565b60206040518083038186803b15801561182957600080fd5b505afa15801561183d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186191906151d0565b6101605460405163516cee8960e11b8152600481018390529192506000916001600160a01b039091169063a2d9dd129060240160206040518083038186803b1580156118ac57600080fd5b505afa1580156118c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e491906151d0565b9050610161548114801561190857506000828152610175602052604090205460ff16155b15611947578185858151811061192e57634e487b7160e01b600052603260045260246000fd5b60209081029190910101528361194381615751565b9450505b5050808061195490615751565b9150506117d5565b50600061196886610f2d565b91505060008060005b83518110156119c25783818151811061199a57634e487b7160e01b600052603260045260246000fd5b602002602001015151826119ae91906156a0565b9150806119ba81615751565b915050611971565b506000816001600160401b038111156119eb57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611a14578160200160208202803683370190505b50905060005b8451811015611b98576000858281518110611a4557634e487b7160e01b600052603260045260246000fd5b6020026020010151905060005b8151811015611b83576000828281518110611a7d57634e487b7160e01b600052603260045260246000fd5b60209081029190910101516101605460405163516cee8960e11b8152600481018390529192506000916001600160a01b039091169063a2d9dd129060240160206040518083038186803b158015611ad357600080fd5b505afa158015611ae7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b0b91906151d0565b90506101615481148015611b2f57506000828152610175602052604090205460ff16155b15611b6e5781868981518110611b5557634e487b7160e01b600052603260045260246000fd5b602090810291909101015287611b6a81615751565b9850505b50508080611b7b90615751565b915050611a52565b50508080611b9090615751565b915050611a1a565b506000611ba584876156a0565b6001600160401b03811115611bca57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611bf3578160200160208202803683370190505b50905060005b86811015611c6657878181518110611c2157634e487b7160e01b600052603260045260246000fd5b6020026020010151828281518110611c4957634e487b7160e01b600052603260045260246000fd5b602090810291909101015280611c5e81615751565b915050611bf9565b50855b611c7385886156a0565b811015611ce85782611c8588836156f7565b81518110611ca357634e487b7160e01b600052603260045260246000fd5b6020026020010151828281518110611ccb57634e487b7160e01b600052603260045260246000fd5b602090810291909101015280611ce081615751565b915050611c69565b509998505050505050505050565b33600090815261016b602052604090205460ff1680611d2d5750611d18612283565b6001600160a01b0316336001600160a01b0316145b611d495760405162461bcd60e51b8152600401610a2490615529565b60005b8151811015610b7d5761017c5482516001600160a01b039091169063dd550bb490849084908110611d8d57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401611db391815260200190565b60206040518083038186803b158015611dcb57600080fd5b505afa158015611ddf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e039190615174565b15611f1a5761017c5482516001600160a01b0390911690639ee679e890849084908110611e4057634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401611e6691815260200190565b602060405180830381600087803b158015611e8057600080fd5b505af1158015611e94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb8919061527e565b507f21c77cf216591cc6caec60a5dd496cd81759bad01d20fd41961a2df074bd0c4f828281518110611efa57634e487b7160e01b600052603260045260246000fd5b6020026020010151604051611f1191815260200190565b60405180910390a15b80611f2481615751565b915050611d4c565b60006001600160a01b038216611f975760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610a24565b6001600160a01b0382166000908152610167602052604081208190611fbb906136b9565b905060005b8181101561201f576001600160a01b0385166000908152610167602052604081206101699190611ff090846136c3565b815260208101919091526040016000205461200b90846156a0565b92508061201781615751565b915050611fc0565b50909392505050565b33612031612283565b6001600160a01b0316146120575760405162461bcd60e51b8152600401610a24906155a5565b61206160006136db565b565b3361206c612283565b6001600160a01b0316146120925760405162461bcd60e51b8152600401610a24906155a5565b61017655565b33600090815261016b602052604090205460ff16806120cf57506120ba612283565b6001600160a01b0316336001600160a01b0316145b6120eb5760405162461bcd60e51b8152600401610a2490615529565b600081116120f857600080fd5b61015f54612111906001600160a01b031633308461372d565b80610166600082825461212491906156a0565b90915550506040517f8ae5bc60487649c7a0deec334e85a8f4e5b0f48c67483b3e10a00024d187c8ec90610f229033908490615355565b600261012d54141561217f5760405162461bcd60e51b8152600401610a2490615625565b600261012d553360009081526101676020526040812061219e906136b9565b116121e25760405162461bcd60e51b81526020600482015260146024820152734e6f205354414b4520746f20776974686472617760601b6044820152606401610a24565b3360009081526101676020526040812081906121fd90613656565b905060005b815181101561227957600082828151811061222d57634e487b7160e01b600052603260045260246000fd5b6020026020010151905061224081613754565b506000818152610169602052604090205461225b90856156a0565b935061226681613845565b508061227181615751565b915050612202565b50610d2282613a61565b6033546001600160a01b031690565b610182546001600160a01b031633146122e55760405162461bcd60e51b815260206004820152601560248201527414995c5d5a5c994818985d1d1b19599b1e48189bdd605a1b6044820152606401610a24565b600261012d5414156123095760405162461bcd60e51b8152600401610a2490615625565b600261012d5561016c546000906123219060016156a0565b9050600061232d613d26565b9050600061233a83613ece565b90506000808080851561254057612710610170548761235991906156d8565b61236391906156b8565b935061237c610166548561237791906156a0565b613fa4565b612710610172548761238e91906156d8565b61239891906156b8565b925082156124005761017354604051637e14643360e01b8152600481018590526001600160a01b0390911690637e14643390602401600060405180830381600087803b1580156123e757600080fd5b505af11580156123fb573d6000803e3d6000fd5b505050505b612710610171548761241291906156d8565b61241c91906156b8565b9150811561250f5761015f5461017e5460405163095ea7b360e01b81526001600160a01b039283169263095ea7b39261245c929116908690600401615355565b602060405180830381600087803b15801561247657600080fd5b505af115801561248a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ae9190615174565b5061017e546040516370055ee160e11b8152600481018490526001600160a01b039091169063e00abdc290602401600060405180830381600087803b1580156124f657600080fd5b505af115801561250a573d6000803e3d6000fd5b505050505b818361251b86896156f7565b61252591906156f7565b61252f91906156f7565b61253990826156a0565b9050612564565b610166541561255a5761255561016654613fa4565b612564565b6125646000613fa4565b80610164600082825461257791906156a0565b909155505061016c879055604080518082018252828152602080820188815260008b815261016d835284902092518355516001909201919091558151888152908101839052908101869052606081018590526080810183905260a081018490527fc87e64b388919dd4e97de1f419aae0bf9ce06b2ff3ee8e776f48a9fd51c067739060c00160405180910390a15050600161012d555050505050565b600261012d5414156126375760405162461bcd60e51b8152600401610a2490615625565b600261012d5580516126845760405162461bcd60e51b81526020600482015260166024820152754e6f20466f756e64657220746f20776974686472617760501b6044820152606401610a24565b6000805b82518110156127b357600061017760008584815181106126b857634e487b7160e01b600052603260045260246000fd5b602090810291909101810151825281810192909252604090810160009081205480825261016990935220600201549091506001600160a01b031633146127315760405162461bcd60e51b815260206004820152600e60248201526d4e6f7420796f7572207374616b6560901b6044820152606401610a24565b61276184838151811061275457634e487b7160e01b600052603260045260246000fd5b6020026020010151614299565b5061279284838151811061278557634e487b7160e01b600052603260045260246000fd5b602002602001015161435b565b8261279c81615751565b9350505080806127ab90615751565b915050612688565b50610d2281613a61565b33600090815261016b602052604090205460ff16806127f457506127df612283565b6001600160a01b0316336001600160a01b0316145b6128105760405162461bcd60e51b8152600401610a2490615529565b610179805460ff1916911515919091179055565b3361282d612283565b6001600160a01b0316146128535760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b03811661286657600080fd5b61017c80546001600160a01b0319166001600160a01b0383811691821790925561015f5460405163095ea7b360e01b815292169163095ea7b391610b2b9160001990600401615355565b33600090815261016b602052604090205460ff16806128e757506128d2612283565b6001600160a01b0316336001600160a01b0316145b6129035760405162461bcd60e51b8152600401610a2490615529565b61016c819055600061291482613ece565b604080518082018252948552602080860192835261016c54600090815261016d9091522093518455516001909301929092555050565b33612953612283565b6001600160a01b0316146129795760405162461bcd60e51b8152600401610a24906155a5565b6127108161298784866156a0565b61299191906156a0565b111561299c57600080fd5b610170929092556101715561017255565b33600090815261016b602052604090205460ff16806129e457506129cf612283565b6001600160a01b0316336001600160a01b0316145b612a005760405162461bcd60e51b8152600401610a2490615529565b806101646000828254612a1391906156f7565b909155505050565b6000612a27600161452d565b90508015612a3f576000805461ff0019166101001790555b612a476145b5565b612a4f6145b5565b612a576145dc565b612a5f61460b565b61015f80546001600160a01b03808d166001600160a01b03199283161790925561016280548c841690831617905561016080548a841692169190911790556101618990554261016855600061016a81905561016388905561016c556101708590556101718490556101728390558516612aea5761017380546001600160a01b03191630179055612b07565b61017380546001600160a01b0319166001600160a01b0387161790555b612710610172546101715461017054612b2091906156a0565b612b2a91906156a0565b1115612b3557600080fd5b6101625461015f54612b56916001600160a01b03918216911660001961463a565b8015612b9c576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050505050565b33600090815261016b602052604090205460ff1680612bdf5750612bca612283565b6001600160a01b0316336001600160a01b0316145b612bfb5760405162461bcd60e51b8152600401610a2490615529565b6000612c0861017a613656565b9050610bfd816131a7565b33600090815261016b602052604090205460ff1680612c4a5750612c35612283565b6001600160a01b0316336001600160a01b0316145b612c665760405162461bcd60e51b8152600401610a2490615529565b60008211612c7357600080fd5b61015f54612c8c906001600160a01b031633308561372d565b612c96828261474d565b507f4c88bc1b2b3af09b7d71bbfba500a369511504bcfcc12131b0f2eb8b736e21893383836040516116e89392919061539e565b33612cd3612283565b6001600160a01b031614612cf95760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b038116612d0c57600080fd5b61017d80546001600160a01b0319166001600160a01b0392909216919091179055565b33600090815261016b602052604090205460ff1680612d665750612d51612283565b6001600160a01b0316336001600160a01b0316145b612d825760405162461bcd60e51b8152600401610a2490615529565b61016355565b600080805b6001600160a01b038416600090815261016760205260409020612daf906136b9565b811015612e00576001600160a01b038416600090815261016760205260409020612de290612ddd90836136c3565b6147e1565b612dec90836156a0565b915080612df881615751565b915050612d8d565b5092915050565b600261012d541415612e2b5760405162461bcd60e51b8152600401610a2490615625565b600261012d556101795460ff1615612ea25760405162461bcd60e51b815260206004820152603460248201527f436c61696d696e672069732063757272656e746c79207061757365642c20706c60448201527332b0b9b2903a393c9030b3b0b4b7103630ba32b960611b6064820152608401610a24565b6000805b33600090815261016760205260409020612ebf906136b9565b811015612f075733600090815261016760205260409020612ee990612ee490836136c3565b613754565b612ef390836156a0565b915080612eff81615751565b915050612ea6565b5060008111612f4d5760405162461bcd60e51b81526020600482015260126024820152714e6f2072657761726420746f20636c61696d60701b6044820152606401610a24565b50600161012d55565b33600090815261016b602052604090205460ff1680612f8d5750612f78612283565b6001600160a01b0316336001600160a01b0316145b612fa95760405162461bcd60e51b8152600401610a2490615529565b6040805180820182529283526020808401928352600094855261016d9052909220905181559051600190910155565b33600090815261016b602052604090205460ff168061300f5750612ffa612283565b6001600160a01b0316336001600160a01b0316145b61302b5760405162461bcd60e51b8152600401610a2490615529565b6001600160a01b03811661303e57600080fd5b61017e80546001600160a01b0319166001600160a01b0392909216919091179055565b60008061306d84610f2d565b91505060005b81518110156131365760005b82828151811061309f57634e487b7160e01b600052603260045260246000fd5b60200260200101515181101561312357848383815181106130d057634e487b7160e01b600052603260045260246000fd5b602002602001015182815181106130f757634e487b7160e01b600052603260045260246000fd5b6020026020010151141561311157600193505050506109cb565b8061311b81615751565b91505061307f565b508061312e81615751565b915050613073565b506000949350505050565b33600090815261016b602052604090205460ff16806131785750613163612283565b6001600160a01b0316336001600160a01b0316145b6131945760405162461bcd60e51b8152600401610a2490615529565b806101646000828254612a1391906156a0565b33600090815261016b602052604090205460ff16806131de57506131c9612283565b6001600160a01b0316336001600160a01b0316145b6131fa5760405162461bcd60e51b8152600401610a2490615529565b613202612292565b60008151116132665760405162461bcd60e51b815260206004820152602a60248201527f426174746c65666c79466c79776865656c5661756c743a204e6f206465706f73604482015269697465642066756e647360b01b6064820152608401610a24565b60005b8151811015610b7d5761017c5482516001600160a01b039091169063fbe85f06908490849081106132aa57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b81526004016132d091815260200190565b60206040518083038186803b1580156132e857600080fd5b505afa1580156132fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133209190615174565b156134745761017c5482516001600160a01b0390911690632e1a7d4d9084908490811061335d57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b815260040161338391815260200190565b602060405180830381600087803b15801561339d57600080fd5b505af11580156133b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133d591906151d0565b506134128282815181106133f957634e487b7160e01b600052603260045260246000fd5b602002602001015161017a6148d590919063ffffffff16565b507f91db1283de26ad94dd00d7a30f701d5d8fd048ea13ca51d2ad90b2bef7368dba82828151811061345457634e487b7160e01b600052603260045260246000fd5b602002602001015160405161346b91815260200190565b60405180910390a15b8061347e81615751565b915050613269565b3361348f612283565b6001600160a01b0316146134b55760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b03811661351a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610a24565b610bfd816136db565b3361352c612283565b6001600160a01b0316146135525760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b0391909116600090815261016b60205260409020805460ff1916911515919091179055565b33600090815261016b602052604081205460ff16806135b557506135a0612283565b6001600160a01b0316336001600160a01b0316145b6135d15760405162461bcd60e51b8152600401610a2490615529565b600082815261017760205260409020546135ea816147e1565b9392505050565b336135fa612283565b6001600160a01b0316146136205760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b03811661363357600080fd5b61018280546001600160a01b0319166001600160a01b0392909216919091179055565b606060006135ea836148e1565b610a9a8363a9059cbb60e01b8484604051602401613682929190615355565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261493d565b60006109cb825490565b60006135ea8383614a0f565b60006135ea8383614a47565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b61374e846323b872dd60e01b85858560405160240161368293929190615331565b50505050565b6000816137975760405162461bcd60e51b81526020600482015260116024820152704e6f207374616b6520746f20636c61696d60781b6044820152606401610a24565b600082815261016960205260408120906137b0846147e1565b90508061016560008282546137c591906156a0565b909155505061016c546003830155600282015461015f546137f3916001600160a01b03918216911683613663565b60028201546040517f34fcbac0073d7c3d388e51312faf357774904998eeb8fca628b9e6f65ee1cbf791613836916001600160a01b0390911690879085906153c2565b60405180910390a19392505050565b60008181526101696020526040902061385d82613754565b5060005b600083815261017860205260409020613879906136b9565b81101561397e576101605460028301546000858152610178602052604090206001600160a01b03928316926342842e0e9230929116906138b990866136c3565b6040518463ffffffff1660e01b81526004016138d793929190615331565b600060405180830381600087803b1580156138f157600080fd5b505af1158015613905573d6000803e3d6000fd5b5050505060028201546000848152610178602052604090207ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568916001600160a01b031690859061395590856136c3565b604051613964939291906153c2565b60405180910390a18061397681615751565b915050613861565b506201518061016c546201518061399591906156d8565b610163546139a391906156a0565b6139ad91906156f7565b816001015410156139e8576000828152610178602052604090206139d0906136b9565b61016e60008282546139e291906156a0565b90915550505b60028101546001600160a01b0316600090815261016760205260409020613a0f90836148d5565b50600082815261016960209081526040808320838155600181018490556002810180546001600160a01b03191690556003018390556101789091528120908181613a598282614d44565b505050505050565b600061016154609614613afa5761017d60009054906101000a90046001600160a01b03166001600160a01b031663508ca1a76040518163ffffffff1660e01b815260040160206040518083038186803b158015613abd57600080fd5b505afa158015613ad1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af591906151d0565b613b81565b61017d60009054906101000a90046001600160a01b03166001600160a01b0316633583d8b86040518163ffffffff1660e01b815260040160206040518083038186803b158015613b4957600080fd5b505afa158015613b5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b8191906151d0565b61017d54604051630e7e736d60e21b81529192506000916001600160a01b03909116906339f9cdb490613bb890339060040161531d565b60206040518083038186803b158015613bd057600080fd5b505afa158015613be4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c0891906151d0565b61017d546040516326d352ab60e11b81529192506000916001600160a01b0390911690634da6a55690613c3f90339060040161531d565b60206040518083038186803b158015613c5757600080fd5b505afa158015613c6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c8f91906151d0565b9050600082613c9e86866156d8565b1115613cab576000613cbf565b613cb585856156d8565b613cbf90846156f7565b905080821115613d1f5760405162461bcd60e51b815260206004820152602560248201527f506c732077697468647261772066726f6d20466c79776865656c5661756c7420604482015264199a5c9cdd60da1b6064820152608401610a24565b5050505050565b600080613d3461017a613656565b905060005b8151811015613ec95761017c5482516000916001600160a01b03169063d387c08b90859085908110613d7b57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401613da191815260200190565b604080518083038186803b158015613db857600080fd5b505afa158015613dcc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613df09190615230565b5090508015613eb65761017c5483516001600160a01b039091169063379607f590859085908110613e3157634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401613e5791815260200190565b602060405180830381600087803b158015613e7157600080fd5b505af1158015613e85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ea991906151d0565b613eb390856156a0565b93505b5080613ec181615751565b915050613d39565b505090565b61016e5461016c54600090815261016d602052604081206001015490918291613ef791906156f7565b600061016e819055909150613f0f84620151806156d8565b61016354613f1d91906156a0565b61016f549091505b61016a548111613f995760008181526101696020526040902060010154613f4b57613f87565b60008181526101696020526040902060010154821015613f6a57613f99565b60008181526101696020526040902054613f8490846156a0565b92505b80613f9181615751565b915050613f25565b61016f555092915050565b806101816000828254613fb791906156a0565b909155505061018054158015613fd05750600061018154115b15613ff257613fe361018154600061474d565b61018055600061018155614290565b600061018054118015614087575061017c5461018054604051637df42f8360e11b81526001600160a01b039092169163fbe85f06916140379160040190815260200190565b60206040518083038186803b15801561404f57600080fd5b505afa158015614063573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140879190615174565b1561416b5761017c5461018054604051632e1a7d4d60e01b815260048101919091526000916001600160a01b031690632e1a7d4d90602401602060405180830381600087803b1580156140d957600080fd5b505af11580156140ed573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061411191906151d0565b905061412b6101805461017a6148d590919063ffffffff16565b506000610181548261413d91906156a0565b905061414a81600061474d565b61018081905561415d9061017a906136cf565b505060006101815550614290565b600061018054118015614200575061017c546101805460405163375542ed60e21b81526001600160a01b039092169163dd550bb4916141b09160040190815260200190565b60206040518083038186803b1580156141c857600080fd5b505afa1580156141dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142009190615174565b156142905761017c54610180546040516313dccf3d60e31b81526001600160a01b0390921691639ee679e89161423c9160040190815260200190565b602060405180830381600087803b15801561425657600080fd5b505af115801561426a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061428e919061527e565b505b50600061016655565b600081815261017760209081526040808320548084526101699092528220805483906142c4846147e1565b6142ce91906156b8565b90508061016560008282546142e391906156a0565b9091555050600282015461015f54614308916001600160a01b03918216911683613663565b60028201546040517f34fcbac0073d7c3d388e51312faf357774904998eeb8fca628b9e6f65ee1cbf79161434b916001600160a01b0390911690869085906153c2565b60405180910390a1949350505050565b60008181526101776020908152604080832054808452610169909252918290206101605460028201549351632142170760e11b8152929391926001600160a01b03918216926342842e0e926143ba923092909116908890600401615331565b600060405180830381600087803b1580156143d457600080fd5b505af11580156143e8573d6000803e3d6000fd5b50508254915082905060006143fc8361573a565b9091555050600083815261017760209081526040808320839055848352610178909152902061442b90846148d5565b506201518061016c546201518061444291906156d8565b6101635461445091906156a0565b61445a91906156f7565b8160010154101561447f57600161016e600082825461447991906156a0565b90915550505b80546144dd5760028101546001600160a01b03166000908152610167602052604090206144ac90836148d5565b50600082815261016960205260408120818155600181018290556002810180546001600160a01b0319169055600301555b60028101546040517ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56891614520916001600160a01b0390911690859087906153c2565b60405180910390a1505050565b60008054610100900460ff1615614574578160ff1660011480156145505750303b155b61456c5760405162461bcd60e51b8152600401610a2490615557565b506000919050565b60005460ff80841691161061459b5760405162461bcd60e51b8152600401610a2490615557565b506000805460ff191660ff92909216919091179055600190565b600054610100900460ff166120615760405162461bcd60e51b8152600401610a24906155da565b600054610100900460ff166146035760405162461bcd60e51b8152600401610a24906155da565b612061614a96565b600054610100900460ff166146325760405162461bcd60e51b8152600401610a24906155da565b612061614ac6565b8015806146c35750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b15801561468957600080fd5b505afa15801561469d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146c191906151d0565b155b61472e5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610a24565b610a9a8363095ea7b360e01b8484604051602401613682929190615355565b61017c5460405163654cfdff60e01b81526000916001600160a01b03169063654cfdff90614781908690869060040161565c565b602060405180830381600087803b15801561479b57600080fd5b505af11580156147af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147d391906151d0565b9050612e0061017a826136cf565b60008181526101696020908152604080832081516080810183528154815260018201549381019390935260028101546001600160a01b031691830191909152600301546060820181905261016c548392911415614842575060009392505050565b60008160600151600161485591906156a0565b90505b61016c54811161201f57600081815261016d6020526040902060010154158061488057508151155b1561488a576148c3565b8151600082815261016d60205260409020600181015490546148ac91906156b8565b6148b691906156d8565b6148c090846156a0565b92505b806148cd81615751565b915050614858565b60006135ea8383614af5565b60608160000180548060200260200160405190810160405280929190818152602001828054801561493157602002820191906000526020600020905b81548152602001906001019080831161491d575b50505050509050919050565b6000614992826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614c129092919063ffffffff16565b805190915015610a9a57808060200190518101906149b09190615174565b610a9a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a24565b6000826000018281548110614a3457634e487b7160e01b600052603260045260246000fd5b9060005260206000200154905092915050565b6000818152600183016020526040812054614a8e575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556109cb565b5060006109cb565b600054610100900460ff16614abd5760405162461bcd60e51b8152600401610a24906155da565b612061336136db565b600054610100900460ff16614aed5760405162461bcd60e51b8152600401610a24906155da565b600161012d55565b60008181526001830160205260408120548015614c08576000614b196001836156f7565b8554909150600090614b2d906001906156f7565b9050818114614bae576000866000018281548110614b5b57634e487b7160e01b600052603260045260246000fd5b9060005260206000200154905080876000018481548110614b8c57634e487b7160e01b600052603260045260246000fd5b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614bcd57634e487b7160e01b600052603160045260246000fd5b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506109cb565b60009150506109cb565b6060610b8a8484600085856001600160a01b0385163b614c745760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a24565b600080866001600160a01b03168587604051614c909190615301565b60006040518083038185875af1925050503d8060008114614ccd576040519150601f19603f3d011682016040523d82523d6000602084013e614cd2565b606091505b5091509150614ce2828286614ced565b979650505050505050565b60608315614cfc5750816135ea565b825115614d0c5782518084602001fd5b8160405162461bcd60e51b8152600401610a2491906154f6565b60405180604001604052806002906020820280368337509192915050565b5080546000825590600052602060002090810190610bfd91905b80821115614d725760008155600101614d5e565b5090565b80356001600160a01b038116811461134d57600080fd5b600082601f830112614d9d578081fd5b813560206001600160401b03821115614db857614db8615782565b8160051b614dc7828201615670565b838152828101908684018388018501891015614de1578687fd5b8693505b85841015614e03578035835260019390930192918401918401614de5565b50979650505050505050565b600082601f830112614e1f578081fd5b81356001600160401b03811115614e3857614e38615782565b614e4b601f8201601f1916602001615670565b818152846020838601011115614e5f578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215614e8a578081fd5b6135ea82614d76565b600080600080600060a08688031215614eaa578081fd5b614eb386614d76565b9450614ec160208701614d76565b935060408601356001600160401b0380821115614edc578283fd5b614ee889838a01614d8d565b94506060880135915080821115614efd578283fd5b614f0989838a01614d8d565b93506080880135915080821115614f1e578283fd5b50614f2b88828901614e0f565b9150509295509295909350565b60008060008060008060008060006101208a8c031215614f56578384fd5b614f5f8a614d76565b9850614f6d60208b01614d76565b975060408a01359650614f8260608b01614d76565b955060808a01359450614f9760a08b01614d76565b935060c08a0135925060e08a013591506101008a013590509295985092959850929598565b60008060008060808587031215614fd1578384fd5b614fda85614d76565b9350614fe860208601614d76565b92506040850135915060608501356001600160401b03811115615009578182fd5b61501587828801614e0f565b91505092959194509250565b600080600080600060a08688031215615038578081fd5b61504186614d76565b945061504f60208701614d76565b9350604086013592506060860135915060808601356001600160401b03811115615077578182fd5b614f2b88828901614e0f565b60008060408385031215615095578182fd5b61509e83614d76565b915060208301356150ae81615798565b809150509250929050565b600080604083850312156150cb578182fd5b6150d483614d76565b946020939093013593505050565b6000602082840312156150f3578081fd5b81356001600160401b03811115615108578182fd5b610b8a84828501614d8d565b60008060408385031215615126578182fd5b82356001600160401b0381111561513b578283fd5b61514785828601614d8d565b92505060208301356150ae81615798565b600060208284031215615169578081fd5b81356135ea81615798565b600060208284031215615185578081fd5b81516135ea81615798565b6000602082840312156151a1578081fd5b81356001600160e01b0319811681146135ea578182fd5b6000602082840312156151c9578081fd5b5035919050565b6000602082840312156151e1578081fd5b5051919050565b600080604083850312156151fa578182fd5b823591506020830135600581106150ae578182fd5b60008060408385031215615221578182fd5b50508035926020909101359150565b60008060408385031215615242578182fd5b505080516020909101519092909150565b600080600060608486031215615267578081fd5b505081359360208301359350604090920135919050565b60006020828403121561528f578081fd5b81516001600160401b03811681146135ea578182fd5b6000815180845260208085019450808401835b838110156152d4578151875295820195908201906001016152b8565b509495945050505050565b600581106152fd57634e487b7160e01b600052602160045260246000fd5b9052565b6000825161531381846020870161570e565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b60018060a01b038416815282602082015260606040820152600061539560608301846152a5565b95945050505050565b6001600160a01b03841681526020810183905260608101610b8a60408301846152df565b6001600160a01b039390931683526020830191909152604082015260600190565b6040808252835182820181905260009190606090818501906020808901865b83811015615442578151805186528381015184870152878101516001600160a01b0316888701528601518686015260809094019390820190600101615402565b505086830381880152875180845281840195509350600584901b830181019150878101865b8581101561549557601f198585030187526154838483516152a5565b96830196935090820190600101615467565b50919998505050505050505050565b60408101818360005b60028110156154cc5781518352602092830192909101906001016154ad565b50505092915050565b6020815260006135ea60208301846152a5565b602081016109cb82846152df565b602081526000825180602084015261551581604085016020870161570e565b601f01601f19169190910160400192915050565b602080825260149082015273526571756972652061646d696e2061636365737360601b604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b828152604081016135ea60208301846152df565b604051601f8201601f191681016001600160401b038111828210171561569857615698615782565b604052919050565b600082198211156156b3576156b361576c565b500190565b6000826156d357634e487b7160e01b81526012600452602481fd5b500490565b60008160001904831182151516156156f2576156f261576c565b500290565b6000828210156157095761570961576c565b500390565b60005b83811015615729578181015183820152602001615711565b8381111561374e5750506000910152565b6000816157495761574961576c565b506000190190565b60006000198214156157655761576561576c565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b8015158114610bfd57600080fdfea2646970667358221220ea67e1d96b116072c58c1b4ed9de50fafe8c64218a2fda14f14aa1877746eb4964736f6c63430008040033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103c35760003560e01c80639212567c11610206578063be70ba311161012b578063e327a6af116100c3578063f23a6e6111610087578063f23a6e611461092f578063f2fde38b1461094e578063f65a0b3e14610961578063fc68866114610974578063fe41d0561461098757600080fd5b8063e327a6af146108e2578063e6fd48bc146108f5578063e9d33b8b146108ff578063ea0f57e114610912578063f01e30351461092557600080fd5b8063be70ba31146107dc578063c0ce0bad146107ef578063c3a7ab0514610813578063c44bef751461081d578063c579007714610830578063d1058e5914610843578063d11dea5a1461084b578063d3e210e31461085e578063d5f80c34146108cf57600080fd5b8063a8ad1ca81161019e578063a8ad1ca8146106ff578063a9d3ce9914610720578063aad8750b14610733578063ac6b640a14610746578063afe89b4f14610783578063b187bd2614610796578063b25c6eaf146107a2578063b84c0b6a146107aa578063bc197c81146107bd57600080fd5b80639212567c14610670578063935b7aca1461067857806396e381541461068c578063983d95ce146106a05780639888cc83146106b35780639e6c2959146106c65780639f51563b146106cf578063a073801f146106d9578063a4ade971146106ec57600080fd5b80633d0fc343116102ec57806365ccc5b01161028457806365ccc5b0146105f6578063705aefc31461060b57806370a0823114610615578063715018a6146106285780637765968714610630578063791253b5146106435780637e1464331461064d578063853828b6146106605780638da5cb5b1461066857600080fd5b80633d0fc343146105425780633e6968b6146105555780633fc276cd1461055d5780634295d22d1461057d5780634719f251146105905780634def312d1461059a5780635daf3146146105a8578063610a8aec146105c357806363a454e0146105e357600080fd5b806317d7de7c1161035f57806317d7de7c146104ad57806318b46e9e146104c25780631d093859146104cc57806321116f4e146104d457806321ec4db9146104e757806332bd72e5146104ef57806333b69c4c146104f95780633819a9931461051a5780633a2a3bf21461052e57600080fd5b806301ffc9a7146103c857806304ef9d58146103f0578063076e298214610408578063090cdd1f1461041d5780630a7b4b2e1461043e5780630d854646146104525780630e82867b14610466578063150b7a021461047957806316c44182146104a5575b600080fd5b6103db6103d6366004615190565b61099a565b60405190151581526020015b60405180910390f35b6103fa6101715481565b6040519081526020016103e7565b61041b6104163660046151b8565b6109d1565b005b61017d54610431906001600160a01b031681565b6040516103e7919061531d565b61017e54610431906001600160a01b031681565b61015f54610431906001600160a01b031681565b61041b610474366004614e79565b610a9f565b61048c610487366004614fbc565b610b81565b6040516001600160e01b031990911681526020016103e7565b61041b610b92565b6104b5610c00565b6040516103e791906154f6565b6103fa6101705481565b61041b610c64565b61041b6104e23660046150b9565b610d2c565b61041b610d97565b6103fa61017f5481565b61050c610507366004614e79565b610f2d565b6040516103e79291906153e3565b61016254610431906001600160a01b031681565b61017c54610431906001600160a01b031681565b61041b610550366004615114565b6111c6565b6103fa611267565b61057061056b3660046151b8565b6112c2565b6040516103e791906154a4565b61041b61058b3660046150e2565b611352565b6103fa6101725481565b610179546103db9060ff1681565b61043173f5411006eefd66c213d2fd2033a1d340458b722681565b6105d66105d1366004614e79565b6116f4565b6040516103e791906154d5565b61041b6105f13660046150e2565b611cf6565b6105fe600081565b6040516103e791906154e8565b6103fa6101615481565b6103fa610623366004614e79565b611f2c565b61041b612028565b61041b61063e3660046151b8565b612063565b6103fa61016c5481565b61041b61065b3660046151b8565b612098565b61041b61215b565b610431612283565b61041b612292565b61016054610431906001600160a01b031681565b61018254610431906001600160a01b031681565b61041b6106ae3660046150e2565b612613565b61041b6106c1366004615158565b6127bd565b6103fa61271081565b6103fa6101815481565b61041b6106e7366004614e79565b612824565b61041b6106fa36600461520f565b6128b0565b6103fa61070d3660046151b8565b6101776020526000908152604090205481565b61041b61072e366004615253565b61294a565b61041b6107413660046151b8565b6129ad565b61076e6107543660046151b8565b61016d602052600090815260409020805460019091015482565b604080519283526020830191909152016103e7565b61041b610791366004614f38565b612a1b565b6101795460ff166103db565b61041b612ba8565b61041b6107b83660046151e8565b612c13565b61048c6107cb366004614e93565b63bc197c8160e01b95945050505050565b61041b6107ea366004614e79565b612cca565b6103db6107fd3660046151b8565b6101756020526000908152604090205460ff1681565b6103fa6101765481565b61041b61082b3660046151b8565b612d2f565b6103fa61083e366004614e79565b612d88565b61041b612e07565b61041b610859366004615253565b612f56565b6108a061086c3660046151b8565b610169602052600090815260409020805460018201546002830154600390930154919290916001600160a01b039091169084565b6040516103e7949392919093845260208401929092526001600160a01b03166040830152606082015260800190565b61041b6108dd366004614e79565b612fd8565b6103db6108f03660046150b9565b613061565b6103fa6101635481565b61041b61090d3660046151b8565b613141565b61041b6109203660046150e2565b6131a7565b6103fa6101805481565b61048c61093d366004615021565b63f23a6e6160e01b95945050505050565b61041b61095c366004614e79565b613486565b61041b61096f366004615083565b613523565b6103fa6109823660046151b8565b61357e565b61041b610995366004614e79565b6135f1565b60006001600160e01b03198216630271189760e51b14806109cb57506301ffc9a760e01b6001600160e01b03198316145b92915050565b33600090815261016b602052604090205460ff1680610a0857506109f3612283565b6001600160a01b0316336001600160a01b0316145b610a2d5760405162461bcd60e51b8152600401610a2490615529565b60405180910390fd5b600081815261016d602052604081206001908101549190610a4f9084906156a0565b90505b61016c548111610a9a57600081815261016d6020526040812060018101805491928592610a809084906156a0565b90915550829150610a92905081615751565b915050610a52565b505050565b33610aa8612283565b6001600160a01b031614610ace5760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b038116610ae157600080fd5b61017380546001600160a01b0319166001600160a01b0383811691821790925561015f5460405163095ea7b360e01b815292169163095ea7b391610b2b9160001990600401615355565b602060405180830381600087803b158015610b4557600080fd5b505af1158015610b59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7d9190615174565b5050565b630a85bd0160e11b5b949350505050565b33600090815261016b602052604090205460ff1680610bc95750610bb4612283565b6001600160a01b0316336001600160a01b0316145b610be55760405162461bcd60e51b8152600401610a2490615529565b6000610bf261017a613656565b9050610bfd81611cf6565b50565b60606101615460961415610c39575060408051808201909152601081526f158c4814dd185ad95c9cc815985d5b1d60821b602082015290565b5060408051808201909152601081526f158c8814dd185ad95c9cc815985d5b1d60821b602082015290565b33600090815261016b602052604090205460ff1680610c9b5750610c86612283565b6001600160a01b0316336001600160a01b0316145b610cb75760405162461bcd60e51b8152600401610a2490615529565b600261012d541415610cdb5760405162461bcd60e51b8152600401610a2490615625565b600261012d5561016a5460005b818111610d225761016354600082815261016960205260408120600181019290925560039091015580610d1a81615751565b915050610ce8565b5050600161012d55565b33600090815261016b602052604090205460ff1680610d635750610d4e612283565b6001600160a01b0316336001600160a01b0316145b610d7f5760405162461bcd60e51b8152600401610a2490615529565b61015f54610b7d906001600160a01b03168383613663565b61017654610df15760405162461bcd60e51b815260206004820152602160248201527f4e6f207061737420666f756e64657220656d697373696f6e20746f20636c61696044820152606d60f81b6064820152608401610a24565b6000610dfc336116f4565b90506000815111610e445760405162461bcd60e51b81526020600482015260126024820152714e6f20746f6b656e7320746f20636c61696d60701b6044820152606401610a24565b60005b8151811015610eb75760016101756000848481518110610e7757634e487b7160e01b600052603260045260246000fd5b6020026020010151815260200190815260200160002060006101000a81548160ff0219169083151502179055508080610eaf90615751565b915050610e47565b50610ee033825161017654610ecc91906156d8565b61015f546001600160a01b03169190613663565b7f93f2234b87606ba2c9f5e61b5e95b7ef9d74b574120ae0be68c57fe636e6ef4733825161017654610f1291906156d8565b83604051610f229392919061536e565b60405180910390a150565b6001600160a01b0381166000908152610167602052604081206060918291610f54906136b9565b6001600160401b03811115610f7957634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610fde57816020015b610fcb6040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b815260200190600190039081610f975790505b506001600160a01b03851660009081526101676020526040812091925090611005906136b9565b6001600160401b0381111561102a57634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561105d57816020015b60608152602001906001900390816110485790505b50905060005b6001600160a01b038616600090815261016760205260409020611085906136b9565b8110156111bb576001600160a01b03861660009081526101676020526040812061016991906110b490846136c3565b8152602080820192909252604090810160002081516080810183528154815260018201549381019390935260028101546001600160a01b031691830191909152600301546060820152835184908390811061111f57634e487b7160e01b600052603260045260246000fd5b602002602001018190525061117d610178600061116a8461016760008c6001600160a01b03166001600160a01b031681526020019081526020016000206136c390919063ffffffff16565b8152602001908152602001600020613656565b82828151811061119d57634e487b7160e01b600052603260045260246000fd5b602002602001018190525080806111b390615751565b915050611063565b509094909350915050565b336111cf612283565b6001600160a01b0316146111f55760405162461bcd60e51b8152600401610a24906155a5565b60005b8251811015610a9a5781610175600085848151811061122757634e487b7160e01b600052603260045260246000fd5b6020026020010151815260200190815260200160002060006101000a81548160ff021916908315150217905550808061125f90615751565b9150506111f8565b33600090815261016b602052604081205460ff168061129e5750611289612283565b6001600160a01b0316336001600160a01b0316145b6112ba5760405162461bcd60e51b8152600401610a2490615529565b5061016c5490565b6112ca614d26565b33600090815261016b602052604090205460ff168061130157506112ec612283565b6001600160a01b0316336001600160a01b0316145b61131d5760405162461bcd60e51b8152600401610a2490615529565b50604080518082018252600083815261016d60208181529382208054845291859052835260010154918101919091525b919050565b80516113b15760405162461bcd60e51b815260206004820152602860248201527f4d7573742070726f76696465206174206c65617374206f6e6520666f756e64656044820152671c8813919508125160c21b6064820152608401610a24565b60005b815181101561155b57610161546101605483516001600160a01b039091169063a2d9dd12908590859081106113f957634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b815260040161141f91815260200190565b60206040518083038186803b15801561143757600080fd5b505afa15801561144b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146f91906151d0565b146114b45760405162461bcd60e51b8152602060048201526015602482015274139bdd081d985b1a5908199bdd5b99195c88139195605a1b6044820152606401610a24565b6101605482516001600160a01b03909116906342842e0e90339030908690869081106114f057634e487b7160e01b600052603260045260246000fd5b60200260200101516040518463ffffffff1660e01b815260040161151693929190615331565b600060405180830381600087803b15801561153057600080fd5b505af1158015611544573d6000803e3d6000fd5b50505050808061155390615751565b9150506113b4565b5061016c5461016a805490600061157183615751565b90915550506040805160808101825283518152426020808301918252338385019081526060840186815261016a546000908152610169909352948220935184559151600184015590516002830180546001600160a01b0319166001600160a01b0390921691909117905591516003909101555b82518110156116935761016a54610177600085848151811061161657634e487b7160e01b600052603260045260246000fd5b602002602001015181526020019081526020016000208190555061168083828151811061165357634e487b7160e01b600052603260045260246000fd5b6020026020010151610178600061016a5481526020019081526020016000206136cf90919063ffffffff16565b508061168b81615751565b9150506115e4565b5061016a54336000908152610167602052604090206116b1916136cf565b507f55331f3fa9ca07b25728aacdb3badf1eacd93d82f1dfe128cc633a137232f9663361016a54846040516116e89392919061536e565b60405180910390a15050565b610160546040516370a0823160e01b81526060916000916001600160a01b03909116906370a082319061172b90869060040161531d565b60206040518083038186803b15801561174357600080fd5b505afa158015611757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177b91906151d0565b90506000816001600160401b038111156117a557634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156117ce578160200160208202803683370190505b5090506000805b8381101561195c5761016054604051632f745c5960e01b81526000916001600160a01b031690632f745c5990611811908a908690600401615355565b60206040518083038186803b15801561182957600080fd5b505afa15801561183d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186191906151d0565b6101605460405163516cee8960e11b8152600481018390529192506000916001600160a01b039091169063a2d9dd129060240160206040518083038186803b1580156118ac57600080fd5b505afa1580156118c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e491906151d0565b9050610161548114801561190857506000828152610175602052604090205460ff16155b15611947578185858151811061192e57634e487b7160e01b600052603260045260246000fd5b60209081029190910101528361194381615751565b9450505b5050808061195490615751565b9150506117d5565b50600061196886610f2d565b91505060008060005b83518110156119c25783818151811061199a57634e487b7160e01b600052603260045260246000fd5b602002602001015151826119ae91906156a0565b9150806119ba81615751565b915050611971565b506000816001600160401b038111156119eb57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611a14578160200160208202803683370190505b50905060005b8451811015611b98576000858281518110611a4557634e487b7160e01b600052603260045260246000fd5b6020026020010151905060005b8151811015611b83576000828281518110611a7d57634e487b7160e01b600052603260045260246000fd5b60209081029190910101516101605460405163516cee8960e11b8152600481018390529192506000916001600160a01b039091169063a2d9dd129060240160206040518083038186803b158015611ad357600080fd5b505afa158015611ae7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b0b91906151d0565b90506101615481148015611b2f57506000828152610175602052604090205460ff16155b15611b6e5781868981518110611b5557634e487b7160e01b600052603260045260246000fd5b602090810291909101015287611b6a81615751565b9850505b50508080611b7b90615751565b915050611a52565b50508080611b9090615751565b915050611a1a565b506000611ba584876156a0565b6001600160401b03811115611bca57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611bf3578160200160208202803683370190505b50905060005b86811015611c6657878181518110611c2157634e487b7160e01b600052603260045260246000fd5b6020026020010151828281518110611c4957634e487b7160e01b600052603260045260246000fd5b602090810291909101015280611c5e81615751565b915050611bf9565b50855b611c7385886156a0565b811015611ce85782611c8588836156f7565b81518110611ca357634e487b7160e01b600052603260045260246000fd5b6020026020010151828281518110611ccb57634e487b7160e01b600052603260045260246000fd5b602090810291909101015280611ce081615751565b915050611c69565b509998505050505050505050565b33600090815261016b602052604090205460ff1680611d2d5750611d18612283565b6001600160a01b0316336001600160a01b0316145b611d495760405162461bcd60e51b8152600401610a2490615529565b60005b8151811015610b7d5761017c5482516001600160a01b039091169063dd550bb490849084908110611d8d57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401611db391815260200190565b60206040518083038186803b158015611dcb57600080fd5b505afa158015611ddf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e039190615174565b15611f1a5761017c5482516001600160a01b0390911690639ee679e890849084908110611e4057634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401611e6691815260200190565b602060405180830381600087803b158015611e8057600080fd5b505af1158015611e94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb8919061527e565b507f21c77cf216591cc6caec60a5dd496cd81759bad01d20fd41961a2df074bd0c4f828281518110611efa57634e487b7160e01b600052603260045260246000fd5b6020026020010151604051611f1191815260200190565b60405180910390a15b80611f2481615751565b915050611d4c565b60006001600160a01b038216611f975760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610a24565b6001600160a01b0382166000908152610167602052604081208190611fbb906136b9565b905060005b8181101561201f576001600160a01b0385166000908152610167602052604081206101699190611ff090846136c3565b815260208101919091526040016000205461200b90846156a0565b92508061201781615751565b915050611fc0565b50909392505050565b33612031612283565b6001600160a01b0316146120575760405162461bcd60e51b8152600401610a24906155a5565b61206160006136db565b565b3361206c612283565b6001600160a01b0316146120925760405162461bcd60e51b8152600401610a24906155a5565b61017655565b33600090815261016b602052604090205460ff16806120cf57506120ba612283565b6001600160a01b0316336001600160a01b0316145b6120eb5760405162461bcd60e51b8152600401610a2490615529565b600081116120f857600080fd5b61015f54612111906001600160a01b031633308461372d565b80610166600082825461212491906156a0565b90915550506040517f8ae5bc60487649c7a0deec334e85a8f4e5b0f48c67483b3e10a00024d187c8ec90610f229033908490615355565b600261012d54141561217f5760405162461bcd60e51b8152600401610a2490615625565b600261012d553360009081526101676020526040812061219e906136b9565b116121e25760405162461bcd60e51b81526020600482015260146024820152734e6f205354414b4520746f20776974686472617760601b6044820152606401610a24565b3360009081526101676020526040812081906121fd90613656565b905060005b815181101561227957600082828151811061222d57634e487b7160e01b600052603260045260246000fd5b6020026020010151905061224081613754565b506000818152610169602052604090205461225b90856156a0565b935061226681613845565b508061227181615751565b915050612202565b50610d2282613a61565b6033546001600160a01b031690565b610182546001600160a01b031633146122e55760405162461bcd60e51b815260206004820152601560248201527414995c5d5a5c994818985d1d1b19599b1e48189bdd605a1b6044820152606401610a24565b600261012d5414156123095760405162461bcd60e51b8152600401610a2490615625565b600261012d5561016c546000906123219060016156a0565b9050600061232d613d26565b9050600061233a83613ece565b90506000808080851561254057612710610170548761235991906156d8565b61236391906156b8565b935061237c610166548561237791906156a0565b613fa4565b612710610172548761238e91906156d8565b61239891906156b8565b925082156124005761017354604051637e14643360e01b8152600481018590526001600160a01b0390911690637e14643390602401600060405180830381600087803b1580156123e757600080fd5b505af11580156123fb573d6000803e3d6000fd5b505050505b612710610171548761241291906156d8565b61241c91906156b8565b9150811561250f5761015f5461017e5460405163095ea7b360e01b81526001600160a01b039283169263095ea7b39261245c929116908690600401615355565b602060405180830381600087803b15801561247657600080fd5b505af115801561248a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ae9190615174565b5061017e546040516370055ee160e11b8152600481018490526001600160a01b039091169063e00abdc290602401600060405180830381600087803b1580156124f657600080fd5b505af115801561250a573d6000803e3d6000fd5b505050505b818361251b86896156f7565b61252591906156f7565b61252f91906156f7565b61253990826156a0565b9050612564565b610166541561255a5761255561016654613fa4565b612564565b6125646000613fa4565b80610164600082825461257791906156a0565b909155505061016c879055604080518082018252828152602080820188815260008b815261016d835284902092518355516001909201919091558151888152908101839052908101869052606081018590526080810183905260a081018490527fc87e64b388919dd4e97de1f419aae0bf9ce06b2ff3ee8e776f48a9fd51c067739060c00160405180910390a15050600161012d555050505050565b600261012d5414156126375760405162461bcd60e51b8152600401610a2490615625565b600261012d5580516126845760405162461bcd60e51b81526020600482015260166024820152754e6f20466f756e64657220746f20776974686472617760501b6044820152606401610a24565b6000805b82518110156127b357600061017760008584815181106126b857634e487b7160e01b600052603260045260246000fd5b602090810291909101810151825281810192909252604090810160009081205480825261016990935220600201549091506001600160a01b031633146127315760405162461bcd60e51b815260206004820152600e60248201526d4e6f7420796f7572207374616b6560901b6044820152606401610a24565b61276184838151811061275457634e487b7160e01b600052603260045260246000fd5b6020026020010151614299565b5061279284838151811061278557634e487b7160e01b600052603260045260246000fd5b602002602001015161435b565b8261279c81615751565b9350505080806127ab90615751565b915050612688565b50610d2281613a61565b33600090815261016b602052604090205460ff16806127f457506127df612283565b6001600160a01b0316336001600160a01b0316145b6128105760405162461bcd60e51b8152600401610a2490615529565b610179805460ff1916911515919091179055565b3361282d612283565b6001600160a01b0316146128535760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b03811661286657600080fd5b61017c80546001600160a01b0319166001600160a01b0383811691821790925561015f5460405163095ea7b360e01b815292169163095ea7b391610b2b9160001990600401615355565b33600090815261016b602052604090205460ff16806128e757506128d2612283565b6001600160a01b0316336001600160a01b0316145b6129035760405162461bcd60e51b8152600401610a2490615529565b61016c819055600061291482613ece565b604080518082018252948552602080860192835261016c54600090815261016d9091522093518455516001909301929092555050565b33612953612283565b6001600160a01b0316146129795760405162461bcd60e51b8152600401610a24906155a5565b6127108161298784866156a0565b61299191906156a0565b111561299c57600080fd5b610170929092556101715561017255565b33600090815261016b602052604090205460ff16806129e457506129cf612283565b6001600160a01b0316336001600160a01b0316145b612a005760405162461bcd60e51b8152600401610a2490615529565b806101646000828254612a1391906156f7565b909155505050565b6000612a27600161452d565b90508015612a3f576000805461ff0019166101001790555b612a476145b5565b612a4f6145b5565b612a576145dc565b612a5f61460b565b61015f80546001600160a01b03808d166001600160a01b03199283161790925561016280548c841690831617905561016080548a841692169190911790556101618990554261016855600061016a81905561016388905561016c556101708590556101718490556101728390558516612aea5761017380546001600160a01b03191630179055612b07565b61017380546001600160a01b0319166001600160a01b0387161790555b612710610172546101715461017054612b2091906156a0565b612b2a91906156a0565b1115612b3557600080fd5b6101625461015f54612b56916001600160a01b03918216911660001961463a565b8015612b9c576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050505050565b33600090815261016b602052604090205460ff1680612bdf5750612bca612283565b6001600160a01b0316336001600160a01b0316145b612bfb5760405162461bcd60e51b8152600401610a2490615529565b6000612c0861017a613656565b9050610bfd816131a7565b33600090815261016b602052604090205460ff1680612c4a5750612c35612283565b6001600160a01b0316336001600160a01b0316145b612c665760405162461bcd60e51b8152600401610a2490615529565b60008211612c7357600080fd5b61015f54612c8c906001600160a01b031633308561372d565b612c96828261474d565b507f4c88bc1b2b3af09b7d71bbfba500a369511504bcfcc12131b0f2eb8b736e21893383836040516116e89392919061539e565b33612cd3612283565b6001600160a01b031614612cf95760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b038116612d0c57600080fd5b61017d80546001600160a01b0319166001600160a01b0392909216919091179055565b33600090815261016b602052604090205460ff1680612d665750612d51612283565b6001600160a01b0316336001600160a01b0316145b612d825760405162461bcd60e51b8152600401610a2490615529565b61016355565b600080805b6001600160a01b038416600090815261016760205260409020612daf906136b9565b811015612e00576001600160a01b038416600090815261016760205260409020612de290612ddd90836136c3565b6147e1565b612dec90836156a0565b915080612df881615751565b915050612d8d565b5092915050565b600261012d541415612e2b5760405162461bcd60e51b8152600401610a2490615625565b600261012d556101795460ff1615612ea25760405162461bcd60e51b815260206004820152603460248201527f436c61696d696e672069732063757272656e746c79207061757365642c20706c60448201527332b0b9b2903a393c9030b3b0b4b7103630ba32b960611b6064820152608401610a24565b6000805b33600090815261016760205260409020612ebf906136b9565b811015612f075733600090815261016760205260409020612ee990612ee490836136c3565b613754565b612ef390836156a0565b915080612eff81615751565b915050612ea6565b5060008111612f4d5760405162461bcd60e51b81526020600482015260126024820152714e6f2072657761726420746f20636c61696d60701b6044820152606401610a24565b50600161012d55565b33600090815261016b602052604090205460ff1680612f8d5750612f78612283565b6001600160a01b0316336001600160a01b0316145b612fa95760405162461bcd60e51b8152600401610a2490615529565b6040805180820182529283526020808401928352600094855261016d9052909220905181559051600190910155565b33600090815261016b602052604090205460ff168061300f5750612ffa612283565b6001600160a01b0316336001600160a01b0316145b61302b5760405162461bcd60e51b8152600401610a2490615529565b6001600160a01b03811661303e57600080fd5b61017e80546001600160a01b0319166001600160a01b0392909216919091179055565b60008061306d84610f2d565b91505060005b81518110156131365760005b82828151811061309f57634e487b7160e01b600052603260045260246000fd5b60200260200101515181101561312357848383815181106130d057634e487b7160e01b600052603260045260246000fd5b602002602001015182815181106130f757634e487b7160e01b600052603260045260246000fd5b6020026020010151141561311157600193505050506109cb565b8061311b81615751565b91505061307f565b508061312e81615751565b915050613073565b506000949350505050565b33600090815261016b602052604090205460ff16806131785750613163612283565b6001600160a01b0316336001600160a01b0316145b6131945760405162461bcd60e51b8152600401610a2490615529565b806101646000828254612a1391906156a0565b33600090815261016b602052604090205460ff16806131de57506131c9612283565b6001600160a01b0316336001600160a01b0316145b6131fa5760405162461bcd60e51b8152600401610a2490615529565b613202612292565b60008151116132665760405162461bcd60e51b815260206004820152602a60248201527f426174746c65666c79466c79776865656c5661756c743a204e6f206465706f73604482015269697465642066756e647360b01b6064820152608401610a24565b60005b8151811015610b7d5761017c5482516001600160a01b039091169063fbe85f06908490849081106132aa57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b81526004016132d091815260200190565b60206040518083038186803b1580156132e857600080fd5b505afa1580156132fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133209190615174565b156134745761017c5482516001600160a01b0390911690632e1a7d4d9084908490811061335d57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b815260040161338391815260200190565b602060405180830381600087803b15801561339d57600080fd5b505af11580156133b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133d591906151d0565b506134128282815181106133f957634e487b7160e01b600052603260045260246000fd5b602002602001015161017a6148d590919063ffffffff16565b507f91db1283de26ad94dd00d7a30f701d5d8fd048ea13ca51d2ad90b2bef7368dba82828151811061345457634e487b7160e01b600052603260045260246000fd5b602002602001015160405161346b91815260200190565b60405180910390a15b8061347e81615751565b915050613269565b3361348f612283565b6001600160a01b0316146134b55760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b03811661351a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610a24565b610bfd816136db565b3361352c612283565b6001600160a01b0316146135525760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b0391909116600090815261016b60205260409020805460ff1916911515919091179055565b33600090815261016b602052604081205460ff16806135b557506135a0612283565b6001600160a01b0316336001600160a01b0316145b6135d15760405162461bcd60e51b8152600401610a2490615529565b600082815261017760205260409020546135ea816147e1565b9392505050565b336135fa612283565b6001600160a01b0316146136205760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b03811661363357600080fd5b61018280546001600160a01b0319166001600160a01b0392909216919091179055565b606060006135ea836148e1565b610a9a8363a9059cbb60e01b8484604051602401613682929190615355565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261493d565b60006109cb825490565b60006135ea8383614a0f565b60006135ea8383614a47565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b61374e846323b872dd60e01b85858560405160240161368293929190615331565b50505050565b6000816137975760405162461bcd60e51b81526020600482015260116024820152704e6f207374616b6520746f20636c61696d60781b6044820152606401610a24565b600082815261016960205260408120906137b0846147e1565b90508061016560008282546137c591906156a0565b909155505061016c546003830155600282015461015f546137f3916001600160a01b03918216911683613663565b60028201546040517f34fcbac0073d7c3d388e51312faf357774904998eeb8fca628b9e6f65ee1cbf791613836916001600160a01b0390911690879085906153c2565b60405180910390a19392505050565b60008181526101696020526040902061385d82613754565b5060005b600083815261017860205260409020613879906136b9565b81101561397e576101605460028301546000858152610178602052604090206001600160a01b03928316926342842e0e9230929116906138b990866136c3565b6040518463ffffffff1660e01b81526004016138d793929190615331565b600060405180830381600087803b1580156138f157600080fd5b505af1158015613905573d6000803e3d6000fd5b5050505060028201546000848152610178602052604090207ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568916001600160a01b031690859061395590856136c3565b604051613964939291906153c2565b60405180910390a18061397681615751565b915050613861565b506201518061016c546201518061399591906156d8565b610163546139a391906156a0565b6139ad91906156f7565b816001015410156139e8576000828152610178602052604090206139d0906136b9565b61016e60008282546139e291906156a0565b90915550505b60028101546001600160a01b0316600090815261016760205260409020613a0f90836148d5565b50600082815261016960209081526040808320838155600181018490556002810180546001600160a01b03191690556003018390556101789091528120908181613a598282614d44565b505050505050565b600061016154609614613afa5761017d60009054906101000a90046001600160a01b03166001600160a01b031663508ca1a76040518163ffffffff1660e01b815260040160206040518083038186803b158015613abd57600080fd5b505afa158015613ad1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af591906151d0565b613b81565b61017d60009054906101000a90046001600160a01b03166001600160a01b0316633583d8b86040518163ffffffff1660e01b815260040160206040518083038186803b158015613b4957600080fd5b505afa158015613b5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b8191906151d0565b61017d54604051630e7e736d60e21b81529192506000916001600160a01b03909116906339f9cdb490613bb890339060040161531d565b60206040518083038186803b158015613bd057600080fd5b505afa158015613be4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c0891906151d0565b61017d546040516326d352ab60e11b81529192506000916001600160a01b0390911690634da6a55690613c3f90339060040161531d565b60206040518083038186803b158015613c5757600080fd5b505afa158015613c6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c8f91906151d0565b9050600082613c9e86866156d8565b1115613cab576000613cbf565b613cb585856156d8565b613cbf90846156f7565b905080821115613d1f5760405162461bcd60e51b815260206004820152602560248201527f506c732077697468647261772066726f6d20466c79776865656c5661756c7420604482015264199a5c9cdd60da1b6064820152608401610a24565b5050505050565b600080613d3461017a613656565b905060005b8151811015613ec95761017c5482516000916001600160a01b03169063d387c08b90859085908110613d7b57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401613da191815260200190565b604080518083038186803b158015613db857600080fd5b505afa158015613dcc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613df09190615230565b5090508015613eb65761017c5483516001600160a01b039091169063379607f590859085908110613e3157634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401613e5791815260200190565b602060405180830381600087803b158015613e7157600080fd5b505af1158015613e85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ea991906151d0565b613eb390856156a0565b93505b5080613ec181615751565b915050613d39565b505090565b61016e5461016c54600090815261016d602052604081206001015490918291613ef791906156f7565b600061016e819055909150613f0f84620151806156d8565b61016354613f1d91906156a0565b61016f549091505b61016a548111613f995760008181526101696020526040902060010154613f4b57613f87565b60008181526101696020526040902060010154821015613f6a57613f99565b60008181526101696020526040902054613f8490846156a0565b92505b80613f9181615751565b915050613f25565b61016f555092915050565b806101816000828254613fb791906156a0565b909155505061018054158015613fd05750600061018154115b15613ff257613fe361018154600061474d565b61018055600061018155614290565b600061018054118015614087575061017c5461018054604051637df42f8360e11b81526001600160a01b039092169163fbe85f06916140379160040190815260200190565b60206040518083038186803b15801561404f57600080fd5b505afa158015614063573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140879190615174565b1561416b5761017c5461018054604051632e1a7d4d60e01b815260048101919091526000916001600160a01b031690632e1a7d4d90602401602060405180830381600087803b1580156140d957600080fd5b505af11580156140ed573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061411191906151d0565b905061412b6101805461017a6148d590919063ffffffff16565b506000610181548261413d91906156a0565b905061414a81600061474d565b61018081905561415d9061017a906136cf565b505060006101815550614290565b600061018054118015614200575061017c546101805460405163375542ed60e21b81526001600160a01b039092169163dd550bb4916141b09160040190815260200190565b60206040518083038186803b1580156141c857600080fd5b505afa1580156141dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142009190615174565b156142905761017c54610180546040516313dccf3d60e31b81526001600160a01b0390921691639ee679e89161423c9160040190815260200190565b602060405180830381600087803b15801561425657600080fd5b505af115801561426a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061428e919061527e565b505b50600061016655565b600081815261017760209081526040808320548084526101699092528220805483906142c4846147e1565b6142ce91906156b8565b90508061016560008282546142e391906156a0565b9091555050600282015461015f54614308916001600160a01b03918216911683613663565b60028201546040517f34fcbac0073d7c3d388e51312faf357774904998eeb8fca628b9e6f65ee1cbf79161434b916001600160a01b0390911690869085906153c2565b60405180910390a1949350505050565b60008181526101776020908152604080832054808452610169909252918290206101605460028201549351632142170760e11b8152929391926001600160a01b03918216926342842e0e926143ba923092909116908890600401615331565b600060405180830381600087803b1580156143d457600080fd5b505af11580156143e8573d6000803e3d6000fd5b50508254915082905060006143fc8361573a565b9091555050600083815261017760209081526040808320839055848352610178909152902061442b90846148d5565b506201518061016c546201518061444291906156d8565b6101635461445091906156a0565b61445a91906156f7565b8160010154101561447f57600161016e600082825461447991906156a0565b90915550505b80546144dd5760028101546001600160a01b03166000908152610167602052604090206144ac90836148d5565b50600082815261016960205260408120818155600181018290556002810180546001600160a01b0319169055600301555b60028101546040517ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56891614520916001600160a01b0390911690859087906153c2565b60405180910390a1505050565b60008054610100900460ff1615614574578160ff1660011480156145505750303b155b61456c5760405162461bcd60e51b8152600401610a2490615557565b506000919050565b60005460ff80841691161061459b5760405162461bcd60e51b8152600401610a2490615557565b506000805460ff191660ff92909216919091179055600190565b600054610100900460ff166120615760405162461bcd60e51b8152600401610a24906155da565b600054610100900460ff166146035760405162461bcd60e51b8152600401610a24906155da565b612061614a96565b600054610100900460ff166146325760405162461bcd60e51b8152600401610a24906155da565b612061614ac6565b8015806146c35750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b15801561468957600080fd5b505afa15801561469d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146c191906151d0565b155b61472e5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610a24565b610a9a8363095ea7b360e01b8484604051602401613682929190615355565b61017c5460405163654cfdff60e01b81526000916001600160a01b03169063654cfdff90614781908690869060040161565c565b602060405180830381600087803b15801561479b57600080fd5b505af11580156147af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147d391906151d0565b9050612e0061017a826136cf565b60008181526101696020908152604080832081516080810183528154815260018201549381019390935260028101546001600160a01b031691830191909152600301546060820181905261016c548392911415614842575060009392505050565b60008160600151600161485591906156a0565b90505b61016c54811161201f57600081815261016d6020526040902060010154158061488057508151155b1561488a576148c3565b8151600082815261016d60205260409020600181015490546148ac91906156b8565b6148b691906156d8565b6148c090846156a0565b92505b806148cd81615751565b915050614858565b60006135ea8383614af5565b60608160000180548060200260200160405190810160405280929190818152602001828054801561493157602002820191906000526020600020905b81548152602001906001019080831161491d575b50505050509050919050565b6000614992826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614c129092919063ffffffff16565b805190915015610a9a57808060200190518101906149b09190615174565b610a9a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a24565b6000826000018281548110614a3457634e487b7160e01b600052603260045260246000fd5b9060005260206000200154905092915050565b6000818152600183016020526040812054614a8e575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556109cb565b5060006109cb565b600054610100900460ff16614abd5760405162461bcd60e51b8152600401610a24906155da565b612061336136db565b600054610100900460ff16614aed5760405162461bcd60e51b8152600401610a24906155da565b600161012d55565b60008181526001830160205260408120548015614c08576000614b196001836156f7565b8554909150600090614b2d906001906156f7565b9050818114614bae576000866000018281548110614b5b57634e487b7160e01b600052603260045260246000fd5b9060005260206000200154905080876000018481548110614b8c57634e487b7160e01b600052603260045260246000fd5b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614bcd57634e487b7160e01b600052603160045260246000fd5b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506109cb565b60009150506109cb565b6060610b8a8484600085856001600160a01b0385163b614c745760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a24565b600080866001600160a01b03168587604051614c909190615301565b60006040518083038185875af1925050503d8060008114614ccd576040519150601f19603f3d011682016040523d82523d6000602084013e614cd2565b606091505b5091509150614ce2828286614ced565b979650505050505050565b60608315614cfc5750816135ea565b825115614d0c5782518084602001fd5b8160405162461bcd60e51b8152600401610a2491906154f6565b60405180604001604052806002906020820280368337509192915050565b5080546000825590600052602060002090810190610bfd91905b80821115614d725760008155600101614d5e565b5090565b80356001600160a01b038116811461134d57600080fd5b600082601f830112614d9d578081fd5b813560206001600160401b03821115614db857614db8615782565b8160051b614dc7828201615670565b838152828101908684018388018501891015614de1578687fd5b8693505b85841015614e03578035835260019390930192918401918401614de5565b50979650505050505050565b600082601f830112614e1f578081fd5b81356001600160401b03811115614e3857614e38615782565b614e4b601f8201601f1916602001615670565b818152846020838601011115614e5f578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215614e8a578081fd5b6135ea82614d76565b600080600080600060a08688031215614eaa578081fd5b614eb386614d76565b9450614ec160208701614d76565b935060408601356001600160401b0380821115614edc578283fd5b614ee889838a01614d8d565b94506060880135915080821115614efd578283fd5b614f0989838a01614d8d565b93506080880135915080821115614f1e578283fd5b50614f2b88828901614e0f565b9150509295509295909350565b60008060008060008060008060006101208a8c031215614f56578384fd5b614f5f8a614d76565b9850614f6d60208b01614d76565b975060408a01359650614f8260608b01614d76565b955060808a01359450614f9760a08b01614d76565b935060c08a0135925060e08a013591506101008a013590509295985092959850929598565b60008060008060808587031215614fd1578384fd5b614fda85614d76565b9350614fe860208601614d76565b92506040850135915060608501356001600160401b03811115615009578182fd5b61501587828801614e0f565b91505092959194509250565b600080600080600060a08688031215615038578081fd5b61504186614d76565b945061504f60208701614d76565b9350604086013592506060860135915060808601356001600160401b03811115615077578182fd5b614f2b88828901614e0f565b60008060408385031215615095578182fd5b61509e83614d76565b915060208301356150ae81615798565b809150509250929050565b600080604083850312156150cb578182fd5b6150d483614d76565b946020939093013593505050565b6000602082840312156150f3578081fd5b81356001600160401b03811115615108578182fd5b610b8a84828501614d8d565b60008060408385031215615126578182fd5b82356001600160401b0381111561513b578283fd5b61514785828601614d8d565b92505060208301356150ae81615798565b600060208284031215615169578081fd5b81356135ea81615798565b600060208284031215615185578081fd5b81516135ea81615798565b6000602082840312156151a1578081fd5b81356001600160e01b0319811681146135ea578182fd5b6000602082840312156151c9578081fd5b5035919050565b6000602082840312156151e1578081fd5b5051919050565b600080604083850312156151fa578182fd5b823591506020830135600581106150ae578182fd5b60008060408385031215615221578182fd5b50508035926020909101359150565b60008060408385031215615242578182fd5b505080516020909101519092909150565b600080600060608486031215615267578081fd5b505081359360208301359350604090920135919050565b60006020828403121561528f578081fd5b81516001600160401b03811681146135ea578182fd5b6000815180845260208085019450808401835b838110156152d4578151875295820195908201906001016152b8565b509495945050505050565b600581106152fd57634e487b7160e01b600052602160045260246000fd5b9052565b6000825161531381846020870161570e565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b60018060a01b038416815282602082015260606040820152600061539560608301846152a5565b95945050505050565b6001600160a01b03841681526020810183905260608101610b8a60408301846152df565b6001600160a01b039390931683526020830191909152604082015260600190565b6040808252835182820181905260009190606090818501906020808901865b83811015615442578151805186528381015184870152878101516001600160a01b0316888701528601518686015260809094019390820190600101615402565b505086830381880152875180845281840195509350600584901b830181019150878101865b8581101561549557601f198585030187526154838483516152a5565b96830196935090820190600101615467565b50919998505050505050505050565b60408101818360005b60028110156154cc5781518352602092830192909101906001016154ad565b50505092915050565b6020815260006135ea60208301846152a5565b602081016109cb82846152df565b602081526000825180602084015261551581604085016020870161570e565b601f01601f19169190910160400192915050565b602080825260149082015273526571756972652061646d696e2061636365737360601b604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b828152604081016135ea60208301846152df565b604051601f8201601f191681016001600160401b038111828210171561569857615698615782565b604052919050565b600082198211156156b3576156b361576c565b500190565b6000826156d357634e487b7160e01b81526012600452602481fd5b500490565b60008160001904831182151516156156f2576156f261576c565b500290565b6000828210156157095761570961576c565b500390565b60005b83811015615729578181015183820152602001615711565b8381111561374e5750506000910152565b6000816157495761574961576c565b506000190190565b60006000198214156157655761576561576c565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b8015158114610bfd57600080fdfea2646970667358221220ea67e1d96b116072c58c1b4ed9de50fafe8c64218a2fda14f14aa1877746eb4964736f6c63430008040033
Deployed ByteCode Sourcemap
1476:30398:71:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;661:254:14;;;;;;:::i;:::-;;:::i;:::-;;;15137:14:122;;15130:22;15112:41;;15100:2;15085:18;661:254:14;;;;;;;;3231:30:71;;;;;;;;;26896:25:122;;;26884:2;26869:18;3231:30:71;26851:76:122;25011:371:71;;;;;;:::i;:::-;;:::i;:::-;;4952:69;;;;;-1:-1:-1;;;;;4952:69:71;;;;;;;;;;:::i;5027:53::-;;;;;-1:-1:-1;;;;;5027:53:71;;;2381:30;;;;;-1:-1:-1;;;;;2381:30:71;;;30604:325;;;;;;:::i;:::-;;:::i;834:200:23:-;;;;;;:::i;:::-;;:::i;:::-;;;-1:-1:-1;;;;;;15326:33:122;;;15308:52;;15296:2;15281:18;834:200:23;15263:103:122;28118:164:71;;;:::i;21549:203::-;;;:::i;:::-;;;;;;;:::i;3194:31::-;;;;;;22683:286;;;:::i;28835:139::-;;;;;;:::i;:::-;;:::i;7147:565::-;;;:::i;5087:30::-;;;;;;10338:610;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;2528:44::-;;;;;-1:-1:-1;;;;;2528:44:71;;;4897:49;;;;;-1:-1:-1;;;;;4897:49:71;;;9784:230;;;;;;:::i;:::-;;:::i;23502:109::-;;;:::i;23695:231::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;11990:985::-;;;;;;:::i;:::-;;:::i;3267:29::-;;;;;;4775:28;;;;;;;;;3494:84;;3536:42;3494:84;;7798:1903;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;28380:347::-;;;;;;:::i;:::-;;:::i;3641:82::-;;3699:24;3641:82;;;;;;;;;:::i;2452:28::-;;;;;;11466:459;;;;;;:::i;:::-;;:::i;1908:101:4:-;;;:::i;10099:108:71:-;;;;;;:::i;:::-;;:::i;3015:29::-;;;;;;22324:267;;;;;;:::i;:::-;;:::i;13736:544::-;;;:::i;1276:85:4:-;;;:::i;25497:1788:71:-;;;:::i;2417:29::-;;;;;-1:-1:-1;;;;;2417:29:71;;;5209:27;;;;;-1:-1:-1;;;;;5209:27:71;;;15613:551;;;;;;:::i;:::-;;:::i;24432:102::-;;;;;;:::i;:::-;;:::i;3584:51::-;;3630:5;3584:51;;5166:37;;;;;;30205:297;;;;;;:::i;:::-;;:::i;23057:391::-;;;;;;:::i;:::-;;:::i;3830:51::-;;;;;;:::i;:::-;;;;;;;;;;;;;;31034:386;;;;;;:::i;:::-;;:::i;24594:110::-;;;;;;:::i;:::-;;:::i;3050:69::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;27381:25:122;;;27437:2;27422:18;;27415:34;;;;27354:18;3050:69:71;27336:119:122;5496:1450:71;;;;;;:::i;:::-;;:::i;13041:89::-;13107:16;;;;13041:89;;27392:150;;;:::i;21944:293::-;;;;;;:::i;:::-;;:::i;972:247:13:-;;;;;;:::i;:::-;-1:-1:-1;;;972:247:13;;;;;;;;29654:186:71;;;;;;:::i;:::-;;:::i;3730:51::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;3787:37;;;;;;23984:118;;;;;;:::i;:::-;;:::i;17498:314::-;;;;;;:::i;:::-;;:::i;13227:433::-;;;:::i;29059:218::-;;;;;;:::i;:::-;;:::i;2881:53::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2881:53:71;;;;;;;;;;;;;;;27691:25:122;;;27747:2;27732:18;;27725:34;;;;-1:-1:-1;;;;;27795:32:122;27790:2;27775:18;;27768:60;27859:2;27844:18;;27837:34;27678:3;27663:19;;27645:232;29342:209:71;;;;;;:::i;:::-;;:::i;10954:431::-;;;;;;:::i;:::-;;:::i;2578:29::-;;;;;;24766:112;;;;;;:::i;:::-;;:::i;27548:467::-;;;;;;:::i;:::-;;:::i;5123:37::-;;;;;;747:219:13;;;;;;:::i;:::-;-1:-1:-1;;;747:219:13;;;;;;;;2158:198:4;;;;;;:::i;:::-;;:::i;31491:113:71:-;;;;;;:::i;:::-;;:::i;24177:195::-;;;;;;:::i;:::-;;:::i;29942:158::-;;;;;;:::i;:::-;;:::i;661:254:14:-;785:4;-1:-1:-1;;;;;;808:60:14;;-1:-1:-1;;;808:60:14;;:100;;-1:-1:-1;;;;;;;;;;1168:51:30;;;872:36:14;801:107;661:254;-1:-1:-1;;661:254:14:o;25011:371:71:-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;;;;;;;;;25100:12:::1;25115:33:::0;;;:21:::1;:33;::::0;;;;:47:::1;::::0;;::::1;::::0;;25100:12;25194:14:::1;::::0;25137:10;;25194:14:::1;:::i;:::-;25178:30;;25173:203;25219:14;;25210:5;:23;25173:203;;25258:34;25295:28:::0;;;:21:::1;:28;::::0;;;;25338:19:::1;::::0;::::1;:27:::0;;25295:28;;25361:4;;25338:27:::1;::::0;25361:4;;25338:27:::1;:::i;:::-;::::0;;;-1:-1:-1;25235:7:71;;-1:-1:-1;25235:7:71::1;::::0;-1:-1:-1;25235:7:71;::::1;:::i;:::-;;;;25173:203;;;;31742:1;25011:371:::0;:::o;30604:325::-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;-1:-1:-1;;;;;30690:26:71;::::1;30682:35;;;::::0;::::1;;30727:23;:62:::0;;-1:-1:-1;;;;;;30727:62:71::1;-1:-1:-1::0;;;;;30727:62:71;;::::1;::::0;;::::1;::::0;;;30863:5:::1;::::0;:59:::1;::::0;-1:-1:-1;;;30863:59:71;;:5;::::1;::::0;:13:::1;::::0;:59:::1;::::0;-1:-1:-1;;30911:10:71;30863:59:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;30604:325:::0;:::o;834:200:23:-;-1:-1:-1;;;834:200:23;;;;;;;:::o;28118:164:71:-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;28193:20:::1;28216:19;:10;:17;:19::i;:::-;28193:42;;28245:30;28271:3;28245:25;:30::i;:::-;31742:1;28118:164::o:0;21549:203::-;21589:13;21618;;21635:3;21618:20;21614:132;;;-1:-1:-1;21654:25:71;;;;;;;;;;;;-1:-1:-1;;;21654:25:71;;;;;21549:203::o;21614:132::-;-1:-1:-1;21710:25:71;;;;;;;;;;;;-1:-1:-1;;;21710:25:71;;;;;21549:203::o;22683:286::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;1815:1:7::1;2569:7;;:19;;2561:63;;;;-1:-1:-1::0;;;2561:63:7::1;;;;;;;:::i;:::-;1815:1;2699:7;:18:::0;22781:11:71::2;::::0;22764:14:::2;22803:160;22828:6;22823:1;:11;22803:160;;22889:14;::::0;22855:16:::2;::::0;;;:13:::2;:16;::::0;;;;:31:::2;::::0;::::2;:48:::0;;;;22917:31:::2;::::0;;::::2;:35:::0;22869:1;22836:3:::2;22869:1:::0;22836:3:::2;:::i;:::-;;;;22803:160;;;-1:-1:-1::0;;1772:1:7::1;2872:7;:22:::0;22683:286:71:o;28835:139::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;28931:5:::1;::::0;:36:::1;::::0;-1:-1:-1;;;;;28931:5:71::1;28950:8:::0;28960:6;28931:18:::1;:36::i;7147:565::-:0;7203:22;;7195:73;;;;-1:-1:-1;;;7195:73:71;;24247:2:122;7195:73:71;;;24229:21:122;24286:2;24266:18;;;24259:30;24325:34;24305:18;;;24298:62;-1:-1:-1;;;24376:18:122;;;24369:31;24417:19;;7195:73:71;24219:223:122;7195:73:71;7278:25;7306:42;7337:10;7306:30;:42::i;:::-;7278:70;;7384:1;7366:8;:15;:19;7358:50;;;;-1:-1:-1;;;7358:50:71;;17806:2:122;7358:50:71;;;17788:21:122;17845:2;17825:18;;;17818:30;-1:-1:-1;;;17864:18:122;;;17857:48;17922:18;;7358:50:71;17778:168:122;7358:50:71;7423:9;7418:110;7442:8;:15;7438:1;:19;7418:110;;;7513:4;7478:19;:32;7498:8;7507:1;7498:11;;;;;;-1:-1:-1;;;7498:11:71;;;;;;;;;;;;;;;7478:32;;;;;;;;;;;;:39;;;;;;;;;;;;;;;;;;7459:3;;;;;:::i;:::-;;;;7418:110;;;;7537:72;7556:10;7593:8;:15;7568:22;;:40;;;;:::i;:::-;7537:5;;-1:-1:-1;;;;;7537:5:71;;:72;:18;:72::i;:::-;7624:81;7642:10;7679:8;:15;7654:22;;:40;;;;:::i;:::-;7696:8;7624:81;;;;;;;;:::i;:::-;;;;;;;;7147:565;:::o;10338:610::-;-1:-1:-1;;;;;10495:26:71;;10445:28;10495:26;;;:19;:26;;;;;10392:21;;;;10495:35;;:33;:35::i;:::-;-1:-1:-1;;;;;10476:55:71;;;;;-1:-1:-1;;;10476:55:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10476:55:71;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;10597:26:71;;10541:37;10597:26;;;:19;:26;;;;;10445:86;;-1:-1:-1;10541:37:71;10597:35;;:33;:35::i;:::-;-1:-1:-1;;;;;10581:52:71;;;;;-1:-1:-1;;;10581:52:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10541:92;;10648:9;10643:254;-1:-1:-1;;;;;10667:26:71;;;;;;:19;:26;;;;;:35;;:33;:35::i;:::-;10663:1;:39;10643:254;;;-1:-1:-1;;;;;10749:26:71;;10735:47;10749:26;;;:19;:26;;;;;10735:13;;:47;10749:32;;10779:1;10749:29;:32::i;:::-;10735:47;;;;;;;;;;;;;;-1:-1:-1;10735:47:71;10723:59;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;10723:59:71;;;;;;;;;;;;;;;:9;;:6;;10730:1;;10723:9;;;;-1:-1:-1;;;10723:9:71;;;;;;;;;;;;;;:59;;;;10820:66;:23;:57;10844:32;10874:1;10844:19;:26;10864:5;-1:-1:-1;;;;;10844:26:71;-1:-1:-1;;;;;10844:26:71;;;;;;;;;;;;:29;;:32;;;;:::i;:::-;10820:57;;;;;;;;;;;:64;:66::i;:::-;10796:18;10815:1;10796:21;;;;;;-1:-1:-1;;;10796:21:71;;;;;;;;;;;;;;:90;;;;10704:3;;;;;:::i;:::-;;;;10643:254;;;-1:-1:-1;10914:6:71;;10922:18;;-1:-1:-1;10338:610:71;-1:-1:-1;;10338:610:71:o;9784:230::-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;9898:9:71::1;9893:115;9917:8;:15;9913:1;:19;9893:115;;;9988:9;9953:19;:32;9973:8;9982:1;9973:11;;;;;;-1:-1:-1::0;;;9973:11:71::1;;;;;;;;;;;;;;;9953:32;;;;;;;;;;;;:44;;;;;;;;;;;;;;;;;;9934:3;;;;;:::i;:::-;;;;9893:115;;23502:109:::0;929:10:25;23564:7:71;31655:25;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;-1:-1:-1;23590:14:71::1;::::0;23502:109;:::o;23695:231::-;23785:17;;:::i;:::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;-1:-1:-1;23814:105:71::1;::::0;;;;::::1;::::0;;-1:-1:-1;23822:33:71;;;:21:::1;:33;::::0;;;;;;:47;;23814:105;;23871:33;;;;;;:47:::1;;::::0;23814:105;;::::1;::::0;;;;31742:1:::1;23695:231:::0;;;:::o;11990:985::-;12064:10;;12056:68;;;;-1:-1:-1;;;12056:68:71;;20840:2:122;12056:68:71;;;20822:21:122;20879:2;20859:18;;;20852:30;20918:34;20898:18;;;20891:62;-1:-1:-1;;;20969:18:122;;;20962:38;21017:19;;12056:68:71;20812:230:122;12056:68:71;12139:9;12134:229;12158:3;:10;12154:1;:14;12134:229;;;12237:13;;12197:10;;12226:6;;-1:-1:-1;;;;;12197:10:71;;;;:28;;12226:3;;12230:1;;12226:6;;;;-1:-1:-1;;;12226:6:71;;;;;;;;;;;;;;;12197:36;;;;;;;;;;;;;26896:25:122;;26884:2;26869:18;;26851:76;12197:36:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:53;12189:87;;;;-1:-1:-1;;;12189:87:71;;26179:2:122;12189:87:71;;;26161:21:122;26218:2;26198:18;;;26191:30;-1:-1:-1;;;26237:18:122;;;26230:51;26298:18;;12189:87:71;26151:171:122;12189:87:71;12290:10;;12345:6;;-1:-1:-1;;;;;12290:10:71;;;;:27;;12318:10;;12338:4;;12345:3;;12349:1;;12345:6;;;;-1:-1:-1;;;12345:6:71;;;;;;;;;;;;;;;12290:62;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12170:3;;;;;:::i;:::-;;;;12134:229;;;-1:-1:-1;12393:14:71;;12417:11;:13;;;12372:18;12417:13;;;:::i;:::-;;;;-1:-1:-1;;12483:192:71;;;;;;;;12522:10;;12483:192;;12566:15;12483:192;;;;;;;12606:10;12483:192;;;;;;;;;;;;12454:11;;-1:-1:-1;12440:26:71;;;:13;:26;;;;;;:245;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;12440:245:71;-1:-1:-1;;;;;12440:245:71;;;;;;;;;;;;;;;;12695:166;12719:3;:10;12715:1;:14;12695:166;;;12777:11;;12750:16;:24;12767:3;12771:1;12767:6;;;;;;-1:-1:-1;;;12767:6:71;;;;;;;;;;;;;;;12750:24;;;;;;;;;;;:38;;;;12802:48;12843:3;12847:1;12843:6;;;;;;-1:-1:-1;;;12843:6:71;;;;;;;;;;;;;;;12802:23;:36;12826:11;;12802:36;;;;;;;;;;;:40;;:48;;;;:::i;:::-;-1:-1:-1;12731:3:71;;;;:::i;:::-;;;;12695:166;;;-1:-1:-1;12906:11:71;;12890:10;12870:31;;;;:19;:31;;;;;:48;;:35;:48::i;:::-;;12933:35;12939:10;12951:11;;12964:3;12933:35;;;;;;;;:::i;:::-;;;;;;;;11990:985;;:::o;7798:1903::-;7919:10;;:26;;-1:-1:-1;;;7919:26:71;;7873:16;;7901:15;;-1:-1:-1;;;;;7919:10:71;;;;:20;;:26;;7940:4;;7919:26;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7901:44;;7955:25;7997:7;-1:-1:-1;;;;;7983:22:71;;;;;-1:-1:-1;;;7983:22:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;7983:22:71;;7955:50;;8015:22;8056:9;8051:378;8075:7;8071:1;:11;8051:378;;;8121:10;;:39;;-1:-1:-1;;;8121:39:71;;8103:15;;-1:-1:-1;;;;;8121:10:71;;:30;;:39;;8152:4;;8158:1;;8121:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;8194:10;;:37;;-1:-1:-1;;;8194:37:71;;;;;26896:25:122;;;8103:57:71;;-1:-1:-1;8174:17:71;;-1:-1:-1;;;;;8194:10:71;;;;:28;;26869:18:122;;8194:37:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;8174:57;;8262:13;;8249:9;:26;:67;;;;-1:-1:-1;8279:28:71;;;;:19;:28;;;;;;;;:37;8249:67;8245:174;;;8363:7;8336:8;8345:14;8336:24;;;;;;-1:-1:-1;;;8336:24:71;;;;;;;;;;;;;;;;;;:34;8388:16;;;;:::i;:::-;;;;8245:174;8051:378;;8084:3;;;;;:::i;:::-;;;;8051:378;;;;8441:30;8475:14;8484:4;8475:8;:14::i;:::-;8438:51;;;8499:28;8541:21;8581:9;8576:112;8600:11;:18;8596:1;:22;8576:112;;;8656:11;8668:1;8656:14;;;;;;-1:-1:-1;;;8656:14:71;;;;;;;;;;;;;;;:21;8639:38;;;;;:::i;:::-;;-1:-1:-1;8620:3:71;;;;:::i;:::-;;;;8576:112;;;;8697:32;8746:13;-1:-1:-1;;;;;8732:28:71;;;;;-1:-1:-1;;;8732:28:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;8732:28:71;;8697:63;;8775:9;8770:549;8794:11;:18;8790:1;:22;8770:549;;;8833:30;8866:11;8878:1;8866:14;;;;;;-1:-1:-1;;;8866:14:71;;;;;;;;;;;;;;;8833:47;;8899:9;8894:415;8918:13;:20;8914:1;:24;8894:415;;;8963:15;8981:13;8995:1;8981:16;;;;;;-1:-1:-1;;;8981:16:71;;;;;;;;;;;;;;;;;;;9035:10;;:37;;-1:-1:-1;;;9035:37:71;;;;;26896:25:122;;;8981:16:71;;-1:-1:-1;9015:17:71;;-1:-1:-1;;;;;9035:10:71;;;;:28;;26869:18:122;;9035:37:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9015:57;;9107:13;;9094:9;:26;:67;;;;-1:-1:-1;9124:28:71;;;;:19;:28;;;;;;;;:37;9094:67;9090:205;;;9225:7;9185:15;9201:20;9185:37;;;;;;-1:-1:-1;;;9185:37:71;;;;;;;;;;;;;;;;;;:47;9254:22;;;;:::i;:::-;;;;9090:205;8894:415;;8940:3;;;;;:::i;:::-;;;;8894:415;;;;8770:549;8814:3;;;;;:::i;:::-;;;;8770:549;;;-1:-1:-1;9329:23:71;9369:37;9386:20;9369:14;:37;:::i;:::-;-1:-1:-1;;;;;9355:52:71;;;;;-1:-1:-1;;;9355:52:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;9355:52:71;;9329:78;;9422:9;9417:93;9441:14;9437:1;:18;9417:93;;;9488:8;9497:1;9488:11;;;;;;-1:-1:-1;;;9488:11:71;;;;;;;;;;;;;;;9476:6;9483:1;9476:9;;;;;;-1:-1:-1;;;9476:9:71;;;;;;;;;;;;;;;;;;:23;9457:3;;;;:::i;:::-;;;;9417:93;;;-1:-1:-1;9536:14:71;9519:153;9556:37;9573:20;9556:14;:37;:::i;:::-;9552:1;:41;9519:153;;;9626:15;9642:18;9646:14;9642:1;:18;:::i;:::-;9626:35;;;;;;-1:-1:-1;;;9626:35:71;;;;;;;;;;;;;;;9614:6;9621:1;9614:9;;;;;;-1:-1:-1;;;9614:9:71;;;;;;;;;;;;;;;;;;:47;9595:3;;;;:::i;:::-;;;;9519:153;;;-1:-1:-1;9688:6:71;7798:1903;-1:-1:-1;;;;;;;;;7798:1903:71:o;28380:347::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;28475:9:::1;28470:251;28494:3;:10;28490:1;:14;28470:251;;;28529:17;::::0;28568:6;;-1:-1:-1;;;;;28529:17:71;;::::1;::::0;:38:::1;::::0;28568:3;;28572:1;;28568:6;::::1;;;-1:-1:-1::0;;;28568:6:71::1;;;;;;;;;;;;;;;28529:46;;;;;;;;;;;;;26896:25:122::0;;26884:2;26869:18;;26851:76;28529:46:71::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;28525:186;;;28595:17;::::0;28631:6;;-1:-1:-1;;;;;28595:17:71;;::::1;::::0;:35:::1;::::0;28631:3;;28635:1;;28631:6;::::1;;;-1:-1:-1::0;;;28631:6:71::1;;;;;;;;;;;;;;;28595:43;;;;;;;;;;;;;26896:25:122::0;;26884:2;26869:18;;26851:76;28595:43:71::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;28661:35;28689:3;28693:1;28689:6;;;;;;-1:-1:-1::0;;;28689:6:71::1;;;;;;;;;;;;;;;28661:35;;;;26896:25:122::0;;26884:2;26869:18;;26851:76;28661:35:71::1;;;;;;;;28525:186;28506:3:::0;::::1;::::0;::::1;:::i;:::-;;;;28470:251;;11466:459:::0;11523:15;-1:-1:-1;;;;;11558:19:71;;11550:74;;;;-1:-1:-1;;;11550:74:71;;22006:2:122;11550:74:71;;;21988:21:122;22045:2;22025:18;;;22018:30;22084:34;22064:18;;;22057:62;-1:-1:-1;;;22135:18:122;;;22128:40;22185:19;;11550:74:71;21978:232:122;11550:74:71;-1:-1:-1;;;;;11698:26:71;;11635:21;11698:26;;;:19;:26;;;;;11635:21;;11698:35;;:33;:35::i;:::-;11670:63;;11749:9;11744:144;11768:17;11764:1;:21;11744:144;;;-1:-1:-1;;;;;11837:26:71;;11823:47;11837:26;;;:19;:26;;;;;11823:13;;:47;11837:32;;11867:1;11837:29;:32::i;:::-;11823:47;;;;;;;;;;;-1:-1:-1;11823:47:71;:54;11806:71;;;;:::i;:::-;;-1:-1:-1;11787:3:71;;;;:::i;:::-;;;;11744:144;;;-1:-1:-1;11905:13:71;;11466:459;-1:-1:-1;;;11466:459:71:o;1908:101:4:-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;1972:30:::1;1999:1;1972:18;:30::i;:::-;1908:101::o:0;10099:108:71:-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;10169:22:71::1;:31:::0;10099:108::o;22324:267::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;22420:1:::1;22411:6;:10;22403:19;;;::::0;::::1;;22432:5;::::0;:57:::1;::::0;-1:-1:-1;;;;;22432:5:71::1;22455:10;22475:4;22482:6:::0;22432:22:::1;:57::i;:::-;22525:6;22499:22;;:32;;;;;;;:::i;:::-;::::0;;;-1:-1:-1;;22546:38:71::1;::::0;::::1;::::0;::::1;::::0;22565:10:::1;::::0;22577:6;;22546:38:::1;:::i;13736:544::-:0;1815:1:7;2569:7;;:19;;2561:63;;;;-1:-1:-1;;;2561:63:7;;;;;;;:::i;:::-;1815:1;2699:7;:18;13819:10:71::1;13842:1;13799:31:::0;;;:19:::1;:31;::::0;;;;:40:::1;::::0;:38:::1;:40::i;:::-;:44;13791:77;;;::::0;-1:-1:-1;;;13791:77:71;;20084:2:122;13791:77:71::1;::::0;::::1;20066:21:122::0;20123:2;20103:18;;;20096:30;-1:-1:-1;;;20142:18:122;;;20135:50;20202:18;;13791:77:71::1;20056:170:122::0;13791:77:71::1;13961:10;13878:21;13941:31:::0;;;:19:::1;:31;::::0;;;;13878:21;;13941:40:::1;::::0;:38:::1;:40::i;:::-;13913:68;;13996:9;13991:239;14015:8;:15;14011:1;:19;13991:239;;;14051:15;14069:8;14078:1;14069:11;;;;;;-1:-1:-1::0;;;14069:11:71::1;;;;;;;;;;;;;;;14051:29;;14094:24;14110:7;14094:15;:24::i;:::-;-1:-1:-1::0;14149:22:71::1;::::0;;;:13:::1;:22;::::0;;;;:29;14132:46:::1;::::0;;::::1;:::i;:::-;;;14192:27;14211:7;14192:18;:27::i;:::-;-1:-1:-1::0;14032:3:71;::::1;::::0;::::1;:::i;:::-;;;;13991:239;;;;14239:34;14259:13;14239:19;:34::i;1276:85:4:-:0;1348:6;;-1:-1:-1;;;;;1348:6:4;;1276:85::o;25497:1788:71:-;31816:12;;-1:-1:-1;;;;;31816:12:71;31802:10;:26;31794:60;;;;-1:-1:-1;;;31794:60:71;;23539:2:122;31794:60:71;;;23521:21:122;23578:2;23558:18;;;23551:30;-1:-1:-1;;;23597:18:122;;;23590:51;23658:18;;31794:60:71;23511:171:122;31794:60:71;1815:1:7::1;2569:7;;:19;;2561:63;;;;-1:-1:-1::0;;;2561:63:7::1;;;;;;;:::i;:::-;1815:1;2699:7;:18:::0;25595:14:71::2;::::0;25574:18:::2;::::0;25595::::2;::::0;25612:1:::2;25595:18;:::i;:::-;25574:39;;25624:26;25653:21;:19;:21::i;:::-;25624:50;;25685:29;25717:39;25745:10;25717:27;:39::i;:::-;25685:71:::0;-1:-1:-1;25767:23:71::2;::::0;;;25900;;25896:905:::2;;3630:5;25980:16;;25959:18;:37;;;;:::i;:::-;25958:61;;;;:::i;:::-;25939:81;;26034:52;26063:22;;26045:15;:40;;;;:::i;:::-;26034:10;:52::i;:::-;3630:5;26139:14;;26118:18;:35;;;;:::i;:::-;26117:59;;;;:::i;:::-;26101:75:::0;-1:-1:-1;26194:18:71;;26190:81:::2;;26214:23;::::0;:57:::2;::::0;-1:-1:-1;;;26214:57:71;;::::2;::::0;::::2;26896:25:122::0;;;-1:-1:-1;;;;;26214:23:71;;::::2;::::0;:42:::2;::::0;26869:18:122;;26214:57:71::2;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;26190:81;3630:5;26325:15;;26304:18;:36;;;;:::i;:::-;26303:60;;;;:::i;:::-;26286:77:::0;-1:-1:-1;26381:19:71;;26377:171:::2;;26420:5;::::0;26442:14:::2;::::0;26420:54:::2;::::0;-1:-1:-1;;;26420:54:71;;-1:-1:-1;;;;;26420:5:71;;::::2;::::0;:13:::2;::::0;:54:::2;::::0;26442:14;::::2;::::0;26459;;26420:54:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;26492:14:71::2;::::0;:41:::2;::::0;-1:-1:-1;;;26492:41:71;;::::2;::::0;::::2;26896:25:122::0;;;-1:-1:-1;;;;;26492:14:71;;::::2;::::0;:25:::2;::::0;26869:18:122;;26492:41:71::2;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;26377:171;26635:14:::0;26619:13;26580:36:::2;26601:15:::0;26580:18;:36:::2;:::i;:::-;:52;;;;:::i;:::-;:69;;;;:::i;:::-;26561:88;::::0;;::::2;:::i;:::-;;;25896:905;;;26670:22;::::0;:26;26666:135:::2;;26712:34;26723:22;;26712:10;:34::i;:::-;26666:135;;;26777:13;26788:1;26777:10;:13::i;:::-;26827:15;26810:13;;:32;;;;;;;:::i;:::-;::::0;;;-1:-1:-1;;26852:14:71::2;:27:::0;;;26929:126:::2;::::0;;;;::::2;::::0;;;;;::::2;::::0;;::::2;::::0;;;-1:-1:-1;26889:37:71;;;:21:::2;:37:::0;;;;;:166;;;;;::::2;::::0;;::::2;::::0;;;;27070:208;;28169:25:122;;;28210:18;;;28203:34;;;28253:18;;;28246:34;;;28311:2;28296:18;;28289:34;;;28354:3;28339:19;;28332:35;;;28398:3;28383:19;;28376:35;;;27070:208:71::2;::::0;28156:3:122;28141:19;27070:208:71::2;;;;;;;-1:-1:-1::0;;1772:1:7::1;2872:7;:22:::0;-1:-1:-1;;;;;25497:1788:71:o;15613:551::-;1815:1:7;2569:7;;:19;;2561:63;;;;-1:-1:-1;;;2561:63:7;;;;;;;:::i;:::-;1815:1;2699:7;:18;15700:17:71;;15692:56:::1;;;::::0;-1:-1:-1;;;15692:56:71;;21655:2:122;15692:56:71::1;::::0;::::1;21637:21:122::0;21694:2;21674:18;;;21667:30;-1:-1:-1;;;21713:18:122;;;21706:52;21775:18;;15692:56:71::1;21627:172:122::0;15692:56:71::1;15758:21;15798:9:::0;15793:321:::1;15817:10;:17;15813:1;:21;15793:321;;;15855:15;15873:16;:31;15890:10;15901:1;15890:13;;;;;;-1:-1:-1::0;;;15890:13:71::1;;;;;;;;;;::::0;;::::1;::::0;;;;;;;15873:31;;;;::::1;::::0;;;;;;;;-1:-1:-1;15873:31:71;;;;15926:22;;;:13:::1;:22:::0;;;;:28:::1;;::::0;15873:31;;-1:-1:-1;;;;;;15926:28:71::1;15958:10;15926:42;15918:69;;;::::0;-1:-1:-1;;;15918:69:71;;19392:2:122;15918:69:71::1;::::0;::::1;19374:21:122::0;19431:2;19411:18;;;19404:30;-1:-1:-1;;;19450:18:122;;;19443:44;19504:18;;15918:69:71::1;19364:164:122::0;15918:69:71::1;16001:35;16022:10;16033:1;16022:13;;;;;;-1:-1:-1::0;;;16022:13:71::1;;;;;;;;;;;;;;;16001:20;:35::i;:::-;;16050:24;16060:10;16071:1;16060:13;;;;;;-1:-1:-1::0;;;16060:13:71::1;;;;;;;;;;;;;;;16050:9;:24::i;:::-;16088:15:::0;::::1;::::0;::::1;:::i;:::-;;;;15793:321;15836:3;;;;;:::i;:::-;;;;15793:321;;;;16123:34;16143:13;16123:19;:34::i;24432:102::-:0;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;24501:16:::1;:26:::0;;-1:-1:-1;;24501:26:71::1;::::0;::::1;;::::0;;;::::1;::::0;;24432:102::o;30205:297::-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;-1:-1:-1;;;;;30286:20:71;::::1;30278:29;;;::::0;::::1;;30317:17;:52:::0;;-1:-1:-1;;;;;;30317:52:71::1;-1:-1:-1::0;;;;;30317:52:71;;::::1;::::0;;::::1;::::0;;;30442:5:::1;::::0;:53:::1;::::0;-1:-1:-1;;;30442:53:71;;:5;::::1;::::0;:13:::1;::::0;:53:::1;::::0;-1:-1:-1;;30484:10:71;30442:53:::1;;;:::i;23057:391::-:0;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;23166:14:::1;:27:::0;;;23203:29:::1;23235:39;23183:10:::0;23235:27:::1;:39::i;:::-;23324:117;::::0;;;;::::1;::::0;;;;;::::1;::::0;;::::1;::::0;;;23306:14:::1;::::0;-1:-1:-1;23284:37:71;;;:21:::1;:37:::0;;;;:157;;;;;::::1;::::0;;::::1;::::0;;;;-1:-1:-1;;23057:391:71:o;31034:386::-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;3630:5:71::1;31242:15:::0;31203:36:::1;31223:16:::0;31203:17;:36:::1;:::i;:::-;:54;;;;:::i;:::-;:77;;31195:86;;;::::0;::::1;;31291:16;:36:::0;;;;31337:15:::1;:34:::0;31381:14:::1;:32:::0;31034:386::o;24594:110::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;24691:6:::1;24674:13;;:23;;;;;;;:::i;:::-;::::0;;;-1:-1:-1;;;24594:110:71:o;5496:1450::-;3111:19:5;3133:25;3156:1;3133:22;:25::i;:::-;3111:47;;3172:14;3168:65;;;3202:13;:20;;-1:-1:-1;;3202:20:5;;;;;3168:65;5877:22:71::1;:20;:22::i;:::-;5909:21;:19;:21::i;:::-;5940:16;:14;:16::i;:::-;5966:24;:22;:24::i;:::-;6001:5;:40:::0;;-1:-1:-1;;;;;6001:40:71;;::::1;-1:-1:-1::0;;;;;;6001:40:71;;::::1;;::::0;;;6051:15:::1;:64:::0;;;;::::1;::::0;;::::1;;::::0;;6125:10:::1;:46:::0;;;;::::1;::::0;::::1;::::0;;;::::1;::::0;;6181:13:::1;:30:::0;;;6242:15:::1;6221:18;:36:::0;6001:5:::1;6267:11;:15:::0;;;6292:14:::1;:32:::0;;;6334:14:::1;:18:::0;6362:16:::1;:36:::0;;;6408:15:::1;:34:::0;;;6452:14:::1;:32:::0;;;6498:45;::::1;6494:222;;6557:23;:63:::0;;-1:-1:-1;;;;;;6557:63:71::1;6614:4;6557:63;::::0;;6494:222:::1;;;6635:23;:81:::0;;-1:-1:-1;;;;;;6635:81:71::1;-1:-1:-1::0;;;;;6635:81:71;::::1;;::::0;;6494:222:::1;3630:5;6772:14;;6754:15;;6735:16;;:34;;;;:::i;:::-;:51;;;;:::i;:::-;:74;;6727:83;;;::::0;::::1;;6910:15;::::0;6884:5:::1;::::0;:55:::1;::::0;-1:-1:-1;;;;;6884:5:71;;::::1;::::0;6910:15:::1;-1:-1:-1::0;;6884:17:71::1;:55::i;:::-;3257:14:5::0;3253:99;;;3303:5;3287:21;;-1:-1:-1;;3287:21:5;;;3327:14;;-1:-1:-1;17169:36:122;;3327:14:5;;17157:2:122;17142:18;3327:14:5;;;;;;;3253:99;5496:1450:71;;;;;;;;;;:::o;27392:150::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;27460:20:::1;27483:19;:10;:17;:19::i;:::-;27460:42;;27512:23;27531:3;27512:18;:23::i;21944:293::-:0;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;22062:1:::1;22053:6;:10;22045:19;;;::::0;::::1;;22074:5;::::0;:57:::1;::::0;-1:-1:-1;;;;;22074:5:71::1;22097:10;22117:4;22124:6:::0;22074:22:::1;:57::i;:::-;22141:30;22158:6;22166:4;22141:16;:30::i;:::-;;22186:44;22205:10;22217:6;22225:4;22186:44;;;;;;;;:::i;29654:186::-:0;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;-1:-1:-1;;;;;29732:19:71;::::1;29724:28;;;::::0;::::1;;29762:30;:71:::0;;-1:-1:-1;;;;;;29762:71:71::1;-1:-1:-1::0;;;;;29762:71:71;;;::::1;::::0;;;::::1;::::0;;29654:186::o;23984:118::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;24066:14:::1;:29:::0;23984:118::o;17498:314::-;17565:7;;;17617:161;-1:-1:-1;;;;;17641:25:71;;;;;;:19;:25;;;;;:34;;:32;:34::i;:::-;17637:1;:38;17617:161;;;-1:-1:-1;;;;;17735:25:71;;;;;;:19;:25;;;;;17711:56;;17735:31;;17764:1;17735:28;:31::i;:::-;17711:23;:56::i;:::-;17696:71;;;;:::i;:::-;;-1:-1:-1;17677:3:71;;;;:::i;:::-;;;;17617:161;;;-1:-1:-1;17794:11:71;17498:314;-1:-1:-1;;17498:314:71:o;13227:433::-;1815:1:7;2569:7;;:19;;2561:63;;;;-1:-1:-1;;;2561:63:7;;;;;;;:::i;:::-;1815:1;2699:7;:18;13283:16:71::1;::::0;::::1;;13279:109;;;13315:62;::::0;-1:-1:-1;;;13315:62:71;;18564:2:122;13315:62:71::1;::::0;::::1;18546:21:122::0;18603:2;18583:18;;;18576:30;18642:34;18622:18;;;18615:62;-1:-1:-1;;;18693:18:122;;;18686:50;18753:19;;13315:62:71::1;18536:242:122::0;13279:109:71::1;13398:19;13437:9:::0;13432:165:::1;13476:10;13456:31;::::0;;;:19:::1;:31;::::0;;;;:40:::1;::::0;:38:::1;:40::i;:::-;13452:1;:44;13432:165;;;13568:10;13548:31;::::0;;;:19:::1;:31;::::0;;;;13532:54:::1;::::0;13548:37:::1;::::0;13583:1;13548:34:::1;:37::i;:::-;13532:15;:54::i;:::-;13517:69;::::0;;::::1;:::i;:::-;::::0;-1:-1:-1;13498:3:71;::::1;::::0;::::1;:::i;:::-;;;;13432:165;;;;13629:1;13615:11;:15;13607:46;;;::::0;-1:-1:-1;;;13607:46:71;;25472:2:122;13607:46:71::1;::::0;::::1;25454:21:122::0;25511:2;25491:18;;;25484:30;-1:-1:-1;;;25530:18:122;;;25523:48;25588:18;;13607:46:71::1;25444:168:122::0;13607:46:71::1;-1:-1:-1::0;1772:1:7;2872:7;:22;13227:433:71:o;29059:218::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;29233:37:::1;::::0;;;;::::1;::::0;;;;;::::1;::::0;;::::1;::::0;;;-1:-1:-1;29204:26:71;;;:21:::1;:26:::0;;;;;:66;;;;;;::::1;::::0;;::::1;::::0;29059:218::o;29342:209::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;-1:-1:-1;;;;;29437:30:71;::::1;29429:39;;;::::0;::::1;;29478:14;:66:::0;;-1:-1:-1;;;;;;29478:66:71::1;-1:-1:-1::0;;;;;29478:66:71;;;::::1;::::0;;;::::1;::::0;;29342:209::o;10954:431::-;11024:4;11043:33;11080:15;11089:5;11080:8;:15::i;:::-;11040:55;;;11110:9;11105:252;11129:14;:21;11125:1;:25;11105:252;;;11176:9;11171:176;11195:14;11210:1;11195:17;;;;;;-1:-1:-1;;;11195:17:71;;;;;;;;;;;;;;;:24;11191:1;:28;11171:176;;;11272:7;11248:14;11263:1;11248:17;;;;;;-1:-1:-1;;;11248:17:71;;;;;;;;;;;;;;;11266:1;11248:20;;;;;;-1:-1:-1;;;11248:20:71;;;;;;;;;;;;;;;:31;11244:89;;;11310:4;11303:11;;;;;;;11244:89;11221:3;;;;:::i;:::-;;;;11171:176;;;-1:-1:-1;11152:3:71;;;;:::i;:::-;;;;11105:252;;;-1:-1:-1;11373:5:71;;10954:431;-1:-1:-1;;;;10954:431:71:o;24766:112::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;24865:6:::1;24848:13;;:23;;;;;;;:::i;27548:467::-:0;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;27631:20:::1;:18;:20::i;:::-;27682:1;27669:3;:10;:14;27661:69;;;::::0;-1:-1:-1;;;27661:69:71;;18153:2:122;27661:69:71::1;::::0;::::1;18135:21:122::0;18192:2;18172:18;;;18165:30;18231:34;18211:18;;;18204:62;-1:-1:-1;;;18282:18:122;;;18275:40;18332:19;;27661:69:71::1;18125:232:122::0;27661:69:71::1;27745:9;27740:269;27764:3;:10;27760:1;:14;27740:269;;;27799:17;::::0;27829:6;;-1:-1:-1;;;;;27799:17:71;;::::1;::::0;:29:::1;::::0;27829:3;;27833:1;;27829:6;::::1;;;-1:-1:-1::0;;;27829:6:71::1;;;;;;;;;;;;;;;27799:37;;;;;;;;;;;;;26896:25:122::0;;26884:2;26869:18;;26851:76;27799:37:71::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;27795:204;;;27856:17;::::0;27883:6;;-1:-1:-1;;;;;27856:17:71;;::::1;::::0;:26:::1;::::0;27883:3;;27887:1;;27883:6;::::1;;;-1:-1:-1::0;;;27883:6:71::1;;;;;;;;;;;;;;;27856:34;;;;;;;;;;;;;26896:25:122::0;;26884:2;26869:18;;26851:76;27856:34:71::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;27908:25;27926:3;27930:1;27926:6;;;;;;-1:-1:-1::0;;;27926:6:71::1;;;;;;;;;;;;;;;27908:10;:17;;:25;;;;:::i;:::-;;27956:28;27977:3;27981:1;27977:6;;;;;;-1:-1:-1::0;;;27977:6:71::1;;;;;;;;;;;;;;;27956:28;;;;26896:25:122::0;;26884:2;26869:18;;26851:76;27956:28:71::1;;;;;;;;27795:204;27776:3:::0;::::1;::::0;::::1;:::i;:::-;;;;27740:269;;2158:198:4::0;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;-1:-1:-1;;;;;2246:22:4;::::1;2238:73;;;::::0;-1:-1:-1;;;2238:73:4;;18985:2:122;2238:73:4::1;::::0;::::1;18967:21:122::0;19024:2;19004:18;;;18997:30;19063:34;19043:18;;;19036:62;-1:-1:-1;;;19114:18:122;;;19107:36;19160:19;;2238:73:4::1;18957:228:122::0;2238:73:4::1;2321:28;2340:8;2321:18;:28::i;31491:113:71:-:0;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;-1:-1:-1;;;;;31571:17:71;;;::::1;;::::0;;;:11:::1;:17;::::0;;;;:26;;-1:-1:-1;;31571:26:71::1;::::0;::::1;;::::0;;;::::1;::::0;;31491:113::o;24177:195::-;929:10:25;24254:7:71;31655:25;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;24273:15:::1;24291:25:::0;;;:16:::1;:25;::::0;;;;;24333:32:::1;24291:25:::0;24333:23:::1;:32::i;:::-;24326:39:::0;24177:195;-1:-1:-1;;;24177:195:71:o;29942:158::-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;-1:-1:-1;;;;;30027:27:71;::::1;30019:36;;;::::0;::::1;;30065:12;:28:::0;;-1:-1:-1;;;;;;30065:28:71::1;-1:-1:-1::0;;;;;30065:28:71;;;::::1;::::0;;;::::1;::::0;;29942:158::o;11950:254:34:-;12010:16;12038:22;12063:19;12071:3;12063:7;:19::i;745:216:16:-;868:86;888:5;918:23;;;943:2;947:5;895:58;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;895:58:16;;;;;;;;;;;;;;-1:-1:-1;;;;;895:58:16;-1:-1:-1;;;;;;895:58:16;;;;;;;;;;868:19;:86::i;10821:112:34:-;10881:7;10907:19;10915:3;4054:18;;3972:107;11275:135;11346:7;11380:22;11384:3;11396:5;11380:3;:22::i;10083:129::-;10150:4;10173:32;10178:3;10198:5;10173:4;:32::i;2510:187:4:-;2602:6;;;-1:-1:-1;;;;;2618:17:4;;;-1:-1:-1;;;;;;2618:17:4;;;;;;;2650:40;;2602:6;;;2618:17;2602:6;;2650:40;;2583:16;;2650:40;2510:187;;:::o;967:252:16:-;1116:96;1136:5;1166:27;;;1195:4;1201:2;1205:5;1143:68;;;;;;;;;;:::i;1116:96::-;967:252;;;;:::o;15068:477:71:-;15128:7;15155:12;15147:42;;;;-1:-1:-1;;;15147:42:71;;22832:2:122;15147:42:71;;;22814:21:122;22871:2;22851:18;;;22844:30;-1:-1:-1;;;22890:18:122;;;22883:47;22947:18;;15147:42:71;22804:167:122;15147:42:71;15199:26;15228:22;;;:13;:22;;;;;;15282:32;15242:7;15282:23;:32::i;:::-;15260:54;;15343:11;15324:15;;:30;;;;;;;:::i;:::-;;;;-1:-1:-1;;15387:14:71;;15364:20;;;:37;15430:11;;;;15411:5;;:44;;-1:-1:-1;;;;;15411:5:71;;;;15430:11;15443;15411:18;:44::i;:::-;15476:11;;;;15470:40;;;;;;-1:-1:-1;;;;;15476:11:71;;;;15489:7;;15498:11;;15470:40;:::i;:::-;;;;;;;;15527:11;15068:477;-1:-1:-1;;;15068:477:71:o;14286:776::-;14350:26;14379:22;;;:13;:22;;;;;14411:24;14393:7;14411:15;:24::i;:::-;;14450:9;14445:281;14469:32;;;;:23;:32;;;;;:41;;:39;:41::i;:::-;14465:1;:45;14445:281;;;14531:10;;14574:11;;;;14531:10;14587:32;;;:23;:32;;;;;-1:-1:-1;;;;;14531:10:71;;;;:27;;14567:4;;14574:11;;;14587:38;;14623:1;14587:35;:38::i;:::-;14531:95;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;14654:11:71;;;;;14676:32;;;:23;:32;;;;;14645:70;;-1:-1:-1;;;;;14654:11:71;;14676:32;;:38;;14712:1;14676:35;:38::i;:::-;14645:70;;;;;;;;:::i;:::-;;;;;;;;14512:3;;;;:::i;:::-;;;;14445:281;;;;14810:8;14781:14;;14799:8;14780:27;;;;:::i;:::-;14763:14;;:44;;;;:::i;:::-;:55;;;;:::i;:::-;14739:5;:20;;;:80;14735:175;;;14858:32;;;;:23;:32;;;;;:41;;:39;:41::i;:::-;14835:19;;:64;;;;;;;:::i;:::-;;;;-1:-1:-1;;14735:175:71;14939:11;;;;-1:-1:-1;;;;;14939:11:71;14919:32;;;;:19;:32;;;;;:48;;14959:7;14919:39;:48::i;:::-;-1:-1:-1;14984:22:71;;;;:13;:22;;;;;;;;14977:29;;;;;;;;;;;;;;-1:-1:-1;;;;;;14977:29:71;;;;;;;;15023:23;:32;;;;;;;14984:22;15016:39;15023:32;14984:22;15016:39;:::i;:::-;;;;;14286:776;;:::o;16170:777::-;16246:33;16282:13;;16299:3;16282:20;:148;;16381:30;;;;;;;;;-1:-1:-1;;;;;16381:30:71;-1:-1:-1;;;;;16381:47:71;;:49;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;16282:148;;;16317:30;;;;;;;;;-1:-1:-1;;;;;16317:30:71;-1:-1:-1;;;;;16317:47:71;;:49;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;16469:30;;:67;;-1:-1:-1;;;16469:67:71;;16246:184;;-1:-1:-1;16440:26:71;;-1:-1:-1;;;;;16469:30:71;;;;:55;;:67;;16525:10;;16469:67;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;16572:30;;:58;;-1:-1:-1;;;16572:58:71;;16440:96;;-1:-1:-1;16546:23:71;;-1:-1:-1;;;;;16572:30:71;;;;:46;;:58;;16619:10;;16572:58;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;16546:84;-1:-1:-1;16640:34:71;16724:18;16678:41;16706:13;16678:25;:41;:::i;:::-;16677:65;;:158;;16834:1;16677:158;;;16778:41;16806:13;16778:25;:41;:::i;:::-;16757:62;;:18;:62;:::i;:::-;16640:195;;16872:26;16853:15;:45;;16845:95;;;;-1:-1:-1;;;16845:95:71;;21249:2:122;16845:95:71;;;21231:21:122;21288:2;21268:18;;;21261:30;21327:34;21307:18;;;21300:62;-1:-1:-1;;;21378:18:122;;;21371:35;21423:19;;16845:95:71;21221:227:122;16845:95:71;16170:777;;;;;:::o;20051:370::-;20099:14;20125:20;20148:19;:10;:17;:19::i;:::-;20125:42;;20182:9;20177:238;20201:3;:10;20197:1;:14;20177:238;;;20254:17;;20293:6;;20233:15;;-1:-1:-1;;;;;20254:17:71;;:38;;20293:3;;20297:1;;20293:6;;;;-1:-1:-1;;;20293:6:71;;;;;;;;;;;;;;;20254:46;;;;;;;;;;;;;26896:25:122;;26884:2;26869:18;;26851:76;20254:46:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;20232:68:71;-1:-1:-1;20318:11:71;;20314:91;;20359:17;;20383:6;;-1:-1:-1;;;;;20359:17:71;;;;:23;;20383:3;;20387:1;;20383:6;;;;-1:-1:-1;;;20383:6:71;;;;;;;;;;;;;;;20359:31;;;;;;;;;;;;;26896:25:122;;26884:2;26869:18;;26851:76;20359:31:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;20349:41;;;;:::i;:::-;;;20314:91;-1:-1:-1;20213:3:71;;;;:::i;:::-;;;;20177:238;;;;20051:370;;:::o;19376:669::-;19540:19;;19508:14;;19450:7;19486:37;;;:21;:37;;;;;:51;;;19450:7;;;;19486:73;;19540:19;19486:73;:::i;:::-;19591:1;19569:19;:23;;;19469:90;;-1:-1:-1;19632:21:71;:10;19645:8;19632:21;:::i;:::-;19615:14;;:38;;;;:::i;:::-;19675:20;;19602:51;;-1:-1:-1;19705:277:71;19717:11;;19712:1;:16;19705:277;;19753:16;;;;:13;:16;;;;;:31;;;19749:83;;19809:8;;19749:83;19849:16;;;;:13;:16;;;;;:31;;;:36;-1:-1:-1;19845:80:71;;;19905:5;;19845:80;19948:16;;;;:13;:16;;;;;:23;19938:33;;;;:::i;:::-;;;19705:277;19730:3;;;;:::i;:::-;;;;19705:277;;;19991:20;:24;-1:-1:-1;20032:6:71;19376:669;-1:-1:-1;;19376:669:71:o;20427:1059::-;20517:15;20491:22;;:41;;;;;;;:::i;:::-;;;;-1:-1:-1;;20546:22:71;;:27;:57;;;;;20602:1;20577:22;;:26;20546:57;20542:902;;;20644:65;20661:22;;3699:24;20644:16;:65::i;:::-;20619:22;:90;20748:1;20723:22;:26;20542:902;;;20795:1;20770:22;;:26;:83;;;;-1:-1:-1;20800:17:71;;20830:22;;20800:53;;-1:-1:-1;;;20800:53:71;;-1:-1:-1;;;;;20800:17:71;;;;:29;;:53;;;;26896:25:122;;;26884:2;26869:18;;26851:76;20800:53:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;20766:678;;;20889:17;;20916:22;;20889:50;;-1:-1:-1;;;20889:50:71;;;;;26896:25:122;;;;20869:17:71;;-1:-1:-1;;;;;20889:17:71;;:26;;26869:18:122;;20889:50:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;20869:70;;20953:41;20971:22;;20953:10;:17;;:41;;;;:::i;:::-;;21008:17;21040:22;;21028:9;:34;;;;:::i;:::-;21008:54;;21101:52;21118:9;3699:24;21101:16;:52::i;:::-;21076:22;:77;;;21167:38;;:10;;:14;:38::i;:::-;-1:-1:-1;;21244:1:71;21219:22;:26;-1:-1:-1;20766:678:71;;;21291:1;21266:22;;:26;:92;;;;-1:-1:-1;21296:17:71;;21335:22;;21296:62;;-1:-1:-1;;;21296:62:71;;-1:-1:-1;;;;;21296:17:71;;;;:38;;:62;;;;26896:25:122;;;26884:2;26869:18;;26851:76;21296:62:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;21262:182;;;21374:17;;21410:22;;21374:59;;-1:-1:-1;;;21374:59:71;;-1:-1:-1;;;;;21374:17:71;;;;:35;;:59;;;;26896:25:122;;;26884:2;26869:18;;26851:76;21374:59:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;21262:182;-1:-1:-1;21478:1:71;21453:22;:26;20427:1059::o;16953:465::-;17020:7;17057:27;;;:16;:27;;;;;;;;;17123:22;;;:13;:22;;;;;17214:12;;17020:7;;17179:32;17057:27;17179:23;:32::i;:::-;:47;;;;:::i;:::-;17155:71;;17255:13;17236:15;;:32;;;;;;;:::i;:::-;;;;-1:-1:-1;;17297:11:71;;;;17278:5;;:46;;-1:-1:-1;;;;;17278:5:71;;;;17297:11;17310:13;17278:18;:46::i;:::-;17345:11;;;;17339:42;;;;;;-1:-1:-1;;;;;17345:11:71;;;;17358:7;;17367:13;;17339:42;:::i;:::-;;;;;;;;17398:13;16953:465;-1:-1:-1;;;;16953:465:71:o;18436:724::-;18493:15;18511:27;;;:16;:27;;;;;;;;;18577:22;;;:13;:22;;;;;;;18610:10;;18653:11;;;;18610:66;;-1:-1:-1;;;18610:66:71;;18511:27;;18577:22;;-1:-1:-1;;;;;18610:10:71;;;;:27;;:66;;18646:4;;18653:11;;;;18528:9;;18610:66;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;18687:14:71;;;-1:-1:-1;18687:5:71;;-1:-1:-1;18687:12:71;:14;;;:::i;:::-;;;;-1:-1:-1;;18718:27:71;;;;:16;:27;;;;;;;;18711:34;;;18755:32;;;:23;:32;;;;;:50;;18735:9;18755:39;:50::i;:::-;;18890:8;18861:14;;18879:8;18860:27;;;;:::i;:::-;18843:14;;:44;;;;:::i;:::-;:55;;;;:::i;:::-;18819:5;:20;;;:80;18815:135;;;18938:1;18915:19;;:24;;;;;;;:::i;:::-;;;;-1:-1:-1;;18815:135:71;18963:12;;18959:139;;19016:11;;;;-1:-1:-1;;;;;19016:11:71;18996:32;;;;:19;:32;;;;;:48;;19036:7;18996:39;:48::i;:::-;-1:-1:-1;19065:22:71;;;;:13;:22;;;;;19058:29;;;;;;;;;;;;;;-1:-1:-1;;;;;;19058:29:71;;;;;;18959:139;19121:11;;;;19112:41;;;;;;-1:-1:-1;;;;;19121:11:71;;;;19134:7;;19143:9;;19112:41;:::i;:::-;;;;;;;;18436:724;;;:::o;5287:808:5:-;5351:4;5684:13;;;;;;;5680:409;;;5738:7;:12;;5749:1;5738:12;:61;;;;-1:-1:-1;5793:4:5;1476:19:24;:23;5738:61:5;5713:166;;;;-1:-1:-1;;;5713:166:5;;;;;;;:::i;:::-;-1:-1:-1;5900:5:5;;5287:808;-1:-1:-1;5287:808:5:o;5680:409::-;5944:12;;:22;;;;:12;;:22;5936:81;;;;-1:-1:-1;;;5936:81:5;;;;;;;:::i;:::-;-1:-1:-1;6031:12:5;:22;;-1:-1:-1;;6031:22:5;;;;;;;;;;;;-1:-1:-1;;5287:808:5:o;596:65:13:-;4698:13:5;;;;;;;4690:69;;;;-1:-1:-1;;;4690:69:5;;;;;;;:::i;988:95:4:-;4698:13:5;;;;;;;4690:69;;;;-1:-1:-1;;;4690:69:5;;;;;;;:::i;:::-;1050:26:4::1;:24;:26::i;1853:111:7:-:0;4698:13:5;;;;;;;4690:69;;;;-1:-1:-1;;;4690:69:5;;;;;;;:::i;:::-;1923:34:7::1;:32;:34::i;1479:614:16:-:0;1845:10;;;1844:62;;-1:-1:-1;1861:39:16;;-1:-1:-1;;;1861:39:16;;1885:4;1861:39;;;10245:34:122;-1:-1:-1;;;;;10315:15:122;;;10295:18;;;10288:43;1861:15:16;;;;;10180:18:122;;1861:39:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:44;1844:62;1823:163;;;;-1:-1:-1;;;1823:163:16;;26529:2:122;1823:163:16;;;26511:21:122;26568:2;26548:18;;;26541:30;26607:34;26587:18;;;26580:62;-1:-1:-1;;;26658:18:122;;;26651:52;26720:19;;1823:163:16;26501:244:122;1823:163:16;1996:90;2016:5;2046:22;;;2070:7;2079:5;2023:62;;;;;;;;;:::i;19166:204:71:-;19289:17;;:39;;-1:-1:-1;;;19289:39:71;;19248:17;;-1:-1:-1;;;;;19289:17:71;;:25;;:39;;19315:6;;19323:4;;19289:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;19277:51;-1:-1:-1;19338:25:71;:10;19277:51;19338:14;:25::i;17818:612::-;17891:7;17971:22;;;:13;:22;;;;;;;;17943:50;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;17943:50:71;;;;;;;;;;;;;;;;;18032:14;;17891:7;;17943:50;18008:38;18004:52;;;-1:-1:-1;18055:1:71;;17818:612;-1:-1:-1;;;17818:612:71:o;18004:52::-;18072:9;18084:5;:20;;;18107:1;18084:24;;;;:::i;:::-;18072:36;;18067:329;18115:14;;18110:1;:19;18067:329;;18154:24;;;;:21;:24;;;;;:38;;;:43;;:64;;-1:-1:-1;18201:12:71;;:17;18154:64;18150:78;;;18220:8;;18150:78;18373:12;;;18315:24;;;:21;:24;;;;;:38;;;;18274;;:79;;18315:38;18274:79;:::i;:::-;18273:112;;;;:::i;:::-;18242:143;;;;:::i;:::-;;;18067:329;18131:3;;;;:::i;:::-;;;;18067:329;;10380:135:34;10450:4;10473:35;10481:3;10501:5;10473:7;:35::i;5079:109::-;5135:16;5170:3;:11;;5163:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5079:109;;;:::o;3306:717:16:-;3736:23;3762:69;3790:4;3762:69;;;;;;;;;;;;;;;;;3770:5;-1:-1:-1;;;;;3762:27:16;;;:69;;;;;:::i;:::-;3845:17;;3736:95;;-1:-1:-1;3845:21:16;3841:176;;3940:10;3929:30;;;;;;;;;;;;:::i;:::-;3921:85;;;;-1:-1:-1;;;3921:85:16;;25061:2:122;3921:85:16;;;25043:21:122;25100:2;25080:18;;;25073:30;25139:34;25119:18;;;25112:62;-1:-1:-1;;;25190:18:122;;;25183:40;25240:19;;3921:85:16;25033:232:122;4421:118:34;4488:7;4514:3;:11;;4526:5;4514:18;;;;;;-1:-1:-1;;;4514:18:34;;;;;;;;;;;;;;;;;4507:25;;4421:118;;;;:::o;1723:404::-;1786:4;3860:19;;;:12;;;:19;;;;;;1802:319;;-1:-1:-1;1844:23:34;;;;;;;;:11;:23;;;;;;;;;;;;;2024:18;;2002:19;;;:12;;;:19;;;;;;:40;;;;2056:11;;1802:319;-1:-1:-1;2105:5:34;2098:12;;1089:111:4;4698:13:5;;;;;;;4690:69;;;;-1:-1:-1;;;4690:69:5;;;;;;;:::i;:::-;1161:32:4::1;929:10:25::0;1161:18:4::1;:32::i;1970:109:7:-:0;4698:13:5;;;;;;;4690:69;;;;-1:-1:-1;;;4690:69:5;;;;;;;:::i;:::-;1772:1:7::1;2050:7;:22:::0;1970:109::o;2295:1388:34:-;2361:4;2498:19;;;:12;;;:19;;;;;;2532:15;;2528:1149;;2901:21;2925:14;2938:1;2925:10;:14;:::i;:::-;2973:18;;2901:38;;-1:-1:-1;2953:17:34;;2973:22;;2994:1;;2973:22;:::i;:::-;2953:42;;3027:13;3014:9;:26;3010:398;;3060:17;3080:3;:11;;3092:9;3080:22;;;;;;-1:-1:-1;;;3080:22:34;;;;;;;;;;;;;;;;;3060:42;;3231:9;3202:3;:11;;3214:13;3202:26;;;;;;-1:-1:-1;;;3202:26:34;;;;;;;;;;;;;;;;;;;;:38;;;;3314:23;;;:12;;;:23;;;;;:36;;;3010:398;3486:17;;:3;;:17;;;-1:-1:-1;;;3486:17:34;;;;;;;;;;;;;;;;;;;;;;;;;;3578:3;:12;;:19;3591:5;3578:19;;;;;;;;;;;3571:26;;;3619:4;3612:11;;;;;;;2528:1149;3661:5;3654:12;;;;;3872:223:24;4005:12;4036:52;4058:6;4066:4;4072:1;4075:12;4005;-1:-1:-1;;;;;1476:19:24;;;5239:60;;;;-1:-1:-1;;;5239:60:24;;23889:2:122;5239:60:24;;;23871:21:122;23928:2;23908:18;;;23901:30;23967:31;23947:18;;;23940:59;24016:18;;5239:60:24;23861:179:122;5239:60:24;5311:12;5325:23;5352:6;-1:-1:-1;;;;;5352:11:24;5371:5;5378:4;5352:31;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5310:73;;;;5400:51;5417:7;5426:10;5438:12;5400:16;:51::i;:::-;5393:58;4959:499;-1:-1:-1;;;;;;;4959:499:24:o;6622:692::-;6768:12;6796:7;6792:516;;;-1:-1:-1;6826:10:24;6819:17;;6792:516;6937:17;;:21;6933:365;;7131:10;7125:17;7191:15;7178:10;7174:2;7170:19;7163:44;7080:145;7270:12;7263:20;;-1:-1:-1;;;7263:20:24;;;;;;;;:::i;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;14:173:122:-;82:20;;-1:-1:-1;;;;;131:31:122;;121:42;;111:2;;177:1;174;167:12;192:743;246:5;299:3;292:4;284:6;280:17;276:27;266:2;;321:5;314;307:20;266:2;361:6;348:20;387:4;-1:-1:-1;;;;;406:2:122;403:26;400:2;;;432:18;;:::i;:::-;478:2;475:1;471:10;501:28;525:2;521;517:11;501:28;:::i;:::-;563:15;;;594:12;;;;626:15;;;660;;;656:24;;653:33;-1:-1:-1;650:2:122;;;703:5;696;689:20;650:2;729:5;720:14;;743:163;757:2;754:1;751:9;743:163;;;814:17;;802:30;;775:1;768:9;;;;;852:12;;;;884;;743:163;;;-1:-1:-1;924:5:122;256:679;-1:-1:-1;;;;;;;256:679:122:o;940:550::-;982:5;1035:3;1028:4;1020:6;1016:17;1012:27;1002:2;;1057:5;1050;1043:20;1002:2;1097:6;1084:20;-1:-1:-1;;;;;1119:2:122;1116:26;1113:2;;;1145:18;;:::i;:::-;1189:55;1232:2;1213:13;;-1:-1:-1;;1209:27:122;1238:4;1205:38;1189:55;:::i;:::-;1269:2;1260:7;1253:19;1315:3;1308:4;1303:2;1295:6;1291:15;1287:26;1284:35;1281:2;;;1336:5;1329;1322:20;1281:2;1405;1398:4;1390:6;1386:17;1379:4;1370:7;1366:18;1353:55;1428:16;;;1446:4;1424:27;1417:42;;;;1432:7;992:498;-1:-1:-1;;992:498:122:o;1495:196::-;1554:6;1607:2;1595:9;1586:7;1582:23;1578:32;1575:2;;;1628:6;1620;1613:22;1575:2;1656:29;1675:9;1656:29;:::i;1696:983::-;1850:6;1858;1866;1874;1882;1935:3;1923:9;1914:7;1910:23;1906:33;1903:2;;;1957:6;1949;1942:22;1903:2;1985:29;2004:9;1985:29;:::i;:::-;1975:39;;2033:38;2067:2;2056:9;2052:18;2033:38;:::i;:::-;2023:48;;2122:2;2111:9;2107:18;2094:32;-1:-1:-1;;;;;2186:2:122;2178:6;2175:14;2172:2;;;2207:6;2199;2192:22;2172:2;2235:61;2288:7;2279:6;2268:9;2264:22;2235:61;:::i;:::-;2225:71;;2349:2;2338:9;2334:18;2321:32;2305:48;;2378:2;2368:8;2365:16;2362:2;;;2399:6;2391;2384:22;2362:2;2427:63;2482:7;2471:8;2460:9;2456:24;2427:63;:::i;:::-;2417:73;;2543:3;2532:9;2528:19;2515:33;2499:49;;2573:2;2563:8;2560:16;2557:2;;;2594:6;2586;2579:22;2557:2;;2622:51;2665:7;2654:8;2643:9;2639:24;2622:51;:::i;:::-;2612:61;;;1893:786;;;;;;;;:::o;2684:764::-;2815:6;2823;2831;2839;2847;2855;2863;2871;2879;2932:3;2920:9;2911:7;2907:23;2903:33;2900:2;;;2954:6;2946;2939:22;2900:2;2982:29;3001:9;2982:29;:::i;:::-;2972:39;;3030:38;3064:2;3053:9;3049:18;3030:38;:::i;:::-;3020:48;;3115:2;3104:9;3100:18;3087:32;3077:42;;3138:38;3172:2;3161:9;3157:18;3138:38;:::i;:::-;3128:48;;3223:3;3212:9;3208:19;3195:33;3185:43;;3247:39;3281:3;3270:9;3266:19;3247:39;:::i;:::-;3237:49;;3333:3;3322:9;3318:19;3305:33;3295:43;;3385:3;3374:9;3370:19;3357:33;3347:43;;3437:3;3426:9;3422:19;3409:33;3399:43;;2890:558;;;;;;;;;;;:::o;3453:557::-;3548:6;3556;3564;3572;3625:3;3613:9;3604:7;3600:23;3596:33;3593:2;;;3647:6;3639;3632:22;3593:2;3675:29;3694:9;3675:29;:::i;:::-;3665:39;;3723:38;3757:2;3746:9;3742:18;3723:38;:::i;:::-;3713:48;;3808:2;3797:9;3793:18;3780:32;3770:42;;3863:2;3852:9;3848:18;3835:32;-1:-1:-1;;;;;3882:6:122;3879:30;3876:2;;;3927:6;3919;3912:22;3876:2;3955:49;3996:7;3987:6;3976:9;3972:22;3955:49;:::i;:::-;3945:59;;;3583:427;;;;;;;:::o;4015:626::-;4119:6;4127;4135;4143;4151;4204:3;4192:9;4183:7;4179:23;4175:33;4172:2;;;4226:6;4218;4211:22;4172:2;4254:29;4273:9;4254:29;:::i;:::-;4244:39;;4302:38;4336:2;4325:9;4321:18;4302:38;:::i;:::-;4292:48;;4387:2;4376:9;4372:18;4359:32;4349:42;;4438:2;4427:9;4423:18;4410:32;4400:42;;4493:3;4482:9;4478:19;4465:33;-1:-1:-1;;;;;4513:6:122;4510:30;4507:2;;;4558:6;4550;4543:22;4507:2;4586:49;4627:7;4618:6;4607:9;4603:22;4586:49;:::i;4646:325::-;4711:6;4719;4772:2;4760:9;4751:7;4747:23;4743:32;4740:2;;;4793:6;4785;4778:22;4740:2;4821:29;4840:9;4821:29;:::i;:::-;4811:39;;4900:2;4889:9;4885:18;4872:32;4913:28;4935:5;4913:28;:::i;:::-;4960:5;4950:15;;;4730:241;;;;;:::o;4976:264::-;5044:6;5052;5105:2;5093:9;5084:7;5080:23;5076:32;5073:2;;;5126:6;5118;5111:22;5073:2;5154:29;5173:9;5154:29;:::i;:::-;5144:39;5230:2;5215:18;;;;5202:32;;-1:-1:-1;;;5063:177:122:o;5245:368::-;5329:6;5382:2;5370:9;5361:7;5357:23;5353:32;5350:2;;;5403:6;5395;5388:22;5350:2;5448:9;5435:23;-1:-1:-1;;;;;5473:6:122;5470:30;5467:2;;;5518:6;5510;5503:22;5467:2;5546:61;5599:7;5590:6;5579:9;5575:22;5546:61;:::i;5618:497::-;5708:6;5716;5769:2;5757:9;5748:7;5744:23;5740:32;5737:2;;;5790:6;5782;5775:22;5737:2;5835:9;5822:23;-1:-1:-1;;;;;5860:6:122;5857:30;5854:2;;;5905:6;5897;5890:22;5854:2;5933:61;5986:7;5977:6;5966:9;5962:22;5933:61;:::i;:::-;5923:71;;;6044:2;6033:9;6029:18;6016:32;6057:28;6079:5;6057:28;:::i;6120:251::-;6176:6;6229:2;6217:9;6208:7;6204:23;6200:32;6197:2;;;6250:6;6242;6235:22;6197:2;6294:9;6281:23;6313:28;6335:5;6313:28;:::i;6376:255::-;6443:6;6496:2;6484:9;6475:7;6471:23;6467:32;6464:2;;;6517:6;6509;6502:22;6464:2;6554:9;6548:16;6573:28;6595:5;6573:28;:::i;6636:306::-;6694:6;6747:2;6735:9;6726:7;6722:23;6718:32;6715:2;;;6768:6;6760;6753:22;6715:2;6799:23;;-1:-1:-1;;;;;;6851:32:122;;6841:43;;6831:2;;6903:6;6895;6888:22;6947:190;7006:6;7059:2;7047:9;7038:7;7034:23;7030:32;7027:2;;;7080:6;7072;7065:22;7027:2;-1:-1:-1;7108:23:122;;7017:120;-1:-1:-1;7017:120:122:o;7142:194::-;7212:6;7265:2;7253:9;7244:7;7240:23;7236:32;7233:2;;;7286:6;7278;7271:22;7233:2;-1:-1:-1;7314:16:122;;7223:113;-1:-1:-1;7223:113:122:o;7341:354::-;7419:6;7427;7480:2;7468:9;7459:7;7455:23;7451:32;7448:2;;;7501:6;7493;7486:22;7448:2;7542:9;7529:23;7519:33;;7602:2;7591:9;7587:18;7574:32;7635:1;7628:5;7625:12;7615:2;;7656:6;7648;7641:22;7700:258;7768:6;7776;7829:2;7817:9;7808:7;7804:23;7800:32;7797:2;;;7850:6;7842;7835:22;7797:2;-1:-1:-1;;7878:23:122;;;7948:2;7933:18;;;7920:32;;-1:-1:-1;7787:171:122:o;7963:255::-;8042:6;8050;8103:2;8091:9;8082:7;8078:23;8074:32;8071:2;;;8124:6;8116;8109:22;8071:2;-1:-1:-1;;8152:16:122;;8208:2;8193:18;;;8187:25;8152:16;;8187:25;;-1:-1:-1;8061:157:122:o;8223:326::-;8300:6;8308;8316;8369:2;8357:9;8348:7;8344:23;8340:32;8337:2;;;8390:6;8382;8375:22;8337:2;-1:-1:-1;;8418:23:122;;;8488:2;8473:18;;8460:32;;-1:-1:-1;8539:2:122;8524:18;;;8511:32;;8327:222;-1:-1:-1;8327:222:122:o;8554:308::-;8623:6;8676:2;8664:9;8655:7;8651:23;8647:32;8644:2;;;8697:6;8689;8682:22;8644:2;8734:9;8728:16;-1:-1:-1;;;;;8777:5:122;8773:30;8766:5;8763:41;8753:2;;8823:6;8815;8808:22;8867:437;8920:3;8958:5;8952:12;8985:6;8980:3;8973:19;9011:4;9040:2;9035:3;9031:12;9024:19;;9077:2;9070:5;9066:14;9098:3;9110:169;9124:6;9121:1;9118:13;9110:169;;;9185:13;;9173:26;;9219:12;;;;9254:15;;;;9146:1;9139:9;9110:169;;;-1:-1:-1;9295:3:122;;8928:376;-1:-1:-1;;;;;8928:376:122:o;9309:232::-;9385:1;9378:5;9375:12;9365:2;;9430:10;9425:3;9421:20;9418:1;9411:31;9465:4;9462:1;9455:15;9493:4;9490:1;9483:15;9365:2;9517:18;;9355:186::o;9546:274::-;9675:3;9713:6;9707:13;9729:53;9775:6;9770:3;9763:4;9755:6;9751:17;9729:53;:::i;:::-;9798:16;;;;;9683:137;-1:-1:-1;;9683:137:122:o;9825:203::-;-1:-1:-1;;;;;9989:32:122;;;;9971:51;;9959:2;9944:18;;9926:102::o;10342:375::-;-1:-1:-1;;;;;10600:15:122;;;10582:34;;10652:15;;;;10647:2;10632:18;;10625:43;10699:2;10684:18;;10677:34;;;;10532:2;10517:18;;10499:218::o;10722:359::-;-1:-1:-1;;;;;10999:32:122;;;;10981:51;;11063:2;11048:18;;11041:34;10969:2;10954:18;;10936:145::o;11365:429::-;11629:1;11625;11620:3;11616:11;11612:19;11604:6;11600:32;11589:9;11582:51;11669:6;11664:2;11653:9;11649:18;11642:34;11712:2;11707;11696:9;11692:18;11685:30;11563:4;11732:56;11784:2;11773:9;11769:18;11761:6;11732:56;:::i;:::-;11724:64;11572:222;-1:-1:-1;;;;;11572:222:122:o;11799:367::-;-1:-1:-1;;;;;12027:32:122;;12009:51;;12091:2;12076:18;;12069:34;;;11997:2;11982:18;;12112:48;12156:2;12141:18;;12133:6;12112:48;:::i;12171:345::-;-1:-1:-1;;;;;12391:32:122;;;;12373:51;;12455:2;12440:18;;12433:34;;;;12498:2;12483:18;;12476:34;12361:2;12346:18;;12328:188::o;12521:1681::-;12882:2;12934:21;;;13004:13;;12907:18;;;13026:22;;;12853:4;;12882:2;13067;;13085:18;;;;13122:4;13149:15;;;12853:4;13195:388;13209:6;13206:1;13203:13;13195:388;;;13268:13;;13306:9;;13294:22;;13356:11;;;13350:18;13336:12;;;13329:40;13413:11;;;13407:18;-1:-1:-1;;;;;13403:44:122;13389:12;;;13382:66;13488:11;;13482:18;13468:12;;;13461:40;13530:4;13521:14;;;;13558:15;;;;13444:1;13224:9;13195:388;;;-1:-1:-1;;13619:19:122;;;13599:18;;;13592:47;13689:13;;13711:21;;;13750:12;;;;-1:-1:-1;13689:13:122;-1:-1:-1;13802:1:122;13798:16;;;13789:26;;13785:35;;;-1:-1:-1;13845:15:122;;;13880:4;13893:280;13909:8;13904:3;13901:17;13893:280;;;14004:2;14000:7;13994:3;13986:6;13982:16;13978:30;13971:5;13964:45;14032:53;14078:6;14067:8;14061:15;14032:53;:::i;:::-;14149:14;;;;14022:63;-1:-1:-1;14110:17:122;;;;13937:1;13928:11;13893:280;;;-1:-1:-1;14190:6:122;;12862:1340;-1:-1:-1;;;;;;;;;12862:1340:122:o;14207:494::-;14387:2;14372:18;;14376:9;14467:6;14345:4;14501:194;14515:4;14512:1;14509:11;14501:194;;;14574:13;;14562:26;;14611:4;14635:12;;;;14670:15;;;;14535:1;14528:9;14501:194;;;14505:3;;;14354:347;;;;:::o;14706:261::-;14885:2;14874:9;14867:21;14848:4;14905:56;14957:2;14946:9;14942:18;14934:6;14905:56;:::i;16813:199::-;16955:2;16940:18;;16967:39;16944:9;16988:6;16967:39;:::i;17216:383::-;17365:2;17354:9;17347:21;17328:4;17397:6;17391:13;17440:6;17435:2;17424:9;17420:18;17413:34;17456:66;17515:6;17510:2;17499:9;17495:18;17490:2;17482:6;17478:15;17456:66;:::i;:::-;17583:2;17562:15;-1:-1:-1;;17558:29:122;17543:45;;;;17590:2;17539:54;;17337:262;-1:-1:-1;;17337:262:122:o;19533:344::-;19735:2;19717:21;;;19774:2;19754:18;;;19747:30;-1:-1:-1;;;19808:2:122;19793:18;;19786:50;19868:2;19853:18;;19707:170::o;22215:410::-;22417:2;22399:21;;;22456:2;22436:18;;;22429:30;22495:34;22490:2;22475:18;;22468:62;-1:-1:-1;;;22561:2:122;22546:18;;22539:44;22615:3;22600:19;;22389:236::o;22976:356::-;23178:2;23160:21;;;23197:18;;;23190:30;23256:34;23251:2;23236:18;;23229:62;23323:2;23308:18;;23150:182::o;24447:407::-;24649:2;24631:21;;;24688:2;24668:18;;;24661:30;24727:34;24722:2;24707:18;;24700:62;-1:-1:-1;;;24793:2:122;24778:18;;24771:41;24844:3;24829:19;;24621:233::o;25617:355::-;25819:2;25801:21;;;25858:2;25838:18;;;25831:30;25897:33;25892:2;25877:18;;25870:61;25963:2;25948:18;;25791:181::o;26932:270::-;27114:25;;;27102:2;27087:18;;27148:48;27192:2;27177:18;;27169:6;27148:48;:::i;28422:275::-;28493:2;28487:9;28558:2;28539:13;;-1:-1:-1;;28535:27:122;28523:40;;-1:-1:-1;;;;;28578:34:122;;28614:22;;;28575:62;28572:2;;;28640:18;;:::i;:::-;28676:2;28669:22;28467:230;;-1:-1:-1;28467:230:122:o;28702:128::-;28742:3;28773:1;28769:6;28766:1;28763:13;28760:2;;;28779:18;;:::i;:::-;-1:-1:-1;28815:9:122;;28750:80::o;28835:217::-;28875:1;28901;28891:2;;-1:-1:-1;;;28926:31:122;;28980:4;28977:1;28970:15;29008:4;28933:1;28998:15;28891:2;-1:-1:-1;29037:9:122;;28881:171::o;29057:168::-;29097:7;29163:1;29159;29155:6;29151:14;29148:1;29145:21;29140:1;29133:9;29126:17;29122:45;29119:2;;;29170:18;;:::i;:::-;-1:-1:-1;29210:9:122;;29109:116::o;29230:125::-;29270:4;29298:1;29295;29292:8;29289:2;;;29303:18;;:::i;:::-;-1:-1:-1;29340:9:122;;29279:76::o;29360:258::-;29432:1;29442:113;29456:6;29453:1;29450:13;29442:113;;;29532:11;;;29526:18;29513:11;;;29506:39;29478:2;29471:10;29442:113;;;29573:6;29570:1;29567:13;29564:2;;;-1:-1:-1;;29608:1:122;29590:16;;29583:27;29413:205::o;29623:136::-;29662:3;29690:5;29680:2;;29699:18;;:::i;:::-;-1:-1:-1;;;29735:18:122;;29670:89::o;29764:135::-;29803:3;-1:-1:-1;;29824:17:122;;29821:2;;;29844:18;;:::i;:::-;-1:-1:-1;29891:1:122;29880:13;;29811:88::o;29904:127::-;29965:10;29960:3;29956:20;29953:1;29946:31;29996:4;29993:1;29986:15;30020:4;30017:1;30010:15;30036:127;30097:10;30092:3;30088:20;30085:1;30078:31;30128:4;30125:1;30118:15;30152:4;30149:1;30142:15;30168:118;30254:5;30247:13;30240:21;30233:5;30230:32;30220:2;;30276:1;30273;30266:12
Metadata Hash
ea67e1d96b116072c58c1b4ed9de50fafe8c64218a2fda14f14aa1877746eb49
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.