Source Code
Overview
BNB Balance
BNB Value
$0.00Cross-Chain Transactions
Loading...
Loading
Contract Name:
NFTMarket
Compiler Version
v0.6.5+commit.f956cc89
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity ^0.6.0;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol";
import "@openzeppelin/contracts/utils/EnumerableSet.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/introspection/ERC165Checker.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "abdk-libraries-solidity/ABDKMath64x64.sol";
import "./interfaces/IPriceOracle.sol";
import "./characters.sol";
import "./weapons.sol";
import "./cryptoblades.sol";
import "./BurningManager.sol";
// *****************************************************************************
// *** NOTE: almost all uses of _tokenAddress in this contract are UNSAFE!!! ***
// *****************************************************************************
contract NFTMarket is
IERC721ReceiverUpgradeable,
Initializable,
AccessControlUpgradeable
{
using SafeMath for uint256;
using ABDKMath64x64 for int128; // kroge beware
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableSet for EnumerableSet.AddressSet;
using SafeERC20 for IERC20;
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
bytes32 public constant GAME_ADMIN = keccak256("GAME_ADMIN");
// ############
// Initializer
// ############
function initialize(IERC20 _skillToken, address _taxRecipient)
public
initializer
{
__AccessControl_init();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
skillToken = _skillToken;
taxRecipient = _taxRecipient;
defaultTax = ABDKMath64x64.divu(1, 10); // 10%
}
function migrateTo_a98a9ac(Characters _charactersContract, Weapons _weaponsContract) external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
characters = _charactersContract;
weapons = _weaponsContract;
}
function migrateTo_2316231(IPriceOracle _priceOracleSkillPerUsd) external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
priceOracleSkillPerUsd = _priceOracleSkillPerUsd;
addFee = ABDKMath64x64.divu(2, 100); // 0.02 usd;
changeFee = ABDKMath64x64.divu(0, 100); // 0.00 usd;
}
function migrateTo_29635ef(BurningManager _burningManager) external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
burningManager = _burningManager;
}
// basic listing; we can easily offer other types (auction / buy it now)
// if the struct can be extended, that's one way, otherwise different mapping per type.
struct Listing {
address seller;
uint256 price;
//int128 usdTether; // this would be to "tether" price dynamically to our oracle
}
// ############
// State
// ############
IERC20 public skillToken; //0x154A9F9cbd3449AD22FDaE23044319D6eF2a1Fab;
address public taxRecipient; //game contract
// address is IERC721 -- kept like this because of OpenZeppelin upgrade plugin bug
mapping(address => mapping(uint256 => Listing)) private listings;
// address is IERC721 -- kept like this because of OpenZeppelin upgrade plugin bug
mapping(address => EnumerableSet.UintSet) private listedTokenIDs;
// address is IERC721
EnumerableSet.AddressSet private listedTokenTypes; // stored for a way to know the types we have on offer
// UNUSED; KEPT FOR UPGRADEABILITY PROXY COMPATIBILITY
mapping(address => bool) public isTokenBanned;
mapping(address => bool) public isUserBanned;
// address is IERC721 -- kept like this because of OpenZeppelin upgrade plugin bug
mapping(address => int128) public tax; // per NFT type tax
// address is IERC721 -- kept like this because of OpenZeppelin upgrade plugin bug
mapping(address => bool) private freeTax; // since tax is 0-default, this specifies it to fix an exploit
int128 public defaultTax; // fallback in case we haven't specified it
// address is IERC721 -- kept like this because of OpenZeppelin upgrade plugin bug
EnumerableSet.AddressSet private allowedTokenTypes;
Weapons internal weapons;
Characters internal characters;
IPriceOracle public priceOracleSkillPerUsd;
int128 public addFee;
int128 public changeFee;
// keeps target buyer for nftId of specific type (address)
mapping(address => mapping(uint256 => address)) nftTargetBuyers;
BurningManager public burningManager;
// ############
// Events
// ############
event NewListing(
address indexed seller,
IERC721 indexed nftAddress,
uint256 indexed nftID,
uint256 price,
address targetBuyer
);
event ListingPriceChange(
address indexed seller,
IERC721 indexed nftAddress,
uint256 indexed nftID,
uint256 newPrice
);
event ListingTargetBuyerChange(
address indexed seller,
IERC721 indexed nftAddress,
uint256 indexed nftID,
address newTargetBuyer
);
event CancelledListing(
address indexed seller,
IERC721 indexed nftAddress,
uint256 indexed nftID
);
event PurchasedListing(
address indexed buyer,
address seller,
IERC721 indexed nftAddress,
uint256 indexed nftID,
uint256 price
);
// ############
// Modifiers
// ############
modifier restricted() {
require(hasRole(GAME_ADMIN, msg.sender), "Not game admin");
_;
}
modifier isListed(IERC721 _tokenAddress, uint256 id) {
require(
listedTokenTypes.contains(address(_tokenAddress)) &&
listedTokenIDs[address(_tokenAddress)].contains(id),
"Token ID not listed"
);
_;
}
modifier isNotListed(IERC721 _tokenAddress, uint256 id) {
require(
!listedTokenTypes.contains(address(_tokenAddress)) ||
!listedTokenIDs[address(_tokenAddress)].contains(id),
"Token ID must not be listed"
);
_;
}
modifier isSeller(IERC721 _tokenAddress, uint256 id) {
require(
listings[address(_tokenAddress)][id].seller == msg.sender,
"Access denied"
);
_;
}
modifier isSellerOrAdmin(IERC721 _tokenAddress, uint256 id) {
require(
listings[address(_tokenAddress)][id].seller == msg.sender ||
hasRole(GAME_ADMIN, msg.sender),
"Access denied"
);
_;
}
modifier tokenNotBanned(IERC721 _tokenAddress) {
require(
isTokenAllowed(_tokenAddress),
"This type of NFT may not be traded here"
);
_;
}
modifier userNotBanned() {
require(isUserBanned[msg.sender] == false, "Forbidden access");
_;
}
modifier userAllowedToPurchase(IERC721 _tokenAddress, uint256 _id) {
require(nftTargetBuyers[address(_tokenAddress)][_id] == address(0) || nftTargetBuyers[address(_tokenAddress)][_id] == msg.sender, "Not target buyer");
_;
}
modifier isValidERC721(IERC721 _tokenAddress) {
require(
ERC165Checker.supportsInterface(
address(_tokenAddress),
_INTERFACE_ID_ERC721
)
);
_;
}
// ############
// Views
// ############
function isTokenAllowed(IERC721 _tokenAddress) public view returns (bool) {
return allowedTokenTypes.contains(address(_tokenAddress));
}
function getAllowedTokenTypes() public view returns (IERC721[] memory) {
EnumerableSet.AddressSet storage set = allowedTokenTypes;
IERC721[] memory tokens = new IERC721[](set.length());
for (uint256 i = 0; i < tokens.length; i++) {
tokens[i] = IERC721(set.at(i));
}
return tokens;
}
function getSellerOfNftID(IERC721 _tokenAddress, uint256 _tokenId) public view returns (address) {
if(!listedTokenTypes.contains(address(_tokenAddress))) {
return address(0);
}
if(!listedTokenIDs[address(_tokenAddress)].contains(_tokenId)) {
return address(0);
}
return listings[address(_tokenAddress)][_tokenId].seller;
}
function defaultTaxAsRoundedPercentRoughEstimate() public view returns (uint256) {
return defaultTax.mulu(100);
}
function getListedTokenTypes() public view returns (IERC721[] memory) {
EnumerableSet.AddressSet storage set = listedTokenTypes;
IERC721[] memory tokens = new IERC721[](set.length());
for (uint256 i = 0; i < tokens.length; i++) {
tokens[i] = IERC721(set.at(i));
}
return tokens;
}
function getListingIDs(IERC721 _tokenAddress)
public
view
returns (uint256[] memory)
{
EnumerableSet.UintSet storage set = listedTokenIDs[address(_tokenAddress)];
uint256[] memory tokens = new uint256[](set.length());
for (uint256 i = 0; i < tokens.length; i++) {
tokens[i] = set.at(i);
}
return tokens;
}
function getWeaponListingIDsPage(IERC721 _tokenAddress, uint8 _limit, uint256 _pageNumber, uint8 _trait, uint8 _stars)
public
view
returns (uint256[] memory)
{
EnumerableSet.UintSet storage set = listedTokenIDs[address(_tokenAddress)];
uint256 matchingWeaponsAmount = getNumberOfWeaponListings(_tokenAddress, _trait, _stars);
uint256 pageEnd = _limit * (_pageNumber + 1);
uint256 tokensSize = matchingWeaponsAmount >= pageEnd ? _limit : matchingWeaponsAmount.sub(_limit * _pageNumber);
uint256[] memory tokens = new uint256[](tokensSize);
uint256 counter = 0;
uint8 tokenIterator = 0;
for (uint256 i = 0; i < set.length() && counter < pageEnd; i++) {
uint8 weaponTrait = weapons.getTrait(set.at(i));
uint8 weaponStars = weapons.getStars(set.at(i));
if((_trait == 255 || weaponTrait == _trait) && (_stars == 255 || weaponStars == _stars)) {
if(counter >= pageEnd - _limit) {
tokens[tokenIterator] = set.at(i);
tokenIterator++;
}
counter++;
}
}
return tokens;
}
function getCharacterListingIDsPage(IERC721 _tokenAddress, uint8 _limit, uint256 _pageNumber, uint8 _trait, uint8 _minLevel, uint8 _maxLevel)
public
view
returns (uint256[] memory)
{
EnumerableSet.UintSet storage set = listedTokenIDs[address(_tokenAddress)];
uint256 matchingCharactersAmount = getNumberOfCharacterListings(_tokenAddress, _trait, _minLevel, _maxLevel);
uint256 pageEnd = _limit * (_pageNumber + 1);
uint256 tokensSize = matchingCharactersAmount >= pageEnd ? _limit : matchingCharactersAmount.sub(_limit * _pageNumber);
uint256[] memory tokens = new uint256[](tokensSize);
uint256 counter = 0;
uint8 tokenIterator = 0;
for (uint256 i = 0; i < set.length() && counter < pageEnd; i++) {
uint8 characterTrait = characters.getTrait(set.at(i));
uint8 characterLevel = characters.getLevel(set.at(i));
if((_trait == 255 || characterTrait == _trait) && (_minLevel == 255 || _maxLevel == 255 || (characterLevel >= _minLevel && characterLevel <= _maxLevel))) {
if(counter >= pageEnd - _limit) {
tokens[tokenIterator] = set.at(i);
tokenIterator++;
}
counter++;
}
}
return tokens;
}
function getNumberOfListingsBySeller(
IERC721 _tokenAddress,
address _seller
) public view returns (uint256) {
EnumerableSet.UintSet storage listedTokens = listedTokenIDs[address(_tokenAddress)];
uint256 amount = 0;
for (uint256 i = 0; i < listedTokens.length(); i++) {
if (
listings[address(_tokenAddress)][listedTokens.at(i)].seller == _seller
) amount++;
}
return amount;
}
function getListingIDsBySeller(IERC721 _tokenAddress, address _seller)
public
view
returns (uint256[] memory tokens)
{
// NOTE: listedTokens is enumerated twice (once for length calc, once for getting token IDs)
uint256 amount = getNumberOfListingsBySeller(_tokenAddress, _seller);
tokens = new uint256[](amount);
EnumerableSet.UintSet storage listedTokens = listedTokenIDs[address(_tokenAddress)];
uint256 index = 0;
for (uint256 i = 0; i < listedTokens.length(); i++) {
uint256 id = listedTokens.at(i);
if (listings[address(_tokenAddress)][id].seller == _seller)
tokens[index++] = id;
}
}
function getNumberOfListingsForToken(IERC721 _tokenAddress)
public
view
returns (uint256)
{
return listedTokenIDs[address(_tokenAddress)].length();
}
function getNumberOfCharacterListings(IERC721 _tokenAddress, uint8 _trait, uint8 _minLevel, uint8 _maxLevel)
public
view
returns (uint256)
{
EnumerableSet.UintSet storage listedTokens = listedTokenIDs[address(_tokenAddress)];
uint256 counter = 0;
uint8 characterLevel;
uint8 characterTrait;
for(uint256 i = 0; i < listedTokens.length(); i++) {
characterLevel = characters.getLevel(listedTokens.at(i));
characterTrait = characters.getTrait(listedTokens.at(i));
if((_trait == 255 || characterTrait == _trait) && (_minLevel == 255 || _maxLevel == 255 || (characterLevel >= _minLevel && characterLevel <= _maxLevel))) {
counter++;
}
}
return counter;
}
function getNumberOfWeaponListings(IERC721 _tokenAddress, uint8 _trait, uint8 _stars)
public
view
returns (uint256)
{
EnumerableSet.UintSet storage listedTokens = listedTokenIDs[address(_tokenAddress)];
uint256 counter = 0;
uint8 weaponTrait;
uint8 weaponStars;
for(uint256 i = 0; i < listedTokens.length(); i++) {
weaponTrait = weapons.getTrait(listedTokens.at(i));
weaponStars = weapons.getStars(listedTokens.at(i));
if((_trait == 255 || weaponTrait == _trait) && (_stars == 255 || weaponStars == _stars)) {
counter++;
}
}
return counter;
}
function getSellerPrice(IERC721 _tokenAddress, uint256 _id)
public
view
returns (uint256)
{
return listings[address(_tokenAddress)][_id].price;
}
function getFinalPrice(IERC721 _tokenAddress, uint256 _id)
public
view
returns (uint256)
{
return
getSellerPrice(_tokenAddress, _id).add(
getTaxOnListing(_tokenAddress, _id)
);
}
function getTaxOnListing(IERC721 _tokenAddress, uint256 _id)
public
view
returns (uint256)
{
return
ABDKMath64x64.mulu(
tax[address(_tokenAddress)],
getSellerPrice(_tokenAddress, _id)
);
}
function getTargetBuyer(IERC721 _tokenAddress, uint256 _id)
public
view
returns (address)
{
return nftTargetBuyers[address(_tokenAddress)][_id];
}
function getListingSlice(IERC721 _tokenAddress, uint256 start, uint256 length)
public
view
returns (uint256 returnedCount, uint256[] memory ids, address[] memory sellers, uint256[] memory prices)
{
returnedCount = length;
ids = new uint256[](length);
sellers = new address[](length);
prices = new uint256[](length);
uint index = 0;
EnumerableSet.UintSet storage listedTokens = listedTokenIDs[address(_tokenAddress)];
for(uint i = start; i < start+length; i++) {
if(i >= listedTokens.length())
return(index, ids, sellers, prices);
uint256 id = listedTokens.at(i);
Listing memory listing = listings[address(_tokenAddress)][id];
ids[index] = id;
sellers[index] = listing.seller;
prices[index++] = listing.price;
}
}
// ############
// Mutative
// ############
function addListing(
IERC721 _tokenAddress,
uint256 _id,
uint256 _price,
address _targetBuyer
)
public
//userNotBanned // temp
tokenNotBanned(_tokenAddress)
isValidERC721(_tokenAddress)
isNotListed(_tokenAddress, _id)
{
if(addFee > 0) {
payTax(usdToSkill(addFee));
}
if(isUserBanned[msg.sender]) {
uint256 app = skillToken.allowance(msg.sender, address(this));
uint256 bal = skillToken.balanceOf(msg.sender);
skillToken.transferFrom(msg.sender, taxRecipient, app > bal ? bal : app);
}
else {
listings[address(_tokenAddress)][_id] = Listing(msg.sender, _price);
nftTargetBuyers[address(_tokenAddress)][_id] = _targetBuyer;
listedTokenIDs[address(_tokenAddress)].add(_id);
_updateListedTokenTypes(_tokenAddress);
}
// in theory the transfer and required approval already test non-owner operations
_tokenAddress.safeTransferFrom(msg.sender, address(this), _id);
emit NewListing(msg.sender, _tokenAddress, _id, _price, _targetBuyer);
}
function changeListingPrice(
IERC721 _tokenAddress,
uint256 _id,
uint256 _newPrice
)
public
userNotBanned
isListed(_tokenAddress, _id)
isSeller(_tokenAddress, _id)
{
if(changeFee > 0) {
payTax(usdToSkill(changeFee));
}
listings[address(_tokenAddress)][_id].price = _newPrice;
emit ListingPriceChange(
msg.sender,
_tokenAddress,
_id,
_newPrice
);
}
function changeListingTargetBuyer(
IERC721 _tokenAddress,
uint256 _id,
address _newTargetBuyer
)
public
userNotBanned
isListed(_tokenAddress, _id)
isSeller(_tokenAddress, _id)
{
nftTargetBuyers[address(_tokenAddress)][_id] = _newTargetBuyer;
emit ListingTargetBuyerChange(
msg.sender,
_tokenAddress,
_id,
_newTargetBuyer
);
}
function cancelListing(IERC721 _tokenAddress, uint256 _id)
public
userNotBanned
isListed(_tokenAddress, _id)
isSellerOrAdmin(_tokenAddress, _id)
{
delete listings[address(_tokenAddress)][_id];
listedTokenIDs[address(_tokenAddress)].remove(_id);
_updateListedTokenTypes(_tokenAddress);
_tokenAddress.safeTransferFrom(address(this), msg.sender, _id);
emit CancelledListing(msg.sender, _tokenAddress, _id);
}
function purchaseListing(
IERC721 _tokenAddress,
uint256 _id,
uint256 _maxPrice
) public userNotBanned isListed(_tokenAddress, _id) userAllowedToPurchase(_tokenAddress, _id) {
(uint256 finalPrice, Listing memory listing) = executePurchaseLogic(_tokenAddress, _id, _maxPrice);
_tokenAddress.safeTransferFrom(address(this), msg.sender, _id);
emit PurchasedListing(
msg.sender,
listing.seller,
_tokenAddress,
_id,
finalPrice
);
}
function executePurchaseLogic(
IERC721 _tokenAddress,
uint256 _id,
uint256 _maxPrice
) private returns(uint256, Listing memory) {
uint256 finalPrice = getFinalPrice(_tokenAddress, _id);
require(finalPrice <= _maxPrice, "Buying price too low");
Listing memory listing = listings[address(_tokenAddress)][_id];
require(isUserBanned[listing.seller] == false, "Banned seller");
uint256 taxAmount = getTaxOnListing(_tokenAddress, _id);
delete listings[address(_tokenAddress)][_id];
listedTokenIDs[address(_tokenAddress)].remove(_id);
_updateListedTokenTypes(_tokenAddress);
payTax(taxAmount);
skillToken.safeTransferFrom(
msg.sender,
listing.seller,
finalPrice.sub(taxAmount)
);
return (finalPrice, listing);
}
function purchaseBurnCharacter(
uint256 _id,
uint256 _maxPrice
) public userNotBanned isListed(IERC721(address(characters)), _id) userAllowedToPurchase(IERC721(address(characters)), _id) {
(uint256 finalPrice, Listing memory listing) = executePurchaseLogic(IERC721(address(characters)), _id, _maxPrice);
emit PurchasedListing(
msg.sender,
listing.seller,
IERC721(address(characters)),
_id,
finalPrice
);
burningManager.burnCharacterFromMarket(_id);
}
function setAddValue(uint256 cents) public restricted {
require(cents <= 100, "AddValue too high");
addFee = ABDKMath64x64.divu(cents, 100);
}
function setChangeValue(uint256 cents) public restricted {
require(cents <= 100, "ChangeValue too high");
changeFee = ABDKMath64x64.divu(cents, 100);
}
function setTaxRecipient(address _taxRecipient) public restricted {
taxRecipient = _taxRecipient;
}
function setDefaultTax(int128 _defaultTax) public restricted {
defaultTax = _defaultTax;
}
function setDefaultTaxAsRational(uint256 _numerator, uint256 _denominator)
public
restricted
{
defaultTax = ABDKMath64x64.divu(_numerator, _denominator);
}
function setDefaultTaxAsPercent(uint256 _percent) public restricted {
defaultTax = ABDKMath64x64.divu(_percent, 100);
}
function setTaxOnTokenType(IERC721 _tokenAddress, int128 _newTax)
public
restricted
isValidERC721(_tokenAddress)
{
_setTaxOnTokenType(_tokenAddress, _newTax);
}
function setTaxOnTokenTypeAsRational(
IERC721 _tokenAddress,
uint256 _numerator,
uint256 _denominator
) public restricted isValidERC721(_tokenAddress) {
_setTaxOnTokenType(
_tokenAddress,
ABDKMath64x64.divu(_numerator, _denominator)
);
}
function setTaxOnTokenTypeAsPercent(
IERC721 _tokenAddress,
uint256 _percent
) public restricted isValidERC721(_tokenAddress) {
_setTaxOnTokenType(
_tokenAddress,
ABDKMath64x64.divu(_percent, 100)
);
}
function payTax(uint256 amount) internal {
skillToken.safeTransferFrom(msg.sender, taxRecipient, amount);
CryptoBlades(taxRecipient).trackIncome(amount);
}
function setUserBan(address user, bool to) external restricted {
isUserBanned[user] = to;
}
function setUserBans(address[] calldata users, bool to) external restricted {
for(uint i = 0; i < users.length; i++) {
isUserBanned[users[i]] = to;
}
}
function unlistItem(IERC721 _tokenAddress, uint256 _id) external restricted {
delete listings[address(_tokenAddress)][_id];
listedTokenIDs[address(_tokenAddress)].remove(_id);
}
function unlistItems(IERC721 _tokenAddress, uint256[] calldata _ids) external restricted {
for(uint i = 0; i < _ids.length; i++) {
delete listings[address(_tokenAddress)][_ids[i]];
listedTokenIDs[address(_tokenAddress)].remove(_ids[i]);
}
}
function allowToken(IERC721 _tokenAddress) public restricted isValidERC721(_tokenAddress) {
allowedTokenTypes.add(address(_tokenAddress));
}
function disallowToken(IERC721 _tokenAddress) public restricted {
allowedTokenTypes.remove(address(_tokenAddress));
}
function recoverSkill(uint256 amount) public restricted {
skillToken.safeTransfer(msg.sender, amount); // dont expect we'll hold tokens here but might as well
}
function usdToSkill(int128 usdAmount) public view returns (uint256) {
return usdAmount.mulu(priceOracleSkillPerUsd.currentPrice());
}
function onERC721Received(
address, /* operator */
address, /* from */
uint256 _id,
bytes calldata /* data */
) external override returns (bytes4) {
// NOTE: The contract address is always the message sender.
address _tokenAddress = msg.sender;
require(
listedTokenTypes.contains(_tokenAddress) &&
listedTokenIDs[_tokenAddress].contains(_id),
"Token ID not listed"
);
return IERC721ReceiverUpgradeable.onERC721Received.selector;
}
// ############
// Internal helpers
// ############
function _setTaxOnTokenType(IERC721 tokenAddress, int128 newTax) private {
require(newTax >= 0, "We're not running a charity here");
tax[address(tokenAddress)] = newTax;
freeTax[address(tokenAddress)] = newTax == 0;
}
function _updateListedTokenTypes(IERC721 tokenAddress) private {
if (listedTokenIDs[address(tokenAddress)].length() > 0) {
_registerTokenAddress(tokenAddress);
} else {
_unregisterTokenAddress(tokenAddress);
}
}
function _registerTokenAddress(IERC721 tokenAddress) private {
if (!listedTokenTypes.contains(address(tokenAddress))) {
listedTokenTypes.add(address(tokenAddress));
// this prevents resetting custom tax by removing all
if (
tax[address(tokenAddress)] == 0 && // unset or intentionally free
freeTax[address(tokenAddress)] == false
) tax[address(tokenAddress)] = defaultTax;
}
}
function _unregisterTokenAddress(IERC721 tokenAddress) private {
listedTokenTypes.remove(address(tokenAddress));
}
}// SPDX-License-Identifier: BSD-4-Clause /* * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting. * Author: Mikhail Vladimirov <[email protected]> */ pragma solidity ^0.5.0 || ^0.6.0 || ^0.7.0; /** * Smart contract library of mathematical functions operating with signed * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is * basically a simple fraction whose numerator is signed 128-bit integer and * denominator is 2^64. As long as denominator is always the same, there is no * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are * represented by int128 type holding only the numerator. */ library ABDKMath64x64 { /* * Minimum value signed 64.64-bit fixed point number may have. */ int128 private constant MIN_64x64 = -0x80000000000000000000000000000000; /* * Maximum value signed 64.64-bit fixed point number may have. */ int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; /** * Convert signed 256-bit integer number into signed 64.64-bit fixed point * number. Revert on overflow. * * @param x signed 256-bit integer number * @return signed 64.64-bit fixed point number */ function fromInt (int256 x) internal pure returns (int128) { require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF); return int128 (x << 64); } /** * Convert signed 64.64 fixed point number into signed 64-bit integer number * rounding down. * * @param x signed 64.64-bit fixed point number * @return signed 64-bit integer number */ function toInt (int128 x) internal pure returns (int64) { return int64 (x >> 64); } /** * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point * number. Revert on overflow. * * @param x unsigned 256-bit integer number * @return signed 64.64-bit fixed point number */ function fromUInt (uint256 x) internal pure returns (int128) { require (x <= 0x7FFFFFFFFFFFFFFF); return int128 (x << 64); } /** * Convert signed 64.64 fixed point number into unsigned 64-bit integer * number rounding down. Revert on underflow. * * @param x signed 64.64-bit fixed point number * @return unsigned 64-bit integer number */ function toUInt (int128 x) internal pure returns (uint64) { require (x >= 0); return uint64 (x >> 64); } /** * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point * number rounding down. Revert on overflow. * * @param x signed 128.128-bin fixed point number * @return signed 64.64-bit fixed point number */ function from128x128 (int256 x) internal pure returns (int128) { int256 result = x >> 64; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } /** * Convert signed 64.64 fixed point number into signed 128.128 fixed point * number. * * @param x signed 64.64-bit fixed point number * @return signed 128.128 fixed point number */ function to128x128 (int128 x) internal pure returns (int256) { return int256 (x) << 64; } /** * Calculate x + y. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function add (int128 x, int128 y) internal pure returns (int128) { int256 result = int256(x) + y; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } /** * Calculate x - y. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function sub (int128 x, int128 y) internal pure returns (int128) { int256 result = int256(x) - y; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } /** * Calculate x * y rounding down. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function mul (int128 x, int128 y) internal pure returns (int128) { int256 result = int256(x) * y >> 64; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } /** * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point * number and y is signed 256-bit integer number. Revert on overflow. * * @param x signed 64.64 fixed point number * @param y signed 256-bit integer number * @return signed 256-bit integer number */ function muli (int128 x, int256 y) internal pure returns (int256) { if (x == MIN_64x64) { require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF && y <= 0x1000000000000000000000000000000000000000000000000); return -y << 63; } else { bool negativeResult = false; if (x < 0) { x = -x; negativeResult = true; } if (y < 0) { y = -y; // We rely on overflow behavior here negativeResult = !negativeResult; } uint256 absoluteResult = mulu (x, uint256 (y)); if (negativeResult) { require (absoluteResult <= 0x8000000000000000000000000000000000000000000000000000000000000000); return -int256 (absoluteResult); // We rely on overflow behavior here } else { require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return int256 (absoluteResult); } } } /** * Calculate x * y rounding down, where x is signed 64.64 fixed point number * and y is unsigned 256-bit integer number. Revert on overflow. * * @param x signed 64.64 fixed point number * @param y unsigned 256-bit integer number * @return unsigned 256-bit integer number */ function mulu (int128 x, uint256 y) internal pure returns (uint256) { if (y == 0) return 0; require (x >= 0); uint256 lo = (uint256 (x) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64; uint256 hi = uint256 (x) * (y >> 128); require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); hi <<= 64; require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo); return hi + lo; } /** * Calculate x / y rounding towards zero. Revert on overflow or when y is * zero. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function div (int128 x, int128 y) internal pure returns (int128) { require (y != 0); int256 result = (int256 (x) << 64) / y; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } /** * Calculate x / y rounding towards zero, where x and y are signed 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x signed 256-bit integer number * @param y signed 256-bit integer number * @return signed 64.64-bit fixed point number */ function divi (int256 x, int256 y) internal pure returns (int128) { require (y != 0); bool negativeResult = false; if (x < 0) { x = -x; // We rely on overflow behavior here negativeResult = true; } if (y < 0) { y = -y; // We rely on overflow behavior here negativeResult = !negativeResult; } uint128 absoluteResult = divuu (uint256 (x), uint256 (y)); if (negativeResult) { require (absoluteResult <= 0x80000000000000000000000000000000); return -int128 (absoluteResult); // We rely on overflow behavior here } else { require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return int128 (absoluteResult); // We rely on overflow behavior here } } /** * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x unsigned 256-bit integer number * @param y unsigned 256-bit integer number * @return signed 64.64-bit fixed point number */ function divu (uint256 x, uint256 y) internal pure returns (int128) { require (y != 0); uint128 result = divuu (x, y); require (result <= uint128 (MAX_64x64)); return int128 (result); } /** * Calculate -x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function neg (int128 x) internal pure returns (int128) { require (x != MIN_64x64); return -x; } /** * Calculate |x|. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function abs (int128 x) internal pure returns (int128) { require (x != MIN_64x64); return x < 0 ? -x : x; } /** * Calculate 1 / x rounding towards zero. Revert on overflow or when x is * zero. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function inv (int128 x) internal pure returns (int128) { require (x != 0); int256 result = int256 (0x100000000000000000000000000000000) / x; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } /** * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function avg (int128 x, int128 y) internal pure returns (int128) { return int128 ((int256 (x) + int256 (y)) >> 1); } /** * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down. * Revert on overflow or in case x * y is negative. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function gavg (int128 x, int128 y) internal pure returns (int128) { int256 m = int256 (x) * int256 (y); require (m >= 0); require (m < 0x4000000000000000000000000000000000000000000000000000000000000000); return int128 (sqrtu (uint256 (m))); } /** * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number * and y is unsigned 256-bit integer number. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y uint256 value * @return signed 64.64-bit fixed point number */ function pow (int128 x, uint256 y) internal pure returns (int128) { uint256 absoluteResult; bool negativeResult = false; if (x >= 0) { absoluteResult = powu (uint256 (x) << 63, y); } else { // We rely on overflow behavior here absoluteResult = powu (uint256 (uint128 (-x)) << 63, y); negativeResult = y & 1 > 0; } absoluteResult >>= 63; if (negativeResult) { require (absoluteResult <= 0x80000000000000000000000000000000); return -int128 (absoluteResult); // We rely on overflow behavior here } else { require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return int128 (absoluteResult); // We rely on overflow behavior here } } /** * Calculate sqrt (x) rounding down. Revert if x < 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function sqrt (int128 x) internal pure returns (int128) { require (x >= 0); return int128 (sqrtu (uint256 (x) << 64)); } /** * Calculate binary logarithm of x. Revert if x <= 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function log_2 (int128 x) internal pure returns (int128) { require (x > 0); int256 msb = 0; int256 xc = x; if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; } if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore int256 result = msb - 64 << 64; uint256 ux = uint256 (x) << uint256 (127 - msb); for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) { ux *= ux; uint256 b = ux >> 255; ux >>= 127 + b; result += bit * int256 (b); } return int128 (result); } /** * Calculate natural logarithm of x. Revert if x <= 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function ln (int128 x) internal pure returns (int128) { require (x > 0); return int128 ( uint256 (log_2 (x)) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128); } /** * Calculate binary exponent of x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function exp_2 (int128 x) internal pure returns (int128) { require (x < 0x400000000000000000); // Overflow if (x < -0x400000000000000000) return 0; // Underflow uint256 result = 0x80000000000000000000000000000000; if (x & 0x8000000000000000 > 0) result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128; if (x & 0x4000000000000000 > 0) result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128; if (x & 0x2000000000000000 > 0) result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128; if (x & 0x1000000000000000 > 0) result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128; if (x & 0x800000000000000 > 0) result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128; if (x & 0x400000000000000 > 0) result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128; if (x & 0x200000000000000 > 0) result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128; if (x & 0x100000000000000 > 0) result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128; if (x & 0x80000000000000 > 0) result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128; if (x & 0x40000000000000 > 0) result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128; if (x & 0x20000000000000 > 0) result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128; if (x & 0x10000000000000 > 0) result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128; if (x & 0x8000000000000 > 0) result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128; if (x & 0x4000000000000 > 0) result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128; if (x & 0x2000000000000 > 0) result = result * 0x1000162E525EE054754457D5995292026 >> 128; if (x & 0x1000000000000 > 0) result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128; if (x & 0x800000000000 > 0) result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128; if (x & 0x400000000000 > 0) result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128; if (x & 0x200000000000 > 0) result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128; if (x & 0x100000000000 > 0) result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128; if (x & 0x80000000000 > 0) result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128; if (x & 0x40000000000 > 0) result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128; if (x & 0x20000000000 > 0) result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128; if (x & 0x10000000000 > 0) result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128; if (x & 0x8000000000 > 0) result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128; if (x & 0x4000000000 > 0) result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128; if (x & 0x2000000000 > 0) result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128; if (x & 0x1000000000 > 0) result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128; if (x & 0x800000000 > 0) result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128; if (x & 0x400000000 > 0) result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128; if (x & 0x200000000 > 0) result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128; if (x & 0x100000000 > 0) result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128; if (x & 0x80000000 > 0) result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128; if (x & 0x40000000 > 0) result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128; if (x & 0x20000000 > 0) result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128; if (x & 0x10000000 > 0) result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128; if (x & 0x8000000 > 0) result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128; if (x & 0x4000000 > 0) result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128; if (x & 0x2000000 > 0) result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128; if (x & 0x1000000 > 0) result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128; if (x & 0x800000 > 0) result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128; if (x & 0x400000 > 0) result = result * 0x100000000002C5C85FDF477B662B26945 >> 128; if (x & 0x200000 > 0) result = result * 0x10000000000162E42FEFA3AE53369388C >> 128; if (x & 0x100000 > 0) result = result * 0x100000000000B17217F7D1D351A389D40 >> 128; if (x & 0x80000 > 0) result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128; if (x & 0x40000 > 0) result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128; if (x & 0x20000 > 0) result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128; if (x & 0x10000 > 0) result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128; if (x & 0x8000 > 0) result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128; if (x & 0x4000 > 0) result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128; if (x & 0x2000 > 0) result = result * 0x1000000000000162E42FEFA39F02B772C >> 128; if (x & 0x1000 > 0) result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128; if (x & 0x800 > 0) result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128; if (x & 0x400 > 0) result = result * 0x100000000000002C5C85FDF473DEA871F >> 128; if (x & 0x200 > 0) result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128; if (x & 0x100 > 0) result = result * 0x100000000000000B17217F7D1CF79E949 >> 128; if (x & 0x80 > 0) result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128; if (x & 0x40 > 0) result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128; if (x & 0x20 > 0) result = result * 0x100000000000000162E42FEFA39EF366F >> 128; if (x & 0x10 > 0) result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128; if (x & 0x8 > 0) result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128; if (x & 0x4 > 0) result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128; if (x & 0x2 > 0) result = result * 0x1000000000000000162E42FEFA39EF358 >> 128; if (x & 0x1 > 0) result = result * 0x10000000000000000B17217F7D1CF79AB >> 128; result >>= uint256 (63 - (x >> 64)); require (result <= uint256 (MAX_64x64)); return int128 (result); } /** * Calculate natural exponent of x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function exp (int128 x) internal pure returns (int128) { require (x < 0x400000000000000000); // Overflow if (x < -0x400000000000000000) return 0; // Underflow return exp_2 ( int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128)); } /** * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x unsigned 256-bit integer number * @param y unsigned 256-bit integer number * @return unsigned 64.64-bit fixed point number */ function divuu (uint256 x, uint256 y) private pure returns (uint128) { require (y != 0); uint256 result; if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) result = (x << 64) / y; else { uint256 msb = 192; uint256 xc = x >> 192; if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1); require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); uint256 hi = result * (y >> 128); uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); uint256 xh = x >> 192; uint256 xl = x << 64; if (xl < lo) xh -= 1; xl -= lo; // We rely on overflow behavior here lo = hi << 128; if (xl < lo) xh -= 1; xl -= lo; // We rely on overflow behavior here assert (xh == hi >> 128); result += xl / y; } require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return uint128 (result); } /** * Calculate x^y assuming 0^0 is 1, where x is unsigned 129.127 fixed point * number and y is unsigned 256-bit integer number. Revert on overflow. * * @param x unsigned 129.127-bit fixed point number * @param y uint256 value * @return unsigned 129.127-bit fixed point number */ function powu (uint256 x, uint256 y) private pure returns (uint256) { if (y == 0) return 0x80000000000000000000000000000000; else if (x == 0) return 0; else { int256 msb = 0; uint256 xc = x; if (xc >= 0x100000000000000000000000000000000) { xc >>= 128; msb += 128; } if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; } if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore int256 xe = msb - 127; if (xe > 0) x >>= uint256 (xe); else x <<= uint256 (-xe); uint256 result = 0x80000000000000000000000000000000; int256 re = 0; while (y > 0) { if (y & 1 > 0) { result = result * x; y -= 1; re += xe; if (result >= 0x8000000000000000000000000000000000000000000000000000000000000000) { result >>= 128; re += 1; } else result >>= 127; if (re < -127) return 0; // Underflow require (re < 128); // Overflow } else { x = x * x; y >>= 1; xe <<= 1; if (x >= 0x8000000000000000000000000000000000000000000000000000000000000000) { x >>= 128; xe += 1; } else x >>= 127; if (xe < -127) return 0; // Underflow require (xe < 128); // Overflow } } if (re > 0) result <<= uint256 (re); else if (re < 0) result >>= uint256 (-re); return result; } } /** * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer * number. * * @param x unsigned 256-bit integer number * @return unsigned 128-bit integer number */ function sqrtu (uint256 x) private pure returns (uint128) { if (x == 0) return 0; else { uint256 xx = x; uint256 r = 1; if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } if (xx >= 0x10000) { xx >>= 16; r <<= 8; } if (xx >= 0x100) { xx >>= 8; r <<= 4; } if (xx >= 0x10) { xx >>= 4; r <<= 2; } if (xx >= 0x8) { r <<= 1; } r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; // Seven iterations should be enough uint256 r1 = x / r; return uint128 (r < r1 ? r : r1); } } }
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <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 EnumerableSet {
// 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;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
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] = toDeleteIndex + 1; // All indexes are 1-based
// 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) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// 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);
}
// 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))));
}
// 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));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(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");
// solhint-disable-next-line avoid-low-level-calls
(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");
// solhint-disable-next-line avoid-low-level-calls
(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");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
import "../../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`, 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 Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @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 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);
/**
* @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;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when 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.
*/
library SafeMath {
/**
* @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) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
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) {
// 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) {
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) {
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) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @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) {
require(b <= a, "SafeMath: subtraction overflow");
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) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @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. 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) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
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) {
require(b > 0, "SafeMath: modulo by zero");
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) {
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.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* 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) {
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) {
require(b > 0, errorMessage);
return a % b;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <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
pragma solidity >=0.6.2 <0.8.0;
/**
* @dev Library used to query support of an interface declared via {IERC165}.
*
* Note that these functions return the actual result of the query: they do not
* `revert` if an interface is not supported. It is up to the caller to decide
* what to do in these cases.
*/
library ERC165Checker {
// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Returns true if `account` supports the {IERC165} interface,
*/
function supportsERC165(address account) internal view returns (bool) {
// Any contract that implements ERC165 must explicitly indicate support of
// InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
return _supportsERC165Interface(account, _INTERFACE_ID_ERC165) &&
!_supportsERC165Interface(account, _INTERFACE_ID_INVALID);
}
/**
* @dev Returns true if `account` supports the interface defined by
* `interfaceId`. Support for {IERC165} itself is queried automatically.
*
* See {IERC165-supportsInterface}.
*/
function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
// query support of both ERC165 as per the spec and support of _interfaceId
return supportsERC165(account) &&
_supportsERC165Interface(account, interfaceId);
}
/**
* @dev Returns a boolean array where each value corresponds to the
* interfaces passed in and whether they're supported or not. This allows
* you to batch check interfaces for a contract where your expectation
* is that some interfaces may not be supported.
*
* See {IERC165-supportsInterface}.
*
* _Available since v3.4._
*/
function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool[] memory) {
// an array of booleans corresponding to interfaceIds and whether they're supported or not
bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
// query support of ERC165 itself
if (supportsERC165(account)) {
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]);
}
}
return interfaceIdsSupported;
}
/**
* @dev Returns true if `account` supports all the interfaces defined in
* `interfaceIds`. Support for {IERC165} itself is queried automatically.
*
* Batch-querying can lead to gas savings by skipping repeated checks for
* {IERC165} support.
*
* See {IERC165-supportsInterface}.
*/
function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
// query support of ERC165 itself
if (!supportsERC165(account)) {
return false;
}
// query support of each interface in _interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
if (!_supportsERC165Interface(account, interfaceIds[i])) {
return false;
}
}
// all interfaces supported
return true;
}
/**
* @notice Query if a contract implements an interface, does not check ERC165 support
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return true if the contract at account indicates support of the interface with
* identifier interfaceId, false otherwise
* @dev Assumes that account contains a contract that supports ERC165, otherwise
* the behavior of this method is undefined. This precondition can be checked
* with {supportsERC165}.
* Interface identification is specified in ERC-165.
*/
function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
// success determines whether the staticcall succeeded and result determines
// whether the contract at account indicates support of _interfaceId
(bool success, bool result) = _callERC165SupportsInterface(account, interfaceId);
return (success && result);
}
/**
* @notice Calls the function with selector 0x01ffc9a7 (ERC165) and suppresses throw
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return success true if the STATICCALL succeeded, false otherwise
* @return result true if the STATICCALL succeeded and the contract at account
* indicates support of the interface with identifier interfaceId, false otherwise
*/
function _callERC165SupportsInterface(address account, bytes4 interfaceId)
private
view
returns (bool, bool)
{
bytes memory encodedParams = abi.encodeWithSelector(_INTERFACE_ID_ERC165, interfaceId);
(bool success, bytes memory result) = account.staticcall{ gas: 30000 }(encodedParams);
if (result.length < 32) return (false, false);
return (success, abi.decode(result, (bool)));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev String operations.
*/
library StringsUpgradeable {
/**
* @dev Converts a `uint256` to its ASCII `string` 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);
uint256 index = digits - 1;
temp = value;
while (temp != 0) {
buffer[index--] = bytes1(uint8(48 + temp % 10));
temp /= 10;
}
return string(buffer);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <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;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
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] = toDeleteIndex + 1; // All indexes are 1-based
// 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) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// 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);
}
// 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))));
}
// 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));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are
* supported.
*/
library EnumerableMapUpgradeable {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Map type with
// bytes32 keys and values.
// The Map implementation uses private functions, and user-facing
// implementations (such as Uint256ToAddressMap) are just wrappers around
// the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit
// in bytes32.
struct MapEntry {
bytes32 _key;
bytes32 _value;
}
struct Map {
// Storage of map keys and values
MapEntry[] _entries;
// Position of the entry defined by a key in the `entries` array, plus 1
// because index 0 means a key is not in the map.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
// We read and store the key's index to prevent multiple reads from the same storage slot
uint256 keyIndex = map._indexes[key];
if (keyIndex == 0) { // Equivalent to !contains(map, key)
map._entries.push(MapEntry({ _key: key, _value: value }));
// The entry is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
map._indexes[key] = map._entries.length;
return true;
} else {
map._entries[keyIndex - 1]._value = value;
return false;
}
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function _remove(Map storage map, bytes32 key) private returns (bool) {
// We read and store the key's index to prevent multiple reads from the same storage slot
uint256 keyIndex = map._indexes[key];
if (keyIndex != 0) { // Equivalent to contains(map, key)
// To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one
// in the array, and then remove the last entry (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = keyIndex - 1;
uint256 lastIndex = map._entries.length - 1;
// When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
MapEntry storage lastEntry = map._entries[lastIndex];
// Move the last entry to the index where the entry to delete is
map._entries[toDeleteIndex] = lastEntry;
// Update the index for the moved entry
map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved entry was stored
map._entries.pop();
// Delete the index for the deleted slot
delete map._indexes[key];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function _contains(Map storage map, bytes32 key) private view returns (bool) {
return map._indexes[key] != 0;
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function _length(Map storage map) private view returns (uint256) {
return map._entries.length;
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
require(map._entries.length > index, "EnumerableMap: index out of bounds");
MapEntry storage entry = map._entries[index];
return (entry._key, entry._value);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) {
uint256 keyIndex = map._indexes[key];
if (keyIndex == 0) return (false, 0); // Equivalent to contains(map, key)
return (true, map._entries[keyIndex - 1]._value); // All indexes are 1-based
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function _get(Map storage map, bytes32 key) private view returns (bytes32) {
uint256 keyIndex = map._indexes[key];
require(keyIndex != 0, "EnumerableMap: nonexistent key"); // Equivalent to contains(map, key)
return map._entries[keyIndex - 1]._value; // All indexes are 1-based
}
/**
* @dev Same as {_get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {_tryGet}.
*/
function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
uint256 keyIndex = map._indexes[key];
require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
return map._entries[keyIndex - 1]._value; // All indexes are 1-based
}
// UintToAddressMap
struct UintToAddressMap {
Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return _remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return _contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return _length(map._inner);
}
/**
* @dev Returns the element 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(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = _at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*
* _Available since v3.4._
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = _tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, bytes32(key)))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage))));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../proxy/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 GSN 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 initializer {
__Context_init_unchained();
}
function __Context_init_unchained() internal initializer {
}
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
/**
* @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
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(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");
// solhint-disable-next-line avoid-low-level-calls
(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");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
import "../../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`, 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 Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @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 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);
/**
* @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;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <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 `IERC721.onERC721Received.selector`.
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <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: MIT
pragma solidity >=0.6.2 <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 tokenId);
/**
* @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
pragma solidity >=0.6.0 <0.8.0;
import "../../utils/ContextUpgradeable.sol";
import "./IERC721Upgradeable.sol";
import "./IERC721MetadataUpgradeable.sol";
import "./IERC721EnumerableUpgradeable.sol";
import "./IERC721ReceiverUpgradeable.sol";
import "../../introspection/ERC165Upgradeable.sol";
import "../../math/SafeMathUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/EnumerableSetUpgradeable.sol";
import "../../utils/EnumerableMapUpgradeable.sol";
import "../../utils/StringsUpgradeable.sol";
import "../../proxy/Initializable.sol";
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable, IERC721EnumerableUpgradeable {
using SafeMathUpgradeable for uint256;
using AddressUpgradeable for address;
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
using EnumerableMapUpgradeable for EnumerableMapUpgradeable.UintToAddressMap;
using StringsUpgradeable for uint256;
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
// which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
// Mapping from holder address to their (enumerable) set of owned tokens
mapping (address => EnumerableSetUpgradeable.UintSet) private _holderTokens;
// Enumerable mapping from token ids to their owners
EnumerableMapUpgradeable.UintToAddressMap private _tokenOwners;
// 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;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Optional mapping for token URIs
mapping (uint256 => string) private _tokenURIs;
// Base URI
string private _baseURI;
/*
* bytes4(keccak256('balanceOf(address)')) == 0x70a08231
* bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
* bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
* bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
* bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
* bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
* bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
*
* => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
* 0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
*/
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
/*
* bytes4(keccak256('name()')) == 0x06fdde03
* bytes4(keccak256('symbol()')) == 0x95d89b41
* bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
*
* => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
*/
bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
/*
* bytes4(keccak256('totalSupply()')) == 0x18160ddd
* bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
* bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
*
* => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
*/
bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
/**
* @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 initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__ERC721_init_unchained(name_, symbol_);
}
function __ERC721_init_unchained(string memory name_, string memory symbol_) internal initializer {
_name = name_;
_symbol = symbol_;
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721);
_registerInterface(_INTERFACE_ID_ERC721_METADATA);
_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
}
/**
* @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 _holderTokens[owner].length();
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
}
/**
* @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 _tokenURI = _tokenURIs[tokenId];
string memory base = baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(base, tokenId.toString()));
}
/**
* @dev Returns the base URI set via {_setBaseURI}. This will be
* automatically added as a prefix in {tokenURI} to each token's URI, or
* to the token ID if no specific URI is set for that token ID.
*/
function baseURI() public view virtual returns (string memory) {
return _baseURI;
}
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
return _holderTokens[owner].at(index);
}
/**
* @dev See {IERC721Enumerable-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
// _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
return _tokenOwners.length();
}
/**
* @dev See {IERC721Enumerable-tokenByIndex}.
*/
function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
(uint256 tokenId, ) = _tokenOwners.at(index);
return tokenId;
}
/**
* @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 || ERC721Upgradeable.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 {
require(operator != _msgSender(), "ERC721: approve to caller");
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_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 _tokenOwners.contains(tokenId);
}
/**
* @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 || getApproved(tokenId) == spender || ERC721Upgradeable.isApprovedForAll(owner, spender));
}
/**
* @dev Safely mints `tokenId` and transfers it to `to`.
*
* Requirements:
d*
* - `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);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
emit Transfer(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); // internal owner
_beforeTokenTransfer(owner, address(0), tokenId);
// Clear approvals
_approve(address(0), tokenId);
// Clear metadata (if any)
if (bytes(_tokenURIs[tokenId]).length != 0) {
delete _tokenURIs[tokenId];
}
_holderTokens[owner].remove(tokenId);
_tokenOwners.remove(tokenId);
emit Transfer(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 of token that is not own"); // internal 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);
_holderTokens[from].remove(tokenId);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
emit Transfer(from, to, tokenId);
}
/**
* @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
/**
* @dev Internal function to set the base URI for all token IDs. It is
* automatically added as a prefix to the value returned in {tokenURI},
* or to the token ID if {tokenURI} is empty.
*/
function _setBaseURI(string memory baseURI_) internal virtual {
_baseURI = baseURI_;
}
/**
* @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()) {
return true;
}
bytes memory returndata = to.functionCall(abi.encodeWithSelector(
IERC721ReceiverUpgradeable(to).onERC721Received.selector,
_msgSender(),
from,
tokenId,
_data
), "ERC721: transfer to non ERC721Receiver implementer");
bytes4 retval = abi.decode(returndata, (bytes4));
return (retval == _ERC721_RECEIVED);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits an {Approval} event.
*/
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId); // internal owner
}
/**
* @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 { }
uint256[41] private __gap;
}// SPDX-License-Identifier: MIT
// solhint-disable-next-line compiler-version
pragma solidity >=0.4.24 <0.8.0;
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 a proxied contract can't have 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.
*
* 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 {UpgradeableProxy-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.
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Modifier to protect an initializer function from being invoked twice.
*/
modifier initializer() {
require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
/// @dev Returns true if and only if the function is running in the constructor
function _isConstructor() private view returns (bool) {
return !AddressUpgradeable.isContract(address(this));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when 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.
*/
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) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
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) {
// 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) {
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) {
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) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @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) {
require(b <= a, "SafeMath: subtraction overflow");
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) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @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. 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) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
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) {
require(b > 0, "SafeMath: modulo by zero");
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) {
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.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* 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) {
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) {
require(b > 0, errorMessage);
return a % b;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <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
pragma solidity >=0.6.0 <0.8.0;
import "./IERC165Upgradeable.sol";
import "../proxy/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts may inherit from this and call {_registerInterface} to declare
* their support of an interface.
*/
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
function __ERC165_init() internal initializer {
__ERC165_init_unchained();
}
function __ERC165_init_unchained() internal initializer {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See {IERC165-supportsInterface}.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See {IERC165-supportsInterface}.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal virtual {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../utils/EnumerableSetUpgradeable.sol";
import "../utils/AddressUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../proxy/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms.
*
* 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 {
function __AccessControl_init() internal initializer {
__Context_init_unchained();
__AccessControl_init_unchained();
}
function __AccessControl_init_unchained() internal initializer {
}
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
using AddressUpgradeable for address;
struct RoleData {
EnumerableSetUpgradeable.AddressSet members;
bytes32 adminRole;
}
mapping (bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @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 {_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) public view returns (bool) {
return _roles[role].members.contains(account);
}
/**
* @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 returns (uint256) {
return _roles[role].members.length();
}
/**
* @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 returns (address) {
return _roles[role].members.at(index);
}
/**
* @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 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 {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
_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 {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
_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 granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) public virtual {
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}.
* ====
*/
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 {
emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
_roles[role].adminRole = adminRole;
}
function _grantRole(bytes32 role, address account) private {
if (_roles[role].members.add(account)) {
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) private {
if (_roles[role].members.remove(account)) {
emit RoleRevoked(role, account, _msgSender());
}
}
uint256[49] private __gap;
}pragma solidity ^0.6.0;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "abdk-libraries-solidity/ABDKMath64x64.sol";
import "./Promos.sol";
import "./util.sol";
contract Weapons is Initializable, ERC721Upgradeable, AccessControlUpgradeable {
using ABDKMath64x64 for int128;
using ABDKMath64x64 for uint16;
bytes32 public constant GAME_ADMIN = keccak256("GAME_ADMIN");
bytes32 public constant RECEIVE_DOES_NOT_SET_TRANSFER_TIMESTAMP = keccak256("RECEIVE_DOES_NOT_SET_TRANSFER_TIMESTAMP");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
function initialize () public initializer {
__ERC721_init("CryptoBlades weapon", "CBW");
__AccessControl_init_unchained();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function migrateTo_e55d8c5() public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
burnPointMultiplier = 2;
lowStarBurnPowerPerPoint = 15;
fourStarBurnPowerPerPoint = 30;
fiveStarBurnPowerPerPoint = 60;
}
function migrateTo_aa9da90() public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
oneFrac = ABDKMath64x64.fromUInt(1);
powerMultPerPointBasic = ABDKMath64x64.divu(1, 400);// 0.25%
powerMultPerPointPWR = powerMultPerPointBasic.mul(ABDKMath64x64.divu(103, 100)); // 0.2575% (+3%)
powerMultPerPointMatching = powerMultPerPointBasic.mul(ABDKMath64x64.divu(107, 100)); // 0.2675% (+7%)
}
function migrateTo_951a020() public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
// Apparently ERC165 interfaces cannot be removed in this version of the OpenZeppelin library.
// But if we remove the registration, then while local deployments would not register the interface ID,
// existing deployments on both testnet and mainnet would still be registered to handle it.
// That sort of inconsistency is a good way to attract bugs that only happens on some environments.
// Hence, we keep registering the interface despite not actually implementing the interface.
_registerInterface(0xe62e6974); // TransferCooldownableInterfaceId.interfaceId()
}
function migrateTo_surprise(Promos _promos) public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
promos = _promos;
}
/*
visual numbers start at 0, increment values by 1
levels: 1-128
stars: 1-5 (1,2,3: primary only, 4: one secondary, 5: two secondaries)
traits: 0-3 [0(fire) > 1(earth) > 2(lightning) > 3(water) > repeat]
stats: STR(fire), DEX(earth), CHA(lightning), INT(water), PWR(traitless)
base stat rolls: 1*(1-50), 2*(45-75), 3*(70-100), 4*(50-100), 5*(66-100, main is 68-100)
burns: add level & main stat, and 50% chance to increase secondaries
power: each point contributes .25% to fight power
cosmetics: 0-255 but only 24 is used, may want to cap so future expansions dont change existing weps
*/
struct Weapon {
uint16 properties; // right to left: 3b stars, 2b trait, 7b stat pattern, 4b EMPTY
// stats (each point refers to .25% improvement)
uint16 stat1;
uint16 stat2;
uint16 stat3;
uint8 level; // separate from stat1 because stat1 will have a pre-roll
}
struct WeaponBurnPoints {
uint8 lowStarBurnPoints;
uint8 fourStarBurnPoints;
uint8 fiveStarBurnPoints;
}
struct WeaponCosmetics {
uint8 version;
uint256 seed;
}
Weapon[] private tokens;
WeaponCosmetics[] private cosmetics;
mapping(uint256 => WeaponBurnPoints) burnPoints;
uint public burnPointMultiplier; // 2
uint public lowStarBurnPowerPerPoint; // 15
uint public fourStarBurnPowerPerPoint; // 30
uint public fiveStarBurnPowerPerPoint; // 60
int128 public oneFrac; // 1.0
int128 public powerMultPerPointBasic; // 0.25%
int128 public powerMultPerPointPWR; // 0.2575% (+3%)
int128 public powerMultPerPointMatching; // 0.2675% (+7%)
// UNUSED; KEPT FOR UPGRADEABILITY PROXY COMPATIBILITY
mapping(uint256 => uint256) public lastTransferTimestamp;
uint256 private lastMintedBlock;
uint256 private firstMintedOfLastBlock;
mapping(uint256 => uint64) durabilityTimestamp;
uint256 public constant maxDurability = 20;
uint256 public constant secondsPerDurability = 3000; //50 * 60
mapping(address => uint256) burnDust; // user address : burned item dust counts
Promos public promos;
uint256 public constant BIT_FEATURE_TRANSFER_BLOCKED = 1;
uint256 public constant NUMBERPARAMETER_FEATURE_BITS = uint256(keccak256("FEATURE_BITS"));
mapping(uint256 => uint256) public numberParameters;
mapping(uint256 => mapping(uint256 => uint256)) public nftVars;//KEYS: NFTID, VARID
uint256 public constant NFTVAR_BUSY = 1; // value bitflags: 1 (pvp) | 2 (raid) | 4 (TBD)..
event Burned(address indexed owner, uint256 indexed burned);
event NewWeapon(uint256 indexed weapon, address indexed minter);
event Reforged(address indexed owner, uint256 indexed reforged, uint256 indexed burned, uint8 lowPoints, uint8 fourPoints, uint8 fivePoints);
event ReforgedWithDust(address indexed owner, uint256 indexed reforged, uint8 lowDust, uint8 fourDust, uint8 fiveDust, uint8 lowPoints, uint8 fourPoints, uint8 fivePoints);
modifier restricted() {
_restricted();
_;
}
function _restricted() internal view {
needRole(hasRole(GAME_ADMIN, msg.sender));
}
modifier minterOnly() {
_minterOnly();
_;
}
function _minterOnly() internal view {
needRole(hasRole(GAME_ADMIN, msg.sender) || hasRole(MINTER_ROLE, msg.sender));
}
function needRole(bool statement) internal pure {
require(statement, "NR");
}
modifier noFreshLookup(uint256 id) {
_noFreshLookup(id);
_;
}
function _noFreshLookup(uint256 id) internal view {
require(id < firstMintedOfLastBlock || lastMintedBlock < block.number, "NFL");
}
function getStats(uint256 id) internal view
returns (uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3, uint8 _level) {
Weapon memory w = tokens[id];
return (w.properties, w.stat1, w.stat2, w.stat3, w.level);
}
function getCosmetics(uint256 id) internal view
returns (uint8 _blade, uint8 _crossguard, uint8 _grip, uint8 _pommel) {
WeaponCosmetics memory wc = cosmetics[id];
_blade = getRandomCosmetic(wc.seed, 1, 24);
_crossguard = getRandomCosmetic(wc.seed, 2, 24);
_grip = getRandomCosmetic(wc.seed, 3, 24);
_pommel = getRandomCosmetic(wc.seed, 4, 24);
}
function getCosmeticsSeed(uint256 id) public view noFreshLookup(id)
returns (uint256) {
WeaponCosmetics memory wc = cosmetics[id];
return wc.seed;
}
function get(uint256 id) public view noFreshLookup(id)
returns (
uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3, uint8 _level,
uint8 _blade, uint8 _crossguard, uint8 _grip, uint8 _pommel,
uint24 _burnPoints, // burn points.. got stack limits so i put them together
uint24 _bonusPower // bonus power
) {
return _get(id);
}
function _get(uint256 id) internal view
returns (
uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3, uint8 _level,
uint8 _blade, uint8 _crossguard, uint8 _grip, uint8 _pommel,
uint24 _burnPoints, // burn points.. got stack limits so i put them together
uint24 _bonusPower // bonus power
) {
(_properties, _stat1, _stat2, _stat3, _level) = getStats(id);
(_blade, _crossguard, _grip, _pommel) = getCosmetics(id);
WeaponBurnPoints memory wbp = burnPoints[id];
_burnPoints =
uint24(wbp.lowStarBurnPoints) |
(uint24(wbp.fourStarBurnPoints) << 8) |
(uint24(wbp.fiveStarBurnPoints) << 16);
_bonusPower = getBonusPower(id);
}
function mintN(address minter, uint32 amount, uint256 seed, uint8 chosenElement) public restricted {
for(uint i = 0; i < amount; i++)
mint(minter, RandomUtil.combineSeeds(seed,i), chosenElement);
}
function mint(address minter, uint256 seed, uint8 chosenElement) public minterOnly returns(uint256) {
uint256 stars;
uint256 roll = seed % 100;
// will need revision, possibly manual configuration if we support more than 5 stars
if(roll < 1) {
stars = 4; // 5* at 1%
}
else if(roll < 6) { // 4* at 5%
stars = 3;
}
else if(roll < 21) { // 3* at 15%
stars = 2;
}
else if(roll < 56) { // 2* at 35%
stars = 1;
}
else {
stars = 0; // 1* at 44%
}
return mintWeaponWithStars(minter, stars, seed, chosenElement);
}
function mintGiveawayWeapon(address to, uint256 stars, uint8 chosenElement) external minterOnly returns(uint256) {
// MANUAL USE ONLY; DO NOT USE IN CONTRACTS!
return mintWeaponWithStars(to, stars, uint256(keccak256(abi.encodePacked(now, tokens.length))), chosenElement);
}
function mintWeaponWithStars(address minter, uint256 stars, uint256 seed, uint8 chosenElement) public minterOnly returns(uint256) {
require(stars < 8);
require(chosenElement == 100 || (chosenElement>= 0 && chosenElement<= 3));
(uint16 stat1, uint16 stat2, uint16 stat3) = getStatRolls(stars, seed);
return performMintWeapon(minter,
getRandomProperties(stars, seed, chosenElement),
stat1,
stat2,
stat3,
RandomUtil.combineSeeds(seed,3)
);
}
function performMintWeapon(address minter,
uint16 properties,
uint16 stat1, uint16 stat2, uint16 stat3,
uint256 cosmeticSeed
) public minterOnly returns(uint256) {
uint256 tokenID = tokens.length;
if(block.number != lastMintedBlock)
firstMintedOfLastBlock = tokenID;
lastMintedBlock = block.number;
tokens.push(Weapon(properties, stat1, stat2, stat3, 0));
cosmetics.push(WeaponCosmetics(0, cosmeticSeed));
_mint(minter, tokenID);
durabilityTimestamp[tokenID] = uint64(now.sub(getDurabilityMaxWait()));
emit NewWeapon(tokenID, minter);
return tokenID;
}
function performMintWeaponDetailed(address minter,
uint256 metaData,
uint256 cosmeticSeed, uint256 tokenID
) public minterOnly returns(uint256) {
uint8 fiveStarBurnPoints = uint8(metaData & 0xFF);
uint8 fourStarBurnPoints = uint8((metaData >> 8) & 0xFF);
uint8 lowStarBurnPoints = uint8((metaData >> 16) & 0xFF);
uint8 level = uint8((metaData >> 24) & 0xFF);
uint16 stat3 = uint16((metaData >> 32) & 0xFFFF);
uint16 stat2 = uint16((metaData >> 48) & 0xFFFF);
uint16 stat1 = uint16((metaData >> 64) & 0xFFFF);
uint16 properties = uint16((metaData >> 80) & 0xFFFF);
require(lowStarBurnPoints <= 100 && fourStarBurnPoints <= 25 && fiveStarBurnPoints <= 10);
if(tokenID == 0){
tokenID = performMintWeapon(minter, properties, stat1, stat2, stat3, 0);
}
else {
Weapon storage wp = tokens[tokenID];
wp.properties = properties;
wp.stat1 = stat1;
wp.stat2 = stat2;
wp.stat3 = stat3;
wp.level = level;
}
WeaponCosmetics storage wc = cosmetics[tokenID];
wc.seed = cosmeticSeed;
tokens[tokenID].level = level;
durabilityTimestamp[tokenID] = uint64(now); // avoid chain jumping abuse
WeaponBurnPoints storage wbp = burnPoints[tokenID];
wbp.lowStarBurnPoints = lowStarBurnPoints;
wbp.fourStarBurnPoints = fourStarBurnPoints;
wbp.fiveStarBurnPoints = fiveStarBurnPoints;
return tokenID;
}
function getRandomProperties(uint256 stars, uint256 seed, uint8 chosenElement) public pure returns (uint16) {
uint256 trait;
if (chosenElement == 100) {
trait = ((RandomUtil.randomSeededMinMax(0,3,RandomUtil.combineSeeds(seed,1)) & 0x3) << 3);
} else {
trait = ((chosenElement & 0x3) << 3);
}
return uint16((stars & 0x7) // stars aren't randomized here!
| trait // trait
| ((RandomUtil.randomSeededMinMax(0,124,RandomUtil.combineSeeds(seed,2)) & 0x7F) << 5)); // statPattern
}
function getStatRolls(uint256 stars, uint256 seed) private pure returns (uint16, uint16, uint16) {
// each point refers to .25%
// so 1 * 4 is 1%
uint16 minRoll = getStatMinRoll(stars);
uint16 maxRoll = getStatMaxRoll(stars);
uint8 statCount = getStatCount(stars);
uint16 stat1 = getRandomStat(minRoll, maxRoll, seed, 5);
uint16 stat2 = 0;
uint16 stat3 = 0;
if(statCount > 1) {
stat2 = getRandomStat(minRoll, maxRoll, seed, 3);
}
if(statCount > 2) {
stat3 = getRandomStat(minRoll, maxRoll, seed, 4);
}
return (stat1, stat2, stat3);
}
function getRandomStat(uint16 minRoll, uint16 maxRoll, uint256 seed, uint256 seed2) public pure returns (uint16) {
return uint16(RandomUtil.randomSeededMinMax(minRoll, maxRoll,RandomUtil.combineSeeds(seed, seed2)));
}
function getRandomCosmetic(uint256 seed, uint256 seed2, uint8 limit) public pure returns (uint8) {
return uint8(RandomUtil.randomSeededMinMax(0, limit, RandomUtil.combineSeeds(seed, seed2)));
}
function getStatMinRoll(uint256 stars) public pure returns (uint16) {
// 1 star
if (stars == 0) return 4;
// 2 star
if (stars == 1) return 180;
// 3 star
if (stars == 2) return 280;
// 4 star
if (stars == 3) return 200;
// 5+ star
return 268;
}
function getStatMaxRoll(uint256 stars) public pure returns (uint16) {
// 3+ star
if (stars > 1) return 400;
// 2 star
if (stars > 0) return 300;
// 1 star
return 200;
}
function getStatCount(uint256 stars) public pure returns (uint8) {
// 1-2 star
if (stars < 3) return 1;
// 3+ star
return uint8(stars)-1;
}
function getProperties(uint256 id) public view noFreshLookup(id) returns (uint16) {
return tokens[id].properties;
}
function getStars(uint256 id) public view noFreshLookup(id) returns (uint8) {
return getStarsFromProperties(getProperties(id));
}
function getStarsFromProperties(uint16 properties) public pure returns (uint8) {
return uint8(properties & 0x7); // first two bits for stars
}
function getTrait(uint256 id) public view noFreshLookup(id) returns (uint8) {
return getTraitFromProperties(getProperties(id));
}
function getTraitFromProperties(uint16 properties) public pure returns (uint8) {
return uint8((properties >> 3) & 0x3); // two bits after star bits (3)
}
function getStatPattern(uint256 id) public view noFreshLookup(id) returns (uint8) {
return getStatPatternFromProperties(getProperties(id));
}
function getStatPatternFromProperties(uint16 properties) public pure returns (uint8) {
return uint8((properties >> 5) & 0x7F); // 7 bits after star(3) and trait(2) bits
}
function getStat1Trait(uint8 statPattern) public pure returns (uint8) {
return uint8(uint256(statPattern) % 5); // 0-3 regular traits, 4 = traitless (PWR)
}
function getStat2Trait(uint8 statPattern) public pure returns (uint8) {
return uint8(SafeMath.div(statPattern, 5) % 5); // 0-3 regular traits, 4 = traitless (PWR)
}
function getStat3Trait(uint8 statPattern) public pure returns (uint8) {
return uint8(SafeMath.div(statPattern, 25) % 5); // 0-3 regular traits, 4 = traitless (PWR)
}
function getLevel(uint256 id) public view noFreshLookup(id) returns (uint8) {
return tokens[id].level;
}
function getStat1(uint256 id) public view noFreshLookup(id) returns (uint16) {
return tokens[id].stat1;
}
function getStat2(uint256 id) public view noFreshLookup(id) returns (uint16) {
return tokens[id].stat2;
}
function getStat3(uint256 id) public view noFreshLookup(id) returns (uint16) {
return tokens[id].stat3;
}
function getPowerMultiplier(uint256 id) public view noFreshLookup(id) returns (int128) {
// returns a 64.64 fixed point number for power multiplier
// this function does not account for traits
// it is used to calculate base enemy powers for targeting
Weapon memory wep = tokens[id];
int128 powerPerPoint = ABDKMath64x64.divu(1, 400); // 0.25% or x0.0025
int128 stat1 = wep.stat1.fromUInt().mul(powerPerPoint);
int128 stat2 = wep.stat2.fromUInt().mul(powerPerPoint);
int128 stat3 = wep.stat3.fromUInt().mul(powerPerPoint);
return ABDKMath64x64.fromUInt(1).add(stat1).add(stat2).add(stat3);
}
function getPowerMultiplierForTrait(
uint16 properties,
uint16 stat1,
uint16 stat2,
uint16 stat3,
uint8 trait
) public view returns(int128) {
// Does not include character trait to weapon trait match
// Only counts arbitrary trait to weapon stat trait
// This function can be used by frontend to get expected % bonus for each type
// Making it easy to see on the market how useful it will be to you
uint8 statPattern = getStatPatternFromProperties(properties);
int128 result = oneFrac;
if(getStat1Trait(statPattern) == trait)
result = result.add(stat1.fromUInt().mul(powerMultPerPointMatching));
else if(getStat1Trait(statPattern) == 4) // PWR, traitless
result = result.add(stat1.fromUInt().mul(powerMultPerPointPWR));
else
result = result.add(stat1.fromUInt().mul(powerMultPerPointBasic));
if(getStat2Trait(statPattern) == trait)
result = result.add(stat2.fromUInt().mul(powerMultPerPointMatching));
else if(getStat2Trait(statPattern) == 4) // PWR, traitless
result = result.add(stat2.fromUInt().mul(powerMultPerPointPWR));
else
result = result.add(stat2.fromUInt().mul(powerMultPerPointBasic));
if(getStat3Trait(statPattern) == trait)
result = result.add(stat3.fromUInt().mul(powerMultPerPointMatching));
else if(getStat3Trait(statPattern) == 4) // PWR, traitless
result = result.add(stat3.fromUInt().mul(powerMultPerPointPWR));
else
result = result.add(stat3.fromUInt().mul(powerMultPerPointBasic));
return result;
}
function getDustSupplies(address playerAddress) public view returns (uint32[] memory) {
uint256 burnDustValue = burnDust[playerAddress];
uint32[] memory supplies = new uint32[](3);
supplies[0] = uint32(burnDustValue);
supplies[1] = uint32(burnDustValue >> 32);
supplies[2] = uint32(burnDustValue >> 64);
return supplies;
}
function _setDustSupplies(address playerAddress, uint32 amountLB, uint32 amount4B, uint32 amount5B) internal {
uint256 burnDustValue = (uint256(amount5B) << 64) + (uint256(amount4B) << 32) + amountLB;
burnDust[playerAddress] = burnDustValue;
}
function _decrementDustSupplies(address playerAddress, uint32 amountLB, uint32 amount4B, uint32 amount5B) internal {
uint32[] memory supplies = getDustSupplies(playerAddress);
require(supplies[0] >= amountLB && supplies[1] >= amount4B && supplies[2] >= amount5B);
supplies[0] -= amountLB;
supplies[1] -= amount4B;
supplies[2] -= amount5B;
_setDustSupplies(playerAddress, supplies[0], supplies[1], supplies[2]);
}
function incrementDustSupplies(address playerAddress, uint32 amountLB, uint32 amount4B, uint32 amount5B) public restricted {
_incrementDustSupplies(playerAddress, amountLB, amount4B, amount5B);
}
function _incrementDustSupplies(address playerAddress, uint32 amountLB, uint32 amount4B, uint32 amount5B) internal {
uint32[] memory supplies = getDustSupplies(playerAddress);
require(uint256(supplies[0]) + amountLB <= 0xFFFFFFFF
&& uint256(supplies[1]) + amount4B <= 0xFFFFFFFF
&& uint256(supplies[2]) + amount5B <= 0xFFFFFFFF);
supplies[0] += amountLB;
supplies[1] += amount4B;
supplies[2] += amount5B;
_setDustSupplies(playerAddress, supplies[0], supplies[1], supplies[2]);
}
function _calculateBurnValues(uint256 burnID) public view returns(uint8[] memory) {
uint8[] memory values = new uint8[](3);
// Carried burn points.
WeaponBurnPoints storage burningbp = burnPoints[burnID];
values[0] = (burningbp.lowStarBurnPoints + 1) / 2;
values[1] = (burningbp.fourStarBurnPoints + 1) / 2;
values[2] = (burningbp.fiveStarBurnPoints + 1) / 2;
// Stars-based burn points.
Weapon storage burning = tokens[burnID];
uint8 stars = getStarsFromProperties(burning.properties);
if(stars < 3) { // 1-3 star
values[0] += uint8(burnPointMultiplier * (stars + 1));
}
else if(stars == 3) { // 4 star
values[1] += uint8(burnPointMultiplier);
}
else if(stars == 4) { // 5 star
values[2] += uint8(burnPointMultiplier);
}
return values;
}
function burn(uint256 burnID) public restricted {
uint8[] memory values = _calculateBurnValues(burnID);
address burnOwner = ownerOf(burnID);
_burn(burnID);
if(promos.getBit(burnOwner, 4) == false)
_incrementDustSupplies(burnOwner, values[0], values[1], values[2]);
emit Burned(
burnOwner,
burnID
);
}
function reforge(uint256 reforgeID, uint256 burnID) public restricted {
uint8[] memory values = _calculateBurnValues(burnID);
// Note: preexisting issue of applying burn points even if _burn fails.
if(promos.getBit(ownerOf(reforgeID), 4) == false)
_applyBurnPoints(reforgeID, values[0], values[1], values[2]);
_burn(burnID);
WeaponBurnPoints storage wbp = burnPoints[reforgeID];
emit Reforged(
ownerOf(reforgeID),
reforgeID,
burnID,
wbp.lowStarBurnPoints,
wbp.fourStarBurnPoints,
wbp.fiveStarBurnPoints
);
}
function reforgeWithDust(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) public restricted {
if(promos.getBit(ownerOf(reforgeID), 4) == false)
_applyBurnPoints(reforgeID, amountLB, amount4B, amount5B);
_decrementDustSupplies(ownerOf(reforgeID), amountLB, amount4B, amount5B);
WeaponBurnPoints storage wbp = burnPoints[reforgeID];
emit ReforgedWithDust(
ownerOf(reforgeID),
reforgeID,
amountLB,
amount4B,
amount5B,
wbp.lowStarBurnPoints,
wbp.fourStarBurnPoints,
wbp.fiveStarBurnPoints
);
}
function _applyBurnPoints(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) private {
WeaponBurnPoints storage wbp = burnPoints[reforgeID];
if(amountLB > 0) {
require(wbp.lowStarBurnPoints < 100, "LB capped");
}
if(amount4B > 0) {
require(wbp.fourStarBurnPoints < 25, "4B capped");
}
if(amount5B > 0) {
require(wbp.fiveStarBurnPoints < 10, "5B capped");
}
wbp.lowStarBurnPoints += amountLB;
wbp.fourStarBurnPoints += amount4B;
wbp.fiveStarBurnPoints += amount5B;
if(wbp.lowStarBurnPoints > 100)
wbp.lowStarBurnPoints = 100;
if(wbp.fourStarBurnPoints > 25)
wbp.fourStarBurnPoints = 25;
if(wbp.fiveStarBurnPoints > 10)
wbp.fiveStarBurnPoints = 10;
}
function getBonusPower(uint256 id) public view noFreshLookup(id) returns (uint24) {
Weapon storage wep = tokens[id];
return getBonusPowerForFight(id, wep.level);
}
function getBonusPowerForFight(uint256 id, uint8 level) public view returns (uint24) {
WeaponBurnPoints storage wbp = burnPoints[id];
return uint24(lowStarBurnPowerPerPoint.mul(wbp.lowStarBurnPoints)
.add(fourStarBurnPowerPerPoint.mul(wbp.fourStarBurnPoints))
.add(fiveStarBurnPowerPerPoint.mul(wbp.fiveStarBurnPoints))
.add(uint256(15).mul(level))
);
}
function getFightData(uint256 id, uint8 charTrait) public view noFreshLookup(id) returns (int128, int128, uint24, uint8) {
Weapon storage wep = tokens[id];
return (
oneFrac.add(powerMultPerPointBasic.mul(
ABDKMath64x64.fromUInt(
wep.stat1 + wep.stat2 + wep.stat3
)
)),//targetMult
getPowerMultiplierForTrait(wep.properties, wep.stat1, wep.stat2, wep.stat3, charTrait),
getBonusPowerForFight(id, wep.level),
getTraitFromProperties(wep.properties)
);
}
function getFightDataAndDrainDurability(address fighter,
uint256 id, uint8 charTrait, uint8 drainAmount, bool allowNegativeDurability, uint256 busyFlag) public
restricted
returns (int128, int128, uint24, uint8) {
require(fighter == ownerOf(id) && nftVars[id][NFTVAR_BUSY] == 0);
nftVars[id][NFTVAR_BUSY] |= busyFlag;
drainDurability(id, drainAmount, allowNegativeDurability);
Weapon storage wep = tokens[id];
return (
oneFrac.add(powerMultPerPointBasic.mul(
ABDKMath64x64.fromUInt(
wep.stat1 + wep.stat2 + wep.stat3
)
)),//targetMult
getPowerMultiplierForTrait(wep.properties, wep.stat1, wep.stat2, wep.stat3, charTrait),
getBonusPowerForFight(id, wep.level),
getTraitFromProperties(wep.properties)
);
}
function drainDurability(uint256 id, uint8 amount, bool allowNegativeDurability) internal {
uint8 durabilityPoints = getDurabilityPointsFromTimestamp(durabilityTimestamp[id]);
require((durabilityPoints >= amount
|| (allowNegativeDurability && durabilityPoints > 0)) // we allow going into negative, but not starting negative
,"Low durability!");
uint64 drainTime = uint64(amount * secondsPerDurability);
if(durabilityPoints >= maxDurability) { // if durability full, we reset timestamp and drain from that
durabilityTimestamp[id] = uint64(now - getDurabilityMaxWait() + drainTime);
}
else {
durabilityTimestamp[id] = uint64(durabilityTimestamp[id] + drainTime);
}
}
function setBurnPointMultiplier(uint256 multiplier) public restricted {
burnPointMultiplier = multiplier;
}
function setLowStarBurnPowerPerPoint(uint256 powerPerBurnPoint) public restricted {
lowStarBurnPowerPerPoint = powerPerBurnPoint;
}
function setFourStarBurnPowerPerPoint(uint256 powerPerBurnPoint) public restricted {
fourStarBurnPowerPerPoint = powerPerBurnPoint;
}
function setFiveStarBurnPowerPerPoint(uint256 powerPerBurnPoint) public restricted {
fiveStarBurnPowerPerPoint = powerPerBurnPoint;
}
function getDurabilityTimestamp(uint256 id) public view returns (uint64) {
return durabilityTimestamp[id];
}
function setDurabilityTimestamp(uint256 id, uint64 timestamp) public restricted {
durabilityTimestamp[id] = timestamp;
}
function getDurabilityPoints(uint256 id) public view returns (uint8) {
return getDurabilityPointsFromTimestamp(durabilityTimestamp[id]);
}
function getDurabilityPointsFromTimestamp(uint64 timestamp) public view returns (uint8) {
if(timestamp > now)
return 0;
uint256 points = (now - timestamp) / secondsPerDurability;
if(points > maxDurability) {
points = maxDurability;
}
return uint8(points);
}
function isDurabilityFull(uint256 id) public view returns (bool) {
return getDurabilityPoints(id) >= maxDurability;
}
function getDurabilityMaxWait() public pure returns (uint64) {
return uint64(maxDurability * secondsPerDurability);
}
function getNftVar(uint256 weaponID, uint256 nftVar) public view returns(uint256) {
return nftVars[weaponID][nftVar];
}
function setNftVar(uint256 weaponID, uint256 nftVar, uint256 value) public restricted {
nftVars[weaponID][nftVar] = value;
}
function setFeatureEnabled(uint256 bit, bool enabled) public restricted {
if (enabled) {
numberParameters[NUMBERPARAMETER_FEATURE_BITS] |= bit;
} else {
numberParameters[NUMBERPARAMETER_FEATURE_BITS] &= ~bit;
}
}
function _isFeatureEnabled(uint256 bit) private view returns (bool) {
return (numberParameters[NUMBERPARAMETER_FEATURE_BITS] & bit) == bit;
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override {
require(nftVars[tokenId][NFTVAR_BUSY] == 0);
// Always allow minting and burning.
if(from != address(0) && to != address(0)) {
// But other transfers require the feature to be enabled.
require(_isFeatureEnabled(BIT_FEATURE_TRANSFER_BLOCKED) == false);
if(promos.getBit(from, 4)) { // bad actors, they can transfer to market but nowhere else
require(hasRole(RECEIVE_DOES_NOT_SET_TRANSFER_TIMESTAMP, to));
}
}
}
}pragma solidity ^0.6.0;
import "abdk-libraries-solidity/ABDKMath64x64.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
library RandomUtil {
using SafeMath for uint256;
function randomSeededMinMax(uint min, uint max, uint seed) internal pure returns (uint) {
// inclusive,inclusive (don't use absolute min and max values of uint256)
// deterministic based on seed provided
uint diff = max.sub(min).add(1);
uint randomVar = uint(keccak256(abi.encodePacked(seed))).mod(diff);
randomVar = randomVar.add(min);
return randomVar;
}
function combineSeeds(uint seed1, uint seed2) internal pure returns (uint) {
return uint(keccak256(abi.encodePacked(seed1, seed2)));
}
function combineSeeds(uint[] memory seeds) internal pure returns (uint) {
return uint(keccak256(abi.encodePacked(seeds)));
}
function plusMinus10PercentSeeded(uint256 num, uint256 seed) internal pure returns (uint256) {
uint256 tenPercent = num.div(10);
return num.sub(tenPercent).add(randomSeededMinMax(0, tenPercent.mul(2), seed));
}
function uint2str(uint _i) internal pure returns (string memory _uintAsString) {
if (_i == 0) {
return "0";
}
uint j = _i;
uint len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint k = len - 1;
while (_i != 0) {
bstr[k--] = byte(uint8(48 + _i % 10));
_i /= 10;
}
return string(bstr);
}
}pragma solidity ^0.6.0;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "abdk-libraries-solidity/ABDKMath64x64.sol";
import "./Promos.sol";
import "./util.sol";
contract Shields is Initializable, ERC721Upgradeable, AccessControlUpgradeable {
using ABDKMath64x64 for int128;
using ABDKMath64x64 for uint16;
bytes32 public constant GAME_ADMIN = keccak256("GAME_ADMIN");
function initialize () public initializer {
__ERC721_init("CryptoBlades shield", "CBS");
__AccessControl_init_unchained();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
shieldBaseMultiplier = ABDKMath64x64.fromUInt(1);
defenseMultPerPointBasic = ABDKMath64x64.divu(1, 400); // 0.25%
defenseMultPerPointDEF = defenseMultPerPointBasic.mul(ABDKMath64x64.divu(103, 100)); // 0.2575% (+3%)
defenseMultPerPointMatching = defenseMultPerPointBasic.mul(ABDKMath64x64.divu(107, 100)); // 0.2675% (+7%)
}
function migrateTo_surprise(Promos _promos) public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
promos = _promos;
}
/*
visual numbers start at 0, increment values by 1
stars: 1-5 (1,2,3: primary only, 4: one secondary, 5: two secondaries)
traits: 0-3 [0(fire) > 1(earth) > 2(lightning) > 3(water) > repeat]
stats: STR(fire), DEX(earth), CHA(lightning), INT(water), BLK(traitless)
base stat rolls: 1*(1-50), 2*(45-75), 3*(70-100), 4*(50-100), 5*(66-100, main is 68-100)
defense: each point contributes .25% to fight defense
cosmetics: 0-255, to be used for future display purposes
*/
struct Shield {
uint16 properties; // right to left: 3b stars, 2b trait, 7b stat pattern, 4b EMPTY
// stats (each point refers to .25% improvement)
uint16 stat1;
uint16 stat2;
uint16 stat3;
}
struct ShieldCosmetics {
uint8 version;
uint256 seed;
}
Shield[] private tokens;
ShieldCosmetics[] private cosmetics;
int128 public shieldBaseMultiplier; // 1.0
int128 public defenseMultPerPointBasic; // 0.25%
int128 public defenseMultPerPointDEF; // 0.2575% (+3%)
int128 public defenseMultPerPointMatching; // 0.2675% (+7%)
uint256 private lastMintedBlock;
uint256 private firstMintedOfLastBlock;
mapping(uint256 => uint64) durabilityTimestamp;
uint256 public constant maxDurability = 20;
uint256 public constant secondsPerDurability = 3000; //50 * 60
Promos public promos;
mapping(uint256 => mapping(uint256 => uint256)) public nftVars;//KEYS: NFTID, VARID
uint256 public constant NFTVAR_BUSY = 1; // value bitflags: 1 (pvp) | 2 (raid) | 4 (TBD)..
uint256 public constant NFTVAR_SHIELD_TYPE = 2; // 0 = normal, 1 = founders, 2 = legendary defender
event NewShield(uint256 indexed shield, address indexed minter);
modifier restricted() {
_restricted();
_;
}
function _restricted() internal view {
require(hasRole(GAME_ADMIN, msg.sender), "Not game admin");
}
modifier noFreshLookup(uint256 id) {
_noFreshLookup(id);
_;
}
function _noFreshLookup(uint256 id) internal view {
require(id < firstMintedOfLastBlock || lastMintedBlock < block.number, "Too fresh for lookup");
}
function getStats(uint256 id) internal view
returns (uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3) {
Shield memory s = tokens[id];
return (s.properties, s.stat1, s.stat2, s.stat3);
}
function get(uint256 id) public view noFreshLookup(id)
returns (
uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3
) {
return _get(id);
}
function _get(uint256 id) internal view
returns (
uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3
) {
return getStats(id);
}
function getOwned() public view returns(uint256[] memory) {
return getOwnedBy(msg.sender);
}
function getOwnedBy(address owner) public view returns(uint256[] memory) {
uint256[] memory tokens = new uint256[](balanceOf(owner));
for(uint256 i = 0; i < tokens.length; i++) {
tokens[i] = tokenOfOwnerByIndex(owner, i);
}
return tokens;
}
function getCosmeticsSeed(uint256 id) public view noFreshLookup(id)
returns (uint256) {
ShieldCosmetics memory sc = cosmetics[id];
return sc.seed;
}
function mint(address minter, uint256 shieldType, uint256 seed) public restricted returns(uint256) {
uint256 stars;
uint256 roll = seed % 100;
// will need revision, possibly manual configuration if we support more than 5 stars
if(roll < 1) {
stars = 4; // 5* at 1%
}
else if(roll < 6) { // 4* at 5%
stars = 3;
}
else if(roll < 21) { // 3* at 15%
stars = 2;
}
else if(roll < 56) { // 2* at 35%
stars = 1;
}
else {
stars = 0; // 1* at 44%
}
return mintShieldWithStars(minter, stars, shieldType, seed);
}
function mintShieldWithStars(address minter, uint256 stars, uint256 shieldType, uint256 seed) public restricted returns(uint256) {
require(stars < 8, "Stars parameter too high! (max 7)");
(uint16 stat1, uint16 stat2, uint16 stat3) = getStatRolls(stars, seed);
return performMintShield(minter,
shieldType,
getRandomProperties(stars, seed),
stat1,
stat2,
stat3,
RandomUtil.combineSeeds(seed,3)
);
}
function performMintShield(address minter,
uint256 shieldType,
uint16 properties,
uint16 stat1, uint16 stat2, uint16 stat3,
uint256 cosmeticSeed
) public restricted returns(uint256) {
uint256 tokenID = tokens.length;
if(block.number != lastMintedBlock)
firstMintedOfLastBlock = tokenID;
lastMintedBlock = block.number;
tokens.push(Shield(properties, stat1, stat2, stat3));
cosmetics.push(ShieldCosmetics(0, cosmeticSeed));
_mint(minter, tokenID);
durabilityTimestamp[tokenID] = uint64(now.sub(getDurabilityMaxWait()));
nftVars[tokenID][NFTVAR_SHIELD_TYPE] = shieldType;
emit NewShield(tokenID, minter);
return tokenID;
}
function performMintShieldDetailed(address minter,
uint256 metaData,
uint256 cosmeticSeed, uint256 tokenID
) public restricted returns(uint256) {
// uint256(uint256(0)) | uint256(stat3) << 16| (uint256(stat2) << 32) | (uint256(stat1) << 48) | (uint256(properties) << 64) | (uint256(appliedCosmetic) << 80);
uint16 stat3 = uint16((metaData >> 16) & 0xFFFF);
uint16 stat2 = uint16((metaData >> 32) & 0xFFFF);
uint16 stat1 = uint16((metaData >> 48) & 0xFFFF);
uint16 properties = uint16((metaData >> 64) & 0xFFFF);
//cosmetics >> 80
uint8 shieldType = uint8(metaData & 0xFF);
if(tokenID == 0){
tokenID = performMintShield(minter, shieldType, properties, stat1, stat2, stat3, 0);
}
else {
Shield storage sh = tokens[tokenID];
sh.properties = properties;
sh.stat1 = stat1;
sh.stat2 = stat2;
sh.stat3 = stat3;
}
ShieldCosmetics storage sc = cosmetics[tokenID];
sc.seed = cosmeticSeed;
durabilityTimestamp[tokenID] = uint64(now); // avoid chain jumping abuse
return tokenID;
}
function getRandomProperties(uint256 stars, uint256 seed) public pure returns (uint16) {
return uint16((stars & 0x7) // stars aren't randomized here!
| ((RandomUtil.randomSeededMinMax(0,3,RandomUtil.combineSeeds(seed,1)) & 0x3) << 3) // trait
| ((RandomUtil.randomSeededMinMax(0,124,RandomUtil.combineSeeds(seed,2)) & 0x7F) << 5)); // statPattern
}
function getStatRolls(uint256 stars, uint256 seed) private pure returns (uint16, uint16, uint16) {
uint16 minRoll = getStatMinRoll(stars);
uint16 maxRoll = getStatMaxRoll(stars);
uint8 statCount = getStatCount(stars);
uint16 stat1 = getRandomStat(minRoll, maxRoll, seed, 5);
uint16 stat2 = 0;
uint16 stat3 = 0;
if(statCount > 1) {
stat2 = getRandomStat(minRoll, maxRoll, seed, 3);
}
if(statCount > 2) {
stat3 = getRandomStat(minRoll, maxRoll, seed, 4);
}
return (stat1, stat2, stat3);
}
function getRandomStat(uint16 minRoll, uint16 maxRoll, uint256 seed, uint256 seed2) public pure returns (uint16) {
return uint16(RandomUtil.randomSeededMinMax(minRoll, maxRoll,RandomUtil.combineSeeds(seed, seed2)));
}
function getRandomCosmetic(uint256 seed, uint256 seed2, uint8 limit) public pure returns (uint8) {
return uint8(RandomUtil.randomSeededMinMax(0, limit, RandomUtil.combineSeeds(seed, seed2)));
}
function getStatMinRoll(uint256 stars) public pure returns (uint16) {
// 1 star
if (stars == 0) return 4;
// 2 star
if (stars == 1) return 180;
// 3 star
if (stars == 2) return 280;
// 4 star
if (stars == 3) return 200;
// 5+ star
return 268;
}
function getStatMaxRoll(uint256 stars) public pure returns (uint16) {
// 3+ star
if (stars > 1) return 400;
// 2 star
if (stars > 0) return 300;
// 1 star
return 200;
}
function getStatCount(uint256 stars) public pure returns (uint8) {
// 1-2 star
if (stars < 3) return 1;
// 3+ star
return uint8(stars)-1;
}
function getProperties(uint256 id) public view noFreshLookup(id) returns (uint16) {
return tokens[id].properties;
}
function getStars(uint256 id) public view noFreshLookup(id) returns (uint8) {
return getStarsFromProperties(getProperties(id));
}
function getStarsFromProperties(uint16 properties) public pure returns (uint8) {
return uint8(properties & 0x7); // first two bits for stars
}
function getTrait(uint256 id) public view noFreshLookup(id) returns (uint8) {
return getTraitFromProperties(getProperties(id));
}
function getTraitFromProperties(uint16 properties) public pure returns (uint8) {
return uint8((properties >> 3) & 0x3); // two bits after star bits (3)
}
function getStatPattern(uint256 id) public view noFreshLookup(id) returns (uint8) {
return getStatPatternFromProperties(getProperties(id));
}
function getStatPatternFromProperties(uint16 properties) public pure returns (uint8) {
return uint8((properties >> 5) & 0x7F); // 7 bits after star(3) and trait(2) bits
}
function getStat1Trait(uint8 statPattern) public pure returns (uint8) {
return uint8(uint256(statPattern) % 5); // 0-3 regular traits, 4 = traitless (DEF)
}
function getStat2Trait(uint8 statPattern) public pure returns (uint8) {
return uint8(SafeMath.div(statPattern, 5) % 5); // 0-3 regular traits, 4 = traitless (DEF)
}
function getStat3Trait(uint8 statPattern) public pure returns (uint8) {
return uint8(SafeMath.div(statPattern, 25) % 5); // 0-3 regular traits, 4 = traitless (DEF)
}
function getStat1(uint256 id) public view noFreshLookup(id) returns (uint16) {
return tokens[id].stat1;
}
function getStat2(uint256 id) public view noFreshLookup(id) returns (uint16) {
return tokens[id].stat2;
}
function getStat3(uint256 id) public view noFreshLookup(id) returns (uint16) {
return tokens[id].stat3;
}
function getDefenseMultiplier(uint256 id) public view noFreshLookup(id) returns (int128) {
// returns a 64.64 fixed point number for defense multiplier
// this function does not account for traits
// it is used to calculate base enemy defenses for targeting
Shield memory shd = tokens[id];
int128 defensePerPoint = defenseMultPerPointBasic;
int128 stat1 = shd.stat1.fromUInt().mul(defensePerPoint);
int128 stat2 = shd.stat2.fromUInt().mul(defensePerPoint);
int128 stat3 = shd.stat3.fromUInt().mul(defensePerPoint);
return shieldBaseMultiplier.add(stat1).add(stat2).add(stat3);
}
function getDefenseMultiplierForTrait(
uint16 properties,
uint16 stat1,
uint16 stat2,
uint16 stat3,
uint8 trait
) public view returns(int128) {
// Does not include character trait to shield trait match
// Only counts arbitrary trait to shield stat trait
// This function can be used by frontend to get expected % bonus for each type
// Making it easy to see on the market how useful it will be to you
uint8 statPattern = getStatPatternFromProperties(properties);
int128 result = shieldBaseMultiplier;
if(getStat1Trait(statPattern) == trait)
result = result.add(stat1.fromUInt().mul(defenseMultPerPointMatching));
else if(getStat1Trait(statPattern) == 4) // DEF, traitless
result = result.add(stat1.fromUInt().mul(defenseMultPerPointDEF));
else
result = result.add(stat1.fromUInt().mul(defenseMultPerPointBasic));
if(getStat2Trait(statPattern) == trait)
result = result.add(stat2.fromUInt().mul(defenseMultPerPointMatching));
else if(getStat2Trait(statPattern) == 4) // DEF, traitless
result = result.add(stat2.fromUInt().mul(defenseMultPerPointDEF));
else
result = result.add(stat2.fromUInt().mul(defenseMultPerPointBasic));
if(getStat3Trait(statPattern) == trait)
result = result.add(stat3.fromUInt().mul(defenseMultPerPointMatching));
else if(getStat3Trait(statPattern) == 4) // DEF, traitless
result = result.add(stat3.fromUInt().mul(defenseMultPerPointDEF));
else
result = result.add(stat3.fromUInt().mul(defenseMultPerPointBasic));
return result;
}
function getFightData(uint256 id, uint8 charTrait) public view noFreshLookup(id) returns (int128, int128, uint24, uint8) {
Shield storage shd = tokens[id];
return (
shieldBaseMultiplier.add(defenseMultPerPointBasic.mul(
ABDKMath64x64.fromUInt(
shd.stat1 + shd.stat2 + shd.stat3
)
)),//targetMult
getDefenseMultiplierForTrait(shd.properties, shd.stat1, shd.stat2, shd.stat3, charTrait),
// Bonus defense support intended in future.
0,
getTraitFromProperties(shd.properties)
);
}
function getFightDataAndDrainDurability(uint256 id, uint8 charTrait, uint8 drainAmount) public
restricted noFreshLookup(id)
returns (int128, int128, uint24, uint8) {
require(nftVars[id][NFTVAR_BUSY] == 0, "Shield is busy");
uint8 durabilityPoints = getDurabilityPointsFromTimestamp(durabilityTimestamp[id]);
require(durabilityPoints >= drainAmount, "Not enough durability!");
uint64 drainTime = uint64(drainAmount * secondsPerDurability);
if(durabilityPoints >= maxDurability) { // if durability full, we reset timestamp and drain from that
durabilityTimestamp[id] = uint64(now - getDurabilityMaxWait() + drainTime);
}
else {
durabilityTimestamp[id] = uint64(durabilityTimestamp[id] + drainTime);
}
Shield storage shd = tokens[id];
return (
shieldBaseMultiplier.add(defenseMultPerPointBasic.mul(
ABDKMath64x64.fromUInt(
shd.stat1 + shd.stat2 + shd.stat3
)
)),//targetMult
getDefenseMultiplierForTrait(shd.properties, shd.stat1, shd.stat2, shd.stat3, charTrait),
// Bonus defense support intended in future.
0,
getTraitFromProperties(shd.properties)
);
}
function drainDurability(uint256 id, uint8 amount) public restricted {
uint8 durabilityPoints = getDurabilityPointsFromTimestamp(durabilityTimestamp[id]);
require(durabilityPoints >= amount, "Not enough durability!");
uint64 drainTime = uint64(amount * secondsPerDurability);
if(durabilityPoints >= maxDurability) { // if durability full, we reset timestamp and drain from that
durabilityTimestamp[id] = uint64(now - getDurabilityMaxWait() + drainTime);
}
else {
durabilityTimestamp[id] = uint64(durabilityTimestamp[id] + drainTime);
}
}
function getDurabilityTimestamp(uint256 id) public view returns (uint64) {
return durabilityTimestamp[id];
}
function setDurabilityTimestamp(uint256 id, uint64 timestamp) public restricted {
durabilityTimestamp[id] = timestamp;
}
function getDurabilityPoints(uint256 id) public view returns (uint8) {
return getDurabilityPointsFromTimestamp(durabilityTimestamp[id]);
}
function getDurabilityPointsFromTimestamp(uint64 timestamp) public view returns (uint8) {
if(timestamp > now)
return 0;
uint256 points = (now - timestamp) / secondsPerDurability;
if(points > maxDurability) {
points = maxDurability;
}
return uint8(points);
}
function isDurabilityFull(uint256 id) public view returns (bool) {
return getDurabilityPoints(id) >= maxDurability;
}
function getDurabilityMaxWait() public pure returns (uint64) {
return uint64(maxDurability * secondsPerDurability);
}
function getNftVar(uint256 shieldID, uint256 nftVar) public view returns(uint256) {
return nftVars[shieldID][nftVar];
}
function setNftVar(uint256 shieldID, uint256 nftVar, uint256 value) public restricted {
nftVars[shieldID][nftVar] = value;
}
function setNftVars(uint256[] calldata ids, uint256 nftVar, uint256 value) external restricted {
for(uint i = 0; i < ids.length; i++)
nftVars[ids[i]][nftVar] = value;
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override {
require(promos.getBit(from, 4) == false && promos.getBit(to, 4) == false
&& nftVars[tokenId][NFTVAR_BUSY] == 0);
}
}pragma solidity ^0.6.5;
interface IStakeFromGame {
function stakeFromGame(address player, uint256 amount) external;
function unstakeToGame(address player, uint256 amount) external;
}pragma solidity ^0.6.5;
interface IRandoms {
// Views
function getRandomSeed(address user) external view returns (uint256 seed);
function getRandomSeedUsingHash(address user, bytes32 hash) external view returns (uint256 seed);
}pragma solidity ^0.6.5;
interface IPriceOracle {
// Views
function currentPrice() external view returns (uint256 price);
// Mutative
function setCurrentPrice(uint256 price) external;
// Events
event CurrentPriceUpdated(uint256 price);
}pragma solidity ^0.6.0;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "abdk-libraries-solidity/ABDKMath64x64.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "./interfaces/IStakeFromGame.sol";
import "./interfaces/IRandoms.sol";
import "./interfaces/IPriceOracle.sol";
import "./characters.sol";
import "./Promos.sol";
import "./weapons.sol";
import "./util.sol";
import "./common.sol";
import "./Blacksmith.sol";
contract CryptoBlades is Initializable, AccessControlUpgradeable {
using ABDKMath64x64 for int128;
using SafeMath for uint256;
using SafeMath for uint64;
using SafeERC20 for IERC20;
bytes32 public constant GAME_ADMIN = keccak256("GAME_ADMIN");
int128 public constant PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT =
14757395258967641292; // 0.8 in fixed-point 64x64 format
// Mapped variables (vars[]) keys, one value per key
// Using small numbers for now to save on contract size (3% for 13 vars vs using uint256(keccak256("name"))!)
// Can be migrated later via setVars if needed
uint256 public constant VAR_HOURLY_INCOME = 1;
uint256 public constant VAR_HOURLY_FIGHTS = 2;
uint256 public constant VAR_HOURLY_POWER_SUM = 3;
uint256 public constant VAR_HOURLY_POWER_AVERAGE = 4;
uint256 public constant VAR_HOURLY_PAY_PER_FIGHT = 5;
uint256 public constant VAR_HOURLY_TIMESTAMP = 6;
uint256 public constant VAR_DAILY_MAX_CLAIM = 7;
uint256 public constant VAR_CLAIM_DEPOSIT_AMOUNT = 8;
uint256 public constant VAR_PARAM_PAYOUT_INCOME_PERCENT = 9;
uint256 public constant VAR_PARAM_DAILY_CLAIM_FIGHTS_LIMIT = 10;
uint256 public constant VAR_PARAM_DAILY_CLAIM_DEPOSIT_PERCENT = 11;
uint256 public constant VAR_PARAM_MAX_FIGHT_PAYOUT = 12;
uint256 public constant VAR_HOURLY_DISTRIBUTION = 13;
uint256 public constant VAR_UNCLAIMED_SKILL = 14;
uint256 public constant VAR_HOURLY_MAX_POWER_AVERAGE = 15;
uint256 public constant VAR_PARAM_HOURLY_MAX_POWER_PERCENT = 16;
uint256 public constant VAR_PARAM_SIGNIFICANT_HOUR_FIGHTS = 17;
uint256 public constant VAR_PARAM_HOURLY_PAY_ALLOWANCE = 18;
// Mapped user variable(userVars[]) keys, one value per wallet
uint256 public constant USERVAR_DAILY_CLAIMED_AMOUNT = 10001;
uint256 public constant USERVAR_CLAIM_TIMESTAMP = 10002;
Characters public characters;
Weapons public weapons;
IERC20 public skillToken;//0x154A9F9cbd3449AD22FDaE23044319D6eF2a1Fab;
IPriceOracle public priceOracleSkillPerUsd;
IRandoms public randoms;
function initialize(IERC20 _skillToken, Characters _characters, Weapons _weapons, IPriceOracle _priceOracleSkillPerUsd, IRandoms _randoms) public initializer {
__AccessControl_init();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(GAME_ADMIN, msg.sender);
skillToken = _skillToken;
characters = _characters;
weapons = _weapons;
priceOracleSkillPerUsd = _priceOracleSkillPerUsd;
randoms = _randoms;
staminaCostFight = 40;
mintCharacterFee = ABDKMath64x64.divu(10, 1);//10 usd;
mintWeaponFee = ABDKMath64x64.divu(3, 1);//3 usd;
// migrateTo_1ee400a
fightXpGain = 32;
// migrateTo_aa9da90
oneFrac = ABDKMath64x64.fromUInt(1);
fightTraitBonus = ABDKMath64x64.divu(75, 1000);
// migrateTo_7dd2a56
// numbers given for the curves were $4.3-aligned so they need to be multiplied
// additional accuracy may be in order for the setter functions for these
fightRewardGasOffset = ABDKMath64x64.divu(23177, 100000); // 0.0539 x 4.3
fightRewardBaseline = ABDKMath64x64.divu(344, 1000); // 0.08 x 4.3
// migrateTo_5e833b0
durabilityCostFight = 1;
}
function migrateTo_ef994e2(Promos _promos) public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
promos = _promos;
}
function migrateTo_23b3a8b(IStakeFromGame _stakeFromGame) external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
stakeFromGameImpl = _stakeFromGame;
}
function migrateTo_801f279() external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
burnWeaponFee = ABDKMath64x64.divu(2, 10);//0.2 usd;
reforgeWeaponWithDustFee = ABDKMath64x64.divu(3, 10);//0.3 usd;
reforgeWeaponFee = burnWeaponFee + reforgeWeaponWithDustFee;//0.5 usd;
}
function migrateTo_60872c8(Blacksmith _blacksmith) external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
blacksmith = _blacksmith;
}
function migrateTo_6a97bd1() external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
rewardsClaimTaxMax = 2767011611056432742; // = ~0.15 = ~15%
rewardsClaimTaxDuration = 15 days;
}
// UNUSED; KEPT FOR UPGRADEABILITY PROXY COMPATIBILITY
uint characterLimit;
// config vars
uint8 staminaCostFight;
// prices & payouts are in USD, with 4 decimals of accuracy in 64.64 fixed point format
int128 public mintCharacterFee;
//int128 public rerollTraitFee;
//int128 public rerollCosmeticsFee;
int128 public refillStaminaFee;
// lvl 1 player power could be anywhere between ~909 to 1666
// cents per fight multiplied by monster power divided by 1000 (lv1 power)
int128 public fightRewardBaseline;
int128 public fightRewardGasOffset;
int128 public mintWeaponFee;
int128 public reforgeWeaponFee;
uint256 nonce;
mapping(address => uint256) lastBlockNumberCalled;
uint256 public fightXpGain; // multiplied based on power differences
mapping(address => uint256) tokenRewards; // user adress : skill wei
mapping(uint256 => uint256) xpRewards; // character id : xp
int128 public oneFrac; // 1.0
int128 public fightTraitBonus; // 7.5%
mapping(address => uint256) public inGameOnlyFunds;
uint256 public totalInGameOnlyFunds;
Promos public promos;
mapping(address => uint256) private _rewardsClaimTaxTimerStart;
IStakeFromGame public stakeFromGameImpl;
uint8 durabilityCostFight;
int128 public burnWeaponFee;
int128 public reforgeWeaponWithDustFee;
Blacksmith public blacksmith;
struct MintPayment {
bytes32 blockHash;
uint256 blockNumber;
address nftAddress;
uint count;
}
mapping(address => MintPayment) mintPayments;
struct MintPaymentSkillDeposited {
uint256 skillDepositedFromWallet;
uint256 skillDepositedFromRewards;
uint256 skillDepositedFromIgo;
uint256 skillRefundableFromWallet;
uint256 skillRefundableFromRewards;
uint256 skillRefundableFromIgo;
uint256 refundClaimableTimestamp;
}
uint256 public totalMintPaymentSkillRefundable;
mapping(address => MintPaymentSkillDeposited) mintPaymentSkillDepositeds;
int128 private rewardsClaimTaxMax;
uint256 private rewardsClaimTaxDuration;
mapping(uint256 => uint256) public vars;
mapping(address => mapping(uint256 => uint256)) public userVars;
event FightOutcome(address indexed owner, uint256 indexed character, uint256 weapon, uint32 target, uint24 playerRoll, uint24 enemyRoll, uint16 xpGain, uint256 skillGain);
event InGameOnlyFundsGiven(address indexed to, uint256 skillAmount);
function recoverSkill(uint256 amount) public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
skillToken.safeTransfer(msg.sender, amount);
}
function REWARDS_CLAIM_TAX_MAX() public view returns (int128) {
return rewardsClaimTaxMax;
}
function REWARDS_CLAIM_TAX_DURATION() public view returns (uint256) {
return rewardsClaimTaxDuration;
}
function getSkillToSubtractSingle(uint256 _needed, uint256 _available)
public
pure
returns (uint256 _used, uint256 _remainder) {
if(_needed <= _available) {
return (_needed, 0);
}
_needed -= _available;
return (_available, _needed);
}
function getSkillToSubtract(uint256 _inGameOnlyFunds, uint256 _tokenRewards, uint256 _skillNeeded)
public
pure
returns (uint256 fromInGameOnlyFunds, uint256 fromTokenRewards, uint256 fromUserWallet) {
if(_skillNeeded <= _inGameOnlyFunds) {
return (_skillNeeded, 0, 0);
}
_skillNeeded -= _inGameOnlyFunds;
if(_skillNeeded <= _tokenRewards) {
return (_inGameOnlyFunds, _skillNeeded, 0);
}
_skillNeeded -= _tokenRewards;
return (_inGameOnlyFunds, _tokenRewards, _skillNeeded);
}
function getSkillNeededFromUserWallet(address playerAddress, uint256 skillNeeded, bool allowInGameOnlyFunds)
public
view
returns (uint256 skillNeededFromUserWallet) {
uint256 inGameOnlyFundsToUse = 0;
if (allowInGameOnlyFunds) {
inGameOnlyFundsToUse = inGameOnlyFunds[playerAddress];
}
(,, skillNeededFromUserWallet) = getSkillToSubtract(
inGameOnlyFundsToUse,
tokenRewards[playerAddress],
skillNeeded
);
}
function unpackFightData(uint96 playerData)
public pure returns (uint8 charTrait, uint24 basePowerLevel, uint64 timestamp) {
charTrait = uint8(playerData & 0xFF);
basePowerLevel = uint24((playerData >> 8) & 0xFFFFFF);
timestamp = uint64((playerData >> 32) & 0xFFFFFFFFFFFFFFFF);
}
function fight(uint256 char, uint256 wep, uint32 target, uint8 fightMultiplier) external
onlyNonContract() {
require(fightMultiplier >= 1 && fightMultiplier <= 5);
(uint8 charTrait, uint24 basePowerLevel, uint64 timestamp) =
unpackFightData(characters.getFightDataAndDrainStamina(msg.sender,
char, staminaCostFight * fightMultiplier, false, 0));
(int128 weaponMultTarget,
int128 weaponMultFight,
uint24 weaponBonusPower,
uint8 weaponTrait) = weapons.getFightDataAndDrainDurability(msg.sender, wep, charTrait,
durabilityCostFight * fightMultiplier, false, 0);
// dirty variable reuse to avoid stack limits
target = grabTarget(
Common.getPlayerPower(basePowerLevel, weaponMultTarget, weaponBonusPower),
timestamp,
target,
now / 1 hours
);
performFight(
char,
wep,
Common.getPlayerPower(basePowerLevel, weaponMultFight, weaponBonusPower),
uint24(charTrait | (uint24(weaponTrait) << 8) | (target & 0xFF000000) >> 8),
uint24(target & 0xFFFFFF),
fightMultiplier
);
}
function performFight(
uint256 char,
uint256 wep,
uint24 playerFightPower,
uint24 traitsCWE, // could fit into uint8 since each trait is only stored on 2 bits (TODO)
uint24 targetPower,
uint8 fightMultiplier
) private {
uint256 seed = uint256(keccak256(abi.encodePacked(now, msg.sender)));
uint24 playerRoll = getPlayerPowerRoll(playerFightPower,traitsCWE,seed);
uint24 monsterRoll = getMonsterPowerRoll(targetPower, RandomUtil.combineSeeds(seed,1));
updateHourlyPayouts(); // maybe only check in trackIncome? (or do via bot)
uint16 xp = getXpGainForFight(playerFightPower, targetPower) * fightMultiplier;
uint256 tokens = getTokenGainForFight(targetPower, true) * fightMultiplier;
if(playerRoll < monsterRoll) {
tokens = 0;
xp = 0;
}
if(tokenRewards[msg.sender] == 0 && tokens > 0) {
_rewardsClaimTaxTimerStart[msg.sender] = block.timestamp;
}
// this may seem dumb but we want to avoid guessing the outcome based on gas estimates!
tokenRewards[msg.sender] += tokens;
vars[VAR_UNCLAIMED_SKILL] += tokens;
vars[VAR_HOURLY_DISTRIBUTION] -= tokens;
xpRewards[char] += xp;
vars[VAR_HOURLY_FIGHTS] += fightMultiplier;
vars[VAR_HOURLY_POWER_SUM] += playerFightPower * fightMultiplier;
emit FightOutcome(msg.sender, char, wep, (targetPower | ((uint32(traitsCWE) << 8) & 0xFF000000)), playerRoll, monsterRoll, xp, tokens);
}
function getMonsterPower(uint32 target) public pure returns (uint24) {
return uint24(target & 0xFFFFFF);
}
function getTokenGainForFight(uint24 monsterPower, bool applyLimit) public view returns (uint256) {
// monsterPower / avgPower * payPerFight * powerMultiplier
uint256 amount = ABDKMath64x64.divu(monsterPower, vars[VAR_HOURLY_POWER_AVERAGE])
.mulu(vars[VAR_HOURLY_PAY_PER_FIGHT]);
if(amount > vars[VAR_PARAM_MAX_FIGHT_PAYOUT])
amount = vars[VAR_PARAM_MAX_FIGHT_PAYOUT];
if(vars[VAR_HOURLY_DISTRIBUTION] < amount * 5 && applyLimit) // the * 5 is a temp measure until we can sync frontend on main
amount = 0;
return amount;
}
function getXpGainForFight(uint24 playerPower, uint24 monsterPower) internal view returns (uint16) {
return uint16(ABDKMath64x64.divu(monsterPower, playerPower).mulu(fightXpGain));
}
function getPlayerPowerRoll(
uint24 playerFightPower,
uint24 traitsCWE,
uint256 seed
) internal view returns(uint24) {
uint256 playerPower = RandomUtil.plusMinus10PercentSeeded(playerFightPower,seed);
return uint24(getPlayerTraitBonusAgainst(traitsCWE).mulu(playerPower));
}
function getMonsterPowerRoll(uint24 monsterPower, uint256 seed) internal pure returns(uint24) {
// roll for fights
return uint24(RandomUtil.plusMinus10PercentSeeded(monsterPower, seed));
}
function getPlayerTraitBonusAgainst(uint24 traitsCWE) public view returns (int128) {
int128 traitBonus = oneFrac;
uint8 characterTrait = uint8(traitsCWE & 0xFF);
if(characterTrait == (traitsCWE >> 8) & 0xFF/*wepTrait*/) {
traitBonus = traitBonus.add(fightTraitBonus);
}
if(Common.isTraitEffectiveAgainst(characterTrait, uint8(traitsCWE >> 16)/*enemy*/)) {
traitBonus = traitBonus.add(fightTraitBonus);
}
else if(Common.isTraitEffectiveAgainst(uint8(traitsCWE >> 16)/*enemy*/, characterTrait)) {
traitBonus = traitBonus.sub(fightTraitBonus);
}
return traitBonus;
}
function getTargets(uint256 char, uint256 wep) public view returns (uint32[4] memory) {
// this is a frontend function
(int128 weaponMultTarget,,
uint24 weaponBonusPower,
) = weapons.getFightData(wep, characters.getTrait(char));
return getTargetsInternal(
Common.getPlayerPower(uint24(characters.getTotalPower(char)), weaponMultTarget, weaponBonusPower),
characters.getStaminaTimestamp(char),
now / 1 hours
);
}
function getTargetsInternal(uint24 playerPower,
uint64 staminaTimestamp,
uint256 currentHour
) private pure returns (uint32[4] memory) {
// 4 targets, roll powers based on character + weapon power
// trait bonuses not accounted for
// targets expire on the hour
uint32[4] memory targets;
for(uint32 i = 0; i < targets.length; i++) {
// we alter seed per-index or they would be all the same
// this is a read only function so it's fine to pack all 4 params each iteration
// for the sake of target picking it needs to be the same as in grabTarget(i)
// even the exact type of "i" is important here
uint256 indexSeed = uint256(keccak256(abi.encodePacked(
staminaTimestamp, currentHour, playerPower, i
)));
targets[i] = uint32(
RandomUtil.plusMinus10PercentSeeded(playerPower, indexSeed) // power
| (uint32(indexSeed % 4) << 24) // trait
);
}
return targets;
}
function grabTarget(
uint24 playerPower,
uint64 staminaTimestamp,
uint32 enemyIndex,
uint256 currentHour
) private pure returns (uint32) {
require(enemyIndex < 4);
uint256 enemySeed = uint256(keccak256(abi.encodePacked(
staminaTimestamp, currentHour, playerPower, enemyIndex
)));
return uint32(
RandomUtil.plusMinus10PercentSeeded(playerPower, enemySeed) // power
| (uint32(enemySeed % 4) << 24) // trait
);
}
function mintCharacter() public onlyNonContract oncePerBlock(msg.sender) {
uint256 skillAmount = usdToSkill(mintCharacterFee);
(,, uint256 fromUserWallet) =
getSkillToSubtract(
0,
tokenRewards[msg.sender],
skillAmount
);
require(skillToken.balanceOf(msg.sender) >= fromUserWallet && promos.getBit(msg.sender, 4) == false);
uint256 convertedAmount = usdToSkill(mintCharacterFee);
_payContractTokenOnly(msg.sender, convertedAmount);
uint256 seed = randoms.getRandomSeed(msg.sender);
characters.mint(msg.sender, seed);
// first weapon free with a character mint, max 1 star
if(weapons.balanceOf(msg.sender) == 0) {
weapons.performMintWeapon(msg.sender,
weapons.getRandomProperties(0, RandomUtil.combineSeeds(seed,100), 100),
weapons.getRandomStat(4, 200, seed, 101),
0, // stat2
0, // stat3
RandomUtil.combineSeeds(seed,102)
);
}
}
function mintWeaponN(uint32 num, uint8 chosenElement)
external
onlyNonContract
oncePerBlock(msg.sender)
{
uint8 chosenElementFee = chosenElement == 100 ? 1 : 2;
_payContractConvertedSupportingStaked(msg.sender, usdToSkill(mintWeaponFee * num * chosenElementFee));
_mintWeaponNLogic(num, chosenElement);
}
function mintWeapon(uint8 chosenElement) external onlyNonContract oncePerBlock(msg.sender) {
uint8 chosenElementFee = chosenElement == 100 ? 1 : 2;
_payContractConvertedSupportingStaked(msg.sender, usdToSkill(mintWeaponFee * chosenElementFee));
_mintWeaponLogic(chosenElement);
}
function mintWeaponNUsingStakedSkill(uint32 num, uint8 chosenElement)
external
onlyNonContract
oncePerBlock(msg.sender)
{
uint8 chosenElementFee = chosenElement == 100 ? 1 : 2;
int128 discountedMintWeaponFee =
mintWeaponFee
.mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT)
.mul(ABDKMath64x64.fromUInt(num))
.mul(ABDKMath64x64.fromUInt(chosenElementFee));
_payContractStakedOnly(msg.sender, usdToSkill(discountedMintWeaponFee));
_mintWeaponNLogic(num, chosenElement);
}
function mintWeaponUsingStakedSkill(uint8 chosenElement) external onlyNonContract oncePerBlock(msg.sender) {
uint8 chosenElementFee = chosenElement == 100 ? 1 : 2;
int128 discountedMintWeaponFee =
mintWeaponFee
.mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT)
.mul(ABDKMath64x64.fromUInt(chosenElementFee));
_payContractStakedOnly(msg.sender, usdToSkill(discountedMintWeaponFee));
_mintWeaponLogic(chosenElement);
}
function _mintWeaponNLogic(uint32 num, uint8 chosenElement) internal {
require(num > 0 && num <= 10);
for (uint i = 0; i < num; i++) {
weapons.mint(msg.sender, uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), msg.sender, i))), chosenElement);
}
}
function _mintWeaponLogic(uint8 chosenElement) internal {
//uint256 seed = randoms.getRandomSeed(msg.sender);
weapons.mint(msg.sender, uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), msg.sender))), chosenElement);
}
function burnWeapon(uint256 burnID) external isWeaponOwner(burnID) {
_payContractConvertedSupportingStaked(msg.sender, usdToSkill(burnWeaponFee));
_burnWeaponLogic(burnID);
}
function burnWeapons(uint256[] calldata burnIDs) external isWeaponsOwner(burnIDs) {
_payContractConvertedSupportingStaked(msg.sender, usdToSkill(burnWeaponFee.mul(ABDKMath64x64.fromUInt(burnIDs.length))));
_burnWeaponsLogic(burnIDs);
}
function reforgeWeapon(uint256 reforgeID, uint256 burnID) external isWeaponOwner(reforgeID) isWeaponOwner(burnID) {
_payContractConvertedSupportingStaked(msg.sender, usdToSkill(reforgeWeaponFee));
_reforgeWeaponLogic(reforgeID, burnID);
}
function reforgeWeaponWithDust(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) external isWeaponOwner(reforgeID) {
_payContractConvertedSupportingStaked(msg.sender, usdToSkill(reforgeWeaponWithDustFee));
_reforgeWeaponWithDustLogic(reforgeID, amountLB, amount4B, amount5B);
}
function burnWeaponUsingStakedSkill(uint256 burnID) external isWeaponOwner(burnID) {
int128 discountedBurnWeaponFee =
burnWeaponFee.mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT);
_payContractStakedOnly(msg.sender, usdToSkill(discountedBurnWeaponFee));
_burnWeaponLogic(burnID);
}
function burnWeaponsUsingStakedSkill(uint256[] calldata burnIDs) external isWeaponsOwner(burnIDs) {
int128 discountedBurnWeaponFee =
burnWeaponFee
.mul(ABDKMath64x64.fromUInt(burnIDs.length))
.mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT);
_payContractStakedOnly(msg.sender, usdToSkill(discountedBurnWeaponFee));
_burnWeaponsLogic(burnIDs);
}
function reforgeWeaponUsingStakedSkill(uint256 reforgeID, uint256 burnID) external isWeaponOwner(reforgeID) isWeaponOwner(burnID) {
int128 discountedReforgeWeaponFee =
reforgeWeaponFee
.mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT);
_payContractStakedOnly(msg.sender, usdToSkill(discountedReforgeWeaponFee));
_reforgeWeaponLogic(reforgeID, burnID);
}
function reforgeWeaponWithDustUsingStakedSkill(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) external isWeaponOwner(reforgeID) {
int128 discountedReforgeWeaponWithDustFee =
reforgeWeaponWithDustFee
.mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT);
_payContractStakedOnly(msg.sender, usdToSkill(discountedReforgeWeaponWithDustFee));
_reforgeWeaponWithDustLogic(reforgeID, amountLB, amount4B, amount5B);
}
function _burnWeaponLogic(uint256 burnID) internal {
weapons.burn(burnID);
}
function _burnWeaponsLogic(uint256[] memory burnIDs) internal {
for(uint i = 0; i < burnIDs.length; i++) {
weapons.burn(burnIDs[i]);
}
}
function _reforgeWeaponLogic(uint256 reforgeID, uint256 burnID) internal {
weapons.reforge(reforgeID, burnID);
}
function _reforgeWeaponWithDustLogic(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) internal {
weapons.reforgeWithDust(reforgeID, amountLB, amount4B, amount5B);
}
function migrateRandoms(IRandoms _newRandoms) external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
randoms = _newRandoms;
}
modifier onlyNonContract() {
_onlyNonContract();
_;
}
function _onlyNonContract() internal view {
require(tx.origin == msg.sender, "ONC");
}
modifier restricted() {
_restricted();
_;
}
function _restricted() internal view {
require(hasRole(GAME_ADMIN, msg.sender), "NGA");
}
modifier oncePerBlock(address user) {
_oncePerBlock(user);
_;
}
function _oncePerBlock(address user) internal {
require(lastBlockNumberCalled[user] < block.number, "OCB");
lastBlockNumberCalled[user] = block.number;
}
modifier isWeaponOwner(uint256 weapon) {
_isWeaponOwner(weapon);
_;
}
function _isWeaponOwner(uint256 weapon) internal view {
require(weapons.ownerOf(weapon) == msg.sender);
}
modifier isWeaponsOwner(uint256[] memory weaponArray) {
_isWeaponsOwner(weaponArray);
_;
}
function _isWeaponsOwner(uint256[] memory weaponArray) internal view {
for(uint i = 0; i < weaponArray.length; i++) {
require(weapons.ownerOf(weaponArray[i]) == msg.sender);
}
}
modifier isCharacterOwner(uint256 character) {
_isCharacterOwner(character);
_;
}
function _isCharacterOwner(uint256 character) internal view {
require(characters.ownerOf(character) == msg.sender);
}
function payPlayerConverted(address playerAddress, uint256 convertedAmount) public restricted {
_payPlayerConverted(playerAddress, convertedAmount);
}
function payContractTokenOnly(address playerAddress, uint256 convertedAmount) public restricted {
_payContractTokenOnly(playerAddress, convertedAmount, true);
}
function payContractTokenOnly(address playerAddress, uint256 convertedAmount, bool track) public restricted {
_payContractTokenOnly(playerAddress, convertedAmount, track);
}
function _payContractTokenOnly(address playerAddress, uint256 convertedAmount) internal {
_payContractTokenOnly(playerAddress, convertedAmount, true);
}
function _payContractTokenOnly(address playerAddress, uint256 convertedAmount, bool track) internal {
(, uint256 fromTokenRewards, uint256 fromUserWallet) =
getSkillToSubtract(
0,
tokenRewards[playerAddress],
convertedAmount
);
_deductPlayerSkillStandard(playerAddress, 0, fromTokenRewards, fromUserWallet, track);
}
function _payContract(address playerAddress, int128 usdAmount) internal
returns (uint256 _fromInGameOnlyFunds, uint256 _fromTokenRewards, uint256 _fromUserWallet) {
return _payContractConverted(playerAddress, usdToSkill(usdAmount));
}
function _payContractConverted(address playerAddress, uint256 convertedAmount) internal
returns (uint256 _fromInGameOnlyFunds, uint256 _fromTokenRewards, uint256 _fromUserWallet) {
(uint256 fromInGameOnlyFunds, uint256 fromTokenRewards, uint256 fromUserWallet) =
getSkillToSubtract(
inGameOnlyFunds[playerAddress],
tokenRewards[playerAddress],
convertedAmount
);
require(skillToken.balanceOf(playerAddress) >= fromUserWallet,
string(abi.encodePacked("Not enough SKILL! Need ",RandomUtil.uint2str(convertedAmount))));
_deductPlayerSkillStandard(playerAddress, fromInGameOnlyFunds, fromTokenRewards, fromUserWallet);
return (fromInGameOnlyFunds, fromTokenRewards, fromUserWallet);
}
function _payContractConvertedSupportingStaked(address playerAddress, uint256 convertedAmount) internal
returns (
uint256 _fromInGameOnlyFunds,
uint256 _fromTokenRewards,
uint256 _fromUserWallet,
uint256 _fromStaked
) {
(uint256 fromInGameOnlyFunds, uint256 fromTokenRewards, uint256 _remainder) =
getSkillToSubtract(
inGameOnlyFunds[playerAddress],
tokenRewards[playerAddress],
convertedAmount
);
(uint256 fromUserWallet, uint256 fromStaked) =
getSkillToSubtractSingle(
_remainder,
skillToken.balanceOf(playerAddress)
);
_deductPlayerSkillStandard(playerAddress, fromInGameOnlyFunds, fromTokenRewards, fromUserWallet);
if(fromStaked > 0) {
stakeFromGameImpl.unstakeToGame(playerAddress, fromStaked);
_trackIncome(fromStaked);
}
return (fromInGameOnlyFunds, fromTokenRewards, fromUserWallet, fromStaked);
}
function _payContractStakedOnly(address playerAddress, uint256 convertedAmount) internal {
stakeFromGameImpl.unstakeToGame(playerAddress, convertedAmount);
_trackIncome(convertedAmount);
}
function _deductPlayerSkillStandard(
address playerAddress,
uint256 fromInGameOnlyFunds,
uint256 fromTokenRewards,
uint256 fromUserWallet
) internal {
_deductPlayerSkillStandard(
playerAddress,
fromInGameOnlyFunds,
fromTokenRewards,
fromUserWallet,
true
);
}
function _deductPlayerSkillStandard(
address playerAddress,
uint256 fromInGameOnlyFunds,
uint256 fromTokenRewards,
uint256 fromUserWallet,
bool trackInflow
) internal {
if(fromInGameOnlyFunds > 0) {
totalInGameOnlyFunds = totalInGameOnlyFunds.sub(fromInGameOnlyFunds);
inGameOnlyFunds[playerAddress] = inGameOnlyFunds[playerAddress].sub(fromInGameOnlyFunds);
}
if(fromTokenRewards > 0) {
tokenRewards[playerAddress] = tokenRewards[playerAddress].sub(fromTokenRewards);
}
if(fromUserWallet > 0) {
skillToken.transferFrom(playerAddress, address(this), fromUserWallet);
if(trackInflow)
_trackIncome(fromUserWallet);
}
}
function deductAfterPartnerClaim(uint256 amount, address player) external restricted {
tokenRewards[player] = tokenRewards[player].sub(amount);
_trackIncome(amount);
}
function trackIncome(uint256 income) public restricted {
_trackIncome(income);
}
function _trackIncome(uint256 income) internal {
vars[VAR_HOURLY_INCOME] += ABDKMath64x64.divu(vars[VAR_PARAM_PAYOUT_INCOME_PERCENT],100)
.mulu(income);
updateHourlyPayouts();
}
function updateHourlyPayouts() internal {
// Could be done by a bot instead?
if(now - vars[VAR_HOURLY_TIMESTAMP] >= 1 hours) {
vars[VAR_HOURLY_TIMESTAMP] = now;
uint256 undistributed = vars[VAR_HOURLY_INCOME] + vars[VAR_HOURLY_DISTRIBUTION];
vars[VAR_HOURLY_DISTRIBUTION] = undistributed > vars[VAR_PARAM_HOURLY_PAY_ALLOWANCE]
? vars[VAR_PARAM_HOURLY_PAY_ALLOWANCE] : undistributed;
vars[VAR_HOURLY_INCOME] = undistributed.sub(vars[VAR_HOURLY_DISTRIBUTION]);
uint256 fights = vars[VAR_HOURLY_FIGHTS];
if(fights >= vars[VAR_PARAM_SIGNIFICANT_HOUR_FIGHTS]) {
uint256 averagePower = vars[VAR_HOURLY_POWER_SUM] / fights;
if(averagePower > vars[VAR_HOURLY_MAX_POWER_AVERAGE])
vars[VAR_HOURLY_MAX_POWER_AVERAGE] = averagePower;
}
vars[VAR_HOURLY_POWER_AVERAGE] = ABDKMath64x64.divu(vars[VAR_PARAM_HOURLY_MAX_POWER_PERCENT],100)
.mulu(vars[VAR_HOURLY_MAX_POWER_AVERAGE]);
vars[VAR_DAILY_MAX_CLAIM] = vars[VAR_HOURLY_PAY_PER_FIGHT] * vars[VAR_PARAM_DAILY_CLAIM_FIGHTS_LIMIT];
vars[VAR_HOURLY_FIGHTS] = 0;
vars[VAR_HOURLY_POWER_SUM] = 0;
}
}
function _payPlayer(address playerAddress, int128 baseAmount) internal {
_payPlayerConverted(playerAddress, usdToSkill(baseAmount));
}
function _payPlayerConverted(address playerAddress, uint256 convertedAmount) internal {
skillToken.transfer(playerAddress, convertedAmount);
}
function setCharacterMintValue(uint256 cents) public restricted {
mintCharacterFee = ABDKMath64x64.divu(cents, 100);
}
function setWeaponMintValue(uint256 cents) public restricted {
mintWeaponFee = ABDKMath64x64.divu(cents, 100);
}
function setBurnWeaponValue(uint256 cents) public restricted {
burnWeaponFee = ABDKMath64x64.divu(cents, 100);
}
function setReforgeWeaponValue(uint256 cents) public restricted {
int128 newReforgeWeaponFee = ABDKMath64x64.divu(cents, 100);
require(newReforgeWeaponFee > burnWeaponFee);
reforgeWeaponWithDustFee = newReforgeWeaponFee - burnWeaponFee;
reforgeWeaponFee = newReforgeWeaponFee;
}
function setReforgeWeaponWithDustValue(uint256 cents) public restricted {
reforgeWeaponWithDustFee = ABDKMath64x64.divu(cents, 100);
reforgeWeaponFee = burnWeaponFee + reforgeWeaponWithDustFee;
}
function setStaminaCostFight(uint8 points) public restricted {
staminaCostFight = points;
}
function setDurabilityCostFight(uint8 points) public restricted {
durabilityCostFight = points;
}
function setFightXpGain(uint256 average) public restricted {
fightXpGain = average;
}
function setRewardsClaimTaxMaxAsPercent(uint256 _percent) public restricted {
rewardsClaimTaxMax = ABDKMath64x64.divu(_percent, 100);
}
function setRewardsClaimTaxDuration(uint256 _rewardsClaimTaxDuration) public restricted {
rewardsClaimTaxDuration = _rewardsClaimTaxDuration;
}
function setVar(uint256 varField, uint256 value) external restricted {
vars[varField] = value;
}
function setVars(uint256[] calldata varFields, uint256[] calldata values) external restricted {
for(uint i = 0; i < varFields.length; i++) {
vars[varFields[i]] = values[i];
}
}
function giveInGameOnlyFunds(address to, uint256 skillAmount) external restricted {
totalInGameOnlyFunds = totalInGameOnlyFunds.add(skillAmount);
inGameOnlyFunds[to] = inGameOnlyFunds[to].add(skillAmount);
skillToken.safeTransferFrom(msg.sender, address(this), skillAmount);
emit InGameOnlyFundsGiven(to, skillAmount);
}
function _giveInGameOnlyFundsFromContractBalance(address to, uint256 skillAmount) internal {
totalInGameOnlyFunds = totalInGameOnlyFunds.add(skillAmount);
inGameOnlyFunds[to] = inGameOnlyFunds[to].add(skillAmount);
emit InGameOnlyFundsGiven(to, skillAmount);
}
function giveInGameOnlyFundsFromContractBalance(address to, uint256 skillAmount) external restricted {
_giveInGameOnlyFundsFromContractBalance(to, skillAmount);
}
function usdToSkill(int128 usdAmount) public view returns (uint256) {
return usdAmount.mulu(priceOracleSkillPerUsd.currentPrice());
}
function claimTokenRewards() public {
claimTokenRewards(getRemainingTokenClaimAmountPreTax());
}
function claimTokenRewards(uint256 _claimingAmount) public {
trackDailyClaim(_claimingAmount);
uint256 _tokenRewardsToPayOut = _claimingAmount.sub(
_getRewardsClaimTax(msg.sender).mulu(_claimingAmount)
);
// Tax goes to game contract itself, which would mean
// transferring from the game contract to ...itself.
// So we don't need to do anything with the tax part of the rewards.
if(promos.getBit(msg.sender, 4) == false) {
_payPlayerConverted(msg.sender, _tokenRewardsToPayOut);
if(_tokenRewardsToPayOut <= vars[VAR_UNCLAIMED_SKILL])
vars[VAR_UNCLAIMED_SKILL] -= _tokenRewardsToPayOut;
}
}
function trackDailyClaim(uint256 _claimingAmount) internal {
if(isDailyTokenClaimAmountExpired()) {
userVars[msg.sender][USERVAR_CLAIM_TIMESTAMP] = now;
userVars[msg.sender][USERVAR_DAILY_CLAIMED_AMOUNT] = 0;
}
require(_claimingAmount <= getRemainingTokenClaimAmountPreTax() && _claimingAmount > 0);
// safemath throws error on negative
tokenRewards[msg.sender] = tokenRewards[msg.sender].sub(_claimingAmount);
userVars[msg.sender][USERVAR_DAILY_CLAIMED_AMOUNT] += _claimingAmount;
}
function isDailyTokenClaimAmountExpired() public view returns (bool) {
return userVars[msg.sender][USERVAR_CLAIM_TIMESTAMP] <= now - 1 days;
}
function getClaimedTokensToday() public view returns (uint256) {
// if claim timestamp is older than a day, it's reset to 0
return isDailyTokenClaimAmountExpired() ? 0 : userVars[msg.sender][USERVAR_DAILY_CLAIMED_AMOUNT];
}
function getRemainingTokenClaimAmountPreTax() public view returns (uint256) {
// used to get how much can be withdrawn until the daily withdraw timer expires
uint256 max = getMaxTokenClaimAmountPreTax();
uint256 claimed = getClaimedTokensToday();
if(claimed >= max)
return 0; // all tapped out for today
uint256 remainingOfMax = max-claimed;
return tokenRewards[msg.sender] >= remainingOfMax ? remainingOfMax : tokenRewards[msg.sender];
}
function getMaxTokenClaimAmountPreTax() public view returns(uint256) {
// if tokenRewards is above VAR_CLAIM_DEPOSIT_AMOUNT, we let them withdraw more
// this function does not account for amount already withdrawn today
if(tokenRewards[msg.sender] >= vars[VAR_CLAIM_DEPOSIT_AMOUNT]) { // deposit bonus active
// max is either 10% of amount above deposit, or 2x the regular limit, whichever is higher
uint256 aboveDepositAdjusted = ABDKMath64x64.divu(vars[VAR_PARAM_DAILY_CLAIM_DEPOSIT_PERCENT],100)
.mulu(tokenRewards[msg.sender]-vars[VAR_CLAIM_DEPOSIT_AMOUNT]); // 10% above deposit
if(aboveDepositAdjusted > vars[VAR_DAILY_MAX_CLAIM] * 2) {
return aboveDepositAdjusted;
}
return vars[VAR_DAILY_MAX_CLAIM] * 2;
}
return vars[VAR_DAILY_MAX_CLAIM];
}
function stakeUnclaimedRewards() public {
stakeUnclaimedRewards(getRemainingTokenClaimAmountPreTax());
}
function stakeUnclaimedRewards(uint256 amount) public {
trackDailyClaim(amount);
if(promos.getBit(msg.sender, 4) == false) {
skillToken.approve(address(stakeFromGameImpl), amount);
stakeFromGameImpl.stakeFromGame(msg.sender, amount);
}
}
function claimXpRewards() public {
// our characters go to the tavern to rest
// they meditate on what they've learned
uint256[] memory chars = characters.getReadyCharacters(msg.sender);
require(chars.length > 0);
uint256[] memory xps = new uint256[](chars.length);
for(uint256 i = 0; i < chars.length; i++) {
xps[i] = xpRewards[chars[i]];
xpRewards[chars[i]] = 0;
}
characters.gainXpAll(chars, xps);
}
function resetXp(uint256[] memory chars) public restricted {
for(uint256 i = 0; i < chars.length; i++) {
xpRewards[chars[i]] = 0;
}
}
function getTokenRewards() public view returns (uint256) {
return tokenRewards[msg.sender];
}
function getXpRewards(uint256[] memory chars) public view returns (uint256[] memory) {
uint charsAmount = chars.length;
uint256[] memory xps = new uint256[](charsAmount);
for(uint i = 0; i < chars.length; i++) {
xps[i] = xpRewards[chars[i]];
}
return xps;
}
function getTokenRewardsFor(address wallet) public view returns (uint256) {
return tokenRewards[wallet];
}
function getTotalSkillOwnedBy(address wallet) public view returns (uint256) {
return inGameOnlyFunds[wallet] + getTokenRewardsFor(wallet) + skillToken.balanceOf(wallet);
}
function _getRewardsClaimTax(address playerAddress) internal view returns (int128) {
assert(_rewardsClaimTaxTimerStart[playerAddress] <= block.timestamp);
uint256 rewardsClaimTaxTimerEnd = _rewardsClaimTaxTimerStart[playerAddress].add(rewardsClaimTaxDuration);
(, uint256 durationUntilNoTax) = rewardsClaimTaxTimerEnd.trySub(block.timestamp);
assert(0 <= durationUntilNoTax && durationUntilNoTax <= rewardsClaimTaxDuration);
int128 frac = ABDKMath64x64.divu(durationUntilNoTax, rewardsClaimTaxDuration);
return rewardsClaimTaxMax.mul(frac);
}
function getOwnRewardsClaimTax() public view returns (int128) {
return _getRewardsClaimTax(msg.sender);
}
}pragma solidity ^0.6.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "abdk-libraries-solidity/ABDKMath64x64.sol";
library Common {
using ABDKMath64x64 for int128;
using SafeMath for uint256;
function isTraitEffectiveAgainst(uint8 attacker, uint8 defender) internal pure returns (bool) {
return (((attacker + 1) % 4) == defender); // Thanks to Tourist
}
function getPlayerPower(
uint24 basePower,
int128 weaponMultiplier,
uint24 bonusPower
) internal pure returns(uint24) {
return uint24(weaponMultiplier.mulu(basePower).add(bonusPower));
}
}pragma solidity ^0.6.0;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "./Promos.sol";
import "./util.sol";
import "./Garrison.sol";
contract Characters is Initializable, ERC721Upgradeable, AccessControlUpgradeable {
using SafeMath for uint16;
using SafeMath for uint8;
bytes32 public constant GAME_ADMIN = keccak256("GAME_ADMIN");
bytes32 public constant NO_OWNED_LIMIT = keccak256("NO_OWNED_LIMIT");
bytes32 public constant RECEIVE_DOES_NOT_SET_TRANSFER_TIMESTAMP = keccak256("RECEIVE_DOES_NOT_SET_TRANSFER_TIMESTAMP");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
function initialize () public initializer {
__ERC721_init("CryptoBlades character", "CBC");
__AccessControl_init_unchained();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function migrateTo_1ee400a() public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
experienceTable = [
16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 33, 36, 39, 42, 46, 50, 55, 60, 66
, 72, 79, 86, 94, 103, 113, 124, 136, 149, 163, 178, 194, 211, 229, 248, 268
, 289, 311, 334, 358, 383, 409, 436, 464, 493, 523, 554, 586, 619, 653, 688
, 724, 761, 799, 838, 878, 919, 961, 1004, 1048, 1093, 1139, 1186, 1234, 1283
, 1333, 1384, 1436, 1489, 1543, 1598, 1654, 1711, 1769, 1828, 1888, 1949, 2011
, 2074, 2138, 2203, 2269, 2336, 2404, 2473, 2543, 2614, 2686, 2759, 2833, 2908
, 2984, 3061, 3139, 3218, 3298, 3379, 3461, 3544, 3628, 3713, 3799, 3886, 3974
, 4063, 4153, 4244, 4336, 4429, 4523, 4618, 4714, 4811, 4909, 5008, 5108, 5209
, 5311, 5414, 5518, 5623, 5729, 5836, 5944, 6053, 6163, 6274, 6386, 6499, 6613
, 6728, 6844, 6961, 7079, 7198, 7318, 7439, 7561, 7684, 7808, 7933, 8059, 8186
, 8314, 8443, 8573, 8704, 8836, 8969, 9103, 9238, 9374, 9511, 9649, 9788, 9928
, 10069, 10211, 10354, 10498, 10643, 10789, 10936, 11084, 11233, 11383, 11534
, 11686, 11839, 11993, 12148, 12304, 12461, 12619, 12778, 12938, 13099, 13261
, 13424, 13588, 13753, 13919, 14086, 14254, 14423, 14593, 14764, 14936, 15109
, 15283, 15458, 15634, 15811, 15989, 16168, 16348, 16529, 16711, 16894, 17078
, 17263, 17449, 17636, 17824, 18013, 18203, 18394, 18586, 18779, 18973, 19168
, 19364, 19561, 19759, 19958, 20158, 20359, 20561, 20764, 20968, 21173, 21379
, 21586, 21794, 22003, 22213, 22424, 22636, 22849, 23063, 23278, 23494, 23711
, 23929, 24148, 24368, 24589, 24811, 25034, 25258, 25483, 25709, 25936, 26164
, 26393, 26623, 26854, 27086, 27319, 27553, 27788, 28024, 28261, 28499, 28738
, 28978
];
}
function migrateTo_951a020() public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
// Apparently ERC165 interfaces cannot be removed in this version of the OpenZeppelin library.
// But if we remove the registration, then while local deployments would not register the interface ID,
// existing deployments on both testnet and mainnet would still be registered to handle it.
// That sort of inconsistency is a good way to attract bugs that only happens on some environments.
// Hence, we keep registering the interface despite not actually implementing the interface.
_registerInterface(0xe62e6974); // TransferCooldownableInterfaceId.interfaceId()
}
function migrateTo_ef994e2(Promos _promos) public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
promos = _promos;
}
function migrateTo_b627f23() external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
characterLimit = 4;
}
function migrateTo_1a19cbb(Garrison _garrison) external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
garrison = _garrison;
}
/*
visual numbers start at 0, increment values by 1
levels: 1-256
traits: 0-3 [0(fire) > 1(earth) > 2(lightning) > 3(water) > repeat]
*/
struct Character {
uint16 xp; // xp to next level
uint8 level; // up to 256 cap
uint8 trait; // 2b trait, TBD
uint64 staminaTimestamp; // standard timestamp in seconds-resolution marking regen start from 0
}
struct CharacterCosmetics {
uint8 version;
uint256 seed;
}
Character[] private tokens;
CharacterCosmetics[] private cosmetics;
uint256 public constant maxStamina = 200;
uint256 public constant secondsPerStamina = 300; //5 * 60
uint256[256] private experienceTable; // fastest lookup in the west
// UNUSED; KEPT FOR UPGRADEABILITY PROXY COMPATIBILITY
mapping(uint256 => uint256) public lastTransferTimestamp;
Promos public promos;
uint256 private lastMintedBlock;
uint256 private firstMintedOfLastBlock;
uint256 public characterLimit;
mapping(uint256 => uint256) public raidsDone;
mapping(uint256 => uint256) public raidsWon;
mapping(uint256 => mapping(uint256 => uint256)) public nftVars;//KEYS: NFTID, VARID
uint256 public constant NFTVAR_BUSY = 1; // value bitflags: 1 (pvp) | 2 (raid) | 4 (TBD)..
Garrison public garrison;
uint256 public constant NFTVAR_BONUS_POWER = 2;
event NewCharacter(uint256 indexed character, address indexed minter);
event LevelUp(address indexed owner, uint256 indexed character, uint16 level);
event Burned(address indexed owner, uint256 indexed id);
modifier restricted() {
_restricted();
_;
}
function _restricted() internal view {
require(hasRole(GAME_ADMIN, msg.sender), "NA");
}
modifier noFreshLookup(uint256 id) {
_noFreshLookup(id);
_;
}
modifier minterOnly() {
_minterOnly();
_;
}
function _minterOnly() internal view {
require(hasRole(GAME_ADMIN, msg.sender) || hasRole(MINTER_ROLE, msg.sender), 'no access');
}
function _noFreshLookup(uint256 id) internal view {
require(id < firstMintedOfLastBlock || lastMintedBlock < block.number, "Too fresh for lookup");
}
function get(uint256 id) public view noFreshLookup(id) returns (uint16, uint8, uint8, uint64, uint16, uint16, uint16, uint16, uint16, uint16) {
Character memory c = tokens[id];
CharacterCosmetics memory cc = cosmetics[id];
return (c.xp, c.level, c.trait, c.staminaTimestamp,
getRandomCosmetic(cc.seed, 1, 13), // head
getRandomCosmetic(cc.seed, 2, 45), // arms
getRandomCosmetic(cc.seed, 3, 61), // torso
getRandomCosmetic(cc.seed, 4, 41), // legs
getRandomCosmetic(cc.seed, 5, 22), // boots
getRandomCosmetic(cc.seed, 6, 2) // race
);
}
function getRandomCosmetic(uint256 seed, uint256 seed2, uint16 limit) private pure returns (uint16) {
return uint16(RandomUtil.randomSeededMinMax(0, limit, RandomUtil.combineSeeds(seed, seed2)));
}
function getCosmeticsSeed(uint256 id) public view noFreshLookup(id) returns (uint256) {
CharacterCosmetics memory cc = cosmetics[id];
return cc.seed;
}
function getSoulForBurns(uint256[] calldata burnIds) external view returns (uint256) {
uint256 soulAmount = 0;
for(uint i = 0; i < burnIds.length; i++) {
soulAmount += getTotalPower(burnIds[i]).div(10);
}
return soulAmount;
}
function mint(address minter, uint256 seed) public restricted {
uint256 tokenID = tokens.length;
if(block.number != lastMintedBlock)
firstMintedOfLastBlock = tokenID;
lastMintedBlock = block.number;
uint16 xp = 0;
uint8 level = 0; // 1
uint8 trait = uint8(RandomUtil.randomSeededMinMax(0,3,seed));
uint64 staminaTimestamp = uint64(now.sub(getStaminaMaxWait()));
tokens.push(Character(xp, level, trait, staminaTimestamp));
cosmetics.push(CharacterCosmetics(0, RandomUtil.combineSeeds(seed, 1)));
address receiver = minter;
if(minter != address(0) && minter != address(0x000000000000000000000000000000000000dEaD) && !hasRole(NO_OWNED_LIMIT, minter) && balanceOf(minter) >= characterLimit) {
receiver = address(garrison);
garrison.redirectToGarrison(minter, tokenID);
_mint(address(garrison), tokenID);
}
else {
_mint(minter, tokenID);
}
emit NewCharacter(tokenID, receiver);
}
function customMint(address minter, uint16 xp, uint8 level, uint8 trait, uint256 seed, uint256 tokenID) minterOnly public returns (uint256) {
uint64 staminaTimestamp = uint64(now); // 0 on purpose to avoid chain jumping abuse
if(tokenID == 0){
tokenID = tokens.length;
if(block.number != lastMintedBlock)
firstMintedOfLastBlock = tokenID;
lastMintedBlock = block.number;
tokens.push(Character(xp, level, trait, staminaTimestamp));
cosmetics.push(CharacterCosmetics(0, RandomUtil.combineSeeds(seed, 1)));
address receiver = minter;
if(minter != address(0) && minter != address(0x000000000000000000000000000000000000dEaD) && !hasRole(NO_OWNED_LIMIT, minter) && balanceOf(minter) >= characterLimit) {
receiver = address(garrison);
garrison.redirectToGarrison(minter, tokenID);
_mint(address(garrison), tokenID);
}
else {
_mint(minter, tokenID);
}
emit NewCharacter(tokenID, receiver);
}
else {
Character storage ch = tokens[tokenID];
ch.xp = xp;
ch.level = level;
ch.trait = trait;
ch.staminaTimestamp = staminaTimestamp;
CharacterCosmetics storage cc = cosmetics[tokenID];
cc.seed = seed;
}
return tokenID;
}
function burnIntoCharacter(uint256[] calldata burnIds, uint256 targetCharId, uint256 burnPowerMultiplier) external restricted {
uint256 burnPower = 0;
for(uint i = 0; i < burnIds.length; i++) {
burnPower += nftVars[burnIds[i]][NFTVAR_BONUS_POWER].add(getPowerAtLevel(tokens[burnIds[i]].level));
address burnOwner = ownerOf(burnIds[i]);
if(burnOwner == address(garrison)) {
burnOwner = garrison.characterOwner(burnIds[i]);
garrison.updateOnBurn(burnOwner, burnIds[i]);
}
_burn(burnIds[i]);
emit Burned(
burnOwner,
burnIds[i]
);
}
require(uint(4).mul(getPowerAtLevel(tokens[targetCharId].level)) >= getTotalPower(targetCharId).add(burnPower), "Power limit");
nftVars[targetCharId][NFTVAR_BONUS_POWER] = burnPower.mul(burnPowerMultiplier).div(1e18).add(nftVars[targetCharId][NFTVAR_BONUS_POWER]);
}
function burnIntoSoul(uint256[] calldata burnIds) external restricted {
for(uint i = 0; i < burnIds.length; i++) {
address burnOwner = ownerOf(burnIds[i]);
if(burnOwner == address(garrison)) {
burnOwner = garrison.characterOwner(burnIds[i]);
garrison.updateOnBurn(burnOwner, burnIds[i]);
}
_burn(burnIds[i]);
emit Burned(
burnOwner,
burnIds[i]
);
}
}
function upgradeWithSoul(uint256 targetCharId, uint256 soulAmount) external restricted {
uint256 burnPower = soulAmount.mul(10);
require(uint(4).mul(getPowerAtLevel(tokens[targetCharId].level)) >= getTotalPower(targetCharId).add(burnPower), "Power limit");
nftVars[targetCharId][NFTVAR_BONUS_POWER] = burnPower.add(nftVars[targetCharId][NFTVAR_BONUS_POWER]);
}
function getLevel(uint256 id) public view noFreshLookup(id) returns (uint8) {
return tokens[id].level; // this is used by dataminers and it benefits us
}
function getRequiredXpForNextLevel(uint8 currentLevel) public view returns (uint16) {
return uint16(experienceTable[currentLevel]); // this is helpful to users as the array is private
}
function getPower(uint256 id) public view noFreshLookup(id) returns (uint24) {
return getPowerAtLevel(tokens[id].level);
}
function getTotalPower(uint256 id) public view noFreshLookup(id) returns (uint256) {
return nftVars[id][NFTVAR_BONUS_POWER].add(getPowerAtLevel(tokens[id].level));
}
function getPowerAtLevel(uint8 level) public pure returns (uint24) {
// does not use fixed points since the numbers are simple
// the breakpoints every 10 levels are floored as expected
// level starts at 0 (visually 1)
// 1000 at lvl 1
// 9000 at lvl 51 (~3months)
// 22440 at lvl 105 (~3 years)
// 92300 at lvl 255 (heat death of the universe)
return uint24(
uint256(1000)
.add(level.mul(10))
.mul(level.div(10).add(1))
);
}
function getTrait(uint256 id) public view noFreshLookup(id) returns (uint8) {
return tokens[id].trait;
}
function setTrait(uint256 id, uint8 trait) public restricted {
tokens[id].trait = trait;
}
function getXp(uint256 id) public view noFreshLookup(id) returns (uint32) {
return tokens[id].xp;
}
function gainXp(uint256 id, uint16 xp) public restricted {
_gainXp(id, xp);
}
function _gainXp(uint256 id, uint256 xp) internal {
Character storage char = tokens[id];
if (char.level < 255) {
uint newXp = char.xp.add(xp);
uint requiredToLevel = experienceTable[char.level]; // technically next level
while (newXp >= requiredToLevel) {
newXp = newXp - requiredToLevel;
char.level += 1;
emit LevelUp(ownerOf(id), id, char.level);
if (char.level < 255)
requiredToLevel = experienceTable[char.level];
else newXp = 0;
}
char.xp = uint16(newXp);
}
}
function gainXpAll(uint256[] calldata chars, uint256[] calldata xps) external restricted {
for(uint i = 0; i < chars.length; i++)
_gainXp(chars[i], xps[i]);
}
function getStaminaTimestamp(uint256 id) public view noFreshLookup(id) returns (uint64) {
return tokens[id].staminaTimestamp;
}
function setStaminaTimestamp(uint256 id, uint64 timestamp) public restricted {
tokens[id].staminaTimestamp = timestamp;
}
function getStaminaPoints(uint256 id) public view noFreshLookup(id) returns (uint8) {
return getStaminaPointsFromTimestamp(tokens[id].staminaTimestamp);
}
function getStaminaPointsFromTimestamp(uint64 timestamp) public view returns (uint8) {
if(timestamp > now)
return 0;
uint256 points = (now - timestamp) / secondsPerStamina;
if(points > maxStamina) {
points = maxStamina;
}
return uint8(points);
}
function isStaminaFull(uint256 id) public view noFreshLookup(id) returns (bool) {
return getStaminaPoints(id) >= maxStamina;
}
function getStaminaMaxWait() public pure returns (uint64) {
return uint64(maxStamina * secondsPerStamina);
}
function getFightDataAndDrainStamina(address fighter,
uint256 id, uint8 amount, bool allowNegativeStamina, uint256 busyFlag) public restricted returns(uint96) {
require(fighter == ownerOf(id) && nftVars[id][NFTVAR_BUSY] == 0);
nftVars[id][NFTVAR_BUSY] |= busyFlag;
Character storage char = tokens[id];
uint8 staminaPoints = getStaminaPointsFromTimestamp(char.staminaTimestamp);
require((staminaPoints > 0 && allowNegativeStamina) // we allow going into negative, but not starting negative
|| staminaPoints >= amount, "Not enough stamina!");
uint64 drainTime = uint64(amount * secondsPerStamina);
uint64 preTimestamp = char.staminaTimestamp;
if(staminaPoints >= maxStamina) { // if stamina full, we reset timestamp and drain from that
char.staminaTimestamp = uint64(now - getStaminaMaxWait() + drainTime);
}
else {
char.staminaTimestamp = uint64(char.staminaTimestamp + drainTime);
}
// bitwise magic to avoid stacking limitations later on
return uint96(char.trait | (uint24(getTotalPower(id)) << 8) | (preTimestamp << 32));
}
function processRaidParticipation(uint256 id, bool won, uint16 xp) public restricted {
raidsDone[id] = raidsDone[id] + 1;
raidsWon[id] = won ? (raidsWon[id] + 1) : (raidsWon[id]);
require(nftVars[id][NFTVAR_BUSY] == 0); // raids do not apply busy flag for now
//nftVars[id][NFTVAR_BUSY] = 0;
_gainXp(id, xp);
}
function getCharactersOwnedBy(address wallet) public view returns(uint256[] memory chars) {
uint256 count = balanceOf(wallet);
chars = new uint256[](count);
for(uint256 i = 0; i < count; i++)
chars[i] = tokenOfOwnerByIndex(wallet, i);
}
function getReadyCharacters(address wallet) public view returns(uint256[] memory chars) {
uint256[] memory owned = getCharactersOwnedBy(wallet);
uint256 ready = 0;
for(uint i = 0; i < owned.length; i++)
if(nftVars[owned[i]][NFTVAR_BUSY] == 0)
ready++;
chars = new uint[](ready);
for(uint i = 0; i < owned.length; i++)
if(nftVars[owned[i]][NFTVAR_BUSY] == 0)
chars[--ready] = owned[i];
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override {
require(nftVars[tokenId][NFTVAR_BUSY] == 0);
promos.setBit(from, promos.BIT_FIRST_CHARACTER());
promos.setBit(to, promos.BIT_FIRST_CHARACTER());
}
function safeTransferFrom(address from, address to, uint256 tokenId) override public {
if(to != address(0) && to != address(0x000000000000000000000000000000000000dEaD) && !hasRole(NO_OWNED_LIMIT, to) && balanceOf(to) >= characterLimit) {
garrison.redirectToGarrison(to, tokenId);
super.safeTransferFrom(from, address(garrison), tokenId);
}
else {
super.safeTransferFrom(from, to, tokenId);
}
}
function setCharacterLimit(uint256 max) public restricted {
characterLimit = max;
}
function getNftVar(uint256 characterID, uint256 nftVar) public view returns(uint256) {
return nftVars[characterID][nftVar];
}
function setNftVar(uint256 characterID, uint256 nftVar, uint256 value) public restricted {
nftVars[characterID][nftVar] = value;
}
}pragma solidity ^0.6.0;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "abdk-libraries-solidity/ABDKMath64x64.sol";
contract Promos is Initializable, AccessControlUpgradeable {
using ABDKMath64x64 for int128;
bytes32 public constant GAME_ADMIN = keccak256("GAME_ADMIN");
function initialize() public initializer {
__AccessControl_init();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function migrateTo_f73df27() external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
firstCharacterPromoInGameOnlyFundsGivenInUsd = ABDKMath64x64.divu(
17220,
100
);
}
mapping(address => uint256) public bits;
uint256 public constant BIT_FIRST_CHARACTER = 1;
uint256 public constant BIT_FOUNDER_SHIELD = 2;
uint256 public constant BIT_BAD_ACTOR = 4;
uint256 public constant BIT_LEGENDARY_DEFENDER = 8;
int128 public firstCharacterPromoInGameOnlyFundsGivenInUsd;
modifier restricted() {
require(hasRole(GAME_ADMIN, msg.sender), "Not game admin");
_;
}
function setBit(address user, uint256 bit) external restricted {
bits[user] |= bit;
}
function setBits(address[] memory user, uint256 bit) public restricted {
for(uint i = 0; i < user.length; i++)
bits[user[i]] |= bit;
}
function unsetBit(address user, uint256 bit) public restricted {
bits[user] &= ~bit;
}
function unsetBits(address[] memory user, uint256 bit) public restricted {
for(uint i = 0; i < user.length; i++)
bits[user[i]] &= ~bit;
}
function getBit(address user, uint256 bit) external view returns (bool) {
return (bits[user] & bit) == bit;
}
function firstCharacterPromoInGameOnlyFundsGivenInUsdAsCents() external view returns (uint256) {
return firstCharacterPromoInGameOnlyFundsGivenInUsd.mulu(100);
}
function setFirstCharacterPromoInGameOnlyFundsGivenInUsdAsCents(
uint256 _usdCents
) external restricted {
firstCharacterPromoInGameOnlyFundsGivenInUsd = ABDKMath64x64.divu(
_usdCents,
100
);
}
function setFirstCharacterPromoInGameOnlyFundsGivenInUsdAsRational(
uint256 _numerator,
uint256 _denominator
) external restricted {
firstCharacterPromoInGameOnlyFundsGivenInUsd = ABDKMath64x64.divu(
_numerator,
_denominator
);
}
}pragma solidity ^0.6.2;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/EnumerableSet.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "./characters.sol";
import "./cryptoblades.sol";
contract Garrison is Initializable, IERC721ReceiverUpgradeable, AccessControlUpgradeable {
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableSet for EnumerableSet.AddressSet;
bytes32 public constant GAME_ADMIN = keccak256("GAME_ADMIN");
// STATE
Characters characters;
EnumerableSet.AddressSet private supportedTokenTypes;
mapping(address => EnumerableSet.UintSet) userGarrison;
mapping(uint256 => address) public characterOwner;
EnumerableSet.UintSet private allCharactersInGarrison;
CryptoBlades game;
event CharacterReceived(uint256 indexed character, address indexed minter);
function initialize(Characters _characters)
public
initializer
{
__AccessControl_init();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
characters = _characters;
}
function migrateTo_d514745(CryptoBlades _game) external restricted {
game = _game;
}
// MODIFIERS
modifier restricted() {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || hasRole(GAME_ADMIN, msg.sender), "Not admin");
_;
}
modifier isCharacterOwner(uint256 id) {
require(characterOwner[id] == msg.sender);
_;
}
modifier isInGarrison(uint256 id) {
require(allCharactersInGarrison.contains(id));
_;
}
modifier isCharactersOwner(uint256[] memory ids) {
_isCharactersOwner(ids);
_;
}
function _isCharactersOwner(uint256[] memory ids) internal view {
for(uint i = 0; i < ids.length; i++) {
require(characterOwner[ids[i]] == msg.sender, 'Not owner');
}
}
// VIEWS
function getUserCharacters() public view returns (uint256[] memory tokens) {
uint256 amount = balanceOf(msg.sender);
tokens = new uint256[](amount);
EnumerableSet.UintSet storage storedTokens = userGarrison[msg.sender];
for (uint256 i = 0; i < amount; i++) {
uint256 id = storedTokens.at(i);
tokens[i] = id;
}
}
function balanceOf(address user) public view returns(uint256) {
return userGarrison[user].length();
}
// MUTATIVE
function sendToGarrison(uint256 id) public {
characterOwner[id] = msg.sender;
userGarrison[msg.sender].add(id);
allCharactersInGarrison.add(id);
characters.safeTransferFrom(msg.sender, address(this), id);
emit CharacterReceived(id, msg.sender);
}
function redirectToGarrison(address user, uint256 id) restricted external {
characterOwner[id] = user;
userGarrison[user].add(id);
allCharactersInGarrison.add(id);
emit CharacterReceived(id, user);
}
function restoreFromGarrison(uint256 id)
public
isCharacterOwner(id)
isInGarrison(id)
{
require(characters.balanceOf(msg.sender) < characters.characterLimit(), "Receiver has too many characters");
delete characterOwner[id];
userGarrison[msg.sender].remove(id);
allCharactersInGarrison.remove(id);
characters.safeTransferFrom(address(this), msg.sender, id);
}
function swapWithGarrison(uint256 plazaId, uint256 garrisonId) external {
sendToGarrison(plazaId);
restoreFromGarrison(garrisonId);
}
function claimAllXp(uint256[] calldata chars) external isCharactersOwner(chars) {
uint256[] memory xps = game.getXpRewards(chars);
game.resetXp(chars);
characters.gainXpAll(chars, xps);
}
function updateOnBurn(address playerAddress, uint256 burnedId) external restricted {
delete characterOwner[burnedId];
userGarrison[playerAddress].remove(burnedId);
allCharactersInGarrison.remove(burnedId);
}
function allowToken(IERC721 _tokenAddress) public restricted {
supportedTokenTypes.add(address(_tokenAddress));
}
function disallowToken(IERC721 _tokenAddress) public restricted {
supportedTokenTypes.remove(address(_tokenAddress));
}
function onERC721Received(
address, /* operator */
address, /* from */
uint256 _id,
bytes calldata /* data */
) external override returns (bytes4) {
// NOTE: The contract address is always the message sender.
address _tokenAddress = msg.sender;
require(
supportedTokenTypes.contains(_tokenAddress) &&
allCharactersInGarrison.contains(_id),
"Token ID not listed"
);
return IERC721ReceiverUpgradeable.onERC721Received.selector;
}
}pragma solidity ^0.6.5;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
contract Cosmetics is Initializable, AccessControlUpgradeable {
bytes32 public constant GAME_ADMIN = keccak256("GAME_ADMIN");
event CosmeticGiven(address indexed owner, uint32 cosmetic, uint32 amount);
event CosmeticUsed(address indexed owner, uint32 cosmetic, uint32 amount);
event CosmeticRestored(address indexed owner, uint32 cosmetic, uint32 amount);
event CosmeticGivenByAdmin(address indexed owner, uint32 cosmetic, uint32 amount);
event CosmeticTakenByAdmin(address indexed owner, uint32 cosmetic, uint32 amount);
mapping(address => mapping(uint32 => uint32)) public owned;
mapping(uint32 => bool) internal _cosmeticAvailable;
uint32 internal constant _noCosmetic = 0;
function initialize()
public
initializer
{
__AccessControl_init_unchained();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
modifier isAdmin() {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
_;
}
modifier cosmeticAvailable(uint32 cosmetic) {
require(_cosmeticAvailable[cosmetic], "Not available");
_;
}
modifier restricted() {
_restricted();
_;
}
function _restricted() internal view {
require(hasRole(GAME_ADMIN, msg.sender), "Not game admin");
}
modifier haveCosmetic(uint32 cosmetic, uint32 amount) {
require(owned[msg.sender][cosmetic] >= amount, "No cosmetic");
_;
}
function giveCosmetic(address buyer, uint32 cosmetic, uint32 amount) public restricted {
owned[buyer][cosmetic] += amount;
emit CosmeticGiven(buyer, cosmetic, amount);
}
function useCosmetic(uint32 cosmetic, uint32 amount) internal haveCosmetic(cosmetic, amount) cosmeticAvailable(cosmetic) {
owned[msg.sender][cosmetic] -= amount;
emit CosmeticUsed(msg.sender, cosmetic, amount);
}
function _restoreCosmetic(uint32 cosmetic, uint32 amount) internal {
owned[msg.sender][cosmetic] += amount;
emit CosmeticRestored(msg.sender, cosmetic, amount);
}
function getCosmeticCount(uint32 cosmetic) public view returns(uint32) {
return owned[msg.sender][cosmetic];
}
function isCosmeticAvailable(uint32 cosmetic) public view returns (bool){
return _cosmeticAvailable[cosmetic];
}
function toggleCosmeticAvailable(uint32 cosmetic, bool available) external isAdmin {
_cosmeticAvailable[cosmetic] = available;
}
function giveCosmeticByAdmin(address receiver, uint32 cosmetic, uint32 amount) external isAdmin cosmeticAvailable(cosmetic) {
owned[receiver][cosmetic] += amount;
emit CosmeticGivenByAdmin(receiver, cosmetic, amount);
}
function takeCosmeticByAdmin(address target, uint32 cosmetic, uint32 amount) external isAdmin {
require(owned[target][cosmetic] >= amount, 'Not enough cosmetic');
owned[target][cosmetic] -= amount;
emit CosmeticTakenByAdmin(target, cosmetic, amount);
}
}pragma solidity ^0.6.5;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
contract Consumables is Initializable, AccessControlUpgradeable {
bytes32 public constant GAME_ADMIN = keccak256("GAME_ADMIN");
event ConsumableGiven(address indexed owner, uint32 amount);
mapping(address => uint32) public owned;
bool internal _enabled;
function initialize()
public
initializer
{
__AccessControl_init_unchained();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_enabled = true;
}
modifier isAdmin() {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
_;
}
modifier itemNotDisabled() {
require(_enabled, "Item disabled");
_;
}
modifier restricted() {
_restricted();
_;
}
function _restricted() internal view {
require(hasRole(GAME_ADMIN, msg.sender), "Not game admin");
}
modifier haveItem(uint32 amount) {
require(owned[msg.sender] >= amount, "No item");
_;
}
function giveItem(address buyer, uint32 amount) public restricted {
owned[buyer] += amount;
emit ConsumableGiven(buyer, amount);
}
function consumeItem(uint32 amount) internal haveItem(amount) itemNotDisabled {
owned[msg.sender] -= amount;
}
function getItemCount() public view returns (uint32) {
return owned[msg.sender];
}
function toggleItemCanUse(bool canUse) external isAdmin {
_enabled = canUse;
}
function giveItemByAdmin(address receiver, uint32 amount) external isAdmin {
owned[receiver] += amount;
}
function takeItemByAdmin(address target, uint32 amount) external isAdmin {
require(owned[target] >= amount, 'Not enough item');
owned[target] -= amount;
}
function itemEnabled() public view returns (bool){
return _enabled;
}
}pragma solidity ^0.6.5;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/utils/EnumerableSet.sol";
import "./CBKLand.sol";
contract CBKLandSale is Initializable, AccessControlUpgradeable {
using EnumerableSet for EnumerableSet.UintSet;
bytes32 public constant GAME_ADMIN = keccak256("GAME_ADMIN");
CBKLand public cbkLand;
/* ========== EVENTS ========== */
event T1Given(address indexed owner, uint256 stamp);
event T2Given(address indexed owner, uint256 chunkId);
event T3Given(address indexed owner, uint256 chunkId);
event T1GivenFree(address indexed owner, uint256 stamp);
event T2GivenFree(address indexed owner, uint256 chunkId);
event T3GivenFree(address indexed owner, uint256 chunkId);
event T1GivenReserved(address indexed reseller, address indexed owner, uint256 chunkId);
event T2GivenReserved(address indexed reseller, address indexed owner, uint256 chunkId);
event T3GivenReserved(address indexed reseller, address indexed owner, uint256 chunkId);
event LandTokenGiven(address indexed reseller, address indexed owner, uint256 tier);
event ReservedLandClaimed(uint256 indexed reservation, address indexed reseller, address indexed owner, uint256 tier, uint256 chunkId);
event MassMintReservedLand(address indexed player, address indexed reseller, uint256 chunkId, uint256 tier1, uint256 tier2, bool giveTier3);
event MassMintReservedChunklessLand(address indexed player, address indexed reseller, uint256 tier1, uint256 tier2, uint256 tier3);
event ReservedLandClaimedForPlayer(uint256 indexed reservation, address indexed reseller, address indexed owner, uint256 tier, uint256 chunkId);
/* ========== LAND SALE INFO ========== */
uint256 private constant NO_LAND = 0;
uint256 public constant TIER_ONE = 1;
uint256 public constant TIER_TWO = 2;
uint256 public constant TIER_THREE = 3;
uint256 private constant MAX_CHUNK_ID = 9999; // 100 x 100
struct purchaseInfo {
address buyer;
uint256 purchasedTier;
uint256 stamp; // chunkId or roundrobin stamp
bool free;
}
uint256 private totalSales;
mapping(uint256 => purchaseInfo) public sales; // Put all sales in an mapping for easier tracking
mapping(address => purchaseInfo) public purchaseAddressMapping;
mapping(uint256 => uint256) public availableLand; // Land that is up for sale.
mapping(uint256 => uint256) public chunkZoneLandSales;
/* ========== T1 LAND SALE INFO ========== */
// T1 land is sold with no exact coordinates commitment and assigned based on round robin
// once minting is done. For now the player gets a stamp which can reflect PROJECTED land coordinates
// should it need be.
uint256 private t1LandsSold;
/* ========== T2 LAND SALE INFO ========== */
uint256 private t2LandsSold;
uint256 private chunksWithT2Land;
uint256 private _allowedLandSalePerChunk;
uint256 private _allowedLandOffset; // Max allowed deviation allowed from theoretical average
// T2 sold per chunk
mapping(uint256 => uint256) public chunkT2LandSales;
/* ========== T3 LAND SALE INFO ========== */
uint256 private t3LandsSold;
mapping(uint256 => address) public chunkT3LandSoldTo;
/* ========== RESERVED CHUNKS SALE INFO ========== */
EnumerableSet.UintSet private reservedChunkIds;
bool internal _enabled;
bool internal _reservedEnabled;
mapping(address => EnumerableSet.UintSet) private reservedChunks;
mapping(address => uint256) private reservedChunksCounter;
mapping(uint256 => address) private chunksReservedFor;
// reseller address => land tier => budget
mapping(address => mapping(uint256 => uint256)) private resellerLandBudget;
// player reserved land
uint256 private playerReservedLandAt;
mapping(address => EnumerableSet.UintSet) private playerReservedLands;
mapping(uint256 => uint256) private playerReservedLandTier;
mapping(uint256 => address) private playerReservedLandReseller;
mapping(uint256 => address) private playerReservedLandForPlayer;
mapping(uint256 => bool) private playerReservedLandClaimed;
EnumerableSet.UintSet private takenT3Chunks;
function initialize(CBKLand _cbkLand)
public
initializer
{
__AccessControl_init_unchained();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_enabled = false;
_allowedLandOffset = 2;
_allowedLandSalePerChunk = 99; // At least 1 reserved for T3
availableLand[TIER_ONE] = 1000; // Placeholder value
availableLand[TIER_TWO] = 100; // Placeholder value
availableLand[TIER_THREE] = 10; // Placeholder value
cbkLand = _cbkLand;
}
modifier isAdmin() {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
_;
}
modifier saleAllowed() {
require(_enabled, "Sales disabled");
_;
}
modifier reservedSaleAllowed() {
require(_reservedEnabled, "Sales disabled");
_;
}
modifier canPurchase(address buyer) {
require(purchaseAddressMapping[buyer].purchasedTier == 0, "Already purchased");
_;
}
modifier chunkAvailable(uint256 chunkId) {
require(chunkId <= MAX_CHUNK_ID, "Chunk not valid");
require(!reservedChunkIds.contains(chunkId), "Chunk reserved");
require(chunkT2LandSales[chunkId] < _allowedLandSalePerChunk, "Chunk not available");
require(_chunkAvailableForT2(chunkId), "Chunk overpopulated");
_;
}
// modifier reservedChunkAvailable(uint256 chunkId) {
// require(chunkId <= MAX_CHUNK_ID, "Chunk not valid");
// require(reservedChunks[msg.sender].contains(chunkId), "Chunk not reserved");
// require(chunkT2LandSales[chunkId] < _allowedLandSalePerChunk, "Chunk not available");
// _;
// }
// Will not overcomplicate the math on this one. Keeping it simple on purpose for gas cost.
// Limited to t2 because T3 not many and T1 round robins
function _chunkAvailableForT2(uint256 chunkId) internal view returns (bool) {
return chunksWithT2Land == 0 ||
(chunkT2LandSales[chunkId] + 1 < _allowedLandOffset + t2LandsSold / chunksWithT2Land);
}
modifier t3Available(uint256 chunkId) {
require(_chunkAvailableForT3(chunkId), "T3 not available");
_;
}
function _chunkAvailableForT3(uint256 chunkId) internal view returns (bool) {
return chunkT3LandSoldTo[chunkId] == address(0);
}
modifier tierAvailable(uint256 tier) {
require(availableLand[tier] > 0, "Tier not available");
_;
}
modifier restricted() {
_restricted();
_;
}
function _restricted() internal view {
require(hasRole(GAME_ADMIN, msg.sender), "Not game admin");
}
function giveT1Land(address buyer) public saleAllowed canPurchase(buyer) tierAvailable(TIER_ONE) restricted {
purchaseAddressMapping[buyer] = purchaseInfo(buyer, TIER_ONE, t1LandsSold, false);
sales[totalSales++] = purchaseAddressMapping[buyer];
emit T1Given(buyer, t1LandsSold);
t1LandsSold++;
availableLand[TIER_ONE]--;
cbkLand.mint(buyer, TIER_ONE, 0);
}
function giveT2Land(address buyer, uint256 chunkId) public saleAllowed canPurchase(buyer) tierAvailable(TIER_TWO) chunkAvailable(chunkId) restricted {
// First t2 sale
if(chunkT2LandSales[chunkId] == 0){
chunksWithT2Land++;
}
t2LandsSold++;
chunkT2LandSales[chunkId]++;
chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;
purchaseAddressMapping[buyer] = purchaseInfo(buyer, TIER_TWO, chunkId, false);
sales[totalSales++] = purchaseAddressMapping[buyer];
availableLand[TIER_TWO]--;
emit T2Given(buyer, chunkId);
cbkLand.mint(buyer, TIER_TWO, chunkId);
}
function giveT3Land(address buyer, uint256 chunkId) public saleAllowed canPurchase(buyer) tierAvailable(TIER_THREE) chunkAvailable(chunkId) t3Available(chunkId) restricted {
t3LandsSold++;
purchaseAddressMapping[buyer] = purchaseInfo(buyer, TIER_THREE, chunkId, false);
sales[totalSales++] = purchaseAddressMapping[buyer];
availableLand[TIER_THREE]--;
chunkT3LandSoldTo[chunkId] = buyer;
chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;
takenT3Chunks.add(chunkId);
emit T3Given(buyer, chunkId);
cbkLand.mint(buyer, TIER_THREE, chunkId);
}
function giveT1LandFree(address buyer) public tierAvailable(TIER_ONE) restricted {
purchaseAddressMapping[buyer] = purchaseInfo(buyer, TIER_ONE, t1LandsSold, true);
sales[totalSales++] = purchaseAddressMapping[buyer];
emit T1GivenFree(buyer, t1LandsSold);
t1LandsSold++;
availableLand[TIER_ONE]--;
cbkLand.mint(buyer, TIER_ONE, 0);
}
function giveT2LandFree(address buyer, uint256 chunkId) public tierAvailable(TIER_TWO) chunkAvailable(chunkId) restricted {
// First t2 sale
if(chunkT2LandSales[chunkId] == 0){
chunksWithT2Land++;
}
t2LandsSold++;
chunkT2LandSales[chunkId]++;
chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;
purchaseAddressMapping[buyer] = purchaseInfo(buyer, TIER_TWO, chunkId, true);
sales[totalSales++] = purchaseAddressMapping[buyer];
availableLand[TIER_TWO]--;
emit T2GivenFree(buyer, chunkId);
cbkLand.mint(buyer, TIER_TWO, chunkId);
}
function giveT3LandFree(address buyer, uint256 chunkId) public tierAvailable(TIER_THREE) chunkAvailable(chunkId) t3Available(chunkId) restricted {
t3LandsSold++;
purchaseAddressMapping[buyer] = purchaseInfo(buyer, TIER_THREE, chunkId, true);
sales[totalSales++] = purchaseAddressMapping[buyer];
availableLand[TIER_THREE]--;
chunkT3LandSoldTo[chunkId] = buyer;
chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;
takenT3Chunks.add(chunkId);
emit T3GivenFree(buyer, chunkId);
cbkLand.mint(buyer, TIER_THREE, chunkId);
}
// function giveLandToken(address buyer, uint256 tier) public reservedSaleAllowed() {
// require(resellerLandBudget[msg.sender][tier] > 0); // will protect against invalid tiers
// resellerLandBudget[msg.sender][tier]--;
// cbkLand.mintLandToken(msg.sender, buyer, tier);
// emit LandTokenGiven(msg.sender, buyer, tier);
// }
// function getResellerBudget(address reseller) public view returns (uint256 t1, uint256 t2, uint256 t3) {
// t1 = resellerLandBudget[reseller][TIER_ONE];
// t2 = resellerLandBudget[reseller][TIER_TWO];
// t3 = resellerLandBudget[reseller][TIER_THREE];
// }
// function setResellerBudget(address reseller, uint256 t1, uint256 t2, uint256 t3) public restricted {
// resellerLandBudget[reseller][TIER_ONE] = t1;
// resellerLandBudget[reseller][TIER_TWO] = t2;
// resellerLandBudget[reseller][TIER_THREE] = t3;
// }
// Solidity hates polymorphism for this particular function
function giveT1LandReservedBulk(address[] memory players, address reseller) public restricted {
for (uint256 i = 0; i < players.length; i++) {
giveT1LandReserved(players[i], reseller);
}
}
function giveT1LandReserved(address player, address reseller) public restricted {
uint256 rcLength = reservedChunks[reseller].length();
require(rcLength > 0, "no reserved chunks");
uint256 counter = reservedChunksCounter[reseller];
for(uint256 i = counter; i < counter + rcLength; i++){
uint256 cId = reservedChunks[reseller].at(uint256(i % rcLength));
// it's actually a T1, but we will play on the T2 because population is shared
if(chunkT2LandSales[cId] < _allowedLandSalePerChunk) {
if(chunkT2LandSales[cId] == 0){
chunksWithT2Land++;
}
chunkT2LandSales[cId]++;
chunkZoneLandSales[chunkIdToZoneId(cId)]++;
cbkLand.mint(player, TIER_ONE, cId, reseller);
emit T1GivenReserved(reseller, player, cId);
reservedChunksCounter[reseller] = uint256(i + 1);
return;
}
}
// Could not find a land
revert();
}
// For mass mint
function massMintReservedLand(address player, address reseller, uint256 chunkId, uint256 tier1, uint256 tier2, bool giveTier3) public restricted {
require(reservedChunks[reseller].contains(chunkId), "not reserved");
require(chunkT2LandSales[chunkId] + tier1 + tier2 <= _allowedLandSalePerChunk, "NA");
require(!giveTier3 || _chunkAvailableForT3(chunkId), "NA2");
if((tier1 + tier2) > 0 && chunkT2LandSales[chunkId] == 0) {
chunksWithT2Land++;
}
if(tier1 > 0) {
cbkLand.massMint(player, TIER_ONE, chunkId, reseller, tier1);
chunkT2LandSales[chunkId] += tier1;
}
if(tier2 > 0) {
cbkLand.massMint(player, TIER_TWO, chunkId, reseller, tier2);
chunkT2LandSales[chunkId] += tier2;
}
if(giveTier3) {
chunkT3LandSoldTo[chunkId] = player;
takenT3Chunks.add(chunkId);
cbkLand.mint(player, TIER_THREE, chunkId, reseller);
}
chunkZoneLandSales[chunkIdToZoneId(chunkId)] += tier1 + tier2 + (giveTier3 ? 1 : 0);
emit MassMintReservedLand(player, reseller, chunkId, tier1, tier2, giveTier3);
}
// For mass mint
// Can change chunk id once
// Call with caution
function massMintReservedLand(address player, address reseller, uint256 tier1, uint256 tier2, uint256 tier3) public restricted {
require(reservedChunks[reseller].length() > 0, "no reservation");
if(tier1 > 0) {
cbkLand.massMint(player, TIER_ONE, 0, reseller, tier1);
}
if(tier2 > 0) {
cbkLand.massMint(player, TIER_TWO, 0, reseller, tier2);
}
if(tier3 > 0) {
cbkLand.massMint(player, TIER_THREE, 0, reseller, tier3);
}
emit MassMintReservedChunklessLand(player, reseller, tier1, tier2, tier3);
}
// Can be called only from land with reseller address and chunkid 0
// For T1; assignedChunkId is a dummy param
function changeLandChunkId(uint256 landId, uint256 assignedChunkid) public {
require(cbkLand.ownerOf(landId) == msg.sender, "NA1"); // Owns the land
(uint256 tier, uint256 chunkId, , , address reseller) = cbkLand.get(landId);
require(chunkId == 0 && reseller != address(0), "NA2"); // Is a reseller land with chunk id 0
require(assignedChunkid == 0 || reservedChunks[reseller].contains(assignedChunkid), "NA3"); // FE didn't send chunkId or reseller owns the assigned chunkId
require(tier != TIER_THREE || _chunkAvailableForT3(assignedChunkid), "NA4"); // Not tier 3 or tier 3 available
require((tier == TIER_ONE && assignedChunkid == 0) || assignedChunkid > 0, "NA5"); // tier 1 or chunkid requested
require(tier != TIER_TWO || chunkT2LandSales[assignedChunkid] < _allowedLandSalePerChunk, "NA6"); // Not T2 or population allows it
// T1 => get random reseller chunkId
if(tier == TIER_ONE) {
uint256 rcLength = reservedChunks[reseller].length();
require(rcLength > 0, "no reserved chunks");
uint256 counter = reservedChunksCounter[reseller];
for(uint256 i = counter; i < counter + rcLength; i++) {
uint256 cId = reservedChunks[reseller].at(uint256(i % rcLength));
if(chunkT2LandSales[cId] < _allowedLandSalePerChunk) {
assignedChunkid = cId;
reservedChunksCounter[reseller] = uint256(i + 1);
break;
}
}
}
require(assignedChunkid != 0, "NA7"); // Would only happen if T1 && round robin failed; shouldn't
// T1 and T2 share population
if(tier != TIER_THREE) {
if(chunkT2LandSales[assignedChunkid] == 0){
chunksWithT2Land++;
}
chunkT2LandSales[assignedChunkid]++;
}
// T3 => tag the land
if(tier == TIER_THREE) {
chunkT3LandSoldTo[assignedChunkid] = msg.sender;
takenT3Chunks.add(assignedChunkid);
}
chunkZoneLandSales[chunkIdToZoneId(assignedChunkid)]++;
cbkLand.updateChunkId(landId, assignedChunkid);
}
function giveT1LandReservedBulk(address[] memory players, address reseller, uint256 chunkId) public restricted {
require(reservedChunks[reseller].contains(chunkId), "not reserved");
for (uint256 i = 0; i < players.length; i++) {
giveT1LandReserved(players[i], reseller, chunkId);
}
}
function giveT1LandReserved(address player, address reseller, uint256 chunkId) public restricted {
require(reservedChunks[reseller].contains(chunkId), "not reserved");
if(chunkT2LandSales[chunkId] < _allowedLandSalePerChunk) {
if(chunkT2LandSales[chunkId] == 0){
chunksWithT2Land++;
}
chunkT2LandSales[chunkId]++;
chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;
cbkLand.mint(player, TIER_ONE, chunkId, reseller);
emit T1GivenReserved(reseller, player, chunkId);
return;
}
// Could not find a land
revert();
}
// function giveT2LandReserved(address buyer, uint256 chunkId) public reservedChunkAvailable(chunkId) reservedSaleAllowed() {
// if(chunkT2LandSales[chunkId] == 0){
// chunksWithT2Land++;
// }
// chunkT2LandSales[chunkId]++;
// chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;
// emit T2GivenReserved(msg.sender, buyer, chunkId);
// cbkLand.mint(buyer, TIER_TWO, chunkId);
// }
// function giveT3LandReserved(address buyer, uint256 chunkId) public reservedChunkAvailable(chunkId) t3Available(chunkId) reservedSaleAllowed() {
// chunkT3LandSoldTo[chunkId] = buyer;
// chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;
// emit T3GivenReserved(msg.sender, buyer, chunkId);
// cbkLand.mint(buyer, TIER_THREE, chunkId);
// }
// Will leave this commented
// function setLandURI(uint256 landId, string memory uri) public {
// (, uint256 chunkId,,) = cbkLand.get(landId);
// require(reservedChunks[msg.sender].contains(chunkId), "no access");
// cbkLand.setURI(landId, uri);
// }
function chunkIdToZoneId(uint256 chunkId) internal pure returns (uint256){
return 10 * (chunkId / 1000) + (chunkId % 100) / 10;
}
function salesAllowed() public view returns (bool){
return _enabled;
}
function reservedSalesAllowed() public view returns (bool){
return _reservedEnabled;
}
function getAllowedLandOffset() public view returns (uint256){
return _allowedLandOffset;
}
function checkIfChunkAvailable(uint256 tier, uint256 chunkId) public view returns (bool){
if(reservedChunkIds.contains(chunkId)){
return false;
}
if(chunkId > MAX_CHUNK_ID){
return false;
}
if(tier == TIER_ONE){
return availableLand[TIER_ONE] > 0;
}
if(tier == TIER_TWO){
return availableLand[TIER_TWO] > 0
&& chunkT2LandSales[TIER_TWO] < _allowedLandSalePerChunk
&& _chunkAvailableForT2(chunkId);
}
if(tier == TIER_THREE){
return availableLand[TIER_THREE] > 0
&& _chunkAvailableForT3(chunkId);
}
return false;
}
function checkChunkReserved(uint256 chunkId) public view returns (bool){
return reservedChunkIds.contains(chunkId);
}
function getAllZonesPopulation() public view returns (uint256[] memory) {
uint256[] memory toReturn = new uint256[](100);
for (uint256 i = 0; i < 100; i++) {
toReturn[i] = chunkZoneLandSales[i];
}
return toReturn;
}
function getZonePopulation(uint256[] memory zoneIds) public view returns (uint256[] memory) {
require(zoneIds.length > 0 && zoneIds.length <= 100, "invalid request");
uint256[] memory toReturn = new uint256[](zoneIds.length);
for (uint256 i = 0; i < zoneIds.length; i++) {
toReturn[i] = chunkZoneLandSales[zoneIds[i]];
}
return toReturn;
}
function getZoneChunkPopulation(uint256 zoneId) public view returns (uint256[] memory) {
require(zoneId < 100, "invalid request");
uint256 zoneX = zoneId % 10;
uint256 zoneY = zoneId / 10;
uint256[] memory toReturn = new uint256[](100);
uint256 counter = 0;
for(uint256 j = zoneY * 1000; j < zoneY * 1000 + 1000; j += 100) {
for(uint256 i = zoneX * 10; i < zoneX * 10 + 10; i++) {
uint256 projectedId = i + j;
toReturn[counter++] = chunkT2LandSales[projectedId] + (chunkT3LandSoldTo[projectedId] != address(0) ? 1 : 0);
}
}
return toReturn;
}
function getChunkPopulation(uint256[] memory chunkIds) public view returns (uint256[] memory) {
require(chunkIds.length > 0 && chunkIds.length <= 100, "invalid request");
uint256[] memory toReturn = new uint256[](chunkIds.length);
for (uint256 i = 0; i < chunkIds.length; i++) {
toReturn[i] = chunkT2LandSales[chunkIds[i]] + (chunkT3LandSoldTo[chunkIds[i]] != address(0) ? 1 : 0);
}
return toReturn;
}
function getAvailableLand() public view returns (uint256, uint256, uint256) {
return (availableLand[TIER_ONE], availableLand[TIER_TWO], availableLand[TIER_THREE]);
}
function getAvailableLandPerChunk() public view returns (uint256) {
return _allowedLandSalePerChunk;
}
function getSoldLand() public view returns (uint256, uint256, uint256) {
return (t1LandsSold, t2LandsSold, t3LandsSold);
}
function getPopulatedT2Chunks() public view returns (uint256) {
return chunksWithT2Land;
}
function getPurchase() public view returns (uint256, uint256) {
return (purchaseAddressMapping[msg.sender].purchasedTier, purchaseAddressMapping[msg.sender].stamp);
}
function getPurchaseOf(address owner) public view returns (uint256, uint256) {
return (purchaseAddressMapping[owner].purchasedTier, purchaseAddressMapping[owner].stamp);
}
function getSalesCount() public view returns (uint256){
return totalSales;
}
function getPurchaseBySale(uint256 sale) public view returns (address, uint256, uint256) {
return (sales[sale].buyer, sales[sale].purchasedTier, sales[sale].stamp);
}
function setSaleAllowed(bool allowed) external isAdmin {
_enabled = allowed;
}
function setReservedSaleAllowed(bool allowed) external isAdmin {
_reservedEnabled = allowed;
}
// do NOT use this for reserve false unless really needed. This doesn't update reseller data
// the purpose of this function is to provide a cheap way to bulk reserve blocks that don't have resellers
function setChunksReservation(uint256[] calldata chunkIds, bool reserved) external isAdmin {
for (uint256 i = 0; i < chunkIds.length; i++) {
if(reserved && !reservedChunkIds.contains(chunkIds[i])) {
reservedChunkIds.add(chunkIds[i]);
}
if(!reserved) {
reservedChunkIds.remove(chunkIds[i]);
}
}
}
// be careful with forced, a chunkId may still remain attached to an existing reseller
// forced should not be used unless something is really wrong
function setChunksReservationInfo(uint256[] calldata chunkIds, address reserveFor, bool reserved, bool forced) external isAdmin {
for (uint256 i = 0; i < chunkIds.length; i++) {
require (chunkIds[i] != 0, "0 NA"); // chunk id 0 shouldn't be reserved
require(!reserved || (forced || chunksReservedFor[chunkIds[i]] == address(0)), "AS"); // already reserved, request has to be forced to avoid issues
if(reserved && !reservedChunkIds.contains(chunkIds[i])) {
reservedChunkIds.add(chunkIds[i]);
}
if(!reserved) {
reservedChunkIds.remove(chunkIds[i]);
chunksReservedFor[chunkIds[i]] = address(0);
reservedChunks[reserveFor].remove(chunkIds[i]);
}
if(reserved && !reservedChunks[reserveFor].contains(chunkIds[i])) {
reservedChunks[reserveFor].add(chunkIds[i]);
chunksReservedFor[chunkIds[i]] = reserveFor;
}
}
}
function givePlayersReservedLand(address[] calldata players, address reseller, uint256 tier) external isAdmin {
for (uint256 i = 0; i < players.length; i++) {
playerReservedLands[players[i]].add(++playerReservedLandAt);
playerReservedLandTier[playerReservedLandAt] = tier;
playerReservedLandReseller[playerReservedLandAt] = reseller;
playerReservedLandForPlayer[playerReservedLandAt] = players[i];
}
}
function getPlayerReservedLand(address player) public view returns(uint256[] memory t2Reservations, uint256[] memory t3Reservations) {
uint256 amount = playerReservedLands[player].length();
uint256 t2Count = 0;
uint256 t3Count = 0;
for (uint256 i = 0; i < amount; i++) {
uint256 reservation = playerReservedLands[player].at(i);
uint256 reservedTier = playerReservedLandTier[reservation];
if(reservedTier == 2) {
t2Count++;
}
else if(reservedTier == 3) {
t3Count++;
}
}
if(t2Count == 0 && t3Count == 0) {
return (new uint256[](0), new uint256[](0));
}
t2Reservations = new uint256[](t2Count);
t3Reservations = new uint256[](t3Count);
t2Count = 0;
t3Count = 0;
for (uint256 i = 0; i < amount; i++) {
uint256 reservation = playerReservedLands[player].at(i);
uint256 reservedTier = playerReservedLandTier[reservation];
if(reservedTier == 2) {
t2Reservations[t2Count++] = reservation;
}
else if(reservedTier == 3) {
t3Reservations[t3Count++] = reservation;
}
}
}
function getChunksOfReservations(uint256 reservationId) public view returns (uint256[] memory chunkIds) {
address reseller = playerReservedLandReseller[reservationId];
return getChunksOfReseller(reseller);
}
function getInfoOfReservation(uint256 reservationId) public view returns (address player, address reseller, uint256 tier, bool claimed) {
return (playerReservedLandForPlayer[reservationId], playerReservedLandReseller[reservationId], playerReservedLandTier[reservationId], playerReservedLandClaimed[reservationId]);
}
function getChunksOfReseller(address reservedFor) public view returns (uint256[] memory chunkIds) {
uint256 amount = reservedChunks[reservedFor].length();
chunkIds = new uint256[](amount);
uint256 index = 0;
for (uint256 i = 0; i < amount; i++) {
uint256 id = reservedChunks[reservedFor].at(i);
chunkIds[index++] = id;
}
}
function claimPlayerReservedLand(uint256 reservation, uint256 chunkId, uint256 tier) public reservedSaleAllowed {
require(tier != 1, "NT1");
require(playerReservedLandClaimed[reservation] == false, "AC"); // already claimed
require(playerReservedLands[msg.sender].contains(reservation), "IR"); // invalid reservation
require(playerReservedLandTier[reservation] == tier, "IT"); // invalid tier
address reseller = playerReservedLandReseller[reservation];
require(reservedChunks[reseller].contains(chunkId), "IR2"); // invalid reseller
if(tier == 3) {
require(_chunkAvailableForT3(chunkId), "T3 NA");
chunkT3LandSoldTo[chunkId] = msg.sender;
takenT3Chunks.add(chunkId);
}
if(tier == 2) {
require(chunkT2LandSales[chunkId] < _allowedLandSalePerChunk, "T2 NA");
if(chunkT2LandSales[chunkId] == 0){
chunksWithT2Land++;
}
chunkT2LandSales[chunkId]++;
}
chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;
playerReservedLands[msg.sender].remove(reservation);
playerReservedLandClaimed[reservation] = true;
cbkLand.mint(msg.sender, tier, chunkId, reseller);
emit ReservedLandClaimed(reservation, reseller, msg.sender, tier, chunkId);
}
function massClaimReservationsForPlayer(address player, uint256[] calldata reservations) external isAdmin {
for (uint256 i = 0; i < reservations.length; i++) {
uint256 reservation = reservations[i];
require(playerReservedLandClaimed[reservation] == false, "AC"); // already claimed
require(playerReservedLands[player].contains(reservation), "IR"); // invalid reservation
address reseller = playerReservedLandReseller[reservation];
uint256 rcLength = reservedChunks[reseller].length();
require(rcLength > 0, "no reserved chunks");
uint256 assignedChunkid = 0;
uint256 reservedTier = playerReservedLandTier[reservation];
require(reservedTier == TIER_TWO || reservedTier == TIER_THREE, "NA");
uint256 counter = reservedChunksCounter[reseller];
for(uint256 i = counter; i < counter + rcLength; i++) {
uint256 cId = reservedChunks[reseller].at(uint256(i % rcLength));
if(reservedTier == TIER_TWO) { // T2, find a spot with enough population
if(chunkT2LandSales[cId] < _allowedLandSalePerChunk) {
assignedChunkid = cId;
reservedChunksCounter[reseller] = uint256(i + 1);
break;
}
}
else if(!takenT3Chunks.contains(cId)) { // This is a T3, find a chunk that isn't claimed as T3
assignedChunkid = cId;
reservedChunksCounter[reseller] = uint256(i + 1);
break;
}
}
require(assignedChunkid != 0, "NA");
if(reservedTier == TIER_TWO) {
if(chunkT2LandSales[assignedChunkid] == 0){
chunksWithT2Land++;
}
chunkT2LandSales[assignedChunkid]++;
}
else {
chunkT3LandSoldTo[assignedChunkid] = player;
takenT3Chunks.add(assignedChunkid);
}
playerReservedLands[player].remove(reservation);
playerReservedLandClaimed[reservation] = true;
cbkLand.mint(player, reservedTier, assignedChunkid, reseller);
chunkZoneLandSales[chunkIdToZoneId(assignedChunkid)]++;
emit ReservedLandClaimedForPlayer(reservation, reseller, player, reservedTier, assignedChunkid);
}
}
function getResellerOfChunk(uint256 chunkId) public view returns (address reservedFor) {
reservedFor = chunksReservedFor[chunkId];
}
function getReservedChunksIds() public view returns (uint256[] memory chunkIds) {
uint256 amount = reservedChunkIds.length();
chunkIds = new uint256[](amount);
uint256 index = 0;
for (uint256 i = 0; i < amount; i++) {
uint256 id = reservedChunkIds.at(i);
chunkIds[index++] = id;
}
}
function getTakenT3Chunks() public view returns (uint256[] memory chunkIds) {
uint256 amount = takenT3Chunks.length();
chunkIds = new uint256[](amount);
uint256 index = 0;
for (uint256 i = 0; i < amount; i++) {
uint256 id = takenT3Chunks.at(i);
chunkIds[index++] = id;
}
}
function setAllowedLandOffset(uint256 allowedOffset) external isAdmin {
_allowedLandOffset = allowedOffset;
}
function setAllowedLandPerChunk(uint256 allowedLandSalePerChunk) external isAdmin {
_allowedLandSalePerChunk = allowedLandSalePerChunk;
}
function setAvailableLand(uint256 tier, uint256 available) external isAdmin {
require(tier >= TIER_ONE && tier <= TIER_THREE, "Invalid tier");
availableLand[tier] = available;
}
function getReservationAt() public view returns (uint256) {
return playerReservedLandAt;
}
// function updateResellerOfReservation(uint256[] calldata ids, address reseller) external isAdmin {
// for (uint256 i = 0; i < ids.length; i++) {
// playerReservedLandReseller[ids[i]] = reseller;
// }
// }
// Do not use forced unless really needed. If used, preferable don't update population
// Do NOT use with T3
// function updateLandChunkIdBulk(uint256[] calldata landIds, uint256 fromChunkId, uint256 toChunkId, bool updateToPopulation, bool forced) external isAdmin {
// require(forced || cbkLand.landsBelongToChunk(landIds, fromChunkId), "NA");
// if(updateToPopulation) {
// uint256 populationFrom = chunkT2LandSales[fromChunkId];
// uint256 populationTo = chunkT2LandSales[toChunkId];
// uint256 populationChange = landIds.length;
// if(populationFrom - populationChange < 0) {
// require(forced, "NA2"); // forced or don't allow. Something is wrong.
// populationChange = populationFrom; // can't have negative population
// }
// if(populationTo + populationChange > _allowedLandSalePerChunk) {
// require(forced, "NA3"); // forced or don't allow. Something is wrong. No reset on populationChange
// }
// chunkT2LandSales[fromChunkId] -= populationChange;
// chunkZoneLandSales[chunkIdToZoneId(fromChunkId)] -= populationChange;
// chunkT2LandSales[toChunkId] += populationChange;
// chunkZoneLandSales[chunkIdToZoneId(toChunkId)] += populationChange;
// }
// cbkLand.updateChunkId(landIds, toChunkId);
// }
}pragma solidity ^0.6.0;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "./util.sol";
contract CBKLand is Initializable, ERC721Upgradeable, AccessControlUpgradeable {
bytes32 public constant GAME_ADMIN = keccak256("GAME_ADMIN");
bytes32 public constant NO_OWNED_LIMIT = keccak256("NO_OWNED_LIMIT");
// Land specific
uint256 public constant LT = 0; // Land Tier
uint256 public constant LC = 1; // Land Chunk Id
uint256 public constant LX = 2; // Land Coordinate X
uint256 public constant LY = 3; // Land Coordinate Y
event LandMinted(address indexed minter, uint256 id, uint256 tier, uint256 chunkId);
event LandTransfered(address indexed from, address indexed to, uint256 id);
event LandTokenMinted(address indexed reseller, address indexed minter, uint256 id, uint256 tier);
event LandMintedWithReseller(address indexed minter, uint256 id, uint256 tier, uint256 chunkId, address reseller);
event LandChunkIdUpdated(uint256 indexed id, uint256 chunkId);
// TotalLand
uint256 landMinted;
// Avoiding structs for stats
mapping(uint256 => mapping(uint256 => uint256)) landData;
mapping(uint256 => mapping(uint256 => string)) landStrData;
uint256 public constant LBT = 0; // Land is a Token, it will have its chunkId updated later
mapping(uint256 => mapping(uint256 => bool)) landBoolData;
uint256 public constant LAR = 0; // Land Reseller, the one who minted the token
mapping(uint256 => mapping(uint256 => address)) landAddressData;
uint256 public constant TSU = 0; // URI of a tier. Will put this in land NFT because it kinda belongs here
mapping(uint256 => mapping(uint256 => string)) tierStrData;
function initialize () public initializer {
__ERC721_init("CryptoBladesKingdoms Land", "CBKL");
__AccessControl_init_unchained();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
modifier restricted() {
_restricted();
_;
}
function _restricted() internal view {
require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "NA");
}
// tier, chunkid, x, y, reseller address
function get(uint256 id) public view returns (uint256, uint256, uint256, uint256, address) {
return (landData[id][LT], landData[id][LC], landData[id][LX], landData[id][LY], landAddressData[id][LAR]);
}
function getOwned(address owner) public view returns (uint256[] memory ownedIds) {
uint256 ownedLandCount = balanceOf(owner);
ownedIds = new uint256[](ownedLandCount);
for(uint256 i = 0; i < ownedLandCount; i++) {
ownedIds[i] = tokenOfOwnerByIndex(owner, i);
}
}
function getLandReseller(uint256 land) public view returns (address) {
return landAddressData[land][LAR];
}
// DO NOT call directly outside the logic of CBKLandSale to avoid breaking tier and chunk logic
function mint(address minter, uint256 tier, uint256 chunkId) public restricted {
uint256 tokenID = landMinted++;
landData[tokenID][LT] = tier;
landData[tokenID][LC] = chunkId;
//landData[tokenID][LX] = x; // not yet
//landData[tokenID][LY] = y; // not yet
_mint(minter, tokenID);
emit LandMinted(minter, tokenID, tier, chunkId);
}
function mint(address minter, uint256 tier, uint256 chunkId, address reseller) public restricted {
uint256 tokenID = landMinted++;
landData[tokenID][LT] = tier;
landData[tokenID][LC] = chunkId;
//landData[tokenID][LX] = x; // not yet
//landData[tokenID][LY] = y; // not yet
landAddressData[tokenID][LAR] = reseller;
_mint(minter, tokenID);
emit LandMintedWithReseller(minter, tokenID, tier, chunkId, reseller);
}
function massMint(address minter, uint256 tier, uint256 chunkId, address reseller, uint256 quantity) public restricted {
for(uint256 i = 0; i < quantity; i++) {
mint(minter, tier, chunkId, reseller);
}
}
function updateChunkId(uint256 id, uint256 chunkId) public restricted {
landData[id][LC] = chunkId;
emit LandChunkIdUpdated(id, chunkId);
}
function updateChunkId(uint256[] memory ids, uint256 chunkId) public restricted {
for(uint256 i = 0; i < ids.length; i++) {
updateChunkId(ids[i], chunkId);
}
}
// Helper function for bulk moving land without having to jump chains
function landsBelongToChunk(uint256[] memory ids, uint256 chunkId) public view returns (bool) {
for(uint256 i = 0; i < ids.length; i++) {
if(landData[ids[i]][LC] != chunkId) {
return false;
}
if(ids[i] > landMinted) {
return false;
}
}
return true;
}
function getLandTierURI(uint256 id) public view returns (string memory uri) {
(uint256 tier,,,,) = get(id);
return getTierURI(tier);
}
function tokenURI(uint256 id) public view override returns (string memory) {
return getLandTierURI(id);
}
function getTierURI(uint256 tier) public view returns (string memory uri) {
return tierStrData[tier][TSU];
}
function setTierStr(uint256 tier, uint256 index, string memory val) public restricted {
tierStrData[tier][index] = val;
}
function getLandTier(uint256 id) public view returns (uint256) {
return landData[id][LT];
}
}pragma solidity ^0.6.2;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "./Promos.sol";
import "./util.sol";
import "./Garrison.sol";
import "./cryptoblades.sol";
contract BurningManager is Initializable, AccessControlUpgradeable {
using SafeMath for uint256;
bytes32 public constant GAME_ADMIN = keccak256("GAME_ADMIN");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
// STATE
Characters public characters;
CryptoBlades public game;
Garrison public garrison;
mapping(address => mapping(uint256 => uint256)) public userVars;
uint256 public constant USERVAR_SOUL_SUPPLY = 1;
mapping(uint256 => uint256) public vars;
uint256 public constant VAR_ROI_DAYS = 1;
uint256 public constant VAR_BURN_POWER_MULTIPLIER = 2;
function initialize(Characters _characters, Garrison _garrison, CryptoBlades _game)
public
initializer
{
__AccessControl_init();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(GAME_ADMIN, msg.sender);
characters = _characters;
garrison = _garrison;
game = _game;
}
// MODIFIERS
modifier isCharactersOwner(uint256[] memory burnIds) {
_isCharactersOwner(burnIds);
_;
}
function _isCharactersOwner(uint256[] memory burnIds) internal view {
for(uint i = 0; i < burnIds.length; i++) {
require(characters.ownerOf(burnIds[i]) == msg.sender || garrison.characterOwner(burnIds[i]) == msg.sender, 'Not owner');
}
}
modifier restricted() {
_restricted();
_;
}
function _restricted() internal view {
require(hasRole(GAME_ADMIN, msg.sender), "NGA");
}
modifier burningEnabled() {
_burningEnabled();
_;
}
function _burningEnabled() internal view {
require(vars[VAR_BURN_POWER_MULTIPLIER] > 0, "Burning disabled");
}
// VIEWS
function burnCharactersFee(uint256[] memory burnIds) public view returns (uint256) {
uint256 burnFee = 0;
for(uint i = 0; i < burnIds.length; i++) {
burnFee += burnCharacterFee(burnIds[i]);
}
return burnFee;
}
function burnCharacterFee(uint256 burnId) public view returns (uint256) {
return (game.vars(game.VAR_HOURLY_PAY_PER_FIGHT()) / game.vars(game.VAR_HOURLY_MAX_POWER_AVERAGE())) * 7 * characters.getTotalPower(burnId) * vars[VAR_ROI_DAYS];
}
//FUNCTIONS
function burnCharacterFromMarket(uint256 burnId) external burningEnabled {
require(hasRole(BURNER_ROLE, msg.sender), 'Not burner');
game.payContractTokenOnly(tx.origin, burnCharacterFee(burnId));
uint256[] memory burnIds = new uint256[](1);
burnIds[0] = burnId;
userVars[tx.origin][USERVAR_SOUL_SUPPLY] += characters.getSoulForBurns(burnIds).mul(vars[VAR_BURN_POWER_MULTIPLIER]).div(1e18);
characters.burnIntoSoul(burnIds);
}
function burnCharactersIntoCharacter(uint256[] memory burnIds, uint256 targetId) public isCharactersOwner(burnIds) burningEnabled {
game.payContractTokenOnly(msg.sender, burnCharactersFee(burnIds));
characters.burnIntoCharacter(burnIds, targetId, vars[VAR_BURN_POWER_MULTIPLIER]);
}
function burnCharactersIntoSoul(uint256[] memory burnIds) public isCharactersOwner(burnIds) burningEnabled {
game.payContractTokenOnly(msg.sender, burnCharactersFee(burnIds));
userVars[msg.sender][USERVAR_SOUL_SUPPLY] += characters.getSoulForBurns(burnIds).mul(vars[VAR_BURN_POWER_MULTIPLIER]).div(1e18);
characters.burnIntoSoul(burnIds);
}
function transferSoul(address targetAddress, uint256 soulAmount) public {
require(userVars[msg.sender][USERVAR_SOUL_SUPPLY] >= soulAmount, 'Not enough soul');
userVars[msg.sender][USERVAR_SOUL_SUPPLY] = userVars[msg.sender][USERVAR_SOUL_SUPPLY].sub(soulAmount);
userVars[targetAddress][USERVAR_SOUL_SUPPLY] = userVars[targetAddress][USERVAR_SOUL_SUPPLY].add(soulAmount);
}
function upgradeCharacterWithSoul(uint256 targetId, uint256 soulAmount) public burningEnabled {
require(userVars[msg.sender][USERVAR_SOUL_SUPPLY] >= soulAmount, 'Not enough soul');
userVars[msg.sender][USERVAR_SOUL_SUPPLY] = userVars[msg.sender][USERVAR_SOUL_SUPPLY].sub(soulAmount);
characters.upgradeWithSoul(targetId, soulAmount);
}
// VARS SETTER
function setVar(uint256 varField, uint256 value) external restricted {
vars[varField] = value;
}
}pragma solidity ^0.6.5;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "./interfaces/IPriceOracle.sol";
import "./interfaces/IRandoms.sol";
import "./shields.sol";
import "./Consumables.sol";
import "./Cosmetics.sol";
import "./weapons.sol";
import "./cryptoblades.sol";
import "./CBKLandSale.sol";
contract Blacksmith is Initializable, AccessControlUpgradeable {
using SafeERC20 for IERC20;
/* ========== CONSTANTS ========== */
bytes32 public constant GAME = keccak256("GAME");
uint256 public constant ITEM_WEAPON_RENAME = 1;
uint256 public constant ITEM_CHARACTER_RENAME = 2;
uint256 public constant ITEM_CHARACTER_TRAITCHANGE_FIRE = 3;
uint256 public constant ITEM_CHARACTER_TRAITCHANGE_EARTH = 4;
uint256 public constant ITEM_CHARACTER_TRAITCHANGE_WATER = 5;
uint256 public constant ITEM_CHARACTER_TRAITCHANGE_LIGHTNING = 6;
uint256 public constant ITEM_COSMETIC_WEAPON = 7; // series
uint256 public constant ITEM_COSMETIC_CHARACTER = 8; // series
uint256 public constant ITEM_SHIELD = 9;
uint256 public constant VAR_PURCHASE_SHIELD_TYPE = 1;
uint256 public constant VAR_PURCHASE_SHIELD_SUPPLY = 2; // only for non-0 type shields
uint256 public constant LINK_SKILL_ORACLE_2 = 1; // technically second skill oracle (it's separate)
uint256 public constant LINK_KING_ORACLE = 2;
/* ========== STATE VARIABLES ========== */
Weapons public weapons;
IRandoms public randoms;
mapping(address => uint32) public tickets;
Shields public shields;
CryptoBlades public game;
// keys: ITEM_ constant
mapping(uint256 => address) public itemAddresses;
mapping(uint256 => uint256) public itemFlatPrices;
mapping(uint256 => uint256) public numberParameters; // AKA "vars"
mapping(uint256 => mapping(uint256 => uint256)) public itemSeriesFlatPrices;
CBKLandSale public cbkLandSale;
// ERC20 => tier => price
mapping(uint256 => mapping(uint256 => uint256)) public landPrices;
mapping(uint256 => address) currencies;
mapping(uint256 => address) public links;
/* ========== INITIALIZERS AND MIGRATORS ========== */
function initialize(Weapons _weapons, IRandoms _randoms)
public
initializer
{
__AccessControl_init();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
weapons = _weapons;
randoms = _randoms;
}
function migrateRandoms(IRandoms _newRandoms) external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
randoms = _newRandoms;
}
function migrateTo_61c10da(Shields _shields, CryptoBlades _game) external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
shields = _shields;
game = _game;
}
function migrateTo_16884dd(
address _characterRename,
address _weaponRename,
address _charFireTraitChange,
address _charEarthTraitChange,
address _charWaterTraitChange,
address _charLightningTraitChange
) external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
itemAddresses[ITEM_WEAPON_RENAME] = _weaponRename;
itemAddresses[ITEM_CHARACTER_RENAME] = _characterRename;
itemAddresses[ITEM_CHARACTER_TRAITCHANGE_FIRE] = _charFireTraitChange;
itemAddresses[ITEM_CHARACTER_TRAITCHANGE_EARTH] = _charEarthTraitChange;
itemAddresses[ITEM_CHARACTER_TRAITCHANGE_WATER] = _charWaterTraitChange;
itemAddresses[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING] = _charLightningTraitChange;
itemFlatPrices[ITEM_WEAPON_RENAME] = 0.1 ether;
itemFlatPrices[ITEM_CHARACTER_RENAME] = 0.1 ether;
itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE] = 0.2 ether;
itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH] = 0.2 ether;
itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER] = 0.2 ether;
itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING] = 0.2 ether;
}
function migrateTo_bcdf4c(CBKLandSale _cbkLandSale) external {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
cbkLandSale = _cbkLandSale;
}
/* ========== VIEWS ========== */
/* ========== MUTATIVE FUNCTIONS ========== */
function recoverToken(address tokenAddress, uint256 amount) public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
IERC20(tokenAddress).safeTransfer(msg.sender, amount);
}
function purchaseShield() public {
require(itemFlatPrices[ITEM_SHIELD] > 0);
uint256 shieldType = numberParameters[VAR_PURCHASE_SHIELD_TYPE];
if(shieldType != 0) {
require(numberParameters[VAR_PURCHASE_SHIELD_SUPPLY] > 0);
numberParameters[VAR_PURCHASE_SHIELD_SUPPLY] -= 1;
}
game.payContractTokenOnly(msg.sender, itemFlatPrices[ITEM_SHIELD]);
shields.mint(msg.sender, shieldType,
uint256(keccak256(abi.encodePacked(msg.sender, blockhash(block.number - 1)))));
}
/* ========== MODIFIERS ========== */
modifier onlyGame() {
require(hasRole(GAME, msg.sender), "Only game");
_;
}
modifier isAdmin() {
_isAdmin();
_;
}
function _isAdmin() internal view {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");
}
/* ========== Generic Getters ========== */
function getAddressOfItem(uint256 itemIndex) public view returns(address) {
return itemAddresses[itemIndex];
}
function getFlatPriceOfItem(uint256 itemIndex) public view returns(uint256) {
return itemFlatPrices[itemIndex];
}
function getFlatPriceOfSeriesItem(uint256 itemIndex, uint256 seriesIndex) public view returns(uint256) {
return itemSeriesFlatPrices[itemIndex][seriesIndex];
}
function getCurrency(uint256 currency) public view returns (address) {
return currencies[currency];
}
function getLink(uint256 linkId) public view returns (address) {
return links[linkId];
}
function vars(uint256 varField) public view returns (uint256) {
return numberParameters[varField];
}
/* ========== Generic Setters ========== */
function setAddressOfItem(uint256 itemIndex, address to) external isAdmin {
itemAddresses[itemIndex] = to;
}
function setFlatPriceOfItem(uint256 itemIndex, uint256 flatWeiPrice) external isAdmin {
itemFlatPrices[itemIndex] = flatWeiPrice;
}
function setFlatPriceOfItemSeries(uint256 itemIndex,
uint256[] calldata seriesIndices,
uint256[] calldata seriesPrices
) external isAdmin {
for(uint i = 0; i < seriesIndices.length; i++) {
itemSeriesFlatPrices[itemIndex][seriesIndices[i]] = seriesPrices[i];
}
}
function setCurrency(uint256 currency, address currencyAddress, bool forced) external isAdmin {
require(currency > 0 && (forced || currencies[currency] == address(0)), 'used');
currencies[currency] = currencyAddress;
}
function setLink(uint256 linkId, address linkAddress) external isAdmin {
links[linkId] = linkAddress;
}
function setVar(uint256 varField, uint256 value) external isAdmin {
numberParameters[varField] = value;
}
function setVars(uint256[] calldata varFields, uint256[] calldata values) external isAdmin {
for(uint i = 0; i < varFields.length; i++) {
numberParameters[varFields[i]] = values[i];
}
}
/* ========== Character Rename ========== */
function setCharacterRenamePrice(uint256 newPrice) external isAdmin {
require(newPrice > 0, 'invalid price');
itemFlatPrices[ITEM_CHARACTER_RENAME] = newPrice;
}
function characterRenamePrice() public view returns (uint256){
return itemFlatPrices[ITEM_CHARACTER_RENAME];
}
function purchaseCharacterRenameTag(uint256 paying) public {
require(paying == itemFlatPrices[ITEM_CHARACTER_RENAME], 'Invalid price');
game.payContractTokenOnly(msg.sender, itemFlatPrices[ITEM_CHARACTER_RENAME]);
Consumables(itemAddresses[ITEM_CHARACTER_RENAME]).giveItem(msg.sender, 1);
}
function purchaseCharacterRenameTagDeal(uint256 paying) public { // 4 for the price of 3
require(paying == itemFlatPrices[ITEM_CHARACTER_RENAME] * 3, 'Invalid price');
game.payContractTokenOnly(msg.sender, itemFlatPrices[ITEM_CHARACTER_RENAME] * 3);
Consumables(itemAddresses[ITEM_CHARACTER_RENAME]).giveItem(msg.sender, 4);
}
/* ========== Weapon Rename ========== */
function setWeaponRenamePrice(uint256 newPrice) external isAdmin {
require(newPrice > 0, 'invalid price');
itemFlatPrices[ITEM_WEAPON_RENAME] = newPrice;
}
function weaponRenamePrice() public view returns (uint256){
return itemFlatPrices[ITEM_WEAPON_RENAME];
}
function purchaseWeaponRenameTag(uint256 paying) public {
require(paying == itemFlatPrices[ITEM_WEAPON_RENAME], 'Invalid price');
game.payContractTokenOnly(msg.sender, itemFlatPrices[ITEM_WEAPON_RENAME]);
Consumables(itemAddresses[ITEM_WEAPON_RENAME]).giveItem(msg.sender, 1);
}
function purchaseWeaponRenameTagDeal(uint256 paying) public { // 4 for the price of 3
require(paying == itemFlatPrices[ITEM_WEAPON_RENAME] * 3, 'Invalid price');
game.payContractTokenOnly(msg.sender, itemFlatPrices[ITEM_WEAPON_RENAME] * 3);
Consumables(itemAddresses[ITEM_WEAPON_RENAME]).giveItem(msg.sender, 4);
}
/* ========== Character Trait Change ========== */
function setCharacterTraitChangePrice(uint256 newPrice) external isAdmin {
require(newPrice > 0, 'invalid price');
itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE] = newPrice;
itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH] = newPrice;
itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER] = newPrice;
itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING] = newPrice;
}
function characterTraitChangePrice() public view returns (uint256){
return itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE];
}
function purchaseCharacterFireTraitChange(uint256 paying) public {
require(paying == itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE], 'Invalid price');
game.payContractTokenOnly(msg.sender, itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE]);
Consumables(itemAddresses[ITEM_CHARACTER_TRAITCHANGE_FIRE]).giveItem(msg.sender, 1);
}
function purchaseCharacterEarthTraitChange(uint256 paying) public {
require(paying == itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH], 'Invalid price');
game.payContractTokenOnly(msg.sender, itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH]);
Consumables(itemAddresses[ITEM_CHARACTER_TRAITCHANGE_EARTH]).giveItem(msg.sender, 1);
}
function purchaseCharacterWaterTraitChange(uint256 paying) public {
require(paying == itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER], 'Invalid price');
game.payContractTokenOnly(msg.sender, itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER]);
Consumables(itemAddresses[ITEM_CHARACTER_TRAITCHANGE_WATER]).giveItem(msg.sender, 1);
}
function purchaseCharacterLightningTraitChange(uint256 paying) public {
require(paying == itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING], 'Invalid price');
game.payContractTokenOnly(msg.sender, itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING]);
Consumables(itemAddresses[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING]).giveItem(msg.sender, 1);
}
/* ========== Weapon cosmetics ========== */
function setWeaponCosmeticPrice(uint32 cosmetic, uint256 newPrice) external isAdmin {
require(cosmetic > 0 && newPrice > 0, 'invalid request');
itemSeriesFlatPrices[ITEM_COSMETIC_WEAPON][cosmetic] = newPrice;
}
function getWeaponCosmeticPrice(uint32 cosmetic) public view returns (uint256){
return itemSeriesFlatPrices[ITEM_COSMETIC_WEAPON][cosmetic];
}
function purchaseWeaponCosmetic(uint32 cosmetic, uint256 paying) public {
require(paying > 0 && paying == itemSeriesFlatPrices[ITEM_COSMETIC_WEAPON][cosmetic], 'Invalid price');
game.payContractTokenOnly(msg.sender, itemSeriesFlatPrices[ITEM_COSMETIC_WEAPON][cosmetic]);
Cosmetics(itemAddresses[ITEM_COSMETIC_WEAPON]).giveCosmetic(msg.sender, cosmetic, 1);
}
/* ========== Character cosmetics ========== */
function setCharacterCosmeticPrice(uint32 cosmetic, uint256 newPrice) external isAdmin {
require(cosmetic > 0 && newPrice > 0, 'invalid request');
itemSeriesFlatPrices[ITEM_COSMETIC_CHARACTER][cosmetic] = newPrice;
}
function getCharacterCosmeticPrice(uint32 cosmetic) public view returns (uint256){
return itemSeriesFlatPrices[ITEM_COSMETIC_CHARACTER][cosmetic];
}
function purchaseCharacterCosmetic(uint32 cosmetic, uint256 paying) public {
require(paying > 0 && paying == itemSeriesFlatPrices[ITEM_COSMETIC_CHARACTER][cosmetic], 'Invalid price');
game.payContractTokenOnly(msg.sender, itemSeriesFlatPrices[ITEM_COSMETIC_CHARACTER][cosmetic]);
Cosmetics(itemAddresses[ITEM_COSMETIC_CHARACTER]).giveCosmetic(msg.sender, cosmetic, 1);
}
/* ========== CBK Land sale ========== */
event CBKLandPurchased(address indexed owner, uint256 tier, uint256 price, uint256 currency);
function purchaseT1CBKLand(uint256 paying, uint256 currency) public {
uint256 price = getCBKLandPrice(cbkLandSale.TIER_ONE(), currency);
require(paying > 0 && price == paying, 'Invalid price');
payCurrency(msg.sender, price, currency);
cbkLandSale.giveT1Land(msg.sender);
emit CBKLandPurchased(msg.sender, cbkLandSale.TIER_ONE(), price, currency);
}
function purchaseT2CBKLand(uint256 paying, uint256 chunkId, uint256 currency) public {
uint256 price = getCBKLandPrice(cbkLandSale.TIER_TWO(), currency);
require(paying > 0 && price == paying, 'Invalid price');
payCurrency(msg.sender, price, currency);
cbkLandSale.giveT2Land(msg.sender, chunkId);
emit CBKLandPurchased(msg.sender, cbkLandSale.TIER_TWO(), price, currency);
}
function purchaseT3CBKLand(uint256 paying, uint256 chunkId, uint256 currency) public {
uint256 price = getCBKLandPrice(cbkLandSale.TIER_THREE(), currency);
require(paying > 0 && price == paying, 'Invalid price');
payCurrency(msg.sender, price, currency);
cbkLandSale.giveT3Land(msg.sender, chunkId);
emit CBKLandPurchased(msg.sender, cbkLandSale.TIER_THREE(), price, currency);
}
function getCBKLandPrice(uint256 tier, uint256 currency) public view returns (uint256){
return landPrices[currency][tier] * getOracledTokenPerUSD(currency);
}
function getOracledTokenPerUSD(uint256 currency) public view returns (uint256) {
if(currency == 0) {
return IPriceOracle(links[LINK_SKILL_ORACLE_2]).currentPrice();
}
else {
return IPriceOracle(links[LINK_KING_ORACLE]).currentPrice();
}
}
function setCBKLandPrice(uint256 tier, uint256 newPrice, uint256 currency) external isAdmin {
require(newPrice > 0, 'invalid price');
require(tier >= cbkLandSale.TIER_ONE() && tier <= cbkLandSale.TIER_THREE(), "Invalid tier");
landPrices[currency][tier] = newPrice;
}
function payCurrency(address payer, uint256 paying, uint256 currency) internal {
if(currency == 0){
game.payContractTokenOnly(payer, paying, true);
}
else {
IERC20(currencies[currency]).transferFrom(payer, address(this), paying);
}
}
}{
"remappings": [],
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "istanbul",
"libraries": {},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":true,"internalType":"contract IERC721","name":"nftAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"nftID","type":"uint256"}],"name":"CancelledListing","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":true,"internalType":"contract IERC721","name":"nftAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"nftID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"ListingPriceChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":true,"internalType":"contract IERC721","name":"nftAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"nftID","type":"uint256"},{"indexed":false,"internalType":"address","name":"newTargetBuyer","type":"address"}],"name":"ListingTargetBuyerChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":true,"internalType":"contract IERC721","name":"nftAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"nftID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"address","name":"targetBuyer","type":"address"}],"name":"NewListing","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"address","name":"seller","type":"address"},{"indexed":true,"internalType":"contract IERC721","name":"nftAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"nftID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"PurchasedListing","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GAME_ADMIN","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"addFee","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"address","name":"_targetBuyer","type":"address"}],"name":"addListing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"}],"name":"allowToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burningManager","outputs":[{"internalType":"contract BurningManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"cancelListing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"changeFee","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_newPrice","type":"uint256"}],"name":"changeListingPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"address","name":"_newTargetBuyer","type":"address"}],"name":"changeListingTargetBuyer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultTax","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultTaxAsRoundedPercentRoughEstimate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"}],"name":"disallowToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAllowedTokenTypes","outputs":[{"internalType":"contract IERC721[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint8","name":"_limit","type":"uint8"},{"internalType":"uint256","name":"_pageNumber","type":"uint256"},{"internalType":"uint8","name":"_trait","type":"uint8"},{"internalType":"uint8","name":"_minLevel","type":"uint8"},{"internalType":"uint8","name":"_maxLevel","type":"uint8"}],"name":"getCharacterListingIDsPage","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getFinalPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getListedTokenTypes","outputs":[{"internalType":"contract IERC721[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"}],"name":"getListingIDs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"address","name":"_seller","type":"address"}],"name":"getListingIDsBySeller","outputs":[{"internalType":"uint256[]","name":"tokens","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"getListingSlice","outputs":[{"internalType":"uint256","name":"returnedCount","type":"uint256"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"address[]","name":"sellers","type":"address[]"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint8","name":"_trait","type":"uint8"},{"internalType":"uint8","name":"_minLevel","type":"uint8"},{"internalType":"uint8","name":"_maxLevel","type":"uint8"}],"name":"getNumberOfCharacterListings","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"address","name":"_seller","type":"address"}],"name":"getNumberOfListingsBySeller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"}],"name":"getNumberOfListingsForToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint8","name":"_trait","type":"uint8"},{"internalType":"uint8","name":"_stars","type":"uint8"}],"name":"getNumberOfWeaponListings","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getSellerOfNftID","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getSellerPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getTargetBuyer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getTaxOnListing","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint8","name":"_limit","type":"uint8"},{"internalType":"uint256","name":"_pageNumber","type":"uint256"},{"internalType":"uint8","name":"_trait","type":"uint8"},{"internalType":"uint8","name":"_stars","type":"uint8"}],"name":"getWeaponListingIDsPage","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_skillToken","type":"address"},{"internalType":"address","name":"_taxRecipient","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"}],"name":"isTokenAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isTokenBanned","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isUserBanned","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IPriceOracle","name":"_priceOracleSkillPerUsd","type":"address"}],"name":"migrateTo_2316231","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract BurningManager","name":"_burningManager","type":"address"}],"name":"migrateTo_29635ef","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Characters","name":"_charactersContract","type":"address"},{"internalType":"contract Weapons","name":"_weaponsContract","type":"address"}],"name":"migrateTo_a98a9ac","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"priceOracleSkillPerUsd","outputs":[{"internalType":"contract IPriceOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_maxPrice","type":"uint256"}],"name":"purchaseBurnCharacter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_maxPrice","type":"uint256"}],"name":"purchaseListing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverSkill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"cents","type":"uint256"}],"name":"setAddValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"cents","type":"uint256"}],"name":"setChangeValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int128","name":"_defaultTax","type":"int128"}],"name":"setDefaultTax","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_percent","type":"uint256"}],"name":"setDefaultTaxAsPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_numerator","type":"uint256"},{"internalType":"uint256","name":"_denominator","type":"uint256"}],"name":"setDefaultTaxAsRational","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"int128","name":"_newTax","type":"int128"}],"name":"setTaxOnTokenType","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_percent","type":"uint256"}],"name":"setTaxOnTokenTypeAsPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_numerator","type":"uint256"},{"internalType":"uint256","name":"_denominator","type":"uint256"}],"name":"setTaxOnTokenTypeAsRational","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_taxRecipient","type":"address"}],"name":"setTaxRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"to","type":"bool"}],"name":"setUserBan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"},{"internalType":"bool","name":"to","type":"bool"}],"name":"setUserBans","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"skillToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tax","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"taxRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"unlistItem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_tokenAddress","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"unlistItems","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int128","name":"usdAmount","type":"int128"}],"name":"usdToSkill","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b5061508b806100206000396000f3fe608060405234801561001057600080fd5b50600436106103d95760003560e01c806374c7b3281161020a578063bb782e7311610125578063d547741f116100b8578063da8e8ca211610087578063da8e8ca214610f1f578063e4ca10dc14610f45578063e79767af14610f4d578063ed9999ca14610f73578063f9eaee0d14610fa5576103d9565b8063d547741f14610e58578063d679756214610e84578063d739d8bc14610ec5578063d9db220d14610ef3576103d9565b8063cb8da193116100f4578063cb8da19314610dc8578063cc8efb1414610e0d578063d1ebdfd314610e33578063d23727a314610e3b576103d9565b8063bb782e7314610d34578063c573a97e14610d5a578063c7e4ff4214610d7d578063ca15c87314610dab576103d9565b80639642b3f71161019d578063a5bc36321161016c578063a5bc363214610c7e578063a6f9572614610cb0578063b2ddee0614610ce2578063b53472ef14610d0e576103d9565b80639642b3f714610c0157806399bd17e414610c245780639ed9b4fd14610c50578063a217fddf14610c76576103d9565b80637ff57c57116101d95780637ff57c5714610b4457806386c51fa914610b725780639010d07c14610b9e57806391d1485414610bc1576103d9565b806374c7b32814610a595780637899d80f14610a8857806378e3079e14610aae578063799e6b6114610ad4576103d9565b8063346710fd116102fa578063490df5301161028d57806364d678681161025c57806364d67868146109f557806366a417fe14610a125780636891de0714610a49578063737ea06e14610a51576103d9565b8063490df53014610924578063534be8fb14610941578063546b45ac146109bf5780635504d3d2146109ed576103d9565b80633b75a46a116102c95780633b75a46a146108a85780633f7e1dd5146108c8578063470e50c5146108d0578063485cc955146108f6576103d9565b8063346710fd146107cd57806335e0406f1461080757806336568abe1461082d57806339378fb314610859576103d9565b80631eb788f5116103725780632c2dea77116103415780632c2dea77146107375780632df8c04a1461076d5780632f2ff15d146107995780632f48f151146107c5576103d9565b80631eb788f5146105ab578063248a9ca3146106c257806327314473146106df578063281fb1651461070b576103d9565b8063150b7a02116103ae578063150b7a021461046557806316a71cfc146105105780631a10c902146105345780631ac0bab01461058c576103d9565b80621e9187146103de57806240ff6c1461041c5780630a8a92151461043d5780630bb99c571461045d575b600080fd5b61040a600480360360408110156103f457600080fd5b506001600160a01b038135169060200135610fcb565b60408051918252519081900360200190f35b610424610ff9565b60408051600f92830b90920b8252519081900360200190f35b61040a6004803603602081101561045357600080fd5b5035600f0b611009565b610424611099565b6104f36004803603608081101561047b57600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b8111156104b557600080fd5b8201836020820111156104c757600080fd5b803590602001918460018302840111600160201b831117156104e857600080fd5b5090925090506110a2565b604080516001600160e01b03199092168252519081900360200190f35b61051861113f565b604080516001600160a01b039092168252519081900360200190f35b61053c61114e565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610578578181015183820152602001610560565b505050509050019250505060405180910390f35b6105a9600480360360208110156105a257600080fd5b50356111ee565b005b6105dd600480360360608110156105c157600080fd5b506001600160a01b03813516906020810135906040013561128c565b60405180858152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b8381101561062b578181015183820152602001610613565b50505050905001848103835286818151815260200191508051906020019060200280838360005b8381101561066a578181015183820152602001610652565b50505050905001848103825285818151815260200191508051906020019060200280838360005b838110156106a9578181015183820152602001610691565b5050505090500197505050505050505060405180910390f35b61040a600480360360208110156106d857600080fd5b5035611475565b610518600480360360408110156106f557600080fd5b506001600160a01b03813516906020013561148a565b6105186004803603604081101561072157600080fd5b506001600160a01b038135169060200135611506565b6105a96004803603606081101561074d57600080fd5b506001600160a01b0381358116916020810135916040909101351661152e565b61040a6004803603604081101561078357600080fd5b506001600160a01b0381351690602001356116f2565b6105a9600480360360408110156107af57600080fd5b50803590602001356001600160a01b0316611728565b610424611794565b6105a9600480360360808110156107e357600080fd5b506001600160a01b038135811691602081013591604082013591606001351661179d565b61053c6004803603602081101561081d57600080fd5b50356001600160a01b0316611bee565b6105a96004803603604081101561084357600080fd5b50803590602001356001600160a01b0316611c99565b61053c600480360360c081101561086f57600080fd5b506001600160a01b038135169060ff60208201358116916040810135916060820135811691608081013582169160a09091013516611cfa565b6105a9600480360360208110156108be57600080fd5b5035600f0b611f84565b610518612016565b6105a9600480360360208110156108e657600080fd5b50356001600160a01b0316612025565b6105a96004803603604081101561090c57600080fd5b506001600160a01b03813581169160200135166120f1565b6105a96004803603602081101561093a57600080fd5b5035612209565b6105a96004803603604081101561095757600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561098157600080fd5b82018360208201111561099357600080fd5b803590602001918460208302840111600160201b831117156109b457600080fd5b5090925090506122f1565b61040a600480360360408110156109d557600080fd5b506001600160a01b0381358116916020013516612404565b61040a612495565b6105a960048036036020811015610a0b57600080fd5b50356124b7565b61040a60048036036060811015610a2857600080fd5b506001600160a01b038135169060ff60208201358116916040013516612542565b61040a6126cb565b6105186126ee565b6105a960048036036040811015610a6f57600080fd5b506001600160a01b038135169060200135600f0b6126fd565b61042460048036036020811015610a9e57600080fd5b50356001600160a01b031661278d565b6105a960048036036020811015610ac457600080fd5b50356001600160a01b03166127a2565b6105a960048036036040811015610aea57600080fd5b810190602081018135600160201b811115610b0457600080fd5b820183602082011115610b1657600080fd5b803590602001918460208302840111600160201b83111715610b3757600080fd5b919350915035151561282f565b6105a960048036036040811015610b5a57600080fd5b506001600160a01b03813581169160200135166128ef565b6105a960048036036040811015610b8857600080fd5b506001600160a01b038135169060200135612965565b61051860048036036040811015610bb457600080fd5b50803590602001356129ff565b610bed60048036036040811015610bd757600080fd5b50803590602001356001600160a01b0316612a1d565b604080519115158252519081900360200190f35b6105a960048036036040811015610c1757600080fd5b5080359060200135612a3b565b6105a960048036036040811015610c3a57600080fd5b506001600160a01b038135169060200135612caf565b610bed60048036036020811015610c6657600080fd5b50356001600160a01b0316612d69565b61040a612d7e565b6105a960048036036060811015610c9457600080fd5b506001600160a01b038135169060208101359060400135612d83565b6105a960048036036060811015610cc657600080fd5b506001600160a01b038135169060208101359060400135612e17565b6105a960048036036040811015610cf857600080fd5b506001600160a01b038135169060200135613078565b6105a960048036036020811015610d2457600080fd5b50356001600160a01b03166132fa565b610bed60048036036020811015610d4a57600080fd5b50356001600160a01b0316613391565b6105a960048036036040811015610d7057600080fd5b50803590602001356133a6565b6105a960048036036040811015610d9357600080fd5b506001600160a01b0381351690602001351515613444565b61040a60048036036020811015610dc157600080fd5b50356134da565b61053c600480360360a0811015610dde57600080fd5b506001600160a01b038135169060ff6020820135811691604081013591606082013581169160800135166134f1565b61040a60048036036020811015610e2357600080fd5b50356001600160a01b0316613759565b61051861377a565b6105a960048036036020811015610e5157600080fd5b5035613789565b6105a960048036036040811015610e6e57600080fd5b50803590602001356001600160a01b031661384c565b61040a60048036036080811015610e9a57600080fd5b506001600160a01b038135169060ff60208201358116916040810135821691606090910135166138a5565b61053c60048036036040811015610edb57600080fd5b506001600160a01b0381358116916020013516613a4f565b61040a60048036036040811015610f0957600080fd5b506001600160a01b038135169060200135613b46565b6105a960048036036020811015610f3557600080fd5b50356001600160a01b0316613b6b565b61053c613bd5565b6105a960048036036020811015610f6357600080fd5b50356001600160a01b0316613c6e565b6105a960048036036060811015610f8957600080fd5b506001600160a01b038135169060208101359060400135613cea565b610bed60048036036020811015610fbb57600080fd5b50356001600160a01b0316613ed4565b6001600160a01b03821660009081526067602090815260408083208484529091529020600101545b92915050565b607554600160801b9004600f0b81565b6000610ff3607460009054906101000a90046001600160a01b03166001600160a01b0316639d1b464a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561105c57600080fd5b505afa158015611070573d6000803e3d6000fd5b505050506040513d602081101561108657600080fd5b5051600f84900b9063ffffffff613ee716565b606f54600f0b81565b6000336110b660698263ffffffff613f4f16565b80156110e557506001600160a01b03811660009081526068602052604090206110e5908663ffffffff613f6416565b61112c576040805162461bcd60e51b8152602060048201526013602482015272151bdad95b881251081b9bdd081b1a5cdd1959606a1b604482015290519081900360640190fd5b50630a85bd0160e11b9695505050505050565b6077546001600160a01b031681565b606060708161115c82613f70565b67ffffffffffffffff8111801561117257600080fd5b5060405190808252806020026020018201604052801561119c578160200160208202803683370190505b50905060005b81518110156111e7576111bb838263ffffffff613f7b16565b8282815181106111c757fe5b6001600160a01b03909216602092830291909101909101526001016111a2565b5091505090565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206112179033612a1d565b611259576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b611264816064613f87565b606f8054600f9290920b6001600160801b03166001600160801b031990921691909117905550565b80606080808367ffffffffffffffff811180156112a857600080fd5b506040519080825280602002602001820160405280156112d2578160200160208202803683370190505b5092508467ffffffffffffffff811180156112ec57600080fd5b50604051908082528060200260200182016040528015611316578160200160208202803683370190505b5091508467ffffffffffffffff8111801561133057600080fd5b5060405190808252806020026020018201604052801561135a578160200160208202803683370190505b506001600160a01b038816600090815260686020526040812091925090875b8789018110156114685761138c82613f70565b811061139d575090945061146c9050565b60006113af838363ffffffff613f7b16565b90506113b9614ee9565b506001600160a01b03808c166000908152606760209081526040808320858452825291829020825180840190935280549093168252600190920154918101919091528751829089908790811061140b57fe5b602002602001018181525050806000015187868151811061142857fe5b6001600160a01b03909216602092830291909101820152810151865160018701968891811061145357fe5b60209081029190910101525050600101611379565b5050505b93509350935093565b60009081526033602052604090206002015490565b600061149d60698463ffffffff613f4f16565b6114a957506000610ff3565b6001600160a01b03831660009081526068602052604090206114d1908363ffffffff613f6416565b6114dd57506000610ff3565b506001600160a01b03918216600090815260676020908152604080832093835292905220541690565b6001600160a01b03918216600090815260766020908152604080832093835292905220541690565b336000908152606c602052604090205460ff1615611586576040805162461bcd60e51b815260206004820152601060248201526f466f7262696464656e2061636365737360801b604482015290519081900360640190fd5b828261159960698363ffffffff613f4f16565b80156115c857506001600160a01b03821660009081526068602052604090206115c8908263ffffffff613f6416565b61160f576040805162461bcd60e51b8152602060048201526013602482015272151bdad95b881251081b9bdd081b1a5cdd1959606a1b604482015290519081900360640190fd5b6001600160a01b0380861660009081526067602090815260408083208884529091529020548691869116331461167c576040805162461bcd60e51b815260206004820152600d60248201526c1058d8d95cdcc819195b9a5959609a1b604482015290519081900360640190fd5b6001600160a01b0387811660008181526076602090815260408083208b845282529182902080546001600160a01b031916948a16948517905581519384529051899333927faa79ba781ed370c2a15b6fe1a86316cab25598fecac3329356cc3f5557c8bae692918290030190a450505050505050565b6001600160a01b0382166000908152606d602052604081205461172190600f0b61171c8585610fcb565b613ee7565b9392505050565b60008281526033602052604090206002015461174b90611746613fc7565b612a1d565b6117865760405162461bcd60e51b815260040180806020018281038252602f815260200180614f23602f913960400191505060405180910390fd5b6117908282613fcb565b5050565b607554600f0b81565b836117a781613ed4565b6117e25760405162461bcd60e51b8152600401808060200182810382526027815260200180614fd66027913960400191505060405180910390fd5b846117f4816380ac58cd60e01b61403a565b6117fd57600080fd5b858561181060698363ffffffff613f4f16565b158061184157506001600160a01b038216600090815260686020526040902061183f908263ffffffff613f6416565b155b611892576040805162461bcd60e51b815260206004820152601b60248201527f546f6b656e204944206d757374206e6f74206265206c69737465640000000000604482015290519081900360640190fd5b6075546000600f91820b90910b13156118bd576075546118bd906118b890600f0b611009565b614056565b336000908152606c602052604090205460ff1615611a985760655460408051636eb1769f60e11b815233600482015230602482015290516000926001600160a01b03169163dd62ed3e916044808301926020929190829003018186803b15801561192657600080fd5b505afa15801561193a573d6000803e3d6000fd5b505050506040513d602081101561195057600080fd5b5051606554604080516370a0823160e01b815233600482015290519293506000926001600160a01b03909216916370a0823191602480820192602092909190829003018186803b1580156119a357600080fd5b505afa1580156119b7573d6000803e3d6000fd5b505050506040513d60208110156119cd57600080fd5b50516065546066549192506001600160a01b03908116916323b872dd913391168486116119fa57856119fc565b845b6040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160a01b03166001600160a01b031681526020018281526020019350505050602060405180830381600087803b158015611a6457600080fd5b505af1158015611a78573d6000803e3d6000fd5b505050506040513d6020811015611a8e57600080fd5b50611b2f92505050565b60408051808201825233815260208082018981526001600160a01b038c81166000818152606785528681208e82528552868120955186549084166001600160a01b03199182161787559351600190960195909555808552607684528585208d865284528585208054928c16929093169190911790915582526068905220611b25908863ffffffff6140e316565b50611b2f886140ef565b60408051632142170760e11b81523360048201523060248201526044810189905290516001600160a01b038a16916342842e0e91606480830192600092919082900301818387803b158015611b8357600080fd5b505af1158015611b97573d6000803e3d6000fd5b5050604080518981526001600160a01b03898116602083015282518c9550908d16935033927f5949a8af96064ae9b490d9118c3636906cab7e3b2c46530c7e2b5ff9f8a297e1928290030190a45050505050505050565b6001600160a01b038116600090815260686020526040902060609081611c1382613f70565b67ffffffffffffffff81118015611c2957600080fd5b50604051908082528060200260200182016040528015611c53578160200160208202803683370190505b50905060005b8151811015611c9157611c72838263ffffffff613f7b16565b828281518110611c7e57fe5b6020908102919091010152600101611c59565b509392505050565b611ca1613fc7565b6001600160a01b0316816001600160a01b031614611cf05760405162461bcd60e51b815260040180806020018281038252602f815260200180615027602f913960400191505060405180910390fd5b611790828261412d565b6001600160a01b0386166000908152606860205260408120606091611d21898787876138a5565b905060ff88166001880102600081831015611d5057611d4b8360ff8c168b0263ffffffff61419c16565b611d55565b8960ff165b905060608167ffffffffffffffff81118015611d7057600080fd5b50604051908082528060200260200182016040528015611d9a578160200160208202803683370190505b509050600080805b611dab88613f70565b81108015611db857508583105b15611f71576073546000906001600160a01b031663acb586df611de18b8563ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015611e1557600080fd5b505afa158015611e29573d6000803e3d6000fd5b505050506040513d6020811015611e3f57600080fd5b50516073549091506000906001600160a01b03166386481d40611e688c8663ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015611e9c57600080fd5b505afa158015611eb0573d6000803e3d6000fd5b505050506040513d6020811015611ec657600080fd5b5051905060ff808f161480611ee057508d60ff168260ff16145b8015611f1e57508c60ff1660ff1480611efc57508b60ff1660ff145b80611f1e57508c60ff168160ff1610158015611f1e57508b60ff168160ff1611155b15611f67578f60ff1688038510611f6057611f3f8a8463ffffffff613f7b16565b868560ff1681518110611f4e57fe5b60209081029190910101526001909301925b6001909401935b5050600101611da2565b50919d9c50505050505050505050505050565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a019020611fad9033612a1d565b611fef576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b606f8054600f9290920b6001600160801b03166001600160801b0319909216919091179055565b6074546001600160a01b031681565b612030600033612a1d565b61206d576040805162461bcd60e51b81526020600482015260096024820152682737ba1030b236b4b760b91b604482015290519081900360640190fd5b607480546001600160a01b0319166001600160a01b03831617905561209460026064613f87565b60758054600f9290920b6001600160801b03166001600160801b03199092169190911790556120c560006064613f87565b607560106101000a8154816001600160801b030219169083600f0b6001600160801b0316021790555050565b600054610100900460ff168061210a575061210a6141f9565b80612118575060005460ff16155b6121535760405162461bcd60e51b815260040180806020018281038252602e815260200180614fa8602e913960400191505060405180910390fd5b600054610100900460ff1615801561217e576000805460ff1961ff0019909116610100171660011790555b61218661420a565b612191600033611786565b606580546001600160a01b038086166001600160a01b03199283161790925560668054928516929091169190911790556121cd6001600a613f87565b606f8054600f9290920b6001600160801b03166001600160801b03199092169190911790558015612204576000805461ff00191690555b505050565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206122329033612a1d565b612274576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b60648111156122be576040805162461bcd60e51b8152602060048201526011602482015270082c8c8acc2d8eaca40e8dede40d0d2ced607b1b604482015290519081900360640190fd5b6122c9816064613f87565b60758054600f9290920b6001600160801b03166001600160801b031990921691909117905550565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a01902061231a9033612a1d565b61235c576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b60005b818110156123fe576001600160a01b03841660009081526067602052604081209084848481811061238c57fe5b60209081029290920135835250810191909152604001600090812080546001600160a01b0319168155600101556123f58383838181106123c857fe5b6001600160a01b03881660009081526068602090815260409091209391020135905063ffffffff6142bb16565b5060010161235f565b50505050565b6001600160a01b038216600090815260686020526040812081805b61242883613f70565b81101561248c576001600160a01b038681166000908152606760205260408120918716919061245d868563ffffffff613f7b16565b81526020810191909152604001600020546001600160a01b03161415612484576001909101905b60010161241f565b50949350505050565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a01902081565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206124e09033612a1d565b612522576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b60655461253f906001600160a01b0316338363ffffffff6142c716565b50565b6001600160a01b0383166000908152606860205260408120818080805b61256885613f70565b8110156126be576072546001600160a01b031663acb586df612590878463ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156125c457600080fd5b505afa1580156125d8573d6000803e3d6000fd5b505050506040513d60208110156125ee57600080fd5b50516072549093506001600160a01b031663adf04f20612614878463ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561264857600080fd5b505afa15801561265c573d6000803e3d6000fd5b505050506040513d602081101561267257600080fd5b5051915060ff808916148061268c57508760ff168360ff16145b80156126aa57508660ff1660ff14806126aa57508660ff168260ff16145b156126b6576001909301925b60010161255f565b5091979650505050505050565b606f546000906126e990600f90810b900b606463ffffffff613ee716565b905090565b6066546001600160a01b031681565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206127269033612a1d565b612768576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b8161277a816380ac58cd60e01b61403a565b61278357600080fd5b6122048383614319565b606d60205260009081526040902054600f0b81565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206127cb9033612a1d565b61280d576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b606680546001600160a01b0319166001600160a01b0392909216919091179055565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206128589033612a1d565b61289a576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b60005b828110156123fe5781606c60008686858181106128b657fe5b602090810292909201356001600160a01b0316835250810191909152604001600020805460ff191691151591909117905560010161289d565b6128fa600033612a1d565b612937576040805162461bcd60e51b81526020600482015260096024820152682737ba1030b236b4b760b91b604482015290519081900360640190fd5b607380546001600160a01b039384166001600160a01b03199182161790915560728054929093169116179055565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a01902061298e9033612a1d565b6129d0576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b816129e2816380ac58cd60e01b61403a565b6129eb57600080fd5b612204836129fa846064613f87565b614319565b6000828152603360205260408120611721908363ffffffff613f7b16565b6000828152603360205260408120611721908363ffffffff613f4f16565b336000908152606c602052604090205460ff1615612a93576040805162461bcd60e51b815260206004820152601060248201526f466f7262696464656e2061636365737360801b604482015290519081900360640190fd5b6073546001600160a01b031682612ab160698363ffffffff613f4f16565b8015612ae057506001600160a01b0382166000908152606860205260409020612ae0908263ffffffff613f6416565b612b27576040805162461bcd60e51b8152602060048201526013602482015272151bdad95b881251081b9bdd081b1a5cdd1959606a1b604482015290519081900360640190fd5b6073546001600160a01b03908116600081815260766020908152604080832089845290915290205490918691161580612b8457506001600160a01b0382811660009081526076602090815260408083208584529091529020541633145b612bc8576040805162461bcd60e51b815260206004820152601060248201526f2737ba103a30b933b2ba10313abcb2b960811b604482015290519081900360640190fd5b6000612bd2614ee9565b607354612be9906001600160a01b031689896143c8565b6073548151604080516001600160a01b0392831681526020810186905281519597509395508c94919092169233927f7762d23dc0afc60972698afa4f7ab7e5853e961dad5c968fe29b0fd3b14fffb592918290030190a460775460408051634389afa960e01b8152600481018b905290516001600160a01b0390921691634389afa99160248082019260009290919082900301818387803b158015612c8d57600080fd5b505af1158015612ca1573d6000803e3d6000fd5b505050505050505050505050565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a019020612cd89033612a1d565b612d1a576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b6001600160a01b0382166000818152606760209081526040808320858452825280832080546001600160a01b03191681556001018390559282526068905220612204908263ffffffff6142bb16565b606c6020526000908152604090205460ff1681565b600081565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a019020612dac9033612a1d565b612dee576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b82612e00816380ac58cd60e01b61403a565b612e0957600080fd5b6123fe846129fa8585613f87565b336000908152606c602052604090205460ff1615612e6f576040805162461bcd60e51b815260206004820152601060248201526f466f7262696464656e2061636365737360801b604482015290519081900360640190fd5b8282612e8260698363ffffffff613f4f16565b8015612eb157506001600160a01b0382166000908152606860205260409020612eb1908263ffffffff613f6416565b612ef8576040805162461bcd60e51b8152602060048201526013602482015272151bdad95b881251081b9bdd081b1a5cdd1959606a1b604482015290519081900360640190fd5b6001600160a01b03808616600090815260766020908152604080832088845290915290205486918691161580612f5257506001600160a01b0382811660009081526076602090815260408083208584529091529020541633145b612f96576040805162461bcd60e51b815260206004820152601060248201526f2737ba103a30b933b2ba10313abcb2b960811b604482015290519081900360640190fd5b6000612fa0614ee9565b612fab8989896143c8565b60408051632142170760e11b8152306004820152336024820152604481018c905290519294509092506001600160a01b038b16916342842e0e9160648082019260009290919082900301818387803b15801561300657600080fd5b505af115801561301a573d6000803e3d6000fd5b50508251604080516001600160a01b0392831681526020810187905281518d9550928e16935033927f7762d23dc0afc60972698afa4f7ab7e5853e961dad5c968fe29b0fd3b14fffb5929181900390910190a4505050505050505050565b336000908152606c602052604090205460ff16156130d0576040805162461bcd60e51b815260206004820152601060248201526f466f7262696464656e2061636365737360801b604482015290519081900360640190fd5b81816130e360698363ffffffff613f4f16565b801561311257506001600160a01b0382166000908152606860205260409020613112908263ffffffff613f6416565b613159576040805162461bcd60e51b8152602060048201526013602482015272151bdad95b881251081b9bdd081b1a5cdd1959606a1b604482015290519081900360640190fd5b6001600160a01b03808516600090815260676020908152604080832087845290915290205485918591163314806131b45750604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206131b49033612a1d565b6131f5576040805162461bcd60e51b815260206004820152600d60248201526c1058d8d95cdcc819195b9a5959609a1b604482015290519081900360640190fd5b6001600160a01b0386166000818152606760209081526040808320898452825280832080546001600160a01b03191681556001018390559282526068905220613244908663ffffffff6142bb16565b5061324e866140ef565b60408051632142170760e11b81523060048201523360248201526044810187905290516001600160a01b038816916342842e0e91606480830192600092919082900301818387803b1580156132a257600080fd5b505af11580156132b6573d6000803e3d6000fd5b50506040518792506001600160a01b038916915033907fd66e7fc4b7515678684c4cbafb29ad2dbe872f0d8913ac2227c7cfead5d6923490600090a4505050505050565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206133239033612a1d565b613365576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b80613377816380ac58cd60e01b61403a565b61338057600080fd5b61220460708363ffffffff61457116565b606b6020526000908152604090205460ff1681565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206133cf9033612a1d565b613411576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b61341b8282613f87565b606f8054600f9290920b6001600160801b03166001600160801b03199092169190911790555050565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a01902061346d9033612a1d565b6134af576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b6001600160a01b03919091166000908152606c60205260409020805460ff1916911515919091179055565b6000818152603360205260408120610ff390613f70565b6001600160a01b0385166000908152606860205260408120606091613517888686612542565b905060ff87166001870102600081831015613546576135418360ff8b168a0263ffffffff61419c16565b61354b565b8860ff165b905060608167ffffffffffffffff8111801561356657600080fd5b50604051908082528060200260200182016040528015613590578160200160208202803683370190505b509050600080805b6135a188613f70565b811080156135ae57508583105b15613747576072546000906001600160a01b031663acb586df6135d78b8563ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561360b57600080fd5b505afa15801561361f573d6000803e3d6000fd5b505050506040513d602081101561363557600080fd5b50516072549091506000906001600160a01b031663adf04f2061365e8c8663ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561369257600080fd5b505afa1580156136a6573d6000803e3d6000fd5b505050506040513d60208110156136bc57600080fd5b5051905060ff808e1614806136d657508c60ff168260ff16145b80156136f457508b60ff1660ff14806136f457508b60ff168160ff16145b1561373d578e60ff1688038510613736576137158a8463ffffffff613f7b16565b868560ff168151811061372457fe5b60209081029190910101526001909301925b6001909401935b5050600101613598565b50919c9b505050505050505050505050565b6001600160a01b0381166000908152606860205260408120610ff390613f70565b6065546001600160a01b031681565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206137b29033612a1d565b6137f4576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b6064811115613841576040805162461bcd60e51b8152602060048201526014602482015273086d0c2dccecaacc2d8eaca40e8dede40d0d2ced60631b604482015290519081900360640190fd5b6120c5816064613f87565b60008281526033602052604090206002015461386a90611746613fc7565b611cf05760405162461bcd60e51b8152600401808060200182810382526030815260200180614f786030913960400191505060405180910390fd5b6001600160a01b0384166000908152606860205260408120818080805b6138cb85613f70565b811015613a41576073546001600160a01b03166386481d406138f3878463ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561392757600080fd5b505afa15801561393b573d6000803e3d6000fd5b505050506040513d602081101561395157600080fd5b50516073549093506001600160a01b031663acb586df613977878463ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156139ab57600080fd5b505afa1580156139bf573d6000803e3d6000fd5b505050506040513d60208110156139d557600080fd5b5051915060ff808a1614806139ef57508860ff168260ff16145b8015613a2d57508760ff1660ff1480613a0b57508660ff1660ff145b80613a2d57508760ff168360ff1610158015613a2d57508660ff168360ff1611155b15613a39576001909301925b6001016138c2565b509198975050505050505050565b60606000613a5d8484612404565b90508067ffffffffffffffff81118015613a7657600080fd5b50604051908082528060200260200182016040528015613aa0578160200160208202803683370190505b506001600160a01b0385166000908152606860205260408120919350805b613ac783613f70565b811015613b3c576000613ae0848363ffffffff613f7b16565b6001600160a01b03898116600090815260676020908152604080832085845290915290205491925088811691161415613b335780868480600101955081518110613b2657fe5b6020026020010181815250505b50600101613abe565b5050505092915050565b6000611721613b5584846116f2565b613b5f8585610fcb565b9063ffffffff61458616565b613b76600033612a1d565b613bb3576040805162461bcd60e51b81526020600482015260096024820152682737ba1030b236b4b760b91b604482015290519081900360640190fd5b607780546001600160a01b0319166001600160a01b0392909216919091179055565b6060606981613be382613f70565b67ffffffffffffffff81118015613bf957600080fd5b50604051908082528060200260200182016040528015613c23578160200160208202803683370190505b50905060005b81518110156111e757613c42838263ffffffff613f7b16565b828281518110613c4e57fe5b6001600160a01b0390921660209283029190910190910152600101613c29565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a019020613c979033612a1d565b613cd9576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b61179060708263ffffffff6145e016565b336000908152606c602052604090205460ff1615613d42576040805162461bcd60e51b815260206004820152601060248201526f466f7262696464656e2061636365737360801b604482015290519081900360640190fd5b8282613d5560698363ffffffff613f4f16565b8015613d8457506001600160a01b0382166000908152606860205260409020613d84908263ffffffff613f6416565b613dcb576040805162461bcd60e51b8152602060048201526013602482015272151bdad95b881251081b9bdd081b1a5cdd1959606a1b604482015290519081900360640190fd5b6001600160a01b03808616600090815260676020908152604080832088845290915290205486918691163314613e38576040805162461bcd60e51b815260206004820152600d60248201526c1058d8d95cdcc819195b9a5959609a1b604482015290519081900360640190fd5b6075546000600160801b909104600f90810b900b1315613e6c57607554613e6c906118b890600160801b9004600f0b611009565b6001600160a01b03871660008181526067602090815260408083208a84528252918290206001018890558151888152915189939233927f35717041cfb73c9917e328a2cdd6bae61d669e3c8b024b049188bb5cd35d00cb92918290030190a450505050505050565b6000610ff360708363ffffffff613f4f16565b600081613ef657506000610ff3565b600083600f0b1215613f0757600080fd5b600f83900b6001600160801b038316810260401c90608084901c026001600160c01b03811115613f3657600080fd5b60401b8119811115613f4757600080fd5b019392505050565b6000611721836001600160a01b0384166145f5565b600061172183836145f5565b6000610ff38261460d565b60006117218383614611565b600081613f9357600080fd5b6000613f9f8484614675565b90506f7fffffffffffffffffffffffffffffff6001600160801b038216111561172157600080fd5b3390565b6000828152603360205260409020613fe9908263ffffffff61457116565b1561179057613ff6613fc7565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000614045836147be565b8015611721575061172183836147f1565b60665460655461407b916001600160a01b03918216913391168463ffffffff61481716565b606654604080516321407c4d60e01b81526004810184905290516001600160a01b03909216916321407c4d9160248082019260009290919082900301818387803b1580156140c857600080fd5b505af11580156140dc573d6000803e3d6000fd5b5050505050565b60006117218383614871565b6001600160a01b038116600090815260686020526040812061411090613f70565b11156141245761411f816148bb565b61253f565b61253f81614970565b600082815260336020526040902061414b908263ffffffff6145e016565b1561179057614158613fc7565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b6000828211156141f3576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b600061420430614981565b15905090565b600054610100900460ff168061422357506142236141f9565b80614231575060005460ff16155b61426c5760405162461bcd60e51b815260040180806020018281038252602e815260200180614fa8602e913960400191505060405180910390fd5b600054610100900460ff16158015614297576000805460ff1961ff0019909116610100171660011790555b61429f614987565b6142a7614987565b801561253f576000805461ff001916905550565b60006117218383614a27565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052612204908490614aed565b600081600f0b1215614372576040805162461bcd60e51b815260206004820181905260248201527f5765277265206e6f742072756e6e696e67206120636861726974792068657265604482015290519081900360640190fd5b6001600160a01b03919091166000908152606d6020908152604080832080546001600160801b0319166001600160801b03600f9690960b958616179055606e90915290208054911560ff19909216919091179055565b60006143d2614ee9565b60006143de8686613b46565b90508381111561442c576040805162461bcd60e51b8152602060048201526014602482015273427579696e6720707269636520746f6f206c6f7760601b604482015290519081900360640190fd5b614434614ee9565b506001600160a01b0380871660009081526067602090815260408083208984528252808320815180830183528154909516808652600190910154858401528352606c90915290205460ff16156144c1576040805162461bcd60e51b815260206004820152600d60248201526c2130b73732b21039b2b63632b960991b604482015290519081900360640190fd5b60006144cd88886116f2565b6001600160a01b03891660008181526067602090815260408083208c8452825280832080546001600160a01b0319168155600101839055928252606890522090915061451f908863ffffffff6142bb16565b50614529886140ef565b61453281614056565b815161456490339061454a868563ffffffff61419c16565b6065546001600160a01b031692919063ffffffff61481716565b5090969095509350505050565b6000611721836001600160a01b038416614871565b600082820183811015611721576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000611721836001600160a01b038416614a27565b60009081526001919091016020526040902054151590565b5490565b815460009082106146535760405162461bcd60e51b8152600401808060200182810382526022815260200180614f016022913960400191505060405180910390fd5b82600001828154811061466257fe5b9060005260206000200154905092915050565b60008161468157600080fd5b60006001600160c01b0384116146a65782604085901b8161469e57fe5b0490506147aa565b60c084811c600160201b81106146be576020918201911c5b6201000081106146d0576010918201911c5b61010081106146e1576008918201911c5b601081106146f1576004918201911c5b60048110614701576002918201911c5b60028110614710576001820191505b60bf820360018603901c6001018260ff0387901b8161472b57fe5b0492506001600160801b0383111561474257600080fd5b608085901c83026001600160801b038616840260c088901c604089901b8281101561476e576001820391505b608084901b92900382811015614785576001820391505b829003608084901c821461479557fe5b88818161479e57fe5b04870196505050505050505b6001600160801b0381111561172157600080fd5b60006147d1826301ffc9a760e01b6147f1565b8015610ff357506147ea826001600160e01b03196147f1565b1592915050565b60008060006148008585614b9e565b9150915081801561480e5750805b95945050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526123fe908590614aed565b600061487d83836145f5565b6148b357508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610ff3565b506000610ff3565b6148cc60698263ffffffff613f4f16565b61253f576148e160698263ffffffff61457116565b506001600160a01b0381166000908152606d6020526040902054600f90810b900b15801561492857506001600160a01b0381166000908152606e602052604090205460ff16155b1561253f57606f546001600160a01b0382166000908152606d602052604090208054600f92830b90920b6001600160801b03166001600160801b031990921691909117905550565b61179060698263ffffffff6145e016565b3b151590565b600054610100900460ff16806149a057506149a06141f9565b806149ae575060005460ff16155b6149e95760405162461bcd60e51b815260040180806020018281038252602e815260200180614fa8602e913960400191505060405180910390fd5b600054610100900460ff161580156142a7576000805460ff1961ff001990911661010017166001179055801561253f576000805461ff001916905550565b60008181526001830160205260408120548015614ae35783546000198083019190810190600090879083908110614a5a57fe5b9060005260206000200154905080876000018481548110614a7757fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080614aa757fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610ff3565b6000915050610ff3565b6060614b42826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614cd29092919063ffffffff16565b80519091501561220457808060200190516020811015614b6157600080fd5b50516122045760405162461bcd60e51b815260040180806020018281038252602a815260200180614ffd602a913960400191505060405180910390fd5b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b1781529151815160009384939284926060926001600160a01b038a169261753092879282918083835b60208310614c265780518252601f199092019160209182019101614c07565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114614c87576040519150601f19603f3d011682016040523d82523d6000602084013e614c8c565b606091505b5091509150602081511015614caa5760008094509450505050614ccb565b81818060200190516020811015614cc057600080fd5b505190955093505050505b9250929050565b6060614ce18484600085614ce9565b949350505050565b606082471015614d2a5760405162461bcd60e51b8152600401808060200182810382526026815260200180614f526026913960400191505060405180910390fd5b614d3385614981565b614d84576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310614dc35780518252601f199092019160209182019101614da4565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114614e25576040519150601f19603f3d011682016040523d82523d6000602084013e614e2a565b606091505b5091509150614e3a828286614e45565b979650505050505050565b60608315614e54575081611721565b825115614e645782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614eae578181015183820152602001614e96565b50505050905090810190601f168015614edb5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b60408051808201909152600080825260208201529056fe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e6473416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f206772616e74416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f207265766f6b65496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a6564546869732074797065206f66204e4654206d6179206e6f742062652074726164656420686572655361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a26469706673582212201bb4a9d828b936122508c20f6940726f6bb670a76eac0db2c7746b6ff04b6ff864736f6c63430006050033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103d95760003560e01c806374c7b3281161020a578063bb782e7311610125578063d547741f116100b8578063da8e8ca211610087578063da8e8ca214610f1f578063e4ca10dc14610f45578063e79767af14610f4d578063ed9999ca14610f73578063f9eaee0d14610fa5576103d9565b8063d547741f14610e58578063d679756214610e84578063d739d8bc14610ec5578063d9db220d14610ef3576103d9565b8063cb8da193116100f4578063cb8da19314610dc8578063cc8efb1414610e0d578063d1ebdfd314610e33578063d23727a314610e3b576103d9565b8063bb782e7314610d34578063c573a97e14610d5a578063c7e4ff4214610d7d578063ca15c87314610dab576103d9565b80639642b3f71161019d578063a5bc36321161016c578063a5bc363214610c7e578063a6f9572614610cb0578063b2ddee0614610ce2578063b53472ef14610d0e576103d9565b80639642b3f714610c0157806399bd17e414610c245780639ed9b4fd14610c50578063a217fddf14610c76576103d9565b80637ff57c57116101d95780637ff57c5714610b4457806386c51fa914610b725780639010d07c14610b9e57806391d1485414610bc1576103d9565b806374c7b32814610a595780637899d80f14610a8857806378e3079e14610aae578063799e6b6114610ad4576103d9565b8063346710fd116102fa578063490df5301161028d57806364d678681161025c57806364d67868146109f557806366a417fe14610a125780636891de0714610a49578063737ea06e14610a51576103d9565b8063490df53014610924578063534be8fb14610941578063546b45ac146109bf5780635504d3d2146109ed576103d9565b80633b75a46a116102c95780633b75a46a146108a85780633f7e1dd5146108c8578063470e50c5146108d0578063485cc955146108f6576103d9565b8063346710fd146107cd57806335e0406f1461080757806336568abe1461082d57806339378fb314610859576103d9565b80631eb788f5116103725780632c2dea77116103415780632c2dea77146107375780632df8c04a1461076d5780632f2ff15d146107995780632f48f151146107c5576103d9565b80631eb788f5146105ab578063248a9ca3146106c257806327314473146106df578063281fb1651461070b576103d9565b8063150b7a02116103ae578063150b7a021461046557806316a71cfc146105105780631a10c902146105345780631ac0bab01461058c576103d9565b80621e9187146103de57806240ff6c1461041c5780630a8a92151461043d5780630bb99c571461045d575b600080fd5b61040a600480360360408110156103f457600080fd5b506001600160a01b038135169060200135610fcb565b60408051918252519081900360200190f35b610424610ff9565b60408051600f92830b90920b8252519081900360200190f35b61040a6004803603602081101561045357600080fd5b5035600f0b611009565b610424611099565b6104f36004803603608081101561047b57600080fd5b6001600160a01b03823581169260208101359091169160408201359190810190608081016060820135600160201b8111156104b557600080fd5b8201836020820111156104c757600080fd5b803590602001918460018302840111600160201b831117156104e857600080fd5b5090925090506110a2565b604080516001600160e01b03199092168252519081900360200190f35b61051861113f565b604080516001600160a01b039092168252519081900360200190f35b61053c61114e565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610578578181015183820152602001610560565b505050509050019250505060405180910390f35b6105a9600480360360208110156105a257600080fd5b50356111ee565b005b6105dd600480360360608110156105c157600080fd5b506001600160a01b03813516906020810135906040013561128c565b60405180858152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b8381101561062b578181015183820152602001610613565b50505050905001848103835286818151815260200191508051906020019060200280838360005b8381101561066a578181015183820152602001610652565b50505050905001848103825285818151815260200191508051906020019060200280838360005b838110156106a9578181015183820152602001610691565b5050505090500197505050505050505060405180910390f35b61040a600480360360208110156106d857600080fd5b5035611475565b610518600480360360408110156106f557600080fd5b506001600160a01b03813516906020013561148a565b6105186004803603604081101561072157600080fd5b506001600160a01b038135169060200135611506565b6105a96004803603606081101561074d57600080fd5b506001600160a01b0381358116916020810135916040909101351661152e565b61040a6004803603604081101561078357600080fd5b506001600160a01b0381351690602001356116f2565b6105a9600480360360408110156107af57600080fd5b50803590602001356001600160a01b0316611728565b610424611794565b6105a9600480360360808110156107e357600080fd5b506001600160a01b038135811691602081013591604082013591606001351661179d565b61053c6004803603602081101561081d57600080fd5b50356001600160a01b0316611bee565b6105a96004803603604081101561084357600080fd5b50803590602001356001600160a01b0316611c99565b61053c600480360360c081101561086f57600080fd5b506001600160a01b038135169060ff60208201358116916040810135916060820135811691608081013582169160a09091013516611cfa565b6105a9600480360360208110156108be57600080fd5b5035600f0b611f84565b610518612016565b6105a9600480360360208110156108e657600080fd5b50356001600160a01b0316612025565b6105a96004803603604081101561090c57600080fd5b506001600160a01b03813581169160200135166120f1565b6105a96004803603602081101561093a57600080fd5b5035612209565b6105a96004803603604081101561095757600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561098157600080fd5b82018360208201111561099357600080fd5b803590602001918460208302840111600160201b831117156109b457600080fd5b5090925090506122f1565b61040a600480360360408110156109d557600080fd5b506001600160a01b0381358116916020013516612404565b61040a612495565b6105a960048036036020811015610a0b57600080fd5b50356124b7565b61040a60048036036060811015610a2857600080fd5b506001600160a01b038135169060ff60208201358116916040013516612542565b61040a6126cb565b6105186126ee565b6105a960048036036040811015610a6f57600080fd5b506001600160a01b038135169060200135600f0b6126fd565b61042460048036036020811015610a9e57600080fd5b50356001600160a01b031661278d565b6105a960048036036020811015610ac457600080fd5b50356001600160a01b03166127a2565b6105a960048036036040811015610aea57600080fd5b810190602081018135600160201b811115610b0457600080fd5b820183602082011115610b1657600080fd5b803590602001918460208302840111600160201b83111715610b3757600080fd5b919350915035151561282f565b6105a960048036036040811015610b5a57600080fd5b506001600160a01b03813581169160200135166128ef565b6105a960048036036040811015610b8857600080fd5b506001600160a01b038135169060200135612965565b61051860048036036040811015610bb457600080fd5b50803590602001356129ff565b610bed60048036036040811015610bd757600080fd5b50803590602001356001600160a01b0316612a1d565b604080519115158252519081900360200190f35b6105a960048036036040811015610c1757600080fd5b5080359060200135612a3b565b6105a960048036036040811015610c3a57600080fd5b506001600160a01b038135169060200135612caf565b610bed60048036036020811015610c6657600080fd5b50356001600160a01b0316612d69565b61040a612d7e565b6105a960048036036060811015610c9457600080fd5b506001600160a01b038135169060208101359060400135612d83565b6105a960048036036060811015610cc657600080fd5b506001600160a01b038135169060208101359060400135612e17565b6105a960048036036040811015610cf857600080fd5b506001600160a01b038135169060200135613078565b6105a960048036036020811015610d2457600080fd5b50356001600160a01b03166132fa565b610bed60048036036020811015610d4a57600080fd5b50356001600160a01b0316613391565b6105a960048036036040811015610d7057600080fd5b50803590602001356133a6565b6105a960048036036040811015610d9357600080fd5b506001600160a01b0381351690602001351515613444565b61040a60048036036020811015610dc157600080fd5b50356134da565b61053c600480360360a0811015610dde57600080fd5b506001600160a01b038135169060ff6020820135811691604081013591606082013581169160800135166134f1565b61040a60048036036020811015610e2357600080fd5b50356001600160a01b0316613759565b61051861377a565b6105a960048036036020811015610e5157600080fd5b5035613789565b6105a960048036036040811015610e6e57600080fd5b50803590602001356001600160a01b031661384c565b61040a60048036036080811015610e9a57600080fd5b506001600160a01b038135169060ff60208201358116916040810135821691606090910135166138a5565b61053c60048036036040811015610edb57600080fd5b506001600160a01b0381358116916020013516613a4f565b61040a60048036036040811015610f0957600080fd5b506001600160a01b038135169060200135613b46565b6105a960048036036020811015610f3557600080fd5b50356001600160a01b0316613b6b565b61053c613bd5565b6105a960048036036020811015610f6357600080fd5b50356001600160a01b0316613c6e565b6105a960048036036060811015610f8957600080fd5b506001600160a01b038135169060208101359060400135613cea565b610bed60048036036020811015610fbb57600080fd5b50356001600160a01b0316613ed4565b6001600160a01b03821660009081526067602090815260408083208484529091529020600101545b92915050565b607554600160801b9004600f0b81565b6000610ff3607460009054906101000a90046001600160a01b03166001600160a01b0316639d1b464a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561105c57600080fd5b505afa158015611070573d6000803e3d6000fd5b505050506040513d602081101561108657600080fd5b5051600f84900b9063ffffffff613ee716565b606f54600f0b81565b6000336110b660698263ffffffff613f4f16565b80156110e557506001600160a01b03811660009081526068602052604090206110e5908663ffffffff613f6416565b61112c576040805162461bcd60e51b8152602060048201526013602482015272151bdad95b881251081b9bdd081b1a5cdd1959606a1b604482015290519081900360640190fd5b50630a85bd0160e11b9695505050505050565b6077546001600160a01b031681565b606060708161115c82613f70565b67ffffffffffffffff8111801561117257600080fd5b5060405190808252806020026020018201604052801561119c578160200160208202803683370190505b50905060005b81518110156111e7576111bb838263ffffffff613f7b16565b8282815181106111c757fe5b6001600160a01b03909216602092830291909101909101526001016111a2565b5091505090565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206112179033612a1d565b611259576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b611264816064613f87565b606f8054600f9290920b6001600160801b03166001600160801b031990921691909117905550565b80606080808367ffffffffffffffff811180156112a857600080fd5b506040519080825280602002602001820160405280156112d2578160200160208202803683370190505b5092508467ffffffffffffffff811180156112ec57600080fd5b50604051908082528060200260200182016040528015611316578160200160208202803683370190505b5091508467ffffffffffffffff8111801561133057600080fd5b5060405190808252806020026020018201604052801561135a578160200160208202803683370190505b506001600160a01b038816600090815260686020526040812091925090875b8789018110156114685761138c82613f70565b811061139d575090945061146c9050565b60006113af838363ffffffff613f7b16565b90506113b9614ee9565b506001600160a01b03808c166000908152606760209081526040808320858452825291829020825180840190935280549093168252600190920154918101919091528751829089908790811061140b57fe5b602002602001018181525050806000015187868151811061142857fe5b6001600160a01b03909216602092830291909101820152810151865160018701968891811061145357fe5b60209081029190910101525050600101611379565b5050505b93509350935093565b60009081526033602052604090206002015490565b600061149d60698463ffffffff613f4f16565b6114a957506000610ff3565b6001600160a01b03831660009081526068602052604090206114d1908363ffffffff613f6416565b6114dd57506000610ff3565b506001600160a01b03918216600090815260676020908152604080832093835292905220541690565b6001600160a01b03918216600090815260766020908152604080832093835292905220541690565b336000908152606c602052604090205460ff1615611586576040805162461bcd60e51b815260206004820152601060248201526f466f7262696464656e2061636365737360801b604482015290519081900360640190fd5b828261159960698363ffffffff613f4f16565b80156115c857506001600160a01b03821660009081526068602052604090206115c8908263ffffffff613f6416565b61160f576040805162461bcd60e51b8152602060048201526013602482015272151bdad95b881251081b9bdd081b1a5cdd1959606a1b604482015290519081900360640190fd5b6001600160a01b0380861660009081526067602090815260408083208884529091529020548691869116331461167c576040805162461bcd60e51b815260206004820152600d60248201526c1058d8d95cdcc819195b9a5959609a1b604482015290519081900360640190fd5b6001600160a01b0387811660008181526076602090815260408083208b845282529182902080546001600160a01b031916948a16948517905581519384529051899333927faa79ba781ed370c2a15b6fe1a86316cab25598fecac3329356cc3f5557c8bae692918290030190a450505050505050565b6001600160a01b0382166000908152606d602052604081205461172190600f0b61171c8585610fcb565b613ee7565b9392505050565b60008281526033602052604090206002015461174b90611746613fc7565b612a1d565b6117865760405162461bcd60e51b815260040180806020018281038252602f815260200180614f23602f913960400191505060405180910390fd5b6117908282613fcb565b5050565b607554600f0b81565b836117a781613ed4565b6117e25760405162461bcd60e51b8152600401808060200182810382526027815260200180614fd66027913960400191505060405180910390fd5b846117f4816380ac58cd60e01b61403a565b6117fd57600080fd5b858561181060698363ffffffff613f4f16565b158061184157506001600160a01b038216600090815260686020526040902061183f908263ffffffff613f6416565b155b611892576040805162461bcd60e51b815260206004820152601b60248201527f546f6b656e204944206d757374206e6f74206265206c69737465640000000000604482015290519081900360640190fd5b6075546000600f91820b90910b13156118bd576075546118bd906118b890600f0b611009565b614056565b336000908152606c602052604090205460ff1615611a985760655460408051636eb1769f60e11b815233600482015230602482015290516000926001600160a01b03169163dd62ed3e916044808301926020929190829003018186803b15801561192657600080fd5b505afa15801561193a573d6000803e3d6000fd5b505050506040513d602081101561195057600080fd5b5051606554604080516370a0823160e01b815233600482015290519293506000926001600160a01b03909216916370a0823191602480820192602092909190829003018186803b1580156119a357600080fd5b505afa1580156119b7573d6000803e3d6000fd5b505050506040513d60208110156119cd57600080fd5b50516065546066549192506001600160a01b03908116916323b872dd913391168486116119fa57856119fc565b845b6040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160a01b03166001600160a01b031681526020018281526020019350505050602060405180830381600087803b158015611a6457600080fd5b505af1158015611a78573d6000803e3d6000fd5b505050506040513d6020811015611a8e57600080fd5b50611b2f92505050565b60408051808201825233815260208082018981526001600160a01b038c81166000818152606785528681208e82528552868120955186549084166001600160a01b03199182161787559351600190960195909555808552607684528585208d865284528585208054928c16929093169190911790915582526068905220611b25908863ffffffff6140e316565b50611b2f886140ef565b60408051632142170760e11b81523360048201523060248201526044810189905290516001600160a01b038a16916342842e0e91606480830192600092919082900301818387803b158015611b8357600080fd5b505af1158015611b97573d6000803e3d6000fd5b5050604080518981526001600160a01b03898116602083015282518c9550908d16935033927f5949a8af96064ae9b490d9118c3636906cab7e3b2c46530c7e2b5ff9f8a297e1928290030190a45050505050505050565b6001600160a01b038116600090815260686020526040902060609081611c1382613f70565b67ffffffffffffffff81118015611c2957600080fd5b50604051908082528060200260200182016040528015611c53578160200160208202803683370190505b50905060005b8151811015611c9157611c72838263ffffffff613f7b16565b828281518110611c7e57fe5b6020908102919091010152600101611c59565b509392505050565b611ca1613fc7565b6001600160a01b0316816001600160a01b031614611cf05760405162461bcd60e51b815260040180806020018281038252602f815260200180615027602f913960400191505060405180910390fd5b611790828261412d565b6001600160a01b0386166000908152606860205260408120606091611d21898787876138a5565b905060ff88166001880102600081831015611d5057611d4b8360ff8c168b0263ffffffff61419c16565b611d55565b8960ff165b905060608167ffffffffffffffff81118015611d7057600080fd5b50604051908082528060200260200182016040528015611d9a578160200160208202803683370190505b509050600080805b611dab88613f70565b81108015611db857508583105b15611f71576073546000906001600160a01b031663acb586df611de18b8563ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015611e1557600080fd5b505afa158015611e29573d6000803e3d6000fd5b505050506040513d6020811015611e3f57600080fd5b50516073549091506000906001600160a01b03166386481d40611e688c8663ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015611e9c57600080fd5b505afa158015611eb0573d6000803e3d6000fd5b505050506040513d6020811015611ec657600080fd5b5051905060ff808f161480611ee057508d60ff168260ff16145b8015611f1e57508c60ff1660ff1480611efc57508b60ff1660ff145b80611f1e57508c60ff168160ff1610158015611f1e57508b60ff168160ff1611155b15611f67578f60ff1688038510611f6057611f3f8a8463ffffffff613f7b16565b868560ff1681518110611f4e57fe5b60209081029190910101526001909301925b6001909401935b5050600101611da2565b50919d9c50505050505050505050505050565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a019020611fad9033612a1d565b611fef576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b606f8054600f9290920b6001600160801b03166001600160801b0319909216919091179055565b6074546001600160a01b031681565b612030600033612a1d565b61206d576040805162461bcd60e51b81526020600482015260096024820152682737ba1030b236b4b760b91b604482015290519081900360640190fd5b607480546001600160a01b0319166001600160a01b03831617905561209460026064613f87565b60758054600f9290920b6001600160801b03166001600160801b03199092169190911790556120c560006064613f87565b607560106101000a8154816001600160801b030219169083600f0b6001600160801b0316021790555050565b600054610100900460ff168061210a575061210a6141f9565b80612118575060005460ff16155b6121535760405162461bcd60e51b815260040180806020018281038252602e815260200180614fa8602e913960400191505060405180910390fd5b600054610100900460ff1615801561217e576000805460ff1961ff0019909116610100171660011790555b61218661420a565b612191600033611786565b606580546001600160a01b038086166001600160a01b03199283161790925560668054928516929091169190911790556121cd6001600a613f87565b606f8054600f9290920b6001600160801b03166001600160801b03199092169190911790558015612204576000805461ff00191690555b505050565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206122329033612a1d565b612274576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b60648111156122be576040805162461bcd60e51b8152602060048201526011602482015270082c8c8acc2d8eaca40e8dede40d0d2ced607b1b604482015290519081900360640190fd5b6122c9816064613f87565b60758054600f9290920b6001600160801b03166001600160801b031990921691909117905550565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a01902061231a9033612a1d565b61235c576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b60005b818110156123fe576001600160a01b03841660009081526067602052604081209084848481811061238c57fe5b60209081029290920135835250810191909152604001600090812080546001600160a01b0319168155600101556123f58383838181106123c857fe5b6001600160a01b03881660009081526068602090815260409091209391020135905063ffffffff6142bb16565b5060010161235f565b50505050565b6001600160a01b038216600090815260686020526040812081805b61242883613f70565b81101561248c576001600160a01b038681166000908152606760205260408120918716919061245d868563ffffffff613f7b16565b81526020810191909152604001600020546001600160a01b03161415612484576001909101905b60010161241f565b50949350505050565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a01902081565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206124e09033612a1d565b612522576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b60655461253f906001600160a01b0316338363ffffffff6142c716565b50565b6001600160a01b0383166000908152606860205260408120818080805b61256885613f70565b8110156126be576072546001600160a01b031663acb586df612590878463ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156125c457600080fd5b505afa1580156125d8573d6000803e3d6000fd5b505050506040513d60208110156125ee57600080fd5b50516072549093506001600160a01b031663adf04f20612614878463ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561264857600080fd5b505afa15801561265c573d6000803e3d6000fd5b505050506040513d602081101561267257600080fd5b5051915060ff808916148061268c57508760ff168360ff16145b80156126aa57508660ff1660ff14806126aa57508660ff168260ff16145b156126b6576001909301925b60010161255f565b5091979650505050505050565b606f546000906126e990600f90810b900b606463ffffffff613ee716565b905090565b6066546001600160a01b031681565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206127269033612a1d565b612768576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b8161277a816380ac58cd60e01b61403a565b61278357600080fd5b6122048383614319565b606d60205260009081526040902054600f0b81565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206127cb9033612a1d565b61280d576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b606680546001600160a01b0319166001600160a01b0392909216919091179055565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206128589033612a1d565b61289a576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b60005b828110156123fe5781606c60008686858181106128b657fe5b602090810292909201356001600160a01b0316835250810191909152604001600020805460ff191691151591909117905560010161289d565b6128fa600033612a1d565b612937576040805162461bcd60e51b81526020600482015260096024820152682737ba1030b236b4b760b91b604482015290519081900360640190fd5b607380546001600160a01b039384166001600160a01b03199182161790915560728054929093169116179055565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a01902061298e9033612a1d565b6129d0576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b816129e2816380ac58cd60e01b61403a565b6129eb57600080fd5b612204836129fa846064613f87565b614319565b6000828152603360205260408120611721908363ffffffff613f7b16565b6000828152603360205260408120611721908363ffffffff613f4f16565b336000908152606c602052604090205460ff1615612a93576040805162461bcd60e51b815260206004820152601060248201526f466f7262696464656e2061636365737360801b604482015290519081900360640190fd5b6073546001600160a01b031682612ab160698363ffffffff613f4f16565b8015612ae057506001600160a01b0382166000908152606860205260409020612ae0908263ffffffff613f6416565b612b27576040805162461bcd60e51b8152602060048201526013602482015272151bdad95b881251081b9bdd081b1a5cdd1959606a1b604482015290519081900360640190fd5b6073546001600160a01b03908116600081815260766020908152604080832089845290915290205490918691161580612b8457506001600160a01b0382811660009081526076602090815260408083208584529091529020541633145b612bc8576040805162461bcd60e51b815260206004820152601060248201526f2737ba103a30b933b2ba10313abcb2b960811b604482015290519081900360640190fd5b6000612bd2614ee9565b607354612be9906001600160a01b031689896143c8565b6073548151604080516001600160a01b0392831681526020810186905281519597509395508c94919092169233927f7762d23dc0afc60972698afa4f7ab7e5853e961dad5c968fe29b0fd3b14fffb592918290030190a460775460408051634389afa960e01b8152600481018b905290516001600160a01b0390921691634389afa99160248082019260009290919082900301818387803b158015612c8d57600080fd5b505af1158015612ca1573d6000803e3d6000fd5b505050505050505050505050565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a019020612cd89033612a1d565b612d1a576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b6001600160a01b0382166000818152606760209081526040808320858452825280832080546001600160a01b03191681556001018390559282526068905220612204908263ffffffff6142bb16565b606c6020526000908152604090205460ff1681565b600081565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a019020612dac9033612a1d565b612dee576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b82612e00816380ac58cd60e01b61403a565b612e0957600080fd5b6123fe846129fa8585613f87565b336000908152606c602052604090205460ff1615612e6f576040805162461bcd60e51b815260206004820152601060248201526f466f7262696464656e2061636365737360801b604482015290519081900360640190fd5b8282612e8260698363ffffffff613f4f16565b8015612eb157506001600160a01b0382166000908152606860205260409020612eb1908263ffffffff613f6416565b612ef8576040805162461bcd60e51b8152602060048201526013602482015272151bdad95b881251081b9bdd081b1a5cdd1959606a1b604482015290519081900360640190fd5b6001600160a01b03808616600090815260766020908152604080832088845290915290205486918691161580612f5257506001600160a01b0382811660009081526076602090815260408083208584529091529020541633145b612f96576040805162461bcd60e51b815260206004820152601060248201526f2737ba103a30b933b2ba10313abcb2b960811b604482015290519081900360640190fd5b6000612fa0614ee9565b612fab8989896143c8565b60408051632142170760e11b8152306004820152336024820152604481018c905290519294509092506001600160a01b038b16916342842e0e9160648082019260009290919082900301818387803b15801561300657600080fd5b505af115801561301a573d6000803e3d6000fd5b50508251604080516001600160a01b0392831681526020810187905281518d9550928e16935033927f7762d23dc0afc60972698afa4f7ab7e5853e961dad5c968fe29b0fd3b14fffb5929181900390910190a4505050505050505050565b336000908152606c602052604090205460ff16156130d0576040805162461bcd60e51b815260206004820152601060248201526f466f7262696464656e2061636365737360801b604482015290519081900360640190fd5b81816130e360698363ffffffff613f4f16565b801561311257506001600160a01b0382166000908152606860205260409020613112908263ffffffff613f6416565b613159576040805162461bcd60e51b8152602060048201526013602482015272151bdad95b881251081b9bdd081b1a5cdd1959606a1b604482015290519081900360640190fd5b6001600160a01b03808516600090815260676020908152604080832087845290915290205485918591163314806131b45750604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206131b49033612a1d565b6131f5576040805162461bcd60e51b815260206004820152600d60248201526c1058d8d95cdcc819195b9a5959609a1b604482015290519081900360640190fd5b6001600160a01b0386166000818152606760209081526040808320898452825280832080546001600160a01b03191681556001018390559282526068905220613244908663ffffffff6142bb16565b5061324e866140ef565b60408051632142170760e11b81523060048201523360248201526044810187905290516001600160a01b038816916342842e0e91606480830192600092919082900301818387803b1580156132a257600080fd5b505af11580156132b6573d6000803e3d6000fd5b50506040518792506001600160a01b038916915033907fd66e7fc4b7515678684c4cbafb29ad2dbe872f0d8913ac2227c7cfead5d6923490600090a4505050505050565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206133239033612a1d565b613365576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b80613377816380ac58cd60e01b61403a565b61338057600080fd5b61220460708363ffffffff61457116565b606b6020526000908152604090205460ff1681565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206133cf9033612a1d565b613411576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b61341b8282613f87565b606f8054600f9290920b6001600160801b03166001600160801b03199092169190911790555050565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a01902061346d9033612a1d565b6134af576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b6001600160a01b03919091166000908152606c60205260409020805460ff1916911515919091179055565b6000818152603360205260408120610ff390613f70565b6001600160a01b0385166000908152606860205260408120606091613517888686612542565b905060ff87166001870102600081831015613546576135418360ff8b168a0263ffffffff61419c16565b61354b565b8860ff165b905060608167ffffffffffffffff8111801561356657600080fd5b50604051908082528060200260200182016040528015613590578160200160208202803683370190505b509050600080805b6135a188613f70565b811080156135ae57508583105b15613747576072546000906001600160a01b031663acb586df6135d78b8563ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561360b57600080fd5b505afa15801561361f573d6000803e3d6000fd5b505050506040513d602081101561363557600080fd5b50516072549091506000906001600160a01b031663adf04f2061365e8c8663ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561369257600080fd5b505afa1580156136a6573d6000803e3d6000fd5b505050506040513d60208110156136bc57600080fd5b5051905060ff808e1614806136d657508c60ff168260ff16145b80156136f457508b60ff1660ff14806136f457508b60ff168160ff16145b1561373d578e60ff1688038510613736576137158a8463ffffffff613f7b16565b868560ff168151811061372457fe5b60209081029190910101526001909301925b6001909401935b5050600101613598565b50919c9b505050505050505050505050565b6001600160a01b0381166000908152606860205260408120610ff390613f70565b6065546001600160a01b031681565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a0190206137b29033612a1d565b6137f4576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b6064811115613841576040805162461bcd60e51b8152602060048201526014602482015273086d0c2dccecaacc2d8eaca40e8dede40d0d2ced60631b604482015290519081900360640190fd5b6120c5816064613f87565b60008281526033602052604090206002015461386a90611746613fc7565b611cf05760405162461bcd60e51b8152600401808060200182810382526030815260200180614f786030913960400191505060405180910390fd5b6001600160a01b0384166000908152606860205260408120818080805b6138cb85613f70565b811015613a41576073546001600160a01b03166386481d406138f3878463ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561392757600080fd5b505afa15801561393b573d6000803e3d6000fd5b505050506040513d602081101561395157600080fd5b50516073549093506001600160a01b031663acb586df613977878463ffffffff613f7b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156139ab57600080fd5b505afa1580156139bf573d6000803e3d6000fd5b505050506040513d60208110156139d557600080fd5b5051915060ff808a1614806139ef57508860ff168260ff16145b8015613a2d57508760ff1660ff1480613a0b57508660ff1660ff145b80613a2d57508760ff168360ff1610158015613a2d57508660ff168360ff1611155b15613a39576001909301925b6001016138c2565b509198975050505050505050565b60606000613a5d8484612404565b90508067ffffffffffffffff81118015613a7657600080fd5b50604051908082528060200260200182016040528015613aa0578160200160208202803683370190505b506001600160a01b0385166000908152606860205260408120919350805b613ac783613f70565b811015613b3c576000613ae0848363ffffffff613f7b16565b6001600160a01b03898116600090815260676020908152604080832085845290915290205491925088811691161415613b335780868480600101955081518110613b2657fe5b6020026020010181815250505b50600101613abe565b5050505092915050565b6000611721613b5584846116f2565b613b5f8585610fcb565b9063ffffffff61458616565b613b76600033612a1d565b613bb3576040805162461bcd60e51b81526020600482015260096024820152682737ba1030b236b4b760b91b604482015290519081900360640190fd5b607780546001600160a01b0319166001600160a01b0392909216919091179055565b6060606981613be382613f70565b67ffffffffffffffff81118015613bf957600080fd5b50604051908082528060200260200182016040528015613c23578160200160208202803683370190505b50905060005b81518110156111e757613c42838263ffffffff613f7b16565b828281518110613c4e57fe5b6001600160a01b0390921660209283029190910190910152600101613c29565b604080516923a0a6a2afa0a226a4a760b11b8152905190819003600a019020613c979033612a1d565b613cd9576040805162461bcd60e51b815260206004820152600e60248201526d2737ba1033b0b6b29030b236b4b760911b604482015290519081900360640190fd5b61179060708263ffffffff6145e016565b336000908152606c602052604090205460ff1615613d42576040805162461bcd60e51b815260206004820152601060248201526f466f7262696464656e2061636365737360801b604482015290519081900360640190fd5b8282613d5560698363ffffffff613f4f16565b8015613d8457506001600160a01b0382166000908152606860205260409020613d84908263ffffffff613f6416565b613dcb576040805162461bcd60e51b8152602060048201526013602482015272151bdad95b881251081b9bdd081b1a5cdd1959606a1b604482015290519081900360640190fd5b6001600160a01b03808616600090815260676020908152604080832088845290915290205486918691163314613e38576040805162461bcd60e51b815260206004820152600d60248201526c1058d8d95cdcc819195b9a5959609a1b604482015290519081900360640190fd5b6075546000600160801b909104600f90810b900b1315613e6c57607554613e6c906118b890600160801b9004600f0b611009565b6001600160a01b03871660008181526067602090815260408083208a84528252918290206001018890558151888152915189939233927f35717041cfb73c9917e328a2cdd6bae61d669e3c8b024b049188bb5cd35d00cb92918290030190a450505050505050565b6000610ff360708363ffffffff613f4f16565b600081613ef657506000610ff3565b600083600f0b1215613f0757600080fd5b600f83900b6001600160801b038316810260401c90608084901c026001600160c01b03811115613f3657600080fd5b60401b8119811115613f4757600080fd5b019392505050565b6000611721836001600160a01b0384166145f5565b600061172183836145f5565b6000610ff38261460d565b60006117218383614611565b600081613f9357600080fd5b6000613f9f8484614675565b90506f7fffffffffffffffffffffffffffffff6001600160801b038216111561172157600080fd5b3390565b6000828152603360205260409020613fe9908263ffffffff61457116565b1561179057613ff6613fc7565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000614045836147be565b8015611721575061172183836147f1565b60665460655461407b916001600160a01b03918216913391168463ffffffff61481716565b606654604080516321407c4d60e01b81526004810184905290516001600160a01b03909216916321407c4d9160248082019260009290919082900301818387803b1580156140c857600080fd5b505af11580156140dc573d6000803e3d6000fd5b5050505050565b60006117218383614871565b6001600160a01b038116600090815260686020526040812061411090613f70565b11156141245761411f816148bb565b61253f565b61253f81614970565b600082815260336020526040902061414b908263ffffffff6145e016565b1561179057614158613fc7565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b6000828211156141f3576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b600061420430614981565b15905090565b600054610100900460ff168061422357506142236141f9565b80614231575060005460ff16155b61426c5760405162461bcd60e51b815260040180806020018281038252602e815260200180614fa8602e913960400191505060405180910390fd5b600054610100900460ff16158015614297576000805460ff1961ff0019909116610100171660011790555b61429f614987565b6142a7614987565b801561253f576000805461ff001916905550565b60006117218383614a27565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052612204908490614aed565b600081600f0b1215614372576040805162461bcd60e51b815260206004820181905260248201527f5765277265206e6f742072756e6e696e67206120636861726974792068657265604482015290519081900360640190fd5b6001600160a01b03919091166000908152606d6020908152604080832080546001600160801b0319166001600160801b03600f9690960b958616179055606e90915290208054911560ff19909216919091179055565b60006143d2614ee9565b60006143de8686613b46565b90508381111561442c576040805162461bcd60e51b8152602060048201526014602482015273427579696e6720707269636520746f6f206c6f7760601b604482015290519081900360640190fd5b614434614ee9565b506001600160a01b0380871660009081526067602090815260408083208984528252808320815180830183528154909516808652600190910154858401528352606c90915290205460ff16156144c1576040805162461bcd60e51b815260206004820152600d60248201526c2130b73732b21039b2b63632b960991b604482015290519081900360640190fd5b60006144cd88886116f2565b6001600160a01b03891660008181526067602090815260408083208c8452825280832080546001600160a01b0319168155600101839055928252606890522090915061451f908863ffffffff6142bb16565b50614529886140ef565b61453281614056565b815161456490339061454a868563ffffffff61419c16565b6065546001600160a01b031692919063ffffffff61481716565b5090969095509350505050565b6000611721836001600160a01b038416614871565b600082820183811015611721576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000611721836001600160a01b038416614a27565b60009081526001919091016020526040902054151590565b5490565b815460009082106146535760405162461bcd60e51b8152600401808060200182810382526022815260200180614f016022913960400191505060405180910390fd5b82600001828154811061466257fe5b9060005260206000200154905092915050565b60008161468157600080fd5b60006001600160c01b0384116146a65782604085901b8161469e57fe5b0490506147aa565b60c084811c600160201b81106146be576020918201911c5b6201000081106146d0576010918201911c5b61010081106146e1576008918201911c5b601081106146f1576004918201911c5b60048110614701576002918201911c5b60028110614710576001820191505b60bf820360018603901c6001018260ff0387901b8161472b57fe5b0492506001600160801b0383111561474257600080fd5b608085901c83026001600160801b038616840260c088901c604089901b8281101561476e576001820391505b608084901b92900382811015614785576001820391505b829003608084901c821461479557fe5b88818161479e57fe5b04870196505050505050505b6001600160801b0381111561172157600080fd5b60006147d1826301ffc9a760e01b6147f1565b8015610ff357506147ea826001600160e01b03196147f1565b1592915050565b60008060006148008585614b9e565b9150915081801561480e5750805b95945050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526123fe908590614aed565b600061487d83836145f5565b6148b357508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610ff3565b506000610ff3565b6148cc60698263ffffffff613f4f16565b61253f576148e160698263ffffffff61457116565b506001600160a01b0381166000908152606d6020526040902054600f90810b900b15801561492857506001600160a01b0381166000908152606e602052604090205460ff16155b1561253f57606f546001600160a01b0382166000908152606d602052604090208054600f92830b90920b6001600160801b03166001600160801b031990921691909117905550565b61179060698263ffffffff6145e016565b3b151590565b600054610100900460ff16806149a057506149a06141f9565b806149ae575060005460ff16155b6149e95760405162461bcd60e51b815260040180806020018281038252602e815260200180614fa8602e913960400191505060405180910390fd5b600054610100900460ff161580156142a7576000805460ff1961ff001990911661010017166001179055801561253f576000805461ff001916905550565b60008181526001830160205260408120548015614ae35783546000198083019190810190600090879083908110614a5a57fe5b9060005260206000200154905080876000018481548110614a7757fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080614aa757fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610ff3565b6000915050610ff3565b6060614b42826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614cd29092919063ffffffff16565b80519091501561220457808060200190516020811015614b6157600080fd5b50516122045760405162461bcd60e51b815260040180806020018281038252602a815260200180614ffd602a913960400191505060405180910390fd5b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b1781529151815160009384939284926060926001600160a01b038a169261753092879282918083835b60208310614c265780518252601f199092019160209182019101614c07565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114614c87576040519150601f19603f3d011682016040523d82523d6000602084013e614c8c565b606091505b5091509150602081511015614caa5760008094509450505050614ccb565b81818060200190516020811015614cc057600080fd5b505190955093505050505b9250929050565b6060614ce18484600085614ce9565b949350505050565b606082471015614d2a5760405162461bcd60e51b8152600401808060200182810382526026815260200180614f526026913960400191505060405180910390fd5b614d3385614981565b614d84576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310614dc35780518252601f199092019160209182019101614da4565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114614e25576040519150601f19603f3d011682016040523d82523d6000602084013e614e2a565b606091505b5091509150614e3a828286614e45565b979650505050505050565b60608315614e54575081611721565b825115614e645782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614eae578181015183820152602001614e96565b50505050905090810190601f168015614edb5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b60408051808201909152600080825260208201529056fe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e6473416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f206772616e74416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f207265766f6b65496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a6564546869732074797065206f66204e4654206d6179206e6f742062652074726164656420686572655361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a26469706673582212201bb4a9d828b936122508c20f6940726f6bb670a76eac0db2c7746b6ff04b6ff864736f6c63430006050033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in BNB
Multichain Portfolio | 32 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.