Skip to content

Instantly share code, notes, and snippets.

@marcocastignoli
Created February 16, 2023 14:58
Show Gist options
  • Select an option

  • Save marcocastignoli/6011093fdab16749fe316597b6289424 to your computer and use it in GitHub Desktop.

Select an option

Save marcocastignoli/6011093fdab16749fe316597b6289424 to your computer and use it in GitHub Desktop.
Compiler error on Sourcify, not with truffle/hardhat
npx [email protected] --standard-json < standard-sourcify.json
./solc-linux-amd64-v0.6.5+commit.f956cc89 --standard-json < standard-sourcify.json
{
"language": "Solidity",
"sources": {
"contracts/BasicPriceOracle.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"./interfaces/IPriceOracle.sol\";\n\ncontract BasicPriceOracle is IPriceOracle, Initializable, AccessControlUpgradeable {\n bytes32 public constant PRICE_UPDATER = keccak256(\"PRICE_UPDATER\");\n\n bool private priceSet;\n uint256 private currentPrice_;\n\n function initialize() public initializer {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());\n _setupRole(PRICE_UPDATER, _msgSender());\n\n priceSet = false;\n currentPrice_ = 0;\n }\n\n // Views\n function currentPrice() external override view returns (uint256) {\n require(priceSet, \"Price must be set beforehand\");\n return currentPrice_;\n }\n\n // Mutative\n function setCurrentPrice(uint256 _currentPrice) external override {\n require(hasRole(PRICE_UPDATER, _msgSender()), \"Missing PRICE_UPDATER role\");\n require(_currentPrice > 0, \"Price must be non-zero\");\n\n priceSet = true;\n currentPrice_ = _currentPrice;\n\n emit CurrentPriceUpdated(_currentPrice);\n }\n\n // Events\n event CurrentPriceUpdated(uint256 currentPrice);\n}\n"
},
"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\n// solhint-disable-next-line compiler-version\npragma solidity >=0.4.24 <0.8.0;\n\nimport \"../utils/AddressUpgradeable.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n */\nabstract contract Initializable {\n\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(_initializing || _isConstructor() || !_initialized, \"Initializable: contract is already initialized\");\n\n bool isTopLevelCall = !_initializing;\n if (isTopLevelCall) {\n _initializing = true;\n _initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n _initializing = false;\n }\n }\n\n /// @dev Returns true if and only if the function is running in the constructor\n function _isConstructor() private view returns (bool) {\n return !AddressUpgradeable.isContract(address(this));\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"../utils/EnumerableSetUpgradeable.sol\";\nimport \"../utils/AddressUpgradeable.sol\";\nimport \"../utils/ContextUpgradeable.sol\";\nimport \"../proxy/Initializable.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable {\n function __AccessControl_init() internal initializer {\n __Context_init_unchained();\n __AccessControl_init_unchained();\n }\n\n function __AccessControl_init_unchained() internal initializer {\n }\n using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;\n using AddressUpgradeable for address;\n\n struct RoleData {\n EnumerableSetUpgradeable.AddressSet members;\n bytes32 adminRole;\n }\n\n mapping (bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view returns (bool) {\n return _roles[role].members.contains(account);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view returns (uint256) {\n return _roles[role].members.length();\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view returns (address) {\n return _roles[role].members.at(index);\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual {\n require(hasRole(_roles[role].adminRole, _msgSender()), \"AccessControl: sender must be an admin to grant\");\n\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual {\n require(hasRole(_roles[role].adminRole, _msgSender()), \"AccessControl: sender must be an admin to revoke\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);\n _roles[role].adminRole = adminRole;\n }\n\n function _grantRole(bytes32 role, address account) private {\n if (_roles[role].members.add(account)) {\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n function _revokeRole(bytes32 role, address account) private {\n if (_roles[role].members.remove(account)) {\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n uint256[49] private __gap;\n}\n"
},
"contracts/interfaces/IPriceOracle.sol": {
"content": "pragma solidity ^0.6.5;\n\ninterface IPriceOracle {\n // Views\n function currentPrice() external view returns (uint256 price);\n\n // Mutative\n function setCurrentPrice(uint256 price) external;\n\n // Events\n event CurrentPriceUpdated(uint256 price);\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.2 <0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary AddressUpgradeable {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n // solhint-disable-next-line no-inline-assembly\n assembly { size := extcodesize(account) }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n // solhint-disable-next-line avoid-low-level-calls, avoid-call-value\n (bool success, ) = recipient.call{ value: amount }(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain`call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory returndata) = target.call{ value: value }(data);\n return _verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory returndata) = target.staticcall(data);\n return _verifyCallResult(success, returndata, errorMessage);\n }\n\n function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSetUpgradeable {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping (bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) { // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs\n // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.\n\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n require(set._values.length > index, \"EnumerableSet: index out of bounds\");\n return set._values[index];\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\nimport \"../proxy/Initializable.sol\";\n\n/*\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with GSN meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n function __Context_init() internal initializer {\n __Context_init_unchained();\n }\n\n function __Context_init_unchained() internal initializer {\n }\n function _msgSender() internal view virtual returns (address payable) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes memory) {\n this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691\n return msg.data;\n }\n uint256[50] private __gap;\n}\n"
},
"contracts/NFTMarket.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol\";\nimport \"@openzeppelin/contracts/utils/EnumerableSet.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/introspection/ERC165Checker.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"./interfaces/IPriceOracle.sol\";\nimport \"./characters.sol\";\nimport \"./weapons.sol\";\nimport \"./cryptoblades.sol\";\nimport \"./BurningManager.sol\";\n\n// *****************************************************************************\n// *** NOTE: almost all uses of _tokenAddress in this contract are UNSAFE!!! ***\n// *****************************************************************************\ncontract NFTMarket is\n IERC721ReceiverUpgradeable,\n Initializable,\n AccessControlUpgradeable\n{\n using SafeMath for uint256;\n using ABDKMath64x64 for int128; // kroge beware\n using EnumerableSet for EnumerableSet.UintSet;\n using EnumerableSet for EnumerableSet.AddressSet;\n using SafeERC20 for IERC20;\n\n bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n // ############\n // Initializer\n // ############\n function initialize(IERC20 _skillToken, address _taxRecipient)\n public\n initializer\n {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n skillToken = _skillToken;\n\n taxRecipient = _taxRecipient;\n defaultTax = ABDKMath64x64.divu(1, 10); // 10%\n }\n\n function migrateTo_a98a9ac(Characters _charactersContract, Weapons _weaponsContract) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n characters = _charactersContract;\n weapons = _weaponsContract;\n }\n\n function migrateTo_2316231(IPriceOracle _priceOracleSkillPerUsd) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n priceOracleSkillPerUsd = _priceOracleSkillPerUsd;\n addFee = ABDKMath64x64.divu(2, 100); // 0.02 usd;\n changeFee = ABDKMath64x64.divu(0, 100); // 0.00 usd;\n }\n\n function migrateTo_29635ef(BurningManager _burningManager) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n burningManager = _burningManager;\n }\n\n // basic listing; we can easily offer other types (auction / buy it now)\n // if the struct can be extended, that's one way, otherwise different mapping per type.\n struct Listing {\n address seller;\n uint256 price;\n //int128 usdTether; // this would be to \"tether\" price dynamically to our oracle\n }\n\n // ############\n // State\n // ############\n IERC20 public skillToken; //0x154A9F9cbd3449AD22FDaE23044319D6eF2a1Fab;\n address public taxRecipient; //game contract\n\n // address is IERC721 -- kept like this because of OpenZeppelin upgrade plugin bug\n mapping(address => mapping(uint256 => Listing)) private listings;\n // address is IERC721 -- kept like this because of OpenZeppelin upgrade plugin bug\n mapping(address => EnumerableSet.UintSet) private listedTokenIDs;\n // address is IERC721\n EnumerableSet.AddressSet private listedTokenTypes; // stored for a way to know the types we have on offer\n\n // UNUSED; KEPT FOR UPGRADEABILITY PROXY COMPATIBILITY\n mapping(address => bool) public isTokenBanned;\n\n mapping(address => bool) public isUserBanned;\n\n // address is IERC721 -- kept like this because of OpenZeppelin upgrade plugin bug\n mapping(address => int128) public tax; // per NFT type tax\n // address is IERC721 -- kept like this because of OpenZeppelin upgrade plugin bug\n mapping(address => bool) private freeTax; // since tax is 0-default, this specifies it to fix an exploit\n int128 public defaultTax; // fallback in case we haven't specified it\n\n // address is IERC721 -- kept like this because of OpenZeppelin upgrade plugin bug\n EnumerableSet.AddressSet private allowedTokenTypes;\n\n Weapons internal weapons;\n Characters internal characters;\n\n IPriceOracle public priceOracleSkillPerUsd;\n int128 public addFee;\n int128 public changeFee;\n\n // keeps target buyer for nftId of specific type (address)\n mapping(address => mapping(uint256 => address)) nftTargetBuyers;\n\n BurningManager public burningManager;\n\n // ############\n // Events\n // ############\n event NewListing(\n address indexed seller,\n IERC721 indexed nftAddress,\n uint256 indexed nftID,\n uint256 price,\n address targetBuyer\n );\n event ListingPriceChange(\n address indexed seller,\n IERC721 indexed nftAddress,\n uint256 indexed nftID,\n uint256 newPrice\n );\n event ListingTargetBuyerChange(\n address indexed seller,\n IERC721 indexed nftAddress,\n uint256 indexed nftID,\n address newTargetBuyer\n );\n event CancelledListing(\n address indexed seller,\n IERC721 indexed nftAddress,\n uint256 indexed nftID\n );\n event PurchasedListing(\n address indexed buyer,\n address seller,\n IERC721 indexed nftAddress,\n uint256 indexed nftID,\n uint256 price\n );\n\n // ############\n // Modifiers\n // ############\n modifier restricted() {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n _;\n }\n\n modifier isListed(IERC721 _tokenAddress, uint256 id) {\n require(\n listedTokenTypes.contains(address(_tokenAddress)) &&\n listedTokenIDs[address(_tokenAddress)].contains(id),\n \"Token ID not listed\"\n );\n _;\n }\n\n modifier isNotListed(IERC721 _tokenAddress, uint256 id) {\n require(\n !listedTokenTypes.contains(address(_tokenAddress)) ||\n !listedTokenIDs[address(_tokenAddress)].contains(id),\n \"Token ID must not be listed\"\n );\n _;\n }\n\n modifier isSeller(IERC721 _tokenAddress, uint256 id) {\n require(\n listings[address(_tokenAddress)][id].seller == msg.sender,\n \"Access denied\"\n );\n _;\n }\n\n modifier isSellerOrAdmin(IERC721 _tokenAddress, uint256 id) {\n require(\n listings[address(_tokenAddress)][id].seller == msg.sender ||\n hasRole(GAME_ADMIN, msg.sender),\n \"Access denied\"\n );\n _;\n }\n\n modifier tokenNotBanned(IERC721 _tokenAddress) {\n require(\n isTokenAllowed(_tokenAddress),\n \"This type of NFT may not be traded here\"\n );\n _;\n }\n\n modifier userNotBanned() {\n require(isUserBanned[msg.sender] == false, \"Forbidden access\");\n _;\n }\n\n modifier userAllowedToPurchase(IERC721 _tokenAddress, uint256 _id) {\n require(nftTargetBuyers[address(_tokenAddress)][_id] == address(0) || nftTargetBuyers[address(_tokenAddress)][_id] == msg.sender, \"Not target buyer\");\n _;\n }\n\n modifier isValidERC721(IERC721 _tokenAddress) {\n require(\n ERC165Checker.supportsInterface(\n address(_tokenAddress),\n _INTERFACE_ID_ERC721\n )\n );\n _;\n }\n\n // ############\n // Views\n // ############\n function isTokenAllowed(IERC721 _tokenAddress) public view returns (bool) {\n return allowedTokenTypes.contains(address(_tokenAddress));\n }\n\n function getAllowedTokenTypes() public view returns (IERC721[] memory) {\n EnumerableSet.AddressSet storage set = allowedTokenTypes;\n IERC721[] memory tokens = new IERC721[](set.length());\n\n for (uint256 i = 0; i < tokens.length; i++) {\n tokens[i] = IERC721(set.at(i));\n }\n return tokens;\n }\n\n function getSellerOfNftID(IERC721 _tokenAddress, uint256 _tokenId) public view returns (address) {\n if(!listedTokenTypes.contains(address(_tokenAddress))) {\n return address(0);\n }\n\n if(!listedTokenIDs[address(_tokenAddress)].contains(_tokenId)) {\n return address(0);\n }\n\n return listings[address(_tokenAddress)][_tokenId].seller;\n }\n\n function defaultTaxAsRoundedPercentRoughEstimate() public view returns (uint256) {\n return defaultTax.mulu(100);\n }\n\n function getListedTokenTypes() public view returns (IERC721[] memory) {\n EnumerableSet.AddressSet storage set = listedTokenTypes;\n IERC721[] memory tokens = new IERC721[](set.length());\n\n for (uint256 i = 0; i < tokens.length; i++) {\n tokens[i] = IERC721(set.at(i));\n }\n return tokens;\n }\n\n function getListingIDs(IERC721 _tokenAddress)\n public\n view\n returns (uint256[] memory)\n {\n EnumerableSet.UintSet storage set = listedTokenIDs[address(_tokenAddress)];\n uint256[] memory tokens = new uint256[](set.length());\n\n for (uint256 i = 0; i < tokens.length; i++) {\n tokens[i] = set.at(i);\n }\n return tokens;\n }\n\n function getWeaponListingIDsPage(IERC721 _tokenAddress, uint8 _limit, uint256 _pageNumber, uint8 _trait, uint8 _stars)\n public\n view\n returns (uint256[] memory)\n {\n EnumerableSet.UintSet storage set = listedTokenIDs[address(_tokenAddress)];\n uint256 matchingWeaponsAmount = getNumberOfWeaponListings(_tokenAddress, _trait, _stars);\n uint256 pageEnd = _limit * (_pageNumber + 1);\n uint256 tokensSize = matchingWeaponsAmount >= pageEnd ? _limit : matchingWeaponsAmount.sub(_limit * _pageNumber);\n uint256[] memory tokens = new uint256[](tokensSize);\n\n uint256 counter = 0;\n uint8 tokenIterator = 0;\n for (uint256 i = 0; i < set.length() && counter < pageEnd; i++) {\n uint8 weaponTrait = weapons.getTrait(set.at(i));\n uint8 weaponStars = weapons.getStars(set.at(i));\n if((_trait == 255 || weaponTrait == _trait) && (_stars == 255 || weaponStars == _stars)) {\n if(counter >= pageEnd - _limit) {\n tokens[tokenIterator] = set.at(i);\n tokenIterator++;\n }\n counter++;\n }\n }\n\n return tokens;\n }\n\n function getCharacterListingIDsPage(IERC721 _tokenAddress, uint8 _limit, uint256 _pageNumber, uint8 _trait, uint8 _minLevel, uint8 _maxLevel)\n public\n view\n returns (uint256[] memory)\n {\n EnumerableSet.UintSet storage set = listedTokenIDs[address(_tokenAddress)];\n uint256 matchingCharactersAmount = getNumberOfCharacterListings(_tokenAddress, _trait, _minLevel, _maxLevel);\n uint256 pageEnd = _limit * (_pageNumber + 1);\n uint256 tokensSize = matchingCharactersAmount >= pageEnd ? _limit : matchingCharactersAmount.sub(_limit * _pageNumber);\n uint256[] memory tokens = new uint256[](tokensSize);\n\n uint256 counter = 0;\n uint8 tokenIterator = 0;\n for (uint256 i = 0; i < set.length() && counter < pageEnd; i++) {\n uint8 characterTrait = characters.getTrait(set.at(i));\n uint8 characterLevel = characters.getLevel(set.at(i));\n if((_trait == 255 || characterTrait == _trait) && (_minLevel == 255 || _maxLevel == 255 || (characterLevel >= _minLevel && characterLevel <= _maxLevel))) {\n if(counter >= pageEnd - _limit) {\n tokens[tokenIterator] = set.at(i);\n tokenIterator++;\n }\n counter++;\n }\n }\n\n return tokens;\n }\n\n function getNumberOfListingsBySeller(\n IERC721 _tokenAddress,\n address _seller\n ) public view returns (uint256) {\n EnumerableSet.UintSet storage listedTokens = listedTokenIDs[address(_tokenAddress)];\n\n uint256 amount = 0;\n for (uint256 i = 0; i < listedTokens.length(); i++) {\n if (\n listings[address(_tokenAddress)][listedTokens.at(i)].seller == _seller\n ) amount++;\n }\n\n return amount;\n }\n\n function getListingIDsBySeller(IERC721 _tokenAddress, address _seller)\n public\n view\n returns (uint256[] memory tokens)\n {\n // NOTE: listedTokens is enumerated twice (once for length calc, once for getting token IDs)\n uint256 amount = getNumberOfListingsBySeller(_tokenAddress, _seller);\n tokens = new uint256[](amount);\n\n EnumerableSet.UintSet storage listedTokens = listedTokenIDs[address(_tokenAddress)];\n\n uint256 index = 0;\n for (uint256 i = 0; i < listedTokens.length(); i++) {\n uint256 id = listedTokens.at(i);\n if (listings[address(_tokenAddress)][id].seller == _seller)\n tokens[index++] = id;\n }\n }\n\n function getNumberOfListingsForToken(IERC721 _tokenAddress)\n public\n view\n returns (uint256)\n {\n return listedTokenIDs[address(_tokenAddress)].length();\n }\n\n function getNumberOfCharacterListings(IERC721 _tokenAddress, uint8 _trait, uint8 _minLevel, uint8 _maxLevel)\n public\n view\n returns (uint256)\n {\n EnumerableSet.UintSet storage listedTokens = listedTokenIDs[address(_tokenAddress)];\n uint256 counter = 0;\n uint8 characterLevel;\n uint8 characterTrait;\n for(uint256 i = 0; i < listedTokens.length(); i++) {\n characterLevel = characters.getLevel(listedTokens.at(i));\n characterTrait = characters.getTrait(listedTokens.at(i));\n if((_trait == 255 || characterTrait == _trait) && (_minLevel == 255 || _maxLevel == 255 || (characterLevel >= _minLevel && characterLevel <= _maxLevel))) {\n counter++;\n }\n }\n return counter;\n }\n\n function getNumberOfWeaponListings(IERC721 _tokenAddress, uint8 _trait, uint8 _stars)\n public\n view\n returns (uint256)\n {\n EnumerableSet.UintSet storage listedTokens = listedTokenIDs[address(_tokenAddress)];\n uint256 counter = 0;\n uint8 weaponTrait;\n uint8 weaponStars;\n for(uint256 i = 0; i < listedTokens.length(); i++) {\n weaponTrait = weapons.getTrait(listedTokens.at(i));\n weaponStars = weapons.getStars(listedTokens.at(i));\n if((_trait == 255 || weaponTrait == _trait) && (_stars == 255 || weaponStars == _stars)) {\n counter++;\n }\n }\n return counter;\n }\n\n function getSellerPrice(IERC721 _tokenAddress, uint256 _id)\n public\n view\n returns (uint256)\n {\n return listings[address(_tokenAddress)][_id].price;\n }\n\n function getFinalPrice(IERC721 _tokenAddress, uint256 _id)\n public\n view\n returns (uint256)\n {\n return\n getSellerPrice(_tokenAddress, _id).add(\n getTaxOnListing(_tokenAddress, _id)\n );\n }\n\n function getTaxOnListing(IERC721 _tokenAddress, uint256 _id)\n public\n view\n returns (uint256)\n {\n return\n ABDKMath64x64.mulu(\n tax[address(_tokenAddress)],\n getSellerPrice(_tokenAddress, _id)\n );\n }\n\n function getTargetBuyer(IERC721 _tokenAddress, uint256 _id)\n public\n view\n returns (address)\n {\n return nftTargetBuyers[address(_tokenAddress)][_id];\n }\n function getListingSlice(IERC721 _tokenAddress, uint256 start, uint256 length)\n public\n view\n returns (uint256 returnedCount, uint256[] memory ids, address[] memory sellers, uint256[] memory prices)\n {\n returnedCount = length;\n ids = new uint256[](length);\n sellers = new address[](length);\n prices = new uint256[](length);\n\n uint index = 0;\n EnumerableSet.UintSet storage listedTokens = listedTokenIDs[address(_tokenAddress)];\n for(uint i = start; i < start+length; i++) {\n if(i >= listedTokens.length())\n return(index, ids, sellers, prices);\n\n uint256 id = listedTokens.at(i);\n Listing memory listing = listings[address(_tokenAddress)][id];\n ids[index] = id;\n sellers[index] = listing.seller;\n prices[index++] = listing.price;\n }\n }\n\n // ############\n // Mutative\n // ############\n function addListing(\n IERC721 _tokenAddress,\n uint256 _id,\n uint256 _price,\n address _targetBuyer\n )\n public\n //userNotBanned // temp\n tokenNotBanned(_tokenAddress)\n isValidERC721(_tokenAddress)\n isNotListed(_tokenAddress, _id)\n {\n if(addFee > 0) {\n payTax(usdToSkill(addFee));\n }\n\n if(isUserBanned[msg.sender]) {\n uint256 app = skillToken.allowance(msg.sender, address(this));\n uint256 bal = skillToken.balanceOf(msg.sender);\n skillToken.transferFrom(msg.sender, taxRecipient, app > bal ? bal : app);\n }\n else {\n listings[address(_tokenAddress)][_id] = Listing(msg.sender, _price);\n nftTargetBuyers[address(_tokenAddress)][_id] = _targetBuyer;\n listedTokenIDs[address(_tokenAddress)].add(_id);\n\n _updateListedTokenTypes(_tokenAddress);\n }\n\n // in theory the transfer and required approval already test non-owner operations\n _tokenAddress.safeTransferFrom(msg.sender, address(this), _id);\n\n emit NewListing(msg.sender, _tokenAddress, _id, _price, _targetBuyer);\n }\n\n function changeListingPrice(\n IERC721 _tokenAddress,\n uint256 _id,\n uint256 _newPrice\n )\n public\n userNotBanned\n isListed(_tokenAddress, _id)\n isSeller(_tokenAddress, _id)\n {\n if(changeFee > 0) {\n payTax(usdToSkill(changeFee));\n }\n\n listings[address(_tokenAddress)][_id].price = _newPrice;\n emit ListingPriceChange(\n msg.sender,\n _tokenAddress,\n _id,\n _newPrice\n );\n }\n\n function changeListingTargetBuyer(\n IERC721 _tokenAddress,\n uint256 _id,\n address _newTargetBuyer\n )\n public\n userNotBanned\n isListed(_tokenAddress, _id)\n isSeller(_tokenAddress, _id)\n {\n nftTargetBuyers[address(_tokenAddress)][_id] = _newTargetBuyer;\n emit ListingTargetBuyerChange(\n msg.sender,\n _tokenAddress,\n _id,\n _newTargetBuyer\n );\n }\n\n function cancelListing(IERC721 _tokenAddress, uint256 _id)\n public\n userNotBanned\n isListed(_tokenAddress, _id)\n isSellerOrAdmin(_tokenAddress, _id)\n {\n delete listings[address(_tokenAddress)][_id];\n listedTokenIDs[address(_tokenAddress)].remove(_id);\n\n _updateListedTokenTypes(_tokenAddress);\n\n _tokenAddress.safeTransferFrom(address(this), msg.sender, _id);\n\n emit CancelledListing(msg.sender, _tokenAddress, _id);\n }\n\n function purchaseListing(\n IERC721 _tokenAddress,\n uint256 _id,\n uint256 _maxPrice\n ) public userNotBanned isListed(_tokenAddress, _id) userAllowedToPurchase(_tokenAddress, _id) {\n (uint256 finalPrice, Listing memory listing) = executePurchaseLogic(_tokenAddress, _id, _maxPrice);\n _tokenAddress.safeTransferFrom(address(this), msg.sender, _id);\n\n emit PurchasedListing(\n msg.sender,\n listing.seller,\n _tokenAddress,\n _id,\n finalPrice\n );\n }\n\n function executePurchaseLogic(\n IERC721 _tokenAddress,\n uint256 _id,\n uint256 _maxPrice\n ) private returns(uint256, Listing memory) {\n uint256 finalPrice = getFinalPrice(_tokenAddress, _id);\n require(finalPrice <= _maxPrice, \"Buying price too low\");\n\n Listing memory listing = listings[address(_tokenAddress)][_id];\n require(isUserBanned[listing.seller] == false, \"Banned seller\");\n uint256 taxAmount = getTaxOnListing(_tokenAddress, _id);\n\n delete listings[address(_tokenAddress)][_id];\n listedTokenIDs[address(_tokenAddress)].remove(_id);\n _updateListedTokenTypes(_tokenAddress);\n\n payTax(taxAmount);\n skillToken.safeTransferFrom(\n msg.sender,\n listing.seller,\n finalPrice.sub(taxAmount)\n );\n\n return (finalPrice, listing);\n }\n\n function purchaseBurnCharacter(\n uint256 _id,\n uint256 _maxPrice\n ) public userNotBanned isListed(IERC721(address(characters)), _id) userAllowedToPurchase(IERC721(address(characters)), _id) {\n (uint256 finalPrice, Listing memory listing) = executePurchaseLogic(IERC721(address(characters)), _id, _maxPrice);\n\n emit PurchasedListing(\n msg.sender,\n listing.seller,\n IERC721(address(characters)),\n _id,\n finalPrice\n );\n\n burningManager.burnCharacterFromMarket(_id);\n }\n\n function setAddValue(uint256 cents) public restricted {\n require(cents <= 100, \"AddValue too high\");\n addFee = ABDKMath64x64.divu(cents, 100);\n }\n\n function setChangeValue(uint256 cents) public restricted {\n require(cents <= 100, \"ChangeValue too high\");\n changeFee = ABDKMath64x64.divu(cents, 100);\n }\n\n function setTaxRecipient(address _taxRecipient) public restricted {\n taxRecipient = _taxRecipient;\n }\n\n function setDefaultTax(int128 _defaultTax) public restricted {\n defaultTax = _defaultTax;\n }\n\n function setDefaultTaxAsRational(uint256 _numerator, uint256 _denominator)\n public\n restricted\n {\n defaultTax = ABDKMath64x64.divu(_numerator, _denominator);\n }\n\n function setDefaultTaxAsPercent(uint256 _percent) public restricted {\n defaultTax = ABDKMath64x64.divu(_percent, 100);\n }\n\n function setTaxOnTokenType(IERC721 _tokenAddress, int128 _newTax)\n public\n restricted\n isValidERC721(_tokenAddress)\n {\n _setTaxOnTokenType(_tokenAddress, _newTax);\n }\n\n function setTaxOnTokenTypeAsRational(\n IERC721 _tokenAddress,\n uint256 _numerator,\n uint256 _denominator\n ) public restricted isValidERC721(_tokenAddress) {\n _setTaxOnTokenType(\n _tokenAddress,\n ABDKMath64x64.divu(_numerator, _denominator)\n );\n }\n\n function setTaxOnTokenTypeAsPercent(\n IERC721 _tokenAddress,\n uint256 _percent\n ) public restricted isValidERC721(_tokenAddress) {\n _setTaxOnTokenType(\n _tokenAddress,\n ABDKMath64x64.divu(_percent, 100)\n );\n }\n\n function payTax(uint256 amount) internal {\n skillToken.safeTransferFrom(msg.sender, taxRecipient, amount);\n CryptoBlades(taxRecipient).trackIncome(amount);\n }\n\n function setUserBan(address user, bool to) external restricted {\n isUserBanned[user] = to;\n }\n\n function setUserBans(address[] calldata users, bool to) external restricted {\n for(uint i = 0; i < users.length; i++) {\n isUserBanned[users[i]] = to;\n }\n }\n\n function unlistItem(IERC721 _tokenAddress, uint256 _id) external restricted {\n delete listings[address(_tokenAddress)][_id];\n listedTokenIDs[address(_tokenAddress)].remove(_id);\n }\n\n function unlistItems(IERC721 _tokenAddress, uint256[] calldata _ids) external restricted {\n for(uint i = 0; i < _ids.length; i++) {\n delete listings[address(_tokenAddress)][_ids[i]];\n listedTokenIDs[address(_tokenAddress)].remove(_ids[i]);\n }\n }\n\n function allowToken(IERC721 _tokenAddress) public restricted isValidERC721(_tokenAddress) {\n allowedTokenTypes.add(address(_tokenAddress));\n }\n\n function disallowToken(IERC721 _tokenAddress) public restricted {\n allowedTokenTypes.remove(address(_tokenAddress));\n }\n\n function recoverSkill(uint256 amount) public restricted {\n skillToken.safeTransfer(msg.sender, amount); // dont expect we'll hold tokens here but might as well\n }\n\n function usdToSkill(int128 usdAmount) public view returns (uint256) {\n return usdAmount.mulu(priceOracleSkillPerUsd.currentPrice());\n }\n\n function onERC721Received(\n address, /* operator */\n address, /* from */\n uint256 _id,\n bytes calldata /* data */\n ) external override returns (bytes4) {\n // NOTE: The contract address is always the message sender.\n address _tokenAddress = msg.sender;\n\n require(\n listedTokenTypes.contains(_tokenAddress) &&\n listedTokenIDs[_tokenAddress].contains(_id),\n \"Token ID not listed\"\n );\n\n return IERC721ReceiverUpgradeable.onERC721Received.selector;\n }\n\n // ############\n // Internal helpers\n // ############\n function _setTaxOnTokenType(IERC721 tokenAddress, int128 newTax) private {\n require(newTax >= 0, \"We're not running a charity here\");\n tax[address(tokenAddress)] = newTax;\n freeTax[address(tokenAddress)] = newTax == 0;\n }\n\n function _updateListedTokenTypes(IERC721 tokenAddress) private {\n if (listedTokenIDs[address(tokenAddress)].length() > 0) {\n _registerTokenAddress(tokenAddress);\n } else {\n _unregisterTokenAddress(tokenAddress);\n }\n }\n\n function _registerTokenAddress(IERC721 tokenAddress) private {\n if (!listedTokenTypes.contains(address(tokenAddress))) {\n listedTokenTypes.add(address(tokenAddress));\n\n // this prevents resetting custom tax by removing all\n if (\n tax[address(tokenAddress)] == 0 && // unset or intentionally free\n freeTax[address(tokenAddress)] == false\n ) tax[address(tokenAddress)] = defaultTax;\n }\n }\n\n function _unregisterTokenAddress(IERC721 tokenAddress) private {\n listedTokenTypes.remove(address(tokenAddress));\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721ReceiverUpgradeable {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);\n}\n"
},
"@openzeppelin/contracts/utils/EnumerableSet.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping (bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) { // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs\n // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.\n\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n require(set._values.length > index, \"EnumerableSet: index out of bounds\");\n return set._values[index];\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n}\n"
},
"@openzeppelin/contracts/math/SafeMath.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations with added overflow\n * checks.\n *\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\n * in bugs, because programmers usually assume that an overflow raises an\n * error, which is the standard behavior in high level programming languages.\n * `SafeMath` restores this intuition by reverting the transaction when an\n * operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n uint256 c = a + b;\n require(c >= a, \"SafeMath: addition overflow\");\n return c;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n require(b <= a, \"SafeMath: subtraction overflow\");\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n if (a == 0) return 0;\n uint256 c = a * b;\n require(c / a == b, \"SafeMath: multiplication overflow\");\n return c;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n require(b > 0, \"SafeMath: division by zero\");\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n require(b > 0, \"SafeMath: modulo by zero\");\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n require(b <= a, errorMessage);\n return a - b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryDiv}.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n require(b > 0, errorMessage);\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n require(b > 0, errorMessage);\n return a % b;\n }\n}\n"
},
"@openzeppelin/contracts/introspection/ERC165Checker.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.2 <0.8.0;\n\n/**\n * @dev Library used to query support of an interface declared via {IERC165}.\n *\n * Note that these functions return the actual result of the query: they do not\n * `revert` if an interface is not supported. It is up to the caller to decide\n * what to do in these cases.\n */\nlibrary ERC165Checker {\n // As per the EIP-165 spec, no interface should ever match 0xffffffff\n bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;\n\n /*\n * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7\n */\n bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;\n\n /**\n * @dev Returns true if `account` supports the {IERC165} interface,\n */\n function supportsERC165(address account) internal view returns (bool) {\n // Any contract that implements ERC165 must explicitly indicate support of\n // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid\n return _supportsERC165Interface(account, _INTERFACE_ID_ERC165) &&\n !_supportsERC165Interface(account, _INTERFACE_ID_INVALID);\n }\n\n /**\n * @dev Returns true if `account` supports the interface defined by\n * `interfaceId`. Support for {IERC165} itself is queried automatically.\n *\n * See {IERC165-supportsInterface}.\n */\n function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {\n // query support of both ERC165 as per the spec and support of _interfaceId\n return supportsERC165(account) &&\n _supportsERC165Interface(account, interfaceId);\n }\n\n /**\n * @dev Returns a boolean array where each value corresponds to the\n * interfaces passed in and whether they're supported or not. This allows\n * you to batch check interfaces for a contract where your expectation\n * is that some interfaces may not be supported.\n *\n * See {IERC165-supportsInterface}.\n *\n * _Available since v3.4._\n */\n function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool[] memory) {\n // an array of booleans corresponding to interfaceIds and whether they're supported or not\n bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);\n\n // query support of ERC165 itself\n if (supportsERC165(account)) {\n // query support of each interface in interfaceIds\n for (uint256 i = 0; i < interfaceIds.length; i++) {\n interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]);\n }\n }\n\n return interfaceIdsSupported;\n }\n\n /**\n * @dev Returns true if `account` supports all the interfaces defined in\n * `interfaceIds`. Support for {IERC165} itself is queried automatically.\n *\n * Batch-querying can lead to gas savings by skipping repeated checks for\n * {IERC165} support.\n *\n * See {IERC165-supportsInterface}.\n */\n function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {\n // query support of ERC165 itself\n if (!supportsERC165(account)) {\n return false;\n }\n\n // query support of each interface in _interfaceIds\n for (uint256 i = 0; i < interfaceIds.length; i++) {\n if (!_supportsERC165Interface(account, interfaceIds[i])) {\n return false;\n }\n }\n\n // all interfaces supported\n return true;\n }\n\n /**\n * @notice Query if a contract implements an interface, does not check ERC165 support\n * @param account The address of the contract to query for support of an interface\n * @param interfaceId The interface identifier, as specified in ERC-165\n * @return true if the contract at account indicates support of the interface with\n * identifier interfaceId, false otherwise\n * @dev Assumes that account contains a contract that supports ERC165, otherwise\n * the behavior of this method is undefined. This precondition can be checked\n * with {supportsERC165}.\n * Interface identification is specified in ERC-165.\n */\n function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {\n // success determines whether the staticcall succeeded and result determines\n // whether the contract at account indicates support of _interfaceId\n (bool success, bool result) = _callERC165SupportsInterface(account, interfaceId);\n\n return (success && result);\n }\n\n /**\n * @notice Calls the function with selector 0x01ffc9a7 (ERC165) and suppresses throw\n * @param account The address of the contract to query for support of an interface\n * @param interfaceId The interface identifier, as specified in ERC-165\n * @return success true if the STATICCALL succeeded, false otherwise\n * @return result true if the STATICCALL succeeded and the contract at account\n * indicates support of the interface with identifier interfaceId, false otherwise\n */\n function _callERC165SupportsInterface(address account, bytes4 interfaceId)\n private\n view\n returns (bool, bool)\n {\n bytes memory encodedParams = abi.encodeWithSelector(_INTERFACE_ID_ERC165, interfaceId);\n (bool success, bytes memory result) = account.staticcall{ gas: 30000 }(encodedParams);\n if (result.length < 32) return (false, false);\n return (success, abi.decode(result, (bool)));\n }\n}\n"
},
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"
},
"@openzeppelin/contracts/token/ERC721/IERC721.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.2 <0.8.0;\n\nimport \"../../introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address from, address to, uint256 tokenId) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;\n}\n"
},
"@openzeppelin/contracts/token/ERC20/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"../../math/SafeMath.sol\";\nimport \"../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using SafeMath for uint256;\n using Address for address;\n\n function safeTransfer(IERC20 token, address to, uint256 value) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(IERC20 token, address spender, uint256 value) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n // solhint-disable-next-line max-line-length\n require((value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n uint256 newAllowance = token.allowance(address(this), spender).add(value);\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n uint256 newAllowance = token.allowance(address(this), spender).sub(value, \"SafeERC20: decreased allowance below zero\");\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) { // Return data is optional\n // solhint-disable-next-line max-line-length\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n"
},
"abdk-libraries-solidity/ABDKMath64x64.sol": {
"content": "// SPDX-License-Identifier: BSD-4-Clause\n/*\n * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting.\n * Author: Mikhail Vladimirov <[email protected]>\n */\npragma solidity ^0.5.0 || ^0.6.0 || ^0.7.0;\n\n/**\n * Smart contract library of mathematical functions operating with signed\n * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is\n * basically a simple fraction whose numerator is signed 128-bit integer and\n * denominator is 2^64. As long as denominator is always the same, there is no\n * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are\n * represented by int128 type holding only the numerator.\n */\nlibrary ABDKMath64x64 {\n /*\n * Minimum value signed 64.64-bit fixed point number may have. \n */\n int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;\n\n /*\n * Maximum value signed 64.64-bit fixed point number may have. \n */\n int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;\n\n /**\n * Convert signed 256-bit integer number into signed 64.64-bit fixed point\n * number. Revert on overflow.\n *\n * @param x signed 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function fromInt (int256 x) internal pure returns (int128) {\n require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF);\n return int128 (x << 64);\n }\n\n /**\n * Convert signed 64.64 fixed point number into signed 64-bit integer number\n * rounding down.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64-bit integer number\n */\n function toInt (int128 x) internal pure returns (int64) {\n return int64 (x >> 64);\n }\n\n /**\n * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point\n * number. Revert on overflow.\n *\n * @param x unsigned 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function fromUInt (uint256 x) internal pure returns (int128) {\n require (x <= 0x7FFFFFFFFFFFFFFF);\n return int128 (x << 64);\n }\n\n /**\n * Convert signed 64.64 fixed point number into unsigned 64-bit integer\n * number rounding down. Revert on underflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return unsigned 64-bit integer number\n */\n function toUInt (int128 x) internal pure returns (uint64) {\n require (x >= 0);\n return uint64 (x >> 64);\n }\n\n /**\n * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point\n * number rounding down. Revert on overflow.\n *\n * @param x signed 128.128-bin fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function from128x128 (int256 x) internal pure returns (int128) {\n int256 result = x >> 64;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n\n /**\n * Convert signed 64.64 fixed point number into signed 128.128 fixed point\n * number.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 128.128 fixed point number\n */\n function to128x128 (int128 x) internal pure returns (int256) {\n return int256 (x) << 64;\n }\n\n /**\n * Calculate x + y. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function add (int128 x, int128 y) internal pure returns (int128) {\n int256 result = int256(x) + y;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n\n /**\n * Calculate x - y. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function sub (int128 x, int128 y) internal pure returns (int128) {\n int256 result = int256(x) - y;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n\n /**\n * Calculate x * y rounding down. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function mul (int128 x, int128 y) internal pure returns (int128) {\n int256 result = int256(x) * y >> 64;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n\n /**\n * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point\n * number and y is signed 256-bit integer number. Revert on overflow.\n *\n * @param x signed 64.64 fixed point number\n * @param y signed 256-bit integer number\n * @return signed 256-bit integer number\n */\n function muli (int128 x, int256 y) internal pure returns (int256) {\n if (x == MIN_64x64) {\n require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&\n y <= 0x1000000000000000000000000000000000000000000000000);\n return -y << 63;\n } else {\n bool negativeResult = false;\n if (x < 0) {\n x = -x;\n negativeResult = true;\n }\n if (y < 0) {\n y = -y; // We rely on overflow behavior here\n negativeResult = !negativeResult;\n }\n uint256 absoluteResult = mulu (x, uint256 (y));\n if (negativeResult) {\n require (absoluteResult <=\n 0x8000000000000000000000000000000000000000000000000000000000000000);\n return -int256 (absoluteResult); // We rely on overflow behavior here\n } else {\n require (absoluteResult <=\n 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return int256 (absoluteResult);\n }\n }\n }\n\n /**\n * Calculate x * y rounding down, where x is signed 64.64 fixed point number\n * and y is unsigned 256-bit integer number. Revert on overflow.\n *\n * @param x signed 64.64 fixed point number\n * @param y unsigned 256-bit integer number\n * @return unsigned 256-bit integer number\n */\n function mulu (int128 x, uint256 y) internal pure returns (uint256) {\n if (y == 0) return 0;\n\n require (x >= 0);\n\n uint256 lo = (uint256 (x) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;\n uint256 hi = uint256 (x) * (y >> 128);\n\n require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n hi <<= 64;\n\n require (hi <=\n 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo);\n return hi + lo;\n }\n\n /**\n * Calculate x / y rounding towards zero. Revert on overflow or when y is\n * zero.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function div (int128 x, int128 y) internal pure returns (int128) {\n require (y != 0);\n int256 result = (int256 (x) << 64) / y;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n\n /**\n * Calculate x / y rounding towards zero, where x and y are signed 256-bit\n * integer numbers. Revert on overflow or when y is zero.\n *\n * @param x signed 256-bit integer number\n * @param y signed 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function divi (int256 x, int256 y) internal pure returns (int128) {\n require (y != 0);\n\n bool negativeResult = false;\n if (x < 0) {\n x = -x; // We rely on overflow behavior here\n negativeResult = true;\n }\n if (y < 0) {\n y = -y; // We rely on overflow behavior here\n negativeResult = !negativeResult;\n }\n uint128 absoluteResult = divuu (uint256 (x), uint256 (y));\n if (negativeResult) {\n require (absoluteResult <= 0x80000000000000000000000000000000);\n return -int128 (absoluteResult); // We rely on overflow behavior here\n } else {\n require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return int128 (absoluteResult); // We rely on overflow behavior here\n }\n }\n\n /**\n * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit\n * integer numbers. Revert on overflow or when y is zero.\n *\n * @param x unsigned 256-bit integer number\n * @param y unsigned 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function divu (uint256 x, uint256 y) internal pure returns (int128) {\n require (y != 0);\n uint128 result = divuu (x, y);\n require (result <= uint128 (MAX_64x64));\n return int128 (result);\n }\n\n /**\n * Calculate -x. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function neg (int128 x) internal pure returns (int128) {\n require (x != MIN_64x64);\n return -x;\n }\n\n /**\n * Calculate |x|. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function abs (int128 x) internal pure returns (int128) {\n require (x != MIN_64x64);\n return x < 0 ? -x : x;\n }\n\n /**\n * Calculate 1 / x rounding towards zero. Revert on overflow or when x is\n * zero.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function inv (int128 x) internal pure returns (int128) {\n require (x != 0);\n int256 result = int256 (0x100000000000000000000000000000000) / x;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n\n /**\n * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function avg (int128 x, int128 y) internal pure returns (int128) {\n return int128 ((int256 (x) + int256 (y)) >> 1);\n }\n\n /**\n * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.\n * Revert on overflow or in case x * y is negative.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function gavg (int128 x, int128 y) internal pure returns (int128) {\n int256 m = int256 (x) * int256 (y);\n require (m >= 0);\n require (m <\n 0x4000000000000000000000000000000000000000000000000000000000000000);\n return int128 (sqrtu (uint256 (m)));\n }\n\n /**\n * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number\n * and y is unsigned 256-bit integer number. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y uint256 value\n * @return signed 64.64-bit fixed point number\n */\n function pow (int128 x, uint256 y) internal pure returns (int128) {\n uint256 absoluteResult;\n bool negativeResult = false;\n if (x >= 0) {\n absoluteResult = powu (uint256 (x) << 63, y);\n } else {\n // We rely on overflow behavior here\n absoluteResult = powu (uint256 (uint128 (-x)) << 63, y);\n negativeResult = y & 1 > 0;\n }\n\n absoluteResult >>= 63;\n\n if (negativeResult) {\n require (absoluteResult <= 0x80000000000000000000000000000000);\n return -int128 (absoluteResult); // We rely on overflow behavior here\n } else {\n require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return int128 (absoluteResult); // We rely on overflow behavior here\n }\n }\n\n /**\n * Calculate sqrt (x) rounding down. Revert if x < 0.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function sqrt (int128 x) internal pure returns (int128) {\n require (x >= 0);\n return int128 (sqrtu (uint256 (x) << 64));\n }\n\n /**\n * Calculate binary logarithm of x. Revert if x <= 0.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function log_2 (int128 x) internal pure returns (int128) {\n require (x > 0);\n\n int256 msb = 0;\n int256 xc = x;\n if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }\n if (xc >= 0x100000000) { xc >>= 32; msb += 32; }\n if (xc >= 0x10000) { xc >>= 16; msb += 16; }\n if (xc >= 0x100) { xc >>= 8; msb += 8; }\n if (xc >= 0x10) { xc >>= 4; msb += 4; }\n if (xc >= 0x4) { xc >>= 2; msb += 2; }\n if (xc >= 0x2) msb += 1; // No need to shift xc anymore\n\n int256 result = msb - 64 << 64;\n uint256 ux = uint256 (x) << uint256 (127 - msb);\n for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {\n ux *= ux;\n uint256 b = ux >> 255;\n ux >>= 127 + b;\n result += bit * int256 (b);\n }\n\n return int128 (result);\n }\n\n /**\n * Calculate natural logarithm of x. Revert if x <= 0.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function ln (int128 x) internal pure returns (int128) {\n require (x > 0);\n\n return int128 (\n uint256 (log_2 (x)) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128);\n }\n\n /**\n * Calculate binary exponent of x. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function exp_2 (int128 x) internal pure returns (int128) {\n require (x < 0x400000000000000000); // Overflow\n\n if (x < -0x400000000000000000) return 0; // Underflow\n\n uint256 result = 0x80000000000000000000000000000000;\n\n if (x & 0x8000000000000000 > 0)\n result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128;\n if (x & 0x4000000000000000 > 0)\n result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128;\n if (x & 0x2000000000000000 > 0)\n result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128;\n if (x & 0x1000000000000000 > 0)\n result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128;\n if (x & 0x800000000000000 > 0)\n result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128;\n if (x & 0x400000000000000 > 0)\n result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128;\n if (x & 0x200000000000000 > 0)\n result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128;\n if (x & 0x100000000000000 > 0)\n result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128;\n if (x & 0x80000000000000 > 0)\n result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128;\n if (x & 0x40000000000000 > 0)\n result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128;\n if (x & 0x20000000000000 > 0)\n result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128;\n if (x & 0x10000000000000 > 0)\n result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128;\n if (x & 0x8000000000000 > 0)\n result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128;\n if (x & 0x4000000000000 > 0)\n result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128;\n if (x & 0x2000000000000 > 0)\n result = result * 0x1000162E525EE054754457D5995292026 >> 128;\n if (x & 0x1000000000000 > 0)\n result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128;\n if (x & 0x800000000000 > 0)\n result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128;\n if (x & 0x400000000000 > 0)\n result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128;\n if (x & 0x200000000000 > 0)\n result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128;\n if (x & 0x100000000000 > 0)\n result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128;\n if (x & 0x80000000000 > 0)\n result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128;\n if (x & 0x40000000000 > 0)\n result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128;\n if (x & 0x20000000000 > 0)\n result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128;\n if (x & 0x10000000000 > 0)\n result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128;\n if (x & 0x8000000000 > 0)\n result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128;\n if (x & 0x4000000000 > 0)\n result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128;\n if (x & 0x2000000000 > 0)\n result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128;\n if (x & 0x1000000000 > 0)\n result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128;\n if (x & 0x800000000 > 0)\n result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128;\n if (x & 0x400000000 > 0)\n result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128;\n if (x & 0x200000000 > 0)\n result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128;\n if (x & 0x100000000 > 0)\n result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128;\n if (x & 0x80000000 > 0)\n result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128;\n if (x & 0x40000000 > 0)\n result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128;\n if (x & 0x20000000 > 0)\n result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128;\n if (x & 0x10000000 > 0)\n result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128;\n if (x & 0x8000000 > 0)\n result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128;\n if (x & 0x4000000 > 0)\n result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128;\n if (x & 0x2000000 > 0)\n result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128;\n if (x & 0x1000000 > 0)\n result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128;\n if (x & 0x800000 > 0)\n result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128;\n if (x & 0x400000 > 0)\n result = result * 0x100000000002C5C85FDF477B662B26945 >> 128;\n if (x & 0x200000 > 0)\n result = result * 0x10000000000162E42FEFA3AE53369388C >> 128;\n if (x & 0x100000 > 0)\n result = result * 0x100000000000B17217F7D1D351A389D40 >> 128;\n if (x & 0x80000 > 0)\n result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128;\n if (x & 0x40000 > 0)\n result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128;\n if (x & 0x20000 > 0)\n result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128;\n if (x & 0x10000 > 0)\n result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128;\n if (x & 0x8000 > 0)\n result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128;\n if (x & 0x4000 > 0)\n result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128;\n if (x & 0x2000 > 0)\n result = result * 0x1000000000000162E42FEFA39F02B772C >> 128;\n if (x & 0x1000 > 0)\n result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128;\n if (x & 0x800 > 0)\n result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128;\n if (x & 0x400 > 0)\n result = result * 0x100000000000002C5C85FDF473DEA871F >> 128;\n if (x & 0x200 > 0)\n result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128;\n if (x & 0x100 > 0)\n result = result * 0x100000000000000B17217F7D1CF79E949 >> 128;\n if (x & 0x80 > 0)\n result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128;\n if (x & 0x40 > 0)\n result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128;\n if (x & 0x20 > 0)\n result = result * 0x100000000000000162E42FEFA39EF366F >> 128;\n if (x & 0x10 > 0)\n result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128;\n if (x & 0x8 > 0)\n result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128;\n if (x & 0x4 > 0)\n result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128;\n if (x & 0x2 > 0)\n result = result * 0x1000000000000000162E42FEFA39EF358 >> 128;\n if (x & 0x1 > 0)\n result = result * 0x10000000000000000B17217F7D1CF79AB >> 128;\n\n result >>= uint256 (63 - (x >> 64));\n require (result <= uint256 (MAX_64x64));\n\n return int128 (result);\n }\n\n /**\n * Calculate natural exponent of x. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function exp (int128 x) internal pure returns (int128) {\n require (x < 0x400000000000000000); // Overflow\n\n if (x < -0x400000000000000000) return 0; // Underflow\n\n return exp_2 (\n int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128));\n }\n\n /**\n * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit\n * integer numbers. Revert on overflow or when y is zero.\n *\n * @param x unsigned 256-bit integer number\n * @param y unsigned 256-bit integer number\n * @return unsigned 64.64-bit fixed point number\n */\n function divuu (uint256 x, uint256 y) private pure returns (uint128) {\n require (y != 0);\n\n uint256 result;\n\n if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)\n result = (x << 64) / y;\n else {\n uint256 msb = 192;\n uint256 xc = x >> 192;\n if (xc >= 0x100000000) { xc >>= 32; msb += 32; }\n if (xc >= 0x10000) { xc >>= 16; msb += 16; }\n if (xc >= 0x100) { xc >>= 8; msb += 8; }\n if (xc >= 0x10) { xc >>= 4; msb += 4; }\n if (xc >= 0x4) { xc >>= 2; msb += 2; }\n if (xc >= 0x2) msb += 1; // No need to shift xc anymore\n\n result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1);\n require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n\n uint256 hi = result * (y >> 128);\n uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n\n uint256 xh = x >> 192;\n uint256 xl = x << 64;\n\n if (xl < lo) xh -= 1;\n xl -= lo; // We rely on overflow behavior here\n lo = hi << 128;\n if (xl < lo) xh -= 1;\n xl -= lo; // We rely on overflow behavior here\n\n assert (xh == hi >> 128);\n\n result += xl / y;\n }\n\n require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return uint128 (result);\n }\n\n /**\n * Calculate x^y assuming 0^0 is 1, where x is unsigned 129.127 fixed point\n * number and y is unsigned 256-bit integer number. Revert on overflow.\n *\n * @param x unsigned 129.127-bit fixed point number\n * @param y uint256 value\n * @return unsigned 129.127-bit fixed point number\n */\n function powu (uint256 x, uint256 y) private pure returns (uint256) {\n if (y == 0) return 0x80000000000000000000000000000000;\n else if (x == 0) return 0;\n else {\n int256 msb = 0;\n uint256 xc = x;\n if (xc >= 0x100000000000000000000000000000000) { xc >>= 128; msb += 128; }\n if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }\n if (xc >= 0x100000000) { xc >>= 32; msb += 32; }\n if (xc >= 0x10000) { xc >>= 16; msb += 16; }\n if (xc >= 0x100) { xc >>= 8; msb += 8; }\n if (xc >= 0x10) { xc >>= 4; msb += 4; }\n if (xc >= 0x4) { xc >>= 2; msb += 2; }\n if (xc >= 0x2) msb += 1; // No need to shift xc anymore\n\n int256 xe = msb - 127;\n if (xe > 0) x >>= uint256 (xe);\n else x <<= uint256 (-xe);\n\n uint256 result = 0x80000000000000000000000000000000;\n int256 re = 0;\n\n while (y > 0) {\n if (y & 1 > 0) {\n result = result * x;\n y -= 1;\n re += xe;\n if (result >=\n 0x8000000000000000000000000000000000000000000000000000000000000000) {\n result >>= 128;\n re += 1;\n } else result >>= 127;\n if (re < -127) return 0; // Underflow\n require (re < 128); // Overflow\n } else {\n x = x * x;\n y >>= 1;\n xe <<= 1;\n if (x >=\n 0x8000000000000000000000000000000000000000000000000000000000000000) {\n x >>= 128;\n xe += 1;\n } else x >>= 127;\n if (xe < -127) return 0; // Underflow\n require (xe < 128); // Overflow\n }\n }\n\n if (re > 0) result <<= uint256 (re);\n else if (re < 0) result >>= uint256 (-re);\n\n return result;\n }\n }\n\n /**\n * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer\n * number.\n *\n * @param x unsigned 256-bit integer number\n * @return unsigned 128-bit integer number\n */\n function sqrtu (uint256 x) private pure returns (uint128) {\n if (x == 0) return 0;\n else {\n uint256 xx = x;\n uint256 r = 1;\n if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; }\n if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; }\n if (xx >= 0x100000000) { xx >>= 32; r <<= 16; }\n if (xx >= 0x10000) { xx >>= 16; r <<= 8; }\n if (xx >= 0x100) { xx >>= 8; r <<= 4; }\n if (xx >= 0x10) { xx >>= 4; r <<= 2; }\n if (xx >= 0x8) { r <<= 1; }\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1; // Seven iterations should be enough\n uint256 r1 = x / r;\n return uint128 (r < r1 ? r : r1);\n }\n }\n}\n"
},
"contracts/characters.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"./Promos.sol\";\nimport \"./util.sol\";\nimport \"./Garrison.sol\";\nimport \"./common.sol\";\n\ncontract Characters is Initializable, ERC721Upgradeable, AccessControlUpgradeable {\n\n using SafeMath for uint16;\n using SafeMath for uint8;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n bytes32 public constant NO_OWNED_LIMIT = keccak256(\"NO_OWNED_LIMIT\");\n\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n\n // Copied from promos.sol, to avoid paying 5k gas to query a constant.\n uint256 private constant BIT_FIRST_CHARACTER = 1;\n\n uint256 public constant VAR_EQUIPMENT_VERSION = 1; // assert nftvar of same name is equal\n\n function initialize () public initializer {\n __ERC721_init(\"CryptoBlades character\", \"CBC\");\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n secondsPerStamina = 300;\n }\n\n function migrateTo_1ee400a(uint256[255] memory _experienceTable) public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n experienceTable = _experienceTable;\n }\n\n function migrateTo_951a020() public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n // Apparently ERC165 interfaces cannot be removed in this version of the OpenZeppelin library.\n // But if we remove the registration, then while local deployments would not register the interface ID,\n // existing deployments on both testnet and mainnet would still be registered to handle it.\n // That sort of inconsistency is a good way to attract bugs that only happens on some environments.\n // Hence, we keep registering the interface despite not actually implementing the interface.\n _registerInterface(0xe62e6974); // TransferCooldownableInterfaceId.interfaceId()\n }\n\n function migrateTo_ef994e2(Promos _promos) public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n promos = _promos;\n }\n\n function migrateTo_b627f23() external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n characterLimit = 4;\n }\n\n function migrateTo_1a19cbb(Garrison _garrison) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n garrison = _garrison;\n }\n\n /*\n visual numbers start at 0, increment values by 1\n levels: 1-256\n traits: 0-3 [0(fire) > 1(earth) > 2(lightning) > 3(water) > repeat]\n */\n\n struct Character {\n uint16 xp; // xp to next level\n uint8 level; // up to 256 cap\n uint8 trait; // 2b trait, TBD\n uint64 staminaTimestamp; // standard timestamp in seconds-resolution marking regen start from 0\n }\n struct CharacterCosmetics {\n uint8 version;\n uint256 seed;\n }\n\n Character[] private tokens;\n CharacterCosmetics[] private cosmetics;\n\n uint256 public constant maxStamina = 200;\n\n uint256[256] private experienceTable; // fastest lookup in the west\n\n // UNUSED; KEPT FOR UPGRADEABILITY PROXY COMPATIBILITY\n mapping(uint256 => uint256) public lastTransferTimestamp;\n\n Promos public promos;\n\n uint256 private lastMintedBlock; // DEPRECATED\n uint256 private firstMintedOfLastBlock; // DEPRECATED\n\n uint256 public characterLimit;\n\n mapping(uint256 => uint256) public raidsDone;\n mapping(uint256 => uint256) public raidsWon;\n\n uint256 public constant NFTVAR_SIMPLEQUEST_PROGRESS = 101;\n uint256 public constant NFTVAR_SIMPLEQUEST_TYPE = 102;\n uint256 public constant NFTVAR_REPUTATION = 103;\n\n uint256 public constant SIMPLEQUEST_TYPE_RAID = 8;\n\n mapping(uint256 => mapping(uint256 => uint256)) public nftVars; // nftID, fieldID, value\n uint256 public constant NFTVAR_BUSY = 1; // value bitflags: 1 (pvp) | 2 (raid) | 4 (TBD)..\n\n Garrison public garrison;\n\n uint256 public constant NFTVAR_BONUS_POWER = 2;\n uint256 public constant NFTVAR_NON_GENESIS_VERSION = 3; // 0 = genesis, 1 = v2\n uint256 public constant NFTVAR_EQUIPMENT_VERSION = 4; // 0 = no equipment/fresh\n /* POWER DATA:\n pve power 24b each: vs Fire, vs Earth, vs Lightning, vs Water, base\n pvp power 14b each tiered: vs Fire, vs Earth, vs Lightning, vs Water\n pvp power 14b each FFA: vs Fire, vs Earth, vs Lightning, vs Water\n level 8b\n traits 2b each: character, weapon, shield\n //246 bits so far\n */\n uint256 public constant NFTVAR_POWER_DATA = 5;\n\n uint256 public secondsPerStamina;\n\n mapping(uint256 => uint256) public vars;\n\n event NewCharacter(uint256 indexed character, address indexed minter);\n event LevelUp(address indexed owner, uint256 indexed character, uint16 level);\n event Burned(address indexed owner, uint256 indexed id);\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"NA\");\n }\n\n modifier minterOnly() {\n _minterOnly();\n _;\n }\n\n function _minterOnly() internal view {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(MINTER_ROLE, msg.sender), 'no access');\n }\n\n function get(uint256 id) public view returns (uint16, uint8, uint8, uint64, uint16, uint16, uint16, uint16, uint16, uint16) {\n Character memory c = tokens[id];\n CharacterCosmetics memory cc = cosmetics[id];\n return (c.xp, c.level, c.trait, c.staminaTimestamp,\n getRandomCosmetic(cc.seed, 1, 13), // head\n getRandomCosmetic(cc.seed, 2, 45), // arms\n getRandomCosmetic(cc.seed, 3, 61), // torso\n getRandomCosmetic(cc.seed, 4, 41), // legs\n getRandomCosmetic(cc.seed, 5, 22), // boots\n getRandomCosmetic(cc.seed, 6, 2) // race\n );\n }\n\n function getRandomCosmetic(uint256 seed, uint256 seed2, uint16 limit) private pure returns (uint16) {\n return uint16(RandomUtil.randomSeededMinMax(0, limit, RandomUtil.combineSeeds(seed, seed2)));\n }\n\n function getCosmeticsSeed(uint256 id) public view returns (uint256) {\n CharacterCosmetics memory cc = cosmetics[id];\n return cc.seed;\n }\n\n function getSoulForBurns(uint256[] calldata burnIds) external view returns (uint256 genesisSoulAmount, uint256 nonGenesisSoulAmount) {\n for(uint i = 0; i < burnIds.length; i++) {\n uint256 power = getTotalPower(burnIds[i]).div(10);\n if(nftVars[burnIds[i]][NFTVAR_NON_GENESIS_VERSION] == 0) {\n genesisSoulAmount += power;\n }\n else {\n nonGenesisSoulAmount += power;\n }\n }\n }\n\n function mint(address minter, uint256 seed) public restricted returns (uint256 tokenID) {\n tokenID = tokens.length;\n uint16 xp = 0;\n uint8 level = 0; // 1\n uint8 trait = uint8(RandomUtil.randomSeededMinMax(0,3,seed));\n uint64 staminaTimestamp = uint64(now.sub(getStaminaMaxWait()));\n\n tokens.push(Character(xp, level, trait, staminaTimestamp));\n cosmetics.push(CharacterCosmetics(0, RandomUtil.combineSeeds(seed, 1)));\n address receiver = minter;\n if(minter != address(0) && minter != address(0x000000000000000000000000000000000000dEaD) && !hasRole(NO_OWNED_LIMIT, minter) && balanceOf(minter) >= characterLimit) {\n receiver = address(garrison);\n garrison.redirectToGarrison(minter, tokenID);\n _mint(address(garrison), tokenID);\n }\n else {\n _mint(minter, tokenID);\n }\n nftVars[tokenID][NFTVAR_NON_GENESIS_VERSION] = 1;\n\n emit NewCharacter(tokenID, receiver);\n }\n\n function customMint(address minter, uint16 xp, uint8 level, uint8 trait, uint256 seed, uint256 tokenID, uint24 bonusPower, uint16 reputation, uint8 version) minterOnly public returns (uint256) {\n uint64 staminaTimestamp = uint64(now); // 0 on purpose to avoid chain jumping abuse\n\n if(tokenID == 0){\n tokenID = tokens.length;\n tokens.push(Character(xp, level, trait, staminaTimestamp));\n cosmetics.push(CharacterCosmetics(0, RandomUtil.combineSeeds(seed, 1)));\n address receiver = minter;\n if(minter != address(0) && minter != address(0x000000000000000000000000000000000000dEaD) && !hasRole(NO_OWNED_LIMIT, minter) && balanceOf(minter) >= characterLimit) {\n receiver = address(garrison);\n garrison.redirectToGarrison(minter, tokenID);\n _mint(address(garrison), tokenID);\n }\n else {\n _mint(minter, tokenID);\n }\n emit NewCharacter(tokenID, receiver);\n }\n else {\n Character storage ch = tokens[tokenID];\n ch.xp = xp;\n ch.level = level;\n ch.trait = trait;\n ch.staminaTimestamp = staminaTimestamp;\n\n CharacterCosmetics storage cc = cosmetics[tokenID];\n cc.seed = seed;\n }\n\n nftVars[tokenID][NFTVAR_BONUS_POWER] = bonusPower;\n nftVars[tokenID][NFTVAR_REPUTATION] = reputation;\n nftVars[tokenID][NFTVAR_NON_GENESIS_VERSION] = version;\n\n return tokenID;\n }\n\n function burnIntoCharacter(uint256[] calldata burnIds, uint256 targetCharId, uint256 burnPowerMultiplier) external restricted {\n uint256 burnPower = 0;\n for(uint i = 0; i < burnIds.length; i++) {\n require(nftVars[burnIds[i]][NFTVAR_NON_GENESIS_VERSION] == nftVars[targetCharId][NFTVAR_NON_GENESIS_VERSION], \"Character version mismatch\");\n burnPower += nftVars[burnIds[i]][NFTVAR_BONUS_POWER].add(getPowerAtLevel(tokens[burnIds[i]].level));\n address burnOwner = ownerOf(burnIds[i]);\n if(burnOwner == address(garrison)) {\n burnOwner = garrison.characterOwner(burnIds[i]);\n garrison.updateOnBurn(burnOwner, burnIds[i]);\n }\n _burn(burnIds[i]);\n\n emit Burned(\n burnOwner,\n burnIds[i]\n );\n }\n require(uint(4).mul(getPowerAtLevel(tokens[targetCharId].level)) >= getTotalPower(targetCharId).add(burnPower), \"Power limit\");\n nftVars[targetCharId][NFTVAR_BONUS_POWER] = burnPower.mul(burnPowerMultiplier).div(1e18).add(nftVars[targetCharId][NFTVAR_BONUS_POWER]);\n }\n\n function burnIntoSoul(uint256[] calldata burnIds) external restricted {\n for(uint i = 0; i < burnIds.length; i++) {\n address burnOwner = ownerOf(burnIds[i]);\n if(burnOwner == address(garrison)) {\n burnOwner = garrison.characterOwner(burnIds[i]);\n garrison.updateOnBurn(burnOwner, burnIds[i]);\n }\n _burn(burnIds[i]);\n\n emit Burned(\n burnOwner,\n burnIds[i]\n );\n }\n }\n\n function upgradeWithSoul(uint256 targetCharId, uint256 soulAmount, bool isCharacterGenesis) external restricted {\n if(isCharacterGenesis) {\n require(nftVars[targetCharId][NFTVAR_NON_GENESIS_VERSION] == 0);\n }\n else {\n require(nftVars[targetCharId][NFTVAR_NON_GENESIS_VERSION] > 0);\n }\n uint256 burnPower = soulAmount.mul(10);\n require(uint(4).mul(getPowerAtLevel(tokens[targetCharId].level)) >= getTotalPower(targetCharId).add(burnPower), \"Power limit\");\n nftVars[targetCharId][NFTVAR_BONUS_POWER] = burnPower.add(nftVars[targetCharId][NFTVAR_BONUS_POWER]);\n }\n\n function getLevel(uint256 id) public view returns (uint8) {\n return tokens[id].level; // this is used by dataminers and it benefits us\n }\n\n function getRequiredXpForNextLevel(uint8 currentLevel) public view returns (uint16) {\n return uint16(experienceTable[currentLevel]); // this is helpful to users as the array is private\n }\n\n function getPower(uint256 id) public view returns (uint24) {\n return getPowerAtLevel(tokens[id].level);\n }\n\n function getTotalPower(uint256 id) public view returns (uint256) {\n return nftVars[id][NFTVAR_BONUS_POWER].add(getPowerAtLevel(tokens[id].level));\n }\n\n function getPowerAtLevel(uint8 level) public pure returns (uint24) {\n return Common.getPowerAtLevel(level);\n }\n\n function getTrait(uint256 id) public view returns (uint8) {\n return tokens[id].trait;\n }\n\n function setTrait(uint256 id, uint8 trait) public restricted {\n tokens[id].trait = trait;\n // require a recalculation on equipment\n nftVars[id][NFTVAR_EQUIPMENT_VERSION] = 0;\n }\n\n function getXp(uint256 id) public view returns (uint32) {\n return tokens[id].xp;\n }\n\n function gainXp(uint256 id, uint16 xp) public restricted {\n _gainXp(id, xp);\n }\n\n function _gainXp(uint256 id, uint256 xp) internal {\n // NOTE: Levelups invalidate power data, but recalculating is optional\n Character storage char = tokens[id];\n if (char.level < 255) {\n uint newXp = char.xp.add(xp);\n uint requiredToLevel = experienceTable[char.level]; // technically next level\n while (newXp >= requiredToLevel) {\n newXp = newXp - requiredToLevel;\n char.level += 1;\n emit LevelUp(ownerOf(id), id, char.level);\n if (char.level < 255)\n requiredToLevel = experienceTable[char.level];\n else newXp = 0;\n }\n char.xp = uint16(newXp);\n }\n }\n\n function gainXpAll(uint256[] calldata chars, uint256[] calldata xps) external restricted {\n for(uint i = 0; i < chars.length; i++)\n _gainXp(chars[i], xps[i]);\n }\n\n function getStaminaTimestamp(uint256 id) public view returns (uint64) {\n return tokens[id].staminaTimestamp;\n }\n\n function setStaminaTimestamp(uint256 id, uint64 timestamp) public restricted {\n tokens[id].staminaTimestamp = timestamp;\n }\n\n function getStaminaPoints(uint256 id) public view returns (uint8) {\n return getStaminaPointsFromTimestamp(tokens[id].staminaTimestamp);\n }\n\n function getStaminaPointsFromTimestamp(uint64 timestamp) public view returns (uint8) {\n if(timestamp > now)\n return 0;\n\n uint256 points = (now - timestamp) / secondsPerStamina;\n if(points > maxStamina) {\n points = maxStamina;\n }\n return uint8(points);\n }\n\n function isStaminaFull(uint256 id) public view returns (bool) {\n return getStaminaPoints(id) >= maxStamina;\n }\n\n function getStaminaMaxWait() public view returns (uint64) {\n return uint64(maxStamina * secondsPerStamina);\n }\n\n function getFightDataAndDrainStamina(address fighter,\n uint256 id, uint8 amount, bool allowNegativeStamina, uint256 busyFlag) public restricted returns(uint72, uint256) {\n require(fighter == ownerOf(id)/* && nftVars[id][NFTVAR_BUSY] == 0*/ && isEquipmentReady(id));\n //nftVars[id][NFTVAR_BUSY] |= busyFlag;\n\n Character storage char = tokens[id];\n uint8 staminaPoints = getStaminaPointsFromTimestamp(char.staminaTimestamp);\n require((staminaPoints > 0 && allowNegativeStamina) // we allow going into negative, but not starting negative\n || staminaPoints >= amount, \"Not enough stamina!\");\n\n uint64 drainTime = uint64(amount * secondsPerStamina);\n uint64 preTimestamp = char.staminaTimestamp;\n if(staminaPoints >= maxStamina) { // if stamina full, we reset timestamp and drain from that\n char.staminaTimestamp = uint64(now - getStaminaMaxWait() + drainTime);\n }\n else {\n char.staminaTimestamp = uint64(char.staminaTimestamp + drainTime);\n }\n // bitwise magic to avoid stacking limitations later on\n return (uint72(preTimestamp | (nftVars[id][NFTVAR_NON_GENESIS_VERSION] << 64)),\n nftVars[id][NFTVAR_POWER_DATA]);//contains traits at bits 240,242,244 for char,wep,shield\n }\n\n function processRaidParticipation(uint256 id, bool won, uint16 xp) public restricted {\n raidsDone[id] = raidsDone[id] + 1;\n raidsWon[id] = won ? (raidsWon[id] + 1) : (raidsWon[id]);\n require(nftVars[id][NFTVAR_BUSY] == 0); // raids do not apply busy flag for now\n //nftVars[id][NFTVAR_BUSY] = 0;\n _gainXp(id, xp);\n if (getNftVar(id, NFTVAR_SIMPLEQUEST_TYPE) == SIMPLEQUEST_TYPE_RAID) {\n uint currentProgress = getNftVar(id, NFTVAR_SIMPLEQUEST_PROGRESS);\n setNftVar(id, NFTVAR_SIMPLEQUEST_PROGRESS, ++currentProgress);\n }\n }\n\n function isEquipmentReady(uint256 id) public view returns (bool) {\n uint ev = vars[VAR_EQUIPMENT_VERSION];\n return nftVars[id][NFTVAR_EQUIPMENT_VERSION] == ev && ev != 0;\n }\n\n function getCharactersOwnedBy(address wallet) public view returns(uint256[] memory chars) {\n uint256 count = balanceOf(wallet);\n chars = new uint256[](count);\n for(uint256 i = 0; i < count; i++)\n chars[i] = tokenOfOwnerByIndex(wallet, i);\n }\n\n function getReadyCharacters(address wallet) public view returns(uint256[] memory chars) {\n uint256[] memory owned = getCharactersOwnedBy(wallet);\n uint256 ready = 0;\n for(uint i = 0; i < owned.length; i++)\n if(nftVars[owned[i]][NFTVAR_BUSY] == 0)\n ready++;\n chars = new uint[](ready);\n for(uint i = 0; i < owned.length; i++)\n if(nftVars[owned[i]][NFTVAR_BUSY] == 0)\n chars[--ready] = owned[i];\n }\n\n function setNFTVars(uint256 id, uint256[] memory fields, uint256[] memory values) public restricted {\n for(uint i = 0; i < fields.length; i++)\n nftVars[id][fields[i]] = values[i];\n }\n\n function getNFTVars(uint256 id, uint256[] memory fields) public view returns(uint256[] memory values) {\n values = new uint256[](fields.length);\n for(uint i = 0; i < fields.length; i++)\n values[i] = nftVars[id][fields[i]];\n }\n\n function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override {\n require(nftVars[tokenId][NFTVAR_BUSY] == 0);\n address[] memory users = new address[](2);\n users[0] = from;\n users[1] = to;\n promos.setBits(users, BIT_FIRST_CHARACTER);\n }\n\n function safeTransferFrom(address from, address to, uint256 tokenId) override public {\n if (ownerOf(tokenId) != from) { // if not owner, then probably garrison\n garrison.transferFromGarrison(to, tokenId);\n } else {\n if (to != address(0) && to != address(0x000000000000000000000000000000000000dEaD) && !hasRole(NO_OWNED_LIMIT, to) && balanceOf(to) >= characterLimit) {\n garrison.redirectToGarrison(to, tokenId);\n super.safeTransferFrom(from, address(garrison), tokenId);\n }\n else {\n super.safeTransferFrom(from, to, tokenId);\n }\n }\n }\n\n function setCharacterLimit(uint256 max) public restricted {\n characterLimit = max;\n }\n\n function setVar(uint256 varField, uint256 value) external restricted {\n vars[varField] = value;\n }\n\n function setVars(uint256[] calldata varFields, uint256[] calldata values) external restricted {\n for(uint i = 0; i < varFields.length; i++) {\n vars[varFields[i]] = values[i];\n }\n }\n\n function getNftVar(uint256 characterID, uint256 nftVar) public view returns(uint256) {\n return nftVars[characterID][nftVar];\n }\n function setNftVar(uint256 characterID, uint256 nftVar, uint256 value) public restricted {\n nftVars[characterID][nftVar] = value;\n }\n\n function setBaseURI(string memory baseUri) public restricted {\n _setBaseURI(baseUri);\n }\n\n function setSecondsPerStamina(uint256 _secondsPerStamina) external restricted {\n secondsPerStamina = _secondsPerStamina;\n }\n}\n"
},
"contracts/weapons.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"./Promos.sol\";\nimport \"./util.sol\";\n\ncontract Weapons is Initializable, ERC721Upgradeable, AccessControlUpgradeable {\n\n using ABDKMath64x64 for int128;\n using ABDKMath64x64 for uint16;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n\n function initialize () public initializer {\n __ERC721_init(\"CryptoBlades weapon\", \"CBW\");\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n }\n\n function migrateTo_e55d8c5() public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n burnPointMultiplier = 2;\n lowStarBurnPowerPerPoint = 15;\n fourStarBurnPowerPerPoint = 30;\n fiveStarBurnPowerPerPoint = 60;\n }\n\n function migrateTo_aa9da90() public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n oneFrac = ABDKMath64x64.fromUInt(1);\n powerMultPerPointBasic = ABDKMath64x64.divu(1, 400);// 0.25%\n powerMultPerPointPWR = powerMultPerPointBasic.mul(ABDKMath64x64.divu(103, 100)); // 0.2575% (+3%)\n powerMultPerPointMatching = powerMultPerPointBasic.mul(ABDKMath64x64.divu(107, 100)); // 0.2675% (+7%)\n }\n\n function migrateTo_951a020() public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n // Apparently ERC165 interfaces cannot be removed in this version of the OpenZeppelin library.\n // But if we remove the registration, then while local deployments would not register the interface ID,\n // existing deployments on both testnet and mainnet would still be registered to handle it.\n // That sort of inconsistency is a good way to attract bugs that only happens on some environments.\n // Hence, we keep registering the interface despite not actually implementing the interface.\n _registerInterface(0xe62e6974); // TransferCooldownableInterfaceId.interfaceId()\n }\n\n function migrateTo_surprise(Promos _promos) public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n promos = _promos;\n }\n\n /*\n visual numbers start at 0, increment values by 1\n levels: 1-128\n stars: 1-5 (1,2,3: primary only, 4: one secondary, 5: two secondaries)\n traits: 0-3 [0(fire) > 1(earth) > 2(lightning) > 3(water) > repeat]\n stats: STR(fire), DEX(earth), CHA(lightning), INT(water), PWR(traitless)\n base stat rolls: 1*(1-50), 2*(45-75), 3*(70-100), 4*(50-100), 5*(66-100, main is 68-100)\n burns: add level & main stat, and 50% chance to increase secondaries\n power: each point contributes .25% to fight power\n cosmetics: 0-255 but only 24 is used, may want to cap so future expansions dont change existing weps\n */\n\n struct Weapon {\n uint16 properties; // right to left: 3b stars, 2b trait, 7b stat pattern, 4b EMPTY\n // stats (each point refers to .25% improvement)\n uint16 stat1;\n uint16 stat2;\n uint16 stat3;\n uint8 level; // separate from stat1 because stat1 will have a pre-roll\n }\n\n struct WeaponBurnPoints {\n uint8 lowStarBurnPoints;\n uint8 fourStarBurnPoints;\n uint8 fiveStarBurnPoints;\n }\n\n struct WeaponCosmetics {\n uint8 version;\n uint256 seed;\n }\n\n Weapon[] private tokens;\n WeaponCosmetics[] private cosmetics;\n mapping(uint256 => WeaponBurnPoints) burnPoints;\n\n uint public burnPointMultiplier; // 2\n uint public lowStarBurnPowerPerPoint; // 15\n uint public fourStarBurnPowerPerPoint; // 30\n uint public fiveStarBurnPowerPerPoint; // 60\n\n int128 internal oneFrac; // 1.0\n int128 internal powerMultPerPointBasic; // 0.25%\n int128 internal powerMultPerPointPWR; // 0.2575% (+3%)\n int128 internal powerMultPerPointMatching; // 0.2675% (+7%)\n\n // UNUSED; KEPT FOR UPGRADEABILITY PROXY COMPATIBILITY\n mapping(uint256 => uint256) public lastTransferTimestamp;\n\n uint256 private lastMintedBlock; // DEPRECATED\n uint256 private firstMintedOfLastBlock; // DEPRECATED\n\n mapping(uint256 => uint64) durabilityTimestamp;\n\n uint256 public constant maxDurability = 20;\n uint256 public constant secondsPerDurability = 3000; //50 * 60\n\n mapping(address => uint256) burnDust; // user address : burned item dust counts\n\n Promos public promos;\n\n mapping(uint256 => uint256) public numberParameters;\n\n mapping(uint256 => mapping(uint256 => uint256)) public nftVars;//KEYS: NFTID, VARID\n uint256 public constant NFTVAR_BUSY = 1; // value bitflags: 1 (pvp) | 2 (raid) | 4 (TBD)..\n uint256 public constant NFTVAR_WEAPON_TYPE = 2; // x = 0: normal, x > 0: special for partner id x\n\n event Burned(address indexed owner, uint256 indexed burned);\n event NewWeapon(uint256 indexed weapon, address indexed minter, uint24 weaponType);\n event Reforged(address indexed owner, uint256 indexed reforged, uint256 indexed burned, uint8 lowPoints, uint8 fourPoints, uint8 fivePoints);\n event ReforgedWithDust(address indexed owner, uint256 indexed reforged, uint8 lowDust, uint8 fourDust, uint8 fiveDust, uint8 lowPoints, uint8 fourPoints, uint8 fivePoints);\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"NR\");\n }\n\n modifier minterOnly() {\n _minterOnly();\n _;\n }\n\n function _minterOnly() internal view {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(MINTER_ROLE, msg.sender), \"NR\");\n }\n\n function getStats(uint256 id) internal view\n returns (uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3, uint8 _level) {\n\n Weapon memory w = tokens[id];\n return (w.properties, w.stat1, w.stat2, w.stat3, w.level);\n }\n\n function getCosmetics(uint256 id) internal view returns (uint8 _blade, uint8 _crossguard, uint8 _grip, uint8 _pommel) {\n\n WeaponCosmetics memory wc = cosmetics[id];\n _blade = getRandomCosmetic(wc.seed, 1, 24);\n _crossguard = getRandomCosmetic(wc.seed, 2, 24);\n _grip = getRandomCosmetic(wc.seed, 3, 24);\n _pommel = getRandomCosmetic(wc.seed, 4, 24);\n }\n\n function getCosmeticsSeed(uint256 id) public view returns (uint256) {\n\n WeaponCosmetics memory wc = cosmetics[id];\n return wc.seed;\n }\n\n function get(uint256 id) public view\n returns (\n uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3, uint8 _level,\n uint32 _cosmetics,\n uint24 _burnPoints, // burn points.. got stack limits so i put them together\n uint24 _bonusPower, // bonus power\n uint24 _weaponType // weapon type for special weapons\n ) {\n return _get(id);\n }\n\n function _get(uint256 id) internal view\n returns (\n uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3, uint8 _level,\n uint32 _cosmetics, // cosmetics put together to avoid stack too deep errors\n uint24 _burnPoints, // burn points.. got stack limits so i put them together\n uint24 _bonusPower, // bonus power\n uint24 _weaponType // weapon type for special weapons\n ) {\n (_properties, _stat1, _stat2, _stat3, _level) = getStats(id);\n\n // scope to avoid stack too deep errors\n {\n (uint8 _blade, uint8 _crossguard, uint8 _grip, uint8 _pommel) = getCosmetics(id);\n _cosmetics = uint32(_blade) | (uint32(_crossguard) << 8) | (uint32(_grip) << 16) | (uint32(_pommel) << 24);\n }\n\n WeaponBurnPoints memory wbp = burnPoints[id];\n _burnPoints =\n uint24(wbp.lowStarBurnPoints) |\n (uint24(wbp.fourStarBurnPoints) << 8) |\n (uint24(wbp.fiveStarBurnPoints) << 16);\n\n _bonusPower = getBonusPower(id);\n _weaponType = getWeaponType(id);\n }\n\n function setBaseURI(string memory baseUri) public restricted {\n _setBaseURI(baseUri);\n }\n\n function mintN(address minter, uint32 amount, uint256 seed, uint8 chosenElement) public restricted {\n for(uint i = 0; i < amount; i++)\n mint(minter, RandomUtil.combineSeeds(seed,i), chosenElement);\n }\n\n function mint(address minter, uint256 seed, uint8 chosenElement) public minterOnly returns(uint256) {\n uint256 stars;\n uint256 roll = seed % 100;\n // will need revision, possibly manual configuration if we support more than 5 stars\n if(roll < 1) {\n stars = 4; // 5* at 1%\n }\n else if(roll < 6) { // 4* at 5%\n stars = 3;\n }\n else if(roll < 21) { // 3* at 15%\n stars = 2;\n }\n else if(roll < 56) { // 2* at 35%\n stars = 1;\n }\n else {\n stars = 0; // 1* at 44%\n }\n\n return mintWeaponWithStars(minter, stars, seed, chosenElement);\n }\n\n function mintSpecialWeapon(address minter, uint256 eventId, uint256 stars, uint256 seed, uint8 element) external minterOnly returns(uint256) {\n require(stars < 8);\n (uint16 stat1, uint16 stat2, uint16 stat3) = getStatRolls(stars, seed);\n\n return performMintWeapon(minter,\n eventId,\n getRandomProperties(stars, seed, element),\n stat1,\n stat2,\n stat3,\n RandomUtil.combineSeeds(seed,3)\n );\n }\n\n function mintGiveawayWeapon(address to, uint256 stars, uint8 chosenElement) external minterOnly returns(uint256) {\n // MANUAL USE ONLY; DO NOT USE IN CONTRACTS!\n return mintWeaponWithStars(to, stars, uint256(keccak256(abi.encodePacked(now, tokens.length))), chosenElement);\n }\n\n function mintWeaponWithStars(address minter, uint256 stars, uint256 seed, uint8 chosenElement) public minterOnly returns(uint256) {\n require(stars < 8);\n require(chosenElement == 100 || (chosenElement>= 0 && chosenElement<= 3));\n (uint16 stat1, uint16 stat2, uint16 stat3) = getStatRolls(stars, seed);\n\n return performMintWeapon(minter,\n 0,\n getRandomProperties(stars, seed, chosenElement),\n stat1,\n stat2,\n stat3,\n RandomUtil.combineSeeds(seed,3)\n );\n }\n\n function performMintWeapon(address minter,\n uint256 weaponType,\n uint16 properties,\n uint16 stat1, uint16 stat2, uint16 stat3,\n uint256 cosmeticSeed\n ) public minterOnly returns(uint256 tokenID) {\n tokenID = tokens.length;\n tokens.push(Weapon(properties, stat1, stat2, stat3, 0));\n cosmetics.push(WeaponCosmetics(0, cosmeticSeed));\n _mint(minter, tokenID);\n durabilityTimestamp[tokenID] = uint64(now.sub(getDurabilityMaxWait()));\n nftVars[tokenID][NFTVAR_WEAPON_TYPE] = weaponType;\n\n emit NewWeapon(tokenID, minter, uint24(weaponType));\n }\n\n function performMintWeaponDetailed(address minter, uint256 metaData, uint256 cosmeticSeed, uint256 tokenID) public minterOnly returns(uint256) {\n uint8 fiveStarBurnPoints = uint8(metaData & 0xFF);\n uint8 fourStarBurnPoints = uint8((metaData >> 8) & 0xFF);\n uint8 lowStarBurnPoints = uint8((metaData >> 16) & 0xFF);\n uint8 level = uint8((metaData >> 24) & 0xFF);\n uint16 stat3 = uint16((metaData >> 32) & 0xFFFF);\n uint16 stat2 = uint16((metaData >> 48) & 0xFFFF);\n uint16 stat1 = uint16((metaData >> 64) & 0xFFFF);\n uint16 properties = uint16((metaData >> 80) & 0xFFFF);\n uint24 weaponType = uint24((metaData >> 128) & 0xFFFFFF);\n\n require(lowStarBurnPoints <= 100 && fourStarBurnPoints <= 25 && fiveStarBurnPoints <= 10);\n\n if(tokenID == 0){\n tokenID = performMintWeapon(minter, weaponType, properties, stat1, stat2, stat3, 0);\n }\n else {\n Weapon storage wp = tokens[tokenID];\n wp.properties = properties;\n wp.stat1 = stat1;\n wp.stat2 = stat2;\n wp.stat3 = stat3;\n wp.level = level;\n }\n WeaponCosmetics storage wc = cosmetics[tokenID];\n wc.seed = cosmeticSeed;\n\n tokens[tokenID].level = level;\n durabilityTimestamp[tokenID] = uint64(now); // avoid chain jumping abuse\n WeaponBurnPoints storage wbp = burnPoints[tokenID];\n\n wbp.lowStarBurnPoints = lowStarBurnPoints;\n wbp.fourStarBurnPoints = fourStarBurnPoints;\n wbp.fiveStarBurnPoints = fiveStarBurnPoints;\n\n return tokenID;\n }\n\n function getRandomProperties(uint256 stars, uint256 seed, uint8 chosenElement) internal pure returns (uint16) {\n uint256 trait;\n if (chosenElement == 100) {\n trait = ((RandomUtil.randomSeededMinMax(0,3,RandomUtil.combineSeeds(seed,1)) & 0x3) << 3);\n } else {\n trait = ((chosenElement & 0x3) << 3);\n }\n return uint16((stars & 0x7) // stars aren't randomized here!\n | trait // trait\n | ((RandomUtil.randomSeededMinMax(0,124,RandomUtil.combineSeeds(seed,2)) & 0x7F) << 5)); // statPattern\n }\n\n function getStatRolls(uint256 stars, uint256 seed) private pure returns (uint16, uint16, uint16) {\n // each point refers to .25%\n // so 1 * 4 is 1%\n uint16 minRoll = getStatMinRoll(stars);\n uint16 maxRoll = getStatMaxRoll(stars);\n uint8 statCount = getStatCount(stars);\n\n uint16 stat1 = getRandomStat(minRoll, maxRoll, seed, 5);\n uint16 stat2 = 0;\n uint16 stat3 = 0;\n if(statCount > 1) {\n stat2 = getRandomStat(minRoll, maxRoll, seed, 3);\n }\n if(statCount > 2) {\n stat3 = getRandomStat(minRoll, maxRoll, seed, 4);\n }\n return (stat1, stat2, stat3);\n }\n\n function getRandomStat(uint16 minRoll, uint16 maxRoll, uint256 seed, uint256 seed2) internal pure returns (uint16) {\n return uint16(RandomUtil.randomSeededMinMax(minRoll, maxRoll,RandomUtil.combineSeeds(seed, seed2)));\n }\n\n function getRandomCosmetic(uint256 seed, uint256 seed2, uint8 limit) internal pure returns (uint8) {\n return uint8(RandomUtil.randomSeededMinMax(0, limit, RandomUtil.combineSeeds(seed, seed2)));\n }\n\n function getStatMinRoll(uint256 stars) internal pure returns (uint16) {\n // 1 star\n if (stars == 0) return 4;\n // 2 star\n if (stars == 1) return 180;\n // 3 star\n if (stars == 2) return 280;\n // 4 star\n if (stars == 3) return 200;\n // 5+ star\n return 268;\n }\n\n function getStatMaxRoll(uint256 stars) internal pure returns (uint16) {\n // 3+ star\n if (stars > 1) return 400;\n // 2 star\n if (stars > 0) return 300;\n // 1 star\n return 200;\n }\n\n function getStatCount(uint256 stars) internal pure returns (uint8) {\n // 1-2 star\n if (stars < 3) return 1;\n // 3+ star\n return uint8(stars)-1;\n }\n\n function getProperties(uint256 id) public view returns (uint16) {\n return tokens[id].properties;\n }\n\n function getStars(uint256 id) public view returns (uint8) {\n return getStarsFromProperties(getProperties(id));\n }\n\n function getStarsFromProperties(uint16 properties) internal pure returns (uint8) {\n return uint8(properties & 0x7); // first two bits for stars\n }\n\n function getTrait(uint256 id) public view returns (uint8) {\n return getTraitFromProperties(getProperties(id));\n }\n\n function getTraitFromProperties(uint16 properties) internal pure returns (uint8) {\n return uint8((properties >> 3) & 0x3); // two bits after star bits (3)\n }\n\n function getStatPattern(uint256 id) public view returns (uint8) {\n return getStatPatternFromProperties(getProperties(id));\n }\n\n function getStatPatternFromProperties(uint16 properties) internal pure returns (uint8) {\n return uint8((properties >> 5) & 0x7F); // 7 bits after star(3) and trait(2) bits\n }\n\n function getStat1Trait(uint8 statPattern) internal pure returns (uint8) {\n return uint8(uint256(statPattern) % 5); // 0-3 regular traits, 4 = traitless (PWR)\n }\n\n function getStat2Trait(uint8 statPattern) internal pure returns (uint8) {\n return uint8(SafeMath.div(statPattern, 5) % 5); // 0-3 regular traits, 4 = traitless (PWR)\n }\n\n function getStat3Trait(uint8 statPattern) internal pure returns (uint8) {\n return uint8(SafeMath.div(statPattern, 25) % 5); // 0-3 regular traits, 4 = traitless (PWR)\n }\n\n function getLevel(uint256 id) public view returns (uint8) {\n return tokens[id].level;\n }\n\n function getStat1(uint256 id) public view returns (uint16) {\n return tokens[id].stat1;\n }\n\n function getStat2(uint256 id) public view returns (uint16) {\n return tokens[id].stat2;\n }\n\n function getStat3(uint256 id) public view returns (uint16) {\n return tokens[id].stat3;\n }\n\n function getPowerMultiplier(uint256 id) public view returns (int128) {\n // returns a 64.64 fixed point number for power multiplier\n // this function does not account for traits\n // it is used to calculate base enemy powers for targeting\n Weapon memory wep = tokens[id];\n int128 powerPerPoint = ABDKMath64x64.divu(1, 400); // 0.25% or x0.0025\n int128 stat1 = wep.stat1.fromUInt().mul(powerPerPoint);\n int128 stat2 = wep.stat2.fromUInt().mul(powerPerPoint);\n int128 stat3 = wep.stat3.fromUInt().mul(powerPerPoint);\n return ABDKMath64x64.fromUInt(1).add(stat1).add(stat2).add(stat3);\n }\n\n function getPowerMultiplierForTrait(\n uint16 properties,\n uint16 stat1,\n uint16 stat2,\n uint16 stat3,\n uint8 trait\n ) public view returns(int128) {\n // Does not include character trait to weapon trait match\n // Only counts arbitrary trait to weapon stat trait\n // This function can be used by frontend to get expected % bonus for each type\n // Making it easy to see on the market how useful it will be to you\n uint8 statPattern = getStatPatternFromProperties(properties);\n int128 result = oneFrac;\n\n if(getStat1Trait(statPattern) == trait)\n result = result.add(stat1.fromUInt().mul(powerMultPerPointMatching));\n else if(getStat1Trait(statPattern) == 4) // PWR, traitless\n result = result.add(stat1.fromUInt().mul(powerMultPerPointPWR));\n else\n result = result.add(stat1.fromUInt().mul(powerMultPerPointBasic));\n\n if(getStat2Trait(statPattern) == trait)\n result = result.add(stat2.fromUInt().mul(powerMultPerPointMatching));\n else if(getStat2Trait(statPattern) == 4) // PWR, traitless\n result = result.add(stat2.fromUInt().mul(powerMultPerPointPWR));\n else\n result = result.add(stat2.fromUInt().mul(powerMultPerPointBasic));\n\n if(getStat3Trait(statPattern) == trait)\n result = result.add(stat3.fromUInt().mul(powerMultPerPointMatching));\n else if(getStat3Trait(statPattern) == 4) // PWR, traitless\n result = result.add(stat3.fromUInt().mul(powerMultPerPointPWR));\n else\n result = result.add(stat3.fromUInt().mul(powerMultPerPointBasic));\n\n return result;\n }\n\n function getDustSupplies(address playerAddress) public view returns (uint32[] memory) {\n uint256 burnDustValue = burnDust[playerAddress];\n uint32[] memory supplies = new uint32[](3);\n supplies[0] = uint32(burnDustValue);\n supplies[1] = uint32(burnDustValue >> 32);\n supplies[2] = uint32(burnDustValue >> 64);\n return supplies;\n }\n\n function _setDustSupplies(address playerAddress, uint32 amountLB, uint32 amount4B, uint32 amount5B) internal {\n uint256 burnDustValue = (uint256(amount5B) << 64) + (uint256(amount4B) << 32) + amountLB;\n burnDust[playerAddress] = burnDustValue;\n }\n\n function decrementDustSupplies(address playerAddress, uint32 amountLB, uint32 amount4B, uint32 amount5B) public restricted {\n _decrementDustSupplies(playerAddress, amountLB, amount4B, amount5B);\n }\n\n function _decrementDustSupplies(address playerAddress, uint32 amountLB, uint32 amount4B, uint32 amount5B) internal {\n uint32[] memory supplies = getDustSupplies(playerAddress);\n require(supplies[0] >= amountLB && supplies[1] >= amount4B && supplies[2] >= amount5B);\n supplies[0] -= amountLB;\n supplies[1] -= amount4B;\n supplies[2] -= amount5B;\n _setDustSupplies(playerAddress, supplies[0], supplies[1], supplies[2]);\n }\n\n function incrementDustSupplies(address playerAddress, uint32 amountLB, uint32 amount4B, uint32 amount5B) public restricted {\n _incrementDustSupplies(playerAddress, amountLB, amount4B, amount5B);\n }\n\n function _incrementDustSupplies(address playerAddress, uint32 amountLB, uint32 amount4B, uint32 amount5B) internal {\n uint32[] memory supplies = getDustSupplies(playerAddress);\n require(uint256(supplies[0]) + amountLB <= 0xFFFFFFFF\n && uint256(supplies[1]) + amount4B <= 0xFFFFFFFF\n && uint256(supplies[2]) + amount5B <= 0xFFFFFFFF);\n supplies[0] += amountLB;\n supplies[1] += amount4B;\n supplies[2] += amount5B;\n _setDustSupplies(playerAddress, supplies[0], supplies[1], supplies[2]);\n }\n\n function _calculateBurnValues(uint256 burnID) public view returns(uint8[] memory) {\n uint8[] memory values = new uint8[](3);\n\n // Carried burn points.\n WeaponBurnPoints storage burningbp = burnPoints[burnID];\n values[0] = (burningbp.lowStarBurnPoints + 1) / 2;\n values[1] = (burningbp.fourStarBurnPoints + 1) / 2;\n values[2] = (burningbp.fiveStarBurnPoints + 1) / 2;\n\n // Stars-based burn points.\n Weapon storage burning = tokens[burnID];\n uint8 stars = getStarsFromProperties(burning.properties);\n if(stars < 3) { // 1-3 star\n values[0] += uint8(burnPointMultiplier * (stars + 1));\n }\n else if(stars == 3) { // 4 star\n values[1] += uint8(burnPointMultiplier);\n }\n else if(stars == 4) { // 5 star\n values[2] += uint8(burnPointMultiplier);\n }\n\n return values;\n }\n\n function burn(uint256 burnID) public restricted {\n uint8[] memory values = _calculateBurnValues(burnID);\n\n address burnOwner = ownerOf(burnID);\n\n _burn(burnID);\n if(promos.getBit(burnOwner, 4) == false)\n _incrementDustSupplies(burnOwner, values[0], values[1], values[2]);\n\n emit Burned(\n burnOwner,\n burnID\n );\n }\n\n function burnWithoutDust(uint256[] memory burnIDs) public restricted {\n for(uint256 i = 0; i < burnIDs.length; i++) {\n _burnWithoutDust(burnIDs[i]);\n }\n }\n\n function _burnWithoutDust(uint256 burnID) internal {\n address burnOwner = ownerOf(burnID);\n _burn(burnID);\n emit Burned(burnOwner, burnID);\n }\n\n function reforge(uint256 reforgeID, uint256 burnID) public restricted {\n uint8[] memory values = _calculateBurnValues(burnID);\n\n // Note: preexisting issue of applying burn points even if _burn fails.\n if(promos.getBit(ownerOf(reforgeID), 4) == false)\n _applyBurnPoints(reforgeID, values[0], values[1], values[2]);\n _burn(burnID);\n\n WeaponBurnPoints storage wbp = burnPoints[reforgeID];\n emit Reforged(\n ownerOf(reforgeID),\n reforgeID,\n burnID,\n wbp.lowStarBurnPoints,\n wbp.fourStarBurnPoints,\n wbp.fiveStarBurnPoints\n );\n }\n\n function reforgeWithDust(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) public restricted {\n\n if(promos.getBit(ownerOf(reforgeID), 4) == false)\n _applyBurnPoints(reforgeID, amountLB, amount4B, amount5B);\n _decrementDustSupplies(ownerOf(reforgeID), amountLB, amount4B, amount5B);\n\n WeaponBurnPoints storage wbp = burnPoints[reforgeID];\n emit ReforgedWithDust(\n ownerOf(reforgeID),\n reforgeID,\n amountLB,\n amount4B,\n amount5B,\n wbp.lowStarBurnPoints,\n wbp.fourStarBurnPoints,\n wbp.fiveStarBurnPoints\n );\n }\n\n function _applyBurnPoints(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) private {\n WeaponBurnPoints storage wbp = burnPoints[reforgeID];\n\n if(amountLB > 0) {\n require(wbp.lowStarBurnPoints < 100);\n }\n if(amount4B > 0) {\n require(wbp.fourStarBurnPoints < 25);\n }\n if(amount5B > 0) {\n require(wbp.fiveStarBurnPoints < 10);\n }\n\n wbp.lowStarBurnPoints += amountLB;\n wbp.fourStarBurnPoints += amount4B;\n wbp.fiveStarBurnPoints += amount5B;\n\n if(wbp.lowStarBurnPoints > 100)\n wbp.lowStarBurnPoints = 100;\n if(wbp.fourStarBurnPoints > 25)\n wbp.fourStarBurnPoints = 25;\n if(wbp.fiveStarBurnPoints > 10)\n wbp.fiveStarBurnPoints = 10;\n }\n\n function getWeaponType(uint256 id) public view returns(uint24) {\n return uint24(nftVars[id][NFTVAR_WEAPON_TYPE]);\n }\n\n function getBonusPower(uint256 id) public view returns (uint24) {\n return getBonusPowerForFight(id, tokens[id].level);\n }\n\n function getBonusPowerForFight(uint256 id, uint8 level) public view returns (uint24) {\n WeaponBurnPoints storage wbp = burnPoints[id];\n return uint24(lowStarBurnPowerPerPoint.mul(wbp.lowStarBurnPoints)\n .add(fourStarBurnPowerPerPoint.mul(wbp.fourStarBurnPoints))\n .add(fiveStarBurnPowerPerPoint.mul(wbp.fiveStarBurnPoints))\n .add(uint256(15).mul(level))\n );\n }\n\n function getFightData(uint256 id, uint8 charTrait) public view returns (int128, int128, uint24, uint8) {\n Weapon storage wep = tokens[id];\n return (\n oneFrac.add(powerMultPerPointBasic.mul(\n ABDKMath64x64.fromUInt(\n wep.stat1 + wep.stat2 + wep.stat3\n )\n )),//targetMult\n getPowerMultiplierForTrait(wep.properties, wep.stat1, wep.stat2, wep.stat3, charTrait),\n getBonusPowerForFight(id, wep.level),\n getTraitFromProperties(wep.properties)\n );\n }\n\n function getFightDataAndDrainDurability(\n address fighter,\n uint256 id,\n uint8 charTrait,\n uint8 drainAmount,\n bool allowNegativeDurability,\n uint256 busyFlag\n ) public restricted returns (int128, int128, uint24, uint8) {\n require(fighter == ownerOf(id)/* && nftVars[id][NFTVAR_BUSY] == 0*/);\n //nftVars[id][NFTVAR_BUSY] |= busyFlag;\n _drainDurability(id, drainAmount, allowNegativeDurability);\n Weapon storage wep = tokens[id];\n return (\n oneFrac.add(powerMultPerPointBasic.mul(\n ABDKMath64x64.fromUInt(\n wep.stat1 + wep.stat2 + wep.stat3\n )\n )),//targetMult\n getPowerMultiplierForTrait(wep.properties, wep.stat1, wep.stat2, wep.stat3, charTrait),\n getBonusPowerForFight(id, wep.level),\n getTraitFromProperties(wep.properties)\n );\n }\n\n function drainDurability(uint256 id, uint8 amount, bool allowNegativeDurabilty) external restricted {\n _drainDurability(id, amount, allowNegativeDurabilty);\n }\n\n function _drainDurability(uint256 id, uint8 amount, bool allowNegativeDurability) internal {\n uint8 durabilityPoints = getDurabilityPointsFromTimestamp(durabilityTimestamp[id]);\n require((durabilityPoints >= amount\n || (allowNegativeDurability && durabilityPoints > 0)) // we allow going into negative, but not starting negative\n );\n\n uint64 drainTime = uint64(amount * secondsPerDurability);\n if(durabilityPoints >= maxDurability) { // if durability full, we reset timestamp and drain from that\n durabilityTimestamp[id] = uint64(now - getDurabilityMaxWait() + drainTime);\n }\n else {\n durabilityTimestamp[id] = uint64(durabilityTimestamp[id] + drainTime);\n }\n }\n\n function setBurnPointMultiplier(uint256 multiplier) public restricted {\n burnPointMultiplier = multiplier;\n }\n function setLowStarBurnPowerPerPoint(uint256 powerPerBurnPoint) public restricted {\n lowStarBurnPowerPerPoint = powerPerBurnPoint;\n }\n function setFourStarBurnPowerPerPoint(uint256 powerPerBurnPoint) public restricted {\n fourStarBurnPowerPerPoint = powerPerBurnPoint;\n }\n function setFiveStarBurnPowerPerPoint(uint256 powerPerBurnPoint) public restricted {\n fiveStarBurnPowerPerPoint = powerPerBurnPoint;\n }\n\n function getDurabilityTimestamp(uint256 id) public view returns (uint64) {\n return durabilityTimestamp[id];\n }\n\n function setDurabilityTimestamp(uint256 id, uint64 timestamp) public restricted {\n durabilityTimestamp[id] = timestamp;\n }\n\n function getDurabilityPoints(uint256 id) public view returns (uint8) {\n return getDurabilityPointsFromTimestamp(durabilityTimestamp[id]);\n }\n\n function getDurabilityPointsFromTimestamp(uint64 timestamp) public view returns (uint8) {\n if(timestamp > now)\n return 0;\n\n uint256 points = (now - timestamp) / secondsPerDurability;\n if(points > maxDurability) {\n points = maxDurability;\n }\n return uint8(points);\n }\n\n function isDurabilityFull(uint256 id) public view returns (bool) {\n return getDurabilityPoints(id) >= maxDurability;\n }\n\n function getDurabilityMaxWait() internal pure returns (uint64) {\n return uint64(maxDurability * secondsPerDurability);\n }\n\n function getNftVar(uint256 weaponID, uint256 nftVar) public view returns(uint256) {\n return nftVars[weaponID][nftVar];\n }\n function setNftVar(uint256 weaponID, uint256 nftVar, uint256 value) public restricted {\n nftVars[weaponID][nftVar] = value;\n }\n\n function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override {\n // if we could afford to set exploiter weapons busy, the promos check becomes redundant, saving ~4.2k gas\n if(from != address(0))\n require(nftVars[tokenId][NFTVAR_BUSY] == 0 && (to == address(0) || promos.getBit(from, 4) == false));\n }\n\n}\n"
},
"contracts/cryptoblades.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"./interfaces/IStakeFromGame.sol\";\nimport \"./interfaces/IRandoms.sol\";\nimport \"./interfaces/IPriceOracle.sol\";\nimport \"./characters.sol\";\nimport \"./Promos.sol\";\nimport \"./weapons.sol\";\nimport \"./util.sol\";\nimport \"./common.sol\";\nimport \"./Blacksmith.sol\";\nimport \"./SpecialWeaponsManager.sol\";\nimport \"./SafeRandoms.sol\";\n\ncontract CryptoBlades is Initializable, AccessControlUpgradeable {\n using ABDKMath64x64 for int128;\n using SafeMath for uint256;\n using SafeMath for uint64;\n using SafeERC20 for IERC20;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n bytes32 public constant WEAPON_SEED = keccak256(\"WEAPON_SEED\");\n\n int128 public constant PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT =\n 14757395258967641292; // 0.8 in fixed-point 64x64 format\n\n // Mapped variables (vars[]) keys, one value per key\n // Using small numbers for now to save on contract size (3% for 13 vars vs using uint256(keccak256(\"name\"))!)\n // Can be migrated later via setVars if needed\n uint256 public constant VAR_HOURLY_INCOME = 1;\n uint256 public constant VAR_HOURLY_FIGHTS = 2;\n uint256 public constant VAR_HOURLY_POWER_SUM = 3;\n uint256 public constant VAR_HOURLY_POWER_AVERAGE = 4;\n uint256 public constant VAR_HOURLY_PAY_PER_FIGHT = 5;\n uint256 public constant VAR_HOURLY_TIMESTAMP = 6;\n uint256 public constant VAR_DAILY_MAX_CLAIM = 7;\n uint256 public constant VAR_CLAIM_DEPOSIT_AMOUNT = 8;\n uint256 public constant VAR_PARAM_PAYOUT_INCOME_PERCENT = 9;\n uint256 public constant VAR_PARAM_DAILY_CLAIM_FIGHTS_LIMIT = 10;\n uint256 public constant VAR_PARAM_DAILY_CLAIM_DEPOSIT_PERCENT = 11;\n uint256 public constant VAR_PARAM_MAX_FIGHT_PAYOUT = 12;\n uint256 public constant VAR_HOURLY_DISTRIBUTION = 13;\n uint256 public constant VAR_UNCLAIMED_SKILL = 14;\n uint256 public constant VAR_HOURLY_MAX_POWER_AVERAGE = 15;\n uint256 public constant VAR_PARAM_HOURLY_MAX_POWER_PERCENT = 16;\n uint256 public constant VAR_PARAM_SIGNIFICANT_HOUR_FIGHTS = 17;\n uint256 public constant VAR_PARAM_HOURLY_PAY_ALLOWANCE = 18;\n uint256 public constant VAR_MINT_WEAPON_FEE_DECREASE_SPEED = 19;\n uint256 public constant VAR_MINT_CHARACTER_FEE_DECREASE_SPEED = 20;\n uint256 public constant VAR_WEAPON_FEE_INCREASE = 21;\n uint256 public constant VAR_CHARACTER_FEE_INCREASE = 22;\n uint256 public constant VAR_MIN_WEAPON_FEE = 23;\n uint256 public constant VAR_MIN_CHARACTER_FEE = 24;\n uint256 public constant VAR_WEAPON_MINT_TIMESTAMP = 25;\n uint256 public constant VAR_CHARACTER_MINT_TIMESTAMP = 26;\n uint256 public constant VAR_GAS_OFFSET_PER_FIGHT_MULTIPLIER = 27;\n uint256 public constant VAR_FIGHT_FLAT_IGO_BONUS = 28;\n\n uint256 public constant LINK_SAFE_RANDOMS = 1;\n\n // Mapped user variable(userVars[]) keys, one value per wallet\n uint256 public constant USERVAR_DAILY_CLAIMED_AMOUNT = 10001;\n uint256 public constant USERVAR_CLAIM_TIMESTAMP = 10002;\n uint256 public constant USERVAR_CLAIM_WEAPON_DATA = 10003;\n // RESERVED USERVAR: 10010\n uint256 public constant USERVAR_GEN2_UNCLAIMED = 10011;\n // RESERVED USERVARS: 10012-10019\n\n Characters public characters;\n Weapons public weapons;\n IERC20 public skillToken;//0x154A9F9cbd3449AD22FDaE23044319D6eF2a1Fab;\n IPriceOracle public priceOracleSkillPerUsd;\n IRandoms public randoms;\n\n function initialize(IERC20 _skillToken, Characters _characters, Weapons _weapons, IPriceOracle _priceOracleSkillPerUsd, IRandoms _randoms) public initializer {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _setupRole(GAME_ADMIN, msg.sender);\n\n skillToken = _skillToken;\n characters = _characters;\n weapons = _weapons;\n priceOracleSkillPerUsd = _priceOracleSkillPerUsd;\n randoms = _randoms;\n\n staminaCostFight = 40;\n mintCharacterFee = ABDKMath64x64.divu(10, 1);//10 usd;\n mintWeaponFee = ABDKMath64x64.divu(3, 1);//3 usd;\n\n // migrateTo_1ee400a\n fightXpGain = 32;\n\n // migrateTo_aa9da90\n oneFrac = ABDKMath64x64.fromUInt(1);\n fightTraitBonus = ABDKMath64x64.divu(75, 1000);\n\n // migrateTo_7dd2a56\n // numbers given for the curves were $4.3-aligned so they need to be multiplied\n // additional accuracy may be in order for the setter functions for these\n fightRewardGasOffset = ABDKMath64x64.divu(23177, 100000); // 0.0539 x 4.3\n fightRewardBaseline = ABDKMath64x64.divu(344, 1000); // 0.08 x 4.3\n\n // migrateTo_5e833b0\n durabilityCostFight = 1;\n }\n\n function migrateTo_ef994e2(Promos _promos) public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n promos = _promos;\n }\n\n function migrateTo_23b3a8b(IStakeFromGame _stakeFromGame) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n stakeFromGameImpl = _stakeFromGame;\n }\n\n function migrateTo_801f279() external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n burnWeaponFee = ABDKMath64x64.divu(2, 10);//0.2 usd;\n reforgeWeaponWithDustFee = ABDKMath64x64.divu(3, 10);//0.3 usd;\n\n reforgeWeaponFee = burnWeaponFee + reforgeWeaponWithDustFee;//0.5 usd;\n }\n\n function migrateTo_60872c8(Blacksmith _blacksmith) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n blacksmith = _blacksmith;\n }\n\n function migrateTo_6a97bd1() external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n rewardsClaimTaxMax = 2767011611056432742; // = ~0.15 = ~15%\n rewardsClaimTaxDuration = 15 days;\n }\n\n function migrateTo_e1fe97c(SpecialWeaponsManager _swm) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n specialWeaponsManager = _swm;\n }\n\n // UNUSED; KEPT FOR UPGRADEABILITY PROXY COMPATIBILITY\n uint characterLimit;\n // config vars\n uint8 staminaCostFight;\n\n // prices & payouts are in USD, with 4 decimals of accuracy in 64.64 fixed point format\n int128 public mintCharacterFee;\n //int128 public rerollTraitFee;\n //int128 public rerollCosmeticsFee;\n int128 public refillStaminaFee;\n // lvl 1 player power could be anywhere between ~909 to 1666\n // cents per fight multiplied by monster power divided by 1000 (lv1 power)\n int128 public fightRewardBaseline;\n int128 public fightRewardGasOffset;\n\n int128 public mintWeaponFee;\n int128 public reforgeWeaponFee;\n\n uint256 nonce;\n\n mapping(address => uint256) lastBlockNumberCalled;\n\n uint256 public fightXpGain; // multiplied based on power differences\n\n mapping(address => uint256) tokenRewards; // user adress : skill wei\n mapping(uint256 => uint256) xpRewards; // character id : xp\n\n int128 public oneFrac; // 1.0\n int128 public fightTraitBonus; // 7.5%\n\n mapping(address => uint256) public inGameOnlyFunds;\n uint256 public totalInGameOnlyFunds;\n\n Promos public promos;\n\n mapping(address => uint256) private _rewardsClaimTaxTimerStart;\n\n IStakeFromGame public stakeFromGameImpl;\n\n uint8 durabilityCostFight;\n\n int128 public burnWeaponFee;\n int128 public reforgeWeaponWithDustFee;\n\n Blacksmith public blacksmith;\n\n struct MintPayment {\n bytes32 blockHash;\n uint256 blockNumber;\n address nftAddress;\n uint count;\n }\n\n mapping(address => MintPayment) mintPayments;\n\n struct MintPaymentSkillDeposited {\n uint256 skillDepositedFromWallet;\n uint256 skillDepositedFromRewards;\n uint256 skillDepositedFromIgo;\n\n uint256 skillRefundableFromWallet;\n uint256 skillRefundableFromRewards;\n uint256 skillRefundableFromIgo;\n\n uint256 refundClaimableTimestamp;\n }\n\n uint256 public totalMintPaymentSkillRefundable;\n mapping(address => MintPaymentSkillDeposited) mintPaymentSkillDepositeds;\n\n int128 private rewardsClaimTaxMax;\n uint256 private rewardsClaimTaxDuration;\n\n mapping(uint256 => uint256) public vars;\n mapping(address => mapping(uint256 => uint256)) public userVars;\n\n SpecialWeaponsManager public specialWeaponsManager;\n mapping(uint256 => address) public links;\n\n event FightOutcome(address indexed owner, uint256 indexed character, uint256 weapon, uint32 target, uint24 playerRoll, uint24 enemyRoll, uint16 xpGain, uint256 skillGain);\n event InGameOnlyFundsGiven(address indexed to, uint256 skillAmount);\n\n function recoverSkill(uint256 amount) public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n\n skillToken.safeTransfer(msg.sender, amount);\n }\n\n function REWARDS_CLAIM_TAX_MAX() public view returns (int128) {\n return rewardsClaimTaxMax;\n }\n\n function REWARDS_CLAIM_TAX_DURATION() public view returns (uint256) {\n return rewardsClaimTaxDuration;\n }\n\n function getSkillToSubtractSingle(uint256 _needed, uint256 _available)\n public\n pure\n returns (uint256 _used, uint256 _remainder) {\n\n if(_needed <= _available) {\n return (_needed, 0);\n }\n\n _needed -= _available;\n\n return (_available, _needed);\n }\n\n function getSkillToSubtract(uint256 _inGameOnlyFunds, uint256 _tokenRewards, uint256 _valorTokenRewards, uint256 _skillNeeded)\n public\n pure\n returns (uint256 fromInGameOnlyFunds, uint256 fromTokenRewards, uint256 fromValorTokenRewards, uint256 fromUserWallet) {\n\n if(_skillNeeded <= _inGameOnlyFunds) {\n return (_skillNeeded, 0, 0, 0);\n }\n\n _skillNeeded -= _inGameOnlyFunds;\n\n if(_skillNeeded <= _tokenRewards) {\n return (_inGameOnlyFunds, _skillNeeded, 0, 0);\n }\n\n _skillNeeded -= _tokenRewards;\n\n if(_skillNeeded <= _valorTokenRewards) {\n return (_inGameOnlyFunds, _tokenRewards, _skillNeeded, 0);\n }\n\n _skillNeeded -= _valorTokenRewards;\n\n return (_inGameOnlyFunds, _tokenRewards, _valorTokenRewards, _skillNeeded);\n }\n\n function getSkillNeededFromUserWallet(address playerAddress, uint256 skillNeeded, bool allowInGameOnlyFunds)\n public\n view\n returns (uint256 skillNeededFromUserWallet) {\n\n uint256 inGameOnlyFundsToUse = 0;\n if (allowInGameOnlyFunds) {\n inGameOnlyFundsToUse = inGameOnlyFunds[playerAddress];\n }\n (,,, skillNeededFromUserWallet) = getSkillToSubtract(\n inGameOnlyFundsToUse,\n tokenRewards[playerAddress],\n userVars[playerAddress][USERVAR_GEN2_UNCLAIMED],\n skillNeeded\n );\n }\n\n function fight(address fighter, uint256 char, uint32 target, uint8 fightMultiplier) external\n restricted returns (uint256, uint256) {\n require(fightMultiplier >= 1 && fightMultiplier <= 5);\n\n (uint72 miscData, uint256 powerData) = characters.getFightDataAndDrainStamina(fighter,\n char, staminaCostFight * fightMultiplier, false, 0);\n \n // dirty variable reuse to avoid stack limits (target is 0-3 atm)\n uint24 playerBasePower = uint24(powerData >> 96);\n target = grabTarget(\n playerBasePower,\n uint64(miscData & 0xFFFFFFFFFFFFFFFF),//timestamp\n target,//passed as index (0-3)\n now / 1 hours\n );\n // target is now using 24 bits for power and topmost 8bits for trait\n uint8 targetTrait = uint8(target >> 24);\n\n return performFight(\n fighter,\n char,\n uint24(powerData >> (targetTrait*24)),//playerFightPower\n playerBasePower,//playerBasePower\n uint24(target),//targetPower\n fightMultiplier,\n uint8((miscData >> 64) & 0xFF)//characterVersion\n );\n }\n\n function performFight(\n address fighter,\n uint256 char,\n uint24 playerFightPower,\n uint24 playerBasePower,\n uint24 targetPower,\n uint8 fightMultiplier,\n uint8 characterVersion\n ) private returns (uint256 tokens, uint256 expectedTokens) {\n //now+/-char is hashed within randomUtil\n uint24 playerRoll = uint24(RandomUtil.plusMinus10PercentSeededFast(playerFightPower,now+char));\n uint24 monsterRoll = uint24(RandomUtil.plusMinus10PercentSeededFast(targetPower, now-char));\n\n uint16 xp = getXpGainForFight(playerBasePower, targetPower) * fightMultiplier;\n tokens = getTokenGainForFight(targetPower) * fightMultiplier;\n expectedTokens = tokens;\n\n if (playerRoll < monsterRoll) {\n tokens = 0;\n xp = 0;\n }\n\n if(characterVersion > 0) {\n userVars[fighter][USERVAR_GEN2_UNCLAIMED] += tokens;\n }\n else {\n tokenRewards[fighter] += tokens;\n }\n xpRewards[char] += xp;\n\n emit FightOutcome(fighter, char, 0/*wep*/, (targetPower /*| ((uint32(data.traitsCWE) << 8) & 0xFF000000)*/), playerRoll, monsterRoll, xp, tokens);\n }\n\n function getTokenGainForFight(uint24 monsterPower) public view returns (uint256) {\n // monsterPower / avgPower * payPerFight * powerMultiplier + gasoffset\n return monsterPower * vars[VAR_HOURLY_PAY_PER_FIGHT] / vars[VAR_HOURLY_POWER_AVERAGE]\n + vars[VAR_GAS_OFFSET_PER_FIGHT_MULTIPLIER];\n }\n \n function getXpGainForFight(uint24 playerPower, uint24 monsterPower) internal view returns (uint16) {\n return uint16(monsterPower * fightXpGain / playerPower);\n }\n\n function getTargets(uint256 char) public view returns (uint32[4] memory) {\n // this is a frontend function\n uint256 powerData = characters.getNftVar(char, characters.NFTVAR_POWER_DATA());\n\n return getTargetsInternal(\n uint24(powerData >> 96), // base power (target)\n characters.getStaminaTimestamp(char),\n now / 1 hours\n );\n }\n\n function getTargetsInternal(uint24 playerPower,\n uint64 staminaTimestamp,\n uint256 currentHour\n ) private pure returns (uint32[4] memory) {\n // 4 targets, roll powers based on character + weapon power\n // trait bonuses not accounted for\n // targets expire on the hour\n\n uint32[4] memory targets;\n for(uint32 i = 0; i < targets.length; i++) {\n // we alter seed per-index or they would be all the same\n // this is a read only function so it's fine to pack all 4 params each iteration\n // for the sake of target picking it needs to be the same as in grabTarget(i)\n // even the exact type of \"i\" is important here\n uint256 indexSeed = uint256(keccak256(abi.encodePacked(\n staminaTimestamp, currentHour, playerPower, i\n )));\n\n targets[i] = uint32(\n RandomUtil.plusMinus10PercentSeededPrehashed(playerPower, indexSeed) // power\n | (uint32(indexSeed % 4) << 24) // trait\n );\n }\n\n return targets;\n }\n\n function grabTarget(\n uint24 playerPower,\n uint64 staminaTimestamp,\n uint32 enemyIndex,\n uint256 currentHour\n ) private pure returns (uint32) {\n require(enemyIndex < 4);\n\n uint256 enemySeed = uint256(keccak256(abi.encodePacked(\n staminaTimestamp, currentHour, playerPower, enemyIndex\n )));\n return uint32(\n RandomUtil.plusMinus10PercentSeededPrehashed(playerPower, enemySeed) // power\n | (uint32(enemySeed % 4) << 24) // trait\n );\n }\n\n function mintCharacter() public onlyNonContract oncePerBlock(msg.sender) {\n\n uint256 skillAmount = usdToSkill(mintCharacterFee);\n (,,, uint256 fromUserWallet) =\n getSkillToSubtract(\n 0,\n 0,\n 0,\n skillAmount\n );\n require(skillToken.balanceOf(msg.sender) >= fromUserWallet && promos.getBit(msg.sender, 4) == false);\n\n uint256 convertedAmount = usdToSkill(getMintCharacterFee());\n _deductPlayerSkillStandard(msg.sender, 0, 0, 0, convertedAmount, true);\n\n uint256 seed = randoms.getRandomSeed(msg.sender);\n uint256 id = characters.mint(msg.sender, seed);\n xpRewards[id] = 1;\n if(userVars[msg.sender][USERVAR_GEN2_UNCLAIMED] == 0) {\n userVars[msg.sender][USERVAR_GEN2_UNCLAIMED] = 1;\n }\n if(inGameOnlyFunds[msg.sender] == 0) {\n inGameOnlyFunds[msg.sender] = 1;\n }\n\n // first weapon free with a character mint, max 1 star\n if(weapons.balanceOf(msg.sender) == 0) {\n weapons.mintWeaponWithStars(msg.sender, 0, uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), msg.sender))), 100);\n }\n\n _updateCharacterMintFee();\n }\n\n function generateWeaponSeed(uint32 quantity, uint8 chosenElement, uint256 eventId) external onlyNonContract oncePerBlock(msg.sender) {\n require(quantity > 0 && quantity <= 10);\n require(userVars[msg.sender][USERVAR_CLAIM_WEAPON_DATA] == 0);\n uint8 chosenElementFee = chosenElement == 100 ? 1 : 2;\n int128 mintWeaponFee =\n getMintWeaponFee()\n .mul(ABDKMath64x64.fromUInt(quantity))\n .mul(ABDKMath64x64.fromUInt(chosenElementFee));\n _payContractConvertedSupportingStaked(msg.sender, usdToSkill(mintWeaponFee));\n _updateWeaponMintFee(quantity);\n if (eventId > 0) {\n specialWeaponsManager.addShards(msg.sender, eventId, quantity);\n }\n SafeRandoms(links[LINK_SAFE_RANDOMS]).requestSingleSeed(msg.sender, getSeed(uint(WEAPON_SEED), quantity, chosenElement));\n userVars[msg.sender][USERVAR_CLAIM_WEAPON_DATA] = uint256(uint256(chosenElement) | (uint256(quantity) << 32));\n }\n\n function generateWeaponSeedUsingStakedSkill(uint32 quantity, uint8 chosenElement, uint256 eventId) external onlyNonContract oncePerBlock(msg.sender) {\n require(quantity > 0 && quantity <= 10);\n require(userVars[msg.sender][USERVAR_CLAIM_WEAPON_DATA] == 0);\n uint8 chosenElementFee = chosenElement == 100 ? 1 : 2;\n int128 discountedMintWeaponFee =\n getMintWeaponFee()\n .mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT)\n .mul(ABDKMath64x64.fromUInt(quantity))\n .mul(ABDKMath64x64.fromUInt(chosenElementFee));\n _payContractStakedOnly(msg.sender, usdToSkill(discountedMintWeaponFee));\n _updateWeaponMintFee(quantity);\n if (eventId > 0) {\n specialWeaponsManager.addShards(msg.sender, eventId, quantity);\n }\n SafeRandoms(links[LINK_SAFE_RANDOMS]).requestSingleSeed(msg.sender, getSeed(uint(WEAPON_SEED), quantity, chosenElement));\n userVars[msg.sender][USERVAR_CLAIM_WEAPON_DATA] = uint256(uint256(chosenElement) | (uint256(quantity) << 32));\n }\n\n function mintWeapon() external onlyNonContract oncePerBlock(msg.sender) {\n uint8 chosenElement = uint8((userVars[msg.sender][USERVAR_CLAIM_WEAPON_DATA]) & 0xFF);\n uint32 quantity = uint32((userVars[msg.sender][USERVAR_CLAIM_WEAPON_DATA] >> 32) & 0xFFFFFFFF);\n require(quantity > 0);\n userVars[msg.sender][USERVAR_CLAIM_WEAPON_DATA] = 0;\n uint256 seed = SafeRandoms(links[LINK_SAFE_RANDOMS]).popSingleSeed(msg.sender, getSeed(uint(WEAPON_SEED), quantity, chosenElement), true, false);\n weapons.mintN(msg.sender, quantity, seed, chosenElement);\n }\n\n function getSeed(uint seedId, uint quantity, uint element) internal pure returns (uint256 seed) {\n uint[] memory seeds = new uint[](3);\n seeds[0] = seedId;\n seeds[1] = quantity;\n seeds[2] = element;\n seed = RandomUtil.combineSeeds(seeds);\n }\n\n function _updateWeaponMintFee(uint256 num) internal {\n mintWeaponFee = getMintWeaponFee() + ABDKMath64x64.divu(vars[VAR_WEAPON_FEE_INCREASE].mul(num), 1e18);\n vars[VAR_WEAPON_MINT_TIMESTAMP] = block.timestamp;\n }\n\n function _updateCharacterMintFee() internal {\n mintCharacterFee = getMintCharacterFee() + ABDKMath64x64.divu(vars[VAR_CHARACTER_FEE_INCREASE], 1e18);\n vars[VAR_CHARACTER_MINT_TIMESTAMP] = block.timestamp;\n }\n\n function migrateRandoms(IRandoms _newRandoms) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n randoms = _newRandoms;\n }\n\n modifier onlyNonContract() {\n _onlyNonContract();\n _;\n }\n\n function _onlyNonContract() internal view {\n require(tx.origin == msg.sender, \"ONC\");\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"NGA\");\n }\n\n modifier oncePerBlock(address user) {\n _oncePerBlock(user);\n _;\n }\n\n function _oncePerBlock(address user) internal {\n require(lastBlockNumberCalled[user] < block.number, \"OCB\");\n lastBlockNumberCalled[user] = block.number;\n }\n\n modifier isWeaponOwner(uint256 weapon) {\n _isWeaponOwner(weapon);\n _;\n }\n\n function _isWeaponOwner(uint256 weapon) internal view {\n require(weapons.ownerOf(weapon) == msg.sender);\n }\n\n modifier isWeaponsOwner(uint256[] memory weaponArray) {\n _isWeaponsOwner(weaponArray);\n _;\n }\n\n function _isWeaponsOwner(uint256[] memory weaponArray) internal view {\n for(uint i = 0; i < weaponArray.length; i++) {\n require(weapons.ownerOf(weaponArray[i]) == msg.sender);\n }\n }\n\n modifier isCharacterOwner(uint256 character) {\n _isCharacterOwner(character);\n _;\n }\n\n function _isCharacterOwner(uint256 character) internal view {\n require(characters.ownerOf(character) == msg.sender);\n }\n\n function payPlayerConverted(address playerAddress, uint256 convertedAmount) public restricted {\n _payPlayerConverted(playerAddress, convertedAmount);\n }\n\n function payContractTokenOnly(address playerAddress, uint256 convertedAmount) public restricted {\n _payContractTokenOnly(playerAddress, convertedAmount, true);\n }\n\n function payContractTokenOnly(address playerAddress, uint256 convertedAmount, bool track) public restricted {\n _payContractTokenOnly(playerAddress, convertedAmount, track);\n }\n\n function _payContractTokenOnly(address playerAddress, uint256 convertedAmount) internal {\n _payContractTokenOnly(playerAddress, convertedAmount, true);\n }\n\n function _payContractTokenOnly(address playerAddress, uint256 convertedAmount, bool track) internal {\n (, uint256 fromTokenRewards, uint256 fromValorTokenRewards, uint256 fromUserWallet) =\n getSkillToSubtract(\n 0,\n tokenRewards[playerAddress],\n userVars[playerAddress][USERVAR_GEN2_UNCLAIMED],\n convertedAmount\n );\n\n _deductPlayerSkillStandard(playerAddress, 0, fromTokenRewards, fromValorTokenRewards, fromUserWallet, track);\n }\n\n function _payContract(address playerAddress, int128 usdAmount) internal\n returns (uint256 _fromInGameOnlyFunds, uint256 _fromTokenRewards, uint256 _fromUserWallet) {\n\n return _payContractConverted(playerAddress, usdToSkill(usdAmount));\n }\n\n function _payContractConverted(address playerAddress, uint256 convertedAmount) internal\n returns (uint256 _fromInGameOnlyFunds, uint256 _fromTokenRewards, uint256 _fromUserWallet) {\n\n (uint256 fromInGameOnlyFunds, uint256 fromTokenRewards, uint256 fromValorTokenRewards, uint256 fromUserWallet) =\n getSkillToSubtract(\n inGameOnlyFunds[playerAddress],\n tokenRewards[playerAddress],\n userVars[playerAddress][USERVAR_GEN2_UNCLAIMED],\n convertedAmount\n );\n\n require(skillToken.balanceOf(playerAddress) >= fromUserWallet,\n string(abi.encodePacked(\"Not enough SKILL! Need \",RandomUtil.uint2str(convertedAmount))));\n\n _deductPlayerSkillStandard(playerAddress, fromInGameOnlyFunds, fromTokenRewards, fromValorTokenRewards, fromUserWallet);\n\n return (fromInGameOnlyFunds, fromTokenRewards, fromUserWallet);\n }\n\n function payContractConvertedSupportingStaked(address playerAddress, uint256 convertedAmount) external restricted\n returns (\n uint256 _fromInGameOnlyFunds,\n uint256 _fromTokenRewards,\n uint256 _fromUserWallet,\n uint256 _fromStaked\n ) {\n return _payContractConvertedSupportingStaked(playerAddress, convertedAmount);\n }\n\n function _payContractConvertedSupportingStaked(address playerAddress, uint256 convertedAmount) internal\n returns (\n uint256 _fromInGameOnlyFunds,\n uint256 _fromTokenRewards,\n uint256 _fromUserWallet,\n uint256 _fromStaked\n ) {\n\n (uint256 fromInGameOnlyFunds, uint256 fromTokenRewards, uint256 fromValorTokenRewards, uint256 _remainder) =\n getSkillToSubtract(\n inGameOnlyFunds[playerAddress],\n tokenRewards[playerAddress],\n userVars[playerAddress][USERVAR_GEN2_UNCLAIMED],\n convertedAmount\n );\n\n (uint256 fromUserWallet, uint256 fromStaked) =\n getSkillToSubtractSingle(\n _remainder,\n skillToken.balanceOf(playerAddress)\n );\n\n _deductPlayerSkillStandard(playerAddress, fromInGameOnlyFunds, fromTokenRewards, fromValorTokenRewards, fromUserWallet);\n\n if(fromStaked > 0) {\n stakeFromGameImpl.unstakeToGame(playerAddress, fromStaked);\n _trackIncome(fromStaked);\n }\n\n return (fromInGameOnlyFunds, fromTokenRewards, fromUserWallet, fromStaked);\n }\n\n function _payContractStakedOnly(address playerAddress, uint256 convertedAmount) internal {\n stakeFromGameImpl.unstakeToGame(playerAddress, convertedAmount);\n _trackIncome(convertedAmount);\n }\n\n function payContractStakedOnly(address playerAddress, uint256 convertedAmount) external restricted {\n _payContractStakedOnly(playerAddress, convertedAmount);\n }\n\n function _deductPlayerSkillStandard(\n address playerAddress,\n uint256 fromInGameOnlyFunds,\n uint256 fromTokenRewards,\n uint256 fromValorTokenRewards,\n uint256 fromUserWallet\n ) internal {\n _deductPlayerSkillStandard(\n playerAddress,\n fromInGameOnlyFunds,\n fromTokenRewards,\n fromValorTokenRewards,\n fromUserWallet,\n true\n );\n }\n\n function _deductPlayerSkillStandard(\n address playerAddress,\n uint256 fromInGameOnlyFunds,\n uint256 fromTokenRewards,\n uint256 fromValorTokenRewards,\n uint256 fromUserWallet,\n bool trackInflow\n ) internal {\n if(fromInGameOnlyFunds > 0) {\n if(totalInGameOnlyFunds >= fromInGameOnlyFunds) // might revert otherwise due to .sub\n totalInGameOnlyFunds = totalInGameOnlyFunds.sub(fromInGameOnlyFunds);\n inGameOnlyFunds[playerAddress] = inGameOnlyFunds[playerAddress].sub(fromInGameOnlyFunds);\n }\n\n if(fromTokenRewards > 0) {\n tokenRewards[playerAddress] = tokenRewards[playerAddress].sub(fromTokenRewards);\n }\n\n if(fromValorTokenRewards > 0) {\n userVars[playerAddress][USERVAR_GEN2_UNCLAIMED] = userVars[playerAddress][USERVAR_GEN2_UNCLAIMED].sub(fromValorTokenRewards);\n }\n\n if(fromUserWallet > 0) {\n skillToken.transferFrom(playerAddress, address(this), fromUserWallet);\n if(trackInflow)\n _trackIncome(fromUserWallet);\n }\n }\n\n function deductAfterPartnerClaim(uint256 amount, address player) external restricted {\n tokenRewards[player] = tokenRewards[player].sub(amount);\n vars[VAR_UNCLAIMED_SKILL] -= amount;\n _trackIncome(amount);\n }\n\n function deductValor(uint256 amount, address player) external restricted {\n userVars[player][USERVAR_GEN2_UNCLAIMED] = userVars[player][USERVAR_GEN2_UNCLAIMED].sub(amount);\n }\n\n function trackIncome(uint256 income) public restricted {\n _trackIncome(income);\n }\n\n function _trackIncome(uint256 income) internal {\n vars[VAR_HOURLY_INCOME] += ABDKMath64x64.divu(vars[VAR_PARAM_PAYOUT_INCOME_PERCENT],100)\n .mulu(income);\n updateHourlyPayouts();\n }\n\n function updateHourlyPayouts() internal {\n // Could be done by a bot instead?\n if(now - vars[VAR_HOURLY_TIMESTAMP] >= 1 hours) {\n vars[VAR_HOURLY_TIMESTAMP] = now;\n\n uint256 undistributed = vars[VAR_HOURLY_INCOME] + vars[VAR_HOURLY_DISTRIBUTION];\n\n vars[VAR_HOURLY_DISTRIBUTION] = undistributed > vars[VAR_PARAM_HOURLY_PAY_ALLOWANCE]\n ? vars[VAR_PARAM_HOURLY_PAY_ALLOWANCE] : undistributed;\n vars[VAR_HOURLY_INCOME] = undistributed.sub(vars[VAR_HOURLY_DISTRIBUTION]);\n\n uint256 fights = vars[VAR_HOURLY_FIGHTS];\n if(fights >= vars[VAR_PARAM_SIGNIFICANT_HOUR_FIGHTS]) {\n uint256 averagePower = vars[VAR_HOURLY_POWER_SUM] / fights;\n\n if(averagePower > vars[VAR_HOURLY_MAX_POWER_AVERAGE])\n vars[VAR_HOURLY_MAX_POWER_AVERAGE] = averagePower;\n }\n vars[VAR_HOURLY_POWER_AVERAGE] = ABDKMath64x64.divu(vars[VAR_PARAM_HOURLY_MAX_POWER_PERCENT],100)\n .mulu(vars[VAR_HOURLY_MAX_POWER_AVERAGE]);\n\n vars[VAR_DAILY_MAX_CLAIM] = vars[VAR_HOURLY_PAY_PER_FIGHT] * vars[VAR_PARAM_DAILY_CLAIM_FIGHTS_LIMIT];\n vars[VAR_HOURLY_FIGHTS] = 0;\n vars[VAR_HOURLY_POWER_SUM] = 0;\n }\n }\n\n function _payPlayer(address playerAddress, int128 baseAmount) internal {\n _payPlayerConverted(playerAddress, usdToSkill(baseAmount));\n }\n\n function _payPlayerConverted(address playerAddress, uint256 convertedAmount) internal {\n skillToken.transfer(playerAddress, convertedAmount);\n }\n\n function setCharacterMintValue(uint256 cents) public restricted {\n mintCharacterFee = ABDKMath64x64.divu(cents, 100);\n }\n\n function setWeaponMintValue(uint256 cents) public restricted {\n mintWeaponFee = ABDKMath64x64.divu(cents, 100);\n }\n\n function setStaminaCostFight(uint8 points) public restricted {\n staminaCostFight = points;\n }\n\n function setDurabilityCostFight(uint8 points) public restricted {\n durabilityCostFight = points;\n }\n\n function setFightXpGain(uint256 average) public restricted {\n fightXpGain = average;\n }\n\n function setRewardsClaimTaxMaxAsPercent(uint256 _percent) public restricted {\n rewardsClaimTaxMax = ABDKMath64x64.divu(_percent, 100);\n }\n\n function setRewardsClaimTaxDuration(uint256 _rewardsClaimTaxDuration) public restricted {\n rewardsClaimTaxDuration = _rewardsClaimTaxDuration;\n }\n\n function setVar(uint256 varField, uint256 value) external restricted {\n vars[varField] = value;\n }\n\n function setVars(uint256[] calldata varFields, uint256[] calldata values) external restricted {\n for(uint i = 0; i < varFields.length; i++) {\n vars[varFields[i]] = values[i];\n }\n }\n\n function setLink(uint256 linkId, address linkAddress) external restricted {\n links[linkId] = linkAddress;\n }\n\n function giveInGameOnlyFunds(address to, uint256 skillAmount) external restricted {\n totalInGameOnlyFunds = totalInGameOnlyFunds.add(skillAmount);\n inGameOnlyFunds[to] = inGameOnlyFunds[to].add(skillAmount);\n\n skillToken.safeTransferFrom(msg.sender, address(this), skillAmount);\n\n emit InGameOnlyFundsGiven(to, skillAmount);\n }\n\n function _giveInGameOnlyFundsFromContractBalance(address to, uint256 skillAmount) internal {\n //totalInGameOnlyFunds = totalInGameOnlyFunds.add(skillAmount);\n inGameOnlyFunds[to] = inGameOnlyFunds[to].add(skillAmount);\n\n emit InGameOnlyFundsGiven(to, skillAmount);\n }\n\n function giveInGameOnlyFundsFromContractBalance(address to, uint256 skillAmount) external restricted {\n _giveInGameOnlyFundsFromContractBalance(to, skillAmount);\n }\n\n function usdToSkill(int128 usdAmount) public view returns (uint256) {\n return usdAmount.mulu(priceOracleSkillPerUsd.currentPrice());\n }\n\n function claimXpRewards() public {\n // our characters go to the tavern to rest\n // they meditate on what they've learned\n\n uint256[] memory chars = characters.getReadyCharacters(msg.sender);\n require(chars.length > 0);\n uint256[] memory xps = new uint256[](chars.length);\n for(uint256 i = 0; i < chars.length; i++) {\n xps[i] = xpRewards[chars[i]];\n xpRewards[chars[i]] = 0;\n }\n characters.gainXpAll(chars, xps);\n }\n\n function resetXp(uint256[] memory chars) public restricted {\n for(uint256 i = 0; i < chars.length; i++) {\n xpRewards[chars[i]] = 0;\n }\n }\n\n function getTokenRewards() public view returns (uint256) {\n return tokenRewards[msg.sender];\n }\n\n function getXpRewards(uint256[] memory chars) public view returns (uint256[] memory) {\n uint charsAmount = chars.length;\n uint256[] memory xps = new uint256[](charsAmount);\n for(uint i = 0; i < chars.length; i++) {\n xps[i] = xpRewards[chars[i]];\n }\n return xps;\n }\n\n function getTokenRewardsFor(address wallet) public view returns (uint256) {\n return tokenRewards[wallet];\n }\n\n function getTotalSkillOwnedBy(address wallet) public view returns (uint256) {\n return inGameOnlyFunds[wallet] + getTokenRewardsFor(wallet) + skillToken.balanceOf(wallet);\n }\n\n function _getRewardsClaimTax(address playerAddress) internal view returns (int128) {\n assert(_rewardsClaimTaxTimerStart[playerAddress] <= block.timestamp);\n\n uint256 rewardsClaimTaxTimerEnd = _rewardsClaimTaxTimerStart[playerAddress].add(rewardsClaimTaxDuration);\n\n (, uint256 durationUntilNoTax) = rewardsClaimTaxTimerEnd.trySub(block.timestamp);\n\n assert(0 <= durationUntilNoTax && durationUntilNoTax <= rewardsClaimTaxDuration);\n\n int128 frac = ABDKMath64x64.divu(durationUntilNoTax, rewardsClaimTaxDuration);\n\n return rewardsClaimTaxMax.mul(frac);\n }\n\n function getOwnRewardsClaimTax() public view returns (int128) {\n return _getRewardsClaimTax(msg.sender);\n }\n\n function getMintWeaponFee() public view returns (int128) {\n int128 decrease = ABDKMath64x64.divu(block.timestamp.sub(vars[VAR_WEAPON_MINT_TIMESTAMP]).mul(vars[VAR_MINT_WEAPON_FEE_DECREASE_SPEED]), 1e18);\n int128 weaponFeeMin = ABDKMath64x64.divu(vars[VAR_MIN_WEAPON_FEE], 100);\n if(decrease > mintWeaponFee) {\n return weaponFeeMin;\n }\n if(mintWeaponFee - decrease < weaponFeeMin) {\n return weaponFeeMin;\n }\n return mintWeaponFee.sub(decrease);\n }\n\n function getMintCharacterFee() public view returns (int128) {\n int128 decrease = ABDKMath64x64.divu(block.timestamp.sub(vars[VAR_CHARACTER_MINT_TIMESTAMP]).mul(vars[VAR_MINT_CHARACTER_FEE_DECREASE_SPEED]), 1e18);\n int128 characterFeeMin = ABDKMath64x64.divu(vars[VAR_MIN_CHARACTER_FEE], 100);\n if(decrease > mintCharacterFee) {\n return characterFeeMin;\n }\n if(mintCharacterFee - decrease < characterFeeMin) {\n return characterFeeMin;\n }\n return mintCharacterFee.sub(decrease);\n }\n\n}\n"
},
"contracts/BurningManager.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"./Promos.sol\";\nimport \"./util.sol\";\nimport \"./Garrison.sol\";\nimport \"./cryptoblades.sol\";\nimport \"./EquipmentManager.sol\";\n\ncontract BurningManager is Initializable, AccessControlUpgradeable {\n using SafeMath for uint256;\n using ABDKMath64x64 for int128;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n bytes32 public constant BURNER_ROLE = keccak256(\"BURNER_ROLE\");\n\n // STATE\n Characters public characters;\n CryptoBlades public game;\n Garrison public garrison;\n\n mapping(address => mapping(uint256 => uint256)) public userVars;\n uint256 public constant USERVAR_SOUL_SUPPLY = 1;\n uint256 public constant USERVAR_NON_GENESIS_SOUL_SUPPLY = 2;\n mapping(uint256 => uint256) public vars;\n uint256 public constant VAR_ROI_DAYS = 1;\n uint256 public constant VAR_BURN_POWER_MULTIPLIER = 2;\n\n int128 public burnWeaponFee;\n int128 public reforgeWeaponWithDustFee;\n int128 public reforgeWeaponFee;\n int128 public constant PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT = 14757395258967641292; // 0.8 in fixed-point 64x64 format\n\n\n Weapons public weapons;\n IPriceOracle public priceOracleSkillPerUsd;\n\n mapping(uint256 => address) public links;\n uint256 public constant LINK_EQUIPMENT_MANAGER = 1;\n\n function initialize(Characters _characters, Garrison _garrison, CryptoBlades _game)\n public\n initializer\n {\n __AccessControl_init();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _setupRole(GAME_ADMIN, msg.sender);\n\n characters = _characters;\n garrison = _garrison;\n game = _game;\n }\n\n function migrateTo_e1fe97c(Weapons _weapons, IPriceOracle _priceOracleSkillPerUsd) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n weapons = _weapons;\n priceOracleSkillPerUsd = _priceOracleSkillPerUsd;\n burnWeaponFee = ABDKMath64x64.divu(2, 10); // 0.2 USD\n reforgeWeaponWithDustFee = ABDKMath64x64.divu(3, 10); // 0.3 USD\n reforgeWeaponFee = burnWeaponFee + reforgeWeaponWithDustFee; // 0.5 USD\n }\n\n // MODIFIERS\n\n modifier isCharactersOwner(uint256[] memory burnIds) {\n _isCharactersOwner(burnIds);\n _;\n }\n\n function _isCharactersOwner(uint256[] memory burnIds) internal view {\n for(uint i = 0; i < burnIds.length; i++) {\n require(characters.ownerOf(burnIds[i]) == msg.sender || garrison.characterOwner(burnIds[i]) == msg.sender, 'Not owner');\n }\n }\n\n modifier isWeaponOwner(uint256 weapon) {\n _isWeaponOwner(weapon);\n _;\n }\n\n function _isWeaponOwner(uint256 weapon) internal view {\n require(weapons.ownerOf(weapon) == msg.sender);\n }\n\n modifier isWeaponsOwner(uint256[] memory weaponArray) {\n _isWeaponsOwner(weaponArray);\n _;\n }\n\n function _isWeaponsOwner(uint256[] memory weaponArray) internal view {\n for(uint i = 0; i < weaponArray.length; i++) {\n require(weapons.ownerOf(weaponArray[i]) == msg.sender);\n }\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"NGA\");\n }\n\n modifier burningEnabled() {\n _burningEnabled();\n _;\n }\n\n function _burningEnabled() internal view {\n require(vars[VAR_BURN_POWER_MULTIPLIER] > 0, \"Burning disabled\");\n }\n\n // VIEWS\n\n function burnCharactersFee(uint256[] memory burnIds) public view returns (uint256) {\n uint256 burnFee = 0;\n for(uint i = 0; i < burnIds.length; i++) {\n burnFee += burnCharacterFee(burnIds[i]);\n }\n return burnFee;\n }\n\n function burnCharacterFee(uint256 burnId) public view returns (uint256) {\n 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];\n }\n\n function usdToSkill(int128 usdAmount) public view returns (uint256) {\n return usdAmount.mulu(priceOracleSkillPerUsd.currentPrice());\n }\n\n //FUNCTIONS\n\n // Characters burning\n function burnCharacterFromMarket(uint256 burnId) external burningEnabled {\n require(hasRole(BURNER_ROLE, msg.sender), 'Not burner');\n require(EquipmentManager(links[LINK_EQUIPMENT_MANAGER])\n .hasNothingEquipped(address(characters), burnId), 'Has equips');\n game.payContractTokenOnly(tx.origin, burnCharacterFee(burnId));\n uint256[] memory burnIds = new uint256[](1);\n burnIds[0] = burnId;\n (uint256 genesisSoul, uint256 nonGenesisSoul) = characters.getSoulForBurns(burnIds);\n userVars[tx.origin][USERVAR_SOUL_SUPPLY] += genesisSoul.mul(vars[VAR_BURN_POWER_MULTIPLIER]).div(1e18);\n userVars[tx.origin][USERVAR_NON_GENESIS_SOUL_SUPPLY] += nonGenesisSoul.mul(vars[VAR_BURN_POWER_MULTIPLIER]).div(1e18);\n characters.burnIntoSoul(burnIds);\n }\n\n function burnCharactersIntoCharacter(uint256[] memory burnIds, uint256 targetId) public isCharactersOwner(burnIds) burningEnabled {\n require(EquipmentManager(links[LINK_EQUIPMENT_MANAGER])\n .haveNothingEquipped(address(characters), burnIds), 'Have equips');\n game.payContractTokenOnly(msg.sender, burnCharactersFee(burnIds));\n characters.burnIntoCharacter(burnIds, targetId, vars[VAR_BURN_POWER_MULTIPLIER]);\n }\n\n function burnCharactersIntoSoul(uint256[] memory burnIds) public isCharactersOwner(burnIds) burningEnabled {\n require(EquipmentManager(links[LINK_EQUIPMENT_MANAGER])\n .haveNothingEquipped(address(characters), burnIds), 'Have equips');\n game.payContractTokenOnly(msg.sender, burnCharactersFee(burnIds));\n (uint256 genesisSoul, uint256 nonGenesisSoul) = characters.getSoulForBurns(burnIds);\n userVars[msg.sender][USERVAR_SOUL_SUPPLY] += genesisSoul.mul(vars[VAR_BURN_POWER_MULTIPLIER]).div(1e18);\n userVars[msg.sender][USERVAR_NON_GENESIS_SOUL_SUPPLY] += nonGenesisSoul.mul(vars[VAR_BURN_POWER_MULTIPLIER]).div(1e18);\n characters.burnIntoSoul(burnIds);\n }\n\n function transferSoul(address targetAddress, uint256 soulAmount) public {\n userVars[msg.sender][USERVAR_SOUL_SUPPLY] = userVars[msg.sender][USERVAR_SOUL_SUPPLY].sub(soulAmount);\n userVars[targetAddress][USERVAR_SOUL_SUPPLY] = userVars[targetAddress][USERVAR_SOUL_SUPPLY].add(soulAmount);\n }\n\n function transferNonGenesisSoul(address targetAddress, uint256 soulAmount) public {\n userVars[msg.sender][USERVAR_NON_GENESIS_SOUL_SUPPLY] = userVars[msg.sender][USERVAR_NON_GENESIS_SOUL_SUPPLY].sub(soulAmount);\n userVars[targetAddress][USERVAR_NON_GENESIS_SOUL_SUPPLY] = userVars[targetAddress][USERVAR_NON_GENESIS_SOUL_SUPPLY].add(soulAmount);\n }\n\n function upgradeCharacterWithSoul(uint256 targetId, uint256 soulAmount) public burningEnabled {\n userVars[msg.sender][USERVAR_SOUL_SUPPLY] = userVars[msg.sender][USERVAR_SOUL_SUPPLY].sub(soulAmount);\n characters.upgradeWithSoul(targetId, soulAmount, true);\n }\n\n function upgradeNonGenesisCharacterWithSoul(uint256 targetId, uint256 soulAmount) public burningEnabled {\n userVars[msg.sender][USERVAR_NON_GENESIS_SOUL_SUPPLY] = userVars[msg.sender][USERVAR_NON_GENESIS_SOUL_SUPPLY].sub(soulAmount);\n characters.upgradeWithSoul(targetId, soulAmount, false);\n }\n\n // Weapons burning\n\n function burnWeapons(uint256[] calldata burnIDs) external isWeaponsOwner(burnIDs) {\n game.payContractConvertedSupportingStaked(msg.sender, game.usdToSkill(burnWeaponFee.mul(ABDKMath64x64.fromUInt(burnIDs.length))));\n\n _burnWeaponsLogic(burnIDs);\n }\n\n function reforgeWeapon(uint256 reforgeID, uint256 burnID) external isWeaponOwner(reforgeID) isWeaponOwner(burnID) {\n game.payContractConvertedSupportingStaked(msg.sender, usdToSkill(burnWeaponFee));\n\n _reforgeWeaponLogic(reforgeID, burnID);\n }\n\n function reforgeWeaponWithDust(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) external isWeaponOwner(reforgeID) {\n game.payContractConvertedSupportingStaked(msg.sender, usdToSkill(reforgeWeaponWithDustFee));\n\n _reforgeWeaponWithDustLogic(reforgeID, amountLB, amount4B, amount5B);\n }\n\n function burnWeaponsUsingStakedSkill(uint256[] calldata burnIDs) external isWeaponsOwner(burnIDs) {\n int128 discountedBurnWeaponFee =\n burnWeaponFee\n .mul(ABDKMath64x64.fromUInt(burnIDs.length))\n .mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT);\n game.payContractStakedOnly(msg.sender, usdToSkill(discountedBurnWeaponFee));\n\n _burnWeaponsLogic(burnIDs);\n }\n\n function reforgeWeaponUsingStakedSkill(uint256 reforgeID, uint256 burnID) external isWeaponOwner(reforgeID) isWeaponOwner(burnID) {\n int128 discountedReforgeWeaponFee =\n reforgeWeaponFee\n .mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT);\n game.payContractStakedOnly(msg.sender, usdToSkill(discountedReforgeWeaponFee));\n\n _reforgeWeaponLogic(reforgeID, burnID);\n }\n\n function reforgeWeaponWithDustUsingStakedSkill(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) external isWeaponOwner(reforgeID) {\n int128 discountedReforgeWeaponWithDustFee =\n reforgeWeaponWithDustFee\n .mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT);\n game.payContractStakedOnly(msg.sender, usdToSkill(discountedReforgeWeaponWithDustFee));\n\n _reforgeWeaponWithDustLogic(reforgeID, amountLB, amount4B, amount5B);\n }\n\n function _burnWeaponLogic(uint256 burnID) internal {\n weapons.burn(burnID);\n }\n\n function _burnWeaponsLogic(uint256[] memory burnIDs) internal {\n for(uint i = 0; i < burnIDs.length; i++) {\n weapons.burn(burnIDs[i]);\n }\n }\n\n function _reforgeWeaponLogic(uint256 reforgeID, uint256 burnID) internal {\n weapons.reforge(reforgeID, burnID);\n }\n\n function _reforgeWeaponWithDustLogic(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) internal {\n weapons.reforgeWithDust(reforgeID, amountLB, amount4B, amount5B);\n }\n\n function giveAwaySoul(address user, uint256 soulAmount) external restricted {\n userVars[user][USERVAR_SOUL_SUPPLY] += soulAmount;\n }\n\n function burnSoul(address user, uint256 soulAmount) external restricted {\n userVars[user][USERVAR_SOUL_SUPPLY] = userVars[user][USERVAR_SOUL_SUPPLY].sub(soulAmount);\n }\n\n // VARS SETTER\n\n function setVar(uint256 varField, uint256 value) external restricted {\n vars[varField] = value;\n }\n\n function setLink(uint256 linkId, address linkAddress) external restricted {\n links[linkId] = linkAddress;\n }\n\n function setBurnWeaponValue(uint256 cents) public restricted {\n burnWeaponFee = ABDKMath64x64.divu(cents, 100);\n }\n\n function setReforgeWeaponValue(uint256 cents) public restricted {\n int128 newReforgeWeaponFee = ABDKMath64x64.divu(cents, 100);\n require(newReforgeWeaponFee > burnWeaponFee);\n reforgeWeaponWithDustFee = newReforgeWeaponFee - burnWeaponFee;\n reforgeWeaponFee = newReforgeWeaponFee;\n }\n\n function setReforgeWeaponWithDustValue(uint256 cents) public restricted {\n reforgeWeaponWithDustFee = ABDKMath64x64.divu(cents, 100);\n reforgeWeaponFee = burnWeaponFee + reforgeWeaponWithDustFee;\n }\n \n}\n"
},
"@openzeppelin/contracts/introspection/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
},
"@openzeppelin/contracts/utils/Address.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.2 <0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n // solhint-disable-next-line no-inline-assembly\n assembly { size := extcodesize(account) }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n // solhint-disable-next-line avoid-low-level-calls, avoid-call-value\n (bool success, ) = recipient.call{ value: amount }(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain`call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory returndata) = target.call{ value: value }(data);\n return _verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory returndata) = target.staticcall(data);\n return _verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return _verifyCallResult(success, returndata, errorMessage);\n }\n\n function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"../../utils/ContextUpgradeable.sol\";\nimport \"./IERC721Upgradeable.sol\";\nimport \"./IERC721MetadataUpgradeable.sol\";\nimport \"./IERC721EnumerableUpgradeable.sol\";\nimport \"./IERC721ReceiverUpgradeable.sol\";\nimport \"../../introspection/ERC165Upgradeable.sol\";\nimport \"../../math/SafeMathUpgradeable.sol\";\nimport \"../../utils/AddressUpgradeable.sol\";\nimport \"../../utils/EnumerableSetUpgradeable.sol\";\nimport \"../../utils/EnumerableMapUpgradeable.sol\";\nimport \"../../utils/StringsUpgradeable.sol\";\nimport \"../../proxy/Initializable.sol\";\n\n/**\n * @title ERC721 Non-Fungible Token Standard basic implementation\n * @dev see https://eips.ethereum.org/EIPS/eip-721\n */\ncontract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable, IERC721EnumerableUpgradeable {\n using SafeMathUpgradeable for uint256;\n using AddressUpgradeable for address;\n using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;\n using EnumerableMapUpgradeable for EnumerableMapUpgradeable.UintToAddressMap;\n using StringsUpgradeable for uint256;\n\n // Equals to `bytes4(keccak256(\"onERC721Received(address,address,uint256,bytes)\"))`\n // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`\n bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;\n\n // Mapping from holder address to their (enumerable) set of owned tokens\n mapping (address => EnumerableSetUpgradeable.UintSet) private _holderTokens;\n\n // Enumerable mapping from token ids to their owners\n EnumerableMapUpgradeable.UintToAddressMap private _tokenOwners;\n\n // Mapping from token ID to approved address\n mapping (uint256 => address) private _tokenApprovals;\n\n // Mapping from owner to operator approvals\n mapping (address => mapping (address => bool)) private _operatorApprovals;\n\n // Token name\n string private _name;\n\n // Token symbol\n string private _symbol;\n\n // Optional mapping for token URIs\n mapping (uint256 => string) private _tokenURIs;\n\n // Base URI\n string private _baseURI;\n\n /*\n * bytes4(keccak256('balanceOf(address)')) == 0x70a08231\n * bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e\n * bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3\n * bytes4(keccak256('getApproved(uint256)')) == 0x081812fc\n * bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465\n * bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5\n * bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd\n * bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e\n * bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde\n *\n * => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^\n * 0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd\n */\n bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;\n\n /*\n * bytes4(keccak256('name()')) == 0x06fdde03\n * bytes4(keccak256('symbol()')) == 0x95d89b41\n * bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd\n *\n * => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f\n */\n bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;\n\n /*\n * bytes4(keccak256('totalSupply()')) == 0x18160ddd\n * bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59\n * bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7\n *\n * => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63\n */\n bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;\n\n /**\n * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.\n */\n function __ERC721_init(string memory name_, string memory symbol_) internal initializer {\n __Context_init_unchained();\n __ERC165_init_unchained();\n __ERC721_init_unchained(name_, symbol_);\n }\n\n function __ERC721_init_unchained(string memory name_, string memory symbol_) internal initializer {\n _name = name_;\n _symbol = symbol_;\n\n // register the supported interfaces to conform to ERC721 via ERC165\n _registerInterface(_INTERFACE_ID_ERC721);\n _registerInterface(_INTERFACE_ID_ERC721_METADATA);\n _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);\n }\n\n /**\n * @dev See {IERC721-balanceOf}.\n */\n function balanceOf(address owner) public view virtual override returns (uint256) {\n require(owner != address(0), \"ERC721: balance query for the zero address\");\n return _holderTokens[owner].length();\n }\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) public view virtual override returns (address) {\n return _tokenOwners.get(tokenId, \"ERC721: owner query for nonexistent token\");\n }\n\n /**\n * @dev See {IERC721Metadata-name}.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev See {IERC721Metadata-symbol}.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev See {IERC721Metadata-tokenURI}.\n */\n function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {\n require(_exists(tokenId), \"ERC721Metadata: URI query for nonexistent token\");\n\n string memory _tokenURI = _tokenURIs[tokenId];\n string memory base = baseURI();\n\n // If there is no base URI, return the token URI.\n if (bytes(base).length == 0) {\n return _tokenURI;\n }\n // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).\n if (bytes(_tokenURI).length > 0) {\n return string(abi.encodePacked(base, _tokenURI));\n }\n // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.\n return string(abi.encodePacked(base, tokenId.toString()));\n }\n\n /**\n * @dev Returns the base URI set via {_setBaseURI}. This will be\n * automatically added as a prefix in {tokenURI} to each token's URI, or\n * to the token ID if no specific URI is set for that token ID.\n */\n function baseURI() public view virtual returns (string memory) {\n return _baseURI;\n }\n\n /**\n * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.\n */\n function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {\n return _holderTokens[owner].at(index);\n }\n\n /**\n * @dev See {IERC721Enumerable-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds\n return _tokenOwners.length();\n }\n\n /**\n * @dev See {IERC721Enumerable-tokenByIndex}.\n */\n function tokenByIndex(uint256 index) public view virtual override returns (uint256) {\n (uint256 tokenId, ) = _tokenOwners.at(index);\n return tokenId;\n }\n\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) public virtual override {\n address owner = ERC721Upgradeable.ownerOf(tokenId);\n require(to != owner, \"ERC721: approval to current owner\");\n\n require(_msgSender() == owner || ERC721Upgradeable.isApprovedForAll(owner, _msgSender()),\n \"ERC721: approve caller is not owner nor approved for all\"\n );\n\n _approve(to, tokenId);\n }\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) public view virtual override returns (address) {\n require(_exists(tokenId), \"ERC721: approved query for nonexistent token\");\n\n return _tokenApprovals[tokenId];\n }\n\n /**\n * @dev See {IERC721-setApprovalForAll}.\n */\n function setApprovalForAll(address operator, bool approved) public virtual override {\n require(operator != _msgSender(), \"ERC721: approve to caller\");\n\n _operatorApprovals[_msgSender()][operator] = approved;\n emit ApprovalForAll(_msgSender(), operator, approved);\n }\n\n /**\n * @dev See {IERC721-isApprovedForAll}.\n */\n function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {\n return _operatorApprovals[owner][operator];\n }\n\n /**\n * @dev See {IERC721-transferFrom}.\n */\n function transferFrom(address from, address to, uint256 tokenId) public virtual override {\n //solhint-disable-next-line max-line-length\n require(_isApprovedOrOwner(_msgSender(), tokenId), \"ERC721: transfer caller is not owner nor approved\");\n\n _transfer(from, to, tokenId);\n }\n\n /**\n * @dev See {IERC721-safeTransferFrom}.\n */\n function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {\n safeTransferFrom(from, to, tokenId, \"\");\n }\n\n /**\n * @dev See {IERC721-safeTransferFrom}.\n */\n function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {\n require(_isApprovedOrOwner(_msgSender(), tokenId), \"ERC721: transfer caller is not owner nor approved\");\n _safeTransfer(from, to, tokenId, _data);\n }\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * `_data` is additional data, it has no specified format and it is sent in call to `to`.\n *\n * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.\n * implement alternative mechanisms to perform token transfer, such as signature-based.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {\n _transfer(from, to, tokenId);\n require(_checkOnERC721Received(from, to, tokenId, _data), \"ERC721: transfer to non ERC721Receiver implementer\");\n }\n\n /**\n * @dev Returns whether `tokenId` exists.\n *\n * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.\n *\n * Tokens start existing when they are minted (`_mint`),\n * and stop existing when they are burned (`_burn`).\n */\n function _exists(uint256 tokenId) internal view virtual returns (bool) {\n return _tokenOwners.contains(tokenId);\n }\n\n /**\n * @dev Returns whether `spender` is allowed to manage `tokenId`.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {\n require(_exists(tokenId), \"ERC721: operator query for nonexistent token\");\n address owner = ERC721Upgradeable.ownerOf(tokenId);\n return (spender == owner || getApproved(tokenId) == spender || ERC721Upgradeable.isApprovedForAll(owner, spender));\n }\n\n /**\n * @dev Safely mints `tokenId` and transfers it to `to`.\n *\n * Requirements:\n d*\n * - `tokenId` must not exist.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function _safeMint(address to, uint256 tokenId) internal virtual {\n _safeMint(to, tokenId, \"\");\n }\n\n /**\n * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is\n * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.\n */\n function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {\n _mint(to, tokenId);\n require(_checkOnERC721Received(address(0), to, tokenId, _data), \"ERC721: transfer to non ERC721Receiver implementer\");\n }\n\n /**\n * @dev Mints `tokenId` and transfers it to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible\n *\n * Requirements:\n *\n * - `tokenId` must not exist.\n * - `to` cannot be the zero address.\n *\n * Emits a {Transfer} event.\n */\n function _mint(address to, uint256 tokenId) internal virtual {\n require(to != address(0), \"ERC721: mint to the zero address\");\n require(!_exists(tokenId), \"ERC721: token already minted\");\n\n _beforeTokenTransfer(address(0), to, tokenId);\n\n _holderTokens[to].add(tokenId);\n\n _tokenOwners.set(tokenId, to);\n\n emit Transfer(address(0), to, tokenId);\n }\n\n /**\n * @dev Destroys `tokenId`.\n * The approval is cleared when the token is burned.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n *\n * Emits a {Transfer} event.\n */\n function _burn(uint256 tokenId) internal virtual {\n address owner = ERC721Upgradeable.ownerOf(tokenId); // internal owner\n\n _beforeTokenTransfer(owner, address(0), tokenId);\n\n // Clear approvals\n _approve(address(0), tokenId);\n\n // Clear metadata (if any)\n if (bytes(_tokenURIs[tokenId]).length != 0) {\n delete _tokenURIs[tokenId];\n }\n\n _holderTokens[owner].remove(tokenId);\n\n _tokenOwners.remove(tokenId);\n\n emit Transfer(owner, address(0), tokenId);\n }\n\n /**\n * @dev Transfers `tokenId` from `from` to `to`.\n * As opposed to {transferFrom}, this imposes no restrictions on msg.sender.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n *\n * Emits a {Transfer} event.\n */\n function _transfer(address from, address to, uint256 tokenId) internal virtual {\n require(ERC721Upgradeable.ownerOf(tokenId) == from, \"ERC721: transfer of token that is not own\"); // internal owner\n require(to != address(0), \"ERC721: transfer to the zero address\");\n\n _beforeTokenTransfer(from, to, tokenId);\n\n // Clear approvals from the previous owner\n _approve(address(0), tokenId);\n\n _holderTokens[from].remove(tokenId);\n _holderTokens[to].add(tokenId);\n\n _tokenOwners.set(tokenId, to);\n\n emit Transfer(from, to, tokenId);\n }\n\n /**\n * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {\n require(_exists(tokenId), \"ERC721Metadata: URI set of nonexistent token\");\n _tokenURIs[tokenId] = _tokenURI;\n }\n\n /**\n * @dev Internal function to set the base URI for all token IDs. It is\n * automatically added as a prefix to the value returned in {tokenURI},\n * or to the token ID if {tokenURI} is empty.\n */\n function _setBaseURI(string memory baseURI_) internal virtual {\n _baseURI = baseURI_;\n }\n\n /**\n * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.\n * The call is not executed if the target address is not a contract.\n *\n * @param from address representing the previous owner of the given token ID\n * @param to target address that will receive the tokens\n * @param tokenId uint256 ID of the token to be transferred\n * @param _data bytes optional data to send along with the call\n * @return bool whether the call correctly returned the expected magic value\n */\n function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)\n private returns (bool)\n {\n if (!to.isContract()) {\n return true;\n }\n bytes memory returndata = to.functionCall(abi.encodeWithSelector(\n IERC721ReceiverUpgradeable(to).onERC721Received.selector,\n _msgSender(),\n from,\n tokenId,\n _data\n ), \"ERC721: transfer to non ERC721Receiver implementer\");\n bytes4 retval = abi.decode(returndata, (bytes4));\n return (retval == _ERC721_RECEIVED);\n }\n\n /**\n * @dev Approve `to` to operate on `tokenId`\n *\n * Emits an {Approval} event.\n */\n function _approve(address to, uint256 tokenId) internal virtual {\n _tokenApprovals[tokenId] = to;\n emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId); // internal owner\n }\n\n /**\n * @dev Hook that is called before any token transfer. This includes minting\n * and burning.\n *\n * Calling conditions:\n *\n * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be\n * transferred to `to`.\n * - When `from` is zero, `tokenId` will be minted for `to`.\n * - When `to` is zero, ``from``'s `tokenId` will be burned.\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }\n uint256[41] private __gap;\n}\n"
},
"contracts/Promos.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\n\ncontract Promos is Initializable, AccessControlUpgradeable {\n using ABDKMath64x64 for int128;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n function initialize() public initializer {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n }\n\n function migrateTo_f73df27() external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n firstCharacterPromoInGameOnlyFundsGivenInUsd = ABDKMath64x64.divu(\n 17220,\n 100\n );\n }\n\n mapping(address => uint256) public bits;\n uint256 public constant BIT_FIRST_CHARACTER = 1;\n uint256 public constant BIT_FOUNDER_SHIELD = 2;\n uint256 public constant BIT_BAD_ACTOR = 4;\n uint256 public constant BIT_LEGENDARY_DEFENDER = 8;\n\n int128 public firstCharacterPromoInGameOnlyFundsGivenInUsd;\n\n modifier restricted() {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n _;\n }\n\n function setBit(address user, uint256 bit) external restricted {\n bits[user] |= bit;\n }\n\n function setBits(address[] memory user, uint256 bit) public restricted {\n for(uint i = 0; i < user.length; i++)\n bits[user[i]] |= bit;\n }\n\n function unsetBit(address user, uint256 bit) public restricted {\n bits[user] &= ~bit;\n }\n\n function unsetBits(address[] memory user, uint256 bit) public restricted {\n for(uint i = 0; i < user.length; i++)\n bits[user[i]] &= ~bit;\n }\n\n function getBit(address user, uint256 bit) external view returns (bool) {\n return (bits[user] & bit) == bit;\n }\n\n function firstCharacterPromoInGameOnlyFundsGivenInUsdAsCents() external view returns (uint256) {\n return firstCharacterPromoInGameOnlyFundsGivenInUsd.mulu(100);\n }\n\n function setFirstCharacterPromoInGameOnlyFundsGivenInUsdAsCents(\n uint256 _usdCents\n ) external restricted {\n firstCharacterPromoInGameOnlyFundsGivenInUsd = ABDKMath64x64.divu(\n _usdCents,\n 100\n );\n }\n\n function setFirstCharacterPromoInGameOnlyFundsGivenInUsdAsRational(\n uint256 _numerator,\n uint256 _denominator\n ) external restricted {\n firstCharacterPromoInGameOnlyFundsGivenInUsd = ABDKMath64x64.divu(\n _numerator,\n _denominator\n );\n }\n}\n"
},
"contracts/util.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\nlibrary RandomUtil {\n\n using SafeMath for uint256;\n\n function randomSeededMinMax(uint min, uint max, uint seed) internal pure returns (uint) {\n // inclusive,inclusive (don't use absolute min and max values of uint256)\n // deterministic based on seed provided\n uint diff = max.sub(min).add(1);\n uint randomVar = uint(keccak256(abi.encodePacked(seed))).mod(diff);\n randomVar = randomVar.add(min);\n return randomVar;\n }\n\n function randomSeededMinMaxFast(uint min, uint max, uint seed) internal pure returns (uint) {\n // inclusive,inclusive (don't use absolute min and max values of uint256)\n // deterministic based on seed provided\n // NOTE: doesn't revert from under/overflow or division by zero!\n uint diff = max - min + 1;\n uint randomVar = uint(keccak256(abi.encodePacked(seed))) % diff;\n randomVar = randomVar + min;\n return randomVar;\n }\n\n function randomSeededMinMaxPrehashed(uint min, uint max, uint hashedSeed) internal pure returns (uint) {\n // inclusive,inclusive (don't use absolute min and max values of uint256)\n // deterministic based on seed provided\n // NOTE: doesn't revert from under/overflow or division by zero!\n // NOTE: the passed seed has to have already been keccak256'd!\n uint diff = max - min + 1;\n uint randomVar = hashedSeed % diff;\n randomVar = randomVar + min;\n return randomVar;\n }\n\n function combineSeeds(uint seed1, uint seed2) internal pure returns (uint) {\n return uint(keccak256(abi.encodePacked(seed1, seed2)));\n }\n\n function combineSeeds(uint[] memory seeds) internal pure returns (uint) {\n return uint(keccak256(abi.encodePacked(seeds)));\n }\n\n function plusMinus10PercentSeeded(uint256 num, uint256 seed) internal pure returns (uint256) {\n uint256 tenPercent = num.div(10);\n return num.sub(tenPercent).add(randomSeededMinMax(0, tenPercent.mul(2), seed));\n }\n\n function plusMinus10PercentSeededFast(uint256 num, uint256 seed) internal pure returns (uint256) {\n uint256 tenPercent = num/10;\n return num - tenPercent + randomSeededMinMaxFast(0, tenPercent*2, seed);\n }\n\n function plusMinus10PercentSeededPrehashed(uint256 num, uint256 hashedSeed) internal pure returns (uint256) {\n uint256 tenPercent = num/10;\n return num - tenPercent + randomSeededMinMaxPrehashed(0, tenPercent*2, hashedSeed);\n }\n\n function plusMinus30PercentSeeded(uint256 num, uint256 seed) internal pure returns (uint256) {\n // avoid decimal loss\n uint256 thirtyPercent = num.mul(30).div(100);\n return num.sub(thirtyPercent).add(randomSeededMinMax(0, thirtyPercent.mul(2), seed));\n }\n \n function uint2str(uint _i) internal pure returns (string memory _uintAsString) {\n if (_i == 0) {\n return \"0\";\n }\n uint j = _i;\n uint len;\n while (j != 0) {\n len++;\n j /= 10;\n }\n bytes memory bstr = new bytes(len);\n uint k = len - 1;\n while (_i != 0) {\n bstr[k--] = byte(uint8(48 + _i % 10));\n _i /= 10;\n }\n return string(bstr);\n }\n}"
},
"contracts/Garrison.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol\";\nimport \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport \"@openzeppelin/contracts/utils/EnumerableSet.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\nimport \"./characters.sol\";\nimport \"./cryptoblades.sol\";\n\ncontract Garrison is Initializable, IERC721ReceiverUpgradeable, AccessControlUpgradeable {\n using EnumerableSet for EnumerableSet.UintSet;\n using EnumerableSet for EnumerableSet.AddressSet;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n // STATE\n Characters characters;\n\n EnumerableSet.AddressSet private supportedTokenTypes;\n\n mapping(address => EnumerableSet.UintSet) userGarrison;\n mapping(uint256 => address) public characterOwner;\n EnumerableSet.UintSet private allCharactersInGarrison;\n\n CryptoBlades game;\n\n event CharacterReceived(uint256 indexed character, address indexed minter);\n\n function initialize(Characters _characters)\n public\n initializer\n {\n __AccessControl_init();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n characters = _characters;\n }\n\n function migrateTo_d514745(CryptoBlades _game) external restricted {\n game = _game;\n }\n\n // MODIFIERS\n modifier restricted() {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || hasRole(GAME_ADMIN, msg.sender), \"Not admin\");\n _;\n }\n\n modifier isCharacterOwner(uint256 id) {\n require(characterOwner[id] == tx.origin);\n _;\n }\n\n modifier isInGarrison(uint256 id) {\n require(allCharactersInGarrison.contains(id));\n _;\n }\n\n modifier isCharactersOwner(uint256[] memory ids) {\n _isCharactersOwner(ids);\n _;\n }\n\n function _isCharactersOwner(uint256[] memory ids) internal view {\n for(uint i = 0; i < ids.length; i++) {\n require(characterOwner[ids[i]] == msg.sender, 'Not owner');\n }\n }\n\n // VIEWS\n function getUserCharacters() public view returns (uint256[] memory tokens) {\n uint256 amount = balanceOf(msg.sender);\n tokens = new uint256[](amount);\n\n EnumerableSet.UintSet storage storedTokens = userGarrison[msg.sender];\n\n for (uint256 i = 0; i < amount; i++) {\n uint256 id = storedTokens.at(i);\n tokens[i] = id;\n }\n }\n\n function balanceOf(address user) public view returns(uint256) {\n return userGarrison[user].length();\n }\n\n // MUTATIVE\n function sendToGarrison(uint256 id) public {\n characterOwner[id] = msg.sender;\n userGarrison[msg.sender].add(id);\n allCharactersInGarrison.add(id);\n characters.safeTransferFrom(msg.sender, address(this), id);\n\n emit CharacterReceived(id, msg.sender);\n }\n\n function redirectToGarrison(address user, uint256 id) restricted external {\n characterOwner[id] = user;\n userGarrison[user].add(id);\n allCharactersInGarrison.add(id);\n\n emit CharacterReceived(id, user);\n }\n\n function restoreFromGarrison(uint256 id)\n public\n isCharacterOwner(id)\n isInGarrison(id)\n {\n require(characters.balanceOf(msg.sender) < characters.characterLimit(), \"Receiver has too many characters\");\n delete characterOwner[id];\n userGarrison[msg.sender].remove(id);\n allCharactersInGarrison.remove(id);\n characters.safeTransferFrom(address(this), msg.sender, id);\n }\n\n function swapWithGarrison(uint256 plazaId, uint256 garrisonId) external {\n sendToGarrison(plazaId);\n restoreFromGarrison(garrisonId);\n }\n\n function transferFromGarrison(address receiver, uint256 id)\n public\n isCharacterOwner(id)\n isInGarrison(id)\n {\n delete characterOwner[id];\n userGarrison[tx.origin].remove(id);\n allCharactersInGarrison.remove(id);\n characters.safeTransferFrom(address(this), receiver, id);\n }\n\n function claimAllXp(uint256[] calldata chars) external isCharactersOwner(chars) {\n uint256[] memory xps = game.getXpRewards(chars);\n game.resetXp(chars);\n characters.gainXpAll(chars, xps);\n }\n\n function updateOnBurn(address playerAddress, uint256 burnedId) external restricted {\n delete characterOwner[burnedId];\n userGarrison[playerAddress].remove(burnedId);\n allCharactersInGarrison.remove(burnedId);\n }\n\n function allowToken(IERC721 _tokenAddress) public restricted {\n supportedTokenTypes.add(address(_tokenAddress));\n }\n\n function disallowToken(IERC721 _tokenAddress) public restricted {\n supportedTokenTypes.remove(address(_tokenAddress));\n }\n\n function onERC721Received(\n address, /* operator */\n address, /* from */\n uint256 _id,\n bytes calldata /* data */\n ) external override returns (bytes4) {\n // NOTE: The contract address is always the message sender.\n address _tokenAddress = msg.sender;\n\n require(\n supportedTokenTypes.contains(_tokenAddress) &&\n allCharactersInGarrison.contains(_id),\n \"Token ID not listed\"\n );\n\n return IERC721ReceiverUpgradeable.onERC721Received.selector;\n }\n}\n"
},
"contracts/common.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\n\nlibrary Common {\n using ABDKMath64x64 for int128;\n using SafeMath for uint256;\n using SafeMath for uint8;\n\n function isTraitEffectiveAgainst(uint8 attacker, uint8 defender) internal pure returns (bool) {\n return (((attacker + 1) % 4) == defender); // Thanks to Tourist\n }\n\n function getPlayerPower(\n uint24 basePower,\n int128 weaponMultiplier,\n uint24 bonusPower\n ) internal pure returns(uint24) {\n return uint24(weaponMultiplier.mulu(basePower).add(bonusPower));\n }\n\n function getBonusRankingPoints(uint256 weakerPower, uint256 strongerPower) internal pure returns (uint256) {\n // @TODO once tested transform to save gas: \n // X < Y: (1 - ( (1.3*(x-y)/0.6*y) + (0.7*(y-x)/0.6*x) )) * 0.5\n // Note: Formula hard-copied in PvPArenaMatchMaking.vue due to contract size limitations in PvPArena.sol\n uint256 bonusRanking;\n\n uint256 strongerMinRoll = strongerPower.mul(90).div(100);\n uint256 strongerMaxRoll = strongerPower.mul(110).div(100);\n \n uint256 weakerMinRoll = weakerPower.mul(90).div(100);\n uint256 weakerMaxRoll = weakerPower.mul(110).div(100);\n\n uint256 strongerRollSpread = strongerMaxRoll.sub(strongerMinRoll);\n uint256 weakerRollSpread = weakerMaxRoll.sub(weakerMinRoll);\n\n uint256 rollOverlap = weakerMaxRoll.sub(strongerMinRoll);\n \n uint256 strongerRollChanceToOverlap = rollOverlap.mul(100).div(strongerRollSpread);\n\n uint256 weakerRollChanceToOverlap = rollOverlap.mul(100).div(weakerRollSpread);\n // A * B * 100 / 10000 * 2\n uint256 winChance = strongerRollChanceToOverlap.mul(weakerRollChanceToOverlap).mul(100).div(20000);\n\n if (winChance < 50) {\n bonusRanking = getBonusRankingPointFormula(uint256(50).sub(winChance));\n return bonusRanking;\n }\n }\n\n function getBonusRankingPointFormula(uint256 processedWinChance) internal pure returns (uint256) {\n // Note: Formula hard-copied in PvPArenaMatchMaking.vue due to contract size limitations in PvPArena.sol\n if (processedWinChance <= 40) {\n // Equivalent to (1.06**processedWinChance)\n return (53**processedWinChance).div(50**processedWinChance);\n } else {\n // Equivalent to (1.5**(1.3*processedWinChance - 48)) + 7\n return ((3**(processedWinChance.mul(13).div(10).sub(48))).div(2**(processedWinChance.mul(13).div(10).sub(48)))).add(7);\n }\n }\n\n function getPlayerPowerBase100(\n uint256 basePower,\n int128 weaponMultiplier,\n uint24 bonusPower\n ) internal pure returns (uint24) {\n // we divide total power by 100 and add the base of 1000\n return uint24 (weaponMultiplier.mulu(basePower).add(bonusPower).div(100).add(1000)); \n }\n function getPowerAtLevel(uint8 level) internal pure returns (uint24) {\n // does not use fixed points since the numbers are simple\n // the breakpoints every 10 levels are floored as expected\n // level starts at 0 (visually 1)\n // 1000 at lvl 1\n // 9000 at lvl 51 (~3months)\n // 22440 at lvl 105 (~3 years)\n // 92300 at lvl 255 (heat death of the universe)\n return uint24(\n uint256(1000)\n .add(level.mul(10))\n .mul(level.div(10).add(1))\n );\n }\n\n function adjustDecimals(uint256 amount, uint256 decimals) internal pure returns (uint256 adjustedAmount){\n if(decimals > 18) {\n adjustedAmount = amount.mul(10**uint(decimals - 18));\n } else {\n adjustedAmount = amount.div(10**uint(18 - decimals));\n }\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.2 <0.8.0;\n\nimport \"../../introspection/IERC165Upgradeable.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721Upgradeable is IERC165Upgradeable {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address from, address to, uint256 tokenId) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721MetadataUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.2 <0.8.0;\n\nimport \"./IERC721Upgradeable.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional metadata extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721MetadataUpgradeable is IERC721Upgradeable {\n\n /**\n * @dev Returns the token collection name.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the token collection symbol.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.\n */\n function tokenURI(uint256 tokenId) external view returns (string memory);\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721EnumerableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.2 <0.8.0;\n\nimport \"./IERC721Upgradeable.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721EnumerableUpgradeable is IERC721Upgradeable {\n\n /**\n * @dev Returns the total amount of tokens stored by the contract.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns a token ID owned by `owner` at a given `index` of its token list.\n * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.\n */\n function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);\n\n /**\n * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.\n * Use along with {totalSupply} to enumerate all tokens.\n */\n function tokenByIndex(uint256 index) external view returns (uint256);\n}\n"
},
"@openzeppelin/contracts-upgradeable/introspection/ERC165Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"./IERC165Upgradeable.sol\";\nimport \"../proxy/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts may inherit from this and call {_registerInterface} to declare\n * their support of an interface.\n */\nabstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {\n /*\n * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7\n */\n bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;\n\n /**\n * @dev Mapping of interface ids to whether or not it's supported.\n */\n mapping(bytes4 => bool) private _supportedInterfaces;\n\n function __ERC165_init() internal initializer {\n __ERC165_init_unchained();\n }\n\n function __ERC165_init_unchained() internal initializer {\n // Derived contracts need only register support for their own interfaces,\n // we register support for ERC165 itself here\n _registerInterface(_INTERFACE_ID_ERC165);\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n *\n * Time complexity O(1), guaranteed to always use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return _supportedInterfaces[interfaceId];\n }\n\n /**\n * @dev Registers the contract as an implementer of the interface defined by\n * `interfaceId`. Support of the actual ERC165 interface is automatic and\n * registering its interface id is not required.\n *\n * See {IERC165-supportsInterface}.\n *\n * Requirements:\n *\n * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).\n */\n function _registerInterface(bytes4 interfaceId) internal virtual {\n require(interfaceId != 0xffffffff, \"ERC165: invalid interface id\");\n _supportedInterfaces[interfaceId] = true;\n }\n uint256[49] private __gap;\n}\n"
},
"@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations with added overflow\n * checks.\n *\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\n * in bugs, because programmers usually assume that an overflow raises an\n * error, which is the standard behavior in high level programming languages.\n * `SafeMath` restores this intuition by reverting the transaction when an\n * operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeMathUpgradeable {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n uint256 c = a + b;\n require(c >= a, \"SafeMath: addition overflow\");\n return c;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n require(b <= a, \"SafeMath: subtraction overflow\");\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n if (a == 0) return 0;\n uint256 c = a * b;\n require(c / a == b, \"SafeMath: multiplication overflow\");\n return c;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n require(b > 0, \"SafeMath: division by zero\");\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n require(b > 0, \"SafeMath: modulo by zero\");\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n require(b <= a, errorMessage);\n return a - b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryDiv}.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n require(b > 0, errorMessage);\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n require(b > 0, errorMessage);\n return a % b;\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/EnumerableMapUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Library for managing an enumerable variant of Solidity's\n * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]\n * type.\n *\n * Maps have the following properties:\n *\n * - Entries are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Entries are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableMap for EnumerableMap.UintToAddressMap;\n *\n * // Declare a set state variable\n * EnumerableMap.UintToAddressMap private myMap;\n * }\n * ```\n *\n * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are\n * supported.\n */\nlibrary EnumerableMapUpgradeable {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Map type with\n // bytes32 keys and values.\n // The Map implementation uses private functions, and user-facing\n // implementations (such as Uint256ToAddressMap) are just wrappers around\n // the underlying Map.\n // This means that we can only create new EnumerableMaps for types that fit\n // in bytes32.\n\n struct MapEntry {\n bytes32 _key;\n bytes32 _value;\n }\n\n struct Map {\n // Storage of map keys and values\n MapEntry[] _entries;\n\n // Position of the entry defined by a key in the `entries` array, plus 1\n // because index 0 means a key is not in the map.\n mapping (bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Adds a key-value pair to a map, or updates the value for an existing\n * key. O(1).\n *\n * Returns true if the key was added to the map, that is if it was not\n * already present.\n */\n function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {\n // We read and store the key's index to prevent multiple reads from the same storage slot\n uint256 keyIndex = map._indexes[key];\n\n if (keyIndex == 0) { // Equivalent to !contains(map, key)\n map._entries.push(MapEntry({ _key: key, _value: value }));\n // The entry is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n map._indexes[key] = map._entries.length;\n return true;\n } else {\n map._entries[keyIndex - 1]._value = value;\n return false;\n }\n }\n\n /**\n * @dev Removes a key-value pair from a map. O(1).\n *\n * Returns true if the key was removed from the map, that is if it was present.\n */\n function _remove(Map storage map, bytes32 key) private returns (bool) {\n // We read and store the key's index to prevent multiple reads from the same storage slot\n uint256 keyIndex = map._indexes[key];\n\n if (keyIndex != 0) { // Equivalent to contains(map, key)\n // To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one\n // in the array, and then remove the last entry (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = keyIndex - 1;\n uint256 lastIndex = map._entries.length - 1;\n\n // When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs\n // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.\n\n MapEntry storage lastEntry = map._entries[lastIndex];\n\n // Move the last entry to the index where the entry to delete is\n map._entries[toDeleteIndex] = lastEntry;\n // Update the index for the moved entry\n map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based\n\n // Delete the slot where the moved entry was stored\n map._entries.pop();\n\n // Delete the index for the deleted slot\n delete map._indexes[key];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the key is in the map. O(1).\n */\n function _contains(Map storage map, bytes32 key) private view returns (bool) {\n return map._indexes[key] != 0;\n }\n\n /**\n * @dev Returns the number of key-value pairs in the map. O(1).\n */\n function _length(Map storage map) private view returns (uint256) {\n return map._entries.length;\n }\n\n /**\n * @dev Returns the key-value pair stored at position `index` in the map. O(1).\n *\n * Note that there are no guarantees on the ordering of entries inside the\n * array, and it may change when more entries are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {\n require(map._entries.length > index, \"EnumerableMap: index out of bounds\");\n\n MapEntry storage entry = map._entries[index];\n return (entry._key, entry._value);\n }\n\n /**\n * @dev Tries to returns the value associated with `key`. O(1).\n * Does not revert if `key` is not in the map.\n */\n function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) {\n uint256 keyIndex = map._indexes[key];\n if (keyIndex == 0) return (false, 0); // Equivalent to contains(map, key)\n return (true, map._entries[keyIndex - 1]._value); // All indexes are 1-based\n }\n\n /**\n * @dev Returns the value associated with `key`. O(1).\n *\n * Requirements:\n *\n * - `key` must be in the map.\n */\n function _get(Map storage map, bytes32 key) private view returns (bytes32) {\n uint256 keyIndex = map._indexes[key];\n require(keyIndex != 0, \"EnumerableMap: nonexistent key\"); // Equivalent to contains(map, key)\n return map._entries[keyIndex - 1]._value; // All indexes are 1-based\n }\n\n /**\n * @dev Same as {_get}, with a custom error message when `key` is not in the map.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {_tryGet}.\n */\n function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {\n uint256 keyIndex = map._indexes[key];\n require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)\n return map._entries[keyIndex - 1]._value; // All indexes are 1-based\n }\n\n // UintToAddressMap\n\n struct UintToAddressMap {\n Map _inner;\n }\n\n /**\n * @dev Adds a key-value pair to a map, or updates the value for an existing\n * key. O(1).\n *\n * Returns true if the key was added to the map, that is if it was not\n * already present.\n */\n function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {\n return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the key was removed from the map, that is if it was present.\n */\n function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {\n return _remove(map._inner, bytes32(key));\n }\n\n /**\n * @dev Returns true if the key is in the map. O(1).\n */\n function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {\n return _contains(map._inner, bytes32(key));\n }\n\n /**\n * @dev Returns the number of elements in the map. O(1).\n */\n function length(UintToAddressMap storage map) internal view returns (uint256) {\n return _length(map._inner);\n }\n\n /**\n * @dev Returns the element stored at position `index` in the set. O(1).\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {\n (bytes32 key, bytes32 value) = _at(map._inner, index);\n return (uint256(key), address(uint160(uint256(value))));\n }\n\n /**\n * @dev Tries to returns the value associated with `key`. O(1).\n * Does not revert if `key` is not in the map.\n *\n * _Available since v3.4._\n */\n function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {\n (bool success, bytes32 value) = _tryGet(map._inner, bytes32(key));\n return (success, address(uint160(uint256(value))));\n }\n\n /**\n * @dev Returns the value associated with `key`. O(1).\n *\n * Requirements:\n *\n * - `key` must be in the map.\n */\n function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {\n return address(uint160(uint256(_get(map._inner, bytes32(key)))));\n }\n\n /**\n * @dev Same as {get}, with a custom error message when `key` is not in the map.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryGet}.\n */\n function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {\n return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage))));\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary StringsUpgradeable {\n /**\n * @dev Converts a `uint256` to its ASCII `string` representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n uint256 index = digits - 1;\n temp = value;\n while (temp != 0) {\n buffer[index--] = bytes1(uint8(48 + temp % 10));\n temp /= 10;\n }\n return string(buffer);\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/introspection/IERC165Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165Upgradeable {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
},
"contracts/interfaces/IStakeFromGame.sol": {
"content": "pragma solidity ^0.6.5;\n\ninterface IStakeFromGame {\n function stakeFromGame(address player, uint256 amount) external;\n\n function unstakeToGame(address player, uint256 amount) external;\n}\n"
},
"contracts/interfaces/IRandoms.sol": {
"content": "pragma solidity ^0.6.5;\n\ninterface IRandoms {\n // Views\n function getRandomSeed(address user) external view returns (uint256 seed);\n function getRandomSeedUsingHash(address user, bytes32 hash) external view returns (uint256 seed);\n}\n"
},
"contracts/Blacksmith.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"./interfaces/IPriceOracle.sol\";\nimport \"./interfaces/IRandoms.sol\";\nimport \"./shields.sol\";\nimport \"./Consumables.sol\";\nimport \"./Cosmetics.sol\";\nimport \"./weapons.sol\";\nimport \"./cryptoblades.sol\";\nimport \"./CBKLandSale.sol\";\n\ncontract Blacksmith is Initializable, AccessControlUpgradeable {\n using SafeERC20 for IERC20;\n /* ========== CONSTANTS ========== */\n\n bytes32 public constant GAME = keccak256(\"GAME\");\n bytes32 public constant SHIELD_SEED = keccak256(\"SHIELD_SEED\");\n\n uint256 public constant ITEM_WEAPON_RENAME = 1;\n uint256 public constant ITEM_CHARACTER_RENAME = 2;\n uint256 public constant ITEM_CHARACTER_TRAITCHANGE_FIRE = 3;\n uint256 public constant ITEM_CHARACTER_TRAITCHANGE_EARTH = 4;\n uint256 public constant ITEM_CHARACTER_TRAITCHANGE_WATER = 5;\n uint256 public constant ITEM_CHARACTER_TRAITCHANGE_LIGHTNING = 6;\n uint256 public constant ITEM_COSMETIC_WEAPON = 7; // series\n uint256 public constant ITEM_COSMETIC_CHARACTER = 8; // series\n uint256 public constant ITEM_SHIELD = 9;\n\n uint256 public constant VAR_PURCHASE_SHIELD_TYPE = 1;\n uint256 public constant VAR_PURCHASE_SHIELD_SUPPLY = 2; // only for non-0 type shields\n\n uint256 public constant LINK_SKILL_ORACLE_2 = 1; // technically second skill oracle (it's separate)\n uint256 public constant LINK_KING_ORACLE = 2;\n uint256 public constant LINK_SAFE_RANDOMS = 3;\n\n uint256 public constant CURRENCY_SKILL = 0;\n //uint256 public constant CURRENCY_KING = 1; // not referenced atm\n\n /* ========== STATE VARIABLES ========== */\n\n Weapons public weapons;\n IRandoms public randoms;\n\n mapping(address => uint32) public tickets;\n\n Shields public shields;\n CryptoBlades public game;\n\n\n // keys: ITEM_ constant\n mapping(uint256 => address) public itemAddresses;\n mapping(uint256 => uint256) public itemFlatPrices;\n\n mapping(uint256 => uint256) public numberParameters; // AKA \"vars\"\n\n mapping(uint256 => mapping(uint256 => uint256)) public itemSeriesFlatPrices;\n CBKLandSale public cbkLandSale;\n // ERC20 => tier => price\n mapping(uint256 => mapping(uint256 => uint256)) public landPrices;\n mapping(uint256 => address) currencies;\n\n mapping(uint256 => address) public links;\n\n /* ========== INITIALIZERS AND MIGRATORS ========== */\n\n function initialize(Weapons _weapons, IRandoms _randoms)\n public\n initializer\n {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n weapons = _weapons;\n randoms = _randoms;\n }\n\n function migrateRandoms(IRandoms _newRandoms) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n randoms = _newRandoms;\n }\n\n function migrateTo_61c10da(Shields _shields, CryptoBlades _game) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n shields = _shields;\n game = _game;\n }\n\n function migrateTo_16884dd(\n address _characterRename,\n address _weaponRename,\n address _charFireTraitChange,\n address _charEarthTraitChange,\n address _charWaterTraitChange,\n address _charLightningTraitChange\n ) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n itemAddresses[ITEM_WEAPON_RENAME] = _weaponRename;\n itemAddresses[ITEM_CHARACTER_RENAME] = _characterRename;\n itemAddresses[ITEM_CHARACTER_TRAITCHANGE_FIRE] = _charFireTraitChange;\n itemAddresses[ITEM_CHARACTER_TRAITCHANGE_EARTH] = _charEarthTraitChange;\n itemAddresses[ITEM_CHARACTER_TRAITCHANGE_WATER] = _charWaterTraitChange;\n itemAddresses[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING] = _charLightningTraitChange;\n\n itemFlatPrices[ITEM_WEAPON_RENAME] = 0.1 ether;\n itemFlatPrices[ITEM_CHARACTER_RENAME] = 0.1 ether;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE] = 0.2 ether;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH] = 0.2 ether;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER] = 0.2 ether;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING] = 0.2 ether;\n }\n\n function migrateTo_bcdf4c(CBKLandSale _cbkLandSale) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n cbkLandSale = _cbkLandSale;\n }\n\n /* ========== VIEWS ========== */\n\n /* ========== MUTATIVE FUNCTIONS ========== */\n\n function recoverToken(address tokenAddress, uint256 amount) public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n IERC20(tokenAddress).safeTransfer(msg.sender, amount);\n }\n\n function getSeed(uint seedId, uint shieldType) internal pure returns (uint256 seed) {\n uint[] memory seeds = new uint[](3);\n seeds[0] = seedId;\n seeds[1] = uint(1);\n seeds[2] = shieldType;\n seed = RandomUtil.combineSeeds(seeds);\n }\n\n function hasSeed(uint seedId, uint shieldType) public view returns (bool) {\n return SafeRandoms(links[LINK_SAFE_RANDOMS]).hasSingleSeedRequest(msg.sender, getSeed(seedId, shieldType));\n }\n\n function generateShieldSeed() external {\n require(itemFlatPrices[ITEM_SHIELD] > 0);\n uint256 shieldType = numberParameters[VAR_PURCHASE_SHIELD_TYPE];\n payCurrency(msg.sender, itemFlatPrices[ITEM_SHIELD], CURRENCY_SKILL);\n SafeRandoms(links[LINK_SAFE_RANDOMS]).requestSingleSeed(msg.sender, getSeed(uint(SHIELD_SEED), shieldType));\n }\n\n function claimShield() external {\n uint256 shieldType = numberParameters[VAR_PURCHASE_SHIELD_TYPE];\n if(shieldType != 0) {\n require(numberParameters[VAR_PURCHASE_SHIELD_SUPPLY] > 0);\n numberParameters[VAR_PURCHASE_SHIELD_SUPPLY] -= 1;\n }\n uint256 seed = SafeRandoms(links[LINK_SAFE_RANDOMS]).popSingleSeed(msg.sender, getSeed(uint(SHIELD_SEED), shieldType), true, false);\n shields.mint(msg.sender, shieldType, seed);\n }\n\n /* ========== MODIFIERS ========== */\n\n modifier onlyGame() {\n require(hasRole(GAME, msg.sender), \"Only game\");\n _;\n }\n\n modifier isAdmin() {\n _isAdmin();\n _;\n }\n\n function _isAdmin() internal view {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n }\n\n /* ========== Generic Getters ========== */\n\n function getAddressOfItem(uint256 itemIndex) public view returns(address) {\n return itemAddresses[itemIndex];\n }\n\n function getFlatPriceOfItem(uint256 itemIndex) public view returns(uint256) {\n return itemFlatPrices[itemIndex];\n }\n\n function getFlatPriceOfSeriesItem(uint256 itemIndex, uint256 seriesIndex) public view returns(uint256) {\n return itemSeriesFlatPrices[itemIndex][seriesIndex];\n }\n\n function getCurrency(uint256 currency) public view returns (address) {\n return currencies[currency];\n }\n\n function vars(uint256 varField) public view returns (uint256) {\n return numberParameters[varField];\n }\n\n /* ========== Generic Setters ========== */\n\n function setAddressOfItem(uint256 itemIndex, address to) external isAdmin {\n itemAddresses[itemIndex] = to;\n }\n\n function setFlatPriceOfItem(uint256 itemIndex, uint256 flatWeiPrice) external isAdmin {\n itemFlatPrices[itemIndex] = flatWeiPrice;\n }\n\n function setFlatPriceOfItemSeries(uint256 itemIndex,\n uint256[] calldata seriesIndices,\n uint256[] calldata seriesPrices\n ) external isAdmin {\n for(uint i = 0; i < seriesIndices.length; i++) {\n itemSeriesFlatPrices[itemIndex][seriesIndices[i]] = seriesPrices[i];\n }\n }\n\n function setCurrency(uint256 currency, address currencyAddress, bool forced) external isAdmin {\n require(currency > 0 && (forced || currencies[currency] == address(0)), 'used');\n currencies[currency] = currencyAddress;\n }\n\n function setLink(uint256 linkId, address linkAddress) external isAdmin {\n links[linkId] = linkAddress;\n }\n\n function setVar(uint256 varField, uint256 value) external isAdmin {\n numberParameters[varField] = value;\n }\n\n function setVars(uint256[] calldata varFields, uint256[] calldata values) external isAdmin {\n for(uint i = 0; i < varFields.length; i++) {\n numberParameters[varFields[i]] = values[i];\n }\n }\n\n /* ========== Character Rename ========== */\n\n function setCharacterRenamePrice(uint256 newPrice) external isAdmin {\n require(newPrice > 0, 'invalid price');\n itemFlatPrices[ITEM_CHARACTER_RENAME] = newPrice;\n }\n\n function characterRenamePrice() public view returns (uint256){\n return itemFlatPrices[ITEM_CHARACTER_RENAME];\n }\n\n function purchaseCharacterRenameTag(uint256 paying) public {\n require(paying == itemFlatPrices[ITEM_CHARACTER_RENAME], 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_CHARACTER_RENAME], CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_CHARACTER_RENAME]).giveItem(msg.sender, 1);\n }\n\n function purchaseCharacterRenameTagDeal(uint256 paying) public { // 4 for the price of 3\n require(paying == itemFlatPrices[ITEM_CHARACTER_RENAME] * 3, 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_CHARACTER_RENAME] * 3, CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_CHARACTER_RENAME]).giveItem(msg.sender, 4);\n }\n\n /* ========== Weapon Rename ========== */\n\n function setWeaponRenamePrice(uint256 newPrice) external isAdmin {\n require(newPrice > 0, 'invalid price');\n itemFlatPrices[ITEM_WEAPON_RENAME] = newPrice;\n }\n\n function weaponRenamePrice() public view returns (uint256){\n return itemFlatPrices[ITEM_WEAPON_RENAME];\n }\n\n function purchaseWeaponRenameTag(uint256 paying) public {\n require(paying == itemFlatPrices[ITEM_WEAPON_RENAME], 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_WEAPON_RENAME], CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_WEAPON_RENAME]).giveItem(msg.sender, 1);\n }\n\n function purchaseWeaponRenameTagDeal(uint256 paying) public { // 4 for the price of 3\n require(paying == itemFlatPrices[ITEM_WEAPON_RENAME] * 3, 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_WEAPON_RENAME] * 3, CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_WEAPON_RENAME]).giveItem(msg.sender, 4);\n }\n\n /* ========== Character Trait Change ========== */\n\n function setCharacterTraitChangePrice(uint256 newPrice) external isAdmin {\n require(newPrice > 0, 'invalid price');\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE] = newPrice;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH] = newPrice;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER] = newPrice;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING] = newPrice;\n }\n\n function characterTraitChangePrice() public view returns (uint256){\n return itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE];\n }\n\n function purchaseCharacterFireTraitChange(uint256 paying) public {\n require(paying == itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE], 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE], CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_CHARACTER_TRAITCHANGE_FIRE]).giveItem(msg.sender, 1);\n }\n\n function purchaseCharacterEarthTraitChange(uint256 paying) public {\n require(paying == itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH], 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH], CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_CHARACTER_TRAITCHANGE_EARTH]).giveItem(msg.sender, 1);\n }\n\n function purchaseCharacterWaterTraitChange(uint256 paying) public {\n require(paying == itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER], 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER], CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_CHARACTER_TRAITCHANGE_WATER]).giveItem(msg.sender, 1);\n }\n\n function purchaseCharacterLightningTraitChange(uint256 paying) public {\n require(paying == itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING], 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING], CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING]).giveItem(msg.sender, 1);\n }\n\n\n /* ========== Weapon cosmetics ========== */\n function setWeaponCosmeticPrice(uint32 cosmetic, uint256 newPrice) external isAdmin {\n require(cosmetic > 0 && newPrice > 0, 'invalid request');\n itemSeriesFlatPrices[ITEM_COSMETIC_WEAPON][cosmetic] = newPrice;\n }\n\n function getWeaponCosmeticPrice(uint32 cosmetic) public view returns (uint256){\n return itemSeriesFlatPrices[ITEM_COSMETIC_WEAPON][cosmetic];\n }\n\n function purchaseWeaponCosmetic(uint32 cosmetic, uint256 paying) public {\n require(paying > 0 && paying == itemSeriesFlatPrices[ITEM_COSMETIC_WEAPON][cosmetic], 'Invalid price');\n payCurrency(msg.sender, itemSeriesFlatPrices[ITEM_COSMETIC_WEAPON][cosmetic], CURRENCY_SKILL);\n Cosmetics(itemAddresses[ITEM_COSMETIC_WEAPON]).giveCosmetic(msg.sender, cosmetic, 1);\n }\n\n /* ========== Character cosmetics ========== */\n function setCharacterCosmeticPrice(uint32 cosmetic, uint256 newPrice) external isAdmin {\n require(cosmetic > 0 && newPrice > 0, 'invalid request');\n itemSeriesFlatPrices[ITEM_COSMETIC_CHARACTER][cosmetic] = newPrice;\n }\n\n function getCharacterCosmeticPrice(uint32 cosmetic) public view returns (uint256){\n return itemSeriesFlatPrices[ITEM_COSMETIC_CHARACTER][cosmetic];\n }\n\n function purchaseCharacterCosmetic(uint32 cosmetic, uint256 paying) public {\n require(paying > 0 && paying == itemSeriesFlatPrices[ITEM_COSMETIC_CHARACTER][cosmetic], 'Invalid price');\n payCurrency(msg.sender, itemSeriesFlatPrices[ITEM_COSMETIC_CHARACTER][cosmetic], CURRENCY_SKILL);\n Cosmetics(itemAddresses[ITEM_COSMETIC_CHARACTER]).giveCosmetic(msg.sender, cosmetic, 1);\n }\n\n /* ========== CBK Land sale ========== */\n\n event CBKLandPurchased(address indexed owner, uint256 tier, uint256 price, uint256 currency);\n\n function purchaseT1CBKLand(uint256 paying, uint256 currency) public {\n uint256 price = getCBKLandPrice(cbkLandSale.TIER_ONE(), currency);\n require(paying > 0 && price == paying, 'Invalid price');\n payCurrency(msg.sender, price, currency);\n cbkLandSale.giveT1Land(msg.sender);\n emit CBKLandPurchased(msg.sender, cbkLandSale.TIER_ONE(), price, currency);\n }\n\n function purchaseT2CBKLand(uint256 paying, uint256 chunkId, uint256 currency) public {\n uint256 price = getCBKLandPrice(cbkLandSale.TIER_TWO(), currency);\n require(paying > 0 && price == paying, 'Invalid price');\n payCurrency(msg.sender, price, currency);\n cbkLandSale.giveT2Land(msg.sender, chunkId);\n emit CBKLandPurchased(msg.sender, cbkLandSale.TIER_TWO(), price, currency);\n }\n\n function purchaseT3CBKLand(uint256 paying, uint256 chunkId, uint256 currency) public {\n uint256 price = getCBKLandPrice(cbkLandSale.TIER_THREE(), currency);\n require(paying > 0 && price == paying, 'Invalid price');\n payCurrency(msg.sender, price, currency);\n cbkLandSale.giveT3Land(msg.sender, chunkId);\n emit CBKLandPurchased(msg.sender, cbkLandSale.TIER_THREE(), price, currency);\n }\n\n function getCBKLandPrice(uint256 tier, uint256 currency) public view returns (uint256){\n return landPrices[currency][tier] * getOracledTokenPerUSD(currency);\n }\n\n function getOracledTokenPerUSD(uint256 currency) public view returns (uint256) {\n if(currency == CURRENCY_SKILL) {\n return IPriceOracle(links[LINK_SKILL_ORACLE_2]).currentPrice();\n }\n else {\n return IPriceOracle(links[LINK_KING_ORACLE]).currentPrice();\n }\n }\n\n function setCBKLandPrice(uint256 tier, uint256 newPrice, uint256 currency) external isAdmin {\n require(newPrice > 0, 'invalid price');\n require(tier >= cbkLandSale.TIER_ONE() && tier <= cbkLandSale.TIER_THREE(), \"Invalid tier\");\n landPrices[currency][tier] = newPrice;\n }\n\n function payCurrency(address payer, uint256 paying, uint256 currency) internal {\n if(currency == CURRENCY_SKILL){\n game.payContractConvertedSupportingStaked(payer, paying);\n }\n else {\n IERC20(currencies[currency]).transferFrom(payer, address(this), paying);\n }\n }\n}\n"
},
"contracts/SpecialWeaponsManager.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"./Promos.sol\";\nimport \"./weapons.sol\";\nimport \"./SafeRandoms.sol\";\nimport \"./util.sol\";\nimport \"./staking/StakingRewardsUpgradeable.sol\";\nimport \"./interfaces/IPriceOracle.sol\";\n\ncontract SpecialWeaponsManager is Initializable, AccessControlUpgradeable {\n using SafeMath for uint256;\n using ABDKMath64x64 for int128;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n bytes32 public constant SPECIAL_WEAPON_SEED = keccak256(\"SPECIAL_WEAPON_SEED\");\n\n // STATE\n Promos public promos;\n Weapons public weapons;\n SafeRandoms public safeRandoms;\n CryptoBlades public game;\n IPriceOracle public priceOracleSkillPerUsd;\n\n struct EventInfo {\n string name;\n uint8 weaponElement;\n uint256 endTime;\n uint256 supply;\n uint256 orderedCount;\n }\n\n mapping(address => mapping(uint256 => uint256)) public userEventShardSupply;\n mapping(uint256 => uint256) public vars;\n uint256 public constant VAR_SHARD_COST_LOW = 1;\n uint256 public constant VAR_SHARD_COST_MEDIUM = 2;\n uint256 public constant VAR_SHARD_COST_HIGH = 3;\n uint256 public constant VAR_SKILL_USD_COST_LOW = 4;\n uint256 public constant VAR_SKILL_USD_COST_MEDIUM = 5;\n uint256 public constant VAR_SKILL_USD_COST_HIGH = 6;\n uint256 public constant VAR_CONVERT_RATIO_DENOMINATOR = 10;\n uint256 public constant VAR_DAILY_SHARDS_PER_SKILL_STAKED = 11;\n uint256 public eventCount;\n\n mapping(uint256 => EventInfo) public eventInfo;\n mapping(address => mapping(uint256 => bool)) public userForgedAtEvent;\n mapping(address => mapping(uint256 => uint256)) public userOrderOptionForEvent;\n mapping(address => uint256) userStakedSkill;\n mapping(address => uint256) userStakedSkillUpdatedTimestamp;\n mapping(address => uint256) userSkillStakingShardsRewards;\n mapping(uint256 => string) public specialWeaponArt;\n mapping(uint256 => string) public specialWeaponDetails;\n mapping(uint256 => string) public specialWeaponWebsite;\n mapping(uint256 => string) public specialWeaponNote;\n\n\n function initialize(Promos _promos, Weapons _weapons, SafeRandoms _safeRandoms, CryptoBlades _game, IPriceOracle _priceOracleSkillPerUsd)\n public\n initializer\n {\n __AccessControl_init();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _setupRole(GAME_ADMIN, msg.sender);\n\n promos = _promos;\n weapons = _weapons;\n safeRandoms = _safeRandoms;\n game = _game;\n priceOracleSkillPerUsd = _priceOracleSkillPerUsd;\n eventCount = 0;\n }\n\n // MODIFIERS\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"NGA\");\n }\n\n modifier hasMinterRole() {\n _hasMinterRole();\n _;\n }\n\n function _hasMinterRole() internal view {\n require(hasRole(MINTER_ROLE, msg.sender), \"Not minter\");\n }\n\n modifier isValidOption(uint256 orderOption) {\n _isValidOption(orderOption);\n _;\n }\n\n function _isValidOption(uint256 orderOption) internal pure {\n require(orderOption >= 1 && orderOption <= 3, \"Invalid option\");\n }\n\n modifier isEventActive(uint256 eventId) {\n _isEventActive(eventId);\n _;\n }\n\n function _isEventActive(uint256 eventId) internal view {\n require(getIsEventActive(eventId), \"Event inactive\");\n }\n\n modifier canBeOrdered(uint256 eventId) {\n _canBeOrdered(eventId);\n _;\n }\n\n function _canBeOrdered(uint256 eventId) internal view {\n require(userOrderOptionForEvent[msg.sender][eventId] == 0, \"Limit 1\");\n require(hasRemainingSupply(eventId), \"Sold out\");\n }\n\n // VARS\n\n function setVar(uint256 varField, uint256 value) external restricted {\n vars[varField] = value;\n }\n\n function setVars(uint256[] calldata varFields, uint256[] calldata values) external restricted {\n for(uint i = 0; i < varFields.length; i++) {\n vars[varFields[i]] = values[i];\n }\n }\n\n // VIEWS\n\n function getSeed(uint256 eventId) internal pure returns(uint256 seed) {\n seed = uint(keccak256(abi.encodePacked(SPECIAL_WEAPON_SEED, uint(1), eventId)));\n }\n\n function getActiveEventsIds() public view returns(uint256[] memory) {\n uint256[] memory activeEventIds = new uint256[](getActiveEventsCount());\n uint256 arrayIterator = 0;\n for(uint i = 1; i <= eventCount; i++) {\n if(eventInfo[i].endTime > block.timestamp) {\n activeEventIds[arrayIterator++] = i;\n }\n }\n\n return activeEventIds;\n }\n\n function getActiveEventsCount() public view returns(uint256 activeEventsCount) {\n for(uint i = 0; i <= eventCount; i++) {\n if(getIsEventActive(i)) {\n activeEventsCount++;\n }\n }\n }\n\n function getIsEventActive(uint256 eventId) public view returns(bool) {\n return eventInfo[eventId].endTime > block.timestamp;\n }\n\n function hasRemainingSupply(uint256 eventId) public view returns(bool) {\n return eventInfo[eventId].supply == 0 || eventInfo[eventId].orderedCount < eventInfo[eventId].supply;\n }\n\n function getTotalOrderedCount(uint256 eventId) public view returns(uint256) {\n return eventInfo[eventId].orderedCount;\n }\n\n function getEventInfo(uint256 eventId) public view returns(string memory, uint8, uint256, uint256, uint256) {\n EventInfo memory info = eventInfo[eventId];\n return (info.name, info.weaponElement, info.endTime, info.supply, info.orderedCount);\n }\n\n function getUserSpecialShardsSupply(address user, uint256 eventId) public view returns(uint256) {\n return userEventShardSupply[user][eventId];\n }\n\n function getUserShardsRewards(address user) public view returns(uint256) {\n return userSkillStakingShardsRewards[user]\n .add(userStakedSkill[user]\n .mul(vars[VAR_DAILY_SHARDS_PER_SKILL_STAKED])\n .mul(block.timestamp - userStakedSkillUpdatedTimestamp[user])\n .div(60 * 60 * 24)\n .div(1e18)\n );\n }\n\n function getSkillForgeCost(uint256 orderOption) public view returns(uint256) {\n return usdToSkill(ABDKMath64x64.divu(vars[orderOption + 3], 1));\n }\n\n function usdToSkill(int128 usdAmount) public view returns (uint256) {\n return usdAmount.mulu(priceOracleSkillPerUsd.currentPrice());\n }\n\n function getSpecialWeaponData(uint256 eventId) public view returns (string memory, string memory, string memory, string memory) {\n return (specialWeaponArt[eventId], specialWeaponDetails[eventId], specialWeaponWebsite[eventId], specialWeaponNote[eventId]);\n }\n\n // FUNCTIONS\n\n // supply 0 = unlimited\n function startNewEvent(string calldata name, uint8 element, uint256 period, uint256 supply, string calldata art, string calldata details, string calldata website, string calldata note) external restricted {\n uint eventId = ++eventCount;\n eventInfo[eventId] = EventInfo(\n name,\n element,\n block.timestamp + period,\n supply,\n 0\n );\n specialWeaponArt[eventId] = art;\n specialWeaponDetails[eventId] = details;\n specialWeaponWebsite[eventId] = website;\n specialWeaponNote[eventId] = note;\n }\n\n function incrementEventCount() external restricted {\n eventCount++;\n }\n\n function updateStakingReward(address user, uint256 stakingAmount) external restricted {\n userSkillStakingShardsRewards[user] = getUserShardsRewards(user);\n userStakedSkill[user] = stakingAmount;\n userStakedSkillUpdatedTimestamp[user] = block.timestamp;\n }\n\n function claimShardRewards(uint256 eventId, uint256 amount) external {\n require(amount.mul(1e18) <= getUserShardsRewards(msg.sender), \"Not enough rewards\");\n userSkillStakingShardsRewards[msg.sender] = getUserShardsRewards(msg.sender).sub(amount.mul(1e18));\n userStakedSkillUpdatedTimestamp[msg.sender] = block.timestamp;\n userEventShardSupply[msg.sender][eventId] += amount;\n }\n\n function orderSpecialWeaponWithShards(uint256 eventId, uint256 orderOption) public canBeOrdered(eventId) isEventActive(eventId) isValidOption(orderOption) {\n require(userEventShardSupply[msg.sender][eventId] >= vars[orderOption], \"Not enough shards\");\n userEventShardSupply[msg.sender][eventId] -= vars[orderOption];\n userOrderOptionForEvent[msg.sender][eventId] = orderOption;\n eventInfo[eventId].orderedCount++;\n safeRandoms.requestSingleSeed(msg.sender, getSeed(eventId));\n }\n\n function orderSpecialWeaponWithSkill(uint256 eventId, uint256 orderOption) public canBeOrdered(eventId) isEventActive(eventId) isValidOption(orderOption) {\n game.payContractTokenOnly(msg.sender, getSkillForgeCost(orderOption));\n userOrderOptionForEvent[msg.sender][eventId] = orderOption;\n eventInfo[eventId].orderedCount++;\n safeRandoms.requestSingleSeed(msg.sender, getSeed(eventId));\n }\n\n function forgeSpecialWeapon(uint256 eventId) public {\n require(userOrderOptionForEvent[msg.sender][eventId] > 0, 'Nothing to forge');\n require(!userForgedAtEvent[msg.sender][eventId], 'Already forged');\n userForgedAtEvent[msg.sender][eventId] = true;\n mintSpecial(\n msg.sender,\n eventId,\n safeRandoms.popSingleSeed(msg.sender, getSeed(eventId), true, false),\n userOrderOptionForEvent[msg.sender][eventId],\n eventInfo[eventId].weaponElement\n );\n }\n\n function addShards(address user, uint256 eventId, uint256 shardsAmount) external restricted isEventActive(eventId){\n userEventShardSupply[user][eventId] += shardsAmount;\n }\n\n function mintSpecial(address minter, uint256 eventId, uint256 seed, uint256 orderOption, uint8 element) private returns(uint256) {\n uint256 stars;\n uint256 roll = seed % 100;\n if(orderOption == 3) {\n stars = 4;\n }\n else if(orderOption == 2) {\n if(roll < 16) {\n stars = 4;\n }\n else {\n stars = 3;\n }\n }\n else {\n if(roll < 5) {\n stars = 4;\n }\n else if (roll < 28) {\n stars = 3;\n }\n else {\n stars = 2;\n }\n }\n\n return mintSpecialWeaponWithStars(minter, eventId, stars, seed, element);\n }\n\n function mintSpecialWeaponWithStars(address minter, uint256 eventId, uint256 stars, uint256 seed, uint8 element) private returns(uint256) {\n return weapons.mintSpecialWeapon(minter, eventId, stars, seed, element);\n }\n\n function convertShards(uint256 eventIdFrom, uint256 eventIdTo, uint256 amount) external isEventActive(eventIdTo) {\n require(userEventShardSupply[msg.sender][eventIdFrom] >= amount, 'Not enough shards');\n userEventShardSupply[msg.sender][eventIdFrom] -= amount;\n uint256 convertedAmount = amount.div(vars[VAR_CONVERT_RATIO_DENOMINATOR]);\n convertedAmount += userEventShardSupply[msg.sender][eventIdFrom] == 0 && amount % vars[VAR_CONVERT_RATIO_DENOMINATOR] > 0 ? 1 : 0;\n userEventShardSupply[msg.sender][eventIdTo] += convertedAmount;\n }\n\n function updateEventEndTime(uint256 eventId, uint256 endTime) external restricted {\n eventInfo[eventId].endTime = endTime;\n }\n\n // MANUAL USE ONLY; DO NOT USE IN CONTRACTS!\n function privatePartnerOrder(address[] calldata receivers, uint256 eventId, uint256 orderOption) external hasMinterRole isValidOption(orderOption) isEventActive(eventId) {\n require(eventInfo[eventId].supply == 0 || receivers.length + eventInfo[eventId].orderedCount <= eventInfo[eventId].supply, \"Not enough supply\");\n for(uint i = 0; i < receivers.length; i++) {\n if(userOrderOptionForEvent[receivers[i]][eventId] != 0) continue;\n userOrderOptionForEvent[receivers[i]][eventId] = orderOption;\n eventInfo[eventId].orderedCount++;\n safeRandoms.requestSingleSeed(receivers[i], getSeed(eventId));\n }\n }\n\n // MANUAL USE ONLY; DO NOT USE IN CONTRACTS!\n function privatePartnerMint(address[] calldata receivers, uint256 eventId, uint256 orderOption) external hasMinterRole isValidOption(orderOption) isEventActive(eventId) {\n require(eventInfo[eventId].supply == 0 || receivers.length + eventInfo[eventId].orderedCount <= eventInfo[eventId].supply, \"Not enough supply\");\n for(uint i = 0; i < receivers.length; i++) {\n if(userOrderOptionForEvent[receivers[i]][eventId] != 0 || userForgedAtEvent[receivers[i]][eventId]) continue;\n userOrderOptionForEvent[receivers[i]][eventId] = orderOption;\n eventInfo[eventId].orderedCount++;\n userForgedAtEvent[receivers[i]][eventId] = true;\n mintSpecial(\n receivers[i],\n eventId,\n uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), receivers[i]))),\n userOrderOptionForEvent[receivers[i]][eventId],\n eventInfo[eventId].weaponElement\n );\n }\n }\n\n // MANUAL USE ONLY; DO NOT USE IN CONTRACTS!\n function reserveForGiveaways(address reservingAddress, uint256 eventId, uint256 orderOption, uint256 amount) external hasMinterRole isValidOption(orderOption) isEventActive(eventId) {\n require(eventInfo[eventId].supply == 0 || amount + eventInfo[eventId].orderedCount <= eventInfo[eventId].supply, \"Not enough supply\");\n for(uint i = 0; i < amount; i++) {\n eventInfo[eventId].orderedCount++;\n mintSpecial(\n reservingAddress,\n eventId,\n uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), reservingAddress, i))),\n orderOption,\n eventInfo[eventId].weaponElement\n );\n }\n }\n\n // MANUAL USE ONLY; DO NOT USE IN CONTRACTS!\n function createManualEvent(string calldata name, uint8 element) external restricted {\n eventInfo[++eventCount] = EventInfo(\n name,\n element,\n 1, // end time 1 to differentiate from non existing events\n 0,\n 0\n );\n }\n\n // MANUAL USE ONLY; DO NOT USE IN CONTRACTS!\n function mintOrderOptionForManualEvent(address[] calldata receivers, uint256 eventId, uint256 orderOption) external hasMinterRole isValidOption(orderOption) {\n require(eventInfo[eventId].endTime == 1, \"Wrong event id\");\n for(uint i = 0; i < receivers.length; i++) {\n eventInfo[eventId].orderedCount++;\n mintSpecial(\n receivers[i],\n eventId,\n uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), receivers[i]))),\n orderOption,\n eventInfo[eventId].weaponElement\n );\n }\n }\n\n // MANUAL USE ONLY; DO NOT USE IN CONTRACTS!\n function mintStarsForManualEvent(address[] calldata receivers, uint256 eventId, uint256 stars) external hasMinterRole {\n require(eventInfo[eventId].endTime == 1, \"Wrong event id\");\n require(stars >= 2 && stars <= 4, \"Wrong stars\");\n for(uint i = 0; i < receivers.length; i++) {\n eventInfo[eventId].orderedCount++;\n mintSpecialWeaponWithStars(\n receivers[i],\n eventId,\n stars,\n uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), receivers[i]))),\n eventInfo[eventId].weaponElement\n );\n }\n }\n\n // SETTERS\n\n function setSpecialWeaponArt(uint256 eventId, string calldata art) external restricted {\n specialWeaponArt[eventId] = art;\n }\n\n function setSpecialWeaponDetails(uint256 eventId, string calldata details) external restricted {\n specialWeaponDetails[eventId] = details;\n }\n\n function setSpecialWeaponWebsite(uint256 eventId, string calldata website) external restricted {\n specialWeaponWebsite[eventId] = website;\n }\n\n function setSpecialWeaponNote(uint256 eventId, string calldata note) external restricted {\n specialWeaponNote[eventId] = note;\n }\n\n}\n"
},
"contracts/SafeRandoms.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\n\ncontract SafeRandoms is Initializable, AccessControlUpgradeable {\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n /* Security\n * Seeds are more secure the faster they are resolved\n * Resolution every block = 100% security\n * Optionally, publicResolutionBlocks can be set to 0 to force admin resolution\n */\n\n /* Usage:\n *\n * Transaction #1: Have the user pay costs (if applicable) and request the seed\n * Transaction #2: Pop the seed (will revert if not ready, use check if necessary)\n *\n * After popping, you can use one seed for multiple outcomes (ie 10 weapon seeds)\n * Just make sure to re-encode with keccak256(abi.encodePacked(seed,value...))\n * ONLY with values that won't change over time:\n * ie. block.number: NO\n * 1,2,3 etc: YES, IF seeds aren't shared under the same action identifier\n * (so 1x weapons produce different requestIDs than 10x weapons)\n *\n * You can use the requestNext boolean for pop calls to request the next seed already,\n * This allows you to have a new secure seed ready for every transaction (except the first)\n *\n * Resolve booleans of functions contribute to seed resolution,\n * (sending false can be used to save gas if necessary)\n * Check costs ~2k gas, resolution costs 35k (+3.1k for event + 1.7k for event check)\n */\n\n /* Seed Types\n *\n * Single (One-at-a-time) seeds:\n * Only one request possible per user for this type at a time\n * The seed must be used before another one can be requested\n * Can be used for actions that don't have up-front cost (besides gas)\n * But! Consider possibility that the user may transfer NFT to another wallet or bridge networks\n * These seeds can be salted after without popping to produce expectable results if tolerable\n * (this saves gas if the initial action needed security but the following ones don't)\n * ! Salted seeds are stored separately, the original one-at-a-time seed will be available raw\n *\n * Queued seeds:\n * More than one seed request can be piled up (costs ~15-20k more gas than single seeds)\n * Example use: Ordering multiple batches of weapons without requiring the first batch to complete\n * MUST HAVE a full upfront cost to avoid abuse! (ie skill cost to mint an NFT)\n *\n * !!! IMPORTANT !!!!\n * Seed types are not to be mixed for the same action type!\n * Requesting a queued seed and popping a one-timer won't work, and vice versa\n */\n\n /* RequestIDs\n *\n * Request ID is a unique value to identify the exact action a random seed is requested for.\n * A seed requested for 1x weapon mint must be different than for 10x weapon mints etc.\n *\n * Produce clean looking request IDs for two properties:\n * RandomUtil.combineSeeds(SEED_WEAPON_MINT, amount)\n *\n * Or dirtier / for many properties: (you can slap arrays into encodePacked directly)\n * uint(keccak256(abi.encodePacked(SEED_WEAPON_MINT, amount, special_weapon_series)))\n *\n * !!! DO NOT USE SIMPLE CONSTANT VALUES FOR THE ACTION IDENTIFIER (1,2,3) !!!\n * USE ENCODED STRING CONSTANTS FOR EXAMPLE:\n * uint256 public constant SEED_WEAPON_MINT = uint(keccak256(\"SEED_WEAPON_MINT\"));\n */\n\n uint256 public currentSeedIndex; // new requests pile up under this index\n uint256 public seedIndexBlockNumber; // the block number \"currentSeedIndex\" was reached on\n uint256 public firstRequestBlockNumber; // first request block for the latest seed index\n mapping(uint256 => bytes32) public seedHashes; // key: seedIndex\n\n // keys: user, requestID / value: seedIndex\n mapping(address => mapping(uint256 => uint256)) public singleSeedRequests; // one-at-a-time (saltable)\n mapping(address => mapping(uint256 => uint256)) public singleSeedSalts; // optional, ONLY for single seeds\n mapping(address => mapping(uint256 => uint256[])) public queuedSeedRequests; // arbitrary in/out LIFO\n\n bool public publicResolutionLimited;\n uint256 public publicResolutionBlocks; // max number of blocks to resolve if limited\n\n bool public emitResolutionEvent;\n bool public emitRequestEvent;\n bool public emitPopEvent;\n\n event SeedResolved(address indexed resolver, uint256 indexed seedIndex);\n event SeedRequested(address indexed requester, uint256 indexed requestId);\n event SeedPopped(address indexed popper, uint256 indexed requestId);\n\n function initialize () public initializer {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n currentSeedIndex = 1; // one-at-a-time seeds have a 0 check\n seedIndexBlockNumber = block.number;\n firstRequestBlockNumber = block.number-1; // save 15k gas for very first user\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not admin\");\n }\n\n // SINGLE SEED REQUESTS\n\n function requestSingleSeed(address user, uint256 requestID) public restricted {\n _resolveSeedPublic(user);\n _requestSingleSeedAssert(user, requestID);\n }\n\n function requestSingleSeed(address user, uint256 requestID, bool force) public restricted {\n _resolveSeedPublic(user);\n if(force)\n _requestSingleSeed(user, requestID);\n else\n _requestSingleSeedAssert(user, requestID);\n }\n\n function _requestSingleSeedAssert(address user, uint256 requestID) internal {\n require(singleSeedRequests[user][requestID] == 0);\n _requestSingleSeed(user, requestID);\n }\n \n function _requestSingleSeed(address user, uint256 requestID) internal {\n singleSeedRequests[user][requestID] = currentSeedIndex;\n if(firstRequestBlockNumber < seedIndexBlockNumber)\n firstRequestBlockNumber = block.number;\n\n if(emitRequestEvent)\n emit SeedRequested(user, requestID);\n }\n\n // QUEUED SEED REQUESTS\n\n function requestQueuedSeed(address user, uint256 requestID) public restricted {\n _resolveSeedPublic(user);\n _requestQueuedSeed(user, requestID);\n }\n\n function _requestQueuedSeed(address user, uint256 requestID) internal {\n queuedSeedRequests[user][requestID].push(currentSeedIndex);\n if(firstRequestBlockNumber < seedIndexBlockNumber)\n firstRequestBlockNumber = block.number;\n\n if(emitRequestEvent)\n emit SeedRequested(user, requestID);\n }\n\n // SEED RESOLUTIONS\n\n function resolveSeedPublic() public {\n _resolveSeedPublic(msg.sender);\n }\n\n function _resolveSeedPublic(address resolver) internal {\n if(!publicResolutionLimited || block.number < firstRequestBlockNumber + publicResolutionBlocks)\n _resolveSeed(resolver);\n }\n\n function resolveSeedAdmin() public restricted {\n _resolveSeed(msg.sender);\n }\n\n function _resolveSeed(address resolver) internal {\n if(block.number > firstRequestBlockNumber && firstRequestBlockNumber >= seedIndexBlockNumber) {\n seedHashes[currentSeedIndex++] = blockhash(block.number - 1);\n seedIndexBlockNumber = block.number;\n if(emitResolutionEvent)\n emit SeedResolved(resolver, currentSeedIndex);\n }\n }\n\n // SINGLE SEED FULFILLMENT\n\n function popSingleSeed(address user, uint256 requestID, bool resolve, bool requestNext) public restricted returns (uint256 seed) {\n if(resolve)\n _resolveSeedPublic(user);\n\n seed = readSingleSeed(user, requestID, false); // reverts on zero\n delete singleSeedRequests[user][requestID];\n\n if(emitPopEvent)\n emit SeedPopped(user, requestID);\n\n if(requestNext)\n _requestSingleSeed(user, requestID);\n }\n\n function readSingleSeed(address user, uint256 requestID, bool allowZero) public view returns (uint256 seed) {\n if(seedHashes[singleSeedRequests[user][requestID]] == 0) {\n require(allowZero);\n // seed stays 0 by default if allowed\n }\n else {\n seed = uint256(keccak256(abi.encodePacked(\n seedHashes[singleSeedRequests[user][requestID]],\n user, requestID\n )));\n }\n }\n\n function saltSingleSeed(address user, uint256 requestID, bool resolve) public restricted returns (uint256 seed) {\n if(resolve)\n _resolveSeedPublic(user);\n\n require(seedHashes[singleSeedRequests[user][requestID]] != 0);\n seed = uint(keccak256(abi.encodePacked(\n seedHashes[singleSeedRequests[user][requestID]]\n ,singleSeedSalts[user][requestID]\n )));\n singleSeedSalts[user][requestID] = seed;\n return seed;\n }\n\n // QUEUED SEED FULFILLMENT\n\n function popQueuedSeed(address user, uint256 requestID, bool resolve, bool requestNext) public restricted returns (uint256 seed) {\n if(resolve)\n _resolveSeedPublic(user);\n\n // will revert on empty queue due to pop()\n seed = readQueuedSeed(user, requestID, false);\n queuedSeedRequests[user][requestID].pop();\n\n if(emitPopEvent)\n emit SeedPopped(user, requestID);\n\n if(requestNext)\n _requestQueuedSeed(user, requestID);\n\n return seed;\n }\n\n function readQueuedSeed(address user, uint256 requestID, bool allowZero) public view returns (uint256 seed) {\n uint256 lastIndex = queuedSeedRequests[user][requestID].length-1;\n seed = uint256(keccak256(abi.encodePacked(\n seedHashes[queuedSeedRequests[user][requestID][lastIndex]],\n user, requestID, lastIndex\n )));\n require(allowZero || seed != 0);\n }\n\n // HELPER VIEWS\n\n function hasSingleSeedRequest(address user, uint256 requestID) public view returns (bool) {\n return singleSeedRequests[user][requestID] != 0;\n }\n\n function getQueuedRequestCount(uint256 requestID) public view returns (uint256) {\n return queuedSeedRequests[msg.sender][requestID].length;\n }\n\n function encode(uint256[] calldata requestData) external pure returns (uint256) {\n return uint256(keccak256(abi.encodePacked(requestData)));\n }\n\n // ADMIN FUNCTIONS (excluding resolveSeedAdmin)\n\n function setPublicResolutionLimited(bool to) public restricted {\n publicResolutionLimited = to;\n }\n\n function setPublicResolutionBlocks(uint256 to) public restricted {\n publicResolutionBlocks = to;\n }\n\n function setEmitResolutionEvent(bool to) public restricted {\n emitResolutionEvent = to;\n }\n\n function setEmitRequestEvent(bool to) public restricted {\n emitRequestEvent = to;\n }\n\n function setEmitPopEvent(bool to) public restricted {\n emitPopEvent = to;\n }\n\n}\n"
},
"contracts/shields.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"./Promos.sol\";\nimport \"./util.sol\";\n\ncontract Shields is Initializable, ERC721Upgradeable, AccessControlUpgradeable {\n\n using ABDKMath64x64 for int128;\n using ABDKMath64x64 for uint16;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n function initialize () public initializer {\n __ERC721_init(\"CryptoBlades shield\", \"CBS\");\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n shieldBaseMultiplier = ABDKMath64x64.fromUInt(1);\n defenseMultPerPointBasic = ABDKMath64x64.divu(1, 400); // 0.25%\n defenseMultPerPointDEF = defenseMultPerPointBasic.mul(ABDKMath64x64.divu(103, 100)); // 0.2575% (+3%)\n defenseMultPerPointMatching = defenseMultPerPointBasic.mul(ABDKMath64x64.divu(107, 100)); // 0.2675% (+7%)\n }\n\n function migrateTo_surprise(Promos _promos) public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n promos = _promos;\n }\n\n /*\n visual numbers start at 0, increment values by 1\n stars: 1-5 (1,2,3: primary only, 4: one secondary, 5: two secondaries)\n traits: 0-3 [0(fire) > 1(earth) > 2(lightning) > 3(water) > repeat]\n stats: STR(fire), DEX(earth), CHA(lightning), INT(water), BLK(traitless)\n base stat rolls: 1*(1-50), 2*(45-75), 3*(70-100), 4*(50-100), 5*(66-100, main is 68-100)\n defense: each point contributes .25% to fight defense\n cosmetics: 0-255, to be used for future display purposes\n */\n\n struct Shield {\n uint16 properties; // right to left: 3b stars, 2b trait, 7b stat pattern, 4b EMPTY\n // stats (each point refers to .25% improvement)\n uint16 stat1;\n uint16 stat2;\n uint16 stat3;\n }\n\n struct ShieldCosmetics {\n uint8 version;\n uint256 seed;\n }\n\n Shield[] private tokens;\n ShieldCosmetics[] private cosmetics;\n\n int128 public shieldBaseMultiplier; // 1.0\n int128 public defenseMultPerPointBasic; // 0.25%\n int128 public defenseMultPerPointDEF; // 0.2575% (+3%)\n int128 public defenseMultPerPointMatching; // 0.2675% (+7%)\n\n uint256 private lastMintedBlock; // DEPRECATED\n uint256 private firstMintedOfLastBlock; // DEPRECATED\n\n mapping(uint256 => uint64) durabilityTimestamp;\n\n uint256 public constant maxDurability = 20;\n uint256 public constant secondsPerDurability = 3000; //50 * 60\n\n Promos public promos;\n\n mapping(uint256 => mapping(uint256 => uint256)) public nftVars;//KEYS: NFTID, VARID\n uint256 public constant NFTVAR_BUSY = 1; // value bitflags: 1 (pvp) | 2 (raid) | 4 (TBD)..\n uint256 public constant NFTVAR_SHIELD_TYPE = 2; // 0 = normal, 1 = founders, 2 = legendary defender\n\n event NewShield(uint256 indexed shield, address indexed minter);\n event Burned(uint256 indexed shield, address indexed burner);\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n }\n\n function getStats(uint256 id) internal view\n returns (uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3) {\n Shield memory s = tokens[id];\n return (s.properties, s.stat1, s.stat2, s.stat3);\n }\n\n function get(uint256 id) public view returns (uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3) {\n return _get(id);\n }\n\n function _get(uint256 id) internal view returns (uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3) {\n return getStats(id);\n }\n\n function getOwned() public view returns(uint256[] memory) {\n return getOwnedBy(msg.sender);\n }\n\n function getOwnedBy(address owner) public view returns(uint256[] memory) {\n uint256[] memory tokens = new uint256[](balanceOf(owner));\n for(uint256 i = 0; i < tokens.length; i++) {\n tokens[i] = tokenOfOwnerByIndex(owner, i);\n }\n return tokens;\n }\n\n function getCosmeticsSeed(uint256 id) public view returns (uint256) {\n ShieldCosmetics memory sc = cosmetics[id];\n return sc.seed;\n }\n\n function mint(address minter, uint256 shieldType, uint256 seed) public restricted returns(uint256) {\n uint256 stars;\n uint256 roll = seed % 100;\n // will need revision, possibly manual configuration if we support more than 5 stars\n if(roll < 1) {\n stars = 4; // 5* at 1%\n }\n else if(roll < 6) { // 4* at 5%\n stars = 3;\n }\n else if(roll < 21) { // 3* at 15%\n stars = 2;\n }\n else if(roll < 56) { // 2* at 35%\n stars = 1;\n }\n else {\n stars = 0; // 1* at 44%\n }\n\n return mintShieldWithStars(minter, stars, shieldType, seed);\n }\n\n function burn(uint256 tokenID) public restricted {\n address burner = ownerOf(tokenID);\n _burn(tokenID);\n emit Burned(tokenID, burner);\n }\n\n function burn(uint256[] memory tokenIDs) public restricted {\n for(uint i = 0; i < tokenIDs.length; i++) {\n burn(tokenIDs[i]);\n }\n }\n\n function mintShieldWithStars(address minter, uint256 stars, uint256 shieldType, uint256 seed) public restricted returns(uint256) {\n require(stars < 8, \"Stars parameter too high! (max 7)\");\n (uint16 stat1, uint16 stat2, uint16 stat3) = getStatRolls(stars, seed);\n\n return performMintShield(minter,\n shieldType,\n getRandomProperties(stars, seed),\n stat1,\n stat2,\n stat3,\n RandomUtil.combineSeeds(seed,3)\n );\n }\n\n function mintShieldsWithStars(address minter, uint256 stars, uint256 shieldType, uint32 amount, uint256 seed) public restricted returns(uint256[] memory tokenIDs) {\n require(stars < 8, \"Stars parameter too high! (max 7)\");\n tokenIDs = new uint256[](amount);\n for(uint i = 0; i < amount; i++) {\n tokenIDs[i] = mintShieldWithStars(minter, stars, shieldType, seed);\n seed = RandomUtil.combineSeeds(seed,i);\n }\n }\n\n function performMintShield(address minter,\n uint256 shieldType,\n uint16 properties,\n uint16 stat1, uint16 stat2, uint16 stat3,\n uint256 cosmeticSeed\n ) public restricted returns(uint256 tokenID) {\n tokenID = tokens.length;\n tokens.push(Shield(properties, stat1, stat2, stat3));\n cosmetics.push(ShieldCosmetics(0, cosmeticSeed));\n _mint(minter, tokenID);\n durabilityTimestamp[tokenID] = uint64(now.sub(getDurabilityMaxWait()));\n nftVars[tokenID][NFTVAR_SHIELD_TYPE] = shieldType;\n\n emit NewShield(tokenID, minter);\n }\n\n function performMintShieldDetailed(address minter,\n uint256 metaData,\n uint256 cosmeticSeed, uint256 tokenID\n ) public restricted returns(uint256) {\n\n // uint256(uint256(0)) | uint256(stat3) << 16| (uint256(stat2) << 32) | (uint256(stat1) << 48) | (uint256(properties) << 64) | (uint256(appliedCosmetic) << 80);\n\n uint16 stat3 = uint16((metaData >> 16) & 0xFFFF);\n uint16 stat2 = uint16((metaData >> 32) & 0xFFFF);\n uint16 stat1 = uint16((metaData >> 48) & 0xFFFF);\n uint16 properties = uint16((metaData >> 64) & 0xFFFF);\n //cosmetics >> 80\n uint8 shieldType = uint8(metaData & 0xFF);\n\n if(tokenID == 0){\n tokenID = performMintShield(minter, shieldType, properties, stat1, stat2, stat3, 0);\n }\n else {\n Shield storage sh = tokens[tokenID];\n sh.properties = properties;\n sh.stat1 = stat1;\n sh.stat2 = stat2;\n sh.stat3 = stat3;\n }\n ShieldCosmetics storage sc = cosmetics[tokenID];\n sc.seed = cosmeticSeed;\n\n durabilityTimestamp[tokenID] = uint64(now); // avoid chain jumping abuse\n\n return tokenID;\n }\n\n function mintGiveawayShield(address to, uint256 stars, uint256 shieldType) external restricted returns(uint256) {\n require(shieldType != 1, \"Can't mint founders shield\");\n // MANUAL USE ONLY; DO NOT USE IN CONTRACTS!\n return mintShieldWithStars(to, stars, shieldType, uint256(keccak256(abi.encodePacked(now, tokens.length))));\n }\n\n function getRandomProperties(uint256 stars, uint256 seed) public pure returns (uint16) {\n return uint16((stars & 0x7) // stars aren't randomized here!\n | ((RandomUtil.randomSeededMinMax(0,3,RandomUtil.combineSeeds(seed,1)) & 0x3) << 3) // trait\n | ((RandomUtil.randomSeededMinMax(0,124,RandomUtil.combineSeeds(seed,2)) & 0x7F) << 5)); // statPattern\n }\n\n function getStatRolls(uint256 stars, uint256 seed) private pure returns (uint16, uint16, uint16) {\n uint16 minRoll = getStatMinRoll(stars);\n uint16 maxRoll = getStatMaxRoll(stars);\n uint8 statCount = getStatCount(stars);\n\n uint16 stat1 = getRandomStat(minRoll, maxRoll, seed, 5);\n uint16 stat2 = 0;\n uint16 stat3 = 0;\n if(statCount > 1) {\n stat2 = getRandomStat(minRoll, maxRoll, seed, 3);\n }\n if(statCount > 2) {\n stat3 = getRandomStat(minRoll, maxRoll, seed, 4);\n }\n return (stat1, stat2, stat3);\n }\n\n function getRandomStat(uint16 minRoll, uint16 maxRoll, uint256 seed, uint256 seed2) public pure returns (uint16) {\n return uint16(RandomUtil.randomSeededMinMax(minRoll, maxRoll,RandomUtil.combineSeeds(seed, seed2)));\n }\n\n function getRandomCosmetic(uint256 seed, uint256 seed2, uint8 limit) public pure returns (uint8) {\n return uint8(RandomUtil.randomSeededMinMax(0, limit, RandomUtil.combineSeeds(seed, seed2)));\n }\n\n function getStatMinRoll(uint256 stars) public pure returns (uint16) {\n // 1 star\n if (stars == 0) return 4;\n // 2 star\n if (stars == 1) return 180;\n // 3 star\n if (stars == 2) return 280;\n // 4 star\n if (stars == 3) return 200;\n // 5+ star\n return 268;\n }\n\n function getStatMaxRoll(uint256 stars) public pure returns (uint16) {\n // 3+ star\n if (stars > 1) return 400;\n // 2 star\n if (stars > 0) return 300;\n // 1 star\n return 200;\n }\n\n function getStatCount(uint256 stars) public pure returns (uint8) {\n // 1-2 star\n if (stars < 3) return 1;\n // 3+ star\n return uint8(stars)-1;\n }\n\n function getProperties(uint256 id) public view returns (uint16) {\n return tokens[id].properties;\n }\n\n function getStars(uint256 id) public view returns (uint8) {\n return getStarsFromProperties(getProperties(id));\n }\n\n function getStars(uint256[] memory ids) public view returns (uint8[] memory) {\n uint8[] memory stars = new uint8[](ids.length);\n for(uint256 i = 0; i < ids.length; i++) {\n stars[i] = getStars(ids[i]);\n }\n return stars;\n }\n\n function getStarsFromProperties(uint16 properties) public pure returns (uint8) {\n return uint8(properties & 0x7); // first two bits for stars\n }\n\n function getTrait(uint256 id) public view returns (uint8) {\n return getTraitFromProperties(getProperties(id));\n }\n\n function getTraitFromProperties(uint16 properties) public pure returns (uint8) {\n return uint8((properties >> 3) & 0x3); // two bits after star bits (3)\n }\n\n function getStatPattern(uint256 id) public view returns (uint8) {\n return getStatPatternFromProperties(getProperties(id));\n }\n\n function getStatPatternFromProperties(uint16 properties) public pure returns (uint8) {\n return uint8((properties >> 5) & 0x7F); // 7 bits after star(3) and trait(2) bits\n }\n\n function getStat1Trait(uint8 statPattern) public pure returns (uint8) {\n return uint8(uint256(statPattern) % 5); // 0-3 regular traits, 4 = traitless (DEF)\n }\n\n function getStat2Trait(uint8 statPattern) public pure returns (uint8) {\n return uint8(SafeMath.div(statPattern, 5) % 5); // 0-3 regular traits, 4 = traitless (DEF)\n }\n\n function getStat3Trait(uint8 statPattern) public pure returns (uint8) {\n return uint8(SafeMath.div(statPattern, 25) % 5); // 0-3 regular traits, 4 = traitless (DEF)\n }\n\n function getStat1(uint256 id) public view returns (uint16) {\n return tokens[id].stat1;\n }\n\n function getStat2(uint256 id) public view returns (uint16) {\n return tokens[id].stat2;\n }\n\n function getStat3(uint256 id) public view returns (uint16) {\n return tokens[id].stat3;\n }\n\n function getDefenseMultiplier(uint256 id) public view returns (int128) {\n // returns a 64.64 fixed point number for defense multiplier\n // this function does not account for traits\n // it is used to calculate base enemy defenses for targeting\n Shield memory shd = tokens[id];\n int128 defensePerPoint = defenseMultPerPointBasic;\n int128 stat1 = shd.stat1.fromUInt().mul(defensePerPoint);\n int128 stat2 = shd.stat2.fromUInt().mul(defensePerPoint);\n int128 stat3 = shd.stat3.fromUInt().mul(defensePerPoint);\n return shieldBaseMultiplier.add(stat1).add(stat2).add(stat3);\n }\n\n function getDefenseMultiplierForTrait(\n uint16 properties,\n uint16 stat1,\n uint16 stat2,\n uint16 stat3,\n uint8 trait\n ) public view returns(int128) {\n // Does not include character trait to shield trait match\n // Only counts arbitrary trait to shield stat trait\n // This function can be used by frontend to get expected % bonus for each type\n // Making it easy to see on the market how useful it will be to you\n uint8 statPattern = getStatPatternFromProperties(properties);\n int128 result = shieldBaseMultiplier;\n\n if(getStat1Trait(statPattern) == trait)\n result = result.add(stat1.fromUInt().mul(defenseMultPerPointMatching));\n else if(getStat1Trait(statPattern) == 4) // DEF, traitless\n result = result.add(stat1.fromUInt().mul(defenseMultPerPointDEF));\n else\n result = result.add(stat1.fromUInt().mul(defenseMultPerPointBasic));\n\n if(getStat2Trait(statPattern) == trait)\n result = result.add(stat2.fromUInt().mul(defenseMultPerPointMatching));\n else if(getStat2Trait(statPattern) == 4) // DEF, traitless\n result = result.add(stat2.fromUInt().mul(defenseMultPerPointDEF));\n else\n result = result.add(stat2.fromUInt().mul(defenseMultPerPointBasic));\n\n if(getStat3Trait(statPattern) == trait)\n result = result.add(stat3.fromUInt().mul(defenseMultPerPointMatching));\n else if(getStat3Trait(statPattern) == 4) // DEF, traitless\n result = result.add(stat3.fromUInt().mul(defenseMultPerPointDEF));\n else\n result = result.add(stat3.fromUInt().mul(defenseMultPerPointBasic));\n\n return result;\n }\n\n function getDefenseMultiplierForTrait(uint256 id, uint8 trait) public view returns(int128) {\n Shield storage shd = tokens[id];\n return getDefenseMultiplierForTrait(shd.properties, shd.stat1, shd.stat2, shd.stat3, trait);\n }\n\n function getFightData(uint256 id, uint8 charTrait) public view returns (int128, int128, uint24, uint8) {\n Shield storage shd = tokens[id];\n return (\n shieldBaseMultiplier.add(defenseMultPerPointBasic.mul(\n ABDKMath64x64.fromUInt(\n shd.stat1 + shd.stat2 + shd.stat3\n )\n )),//targetMult\n getDefenseMultiplierForTrait(shd.properties, shd.stat1, shd.stat2, shd.stat3, charTrait),\n // Bonus defense support intended in future.\n 0,\n getTraitFromProperties(shd.properties)\n );\n }\n\n function getFightDataAndDrainDurability(uint256 id, uint8 charTrait, uint8 drainAmount) public restricted returns (int128, int128, uint24, uint8) {\n require(nftVars[id][NFTVAR_BUSY] == 0, \"Shield is busy\");\n uint8 durabilityPoints = getDurabilityPointsFromTimestamp(durabilityTimestamp[id]);\n require(durabilityPoints >= drainAmount, \"Not enough durability!\");\n\n uint64 drainTime = uint64(drainAmount * secondsPerDurability);\n if(durabilityPoints >= maxDurability) { // if durability full, we reset timestamp and drain from that\n durabilityTimestamp[id] = uint64(now - getDurabilityMaxWait() + drainTime);\n }\n else {\n durabilityTimestamp[id] = uint64(durabilityTimestamp[id] + drainTime);\n }\n\n Shield storage shd = tokens[id];\n return (\n shieldBaseMultiplier.add(defenseMultPerPointBasic.mul(\n ABDKMath64x64.fromUInt(\n shd.stat1 + shd.stat2 + shd.stat3\n )\n )),//targetMult\n getDefenseMultiplierForTrait(shd.properties, shd.stat1, shd.stat2, shd.stat3, charTrait),\n // Bonus defense support intended in future.\n 0,\n getTraitFromProperties(shd.properties)\n );\n }\n\n function drainDurability(uint256 id, uint8 amount) public restricted {\n uint8 durabilityPoints = getDurabilityPointsFromTimestamp(durabilityTimestamp[id]);\n require(durabilityPoints >= amount, \"Not enough durability!\");\n\n uint64 drainTime = uint64(amount * secondsPerDurability);\n if(durabilityPoints >= maxDurability) { // if durability full, we reset timestamp and drain from that\n durabilityTimestamp[id] = uint64(now - getDurabilityMaxWait() + drainTime);\n }\n else {\n durabilityTimestamp[id] = uint64(durabilityTimestamp[id] + drainTime);\n }\n }\n\n function getDurabilityTimestamp(uint256 id) public view returns (uint64) {\n return durabilityTimestamp[id];\n }\n\n function setDurabilityTimestamp(uint256 id, uint64 timestamp) public restricted {\n durabilityTimestamp[id] = timestamp;\n }\n\n function getDurabilityPoints(uint256 id) public view returns (uint8) {\n return getDurabilityPointsFromTimestamp(durabilityTimestamp[id]);\n }\n\n function getDurabilityPointsFromTimestamp(uint64 timestamp) public view returns (uint8) {\n if(timestamp > now)\n return 0;\n\n uint256 points = (now - timestamp) / secondsPerDurability;\n if(points > maxDurability) {\n points = maxDurability;\n }\n return uint8(points);\n }\n\n function isDurabilityFull(uint256 id) public view returns (bool) {\n return getDurabilityPoints(id) >= maxDurability;\n }\n\n function getDurabilityMaxWait() public pure returns (uint64) {\n return uint64(maxDurability * secondsPerDurability);\n }\n\n function getNftVar(uint256 shieldID, uint256 nftVar) public view returns(uint256) {\n return nftVars[shieldID][nftVar];\n }\n function setNftVar(uint256 shieldID, uint256 nftVar, uint256 value) public restricted {\n nftVars[shieldID][nftVar] = value;\n }\n function setNftVars(uint256[] calldata ids, uint256 nftVar, uint256 value) external restricted {\n for(uint i = 0; i < ids.length; i++)\n nftVars[ids[i]][nftVar] = value;\n }\n\n function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override {\n require(promos.getBit(from, 4) == false && promos.getBit(to, 4) == false\n && nftVars[tokenId][NFTVAR_BUSY] == 0);\n }\n\n function setBaseURI(string memory baseUri) public restricted {\n _setBaseURI(baseUri);\n }\n}\n"
},
"contracts/Consumables.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\n\n\ncontract Consumables is Initializable, AccessControlUpgradeable {\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n event ConsumableGiven(address indexed owner, uint32 amount);\n\n mapping(address => uint32) public owned;\n \n bool internal _enabled;\n\n function initialize()\n public\n initializer\n {\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n _enabled = true;\n }\n\n modifier isAdmin() {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n _;\n }\n\n modifier itemNotDisabled() {\n require(_enabled, \"Item disabled\");\n _;\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n }\n\n modifier haveItem(uint32 amount) {\n require(owned[msg.sender] >= amount, \"No item\");\n _;\n }\n\n function giveItem(address buyer, uint32 amount) public restricted {\n owned[buyer] += amount;\n emit ConsumableGiven(buyer, amount);\n }\n\n function consumeItem(uint32 amount) internal haveItem(amount) itemNotDisabled {\n owned[msg.sender] -= amount;\n }\n\n function getItemCount() public view returns (uint32) {\n return owned[msg.sender];\n }\n\n function toggleItemCanUse(bool canUse) external isAdmin {\n _enabled = canUse;\n }\n\n function giveItemByAdmin(address receiver, uint32 amount) external isAdmin {\n owned[receiver] += amount;\n }\n\n function takeItemByAdmin(address target, uint32 amount) external isAdmin {\n require(owned[target] >= amount, 'Not enough item');\n owned[target] -= amount;\n }\n\n function itemEnabled() public view returns (bool){\n return _enabled;\n }\n}"
},
"contracts/Cosmetics.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\n\n\ncontract Cosmetics is Initializable, AccessControlUpgradeable {\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n event CosmeticGiven(address indexed owner, uint32 cosmetic, uint32 amount);\n event CosmeticUsed(address indexed owner, uint32 cosmetic, uint32 amount);\n event CosmeticRestored(address indexed owner, uint32 cosmetic, uint32 amount);\n \n event CosmeticGivenByAdmin(address indexed owner, uint32 cosmetic, uint32 amount);\n event CosmeticTakenByAdmin(address indexed owner, uint32 cosmetic, uint32 amount);\n\n mapping(address => mapping(uint32 => uint32)) public owned;\n\n mapping(uint32 => bool) internal _cosmeticAvailable;\n\n uint32 internal constant _noCosmetic = 0;\n\n function initialize()\n public\n initializer\n {\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n }\n\n modifier isAdmin() {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n _;\n }\n\n modifier cosmeticAvailable(uint32 cosmetic) {\n require(_cosmeticAvailable[cosmetic], \"Not available\");\n _;\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n }\n\n modifier haveCosmetic(uint32 cosmetic, uint32 amount) {\n require(owned[msg.sender][cosmetic] >= amount, \"No cosmetic\");\n _;\n }\n\n function giveCosmetic(address buyer, uint32 cosmetic, uint32 amount) public restricted {\n owned[buyer][cosmetic] += amount;\n emit CosmeticGiven(buyer, cosmetic, amount);\n }\n\n function useCosmetic(uint32 cosmetic, uint32 amount) internal haveCosmetic(cosmetic, amount) cosmeticAvailable(cosmetic) {\n owned[msg.sender][cosmetic] -= amount;\n emit CosmeticUsed(msg.sender, cosmetic, amount);\n }\n\n function _restoreCosmetic(uint32 cosmetic, uint32 amount) internal {\n owned[msg.sender][cosmetic] += amount;\n emit CosmeticRestored(msg.sender, cosmetic, amount);\n }\n\n function getCosmeticCount(uint32 cosmetic) public view returns(uint32) {\n return owned[msg.sender][cosmetic];\n }\n\n function isCosmeticAvailable(uint32 cosmetic) public view returns (bool){\n return _cosmeticAvailable[cosmetic];\n }\n\n function toggleCosmeticAvailable(uint32 cosmetic, bool available) external isAdmin {\n _cosmeticAvailable[cosmetic] = available;\n }\n\n function giveCosmeticByAdmin(address receiver, uint32 cosmetic, uint32 amount) external isAdmin cosmeticAvailable(cosmetic) {\n owned[receiver][cosmetic] += amount;\n emit CosmeticGivenByAdmin(receiver, cosmetic, amount);\n }\n\n function takeCosmeticByAdmin(address target, uint32 cosmetic, uint32 amount) external isAdmin {\n require(owned[target][cosmetic] >= amount, 'Not enough cosmetic');\n owned[target][cosmetic] -= amount;\n emit CosmeticTakenByAdmin(target, cosmetic, amount);\n }\n}"
},
"contracts/CBKLandSale.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/utils/EnumerableSet.sol\";\nimport \"./CBKLand.sol\";\n\ncontract CBKLandSale is Initializable, AccessControlUpgradeable {\n\n using EnumerableSet for EnumerableSet.UintSet;\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n CBKLand public cbkLand;\n /* ========== EVENTS ========== */\n event T1Given(address indexed owner, uint256 stamp);\n event T2Given(address indexed owner, uint256 chunkId);\n event T3Given(address indexed owner, uint256 chunkId);\n event T1GivenFree(address indexed owner, uint256 stamp);\n event T2GivenFree(address indexed owner, uint256 chunkId);\n event T3GivenFree(address indexed owner, uint256 chunkId);\n event T1GivenReserved(address indexed reseller, address indexed owner, uint256 chunkId);\n event T2GivenReserved(address indexed reseller, address indexed owner, uint256 chunkId);\n event T3GivenReserved(address indexed reseller, address indexed owner, uint256 chunkId);\n event LandTokenGiven(address indexed reseller, address indexed owner, uint256 tier);\n\n event ReservedLandClaimed(uint256 indexed reservation, address indexed reseller, address indexed owner, uint256 tier, uint256 chunkId);\n event MassMintReservedLand(address indexed player, address indexed reseller, uint256 chunkId, uint256 tier1, uint256 tier2, bool giveTier3);\n event MassMintReservedChunklessLand(address indexed player, address indexed reseller, uint256 tier1, uint256 tier2, uint256 tier3);\n event ReservedLandClaimedForPlayer(uint256 indexed reservation, address indexed reseller, address indexed owner, uint256 tier, uint256 chunkId);\n \n /* ========== LAND SALE INFO ========== */\n uint256 private constant NO_LAND = 0;\n uint256 public constant TIER_ONE = 1;\n uint256 public constant TIER_TWO = 2;\n uint256 public constant TIER_THREE = 3;\n uint256 private constant MAX_CHUNK_ID = 9999; // 100 x 100\n\n struct purchaseInfo {\n address buyer;\n uint256 purchasedTier;\n uint256 stamp; // chunkId or roundrobin stamp\n bool free;\n }\n\n uint256 private totalSales;\n mapping(uint256 => purchaseInfo) public sales; // Put all sales in an mapping for easier tracking\n mapping(address => purchaseInfo) public purchaseAddressMapping;\n mapping(uint256 => uint256) public availableLand; // Land that is up for sale. \n mapping(uint256 => uint256) public chunkZoneLandSales;\n\n /* ========== T1 LAND SALE INFO ========== */\n // T1 land is sold with no exact coordinates commitment and assigned based on round robin\n // once minting is done. For now the player gets a stamp which can reflect PROJECTED land coordinates\n // should it need be.\n uint256 private t1LandsSold;\n\n\n\n /* ========== T2 LAND SALE INFO ========== */\n uint256 private t2LandsSold;\n uint256 private chunksWithT2Land;\n\n uint256 private _allowedLandSalePerChunk;\n uint256 private _allowedLandOffset; // Max allowed deviation allowed from theoretical average\n\n // T2 sold per chunk\n mapping(uint256 => uint256) public chunkT2LandSales;\n\n\n /* ========== T3 LAND SALE INFO ========== */\n uint256 private t3LandsSold;\n mapping(uint256 => address) public chunkT3LandSoldTo;\n\n\n /* ========== RESERVED CHUNKS SALE INFO ========== */\n EnumerableSet.UintSet private reservedChunkIds;\n\n bool internal _enabled;\n bool internal _reservedEnabled;\n \n mapping(address => EnumerableSet.UintSet) private reservedChunks;\n mapping(address => uint256) private reservedChunksCounter;\n mapping(uint256 => address) private chunksReservedFor;\n\n // reseller address => land tier => budget\n mapping(address => mapping(uint256 => uint256)) private resellerLandBudget;\n\n // player reserved land\n uint256 private playerReservedLandAt;\n mapping(address => EnumerableSet.UintSet) private playerReservedLands;\n mapping(uint256 => uint256) private playerReservedLandTier;\n mapping(uint256 => address) private playerReservedLandReseller;\n mapping(uint256 => address) private playerReservedLandForPlayer;\n mapping(uint256 => bool) private playerReservedLandClaimed;\n \n EnumerableSet.UintSet private takenT3Chunks;\n\n function initialize(CBKLand _cbkLand)\n public\n initializer\n {\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n _enabled = false;\n\n _allowedLandOffset = 2;\n _allowedLandSalePerChunk = 99; // At least 1 reserved for T3\n availableLand[TIER_ONE] = 1000; // Placeholder value\n availableLand[TIER_TWO] = 100; // Placeholder value\n availableLand[TIER_THREE] = 10; // Placeholder value\n\n cbkLand = _cbkLand;\n }\n\n modifier isAdmin() {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n _;\n }\n\n modifier saleAllowed() {\n require(_enabled, \"Sales disabled\");\n _;\n }\n\n modifier reservedSaleAllowed() {\n require(_reservedEnabled, \"Sales disabled\");\n _;\n }\n\n modifier canPurchase(address buyer) {\n require(purchaseAddressMapping[buyer].purchasedTier == 0, \"Already purchased\");\n _;\n }\n\n modifier chunkAvailable(uint256 chunkId) {\n require(chunkId <= MAX_CHUNK_ID, \"Chunk not valid\");\n require(!reservedChunkIds.contains(chunkId), \"Chunk reserved\");\n require(chunkT2LandSales[chunkId] < _allowedLandSalePerChunk, \"Chunk not available\");\n require(_chunkAvailableForT2(chunkId), \"Chunk overpopulated\");\n _;\n }\n\n // modifier reservedChunkAvailable(uint256 chunkId) {\n // require(chunkId <= MAX_CHUNK_ID, \"Chunk not valid\");\n // require(reservedChunks[msg.sender].contains(chunkId), \"Chunk not reserved\");\n // require(chunkT2LandSales[chunkId] < _allowedLandSalePerChunk, \"Chunk not available\");\n // _;\n // }\n\n // Will not overcomplicate the math on this one. Keeping it simple on purpose for gas cost.\n // Limited to t2 because T3 not many and T1 round robins\n function _chunkAvailableForT2(uint256 chunkId) internal view returns (bool) {\n return chunksWithT2Land == 0 ||\n (chunkT2LandSales[chunkId] + 1 < _allowedLandOffset + t2LandsSold / chunksWithT2Land);\n }\n\n modifier t3Available(uint256 chunkId) {\n require(_chunkAvailableForT3(chunkId), \"T3 not available\");\n _;\n }\n\n function _chunkAvailableForT3(uint256 chunkId) internal view returns (bool) {\n return chunkT3LandSoldTo[chunkId] == address(0);\n }\n\n modifier tierAvailable(uint256 tier) {\n require(availableLand[tier] > 0, \"Tier not available\");\n _;\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n }\n\n function giveT1Land(address buyer) public saleAllowed canPurchase(buyer) tierAvailable(TIER_ONE) restricted {\n purchaseAddressMapping[buyer] = purchaseInfo(buyer, TIER_ONE, t1LandsSold, false);\n sales[totalSales++] = purchaseAddressMapping[buyer];\n emit T1Given(buyer, t1LandsSold);\n t1LandsSold++;\n availableLand[TIER_ONE]--;\n cbkLand.mint(buyer, TIER_ONE, 0);\n }\n\n function giveT2Land(address buyer, uint256 chunkId) public saleAllowed canPurchase(buyer) tierAvailable(TIER_TWO) chunkAvailable(chunkId) restricted {\n // First t2 sale\n if(chunkT2LandSales[chunkId] == 0){\n chunksWithT2Land++;\n }\n\n t2LandsSold++;\n chunkT2LandSales[chunkId]++;\n chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;\n\n purchaseAddressMapping[buyer] = purchaseInfo(buyer, TIER_TWO, chunkId, false);\n sales[totalSales++] = purchaseAddressMapping[buyer];\n availableLand[TIER_TWO]--;\n\n emit T2Given(buyer, chunkId);\n cbkLand.mint(buyer, TIER_TWO, chunkId);\n }\n\n function giveT3Land(address buyer, uint256 chunkId) public saleAllowed canPurchase(buyer) tierAvailable(TIER_THREE) chunkAvailable(chunkId) t3Available(chunkId) restricted {\n t3LandsSold++;\n \n purchaseAddressMapping[buyer] = purchaseInfo(buyer, TIER_THREE, chunkId, false);\n sales[totalSales++] = purchaseAddressMapping[buyer];\n availableLand[TIER_THREE]--;\n chunkT3LandSoldTo[chunkId] = buyer;\n chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;\n\n takenT3Chunks.add(chunkId);\n emit T3Given(buyer, chunkId);\n cbkLand.mint(buyer, TIER_THREE, chunkId);\n }\n\n function giveT1LandFree(address buyer) public tierAvailable(TIER_ONE) restricted {\n purchaseAddressMapping[buyer] = purchaseInfo(buyer, TIER_ONE, t1LandsSold, true);\n sales[totalSales++] = purchaseAddressMapping[buyer];\n emit T1GivenFree(buyer, t1LandsSold);\n t1LandsSold++;\n availableLand[TIER_ONE]--;\n cbkLand.mint(buyer, TIER_ONE, 0);\n }\n\n function giveT2LandFree(address buyer, uint256 chunkId) public tierAvailable(TIER_TWO) chunkAvailable(chunkId) restricted {\n // First t2 sale\n if(chunkT2LandSales[chunkId] == 0){\n chunksWithT2Land++;\n }\n\n t2LandsSold++;\n chunkT2LandSales[chunkId]++;\n chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;\n\n purchaseAddressMapping[buyer] = purchaseInfo(buyer, TIER_TWO, chunkId, true);\n sales[totalSales++] = purchaseAddressMapping[buyer];\n availableLand[TIER_TWO]--;\n\n emit T2GivenFree(buyer, chunkId);\n cbkLand.mint(buyer, TIER_TWO, chunkId);\n }\n\n function giveT3LandFree(address buyer, uint256 chunkId) public tierAvailable(TIER_THREE) chunkAvailable(chunkId) t3Available(chunkId) restricted {\n t3LandsSold++;\n \n purchaseAddressMapping[buyer] = purchaseInfo(buyer, TIER_THREE, chunkId, true);\n sales[totalSales++] = purchaseAddressMapping[buyer];\n availableLand[TIER_THREE]--;\n chunkT3LandSoldTo[chunkId] = buyer;\n chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;\n takenT3Chunks.add(chunkId);\n emit T3GivenFree(buyer, chunkId);\n cbkLand.mint(buyer, TIER_THREE, chunkId);\n }\n\n // function giveLandToken(address buyer, uint256 tier) public reservedSaleAllowed() {\n // require(resellerLandBudget[msg.sender][tier] > 0); // will protect against invalid tiers\n // resellerLandBudget[msg.sender][tier]--;\n // cbkLand.mintLandToken(msg.sender, buyer, tier);\n // emit LandTokenGiven(msg.sender, buyer, tier);\n // }\n\n // function getResellerBudget(address reseller) public view returns (uint256 t1, uint256 t2, uint256 t3) {\n // t1 = resellerLandBudget[reseller][TIER_ONE];\n // t2 = resellerLandBudget[reseller][TIER_TWO];\n // t3 = resellerLandBudget[reseller][TIER_THREE];\n // }\n\n // function setResellerBudget(address reseller, uint256 t1, uint256 t2, uint256 t3) public restricted {\n // resellerLandBudget[reseller][TIER_ONE] = t1;\n // resellerLandBudget[reseller][TIER_TWO] = t2;\n // resellerLandBudget[reseller][TIER_THREE] = t3;\n // }\n\n // Solidity hates polymorphism for this particular function\n function giveT1LandReservedBulk(address[] memory players, address reseller) public restricted {\n for (uint256 i = 0; i < players.length; i++) {\n giveT1LandReserved(players[i], reseller);\n }\n }\n\n function giveT1LandReserved(address player, address reseller) public restricted {\n uint256 rcLength = reservedChunks[reseller].length();\n require(rcLength > 0, \"no reserved chunks\");\n uint256 counter = reservedChunksCounter[reseller];\n for(uint256 i = counter; i < counter + rcLength; i++){\n uint256 cId = reservedChunks[reseller].at(uint256(i % rcLength));\n // it's actually a T1, but we will play on the T2 because population is shared\n if(chunkT2LandSales[cId] < _allowedLandSalePerChunk) {\n if(chunkT2LandSales[cId] == 0){\n chunksWithT2Land++;\n }\n chunkT2LandSales[cId]++;\n chunkZoneLandSales[chunkIdToZoneId(cId)]++;\n cbkLand.mint(player, TIER_ONE, cId, reseller);\n emit T1GivenReserved(reseller, player, cId);\n reservedChunksCounter[reseller] = uint256(i + 1);\n return;\n }\n }\n\n // Could not find a land\n revert();\n }\n\n // For mass mint\n function massMintReservedLand(address player, address reseller, uint256 chunkId, uint256 tier1, uint256 tier2, bool giveTier3) public restricted {\n require(reservedChunks[reseller].contains(chunkId), \"not reserved\");\n require(chunkT2LandSales[chunkId] + tier1 + tier2 <= _allowedLandSalePerChunk, \"NA\");\n require(!giveTier3 || _chunkAvailableForT3(chunkId), \"NA2\");\n \n if((tier1 + tier2) > 0 && chunkT2LandSales[chunkId] == 0) {\n chunksWithT2Land++;\n }\n \n if(tier1 > 0) {\n cbkLand.massMint(player, TIER_ONE, chunkId, reseller, tier1);\n chunkT2LandSales[chunkId] += tier1;\n }\n\n if(tier2 > 0) {\n cbkLand.massMint(player, TIER_TWO, chunkId, reseller, tier2);\n chunkT2LandSales[chunkId] += tier2;\n }\n\n if(giveTier3) {\n chunkT3LandSoldTo[chunkId] = player;\n takenT3Chunks.add(chunkId);\n cbkLand.mint(player, TIER_THREE, chunkId, reseller);\n }\n\n chunkZoneLandSales[chunkIdToZoneId(chunkId)] += tier1 + tier2 + (giveTier3 ? 1 : 0);\n emit MassMintReservedLand(player, reseller, chunkId, tier1, tier2, giveTier3);\n }\n\n // For mass mint \n // Can change chunk id once\n // Call with caution\n function massMintReservedLand(address player, address reseller, uint256 tier1, uint256 tier2, uint256 tier3) public restricted {\n require(reservedChunks[reseller].length() > 0, \"no reservation\");\n \n if(tier1 > 0) {\n cbkLand.massMint(player, TIER_ONE, 0, reseller, tier1);\n }\n\n if(tier2 > 0) {\n cbkLand.massMint(player, TIER_TWO, 0, reseller, tier2);\n }\n\n if(tier3 > 0) {\n cbkLand.massMint(player, TIER_THREE, 0, reseller, tier3);\n }\n\n emit MassMintReservedChunklessLand(player, reseller, tier1, tier2, tier3);\n }\n\n // Can be called only from land with reseller address and chunkid 0\n // For T1; assignedChunkId is a dummy param\n function changeLandChunkId(uint256 landId, uint256 assignedChunkid) public {\n require(cbkLand.ownerOf(landId) == msg.sender, \"NA1\"); // Owns the land\n (uint256 tier, uint256 chunkId, , , address reseller) = cbkLand.get(landId);\n require(chunkId == 0 && reseller != address(0), \"NA2\"); // Is a reseller land with chunk id 0\n require(assignedChunkid == 0 || reservedChunks[reseller].contains(assignedChunkid), \"NA3\"); // FE didn't send chunkId or reseller owns the assigned chunkId\n require(tier != TIER_THREE || _chunkAvailableForT3(assignedChunkid), \"NA4\"); // Not tier 3 or tier 3 available\n require((tier == TIER_ONE && assignedChunkid == 0) || assignedChunkid > 0, \"NA5\"); // tier 1 or chunkid requested\n require(tier != TIER_TWO || chunkT2LandSales[assignedChunkid] < _allowedLandSalePerChunk, \"NA6\"); // Not T2 or population allows it\n\n // T1 => get random reseller chunkId\n if(tier == TIER_ONE) {\n uint256 rcLength = reservedChunks[reseller].length();\n require(rcLength > 0, \"no reserved chunks\");\n uint256 counter = reservedChunksCounter[reseller];\n for(uint256 i = counter; i < counter + rcLength; i++) {\n uint256 cId = reservedChunks[reseller].at(uint256(i % rcLength));\n if(chunkT2LandSales[cId] < _allowedLandSalePerChunk) {\n assignedChunkid = cId;\n reservedChunksCounter[reseller] = uint256(i + 1);\n break;\n }\n }\n }\n\n require(assignedChunkid != 0, \"NA7\"); // Would only happen if T1 && round robin failed; shouldn't\n\n // T1 and T2 share population\n if(tier != TIER_THREE) {\n if(chunkT2LandSales[assignedChunkid] == 0){\n chunksWithT2Land++;\n }\n\n chunkT2LandSales[assignedChunkid]++;\n }\n\n // T3 => tag the land\n if(tier == TIER_THREE) {\n chunkT3LandSoldTo[assignedChunkid] = msg.sender;\n takenT3Chunks.add(assignedChunkid);\n }\n\n chunkZoneLandSales[chunkIdToZoneId(assignedChunkid)]++;\n cbkLand.updateChunkId(landId, assignedChunkid);\n }\n\n function giveT1LandReservedBulk(address[] memory players, address reseller, uint256 chunkId) public restricted {\n require(reservedChunks[reseller].contains(chunkId), \"not reserved\");\n for (uint256 i = 0; i < players.length; i++) {\n giveT1LandReserved(players[i], reseller, chunkId);\n }\n }\n\n function giveT1LandReserved(address player, address reseller, uint256 chunkId) public restricted {\n require(reservedChunks[reseller].contains(chunkId), \"not reserved\");\n if(chunkT2LandSales[chunkId] < _allowedLandSalePerChunk) {\n if(chunkT2LandSales[chunkId] == 0){\n chunksWithT2Land++;\n }\n chunkT2LandSales[chunkId]++;\n chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;\n cbkLand.mint(player, TIER_ONE, chunkId, reseller);\n emit T1GivenReserved(reseller, player, chunkId);\n return;\n }\n // Could not find a land\n revert();\n }\n\n // function giveT2LandReserved(address buyer, uint256 chunkId) public reservedChunkAvailable(chunkId) reservedSaleAllowed() {\n // if(chunkT2LandSales[chunkId] == 0){\n // chunksWithT2Land++;\n // }\n\n // chunkT2LandSales[chunkId]++;\n // chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;\n\n // emit T2GivenReserved(msg.sender, buyer, chunkId);\n // cbkLand.mint(buyer, TIER_TWO, chunkId);\n // }\n\n // function giveT3LandReserved(address buyer, uint256 chunkId) public reservedChunkAvailable(chunkId) t3Available(chunkId) reservedSaleAllowed() {\n // chunkT3LandSoldTo[chunkId] = buyer;\n // chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;\n\n // emit T3GivenReserved(msg.sender, buyer, chunkId);\n // cbkLand.mint(buyer, TIER_THREE, chunkId);\n // }\n\n // Will leave this commented\n // function setLandURI(uint256 landId, string memory uri) public {\n // (, uint256 chunkId,,) = cbkLand.get(landId);\n // require(reservedChunks[msg.sender].contains(chunkId), \"no access\");\n // cbkLand.setURI(landId, uri);\n // }\n\n function chunkIdToZoneId(uint256 chunkId) internal pure returns (uint256){\n return 10 * (chunkId / 1000) + (chunkId % 100) / 10;\n }\n\n function salesAllowed() public view returns (bool){\n return _enabled;\n }\n\n function reservedSalesAllowed() public view returns (bool){\n return _reservedEnabled;\n }\n\n function getAllowedLandOffset() public view returns (uint256){\n return _allowedLandOffset;\n }\n\n function checkIfChunkAvailable(uint256 tier, uint256 chunkId) public view returns (bool){\n if(reservedChunkIds.contains(chunkId)){\n return false;\n }\n\n if(chunkId > MAX_CHUNK_ID){\n return false;\n }\n\n if(tier == TIER_ONE){\n return availableLand[TIER_ONE] > 0;\n }\n\n if(tier == TIER_TWO){\n return availableLand[TIER_TWO] > 0\n && chunkT2LandSales[TIER_TWO] < _allowedLandSalePerChunk\n && _chunkAvailableForT2(chunkId);\n }\n\n if(tier == TIER_THREE){\n return availableLand[TIER_THREE] > 0\n && _chunkAvailableForT3(chunkId);\n }\n\n return false;\n }\n\n function checkChunkReserved(uint256 chunkId) public view returns (bool){\n return reservedChunkIds.contains(chunkId);\n }\n\n function getAllZonesPopulation() public view returns (uint256[] memory) {\n uint256[] memory toReturn = new uint256[](100);\n\n for (uint256 i = 0; i < 100; i++) {\n toReturn[i] = chunkZoneLandSales[i];\n }\n\n return toReturn;\n }\n\n function getZonePopulation(uint256[] memory zoneIds) public view returns (uint256[] memory) {\n require(zoneIds.length > 0 && zoneIds.length <= 100, \"invalid request\");\n uint256[] memory toReturn = new uint256[](zoneIds.length);\n\n for (uint256 i = 0; i < zoneIds.length; i++) {\n toReturn[i] = chunkZoneLandSales[zoneIds[i]];\n }\n\n return toReturn;\n }\n\n function getZoneChunkPopulation(uint256 zoneId) public view returns (uint256[] memory) {\n require(zoneId < 100, \"invalid request\");\n uint256 zoneX = zoneId % 10;\n uint256 zoneY = zoneId / 10;\n\n uint256[] memory toReturn = new uint256[](100);\n uint256 counter = 0;\n\n for(uint256 j = zoneY * 1000; j < zoneY * 1000 + 1000; j += 100) {\n for(uint256 i = zoneX * 10; i < zoneX * 10 + 10; i++) {\n uint256 projectedId = i + j;\n toReturn[counter++] = chunkT2LandSales[projectedId] + (chunkT3LandSoldTo[projectedId] != address(0) ? 1 : 0);\n }\n }\n\n return toReturn;\n }\n\n function getChunkPopulation(uint256[] memory chunkIds) public view returns (uint256[] memory) {\n require(chunkIds.length > 0 && chunkIds.length <= 100, \"invalid request\");\n uint256[] memory toReturn = new uint256[](chunkIds.length);\n\n for (uint256 i = 0; i < chunkIds.length; i++) {\n toReturn[i] = chunkT2LandSales[chunkIds[i]] + (chunkT3LandSoldTo[chunkIds[i]] != address(0) ? 1 : 0);\n }\n\n return toReturn;\n }\n\n function getAvailableLand() public view returns (uint256, uint256, uint256) {\n return (availableLand[TIER_ONE], availableLand[TIER_TWO], availableLand[TIER_THREE]);\n }\n\n function getAvailableLandPerChunk() public view returns (uint256) {\n return _allowedLandSalePerChunk;\n }\n\n function getSoldLand() public view returns (uint256, uint256, uint256) {\n return (t1LandsSold, t2LandsSold, t3LandsSold);\n }\n\n function getPopulatedT2Chunks() public view returns (uint256) {\n return chunksWithT2Land;\n }\n\n function getPurchase() public view returns (uint256, uint256) {\n return (purchaseAddressMapping[msg.sender].purchasedTier, purchaseAddressMapping[msg.sender].stamp);\n }\n\n function getPurchaseOf(address owner) public view returns (uint256, uint256) {\n return (purchaseAddressMapping[owner].purchasedTier, purchaseAddressMapping[owner].stamp);\n }\n\n function getSalesCount() public view returns (uint256){\n return totalSales;\n }\n\n function getPurchaseBySale(uint256 sale) public view returns (address, uint256, uint256) {\n return (sales[sale].buyer, sales[sale].purchasedTier, sales[sale].stamp);\n }\n\n function setSaleAllowed(bool allowed) external isAdmin {\n _enabled = allowed;\n }\n\n function setReservedSaleAllowed(bool allowed) external isAdmin {\n _reservedEnabled = allowed;\n }\n\n // do NOT use this for reserve false unless really needed. This doesn't update reseller data\n // the purpose of this function is to provide a cheap way to bulk reserve blocks that don't have resellers\n function setChunksReservation(uint256[] calldata chunkIds, bool reserved) external isAdmin {\n for (uint256 i = 0; i < chunkIds.length; i++) {\n \n if(reserved && !reservedChunkIds.contains(chunkIds[i])) {\n reservedChunkIds.add(chunkIds[i]);\n }\n\n if(!reserved) {\n reservedChunkIds.remove(chunkIds[i]);\n }\n }\n }\n\n // be careful with forced, a chunkId may still remain attached to an existing reseller\n // forced should not be used unless something is really wrong\n function setChunksReservationInfo(uint256[] calldata chunkIds, address reserveFor, bool reserved, bool forced) external isAdmin {\n for (uint256 i = 0; i < chunkIds.length; i++) {\n require (chunkIds[i] != 0, \"0 NA\"); // chunk id 0 shouldn't be reserved\n require(!reserved || (forced || chunksReservedFor[chunkIds[i]] == address(0)), \"AS\"); // already reserved, request has to be forced to avoid issues\n \n if(reserved && !reservedChunkIds.contains(chunkIds[i])) {\n reservedChunkIds.add(chunkIds[i]);\n }\n\n if(!reserved) {\n reservedChunkIds.remove(chunkIds[i]);\n chunksReservedFor[chunkIds[i]] = address(0);\n reservedChunks[reserveFor].remove(chunkIds[i]);\n }\n\n if(reserved && !reservedChunks[reserveFor].contains(chunkIds[i])) {\n reservedChunks[reserveFor].add(chunkIds[i]);\n chunksReservedFor[chunkIds[i]] = reserveFor;\n }\n }\n }\n\n function givePlayersReservedLand(address[] calldata players, address reseller, uint256 tier) external isAdmin {\n for (uint256 i = 0; i < players.length; i++) {\n playerReservedLands[players[i]].add(++playerReservedLandAt);\n playerReservedLandTier[playerReservedLandAt] = tier;\n playerReservedLandReseller[playerReservedLandAt] = reseller;\n playerReservedLandForPlayer[playerReservedLandAt] = players[i];\n }\n }\n\n function getPlayerReservedLand(address player) public view returns(uint256[] memory t2Reservations, uint256[] memory t3Reservations) {\n uint256 amount = playerReservedLands[player].length();\n uint256 t2Count = 0;\n uint256 t3Count = 0;\n for (uint256 i = 0; i < amount; i++) {\n uint256 reservation = playerReservedLands[player].at(i);\n uint256 reservedTier = playerReservedLandTier[reservation];\n if(reservedTier == 2) {\n t2Count++;\n }\n else if(reservedTier == 3) {\n t3Count++;\n }\n }\n\n if(t2Count == 0 && t3Count == 0) {\n return (new uint256[](0), new uint256[](0));\n }\n\n t2Reservations = new uint256[](t2Count); \n t3Reservations = new uint256[](t3Count); \n t2Count = 0;\n t3Count = 0;\n\n for (uint256 i = 0; i < amount; i++) {\n uint256 reservation = playerReservedLands[player].at(i);\n uint256 reservedTier = playerReservedLandTier[reservation];\n if(reservedTier == 2) {\n t2Reservations[t2Count++] = reservation;\n }\n else if(reservedTier == 3) {\n t3Reservations[t3Count++] = reservation;\n }\n }\n }\n\n function getChunksOfReservations(uint256 reservationId) public view returns (uint256[] memory chunkIds) {\n address reseller = playerReservedLandReseller[reservationId];\n return getChunksOfReseller(reseller);\n }\n\n function getInfoOfReservation(uint256 reservationId) public view returns (address player, address reseller, uint256 tier, bool claimed) {\n return (playerReservedLandForPlayer[reservationId], playerReservedLandReseller[reservationId], playerReservedLandTier[reservationId], playerReservedLandClaimed[reservationId]);\n }\n\n function getChunksOfReseller(address reservedFor) public view returns (uint256[] memory chunkIds) {\n uint256 amount = reservedChunks[reservedFor].length();\n chunkIds = new uint256[](amount);\n\n uint256 index = 0;\n for (uint256 i = 0; i < amount; i++) {\n uint256 id = reservedChunks[reservedFor].at(i);\n chunkIds[index++] = id;\n }\n }\n\n function claimPlayerReservedLand(uint256 reservation, uint256 chunkId, uint256 tier) public reservedSaleAllowed {\n require(tier != 1, \"NT1\");\n require(playerReservedLandClaimed[reservation] == false, \"AC\"); // already claimed\n require(playerReservedLands[msg.sender].contains(reservation), \"IR\"); // invalid reservation\n require(playerReservedLandTier[reservation] == tier, \"IT\"); // invalid tier\n address reseller = playerReservedLandReseller[reservation];\n require(reservedChunks[reseller].contains(chunkId), \"IR2\"); // invalid reseller\n\n if(tier == 3) {\n require(_chunkAvailableForT3(chunkId), \"T3 NA\"); \n chunkT3LandSoldTo[chunkId] = msg.sender;\n takenT3Chunks.add(chunkId);\n }\n\n if(tier == 2) {\n require(chunkT2LandSales[chunkId] < _allowedLandSalePerChunk, \"T2 NA\");\n if(chunkT2LandSales[chunkId] == 0){\n chunksWithT2Land++;\n }\n chunkT2LandSales[chunkId]++;\n }\n \n chunkZoneLandSales[chunkIdToZoneId(chunkId)]++;\n playerReservedLands[msg.sender].remove(reservation);\n playerReservedLandClaimed[reservation] = true;\n cbkLand.mint(msg.sender, tier, chunkId, reseller);\n emit ReservedLandClaimed(reservation, reseller, msg.sender, tier, chunkId);\n }\n\n\n \n function massClaimReservationsForPlayer(address player, uint256[] calldata reservations) external isAdmin {\n for (uint256 i = 0; i < reservations.length; i++) {\n uint256 reservation = reservations[i];\n require(playerReservedLandClaimed[reservation] == false, \"AC\"); // already claimed\n require(playerReservedLands[player].contains(reservation), \"IR\"); // invalid reservation\n address reseller = playerReservedLandReseller[reservation];\n uint256 rcLength = reservedChunks[reseller].length();\n require(rcLength > 0, \"no reserved chunks\");\n \n uint256 assignedChunkid = 0;\n uint256 reservedTier = playerReservedLandTier[reservation];\n \n require(reservedTier == TIER_TWO || reservedTier == TIER_THREE, \"NA\");\n\n uint256 counter = reservedChunksCounter[reseller];\n for(uint256 i = counter; i < counter + rcLength; i++) {\n uint256 cId = reservedChunks[reseller].at(uint256(i % rcLength));\n if(reservedTier == TIER_TWO) { // T2, find a spot with enough population\n if(chunkT2LandSales[cId] < _allowedLandSalePerChunk) {\n assignedChunkid = cId;\n reservedChunksCounter[reseller] = uint256(i + 1);\n break;\n }\n }\n else if(!takenT3Chunks.contains(cId)) { // This is a T3, find a chunk that isn't claimed as T3\n assignedChunkid = cId;\n reservedChunksCounter[reseller] = uint256(i + 1);\n break;\n }\n \n }\n\n require(assignedChunkid != 0, \"NA\");\n\n if(reservedTier == TIER_TWO) {\n if(chunkT2LandSales[assignedChunkid] == 0){\n chunksWithT2Land++;\n }\n chunkT2LandSales[assignedChunkid]++;\n }\n else {\n chunkT3LandSoldTo[assignedChunkid] = player;\n takenT3Chunks.add(assignedChunkid);\n }\n\n playerReservedLands[player].remove(reservation);\n playerReservedLandClaimed[reservation] = true;\n cbkLand.mint(player, reservedTier, assignedChunkid, reseller);\n chunkZoneLandSales[chunkIdToZoneId(assignedChunkid)]++;\n emit ReservedLandClaimedForPlayer(reservation, reseller, player, reservedTier, assignedChunkid);\n }\n }\n\n function getResellerOfChunk(uint256 chunkId) public view returns (address reservedFor) {\n reservedFor = chunksReservedFor[chunkId];\n }\n\n function getReservedChunksIds() public view returns (uint256[] memory chunkIds) {\n uint256 amount = reservedChunkIds.length();\n chunkIds = new uint256[](amount);\n\n uint256 index = 0;\n for (uint256 i = 0; i < amount; i++) {\n uint256 id = reservedChunkIds.at(i);\n chunkIds[index++] = id;\n }\n }\n\n function getTakenT3Chunks() public view returns (uint256[] memory chunkIds) {\n uint256 amount = takenT3Chunks.length();\n chunkIds = new uint256[](amount);\n\n uint256 index = 0;\n for (uint256 i = 0; i < amount; i++) {\n uint256 id = takenT3Chunks.at(i);\n chunkIds[index++] = id;\n }\n }\n\n function setAllowedLandOffset(uint256 allowedOffset) external isAdmin {\n _allowedLandOffset = allowedOffset;\n }\n\n function setAllowedLandPerChunk(uint256 allowedLandSalePerChunk) external isAdmin {\n _allowedLandSalePerChunk = allowedLandSalePerChunk;\n }\n\n function setAvailableLand(uint256 tier, uint256 available) external isAdmin {\n require(tier >= TIER_ONE && tier <= TIER_THREE, \"Invalid tier\");\n availableLand[tier] = available;\n }\n\n function getReservationAt() public view returns (uint256) {\n return playerReservedLandAt;\n }\n\n // function updateResellerOfReservation(uint256[] calldata ids, address reseller) external isAdmin {\n // for (uint256 i = 0; i < ids.length; i++) {\n // playerReservedLandReseller[ids[i]] = reseller;\n // }\n // }\n\n // Do not use forced unless really needed. If used, preferable don't update population\n // Do NOT use with T3\n // function updateLandChunkIdBulk(uint256[] calldata landIds, uint256 fromChunkId, uint256 toChunkId, bool updateToPopulation, bool forced) external isAdmin {\n // require(forced || cbkLand.landsBelongToChunk(landIds, fromChunkId), \"NA\");\n\n // if(updateToPopulation) {\n // uint256 populationFrom = chunkT2LandSales[fromChunkId];\n // uint256 populationTo = chunkT2LandSales[toChunkId];\n // uint256 populationChange = landIds.length;\n\n // if(populationFrom - populationChange < 0) {\n // require(forced, \"NA2\"); // forced or don't allow. Something is wrong.\n // populationChange = populationFrom; // can't have negative population\n // }\n\n // if(populationTo + populationChange > _allowedLandSalePerChunk) {\n // require(forced, \"NA3\"); // forced or don't allow. Something is wrong. No reset on populationChange\n // }\n\n // chunkT2LandSales[fromChunkId] -= populationChange;\n // chunkZoneLandSales[chunkIdToZoneId(fromChunkId)] -= populationChange;\n\n // chunkT2LandSales[toChunkId] += populationChange;\n // chunkZoneLandSales[chunkIdToZoneId(toChunkId)] += populationChange;\n // }\n\n // cbkLand.updateChunkId(landIds, toChunkId);\n // }\n}"
},
"contracts/staking/StakingRewardsUpgradeable.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/Math.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\n\nimport \"../cryptoblades.sol\";\n\n// Inheritance\nimport \"./interfaces/IStakingRewards.sol\";\nimport \"./RewardsDistributionRecipientUpgradeable.sol\";\nimport \"./FailsafeUpgradeable.sol\";\n\n// https://docs.synthetix.io/contracts/source/contracts/stakingrewards\ncontract StakingRewardsUpgradeable is\n IStakingRewards,\n Initializable,\n RewardsDistributionRecipientUpgradeable,\n ReentrancyGuardUpgradeable,\n FailsafeUpgradeable,\n PausableUpgradeable\n{\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n /* ========== STATE VARIABLES ========== */\n\n IERC20 public rewardsToken;\n IERC20 public stakingToken;\n uint256 public periodFinish;\n uint256 public override rewardRate;\n uint256 public override rewardsDuration;\n uint256 public override minimumStakeTime;\n uint256 public lastUpdateTime;\n uint256 public rewardPerTokenStored;\n\n mapping(address => uint256) public userRewardPerTokenPaid;\n mapping(address => uint256) public rewards;\n\n uint256 internal _totalSupply;\n mapping(address => uint256) internal _balances;\n mapping(address => uint256) internal _stakeTimestamp;\n\n // used only by the SKILL-for-SKILL staking contract\n CryptoBlades internal __game;\n\n uint256 public override minimumStakeAmount;\n\n /* ========== CONSTRUCTOR ========== */\n\n function initialize(\n address _owner,\n address _rewardsDistribution,\n address _rewardsToken,\n address _stakingToken,\n uint256 _minimumStakeTime\n ) public virtual initializer {\n __Context_init();\n __Ownable_init_unchained();\n __Pausable_init_unchained();\n __Failsafe_init_unchained();\n __ReentrancyGuard_init_unchained();\n __RewardsDistributionRecipient_init_unchained();\n\n // for consistency with the old contract\n transferOwnership(_owner);\n\n rewardsToken = IERC20(_rewardsToken);\n stakingToken = IERC20(_stakingToken);\n rewardsDistribution = _rewardsDistribution;\n minimumStakeTime = _minimumStakeTime;\n\n periodFinish = 0;\n rewardRate = 0;\n rewardsDuration = 180 days;\n }\n\n function migrateTo_8cb6e70(uint256 _minimumStakeAmount) external onlyOwner {\n minimumStakeAmount = _minimumStakeAmount;\n }\n\n /* ========== VIEWS ========== */\n\n function totalSupply() external view override returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account)\n external\n view\n override\n returns (uint256)\n {\n return _balances[account];\n }\n\n function lastTimeRewardApplicable() public view override returns (uint256) {\n return Math.min(block.timestamp, periodFinish);\n }\n\n function rewardPerToken() public view override returns (uint256) {\n if (_totalSupply == 0) {\n return rewardPerTokenStored;\n }\n return\n rewardPerTokenStored.add(\n lastTimeRewardApplicable()\n .sub(lastUpdateTime)\n .mul(rewardRate)\n .mul(1e18)\n .div(_totalSupply)\n );\n }\n\n function earned(address account) public view override returns (uint256) {\n return\n _balances[account]\n .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))\n .div(1e18)\n .add(rewards[account]);\n }\n\n function getRewardForDuration() external view override returns (uint256) {\n return rewardRate.mul(rewardsDuration);\n }\n\n function getStakeRewardDistributionTimeLeft()\n external\n override\n view\n returns (uint256)\n {\n (bool success, uint256 diff) = periodFinish.trySub(block.timestamp);\n return success ? diff : 0;\n }\n\n function getStakeUnlockTimeLeft() external override view returns (uint256) {\n if(periodFinish <= block.timestamp) return 0;\n (bool success, uint256 diff) =\n _stakeTimestamp[msg.sender].add(minimumStakeTime).trySub(\n block.timestamp\n );\n return success ? diff : 0;\n }\n\n /* ========== MUTATIVE FUNCTIONS ========== */\n\n function stake(uint256 amount)\n external\n virtual\n override\n normalMode\n nonReentrant\n whenNotPaused\n updateReward(msg.sender)\n {\n _stake(msg.sender, amount);\n stakingToken.safeTransferFrom(msg.sender, address(this), amount);\n }\n\n function withdraw(uint256 amount)\n public\n virtual\n override\n normalMode\n nonReentrant\n updateReward(msg.sender)\n {\n require(\n minimumStakeTime == 0 ||\n block.timestamp.sub(_stakeTimestamp[msg.sender]) >=\n minimumStakeTime ||\n periodFinish <= block.timestamp,\n \"Cannot withdraw until minimum staking time has passed\"\n );\n _unstake(msg.sender, amount);\n stakingToken.safeTransfer(msg.sender, amount);\n }\n\n function getReward()\n public\n virtual\n override\n normalMode\n nonReentrant\n updateReward(msg.sender)\n {\n require(\n minimumStakeTime == 0 ||\n block.timestamp.sub(_stakeTimestamp[msg.sender]) >=\n minimumStakeTime ||\n periodFinish <= block.timestamp,\n \"Cannot get reward until minimum staking time has passed\"\n );\n uint256 reward = rewards[msg.sender];\n if (reward > 0) {\n rewards[msg.sender] = 0;\n rewardsToken.safeTransfer(msg.sender, reward);\n emit RewardPaid(msg.sender, reward);\n }\n }\n\n function exit() external virtual override normalMode {\n withdraw(_balances[msg.sender]);\n getReward();\n }\n\n function recoverOwnStake() external virtual failsafeMode {\n uint256 amount = _balances[msg.sender];\n if (amount > 0) {\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n stakingToken.safeTransfer(msg.sender, amount);\n }\n }\n\n /* ========== RESTRICTED FUNCTIONS ========== */\n\n function notifyRewardAmount(uint256 reward)\n external\n override\n normalMode\n onlyRewardsDistribution\n updateReward(address(0))\n {\n if (block.timestamp >= periodFinish) {\n rewardRate = reward.div(rewardsDuration);\n } else {\n uint256 remaining = periodFinish.sub(block.timestamp);\n uint256 leftover = remaining.mul(rewardRate);\n rewardRate = reward.add(leftover).div(rewardsDuration);\n }\n\n // Ensure the provided reward amount is not more than the balance in the contract.\n // This keeps the reward rate in the right range, preventing overflows due to\n // very high values of rewardRate in the earned and rewardsPerToken functions;\n // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.\n uint256 balance = rewardsToken.balanceOf(address(this));\n require(\n rewardRate <= balance.div(rewardsDuration),\n \"Provided reward too high\"\n );\n\n lastUpdateTime = block.timestamp;\n periodFinish = block.timestamp.add(rewardsDuration);\n emit RewardAdded(reward);\n }\n\n // End rewards emission earlier\n function updatePeriodFinish(uint256 timestamp)\n external\n normalMode\n onlyOwner\n updateReward(address(0))\n {\n require(\n timestamp > lastUpdateTime,\n \"Timestamp must be after lastUpdateTime\"\n );\n periodFinish = timestamp;\n }\n\n // Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders\n function recoverERC20(address tokenAddress, uint256 tokenAmount)\n external\n virtual\n onlyOwner\n {\n require(\n tokenAddress != address(stakingToken),\n \"Cannot withdraw the staking token\"\n );\n IERC20(tokenAddress).safeTransfer(owner(), tokenAmount);\n emit Recovered(tokenAddress, tokenAmount);\n }\n\n function setRewardsDuration(uint256 _rewardsDuration)\n external\n normalMode\n onlyOwner\n {\n require(\n block.timestamp > periodFinish,\n \"Previous rewards period must be complete before changing the duration for the new period\"\n );\n rewardsDuration = _rewardsDuration;\n emit RewardsDurationUpdated(rewardsDuration);\n }\n\n function setMinimumStakeTime(uint256 _minimumStakeTime)\n external\n normalMode\n onlyOwner\n {\n minimumStakeTime = _minimumStakeTime;\n emit MinimumStakeTimeUpdated(_minimumStakeTime);\n }\n\n function setMinimumStakeAmount(uint256 _minimumStakeAmount)\n external\n normalMode\n onlyOwner\n {\n minimumStakeAmount = _minimumStakeAmount;\n emit MinimumStakeAmountUpdated(_minimumStakeAmount);\n }\n\n function enableFailsafeMode() public override normalMode onlyOwner {\n minimumStakeAmount = 0;\n minimumStakeTime = 0;\n periodFinish = 0;\n rewardRate = 0;\n rewardPerTokenStored = 0;\n\n super.enableFailsafeMode();\n }\n\n function recoverExtraStakingTokensToOwner() external onlyOwner {\n // stake() and withdraw() should guarantee that\n // _totalSupply <= stakingToken.balanceOf(this)\n uint256 stakingTokenAmountBelongingToOwner =\n stakingToken.balanceOf(address(this)).sub(_totalSupply);\n\n if (stakingTokenAmountBelongingToOwner > 0) {\n stakingToken.safeTransfer(\n owner(),\n stakingTokenAmountBelongingToOwner\n );\n }\n }\n\n function pause() external onlyOwner whenNotPaused {\n _pause();\n }\n\n function unpause() external onlyOwner whenPaused {\n _unpause();\n }\n\n /* ========== INTERNAL FUNCTIONS ========== */\n\n function _stake(address staker, uint256 amount) internal\n {\n require(amount >= minimumStakeAmount, \"Minimum stake amount required\");\n _totalSupply = _totalSupply.add(amount);\n _balances[staker] = _balances[staker].add(amount);\n _stakeTimestamp[staker] = block.timestamp; // reset timer on adding to stake\n\n emit Staked(staker, amount);\n }\n\n function _unstake(address staker, uint256 amount) internal\n {\n require(amount > 0, \"Cannot withdraw 0\");\n _totalSupply = _totalSupply.sub(amount);\n _balances[staker] = _balances[staker].sub(amount);\n if (_balances[staker] == 0) {\n _stakeTimestamp[staker] = 0;\n } else {\n _stakeTimestamp[staker] = block.timestamp;\n }\n emit Withdrawn(staker, amount);\n }\n\n /* ========== MODIFIERS ========== */\n\n modifier updateReward(address account) {\n if (!failsafeModeActive) {\n rewardPerTokenStored = rewardPerToken();\n lastUpdateTime = lastTimeRewardApplicable();\n if (account != address(0)) {\n rewards[account] = earned(account);\n userRewardPerTokenPaid[account] = rewardPerTokenStored;\n }\n }\n _;\n }\n\n /* ========== EVENTS ========== */\n\n event RewardAdded(uint256 reward);\n event Staked(address indexed user, uint256 amount);\n event Withdrawn(address indexed user, uint256 amount);\n event RewardPaid(address indexed user, uint256 reward);\n event RewardsDurationUpdated(uint256 newDuration);\n event MinimumStakeTimeUpdated(uint256 newMinimumStakeTime);\n event MinimumStakeAmountUpdated(uint256 newMinimumStakeAmount);\n event Recovered(address token, uint256 amount);\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\nimport \"../proxy/Initializable.sol\";\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuardUpgradeable is Initializable {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n function __ReentrancyGuard_init() internal initializer {\n __ReentrancyGuard_init_unchained();\n }\n\n function __ReentrancyGuard_init_unchained() internal initializer {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n // On the first call to nonReentrant, _notEntered will be true\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n uint256[49] private __gap;\n}\n"
},
"@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"./ContextUpgradeable.sol\";\nimport \"../proxy/Initializable.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract PausableUpgradeable is Initializable, ContextUpgradeable {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n function __Pausable_init() internal initializer {\n __Context_init_unchained();\n __Pausable_init_unchained();\n }\n\n function __Pausable_init_unchained() internal initializer {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n uint256[49] private __gap;\n}\n"
},
"@openzeppelin/contracts/math/Math.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow, so we distribute\n return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);\n }\n}\n"
},
"contracts/staking/interfaces/IStakingRewards.sol": {
"content": "pragma solidity >=0.4.24;\n\n// https://docs.synthetix.io/contracts/source/interfaces/istakingrewards\ninterface IStakingRewards {\n // Views\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function earned(address account) external view returns (uint256);\n\n function getRewardForDuration() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n function minimumStakeAmount() external view returns (uint256);\n\n function minimumStakeTime() external view returns (uint256);\n\n function getStakeRewardDistributionTimeLeft() external view returns (uint256);\n\n function getStakeUnlockTimeLeft() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function rewardsDuration() external view returns (uint256);\n\n // Mutative\n function stake(uint256 amount) external;\n\n function withdraw(uint256 amount) external;\n\n function getReward() external;\n\n function exit() external;\n\n // Events\n event RewardAdded(uint256 reward);\n\n event Staked(address indexed user, uint256 amount);\n\n event Withdrawn(address indexed user, uint256 amount);\n\n event RewardPaid(address indexed user, uint256 reward);\n\n event RewardsDurationUpdated(uint256 newDuration);\n\n event MinimumStakeTimeUpdated(uint256 newMinimumStakeTime);\n}\n"
},
"contracts/staking/RewardsDistributionRecipientUpgradeable.sol": {
"content": "pragma solidity ^0.6.2;\n\n// Inheritance\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\n\n// https://docs.synthetix.io/contracts/source/contracts/rewardsdistributionrecipient\nabstract contract RewardsDistributionRecipientUpgradeable is Initializable, OwnableUpgradeable {\n address public rewardsDistribution;\n\n function __RewardsDistributionRecipient_init() internal initializer {\n __Ownable_init();\n __RewardsDistributionRecipient_init_unchained();\n }\n\n function __RewardsDistributionRecipient_init_unchained() internal initializer {\n }\n\n function notifyRewardAmount(uint256 reward) external virtual;\n\n modifier onlyRewardsDistribution() {\n require(\n msg.sender == rewardsDistribution,\n \"Caller is not RewardsDistribution contract\"\n );\n _;\n }\n\n function setRewardsDistribution(address _rewardsDistribution)\n external\n onlyOwner\n {\n rewardsDistribution = _rewardsDistribution;\n }\n}\n"
},
"contracts/staking/FailsafeUpgradeable.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\n\nabstract contract FailsafeUpgradeable is Initializable, OwnableUpgradeable {\n bool public failsafeModeActive;\n\n function __Failsafe_init() internal initializer {\n __Ownable_init();\n __Failsafe_init_unchained();\n }\n\n function __Failsafe_init_unchained() internal initializer {\n failsafeModeActive = false;\n }\n\n function enableFailsafeMode() public virtual onlyOwner {\n failsafeModeActive = true;\n emit FailsafeModeEnabled();\n }\n\n event FailsafeModeEnabled();\n\n modifier normalMode {\n require(\n !failsafeModeActive,\n \"This action cannot be performed while the contract is in Failsafe Mode\"\n );\n _;\n }\n\n modifier failsafeMode {\n require(\n failsafeModeActive,\n \"This action can only be performed while the contract is in Failsafe Mode\"\n );\n _;\n }\n}\n"
},
"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"../utils/ContextUpgradeable.sol\";\nimport \"../proxy/Initializable.sol\";\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n function __Ownable_init() internal initializer {\n __Context_init_unchained();\n __Ownable_init_unchained();\n }\n\n function __Ownable_init_unchained() internal initializer {\n address msgSender = _msgSender();\n _owner = msgSender;\n emit OwnershipTransferred(address(0), msgSender);\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n emit OwnershipTransferred(_owner, address(0));\n _owner = address(0);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n emit OwnershipTransferred(_owner, newOwner);\n _owner = newOwner;\n }\n uint256[49] private __gap;\n}\n"
},
"contracts/CBKLand.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"./util.sol\";\n\ncontract CBKLand is Initializable, ERC721Upgradeable, AccessControlUpgradeable {\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n bytes32 public constant NO_OWNED_LIMIT = keccak256(\"NO_OWNED_LIMIT\");\n\n // Land specific\n uint256 public constant LT = 0; // Land Tier\n uint256 public constant LC = 1; // Land Chunk Id\n uint256 public constant LX = 2; // Land Coordinate X\n uint256 public constant LY = 3; // Land Coordinate Y\n \n\n event LandMinted(address indexed minter, uint256 id, uint256 tier, uint256 chunkId);\n event LandTransfered(address indexed from, address indexed to, uint256 id);\n event LandTokenMinted(address indexed reseller, address indexed minter, uint256 id, uint256 tier);\n event LandMintedWithReseller(address indexed minter, uint256 id, uint256 tier, uint256 chunkId, address reseller);\n event LandChunkIdUpdated(uint256 indexed id, uint256 chunkId);\n\n // TotalLand\n uint256 landMinted;\n // Avoiding structs for stats\n mapping(uint256 => mapping(uint256 => uint256)) landData;\n\n mapping(uint256 => mapping(uint256 => string)) landStrData;\n\n uint256 public constant LBT = 0; // Land is a Token, it will have its chunkId updated later\n mapping(uint256 => mapping(uint256 => bool)) landBoolData;\n\n uint256 public constant LAR = 0; // Land Reseller, the one who minted the token\n mapping(uint256 => mapping(uint256 => address)) landAddressData;\n\n uint256 public constant TSU = 0; // URI of a tier. Will put this in land NFT because it kinda belongs here\n mapping(uint256 => mapping(uint256 => string)) tierStrData;\n\n function initialize () public initializer {\n __ERC721_init(\"CryptoBladesKingdoms Land\", \"CBKL\");\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"NA\");\n }\n\n // tier, chunkid, x, y, reseller address\n function get(uint256 id) public view returns (uint256, uint256, uint256, uint256, address) {\n return (landData[id][LT], landData[id][LC], landData[id][LX], landData[id][LY], landAddressData[id][LAR]);\n }\n\n function getOwned(address owner) public view returns (uint256[] memory ownedIds) {\n uint256 ownedLandCount = balanceOf(owner);\n ownedIds = new uint256[](ownedLandCount);\n for(uint256 i = 0; i < ownedLandCount; i++) {\n ownedIds[i] = tokenOfOwnerByIndex(owner, i);\n }\n }\n\n function getLandReseller(uint256 land) public view returns (address) {\n return landAddressData[land][LAR];\n }\n\n // DO NOT call directly outside the logic of CBKLandSale to avoid breaking tier and chunk logic\n function mint(address minter, uint256 tier, uint256 chunkId) public restricted {\n uint256 tokenID = landMinted++;\n \n landData[tokenID][LT] = tier;\n landData[tokenID][LC] = chunkId;\n //landData[tokenID][LX] = x; // not yet\n //landData[tokenID][LY] = y; // not yet\n \n _mint(minter, tokenID);\n emit LandMinted(minter, tokenID, tier, chunkId);\n }\n\n function mint(address minter, uint256 tier, uint256 chunkId, address reseller) public restricted {\n uint256 tokenID = landMinted++;\n \n landData[tokenID][LT] = tier;\n landData[tokenID][LC] = chunkId;\n //landData[tokenID][LX] = x; // not yet\n //landData[tokenID][LY] = y; // not yet\n \n landAddressData[tokenID][LAR] = reseller;\n\n _mint(minter, tokenID);\n emit LandMintedWithReseller(minter, tokenID, tier, chunkId, reseller);\n }\n\n // To be used by the NFT bridge. This is fine as the logic will still be maintend by CBKLandSale deployed to BSC.\n // ChunkId set on global T1s or reseller T1s will include BE server which has access to BSC CBKLandSale\n function mintOrUpdate(uint256 tokenID, address minter, uint256 tier, uint256 chunkId, uint256 x, uint256 y, address reseller) public restricted returns (uint256) {\n \n if(tokenID == 0) {\n tokenID = landMinted++;\n _mint(minter, tokenID);\n landData[tokenID][LT] = tier;\n landAddressData[tokenID][LAR] = reseller;\n }\n \n // Unlike vars above, these may change at some point (or multiple) \n landData[tokenID][LX] = x;\n landData[tokenID][LY] = y;\n landData[tokenID][LC] = chunkId;\n\n return tokenID;\n }\n\n function massMint(address minter, uint256 tier, uint256 chunkId, address reseller, uint256 quantity) public restricted {\n for(uint256 i = 0; i < quantity; i++) {\n mint(minter, tier, chunkId, reseller);\n }\n }\n\n function updateChunkId(uint256 id, uint256 chunkId) public restricted {\n landData[id][LC] = chunkId;\n emit LandChunkIdUpdated(id, chunkId);\n }\n\n function updateChunkId(uint256[] memory ids, uint256 chunkId) public restricted {\n for(uint256 i = 0; i < ids.length; i++) {\n updateChunkId(ids[i], chunkId);\n }\n }\n\n // Helper function for bulk moving land without having to jump chains\n function landsBelongToChunk(uint256[] memory ids, uint256 chunkId) public view returns (bool) {\n for(uint256 i = 0; i < ids.length; i++) {\n if(landData[ids[i]][LC] != chunkId) {\n return false;\n }\n\n if(ids[i] > landMinted) {\n return false;\n }\n }\n\n return true;\n }\n\n function getLandTierURI(uint256 id) public view returns (string memory uri) {\n (uint256 tier,,,,) = get(id);\n return getTierURI(tier);\n }\n\n function tokenURI(uint256 id) public view override returns (string memory) {\n return getLandTierURI(id);\n }\n\n function getTierURI(uint256 tier) public view returns (string memory uri) {\n return tierStrData[tier][TSU];\n }\n\n function setTierStr(uint256 tier, uint256 index, string memory val) public restricted {\n tierStrData[tier][index] = val;\n }\n\n function getLandTier(uint256 id) public view returns (uint256) {\n return landData[id][LT];\n }\n}\n"
},
"contracts/EquipmentManager.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/utils/EnumerableSet.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"./characters.sol\";\nimport \"./weapons.sol\";\nimport \"./shields.sol\";\nimport \"./util.sol\";\n\ncontract EquipmentManager is Initializable, AccessControlUpgradeable {\n\n using ABDKMath64x64 for int128;\n using EnumerableSet for EnumerableSet.UintSet;\n using EnumerableSet for EnumerableSet.AddressSet;\n\n ////////////\n // CONSTANTS\n ////////////\n\n // Permissions\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n // Contract Vars\n\n uint256 public constant VAR_WEAPON_EQUIP_DURABILITY = 101;\n\n // Contract links\n\n uint256 public constant LINK_CHARACTERS = 1;\n uint256 public constant LINK_WEAPONS = 2;\n uint256 public constant LINK_SHIELDS = 3;\n\n // User vars\n\n //uint256 public constant USERVAR_ = 10001;\n\n // NFT vars\n\n uint256 public constant NFTVAR_EQUIPPED_SLOTS = 1;//each bit corresponding to its slot\n\n // Misc\n\n uint256 public constant SLOT_CHARACTER_WEAPON = 1;\n uint256 public constant SLOT_CHARACTER_SHIELD = 2;\n\n\n //////////\n // GENERAL\n //////////\n struct PowerData {\n uint256 onID;\n uint256 souledPower;\n uint256 pvpPower;\n uint256 ffaPower;\n int128 weaponMultBase;\n int128 weaponMultFight;\n uint24 souledBasePower;\n uint24 weaponBonusPower;\n uint8 level;\n uint8 charTrait;\n uint8 weaponTrait;\n uint8 shieldTrait;\n }\n\n event Equipped(address indexed onAddr, uint256 indexed onID, uint256 indexed slot, address itemAddr, uint256 itemID);\n event Unequipped(address indexed onAddr, uint256 indexed onID, uint256 indexed slot);\n event Recalculated(address indexed onAddr, uint256 indexed onID, uint256 indexed calculationID);\n\n function initialize () public initializer {\n __AccessControl_init_unchained();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"NA\");\n }\n\n\n //////////////////\n // STATE VARIABLES\n //////////////////\n\n mapping(uint256 => uint256) public vars;\n mapping(uint256 => address) public links;\n mapping(address => mapping(uint256 => uint256)) public userVars;\n mapping(address => mapping(uint256 => mapping(uint256 => uint256))) public nftVars;//contract>id>field>value\n\n // equipper address, ID, slot, equipped item address\n mapping(address => mapping(uint256 => mapping(uint256 => address))) public equippedSlotAddress;\n // equipper address, ID, slot, equipped item ID(/amount?)\n mapping(address => mapping(uint256 => mapping(uint256 => uint256))) public equippedSlotID;\n\n // equipper address, calculation identifier (future use), proxy address (default = this)\n mapping(address => mapping(uint256 => address)) calculatorProxy;\n // equipper address, slot, set of addresses supported\n mapping(address => mapping(uint256 => EnumerableSet.AddressSet)) private equippableInSlot;\n\n\n ////////////\n // FUNCTIONS\n ////////////\n\n // Mutative\n\n function equipNFT(address onAddr, uint256 onID, uint256 slot, address itemAddr, uint256 itemID) external {\n require(isEquippable(onAddr, slot, itemAddr), \"Invalid item\");\n require(IERC721(onAddr).ownerOf(onID) == msg.sender);//item owner check done on transfer\n require(equippedSlotAddress[onAddr][onID][slot] == address(0), \"Slot taken\");\n\n equippedSlotAddress[onAddr][onID][slot] = itemAddr;\n equippedSlotID[onAddr][onID][slot] = itemID;\n nftVars[onAddr][onID][NFTVAR_EQUIPPED_SLOTS] |= (0x1 << slot);//turn slot bit on\n\n processEquippedItem(onAddr, onID, slot, itemAddr, itemID);\n IERC721(itemAddr).transferFrom(msg.sender, address(this), itemID);\n\n recalculate(onAddr, onID);\n emit Equipped(onAddr, onID, slot, itemAddr, itemID);\n }\n\n function unequipNFT(address onAddr, uint256 onID, uint256 slot) external {\n require(IERC721(onAddr).ownerOf(onID) == msg.sender);\n\n IERC721(equippedSlotAddress[onAddr][onID][slot])\n .transferFrom(address(this), msg.sender, equippedSlotID[onAddr][onID][slot]);\n\n equippedSlotAddress[onAddr][onID][slot] = address(0);\n equippedSlotID[onAddr][onID][slot] = 0;\n nftVars[onAddr][onID][NFTVAR_EQUIPPED_SLOTS] &= ~(0x1 << slot);//turns the slot bit off\n\n recalculate(onAddr, onID);\n emit Unequipped(onAddr, onID, slot);\n }\n\n function recalculate(address onAddr, uint256 onID) public {\n // will use proxy later, for now we assume all items use the manager directly for chars\n if(onAddr == links[LINK_CHARACTERS])\n calculateCharacterVars(onID);\n else\n revert(\"Not implemented\");\n emit Recalculated(onAddr, onID, 0);\n }\n\n function processEquippedItem(address onAddr, uint256 onID, uint256 slot, address itemAddr, uint256 itemID) internal {\n if(itemAddr == links[LINK_WEAPONS]) {\n Weapons weapons = Weapons(links[LINK_WEAPONS]);\n // Limit sharing by draining durability on equip\n weapons.drainDurability(itemID, uint8(vars[VAR_WEAPON_EQUIP_DURABILITY]), false);\n }\n }\n\n // Would-be proxy functions\n\n function calculateCharacterVars(uint256 onID) internal {\n Characters characters = Characters(links[LINK_CHARACTERS]);\n require(characters.getNftVar(onID, 1) == 0, \"Busy\");//don't let them switcharoo while in arena etc\n\n // equipped weapon is required for anything to be calculated\n if(_hasSlotEquipped(LINK_CHARACTERS, onID, SLOT_CHARACTER_WEAPON)) {\n uint256[] memory fields = new uint256[](2);\n fields[0] = 4;\n fields[1] = 5;\n uint256[] memory values = new uint256[](2);\n values[0] = characters.vars(1/*equip version*/);\n values[1] = getPowerData(onID);\n \n characters.setNFTVars(onID, fields, values );\n }\n else {\n // no weapon equipped, we null out the equipment version\n characters.setNftVar(onID, 4/*nft equipment version*/, 0);\n }\n }\n\n function getPowerData(uint256 onID) public view returns (uint256) {\n // May be called from frontend to check if a refresh is required/advised\n // Compare with characters.getNftVar(NFTVAR_POWER_DATA)\n\n Characters characters = Characters(links[LINK_CHARACTERS]);\n // Some values are set later\n PowerData memory pd = PowerData(\n onID,\n 0,0,0,0,0,//souledPower,pvpPower,ffaPower,weaponMultBase,weaponMultFight\n uint24(characters.getTotalPower(onID)),//souledBasePower\n 0,//weaponBonusPower\n characters.getLevel(onID),\n characters.getTrait(onID),\n 0,0//weaponTrait, shieldTrait\n );\n\n // assume weapon slot is using weapon address for now, will revert anyway if not\n (int128 weaponMultBase, // does not contain stat trait benefits\n int128 weaponMultFight, // contains stat benefits (not wep-char trait match yet though!)\n uint24 weaponBonusPower,\n uint8 weaponTrait) = Weapons(links[LINK_WEAPONS])\n .getFightData(equippedSlotID[links[LINK_CHARACTERS]][onID][SLOT_CHARACTER_WEAPON],\n pd.charTrait);\n pd.weaponMultBase = weaponMultBase;\n pd.weaponMultFight = weaponMultFight;\n pd.weaponBonusPower = weaponBonusPower;\n pd.weaponTrait = weaponTrait;\n pd.souledPower = Common.getPlayerPower(pd.souledBasePower, pd.weaponMultFight, pd.weaponBonusPower);\n cachePvpData(pd);\n return getPvePowerData(pd) | getPvpPowerData(pd);\n }\n\n function getPvePowerData(PowerData memory pd) internal pure returns(uint256) {\n // pve, 24 bits each\n return (getPveTraitBonus(pd.charTrait, pd.weaponTrait, 0).mulu(pd.souledPower) & 0xFFFFFF)//v fire\n | ((getPveTraitBonus(pd.charTrait, pd.weaponTrait, 1).mulu(pd.souledPower) & 0xFFFFFF) << 24)//v earth\n | ((getPveTraitBonus(pd.charTrait, pd.weaponTrait, 2).mulu(pd.souledPower) & 0xFFFFFF) << 48)//v lightning\n | ((getPveTraitBonus(pd.charTrait, pd.weaponTrait, 3).mulu(pd.souledPower) & 0xFFFFFF) << 72)//v water\n | (uint256(Common.getPlayerPower(pd.souledBasePower, pd.weaponMultBase, pd.weaponBonusPower)) << 96)//base(target)\n // some extra info to save on calls, if necessary\n | (uint256(pd.level) << 232)\n | (uint256(pd.charTrait) << 240)\n | (uint256(pd.weaponTrait) << 242)\n ;\n }\n\n function cachePvpData(PowerData memory pd) internal view returns (PowerData memory) {\n Shields shields = Shields(links[LINK_SHIELDS]);\n int128 shieldMult = 0;\n uint8 shieldTrait = 4; // signifies no shield\n if(_hasSlotEquipped(LINK_CHARACTERS, pd.onID, SLOT_CHARACTER_SHIELD)) {\n shieldMult = shields.getDefenseMultiplierForTrait(\n equippedSlotID[links[LINK_CHARACTERS]][pd.onID][SLOT_CHARACTER_SHIELD],\n pd.charTrait)\n .sub(mathOneFrac()).mul(2).div(10); // 20% multiplier contribution vs weapons\n shieldTrait = shields.getTrait(equippedSlotID[links[LINK_CHARACTERS]][pd.onID][SLOT_CHARACTER_SHIELD]);\n }\n\n pd.pvpPower = Common.getPlayerPowerBase100(Common.getPowerAtLevel(pd.level),\n pd.weaponMultFight.add(shieldMult), pd.weaponBonusPower);\n pd.ffaPower = Common.getPlayerPowerBase100(Common.getPowerAtLevel(34),\n pd.weaponMultFight.add(shieldMult), pd.weaponBonusPower);\n pd.shieldTrait = shieldTrait == 4 ? 0 : shieldTrait;\n return pd;\n }\n\n function getPvpPowerData(PowerData memory pd) internal pure returns (uint256) {\n //pvp, 14 bits each, tiered\n return ((getPvpTraitBonus(pd.charTrait, pd.weaponTrait, pd.shieldTrait, 0).mulu(pd.pvpPower) & 0x3FFF) << 120)//v fire\n | ((getPvpTraitBonus(pd.charTrait, pd.weaponTrait, pd.shieldTrait, 1).mulu(pd.pvpPower) & 0x3FFF) << 134)//v earth\n | ((getPvpTraitBonus(pd.charTrait, pd.weaponTrait, pd.shieldTrait, 2).mulu(pd.pvpPower) & 0x3FFF) << 148)//v lightning\n | ((getPvpTraitBonus(pd.charTrait, pd.weaponTrait, pd.shieldTrait, 3).mulu(pd.pvpPower) & 0x3FFF) << 162)//v water\n // pvp, 14 bits each, ffa\n | ((getPvpTraitBonus(pd.charTrait, pd.weaponTrait, pd.shieldTrait, 0).mulu(pd.ffaPower) & 0x3FFF) << 176)//v fire\n | ((getPvpTraitBonus(pd.charTrait, pd.weaponTrait, pd.shieldTrait, 1).mulu(pd.ffaPower) & 0x3FFF) << 190)//v earth\n | ((getPvpTraitBonus(pd.charTrait, pd.weaponTrait, pd.shieldTrait, 2).mulu(pd.ffaPower) & 0x3FFF) << 204)//v lightning\n | ((getPvpTraitBonus(pd.charTrait, pd.weaponTrait, pd.shieldTrait, 3).mulu(pd.ffaPower) & 0x3FFF) << 218)//v water\n \n | (uint256(pd.shieldTrait) << 244);\n }\n\n function getPveTraitBonus(uint8 char, uint8 wep, uint8 enemy) internal pure returns (int128) {\n int128 traitBonus = mathOneFrac();\n if(char == wep)\n traitBonus = traitBonus.add(mathTraitBonus());\n \n if(Common.isTraitEffectiveAgainst(char, enemy))\n traitBonus = traitBonus.add(mathTraitBonus());\n else if(Common.isTraitEffectiveAgainst(enemy, char))\n traitBonus = traitBonus.sub(mathTraitBonus());\n \n return traitBonus;\n }\n\n function getPvpTraitBonus(uint8 char, uint8 wep, uint8 shield, uint8 enemy) internal pure returns (int128) {\n int128 traitBonus = getPveTraitBonus(char, wep, enemy);\n if(char == wep)\n traitBonus = traitBonus.add(mathTraitBonus().mul(2));//for a total of 3x weapon trait boost\n\n //4 or higher means no shield\n if(shield < 4) {\n if(Common.isTraitEffectiveAgainst(shield, enemy))\n traitBonus = traitBonus.mul(11).div(10); //+10% power for shield vs enemy\n else\n traitBonus = traitBonus.mul(103).div(100); //+3% power just for having a shield\n }\n \n return traitBonus;\n }\n\n function getStoredPowerData(uint256 charID) external view returns (\n uint24[5] memory pvePower,\n uint16[4] memory pvpTierPower,\n uint16[4] memory pvpFfaPower,\n uint8 level,\n uint8 charTrait,\n uint8 wepTrait,\n uint8 shieldTrait,\n uint256 powerData\n ) {\n powerData = Characters(links[LINK_CHARACTERS]).getNftVar(charID, 5);\n\n pvePower[0] = uint24(powerData & 0xFFFFFF);\n pvePower[1] = uint24((powerData >> 24) & 0xFFFFFF);\n pvePower[2] = uint24((powerData >> 48) & 0xFFFFFF);\n pvePower[3] = uint24((powerData >> 72) & 0xFFFFFF);\n pvePower[4] = uint24((powerData >> 96) & 0xFFFFFF);\n\n pvpTierPower[0] = uint16((powerData >> 120) & 0x3FFF);\n pvpTierPower[1] = uint16((powerData >> 134) & 0x3FFF);\n pvpTierPower[2] = uint16((powerData >> 148) & 0x3FFF);\n pvpTierPower[3] = uint16((powerData >> 162) & 0x3FFF);\n \n pvpFfaPower[0] = uint16((powerData >> 176) & 0x3FFF);\n pvpFfaPower[1] = uint16((powerData >> 190) & 0x3FFF);\n pvpFfaPower[2] = uint16((powerData >> 204) & 0x3FFF);\n pvpFfaPower[3] = uint16((powerData >> 218) & 0x3FFF);\n\n level = uint8((powerData >> 232) & 0xFF);\n charTrait = uint8((powerData >> 240) & 0x3);\n wepTrait = uint8((powerData >> 242) & 0x3);\n shieldTrait = uint8((powerData >> 244) & 0x3);\n }\n\n function getPowerDataBytes(uint charID) external view returns (uint8[32] memory data) {\n uint256 powerData = Characters(links[LINK_CHARACTERS]).getNftVar(charID, 5);\n for(uint i = 0; i < 32; i++) {\n data[i] = uint8(powerData >> i*8);\n }\n }\n\n // Read-only\n\n function isEquippable(address onAddr, uint256 slot, address itemAddr) public view returns (bool) {\n return equippableInSlot[onAddr][slot].contains(itemAddr);\n }\n\n function getEquippedItem(address onAddr, uint256 onID, uint256 slot) public view returns(address, uint256) {\n return (equippedSlotAddress[onAddr][onID][slot], equippedSlotID[onAddr][onID][slot]);\n }\n\n function _hasSlotEquipped(uint256 link, uint256 onID, uint256 slot) internal view returns (bool) {\n return equippedSlotAddress[links[link]][onID][slot] != address(0);\n }\n\n function hasSlotEquipped(address onAddr, uint256 onID, uint256 slot) public view returns (bool) {\n return equippedSlotAddress[onAddr][onID][slot] != address(0);\n }\n\n function hasNothingEquipped(address onAddr, uint256 onID) public view returns (bool) {\n return nftVars[onAddr][onID][NFTVAR_EQUIPPED_SLOTS] == 0;\n }\n\n function haveNothingEquipped(address onAddr, uint256[] memory onIDs) public view returns (bool) {\n for(uint i = 0; i < onIDs.length; i++) {\n if(!hasNothingEquipped(onAddr, onIDs[i]))\n return false;\n }\n return true;\n }\n\n function mathOneFrac() internal pure returns (int128) {\n return ABDKMath64x64.fromUInt(1);\n }\n\n function mathTraitBonus() internal pure returns (int128) {\n return ABDKMath64x64.divu(75, 1000);\n }\n\n // Admin\n\n function setVar(uint256 varField, uint256 value) external restricted {\n vars[varField] = value;\n }\n\n function setVars(uint256[] calldata varFields, uint256[] calldata values) external restricted {\n for(uint i = 0; i < varFields.length; i++) {\n vars[varFields[i]] = values[i];\n }\n }\n\n function getNftVar(address onAddr, uint256 onID, uint256 nftVar) public view returns(uint256) {\n return nftVars[onAddr][onID][nftVar];\n }\n function setNftVar(address onAddr, uint256 onID, uint256 nftVar, uint256 value) public restricted {\n nftVars[onAddr][onID][nftVar] = value;\n }\n\n function setLink(uint256 linkId, address linkAddress) external restricted {\n links[linkId] = linkAddress;\n }\n\n function setEquippable(address onAddr, uint256 slot, address itemAddr, bool equippable) external restricted {\n if(equippable)\n equippableInSlot[onAddr][slot].add(itemAddr);\n else\n equippableInSlot[onAddr][slot].remove(itemAddr);\n }\n\n}"
},
"contracts/NFTStorage.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol\";\nimport \"@openzeppelin/contracts/utils/EnumerableSet.sol\";\nimport \"@openzeppelin/contracts/introspection/ERC165Checker.sol\";\nimport \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport \"./weapons.sol\";\nimport \"./characters.sol\";\nimport \"./shields.sol\";\nimport \"./NFTMarket.sol\";\nimport \"./Promos.sol\";\nimport \"./WeaponRenameTagConsumables.sol\";\nimport \"./CharacterRenameTagConsumables.sol\";\nimport \"./WeaponCosmetics.sol\";\nimport \"./CharacterCosmetics.sol\";\nimport \"./interfaces/IBridgeProxy.sol\";\n\ncontract NFTStorage is IERC721ReceiverUpgradeable, Initializable, AccessControlUpgradeable\n{\n using EnumerableSet for EnumerableSet.UintSet;\n using EnumerableSet for EnumerableSet.AddressSet;\n\n bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n Weapons weapons;\n Characters characters;\n NFTMarket nftMarket;\n WeaponRenameTagConsumables weaponRenameTagConsumables;\n CharacterRenameTagConsumables characterRenameTagConsumables;\n WeaponCosmetics weaponCosmetics;\n CharacterCosmetics characterCosmetics;\n\n bool storageEnabled;\n // sender + token address => ids\n mapping(address => mapping(address => EnumerableSet.UintSet)) private storedItems;\n // token address + id => owner who stored\n mapping(address => mapping(uint256 => address)) private storedItemsOwners;\n // All stored items\n mapping(address => EnumerableSet.UintSet) private allStoredItems;\n // Ideally this would be put in weapons / chars etc.. but hit contract limit on weapons so putting this here for now\n mapping(address => mapping(uint256 => string)) private nftChainIds;\n\n // Keeping thing in check\n // A player can only have one pending request at a time\n\n uint8 public constant TRANSFER_OUT_STATUS_NONE = 0;\n uint8 public constant TRANSFER_OUT_STATUS_PENDING = 1;\n uint8 public constant TRANSFER_OUT_STATUS_PROCESSING = 2;\n uint8 public constant TRANSFER_OUT_STATUS_DONE = 3;\n uint8 public constant TRANSFER_OUT_STATUS_ERROR = 4;\n uint8 public constant TRANSFER_OUT_STATUS_RESTORED = 5;\n\n struct TransferOut {\n address owner;\n address nftAddress;\n\n uint256 requestBlock; // For tracking. Requested on block x\n uint256 lastUpdateBlock; // For tracking. Last Update on block y\n uint256 nftId;\n\n uint256 chainId;\n uint8 status; // enumeration. 0 => nothing, 1 => transfer requested, 2 => moved\n }\n\n // Bot stuff\n uint256 private _transfersOutAt;\n uint256 private _transfersOutCount;\n mapping(uint256 => bool) private _bridgeEnabled; // Which chain can we go to from here?\n mapping(uint256 => string) private _chainPrefix; // Prefix to prepend to chainId\n bool private _botEnabled;\n mapping(uint256 => TransferOut) private transferOuts;\n\n // Player stuff\n // Player => bridgeTransferId\n mapping(address => uint256) private transferOutOfPlayers;\n\n // NFT stuff, an NFT should at most have one BridgeTransfer\n // NFT => id => bridgeTransferId\n mapping(address => mapping(uint256 => uint256)) private transferOutOfNFTs;\n\n // address is IERC721\n EnumerableSet.AddressSet private supportedTokenTypes;\n\n uint8 public constant TRANSFER_IN_STATUS_AVAILABLE = 1;\n uint8 public constant TRANSFER_IN_STATUS_WITHDRAWN = 2;\n\n // Bot deposited items to be extracted by player\n uint8 public constant NFT_TYPE_WEAPON = 1;\n uint8 public constant NFT_TYPE_CHARACTER = 2;\n uint8 public constant NFT_TYPE_SHIELD = 3;\n\n mapping(uint8 => address) private nftTypeToAddress;\n\n // Struct to hold data that will always be needed + rename because it can't be packed.\n // transferInsMeta will hold actual NFT data packed\n struct TransferIn {\n address owner;\n\n uint8 nftType;\n uint256 sourceChain;\n uint256 sourceId;\n\n string rename;\n\n uint256 arrivedAt; // For tracking. Arrived on block x\n uint256 lastUpdateBlock; // For tracking. Last Update on block y\n\n uint8 status; // enumeration. 0 => waiting, 1 => claimed\n }\n\n // Player address => items to be extracted\n mapping(address => EnumerableSet.UintSet) private receivedNFTs;\n\n uint256 private _transferInsAt;\n mapping(uint256 => TransferIn) private transferIns;\n mapping(uint256 => uint256) private transferInsMeta;\n mapping(uint256 => uint256) private transferInSeeds;\n\n\n EnumerableSet.UintSet private _supportedChains;\n\n // Source chain => NFT Type => NFTId of source chain => transfer id\n mapping(uint256 => mapping(uint256 => mapping(uint256 => uint256))) private _transferInsLog;\n // Transfer id => minted item Id; no need for NFT type; we can get that from trasnfer in info\n mapping(uint256 => uint256) private _withdrawFromBridgeLog;\n\n CryptoBlades public game;\n uint256 private _bridgeFee;\n\n // global chainid => local mint id\n mapping(address => mapping(string => uint256)) private nftChainIdsToMintId;\n\n mapping(uint256 => string) private transferInChainId;\n\n Promos public promos;\n\n string private _localChainPrefix;\n\n Shields shields;\n\n // Bot 2.0\n // Proxy contracts\n mapping(address => address) private nftProxyContract;\n // Source chain => transfer out id of source chain => mintid (mint id 0 doesn't exist...)\n mapping(uint256 => mapping(uint256 => uint256)) private bot2p0Log;\n // NFT => allowed networks to bridge into\n mapping(address => EnumerableSet.UintSet) private nftAllowedChains;\n // Target network => allowed nfts\n mapping(uint256 => EnumerableSet.AddressSet) private targetChainAllowedNFTs;\n // Is NFT bridged or only stored\n mapping(address => mapping(uint256 => bool)) public isNftBridged;\n mapping(address => uint256) public withdrawFromStorageNativeFee;\n mapping(address => uint256) public requestBridgeNativeFee;\n\n event NFTStored(address indexed owner, IERC721 indexed nftAddress, uint256 indexed nftID);\n event NFTWithdrawn(address indexed owner, IERC721 indexed nftAddress, uint256 indexed nftID);\n event NFTTransferOutRequest(address indexed owner, IERC721 indexed nftAddress, uint256 indexed nftID);\n event NFTTransferOutCanceled(address indexed owner);\n event NFTTransferUpdate(uint256 indexed requestId, uint8 status, bool forced);\n event TransferedIn(address indexed receiver, uint8 nftType, uint256 sourceChain, uint256 indexed sourceId);\n event NFTWithdrawnFromBridge(address indexed receiver, uint256 indexed bridgedId, uint8 nftType, uint256 indexed mintedId);\n \n // Copied from promos.sol, to avoid paying 5k gas to query a constant.\n uint256 private constant BIT_FIRST_CHARACTER = 1;\n // Copied from characters.sol, to avoid paying 5k gas to query a constant.\n uint256 public constant NFTVAR_NON_GENESIS_VERSION = 3;\n bool giveawayGen2Enabled;\n\n function initialize(address _weaponsAddress, address _charactersAddress, WeaponRenameTagConsumables _weaponRenameTagConsumables, CharacterRenameTagConsumables _characterRenameTagConsumables,\n WeaponCosmetics _weaponCosmetics, CharacterCosmetics _characterCosmetics, NFTMarket _nftMarket)\n public\n initializer\n {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n weapons = Weapons(_weaponsAddress);\n nftTypeToAddress[NFT_TYPE_WEAPON] = _weaponsAddress;\n\n characters = Characters(_charactersAddress);\n nftTypeToAddress[NFT_TYPE_CHARACTER] =_charactersAddress;\n\n weaponRenameTagConsumables = _weaponRenameTagConsumables;\n characterRenameTagConsumables = _characterRenameTagConsumables;\n\n weaponCosmetics = _weaponCosmetics;\n characterCosmetics = _characterCosmetics;\n\n nftMarket = _nftMarket;\n }\n\n function migrateTo_56837f7(CryptoBlades _game) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n game = _game;\n }\n\n function migrateTo_98bf302(Promos _promos) public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n promos = _promos;\n }\n\n function migrateTo_3f597dc(address _shieldsAddress) public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n shields = Shields(_shieldsAddress);\n nftTypeToAddress[NFT_TYPE_SHIELD] =_shieldsAddress;\n }\n\n modifier restricted() {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"AO\");\n _;\n }\n\n modifier gameAdminRestricted() {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"NGA\");\n _;\n }\n\n modifier isStored(IERC721 _tokenAddress, uint256 id) {\n require(\n supportedTokenTypes.contains(address(_tokenAddress)) &&\n storedItems[msg.sender][address(_tokenAddress)].contains(id),\n \"Not listed\"\n );\n _;\n }\n\n modifier isNotStored(IERC721 _tokenAddress, uint256 id) {\n require(\n !supportedTokenTypes.contains(address(_tokenAddress)) ||\n !storedItems[msg.sender][address(_tokenAddress)].contains(id),\n \"Already listed\"\n );\n _;\n }\n\n modifier isOwner(IERC721 _tokenAddress, uint256 id) {\n require(\n storedItemsOwners[address(_tokenAddress)][id] == msg.sender,\n \"Access denied\"\n );\n _;\n }\n\n modifier tokenNotBanned(IERC721 _tokenAddress) {\n require(\n isTokenSupported(_tokenAddress),\n \"Error\"\n );\n _;\n }\n\n modifier notBridged(IERC721 _tokenAddress, uint256 id) {\n require(\n transferOuts[transferOutOfNFTs[address(_tokenAddress)][id]].status == TRANSFER_OUT_STATUS_NONE\n || transferOuts[transferOutOfNFTs[address(_tokenAddress)][id]].status == TRANSFER_OUT_STATUS_RESTORED,\n \"Pending bridge\"\n );\n _;\n }\n\n modifier canStore() {\n require(\n storageEnabled && promos.getBit(msg.sender, promos.BIT_BAD_ACTOR()) == false,\n \"storage disabled\"\n );\n _;\n }\n\n modifier bridgeEnabled(uint256 targetChain) {\n require(\n _bridgeEnabled[targetChain],\n \"bridging disabled\"\n );\n _;\n }\n\n modifier noPendingBridge() {\n require(\n transferOuts[transferOutOfPlayers[msg.sender]].status == TRANSFER_OUT_STATUS_NONE\n || transferOuts[transferOutOfPlayers[msg.sender]].status == TRANSFER_OUT_STATUS_DONE\n || transferOuts[transferOutOfPlayers[msg.sender]].status == TRANSFER_OUT_STATUS_RESTORED,\n \"Cannot request a bridge\"\n );\n _;\n }\n\n modifier ownsBridgedNFT(uint256 bridgedNFT) {\n require( transferIns[bridgedNFT].owner == msg.sender, \"not bridged NFT owner\" );\n _;\n }\n\n modifier bridgeSupported(IERC721 _tokenAddress, uint256 _id, uint256 targetChain) {\n require(_id != 0, \"BNS1\"); // avoid sanity issues with mappings; just dont support 0\n require(nftAllowedChains[address(_tokenAddress)].contains(targetChain), \"BNS2\"); // We support bridging from this chain to that chain\n require(IBridgeProxy(nftProxyContract[address(_tokenAddress)]).isEnabled(), \"BNS3\");\n require(IBridgeProxy(nftProxyContract[address(_tokenAddress)]).canBridge(msg.sender, _id, targetChain), \"BNS4\");\n _;\n }\n \n // fail safes\n modifier canWithdrawBridgedNFT(uint256 bridgedNFT) {\n require( transferIns[bridgedNFT].status == TRANSFER_IN_STATUS_AVAILABLE, \"not available\" );\n require( nftTypeToAddress[transferIns[bridgedNFT].nftType] != address(0), \"NFT not defined\" );\n _;\n }\n\n function isTokenSupported(IERC721 _tokenAddress) public view returns (bool) {\n return supportedTokenTypes.contains(address(_tokenAddress));\n }\n\n function getSupportedTokenTypes() public view returns (IERC721[] memory) {\n EnumerableSet.AddressSet storage set = supportedTokenTypes;\n IERC721[] memory tokens = new IERC721[](set.length());\n\n for (uint256 i = 0; i < tokens.length; i++) {\n tokens[i] = IERC721(set.at(i));\n }\n return tokens;\n }\n\n // Get # of stored items of player for NFT\n function getNumberOfStoragedItems(\n IERC721 _tokenAddress\n ) public view returns (uint256) {\n return getNumberOfStoragedItems(_tokenAddress, msg.sender);\n }\n\n function getNumberOfStoragedItems(\n IERC721 _tokenAddress,\n address playerAddress\n ) public view returns (uint256) {\n return storedItems[playerAddress][address(_tokenAddress)].length();\n }\n\n // Get stored items of player for NFT\n function getStorageItemIds(\n IERC721 _tokenAddress\n ) public view returns (uint256[] memory tokens) {\n\n return getStorageItemIds(_tokenAddress, msg.sender);\n }\n\n function getStorageItemIds(\n IERC721 _tokenAddress,\n address playerAddress\n ) public view returns (uint256[] memory tokens) {\n\n uint256 amount = getNumberOfStoragedItems(_tokenAddress, playerAddress);\n tokens = new uint256[](amount);\n\n EnumerableSet.UintSet storage storedTokens = storedItems[playerAddress][address(_tokenAddress)];\n\n for (uint256 i = 0; i < storedTokens.length(); i++) {\n uint256 id = storedTokens.at(i);\n tokens[i] = id;\n }\n }\n\n // Player adds NFT to storage\n function storeItem(\n IERC721 _tokenAddress,\n uint256 _id\n )\n public\n tokenNotBanned(_tokenAddress)\n isNotStored(_tokenAddress, _id)\n canStore()\n {\n storedItems[msg.sender][address(_tokenAddress)].add(_id);\n allStoredItems[address(_tokenAddress)].add(_id);\n storedItemsOwners[address(_tokenAddress)][_id] = msg.sender;\n\n _tokenAddress.safeTransferFrom(msg.sender, address(this), _id);\n\n emit NFTStored(msg.sender, _tokenAddress, _id);\n }\n\n // Player withdraws NFT from storage.\n function withdrawFromStorage(IERC721 _tokenAddress, uint256 _id)\n public\n isStored(_tokenAddress, _id)\n isOwner(_tokenAddress, _id)\n notBridged(_tokenAddress, _id)\n payable\n {\n if(isNftBridged[address(_tokenAddress)][_id]) {\n require(msg.value == withdrawFromStorageNativeFee[address(_tokenAddress)], 'Bad fee amount');\n delete isNftBridged[address(_tokenAddress)][_id];\n if(address(_tokenAddress) == address(characters) && giveawayGen2Enabled && characters.getNftVar(_id, NFTVAR_NON_GENESIS_VERSION) == 0 && !promos.getBit(msg.sender, BIT_FIRST_CHARACTER)) {\n uint256 seed = uint256(keccak256(abi.encodePacked(now, msg.sender)));\n characters.mint(msg.sender, seed);\n }\n }\n\n storedItems[msg.sender][address(_tokenAddress)].remove(_id);\n allStoredItems[address(_tokenAddress)].remove(_id);\n delete storedItemsOwners[address(_tokenAddress)][_id];\n\n _tokenAddress.safeTransferFrom(address(this), msg.sender, _id);\n\n emit NFTWithdrawn(msg.sender, _tokenAddress, _id);\n }\n\n function allowToken(IERC721 _tokenAddress) public restricted {\n supportedTokenTypes.add(address(_tokenAddress));\n }\n\n function disallowToken(IERC721 _tokenAddress) public restricted {\n supportedTokenTypes.remove(address(_tokenAddress));\n }\n\n // something\n function onERC721Received(\n address, /* operator */\n address, /* from */\n uint256 _id,\n bytes calldata /* data */\n ) external override returns (bytes4) {\n // NOTE: The contract address is always the message sender.\n address _tokenAddress = msg.sender;\n\n require(\n supportedTokenTypes.contains(_tokenAddress) &&\n allStoredItems[_tokenAddress].contains(_id),\n \"Token ID not listed\"\n );\n\n return IERC721ReceiverUpgradeable.onERC721Received.selector;\n }\n\n\n function storageIsEnabled() public view returns (bool) {\n return storageEnabled;\n }\n\n function setStorageEnabled(bool enabled) public restricted {\n storageEnabled = enabled;\n }\n\n // bridge stuff\n function chainBridgeEnabled(uint256 chainId) public view returns (bool) {\n return _bridgeEnabled[chainId];\n }\n\n function getEnabledChainsForBridging() public view returns (uint256[] memory chainIds) {\n uint256 amount = _supportedChains.length();\n chainIds = new uint256[](amount);\n\n for (uint256 i = 0; i < amount; i++) {\n uint256 id = _supportedChains.at(i);\n chainIds[i] = id;\n }\n }\n\n\n function toggleChainBridgeEnabled(uint256 chainId, string memory prefix, bool enable) public restricted {\n _bridgeEnabled[chainId] = enable;\n _chainPrefix[chainId] = prefix;\n\n if(enable) {\n _supportedChains.add(chainId);\n } else {\n _supportedChains.remove(chainId);\n }\n }\n\n // Player requests to bridge an item\n function bridgeItem(\n IERC721 _tokenAddress,\n uint256 _id,\n uint256 targetChain\n )\n public\n payable\n tokenNotBanned(_tokenAddress)\n isStored(_tokenAddress, _id)\n isOwner(_tokenAddress, _id) // isStored built in but why not\n bridgeSupported(_tokenAddress, _id, targetChain)\n bridgeEnabled(targetChain)\n noPendingBridge()\n canStore()\n {\n require(msg.value == requestBridgeNativeFee[address(_tokenAddress)], 'Bad fee amount');\n game.payContractTokenOnly(msg.sender, _bridgeFee);\n transferOuts[++_transfersOutCount] = TransferOut(msg.sender, address(_tokenAddress), block.number, 0, _id, targetChain, 1);\n transferOutOfPlayers[msg.sender] = _transfersOutCount;\n transferOutOfNFTs[address(_tokenAddress)][_id] = _transfersOutCount;\n\n // Set the chainid if none is set already\n // Not set => this is a local mint that was not bridged in\n // Set it to LocalPrefixMintId (Example BSC123, HECO456)\n if(bytes(nftChainIds[address(_tokenAddress)][_id]).length == 0){\n string memory chainId = buildChainId(_id);\n nftChainIds[address(_tokenAddress)][_id] = chainId;\n nftChainIdsToMintId[address(_tokenAddress)][chainId] = _id;\n }\n\n emit NFTTransferOutRequest(msg.sender, _tokenAddress, _id);\n }\n\n // Player cancels bridge request. Can only happen if it's pending.\n function cancelBridge() public\n {\n TransferOut storage transferOut = transferOuts[transferOutOfPlayers[msg.sender]];\n\n require(transferOut.status == TRANSFER_OUT_STATUS_PENDING, 'no pending bridge');\n transferOut.status = TRANSFER_OUT_STATUS_NONE;\n transferOut.lastUpdateBlock = block.number;\n\n emit NFTTransferOutCanceled(msg.sender);\n }\n\n // Player received NFTs waiting to be converted to NFTs\n function getReceivedNFTs() public view returns (uint256[] memory tokens) {\n uint256 amount = receivedNFTs[msg.sender].length();\n tokens = new uint256[](amount);\n\n EnumerableSet.UintSet storage storedTokens = receivedNFTs[msg.sender];\n\n for (uint256 i = 0; i < storedTokens.length(); i++) {\n uint256 id = storedTokens.at(i);\n tokens[i] = id;\n }\n }\n\n function getReceivedNFT(uint256 receivedNFT) public view returns (address, uint8, uint256, uint256, uint8, uint256) {\n TransferIn storage transferIn = transferIns[receivedNFT];\n return (transferIn.owner, transferIn.nftType, transferIn.sourceChain, transferIn.sourceId, transferIn.status, transferInsMeta[receivedNFT]);\n }\n\n function getNFTChainId(address nftAddress, uint256 nftId) public view returns (string memory chainId) {\n chainId = nftChainIds[nftAddress][nftId];\n }\n\n function getNFTFromChainId(string calldata chainid, address nftAddress) external view returns (uint256 tokenId) {\n tokenId = nftChainIdsToMintId[nftAddress][chainid];\n }\n\n function setNFTChainId(address nftAddress, uint256 nftId, string calldata chainId, bool forced) external gameAdminRestricted {\n require(forced || (bytes(nftChainIds[nftAddress][nftId]).length == 0 && nftChainIdsToMintId[nftAddress][chainId] == 0), \"NA\");\n nftChainIds[nftAddress][nftId] = chainId;\n nftChainIdsToMintId[nftAddress][chainId] = nftId;\n }\n\n function setLocalChainPrefix(string calldata prefix) external restricted {\n _localChainPrefix = prefix;\n }\n\n function getLocalChainPrefix() public view returns (string memory) {\n return _localChainPrefix;\n }\n\n function setBridgeFee(uint256 newFee) external restricted {\n _bridgeFee = newFee;\n }\n\n function getBridgeFee() public view returns (uint256){\n return _bridgeFee;\n }\n\n // Bot stuff\n function botEnabled() public view returns (bool) {\n return _botEnabled;\n }\n\n function toggleBotEnabled(bool enable) public restricted {\n _botEnabled = enable;\n }\n\n function getBridgeTransferAt() public view returns (uint256) {\n return _transfersOutAt;\n }\n\n function setBridgeTransferAt(uint256 transfersOutAt) public gameAdminRestricted {\n _transfersOutAt = transfersOutAt;\n }\n\n function getBridgeTransfers() public view returns (uint256) {\n return _transfersOutCount;\n }\n\n // Transfer request of a player if any (only active one). Not bot... will move up\n function getBridgeTransfer() public view returns (uint256) {\n return getBridgeTransferOfPlayer(msg.sender);\n }\n\n function getBridgeTransferOfPlayer(address player) public view returns (uint256) {\n return transferOutOfPlayers[player];\n }\n\n function getBridgeTransfer(uint256 bridgeTransferId) public view returns (address, address, uint256, uint256, uint256, uint256, uint8) {\n TransferOut storage transferOut = transferOuts[bridgeTransferId];\n return (transferOut.owner,\n transferOut.nftAddress,\n transferOut.nftId,\n transferOut.requestBlock,\n transferOut.lastUpdateBlock,\n transferOut.chainId,\n transferOut.status);\n }\n\n // Bot to update status of a transfer request (this chain => outside chain)\n function updateBridgeTransferStatus(uint256 bridgeTransferId, uint8 status, bool forced) public gameAdminRestricted {\n TransferOut storage transferOut = transferOuts[bridgeTransferId];\n require(forced ||\n (transferOut.status == TRANSFER_OUT_STATUS_PENDING && status == TRANSFER_OUT_STATUS_PROCESSING)\n || (transferOut.status == TRANSFER_OUT_STATUS_PROCESSING && status == TRANSFER_OUT_STATUS_DONE)\n || status == TRANSFER_OUT_STATUS_ERROR, 'ISC');\n transferOut.status = status;\n transferOut.lastUpdateBlock = block.number;\n\n if(status == TRANSFER_OUT_STATUS_DONE) {\n transferOut = transferOuts[bridgeTransferId];\n storedItems[transferOut.owner][transferOut.nftAddress].remove(transferOut.nftId);\n allStoredItems[transferOut.nftAddress].remove(transferOut.nftId);\n delete storedItemsOwners[transferOut.nftAddress][transferOut.nftId];\n }\n\n emit NFTTransferUpdate(bridgeTransferId, status, forced);\n }\n\n // To know if it actually made it\n function getTransferInFromLog(uint256 sourceChain, uint8 nftType, uint256 sourceId) public view returns (uint256) {\n return _transferInsLog[sourceChain][nftType][sourceId];\n }\n\n // mint 0 wont be supported in bridging => if != 0 => it got here => transfer was made\n function getTransferInFromLogV2(uint256 sourceChain, uint256 sourceTransferId) public view returns (uint256) {\n return bot2p0Log[sourceChain][sourceTransferId];\n }\n\n function collectNFTData(address nftAddress, uint256 tokenId) external view returns (uint256[] memory uintVars, string memory stringVar) {\n return IBridgeProxy(nftProxyContract[nftAddress]).collectData(tokenId);\n }\n\n function mintOrUpdate(address receiver, uint256 sourceChain, uint256 sourceTransfer, address nftAddress, string calldata chainId, uint256[] calldata uintVars, string calldata stringVar) external gameAdminRestricted {\n require(bot2p0Log[sourceChain][sourceTransfer] == 0, \"NA\");\n uint256 mintedId = nftChainIdsToMintId[nftAddress][chainId];\n\n mintedId = IBridgeProxy(nftProxyContract[nftAddress]).mintOrUpdate(receiver, mintedId, uintVars, stringVar);\n\n // Whether minted or updated, the bridge owns the NFT\n require(IERC721(nftAddress).ownerOf(mintedId) == address(this), \"NA2\");\n _logMintOrUpdate(sourceChain, sourceTransfer, nftAddress, mintedId, chainId);\n _attachToWallet(receiver, nftAddress, mintedId);\n isNftBridged[nftAddress][mintedId] = true;\n }\n\n function _logMintOrUpdate(uint256 sourceChain, uint256 sourceTransfer, address nftAddress, uint256 tokenId, string memory chainId) internal {\n bot2p0Log[sourceChain][sourceTransfer] = tokenId;\n nftChainIds[nftAddress][tokenId] = chainId;\n nftChainIdsToMintId[nftAddress][chainId] = tokenId;\n }\n\n function _attachToWallet(address owner, address nftAddress, uint256 tokenId) internal {\n // Not already attached\n require(storedItemsOwners[nftAddress][tokenId] == address(0), \"NA3\");\n \n if(!allStoredItems[nftAddress].contains(tokenId)) {\n allStoredItems[nftAddress].add(tokenId);\n }\n \n storedItemsOwners[nftAddress][tokenId] = owner;\n storedItems[owner][nftAddress].add(tokenId);\n\n if(transferOuts[transferOutOfNFTs[nftAddress][tokenId]].status == TRANSFER_OUT_STATUS_DONE) {\n transferOuts[transferOutOfNFTs[nftAddress][tokenId]].status = TRANSFER_OUT_STATUS_RESTORED;\n }\n }\n\n function setProxyContract(address nft, address proxy, bool forced) external restricted {\n require(forced || nftProxyContract[nft] == address(0), \"NA\");\n nftProxyContract[nft] = proxy;\n }\n\n function getProxyContract(address nft) public view returns (address) {\n return nftProxyContract[nft];\n }\n\n function setChainSupportedForNFT(address nft, uint256[] calldata chainIds, bool support) external restricted {\n for (uint256 i = 0; i < chainIds.length; i++) {\n uint256 chainId = chainIds[i];\n if(support) {\n require(!nftAllowedChains[nft].contains(chainId), \"NA\");\n nftAllowedChains[nft].add(chainId);\n targetChainAllowedNFTs[chainId].add(nft);\n } else {\n require(nftAllowedChains[nft].contains(chainId), \"NA2\");\n nftAllowedChains[nft].remove(chainId);\n targetChainAllowedNFTs[chainId].remove(nft);\n }\n }\n }\n\n function setWithdrawFromStorageNativeFee(address nftAddress, uint256 newFee) external restricted {\n withdrawFromStorageNativeFee[nftAddress] = newFee;\n }\n\n function setGiveawayGen2Enabled(bool _enabled) external restricted {\n giveawayGen2Enabled = _enabled;\n }\n\n function setRequestBridgeNativeFee(address nftAddress, uint256 newFee) external restricted {\n requestBridgeNativeFee[nftAddress] = newFee;\n }\n\n function recoverFees(address receiver, uint256 amount) external gameAdminRestricted {\n payable(receiver).transfer(amount);\n }\n\n function getChainsSupportingNFT(address nft) public view returns (uint256[] memory chains) {\n chains = new uint256[](nftAllowedChains[nft].length());\n\n for (uint256 i = 0; i < nftAllowedChains[nft].length(); i++) {\n uint256 id = nftAllowedChains[nft].at(i);\n chains[i] = id;\n }\n }\n\n function getNFTsSupportedByChain(uint256 chain) public view returns (address[] memory nfts) {\n nfts = new address[](targetChainAllowedNFTs[chain].length());\n\n for (uint256 i = 0; i < targetChainAllowedNFTs[chain].length(); i++) {\n address nft = targetChainAllowedNFTs[chain].at(i);\n nfts[i] = nft;\n }\n }\n\n function unpackCharactersData(uint256 metaData) public pure returns (uint32 appliedCosmetic, uint16 xp, uint8 level, uint8 trait, uint24 bonusPower, uint16 reputation) {\n trait = uint8((metaData) & 0xFF);\n level = uint8((metaData >> 8) & 0xFF);\n xp = uint16(metaData >> 16 & 0xFFFF);\n appliedCosmetic = uint32((metaData >> 32) & 0xFFFFFFFF);\n bonusPower = uint24((metaData >> 64) & 0xFFFFFF);\n reputation = uint16((metaData >> 88) & 0xFFFF);\n }\n\n function buildChainId(uint256 sourceId) internal view returns (string memory) {\n return string(abi.encodePacked(_localChainPrefix, uint2str(sourceId)));\n }\n\n function uint2str(uint i) internal pure returns (string memory) {\n if (i == 0) return \"0\";\n uint j = i;\n uint length;\n while (j != 0) {\n length++;\n j /= 10;\n }\n bytes memory bstr = new bytes(length);\n uint k = length - 1;\n while (i != 0) {\n bstr[k--] = byte(uint8(48 + i % 10));\n i /= 10;\n }\n return string(bstr);\n }\n}\n"
},
"contracts/WeaponRenameTagConsumables.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"./Consumables.sol\";\nimport \"./weapons.sol\";\n\ncontract WeaponRenameTagConsumables is Consumables {\n\n Weapons public weapons;\n\n uint8 private _minSize;\n uint8 private _maxSize;\n\n mapping(uint256 => string) public renames;\n\n event WeaponRenamed(address indexed owner, uint256 indexed weapon);\n\n function initialize(Weapons _weapons)\n public\n initializer\n {\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n _enabled = true;\n\n weapons = _weapons;\n _minSize = 1;\n _maxSize = 24;\n }\n\n function renameWeapon(uint256 weaponId, string memory newName) public {\n require(bytes(newName).length >= _minSize && bytes(newName).length <= _maxSize, 'size not valid');\n require(weapons.ownerOf(weaponId) == msg.sender, \"Not the weapon owner\");\n consumeItem(1);\n renames[weaponId] = newName;\n emit WeaponRenamed(msg.sender, weaponId);\n }\n\n function getWeaponRename(uint256 weaponId) public view returns (string memory) {\n return renames[weaponId];\n }\n\n function setMinSize(uint8 newMinSize) external isAdmin {\n require(newMinSize > 0, 'invalid size');\n _minSize = newMinSize;\n }\n\n function setMaxSize(uint8 newMaxSize) external isAdmin {\n require(newMaxSize > 0, 'invalid size');\n _maxSize = newMaxSize;\n }\n\n function getMinSize() public view returns (uint8){\n return _minSize;\n }\n\n function getMaxSize() public view returns (uint8){\n return _maxSize;\n }\n\n function setName(uint256 weaponId, string memory newName) public restricted {\n renames[weaponId] = newName;\n }\n}"
},
"contracts/CharacterRenameTagConsumables.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"./Consumables.sol\";\nimport \"./characters.sol\";\n\ncontract CharacterRenameTagConsumables is Consumables {\n\n Characters characters;\n\n uint8 private _minSize;\n uint8 private _maxSize;\n\n mapping(uint256 => string) public renames;\n\n event CharacterRenamed(address indexed owner, uint256 indexed character);\n\n function initialize(Characters _characters)\n public\n initializer\n {\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n _enabled = true;\n\n characters = _characters;\n _minSize = 1;\n _maxSize = 24;\n }\n\n function renameCharacter(uint256 characterId, string memory newName) public {\n require(bytes(newName).length >= _minSize && bytes(newName).length <= _maxSize, 'size not valid');\n require(characters.ownerOf(characterId) == msg.sender, \"Not the character owner\");\n consumeItem(1);\n renames[characterId] = newName;\n emit CharacterRenamed(msg.sender, characterId);\n }\n\n function getCharacterRename(uint256 characterId) public view returns (string memory) {\n return renames[characterId];\n }\n\n function setMinSize(uint8 newMinSize) external isAdmin {\n require(newMinSize > 0, 'invalid size');\n _minSize = newMinSize;\n }\n\n function setMaxSize(uint8 newMaxSize) external isAdmin {\n require(newMaxSize > 0, 'invalid size');\n _maxSize = newMaxSize;\n }\n\n function getMinSize() public view returns (uint8){\n return _minSize;\n }\n\n function getMaxSize() public view returns (uint8){\n return _maxSize;\n }\n\n function setName(uint256 characterId, string memory newName) public restricted {\n renames[characterId] = newName;\n }\n}"
},
"contracts/WeaponCosmetics.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"./Cosmetics.sol\";\nimport \"./weapons.sol\";\n\ncontract WeaponCosmetics is Cosmetics {\n\n Weapons public weapons;\n\n mapping(uint256 => uint32) public appliedCosmetics;\n\n event WeaponCosmeticApplied(address indexed owner, uint256 indexed weapon, uint32 cosmetic);\n event WeaponCosmeticRemoved(address indexed owner, uint256 indexed weapon, uint32 cosmetic);\n\n function initialize(Weapons _weapons)\n public\n initializer\n {\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n weapons = _weapons;\n for(uint8 i = 1; i < 20; i++) {\n _cosmeticAvailable[i] = true;\n }\n }\n\n function applyCosmetic(uint256 weaponId, uint32 cosmetic) public {\n require(weapons.ownerOf(weaponId) == msg.sender, \"Not the weapon owner\");\n if(appliedCosmetics[weaponId] != _noCosmetic){\n _restoreCosmetic(appliedCosmetics[weaponId], 1);\n }\n\n useCosmetic(cosmetic, 1);\n\n appliedCosmetics[weaponId] = cosmetic;\n emit WeaponCosmeticApplied(msg.sender, weaponId, cosmetic);\n }\n\n function removeCosmetic(uint256 weaponId) public {\n require(weapons.ownerOf(weaponId) == msg.sender, \"Not the weapon owner\");\n require(appliedCosmetics[weaponId] != _noCosmetic, \"No cosmetic applied\");\n _restoreCosmetic(appliedCosmetics[weaponId], 1);\n emit WeaponCosmeticRemoved(msg.sender, weaponId, appliedCosmetics[weaponId]);\n appliedCosmetics[weaponId] = _noCosmetic;\n }\n\n function getWeaponCosmetic(uint256 weaponId) public view returns (uint32) {\n return appliedCosmetics[weaponId];\n }\n\n function setWeaponCosmetic(uint256 weaponId, uint32 cosmetic) public restricted {\n appliedCosmetics[weaponId] = cosmetic;\n }\n}"
},
"contracts/CharacterCosmetics.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"./Cosmetics.sol\";\nimport \"./characters.sol\";\n\ncontract CharacterCosmetics is Cosmetics {\n\n Characters public characters;\n\n mapping(uint256 => uint32) public appliedCosmetics;\n\n event CharacterCosmeticApplied(address indexed owner, uint256 indexed character, uint32 cosmetic);\n event CharacterCosmeticRemoved(address indexed owner, uint256 indexed character, uint32 cosmetic);\n\n function initialize(Characters _characters)\n public\n initializer\n {\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n characters = _characters;\n for(uint8 i = 1; i < 19; i++) {\n _cosmeticAvailable[i] = true;\n } \n }\n\n function applyCosmetic(uint256 characterId, uint32 cosmetic) public {\n require(characters.ownerOf(characterId) == msg.sender, \"Not the character owner\");\n if(appliedCosmetics[characterId] != _noCosmetic){\n _restoreCosmetic(appliedCosmetics[characterId], 1);\n }\n\n useCosmetic(cosmetic, 1);\n\n appliedCosmetics[characterId] = cosmetic;\n emit CharacterCosmeticApplied(msg.sender, characterId, cosmetic);\n }\n\n function removeCosmetic(uint256 characterId) public {\n require(characters.ownerOf(characterId) == msg.sender, \"Not the character owner\");\n require(appliedCosmetics[characterId] != _noCosmetic, \"No cosmetic applied\");\n _restoreCosmetic(appliedCosmetics[characterId], 1);\n emit CharacterCosmeticRemoved(msg.sender, characterId, appliedCosmetics[characterId]);\n appliedCosmetics[characterId] = _noCosmetic;\n }\n\n function getCharacterCosmetic(uint256 characterId) public view returns (uint32) {\n return appliedCosmetics[characterId];\n }\n\n function setCharacterCosmetic(uint256 characterId, uint32 cosmetic) public restricted {\n appliedCosmetics[characterId] = cosmetic;\n }\n}"
},
"contracts/interfaces/IBridgeProxy.sol": {
"content": "pragma solidity ^0.6.5;\n\ninterface IBridgeProxy {\n function collectData(uint256 tokenId) external view returns (uint256[] memory uintVars, string memory stringVar);\n\n function mintOrUpdate(address receiver, uint256 tokenId, uint256[] calldata uintVars, string calldata stringVar) external returns (uint256);\n\n function isEnabled() external view returns (bool);\n\n function sigVersion() external view returns (uint256);\n function canBridge(address wallet, uint256 tokenId, uint256 targetChain) external view returns (bool);\n}\n"
},
"contracts/WeaponBridgeProxyContract.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"./WeaponCosmetics.sol\";\nimport \"./WeaponRenameTagConsumables.sol\";\nimport \"./weapons.sol\";\nimport \"./interfaces/IBridgeProxy.sol\";\n\n\ncontract WeaponBridgeProxyContract is Initializable, AccessControlUpgradeable, IBridgeProxy {\n \n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n Weapons weapons;\n WeaponCosmetics weaponCosmetics;\n WeaponRenameTagConsumables weaponRenameTagConsumables;\n address nftStorageAddress;\n bool enabled;\n\n uint8 public constant UINT_NFT_VAR_META = 0;\n uint8 public constant UINT_NFT_VAR_SEED3DCOSMETIC = 1;\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"NA\");\n }\n\n function initialize(address _nftStorageAddress, address _weapons, address _weaponCosmetics, address _weaponRenameTagConsumables) public initializer {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n nftStorageAddress = _nftStorageAddress;\n weapons = Weapons(_weapons);\n weaponCosmetics = WeaponCosmetics(_weaponCosmetics);\n weaponRenameTagConsumables = WeaponRenameTagConsumables(_weaponRenameTagConsumables);\n }\n\n \n function collectData(uint256 tokenId) external view override returns (uint256[] memory uintVars, string memory stringVar) {\n \n (uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3, uint8 _level,, uint24 _burnPoints,, uint24 _weaponType) = weapons.get(tokenId);\n uint32 appliedCosmetic = weaponCosmetics.getWeaponCosmetic(tokenId);\n string memory rename = weaponRenameTagConsumables.getWeaponRename(tokenId);\n uint256 seed3dCosmetics = weapons.getCosmeticsSeed(tokenId);\n\n uintVars = new uint256[](2);\n uintVars[UINT_NFT_VAR_META] = _packWeaponsData(appliedCosmetic, _properties, _stat1, _stat2, _stat3, _level, uint8(_burnPoints & 0xFF), uint8((_burnPoints >> 8) & 0xFF), uint8((_burnPoints >> 16) & 0xFF), _weaponType);\n uintVars[UINT_NFT_VAR_SEED3DCOSMETIC] = seed3dCosmetics;\n\n stringVar = rename;\n }\n\n // for future use, bot will probe the returned value to know if the proxy contract has proper signature behavior\n function sigVersion() external view override returns (uint256) {\n return 3;\n }\n\n function isEnabled() external view override returns (bool) {\n return enabled;\n }\n\n function setEnabled(bool _enabled) external restricted {\n enabled = _enabled;\n }\n\n function mintOrUpdate(address /*receiver*/, uint256 tokenId, uint256[] calldata uintVars, string calldata stringVar) external restricted override returns (uint256) {\n require(enabled, \"not enabled\");\n\n uint32 appliedCosmetic = uint32((uintVars[UINT_NFT_VAR_META] >> 96) & 0xFFFFFFFF);\n\n tokenId = \n weapons.performMintWeaponDetailed(nftStorageAddress, uintVars[UINT_NFT_VAR_META], uintVars[UINT_NFT_VAR_SEED3DCOSMETIC], tokenId);\n\n if(appliedCosmetic > 0) {\n weaponCosmetics.setWeaponCosmetic(tokenId, appliedCosmetic);\n }\n\n if(bytes(stringVar).length > 0) {\n weaponRenameTagConsumables.setName(tokenId, stringVar);\n }\n\n return tokenId;\n }\n\n function _packWeaponsData(uint32 appliedCosmetic, uint16 properties, uint16 stat1, uint16 stat2, uint16 stat3, uint8 weaponLevel, uint8 lowStarBurnPoints, uint8 fourStarBurnPoints, uint8 fiveStarBurnPoints, uint24 weaponType) internal pure returns (uint256) {\n return uint256(fiveStarBurnPoints | (uint256(fourStarBurnPoints) << 8) | (uint256(lowStarBurnPoints) << 16) | (uint256(weaponLevel) << 24) | (uint256(stat3) << 32) | (uint256(stat2) << 48) | (uint256(stat1) << 64) | (uint256(properties) << 80) | (uint256(appliedCosmetic) << 96) | (uint256(weaponType) << 128));\n }\n\n function canBridge(address wallet, uint256 tokenId, uint256 targetChain) external view override returns (bool) {\n return true;\n }\n}\n"
},
"contracts/SimpleQuests.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"./cryptoblades.sol\";\nimport \"./characters.sol\";\nimport \"./weapons.sol\";\nimport \"./shields.sol\";\nimport \"./Promos.sol\";\nimport \"./util.sol\";\nimport \"./items/Junk.sol\";\nimport \"./items/RaidTrinket.sol\";\nimport \"./SafeRandoms.sol\";\nimport \"./BurningManager.sol\";\nimport \"./PartnerVault.sol\";\n\ncontract SimpleQuests is Initializable, AccessControlUpgradeable {\n\n using ABDKMath64x64 for int128;\n using ABDKMath64x64 for uint256;\n using SafeMath for uint256;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n uint256 internal constant SEED_RANDOM_QUEST = uint(keccak256(\"SEED_RANDOM_QUEST\"));\n uint256 internal constant SEED_REWARD_QUEST = uint(keccak256(\"SEED_REWARD_QUEST\"));\n uint256 internal constant SEED_REWARD_WALLET_QUEST = uint(keccak256(\"SEED_REWARD_WALLET_QUEST\"));\n uint256 internal constant SEED_REWARD_WEEKLY = uint(keccak256(\"SEED_REWARD_WEEKLY\"));\n\n /* Quest rarities\n * Quests templates rarities on (0 - common, 1 - uncommon, 2 - rare, 3 - epic, 4 - legendary)\n * Promo quests arrays are accordingly (10 - common, 11 - uncommon, 12 - rare, 13 - epic, 14 - legendary)\n * Pickable quests on 20\n */\n\n /* Promo quest templates\n * Use promo quests flag in order to use promo quest templates, when it's true, it will\n * use promo quest templates, when it's false, it will use regular quest templates\n */\n\n Characters public characters;\n Weapons public weapons;\n Junk public junk;\n RaidTrinket public trinket;\n Shields public shields;\n BurningManager public burningManager;\n SafeRandoms public safeRandoms;\n PartnerVault public partnerVault;\n\n uint8 public constant VAR_COMMON_TIER = 0;\n uint8 public constant VAR_UNCOMMON_TIER = 1;\n uint8 public constant VAR_RARE_TIER = 2;\n uint8 public constant VAR_EPIC_TIER = 3;\n uint8 public constant VAR_LEGENDARY_TIER = 4;\n uint8 public constant VAR_CONTRACT_ENABLED = 9;\n uint8 public constant VAR_REPUTATION_LEVEL_2 = 20;\n uint8 public constant VAR_REPUTATION_LEVEL_3 = 21;\n uint8 public constant VAR_REPUTATION_LEVEL_4 = 22;\n uint8 public constant VAR_REPUTATION_LEVEL_5 = 23;\n uint8 public constant VAR_SKIP_QUEST_STAMINA_COST = 30;\n// uint8 public constant VAR_WEEKLY_COMPLETIONS_GOAL = 31;\n uint256 internal constant NFTVAR_SIMPLEQUEST_PROGRESS = 101;\n uint256 internal constant NFTVAR_SIMPLEQUEST_TYPE = 102;\n uint256 internal constant NFTVAR_REPUTATION = 103;\n\n struct Quest {\n uint256 id;\n Rarity tier;\n ItemType requirementType;\n uint256 requirementRarity;\n uint256 requirementAmount;\n address requirementExternalAddress;\n ItemType rewardType;\n uint256 rewardRarity;\n uint256 rewardAmount;\n address rewardExternalAddress;\n uint256 reputationAmount;\n }\n\n struct Reward {\n uint256 id;\n ItemType rewardType;\n uint256 rewardRarity;\n uint256 rewardAmount;\n address rewardExternalAddress;\n uint256 reputationAmount;\n }\n\n enum ItemType{NONE, WEAPON, JUNK, DUST, TRINKET, SHIELD, STAMINA, SOUL, RAID, EXPERIENCE, EXTERNAL, EXTERNAL_HOLD, CHARACTER}\n enum Rarity{COMMON, UNCOMMON, RARE, EPIC, LEGENDARY}\n\n uint256 public nextQuestID;\n uint256 public nextRewardID;\n\n mapping(uint256 => uint256[]) public questTemplates;\n mapping(uint256 => Quest) public quests;\n mapping(uint256 => uint256) public questSupplies;\n mapping(uint256 => uint256) public questDeadlines;\n mapping(uint256 => uint256) public questIndexes;\n mapping(uint256 => uint256) public characterQuest;\n mapping(uint256 => uint256[4]) public tierChances;\n mapping(uint256 => uint256) public vars;\n mapping(uint256 => uint256) public lastFreeSkipUsage;\n mapping(address => mapping(uint256 => uint256)) public weeklyCompletions; //user to week to completions\n mapping(address => mapping(uint256 => bool)) public weeklyRewardClaimed;\n mapping(uint256 => Reward) public rewards;\n mapping(uint256 => uint256) private weeklyRewards; //unused\n mapping(uint256 => uint256) public weeklyCompletionsGoal;\n mapping(address => mapping(uint256 => uint256)) public walletQuestProgress; // wallet, questID, progress\n mapping(address => mapping(uint256 => uint256)) public questCompletions; // wallet, questID, timesCompleted\n uint constant maxWeeksInAYear = 53;\n uint constant pickableTier = 20;\n uint constant walletTier = 30;\n\n event QuestAssigned(uint256 indexed questID, uint256 indexed characterID);\n event QuestProgressed(uint256 indexed questID, uint256 indexed characterID);\n event QuestComplete(uint256 indexed questID, uint256 indexed characterID);\n event QuestRewarded(uint256 indexed questID, uint256 indexed characterID, uint256[] rewards);\n event QuestSkipped(uint256 indexed questID, uint256 indexed characterID);\n event WeeklyRewardClaimed(address user, uint256 indexed rewardID, uint256[] rewards);\n event RewardAdded(uint256 indexed rewardID);\n event WeeklyRewardSet(uint256 indexed rewardID, uint256 indexed week);\n event WalletQuestProgressed(uint256 indexed questID, address indexed wallet);\n event WalletQuestComplete(uint256 indexed questID, address indexed wallet);\n event WalletQuestRewarded(uint256 indexed questID, address indexed wallet, uint256[] rewards);\n\n function initialize(Characters _characters, Weapons _weapons, Junk _junk, RaidTrinket _trinket, Shields _shields, BurningManager _burningManager, SafeRandoms _safeRandoms, PartnerVault _partnerVault) public initializer {\n __AccessControl_init_unchained();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _setupRole(GAME_ADMIN, msg.sender);\n\n characters = _characters;\n weapons = _weapons;\n junk = _junk;\n trinket = _trinket;\n shields = _shields;\n burningManager = _burningManager;\n safeRandoms = _safeRandoms;\n partnerVault = _partnerVault;\n nextQuestID = 1;\n nextRewardID = 1;\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"NA\");\n }\n\n modifier assertQuestsEnabled() {\n _assertQuestsEnabled();\n _;\n }\n\n function _assertQuestsEnabled() internal view {\n require(vars[VAR_CONTRACT_ENABLED] == 1, \"Quests disabled\");\n }\n\n modifier assertOnQuest(uint256 characterID, bool onQuest) {\n _assertOnQuest(characterID, onQuest);\n _;\n }\n\n function _assertOnQuest(uint256 characterID, bool onQuest) internal view {\n require((characterQuest[characterID] != 0) == onQuest, \"Invalid quest state\");\n }\n\n modifier assertOwnsCharacter(uint256 characterID) {\n _assertOwnsCharacter(characterID);\n _;\n }\n\n function _assertOwnsCharacter(uint256 characterID) internal view {\n require(characters.ownerOf(characterID) == msg.sender, \"Not character owner\");\n }\n\n modifier assertCanClaimWeeklyReward(address user) {\n _assertCanClaimWeeklyReward(user);\n _;\n }\n\n function _assertCanClaimWeeklyReward(address user) internal view {\n require(weeklyRewardClaimed[user][now / 1 weeks] == false, \"Reward already claimed\");\n require(getWeeklyCompletions(user) >= weeklyCompletionsGoal[getWeekNumber()], \"Not enough weekly completions\");\n }\n\n // FUNCTIONS\n\n function generateRequestQuestSeed(uint256 characterID) assertQuestsEnabled assertOwnsCharacter(characterID) public {\n safeRandoms.requestSingleSeed(address(this), RandomUtil.combineSeeds(SEED_RANDOM_QUEST, characterID));\n }\n\n function requestQuest(uint256 characterID) public assertQuestsEnabled assertOnQuest(characterID, false) returns (uint256) {\n return assignNewQuest(characterID);\n }\n\n function requestPickableQuest(uint256 characterID, uint256 questID) public assertQuestsEnabled assertOwnsCharacter(characterID) assertOnQuest(characterID, false) returns (uint256) {\n require(questID == questTemplates[pickableTier][questIndexes[questID]]); // pickable quest tier check\n return setCharacterQuest(characterID, pickableTier, questTemplates[pickableTier][questIndexes[questID]]);\n }\n\n function assignNewQuest(uint256 characterID) private returns (uint256) {\n uint256 seed = safeRandoms.popSingleSeed(address(this), RandomUtil.combineSeeds(SEED_RANDOM_QUEST, characterID), true, true);\n uint256 currentReputation = characters.getNftVar(characterID, NFTVAR_REPUTATION);\n uint256 reputationTier;\n if (currentReputation > vars[VAR_REPUTATION_LEVEL_5]) {\n reputationTier = 4;\n } else if (currentReputation > vars[VAR_REPUTATION_LEVEL_4]) {\n reputationTier = 3;\n } else if (currentReputation > vars[VAR_REPUTATION_LEVEL_3]) {\n reputationTier = 2;\n } else if (currentReputation > vars[VAR_REPUTATION_LEVEL_2]) {\n reputationTier = 1;\n } else {\n reputationTier = 0;\n }\n uint256[4] memory chances = tierChances[reputationTier];\n uint256 tierRoll = RandomUtil.randomSeededMinMax(1, 100, seed);\n seed = RandomUtil.combineSeeds(seed, 1);\n\n uint256 tier;\n if (tierRoll > chances[3]) {\n tier = vars[VAR_LEGENDARY_TIER];\n }\n else if (tierRoll > chances[2]) {\n tier = vars[VAR_EPIC_TIER];\n }\n else if (tierRoll > chances[1]) {\n tier = vars[VAR_RARE_TIER];\n }\n else if (tierRoll > chances[0]) {\n tier = vars[VAR_UNCOMMON_TIER];\n }\n else {\n tier = vars[VAR_COMMON_TIER];\n }\n uint256 questID = generateNewQuest(tier, seed);\n return setCharacterQuest(characterID, tier, questID);\n }\n\n function setCharacterQuest(uint256 characterID, uint256 tier, uint256 questID) private returns (uint256) {\n decrementQuestSupply(tier, questID);\n characterQuest[characterID] = questID;\n uint256[] memory fields = new uint256[](2);\n fields[0] = NFTVAR_SIMPLEQUEST_PROGRESS;\n fields[1] = NFTVAR_SIMPLEQUEST_TYPE;\n uint256[] memory values = new uint256[](2);\n values[0] = 0;\n values[1] = uint256(quests[questID].requirementType);\n characters.setNFTVars(characterID, fields, values);\n emit QuestAssigned(questID, characterID);\n return questID;\n }\n\n function decrementQuestSupply(uint256 tier, uint256 questID) private {\n if (questSupplies[questID] > 0) {\n questSupplies[questID]--;\n if (questSupplies[questID] == 0) {\n _deleteQuestTemplate(tier, questID);\n }\n }\n }\n\n function generateNewQuest(uint256 tier, uint256 seed) private view returns (uint256) {\n uint256[] memory tierQuestTemplates = questTemplates[tier];\n uint256 index = RandomUtil.randomSeededMinMax(0, tierQuestTemplates.length - 1, seed);\n return tierQuestTemplates[index];\n }\n\n function skipQuest(uint256 characterID) public returns (uint256) {\n return skipQuest(characterID, 0);\n }\n\n function skipQuest(uint256 characterID, uint256 pickedQuestID) public assertQuestsEnabled assertOnQuest(characterID, true) assertOwnsCharacter(characterID) returns (uint256) {\n if (hasFreeSkip(characterID)) {\n lastFreeSkipUsage[characterID] = now;\n } else {\n characters.getFightDataAndDrainStamina(msg.sender, characterID, uint8(vars[VAR_SKIP_QUEST_STAMINA_COST]), false, 0);\n }\n emit QuestSkipped(characterQuest[characterID], characterID);\n return _assignNextQuest(characterID, pickedQuestID);\n }\n\n function completeQuest(uint256 characterID) public returns (uint256[] memory questRewards) {\n return completeQuest(characterID, 0);\n }\n\n function completeQuest(uint256 characterID, uint256 pickedQuestID) public assertQuestsEnabled assertOwnsCharacter(characterID) assertOnQuest(characterID, true) returns (uint256[] memory questRewards) {\n uint256[] memory questData = getCharacterQuestData(characterID);\n require(questData[0] >= quests[characterQuest[characterID]].requirementAmount, \"Not completed\");\n uint256 questID = characterQuest[characterID];\n require(questDeadlines[questID] == 0 || questDeadlines[questID] >= now, \"Quest deadline has passed\");\n uint256 currentReputation = questData[2];\n questRewards = rewardCharacterQuest(questID, characterID);\n emit QuestRewarded(questID, characterID, questRewards);\n characters.setNftVar(characterID, NFTVAR_REPUTATION, currentReputation + quests[questID].reputationAmount);\n emit QuestComplete(questID, characterID);\n weeklyCompletions[msg.sender][now / 1 weeks] += 1;\n _assignNextQuest(characterID, pickedQuestID);\n }\n\n function completeWalletQuest(uint256 questID) public assertQuestsEnabled returns (uint256[] memory questRewards) {\n require(walletQuestProgress[msg.sender][questID] >= quests[questID].requirementAmount, \"Not completed\");\n require(questDeadlines[questID] == 0 || questDeadlines[questID] >= now, \"Quest deadline has passed\");\n questRewards = rewardWalletQuest(questID);\n emit WalletQuestRewarded(questID, msg.sender, questRewards);\n emit WalletQuestComplete(questID, msg.sender);\n weeklyCompletions[msg.sender][now / 1 weeks] += 1;\n walletQuestProgress[msg.sender][questID] = 0;\n decrementQuestSupply(walletTier, questID);\n }\n\n function _assignNextQuest(uint256 characterID, uint256 pickedQuestID) internal returns (uint256) {\n if(pickedQuestID == 0) // random quest\n return assignNewQuest(characterID);\n else {\n require(pickedQuestID == questTemplates[pickableTier][questIndexes[pickedQuestID]]); // pickable quest tier check\n return setCharacterQuest(characterID, pickableTier, pickedQuestID);\n }\n }\n\n function generateRewardQuestSeed(uint256 characterID) assertQuestsEnabled assertOwnsCharacter(characterID) assertOnQuest(characterID, true) public {\n uint256[] memory questData = getCharacterQuestData(characterID);\n require(questData[0] >= quests[characterQuest[characterID]].requirementAmount);\n _generateRewardQuestSeed(characterID, false);\n }\n\n function _generateRewardQuestSeed(uint256 characterID, bool forced) internal {\n safeRandoms.requestSingleSeed(address(this), RandomUtil.combineSeeds(SEED_REWARD_QUEST, characterID), forced);\n }\n\n function _generateRewardWalletQuestSeed(uint256 questID, bool forced) internal {\n safeRandoms.requestSingleSeed(msg.sender, RandomUtil.combineSeeds(SEED_REWARD_WALLET_QUEST, questID), forced);\n }\n\n function rewardCharacterQuest(uint256 questID, uint256 characterID) private returns (uint256[] memory) {\n uint256 seed = safeRandoms.popSingleSeed(address(this), RandomUtil.combineSeeds(SEED_REWARD_QUEST, characterID), true, false);\n return rewardQuest(getQuestReward(quests[questID]), characterID, seed);\n }\n\n function rewardWalletQuest(uint256 questID) private returns (uint256[] memory) {\n uint256 seed = safeRandoms.popSingleSeed(msg.sender, RandomUtil.combineSeeds(SEED_REWARD_WALLET_QUEST, questID), true, false);\n return rewardQuest(getQuestReward(quests[questID]), SEED_REWARD_WALLET_QUEST/*hack number to avoid bumping 0 accidentally*/, seed);\n }\n\n function rewardQuest(Reward memory reward, uint256 characterID, uint256 seed) private returns (uint256[] memory) {\n //reward.id is questID for quests and weekNumber for weekly rewards!\n if (reward.rewardType == ItemType.WEAPON) {\n uint256[] memory tokenIDs = new uint256[](reward.rewardAmount);\n for (uint8 i = 0; i < reward.rewardAmount; i++) {\n tokenIDs[i] = weapons.mintWeaponWithStars(msg.sender, reward.rewardRarity, seed, 100);\n seed = RandomUtil.combineSeeds(seed, i);\n }\n return tokenIDs;\n } else if (reward.rewardType == ItemType.JUNK) {\n return junk.mintN(msg.sender, uint8(reward.rewardRarity), uint32(reward.rewardAmount));\n } else if (reward.rewardType == ItemType.TRINKET) {\n return trinket.mintN(msg.sender, uint8(reward.rewardRarity), uint32(reward.rewardAmount), seed);\n } else if (reward.rewardType == ItemType.SHIELD) {\n //0 is NORMAL SHIELD TYPE\n return shields.mintShieldsWithStars(msg.sender, uint8(reward.rewardRarity), 0, uint32(reward.rewardAmount), seed);\n } else if (reward.rewardType == ItemType.DUST) {\n uint32[] memory incrementDustSupplies = new uint32[](3);\n incrementDustSupplies[uint256(reward.rewardRarity)] = uint32(reward.rewardAmount);\n weapons.incrementDustSupplies(msg.sender, incrementDustSupplies[0], incrementDustSupplies[1], incrementDustSupplies[2]);\n } else if (reward.rewardType == ItemType.EXPERIENCE) {\n characters.gainXp(characterID, uint16(reward.rewardAmount));\n } else if (reward.rewardType == ItemType.SOUL) {\n burningManager.giveAwaySoul(msg.sender, reward.rewardAmount);\n } else if (reward.rewardType == ItemType.EXTERNAL) {\n partnerVault.transferReward(reward.rewardExternalAddress, msg.sender, reward.rewardAmount, seed);\n } else if (reward.rewardType == ItemType.CHARACTER) {\n for (uint8 i = 0; i < reward.rewardAmount; i++) {\n characters.mint(msg.sender, seed);\n seed = RandomUtil.combineSeeds(seed, i);\n }\n }\n else {\n revert(\"Unknown reward type\");\n }\n return new uint256[](0);\n }\n\n function generateRewardWeeklySeed(uint256 rewardID) assertQuestsEnabled assertCanClaimWeeklyReward(msg.sender) public {\n safeRandoms.requestSingleSeed(msg.sender, RandomUtil.combineSeeds(SEED_REWARD_WEEKLY, rewardID));\n }\n\n function claimWeeklyReward() assertQuestsEnabled assertCanClaimWeeklyReward(msg.sender) public returns (uint256[] memory weeklyRewardIDs) {\n uint256 rewardID = getWeekNumber();\n weeklyRewardIDs = rewardWeekly(rewardID);\n weeklyRewardClaimed[msg.sender][now / 1 weeks] = true;\n emit WeeklyRewardClaimed(msg.sender, rewardID, weeklyRewardIDs);\n }\n\n function rewardWeekly(uint256 rewardID) private returns (uint256[] memory) {\n uint256 seed = safeRandoms.popSingleSeed(tx.origin, RandomUtil.combineSeeds(SEED_REWARD_WEEKLY, rewardID), true, true);\n return rewardQuest(rewards[rewardID], SEED_REWARD_WEEKLY/*hack number to avoid bumping 0 accidentally*/, seed);\n }\n\n // SUBMITTING PROGRESS\n\n function submitProgress(uint256 characterID, uint256[] memory tokenIds) public assertOnQuest(characterID, true) {\n uint256 questID = characterQuest[characterID];\n _submitProgress(questID, tokenIds);\n incrementCharacterQuestProgress(characterID, questID, tokenIds.length);\n }\n\n function submitWalletProgress(uint256 questID, uint256[] memory tokenIds) public {\n _submitProgress(questID, tokenIds);\n incrementWalletQuestProgress(questID, tokenIds.length);\n }\n\n function _submitProgress(uint256 questID, uint256[] memory tokenIds) private assertQuestsEnabled {\n require(tokenIds.length != 0, \"No tokenIds\");\n Quest memory quest = quests[questID];\n if (quest.requirementType == ItemType.WEAPON) {\n for (uint256 i = 0; i < tokenIds.length; i++) {\n uint256 tokenID = tokenIds[i];\n require(weapons.ownerOf(tokenID) == msg.sender, \"Not weapon owner\");\n require(weapons.getStars(tokenID) >= quest.requirementRarity, \"Wrong weapon rarity\");\n }\n weapons.burnWithoutDust(tokenIds);\n } else if (quest.requirementType == ItemType.JUNK) {\n for (uint256 i = 0; i < tokenIds.length; i++) {\n uint256 tokenID = tokenIds[i];\n require(junk.ownerOf(tokenID) == msg.sender, \"Not junk owner\");\n require(junk.tokenStars(tokenID) >= quest.requirementRarity, \"Wrong junk rarity\");\n }\n junk.burn(tokenIds);\n } else if (quest.requirementType == ItemType.TRINKET) {\n for (uint256 i = 0; i < tokenIds.length; i++) {\n uint256 tokenID = tokenIds[i];\n require(trinket.ownerOf(tokenID) == msg.sender, \"Not trinket owner\");\n require(trinket.tokenStars(tokenID) >= quest.requirementRarity, \"Wrong trinket rarity\");\n }\n trinket.burn(tokenIds);\n } else if (quest.requirementType == ItemType.SHIELD) {\n for (uint256 i = 0; i < tokenIds.length; i++) {\n uint256 tokenID = tokenIds[i];\n require(shields.ownerOf(tokenID) == msg.sender, \"Not shield owner\");\n require(shields.getStars(tokenID) >= quest.requirementRarity, \"Wrong shield rarity\");\n }\n shields.burn(tokenIds);\n } else if (quest.requirementType == ItemType.EXTERNAL) {\n partnerVault.storeNfts(IERC721(quest.requirementExternalAddress), tokenIds);\n } else if (quest.requirementType == ItemType.EXTERNAL_HOLD) {\n partnerVault.showHeldNfts(IERC721(quest.requirementExternalAddress), tokenIds, msg.sender, questID);\n } else {\n revert(\"Unknown requirement type\");\n }\n }\n\n function submitProgressAmount(uint256 characterID, uint256 amount) public assertOnQuest(characterID, true) {\n uint256 questID = characterQuest[characterID];\n _submitProgressAmount(questID, characterID, amount);\n incrementCharacterQuestProgress(characterID, questID, amount);\n }\n\n function submitWalletProgressAmount(uint256 questID, uint256 amount) public {\n _submitProgressAmount(questID, SEED_REWARD_WALLET_QUEST/*hack number to avoid bumping 0 accidentally*/, amount);\n incrementWalletQuestProgress(questID, amount);\n }\n\n function _submitProgressAmount(uint256 questID, uint256 characterID, uint256 amount) private assertQuestsEnabled {\n Quest memory quest = quests[questID];\n if (quest.requirementType == ItemType.STAMINA) {\n require(amount <= 255, \"Incorrect stamina value\");\n characters.getFightDataAndDrainStamina(msg.sender, characterID, uint8(amount), false, 0);\n } else if (quest.requirementType == ItemType.DUST) {\n uint32[] memory decrementDustSupplies = new uint32[](3);\n decrementDustSupplies[quest.requirementRarity] = uint32(amount);\n weapons.decrementDustSupplies(msg.sender, decrementDustSupplies[0], decrementDustSupplies[1], decrementDustSupplies[2]);\n } else if (quest.requirementType == ItemType.SOUL) {\n burningManager.burnSoul(msg.sender, amount);\n } else if (quest.requirementType == ItemType.EXTERNAL) {\n partnerVault.storeCurrency(IERC20(quest.requirementExternalAddress), amount);\n } else {\n revert(\"Unknown requirement type\");\n }\n }\n\n function incrementCharacterQuestProgress(uint256 characterID, uint256 questID, uint256 progress) private {\n require(progress > 0);\n uint totalProgress = characters.getNftVar(characterID, NFTVAR_SIMPLEQUEST_PROGRESS) + progress;\n characters.setNftVar(characterID, NFTVAR_SIMPLEQUEST_PROGRESS, totalProgress);\n emit QuestProgressed(questID, characterID);\n //.sub reverts on negatives\n if (quests[characterQuest[characterID]].requirementAmount.sub(totalProgress) == 0) {\n _generateRewardQuestSeed(characterID, true);\n questCompletions[tx.origin][questID] += 1;\n }\n }\n\n function incrementWalletQuestProgress(uint256 questID, uint256 progress) private {\n require(progress > 0);\n require(questID == questTemplates[walletTier][questIndexes[questID]]); // wallet quest tier check\n require(questCompletions[tx.origin][questID] == 0); // wallet quest completion check\n uint totalProgress = walletQuestProgress[msg.sender][questID] + progress;\n walletQuestProgress[msg.sender][questID] = totalProgress;\n emit WalletQuestProgressed(questID, msg.sender);\n //.sub reverts on negatives\n if (quests[questID].requirementAmount.sub(totalProgress) == 0) {\n _generateRewardWalletQuestSeed(questID, true);\n questCompletions[tx.origin][questID] += 1;\n }\n }\n\n // VIEWS\n\n function hasRandomQuestSeedRequested(uint256 characterID) public view returns (bool) {\n return safeRandoms.hasSingleSeedRequest(address(this), RandomUtil.combineSeeds(SEED_RANDOM_QUEST, characterID));\n }\n\n function hasRandomQuestRewardSeedRequested(uint256 characterID) public view returns (bool) {\n return safeRandoms.hasSingleSeedRequest(address(this), RandomUtil.combineSeeds(SEED_REWARD_QUEST, characterID));\n }\n\n function hasRandomWalletQuestRewardSeedRequested(uint256 questID) public view returns (bool) {\n return safeRandoms.hasSingleSeedRequest(msg.sender, RandomUtil.combineSeeds(SEED_REWARD_WALLET_QUEST, questID));\n }\n\n function hasRandomWeeklyRewardSeedRequested(uint256 rewardID) public view returns (bool) {\n return safeRandoms.hasSingleSeedRequest(msg.sender, RandomUtil.combineSeeds(SEED_REWARD_WEEKLY, rewardID));\n }\n\n function getVars(uint256[] calldata varFields) external view returns (uint256[] memory) {\n uint256[] memory result = new uint256[](varFields.length);\n for (uint i = 0; i < varFields.length; i++) {\n result[i] = vars[varFields[i]];\n }\n return result;\n }\n\n function getTierChances(uint256 reputationLevel) external view returns (uint256[4] memory) {\n return tierChances[reputationLevel];\n }\n\n function getQuestTemplates(uint8 tier) public view returns (uint256[] memory) {\n return questTemplates[tier];\n }\n\n function getCharacterQuestData(uint256 characterID) public view returns (uint256[] memory) {\n uint256[] memory questDataKeys = new uint256[](3);\n questDataKeys[0] = NFTVAR_SIMPLEQUEST_PROGRESS;\n questDataKeys[1] = NFTVAR_SIMPLEQUEST_TYPE;\n questDataKeys[2] = NFTVAR_REPUTATION;\n return characters.getNFTVars(characterID, questDataKeys);\n }\n\n function getQuestReward(Quest memory quest) private pure returns (Reward memory) {\n return Reward(quest.id, quest.rewardType, quest.rewardRarity, quest.rewardAmount, quest.rewardExternalAddress, quest.reputationAmount);\n }\n\n function hasFreeSkip(uint256 characterID) public view returns (bool) {\n return now / 1 days > lastFreeSkipUsage[characterID] / 1 days;\n }\n\n function nextWeeklyQuestCompletionGoalReset() public view returns (uint256) {\n return now + 1 weeks - now % 1 weeks;\n }\n\n function nextFreeSkip() public view returns (uint256) {\n return now + 1 days - now % 1 days;\n }\n\n function getWeeklyCompletions(address user) public view returns (uint256) {\n return weeklyCompletions[user][now / 1 weeks];\n }\n\n function getWeekNumber() public view returns (uint256) {\n return now / 1 weeks % maxWeeksInAYear + 1;\n }\n\n // ADMIN\n\n function setVar(uint256 varField, uint256 value) external restricted {\n vars[varField] = value;\n }\n\n function setVars(uint256[] calldata varFields, uint256[] calldata values) external restricted {\n for (uint i = 0; i < varFields.length; i++) {\n vars[varFields[i]] = values[i];\n }\n }\n\n function setTierChances(uint256 tier, uint256[4] memory chances) public restricted {\n tierChances[tier] = chances;\n }\n\n function addNewQuestTemplate(uint8 tier,\n ItemType requirementType, uint256 requirementRarity, uint256 requirementAmount, address requirementExternalAddress,\n ItemType rewardType, uint256 rewardRarity, uint256 rewardAmount, address rewardExternalAddress,\n uint256 reputationAmount, uint256 supply, uint256 deadline) public restricted {\n uint256 questID = nextQuestID++;\n quests[questID] = Quest(questID, Rarity(tier % 10),\n requirementType, requirementRarity, requirementAmount, requirementExternalAddress,\n rewardType, rewardRarity, rewardAmount, rewardExternalAddress,\n reputationAmount);\n questTemplates[tier].push(questID);\n questIndexes[questID] = questTemplates[tier].length - 1;\n if (supply > 0) {\n require(deadline > 0, \"Missing deadline\");\n questSupplies[questID] = supply;\n questDeadlines[questID] = deadline;\n }\n if(tier == walletTier) {\n require(requirementType != ItemType.STAMINA && requirementType != ItemType.RAID, \"Wrong input\");\n require(reputationAmount == 0 && rewardType != ItemType.EXPERIENCE, \"Wrong reward\");\n }\n }\n\n function setWeeklyReward(ItemType rewardType, uint256 rewardRarity, uint256 rewardAmount, address rewardExternalAddress, uint256 reputationAmount, uint256 weekNumber, uint256 completionsGoal) public restricted {\n rewards[weekNumber] = Reward(weekNumber, rewardType, rewardRarity, rewardAmount, rewardExternalAddress, reputationAmount);\n weeklyCompletionsGoal[weekNumber] = completionsGoal;\n emit RewardAdded(weekNumber);\n }\n\n function deleteQuestTemplate(uint256 tier, uint256 questID) public restricted {\n _deleteQuestTemplate(tier, questID);\n }\n\n function _deleteQuestTemplate(uint256 tier, uint256 questID) internal {\n uint256 questIndex = questIndexes[questID];\n uint256 lastQuestId = questTemplates[tier][questTemplates[tier].length - 1];\n questTemplates[tier][questIndex] = lastQuestId;\n questIndexes[lastQuestId] = questIndex;\n questTemplates[tier].pop();\n }\n}\n"
},
"contracts/items/Junk.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"../Promos.sol\";\n\ncontract Junk is Initializable, ERC721Upgradeable, AccessControlUpgradeable {\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n function initialize (Promos _promos) public initializer {\n __ERC721_init(\"CryptoBlades Junk\", \"CBJ\");\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n promos = _promos;\n }\n\n Promos public promos;\n\n mapping(uint256 => uint8) public tokenStars;\n\n uint256 public nextTokenID;\n\n event Minted(uint256 indexed id, address indexed minter);\n event Burned(uint256 indexed id, address indexed burner);\n\n modifier restricted() {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n _;\n }\n\n function get(uint256 id) public view\n returns (\n uint8 _stars\n ) {\n _stars = tokenStars[id];\n }\n\n function getOwned() public view returns(uint256[] memory) {\n return getOwnedBy(msg.sender);\n }\n\n function getOwnedBy(address owner) public view returns(uint256[] memory tokens) {\n tokens = new uint256[](balanceOf(owner));\n for(uint256 i = 0; i < tokens.length; i++) {\n tokens[i] = tokenOfOwnerByIndex(owner, i);\n }\n }\n\n function getStars(uint256[] memory ids) public view returns (uint8[] memory stars) {\n stars = new uint8[](ids.length);\n for(uint256 i = 0; i < ids.length; i++) {\n stars[i] = tokenStars[ids[i]];\n }\n }\n\n function mint(address minter, uint8 mintStars) public restricted returns(uint256 tokenID) {\n tokenID = nextTokenID++;\n tokenStars[tokenID] = mintStars;\n _mint(minter, tokenID);\n emit Minted(tokenID, minter);\n }\n\n function performMintJunkDetailed(address minter,\n uint256 metaData, uint256 tokenID\n ) public restricted returns(uint256) {\n\n uint8 mintStars = uint8(metaData & 0xFF);\n\n // tokenId 0 => it's a new mint\n if(tokenID == 0){\n tokenID = mint(minter, mintStars);\n } else {\n // tokenId is not 0 => we are updating an existing mint; update the stars in case they can change\n tokenStars[tokenID] = mintStars;\n }\n\n return tokenID;\n }\n\n function mintN(address minter, uint8 mintStars, uint32 amount) public restricted returns(uint256[] memory tokenIds) {\n tokenIds = new uint256[](amount);\n for(uint i = 0; i < amount; i++) {\n tokenIds[i] = mint(minter, mintStars);\n }\n }\n\n function burn(uint256 tokenID) public restricted {\n address burner = ownerOf(tokenID);\n _burn(tokenID);\n emit Burned(tokenID, burner);\n }\n\n function burn(uint256[] memory tokenIDs) public restricted {\n for(uint i = 0; i < tokenIDs.length; i++) {\n burn(tokenIDs[i]);\n }\n }\n\n function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override {\n require(promos.getBit(from, 4) == false && promos.getBit(to, 4) == false);\n }\n\n function setNextTokenID(uint to) public restricted {\n nextTokenID = to;\n }\n\n function setBaseURI(string memory baseUri) public restricted {\n _setBaseURI(baseUri);\n }\n}\n"
},
"contracts/items/RaidTrinket.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"../Promos.sol\";\nimport \"../util.sol\";\n\ncontract RaidTrinket is Initializable, ERC721Upgradeable, AccessControlUpgradeable {\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n function initialize (Promos _promos) public initializer {\n __ERC721_init(\"CryptoBlades Trinket\", \"CBRT\");\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n promos = _promos;\n }\n\n Promos public promos;\n\n mapping(uint256 => uint8) public tokenStars;\n mapping(uint256 => uint256) public tokenEffect;\n\n uint256 public nextTokenID;\n\n event Minted(uint256 indexed id, address indexed minter);\n event Burned(uint256 indexed id, address indexed burner);\n\n modifier restricted() {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n _;\n }\n\n function get(uint256 id) public view\n returns (\n uint8 _stars, uint256 _effect\n ) {\n _stars = tokenStars[id];\n _effect = tokenEffect[id];\n }\n\n function getOwned() public view returns(uint256[] memory) {\n return getOwnedBy(msg.sender);\n }\n\n function getOwnedBy(address owner) public view returns(uint256[] memory tokens) {\n tokens = new uint256[](balanceOf(owner));\n for(uint256 i = 0; i < tokens.length; i++) {\n tokens[i] = tokenOfOwnerByIndex(owner, i);\n }\n }\n\n function getStars(uint256[] memory ids) public view returns (uint8[] memory stars) {\n stars = new uint8[](ids.length);\n for(uint256 i = 0; i < ids.length; i++) {\n stars[i] = tokenStars[ids[i]];\n }\n }\n\n function mint(address minter, uint8 mintStars, uint256 seed) public restricted returns(uint256 tokenID) {\n tokenID = nextTokenID++;\n uint256 mintEffect = (seed / 100) % 5;\n tokenStars[tokenID] = mintStars;\n tokenEffect[tokenID] = mintEffect;\n _mint(minter, tokenID);\n emit Minted(tokenID, minter);\n }\n\n function mintN(address minter, uint8 mintStars, uint32 amount, uint256 seed) public restricted returns(uint256[] memory tokenIDs) {\n tokenIDs = new uint256[](amount);\n for(uint256 i = 0; i < amount; i++) {\n tokenIDs[i] = mint(minter, mintStars, seed);\n seed = RandomUtil.combineSeeds(seed,i);\n }\n }\n\n function burn(uint256 tokenID) public restricted {\n address burner = ownerOf(tokenID);\n _burn(tokenID);\n emit Burned(tokenID, burner);\n }\n\n function burn(uint256[] memory tokenIDs) public restricted {\n for(uint i = 0; i < tokenIDs.length; i++) {\n burn(tokenIDs[i]);\n }\n }\n\n function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override {\n require(promos.getBit(from, 4) == false && promos.getBit(to, 4) == false);\n }\n\n function setNextTokenID(uint to) public restricted {\n nextTokenID = to;\n }\n\n function setBaseURI(string memory baseUri) public restricted {\n _setBaseURI(baseUri);\n }\n}\n"
},
"contracts/PartnerVault.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol\";\nimport \"@openzeppelin/contracts/introspection/ERC165Checker.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport \"@openzeppelin/contracts/utils/EnumerableSet.sol\";\nimport \"./util.sol\";\n\ncontract PartnerVault is Initializable, AccessControlUpgradeable, IERC721ReceiverUpgradeable {\n using ABDKMath64x64 for int128;\n using SafeMath for uint256;\n using EnumerableSet for EnumerableSet.UintSet;\n\n bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n mapping(address => uint256[]) public nfts;\n mapping(address => uint256) public currencies;\n mapping(address => EnumerableSet.UintSet) private shownNfts; // DEPRECATED\n mapping(address => mapping(uint => EnumerableSet.UintSet)) private shownQuestNfts; // tokenAddress -> questId -> ID confirmed held by a user (private due to limitation)\n\n function initialize() public initializer {\n __AccessControl_init_unchained();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _setupRole(GAME_ADMIN, msg.sender);\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"NA\");\n }\n\n modifier isValidERC721(IERC721 tokenAddress) {\n require(ERC165Checker.supportsInterface(address(tokenAddress), _INTERFACE_ID_ERC721), \"Not IERC721\");\n _;\n }\n\n function onERC721Received(address, address, uint256 _id, bytes calldata) external override returns (bytes4) {\n return IERC721ReceiverUpgradeable.onERC721Received.selector;\n }\n\n // FUNCTIONS\n\n function storeNfts(IERC721 tokenAddress, uint256[] calldata tokenIds) external restricted isValidERC721(tokenAddress) {\n for (uint i = 0; i < tokenIds.length; i++) {\n tokenAddress.safeTransferFrom(tx.origin, address(this), tokenIds[i]);\n nfts[address(tokenAddress)].push(tokenIds[i]);\n }\n }\n\n function recordNfts(IERC721 tokenAddress, uint256 startId, uint256 endId) external restricted isValidERC721(tokenAddress) {\n for (uint i = startId; i <= endId; i++) {\n nfts[address(tokenAddress)].push(i);\n }\n }\n\n function showHeldNfts(IERC721 tokenAddress, uint256[] calldata tokenIds, address holder, uint questId) external restricted isValidERC721(tokenAddress) {\n for (uint i = 0; i < tokenIds.length; i++) {\n require(tokenAddress.ownerOf(tokenIds[i]) == holder, \"Not holder\");\n require(shownQuestNfts[address(tokenAddress)][questId].contains(tokenIds[i]) == false, \"NFT already shown for this quest\");\n shownQuestNfts[address(tokenAddress)][questId].add(tokenIds[i]);\n }\n }\n\n function storeCurrency(IERC20 tokenAddress, uint256 amount) external restricted {\n tokenAddress.transferFrom(tx.origin, address(this), amount);\n currencies[address(tokenAddress)] = currencies[address(tokenAddress)].add(amount);\n }\n\n function transferReward(address tokenAddress, address to, uint256 amount, uint256 seed) external restricted {\n require(amount <= nfts[tokenAddress].length || amount <= currencies[tokenAddress], \"Not enough NFTs or currency\");\n if (amount <= currencies[tokenAddress]) {\n IERC20 currency = IERC20(tokenAddress);\n currency.transfer(to, amount);\n currencies[tokenAddress] = currencies[tokenAddress].sub(amount);\n } else {\n IERC721 nft = IERC721(tokenAddress);\n for (uint i = 0; i < amount; i++) {\n uint256 index = RandomUtil.randomSeededMinMax(0, nfts[tokenAddress].length - 1, seed);\n uint256 tokenId = nfts[tokenAddress][index];\n nft.safeTransferFrom(address(this), to, tokenId);\n deleteNft(nft, index);\n seed = RandomUtil.combineSeeds(seed, i);\n }\n }\n }\n\n function deleteNft(IERC721 tokenAddress, uint256 index) internal {\n require(index < nfts[address(tokenAddress)].length, \"Index out of bounds\");\n nfts[address(tokenAddress)][index] = nfts[address(tokenAddress)][nfts[address(tokenAddress)].length - 1];\n nfts[address(tokenAddress)].pop();\n }\n\n\n // VIEWS\n\n function getNftsInVault(address tokenAddress) public view returns (uint256[] memory) {\n return nfts[tokenAddress];\n }\n\n function haveNftsBeenShown(address tokenAddress, uint questId, uint256[] memory tokenIds) public view returns (bool[] memory result) {\n result = new bool[](tokenIds.length);\n for(uint i = 0; i < tokenIds.length; i++) {\n result[i] = shownQuestNfts[tokenAddress][questId].contains(tokenIds[i]);\n }\n }\n}\n"
},
"contracts/raid1.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"./cryptoblades.sol\";\nimport \"./characters.sol\";\nimport \"./weapons.sol\";\nimport \"./Promos.sol\";\nimport \"./util.sol\";\nimport \"./items/RaidTrinket.sol\";\nimport \"./items/KeyLootbox.sol\";\nimport \"./items/Junk.sol\";\nimport \"./common.sol\";\nimport \"./EquipmentManager.sol\";\n\n\ncontract Raid1 is Initializable, AccessControlUpgradeable {\n\n /*\n Actual raids reimplementation\n Figured the old contract may have a lot of redundant variables and it's already deployed\n Maybe the raid interface isn't the way to go\n Either way it's probably fine to lay out the new one in a single file and compare\n The idea is to store all participants and raid details using an indexed mapping system\n And players get to claim their rewards as a derivative of a raid completion seed that\n a safe verifiable random source will provide (ideally)\n It may be better to convert the mappings using raidIndex into a struct\n Need to test gas impact or if stack limits are any different\n */\n\n using ABDKMath64x64 for int128;\n using ABDKMath64x64 for uint256;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n uint8 public constant STATUS_UNSTARTED = 0;\n uint8 public constant STATUS_STARTED = 1;\n uint8 public constant STATUS_WON = 2;\n uint8 public constant STATUS_LOST = 3;\n uint8 public constant STATUS_PAUSED = 4; // in case of emergency\n\n // leaving link 0 empty intentionally\n uint256 public constant LINK_TRINKET = 1;\n uint256 public constant LINK_KEYBOX = 2;\n uint256 public constant LINK_JUNK = 3;\n uint256 public constant LINK_EQUIPMENT_MANAGER = 4;\n\n uint256 public constant NUMBERPARAMETER_AUTO_DURATION = 1;\n uint256 public constant NUMBERPARAMETER_AUTO_BOSSPOWER_PERCENT = uint256(keccak256(\"BOSSPOWER_PERCENT\"));\n\n CryptoBlades public game;\n Characters public characters;\n Weapons public weapons;\n Promos public promos;\n\n struct Raider {\n address owner;\n uint256 charID;\n uint256 wepID;\n uint24 power;\n uint24 traitsCWS;//char trait, wep trait, wep statpattern, unused for now\n }\n\n uint64 public staminaCost;\n uint64 public durabilityCost;\n int128 public joinCost;\n uint16 public xpReward;\n\n uint256 public raidIndex;\n // all (first) keys are raidIndex\n mapping(uint256 => uint8) public raidStatus;\n mapping(uint256 => uint256) public raidEndTime;\n mapping(uint256 => uint256) public raidSeed;\n mapping(uint256 => uint8) public raidBossTrait;\n mapping(uint256 => uint256) public raidBossPower;\n mapping(uint256 => uint256) public raidPlayerPower;\n mapping(uint256 => Raider[]) public raidParticipants;\n mapping(uint256 => mapping(address => uint256[])) public raidParticipantIndices;\n mapping(uint256 => mapping(address => bool)) public raidRewardClaimed;\n\n // link interface\n // the idea is to avoid littering the contract with variables for each type of reward\n mapping(uint256 => address) public links;\n // parameters to avoid littering the contract with more state vars\n mapping(uint256 => uint256) public numberParameters;\n\n event RaidStarted(uint256 indexed raidIndex,\n uint8 bossTrait,\n uint256 bossPower,\n uint256 endTime);\n event RaidJoined(uint256 raidIndex,\n address indexed user,\n uint256 indexed character,\n uint256 indexed weapon,\n uint256 skillPaid);\n event RaidCompleted(uint256 indexed raidIndex,\n uint8 outcome,\n uint256 bossRoll,\n uint256 playerRoll);\n\n // reward specific events for analytics\n event RewardClaimed(uint256 indexed raidIndex, address indexed user, uint256 characterCount);\n event RewardedXpBonus(uint256 indexed raidIndex, address indexed user, uint256 indexed charID, uint16 amount);\n event RewardedDustLB(uint256 indexed raidIndex, address indexed user, uint32 amount);\n event RewardedDust4B(uint256 indexed raidIndex, address indexed user, uint32 amount);\n event RewardedDust5B(uint256 indexed raidIndex, address indexed user, uint32 amount);\n event RewardedWeapon(uint256 indexed raidIndex, address indexed user, uint8 stars, uint256 indexed tokenID);\n event RewardedJunk(uint256 indexed raidIndex, address indexed user, uint8 stars, uint256 indexed tokenID);\n event RewardedTrinket(uint256 indexed raidIndex, address indexed user, uint8 stars, uint256 effect, uint256 indexed tokenID);\n event RewardedKeyBox(uint256 indexed raidIndex, address indexed user, uint256 indexed tokenID);\n\n function initialize(address gameContract) public initializer {\n\n __AccessControl_init_unchained();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _setupRole(GAME_ADMIN, msg.sender);\n\n game = CryptoBlades(gameContract);\n characters = Characters(game.characters());\n weapons = Weapons(game.weapons());\n promos = Promos(game.promos());\n\n staminaCost = 200; // 5 mins each, or 16.666 hours\n durabilityCost = 20; // 50 mins each, or 16.666 hours\n joinCost = 0;// free (was going to be 10 USD)\n xpReward = 128 * 2; // 13 hour 20 min worth of fight xp, but we had double xp active on launch\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n }\n\n function doRaidAuto() public restricted {\n uint256 seed = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1))));\n uint256 power = ABDKMath64x64.divu(numberParameters[NUMBERPARAMETER_AUTO_BOSSPOWER_PERCENT],100)\n .mulu(raidPlayerPower[raidIndex]);\n uint8 trait = uint8(seed % 4);\n uint256 duration = numberParameters[NUMBERPARAMETER_AUTO_DURATION];\n if(duration == 0) {\n duration = 480; // 8 hrs\n }\n doRaidWithSeed(power, trait, duration, seed);\n }\n\n function doRaid(uint256 bossPower, uint8 bossTrait, uint256 durationMinutes) public restricted {\n doRaidWithSeed(bossPower, bossTrait, durationMinutes, uint256(keccak256(abi.encodePacked(blockhash(block.number - 1)))));\n }\n\n function doRaidWithSeed(uint256 bossPower, uint8 bossTrait, uint256 durationMinutes, uint256 seed) public restricted {\n require(raidStatus[raidIndex] != STATUS_PAUSED, \"Raid paused\");\n\n if(raidStatus[raidIndex] == STATUS_STARTED\n && raidParticipants[raidIndex].length > 0) {\n completeRaidWithSeed(seed);\n }\n startRaid(bossPower, bossTrait, durationMinutes);\n }\n\n function startRaid(uint256 bossPower, uint8 bossTrait, uint256 durationMinutes) public restricted {\n raidStatus[raidIndex] = STATUS_STARTED;\n raidBossPower[raidIndex] = bossPower;\n raidBossTrait[raidIndex] = bossTrait;\n\n uint256 endTime = now + (durationMinutes * 1 minutes);\n raidEndTime[raidIndex] = endTime;\n\n emit RaidStarted(raidIndex, bossTrait, bossPower, endTime);\n }\n\n function joinRaid(uint256 characterID) public {\n // owner and stamina/durability checks in the fightdata functions\n require(raidStatus[raidIndex] == STATUS_STARTED, \"Cannot join raid right now!\");\n require(raidEndTime[raidIndex] > now, \"It is too late to join this raid!\");\n\n uint256 weaponID = EquipmentManager(links[LINK_EQUIPMENT_MANAGER]).equippedSlotID(address(characters), characterID, 1);\n\n uint256[] memory raiderIndices = raidParticipantIndices[raidIndex][msg.sender];\n for(uint i = 0; i < raiderIndices.length; i++) {\n require(raidParticipants[raidIndex][raiderIndices[i]].wepID != weaponID,\n \"This weapon is already used in the raid\");\n require(raidParticipants[raidIndex][raiderIndices[i]].charID != characterID,\n \"This character is already participating\");\n }\n\n (/*uint72 miscData*/, uint256 powerData) = characters.getFightDataAndDrainStamina(msg.sender,\n characterID, uint8(staminaCost), true, 0);\n \n uint24 power = uint24(powerData >> raidBossTrait[raidIndex]);\n raidPlayerPower[raidIndex] += power;\n\n //uint8 wepStatPattern = weapons.getStatPattern(weaponID);\n raidParticipantIndices[raidIndex][msg.sender].push(raidParticipants[raidIndex].length);\n raidParticipants[raidIndex].push(Raider(\n msg.sender,\n characterID,\n weaponID,\n power,\n 0//uint24(charTrait) | (uint24(weaponTrait) << 8) | ((uint24(wepStatPattern)) << 16)//traitCWS\n ));\n\n uint256 joinCostPaid = 0;\n if(joinCost > 0) {\n joinCostPaid = game.usdToSkill(joinCost);\n game.payContractTokenOnly(msg.sender, joinCostPaid);\n }\n emit RaidJoined(raidIndex,\n msg.sender,\n characterID,\n weaponID,\n joinCostPaid);\n }\n\n function setRaidStatus(uint256 index, uint8 status) public restricted {\n // only use if absolutely necessary\n raidStatus[index] = status;\n }\n\n function completeRaid() public restricted {\n completeRaidWithSeed(uint256(keccak256(abi.encodePacked(blockhash(block.number - 1)))));\n }\n\n function completeRaidWithSeed(uint256 seed) internal {\n\n raidSeed[raidIndex] = seed;\n raidEndTime[raidIndex] = now;\n\n uint256 bossPower = raidBossPower[raidIndex];\n // we could also not include bossPower in the roll to have slightly higher chances of failure\n // with bosspower added to roll ceiling the likelyhood of a win is: playerPower / bossPower\n uint256 roll = RandomUtil.randomSeededMinMax(0,raidPlayerPower[raidIndex]+bossPower, seed);\n uint8 outcome = roll >= bossPower ? STATUS_WON : STATUS_LOST;\n raidStatus[raidIndex] = outcome;\n\n if(outcome == STATUS_WON) {\n // since we pay out exactly one trinket per raid, we might as well do it here\n Raider memory trinketWinner = raidParticipants[raidIndex][seed % raidParticipants[raidIndex].length];\n uint8 trinketStars = getTrinketStarsFromSeed(seed);\n uint tokenID =\n RaidTrinket(links[LINK_TRINKET]).mint(\n trinketWinner.owner, trinketStars, seed\n );\n uint256 trinketEffect = (seed / 100) % 5;\n emit RewardedTrinket(raidIndex, trinketWinner.owner, trinketStars, trinketEffect, tokenID);\n }\n emit RaidCompleted(raidIndex, outcome, bossPower, roll);\n raidIndex++;\n }\n\n function getTrinketStarsFromSeed(uint256 seed) private pure returns(uint8 stars) {\n uint256 roll = seed % 100;\n if(roll < 1) {\n return 4; // 5* at 1%\n }\n else if(roll < 6) { // 4* at 5%\n return 3;\n }\n else if(roll < 21) { // 3* at 15%\n return 2;\n }\n else if(roll < 56) { // 2* at 35%\n return 1;\n }\n else {\n return 0; // 1* at 44%\n }\n }\n\n function unpackFightData(uint192 playerData)\n public pure returns (uint16 charTraitAndVersion, uint24 basePowerLevel, uint64 timestamp) {\n\n charTraitAndVersion = uint16(playerData & 0xFFFF);\n basePowerLevel = uint24((playerData >> 16) & 0xFFFFFF);\n timestamp = uint64((playerData >> 40) & 0xFFFFFFFFFFFFFFFF);\n }\n\n function getPlayerFinalPower(uint24 playerPower, uint8 charTrait, uint8 bossTrait) public pure returns(uint24) {\n if(Common.isTraitEffectiveAgainst(charTrait, bossTrait))\n return uint24(ABDKMath64x64.divu(1075,1000).mulu(uint256(playerPower)));\n return playerPower;\n }\n\n function claimReward(uint256 claimRaidIndex) public {\n // NOTE: this function is stack limited\n //claimRaidIndex can act as a version integer if future rewards change\n bool victory = raidStatus[claimRaidIndex] == STATUS_WON;\n require(victory || raidStatus[claimRaidIndex] == STATUS_LOST, \"Raid not over\");\n require(raidRewardClaimed[claimRaidIndex][msg.sender] == false, \"Already claimed\");\n\n uint256[] memory raiderIndices = raidParticipantIndices[claimRaidIndex][msg.sender];\n require(raiderIndices.length > 0, \"None of your characters participated\");\n\n uint256 earlyBonusCutoff = raidParticipants[claimRaidIndex].length/2+1; // first half of players\n // we grab raider info (power) and give out xp and raid stats\n for(uint i = 0; i < raiderIndices.length; i++) {\n uint256 raiderIndex = raiderIndices[i];\n Raider memory raider = raidParticipants[claimRaidIndex][raiderIndex];\n int128 earlyMultiplier = ABDKMath64x64.fromUInt(1).add(\n raiderIndex < earlyBonusCutoff ?\n ABDKMath64x64.divu(1,10).mul( // early bonus, 10%\n (earlyBonusCutoff-raiderIndex).divu(earlyBonusCutoff)\n )\n : ABDKMath64x64.fromUInt(0)\n );\n if(victory) {\n distributeRewards(\n claimRaidIndex,\n raiderIndex,\n ABDKMath64x64.divu(earlyMultiplier.mulu(raider.power),\n raidPlayerPower[claimRaidIndex]/raidParticipants[claimRaidIndex].length)\n );\n }\n //weapons.setNftVar(raider.wepID, 1, 0); // NFTVAR_BUSY // no busy flag for raids for now\n characters.processRaidParticipation(raider.charID, victory, uint16(earlyMultiplier.mulu(xpReward)));\n }\n\n raidRewardClaimed[claimRaidIndex][msg.sender] = true;\n emit RewardClaimed(claimRaidIndex, msg.sender, raiderIndices.length);\n }\n\n function distributeRewards(\n uint256 claimRaidIndex,\n uint256 raiderIndex,\n int128 comparedToAverage\n ) private {\n // at most 2 types of rewards\n // common: Lb dust, 1-3 star junk, 3 star wep\n // rare: 4-5b dust, 4-5 star wep, 4-5 star junk, keybox\n // chances are a bit generous compared to weapon mints because stamina cost equals lost skill\n // That being said these rates stink if the oracle is 3x lower than real value.\n uint256 seed = uint256(keccak256(abi.encodePacked(raidSeed[claimRaidIndex], raiderIndex, uint256(msg.sender))));\n\n uint256 commonRoll = RandomUtil.randomSeededMinMax(1, 15 + comparedToAverage.mulu(85), seed);\n if(commonRoll > 20) { // Expected: ~75% (at least 25% at bottom, 90+% past 65% power)\n uint mod = seed % 10;\n if(mod < 2) { // 1 star junk, 2 out of 10 (20%)\n distributeJunk(msg.sender, claimRaidIndex, 0);\n }\n else if(mod < 4) { // 2 star junk, 2 out of 10 (20%)\n distributeJunk(msg.sender, claimRaidIndex, 1);\n }\n else if(mod < 6) { // 2 star weapon, 2 out of 10 (20%)\n distributeWeapon(msg.sender, claimRaidIndex, seed, 1);\n }\n else if(mod == 6) { // 3 star junk, 1 out of 10 (10%)\n distributeJunk(msg.sender, claimRaidIndex, 2);\n }\n else if(mod == 7) { // 1x LB Dust, 1 out of 10 (10%)\n distributeLBDust(msg.sender, claimRaidIndex, 1);\n }\n else if(mod == 8) { // 2x LB Dust, 1 out of 10 (10%)\n distributeLBDust(msg.sender, claimRaidIndex, 2);\n }\n else { // 3 star weapon, 1 out of 10 (10%)\n distributeWeapon(msg.sender, claimRaidIndex, seed, 2);\n }\n }\n\n uint256 rareRoll = RandomUtil.randomSeededMinMax(1, 950 + comparedToAverage.mulu(50), seed + 1);\n if(rareRoll > 950) { // Expected: ~5% (0.72% at bottom, 15% at top, 8.43% middle)\n uint mod = (seed / 10) % 20;\n if(mod < 8) { // key box, 8 out of 20 (40%)\n distributeKeyBox(msg.sender, claimRaidIndex);\n }\n else if(mod == 8) { // 5 star sword, 1 out of 20 (5%)\n distributeWeapon(msg.sender, claimRaidIndex, seed, 4);\n }\n else if(mod == 9) { // 5 star junk, 1 out of 20 (5%)\n distributeJunk(msg.sender, claimRaidIndex, 4);\n }\n else if(mod < 14) { // 4 star sword, 4 out of 20 (20%)\n distributeWeapon(msg.sender, claimRaidIndex, seed, 3);\n }\n else if(mod == 14) { // 1x 4B Dust, 1 out of 20 (5%)\n distribute4BDust(msg.sender, claimRaidIndex, 1);\n }\n else if(mod == 15) { // 1x 5B Dust, 1 out of 20 (5%)\n distribute5BDust(msg.sender, claimRaidIndex, 1);\n }\n else { // 4 star junk, 4 out of 20 (20%)\n distributeJunk(msg.sender, claimRaidIndex, 3);\n }\n }\n\n uint256 bonusXpRoll = RandomUtil.randomSeededMinMax(1, 2000, seed + 2); // 0.05% per point\n if(bonusXpRoll <= 100) { // 5% for any bonus xp result\n if(bonusXpRoll > 50) { // 2.5% for +25% xp\n distributeBonusXp(msg.sender, claimRaidIndex, raiderIndex, uint16(ABDKMath64x64.divu(25,100).mulu(xpReward)));\n }\n else if(bonusXpRoll > 20) { // 1.5% for +150% xp\n distributeBonusXp(msg.sender, claimRaidIndex, raiderIndex, uint16(ABDKMath64x64.divu(150,100).mulu(xpReward)));\n }\n else if(bonusXpRoll > 10) { // 0.5% for +275% xp\n distributeBonusXp(msg.sender, claimRaidIndex, raiderIndex, uint16(ABDKMath64x64.divu(275,100).mulu(xpReward)));\n }\n else if(bonusXpRoll > 4) { // 0.3% for +525% xp\n distributeBonusXp(msg.sender, claimRaidIndex, raiderIndex, uint16(ABDKMath64x64.divu(525,100).mulu(xpReward)));\n }\n else if(bonusXpRoll > 1) { // 0.15% for +1150% xp\n distributeBonusXp(msg.sender, claimRaidIndex, raiderIndex, uint16(ABDKMath64x64.divu(1150,100).mulu(xpReward)));\n }\n else { // 0.05% for +2400% xp\n distributeBonusXp(msg.sender, claimRaidIndex, raiderIndex, uint16(ABDKMath64x64.divu(2400,100).mulu(xpReward)));\n }\n }\n }\n\n function distributeBonusXp(address claimant, uint256 claimRaidIndex, uint256 raiderIndex, uint16 amount) private {\n uint256 charID = raidParticipants[claimRaidIndex][raiderIndex].charID;\n characters.gainXp(charID, amount);\n emit RewardedXpBonus(claimRaidIndex, claimant, charID, amount);\n }\n\n function distributeKeyBox(address claimant, uint256 claimRaidIndex) private {\n uint tokenID = KeyLootbox(links[LINK_KEYBOX]).mint(claimant);\n emit RewardedKeyBox(claimRaidIndex, claimant, tokenID);\n }\n\n function distributeJunk(address claimant, uint256 claimRaidIndex, uint8 stars) private {\n uint tokenID = Junk(links[LINK_JUNK]).mint(claimant, stars);\n emit RewardedJunk(claimRaidIndex, claimant, stars, tokenID);\n }\n\n function distributeWeapon(address claimant, uint256 claimRaidIndex, uint256 seed, uint8 stars) private {\n uint tokenID = weapons.mintWeaponWithStars(claimant, stars, seed / 100, 100);\n emit RewardedWeapon(claimRaidIndex, claimant, stars, tokenID);\n }\n\n function distributeLBDust(address claimant, uint256 claimRaidIndex, uint32 amount) private {\n weapons.incrementDustSupplies(claimant, amount, 0, 0);\n emit RewardedDustLB(claimRaidIndex, claimant, amount);\n }\n\n function distribute4BDust(address claimant, uint256 claimRaidIndex, uint32 amount) private {\n weapons.incrementDustSupplies(claimant, 0, amount, 0);\n emit RewardedDust4B(claimRaidIndex, claimant, amount);\n }\n\n function distribute5BDust(address claimant, uint256 claimRaidIndex, uint32 amount) private {\n weapons.incrementDustSupplies(claimant, 0, 0, amount);\n emit RewardedDust5B(claimRaidIndex, claimant, amount);\n }\n\n function registerLink(address addr, uint256 index) public restricted {\n links[index] = addr;\n }\n\n function setStaminaPointCost(uint8 points) public restricted {\n staminaCost = points;\n }\n\n function setDurabilityPointCost(uint8 points) public restricted {\n durabilityCost = points;\n }\n\n function setJoinCostInCents(uint256 cents) public restricted {\n joinCost = ABDKMath64x64.divu(cents, 100);\n }\n\n function getJoinCostInSkill() public view returns(uint256) {\n return game.usdToSkill(joinCost);\n }\n\n function setXpReward(uint16 xp) public restricted {\n xpReward = xp;\n }\n\n function setNumberParameter(uint256 paramIndex, uint256 value) public restricted {\n numberParameters[paramIndex] = value;\n }\n\n function getNumberParameter(uint256 paramIndex) public view returns(uint256) {\n return numberParameters[paramIndex];\n }\n\n function getRaidStatus(uint256 index) public view returns(uint8) {\n return raidStatus[index];\n }\n\n function getRaidEndTime(uint256 index) public view returns(uint256) {\n return raidEndTime[index];\n }\n\n function getRaidBossTrait(uint256 index) public view returns(uint8) {\n return raidBossTrait[index];\n }\n\n function getRaidBossPower(uint256 index) public view returns(uint256) {\n return raidBossPower[index];\n }\n\n function getRaidPlayerPower(uint256 index) public view returns(uint256) {\n return raidPlayerPower[index];\n }\n\n function getRaidParticipantCount(uint256 index) public view returns(uint256) {\n return raidParticipants[index].length;\n }\n\n function getEligibleRewardIndexes(uint256 startIndex, uint256 endIndex) public view returns(uint256[] memory) {\n uint indexCount = 0;\n for(uint i = startIndex; i <= endIndex; i++) {\n if(isEligibleForReward(i)) {\n indexCount++;\n }\n }\n uint256[] memory result = new uint256[](indexCount);\n uint currentIndex = 0;\n for(uint i = startIndex; i <= endIndex; i++) {\n if(isEligibleForReward(i)) {\n result[currentIndex++] = i;\n }\n }\n return result;\n }\n\n function isEligibleForReward(uint256 index) public view returns(bool) {\n uint8 status = raidStatus[index];\n return (status == STATUS_WON || status == STATUS_LOST)\n && raidParticipantIndices[index][msg.sender].length > 0\n && raidRewardClaimed[index][msg.sender] == false;\n }\n\n function getParticipatingCharacters() public view returns(uint256[] memory) {\n uint256[] memory indices = raidParticipantIndices[raidIndex][msg.sender];\n uint256[] memory chars = new uint256[](indices.length);\n for(uint i = 0; i < indices.length; i++) {\n chars[i] = raidParticipants[raidIndex][indices[i]].charID;\n }\n return chars;\n }\n\n function getParticipatingWeapons() public view returns(uint256[] memory) {\n uint256[] memory indices = raidParticipantIndices[raidIndex][msg.sender];\n uint256[] memory weps = new uint256[](indices.length);\n for(uint i = 0; i < indices.length; i++) {\n weps[i] = raidParticipants[raidIndex][indices[i]].wepID;\n }\n return weps;\n }\n\n function getAccountsRaiderIndexes(uint256 index) public view returns(uint256[] memory){\n return raidParticipantIndices[index][msg.sender];\n }\n\n function getAccountsPower(uint256 index) public view returns(uint256) {\n uint256 totalAccountPower = 0;\n uint256[] memory raiderIndexes = getAccountsRaiderIndexes(index);\n for(uint256 i = 0; i < raiderIndexes.length; i++) {\n totalAccountPower += raidParticipants[index][raiderIndexes[i]].power;\n }\n return totalAccountPower;\n }\n\n function canJoinRaid(uint256 characterID) public view returns(bool) {\n uint256 weaponID = EquipmentManager(links[LINK_EQUIPMENT_MANAGER]).equippedSlotID(address(characters), characterID, 1);\n return isRaidStarted()\n && haveEnoughEnergy(characterID)\n && !isCharacterRaiding(characterID)\n && !isWeaponRaiding(weaponID);\n }\n\n function haveEnoughEnergy(uint256 characterID) public view returns(bool) {\n return characters.getStaminaPoints(characterID) > 0;\n }\n\n function isRaidStarted() public view returns(bool) {\n return raidStatus[raidIndex] == STATUS_STARTED && raidEndTime[raidIndex] > now;\n }\n\n function isWeaponRaiding(uint256 weaponID) public view returns(bool) {\n uint256[] memory raiderIndices = raidParticipantIndices[raidIndex][msg.sender];\n for(uint i = 0; i < raiderIndices.length; i++) {\n if(raidParticipants[raidIndex][raiderIndices[i]].wepID == weaponID) {\n return true;\n }\n }\n\n return false;\n }\n\n function isCharacterRaiding(uint256 characterID) public view returns(bool) {\n uint256[] memory raiderIndices = raidParticipantIndices[raidIndex][msg.sender];\n for(uint i = 0; i < raiderIndices.length; i++) {\n if(raidParticipants[raidIndex][raiderIndices[i]].charID == characterID) {\n return true;\n }\n }\n\n return false;\n }\n\n function getLinkAddress(uint256 linkIndex) public view returns (address) {\n return links[linkIndex];\n }\n\n function getRaidData() public view returns(\n uint256 index, uint256 endTime, uint256 raiderCount, uint256 playerPower, uint256 bossPower,\n uint8 trait, uint8 status, uint256 joinSkill, uint64 stamina, uint64 durability, uint64 xp, uint256 accountPower\n ) {\n index = raidIndex;\n endTime = raidEndTime[raidIndex];\n raiderCount = getRaidParticipantCount(raidIndex);\n playerPower = getRaidPlayerPower(raidIndex);\n bossPower = getRaidBossPower(raidIndex);\n trait = getRaidBossTrait(raidIndex);\n status = getRaidStatus(raidIndex);\n joinSkill = getJoinCostInSkill();\n stamina = staminaCost;\n durability = durabilityCost;\n xp = xpReward;\n accountPower = getAccountsPower(raidIndex);\n }\n}\n"
},
"contracts/items/KeyLootbox.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"../Promos.sol\";\n\ncontract KeyLootbox is Initializable, ERC721Upgradeable, AccessControlUpgradeable {\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n Promos public promos;\n\n uint256 public nextTokenID;\n \n event Minted(uint256 indexed id, address indexed minter);\n\n function initialize (Promos _promos) public initializer {\n __ERC721_init(\"CryptoBlades Key Lootbox\", \"CBKBX\");\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n promos = _promos;\n }\n\n modifier restricted() {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n _;\n }\n\n function mint(address minter) public restricted returns(uint256) {\n\n uint256 tokenID = nextTokenID++;\n _mint(minter, tokenID);\n emit Minted(tokenID, minter);\n return tokenID;\n }\n\n function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override {\n require(promos.getBit(from, 4) == false && promos.getBit(to, 4) == false);\n }\n\n function getOwned() public view returns(uint256[] memory) {\n return getOwnedBy(msg.sender);\n }\n\n function getOwnedBy(address owner) public view returns(uint256[] memory) {\n uint256[] memory tokens = new uint256[](balanceOf(owner));\n for(uint256 i = 0; i < tokens.length; i++) {\n tokens[i] = tokenOfOwnerByIndex(owner, i);\n }\n return tokens;\n }\n\n function setNextTokenID(uint to) public restricted {\n nextTokenID = to;\n }\n\n function setBaseURI(string memory baseUri) public restricted {\n _setBaseURI(baseUri);\n }\n}"
},
"contracts/PvpCore.sol": {
"content": "pragma solidity ^0.6.0;\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/EnumerableSet.sol\";\nimport \"./interfaces/IRandoms.sol\";\nimport \"./cryptoblades.sol\";\nimport \"./characters.sol\";\nimport \"./weapons.sol\";\nimport \"./shields.sol\";\nimport \"./common.sol\";\nimport \"./PvpRankings.sol\";\nimport \"./EquipmentManager.sol\";\n\ncontract PvpCore is Initializable, AccessControlUpgradeable {\n using EnumerableSet for EnumerableSet.UintSet;\n using SafeMath for uint8;\n using SafeMath for uint24;\n using SafeMath for uint256;\n using ABDKMath64x64 for int128;\n using SafeERC20 for IERC20;\n\n struct Fighter {\n uint256 characterID;\n uint256 weaponID;\n uint256 shieldID;\n uint256 wager;\n bool useShield;\n }\n\n struct Match {\n uint256 attackerID;\n uint256 defenderID;\n uint256 createdAt;\n }\n\n struct Duelist {\n uint256 ID;\n uint8 level;\n uint8 trait;\n uint24 roll;\n uint256 power;\n }\n\n struct Duel {\n Duelist attacker;\n Duelist defender;\n uint8 tier;\n uint256 cost;\n bool attackerWon;\n uint256 bonusRank;\n }\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n CryptoBlades public game;\n Characters public characters;\n Weapons public weapons;\n Shields public shields;\n IERC20 public skillToken;\n IRandoms public randoms;\n PvpRankings public pvprankings;\n\n /// @dev the base amount wagered per duel in dollars\n int128 private _baseWagerUSD;\n /// @dev how much extra USD is wagered per level tier\n int128 private _tierWagerUSD;\n /// @dev how many times the cost of battling must be wagered to enter the arena\n uint8 public wageringFactor;\n /// @dev percentage of duel cost charged when rerolling opponent\n uint256 public reRollFeePercent;\n /// @dev percentage of entry wager charged when withdrawing from arena with pending duel\n uint256 public withdrawFeePercent;\n /// @dev amount of time a match finder has to make a decision\n uint256 public decisionSeconds;\n /// @dev allows or blocks entering arena (we can extend later to disable other parts such as rerolls)\n uint256 public arenaAccess; // 0 = cannot join, 1 = can join\n /// @dev value sent by players to offset bot's duel costs\n uint256 public duelOffsetCost;\n /// @dev PvP bot address\n address payable public pvpBotAddress;\n /// @dev characters by id that are on queue to perform a duel\n EnumerableSet.UintSet private _duelQueue;\n\n /// @dev Fighter by characterID\n mapping(uint256 => Fighter) public fighterByCharacter;\n /// @dev Active match by characterID of the finder\n mapping(uint256 => Match) public matchByFinder;\n /// @dev if character is currently in the arena\n mapping(uint256 => bool) public isCharacterInArena;\n /// @dev if weapon is currently in the arena\n mapping(uint256 => bool) public isWeaponInArena;\n /// @dev if shield is currently in the arena\n mapping(uint256 => bool) public isShieldInArena;\n /// @dev if defender is in a duel that has not finished processing\n mapping(uint256 => bool) public isDefending;\n /// @dev if a character is someone else's opponent\n mapping(uint256 => uint256) public finderByOpponent;\n /// @dev character's tier when it last entered arena. Used to reset rank if it changes\n mapping(uint256 => uint8) public previousTierByCharacter;\n /// @dev excess wager by character for when they re-enter the arena\n mapping(uint256 => uint256) public excessWagerByCharacter;\n /// @dev IDs of characters available for matchmaking by tier\n mapping(uint8 => EnumerableSet.UintSet) private _matchableCharactersByTier;\n /// @dev special weapon reroll timestamp\n mapping(uint256 => uint256) public specialWeaponRerollTimestamp;\n /// @dev owner's address by character ID\n mapping(uint256 => address) private _ownerByCharacter;\n\n \n uint256 public constant LINK_EQUIPMENT_MANAGER = 1;\n\n mapping(uint256 => address) private links;\n \n event DuelFinished(\n uint256 indexed attacker,\n uint256 indexed defender,\n uint256 timestamp,\n uint256 attackerRoll,\n uint256 defenderRoll,\n bool attackerWon,\n uint256 bonusRank\n );\n\n event CharacterKicked(\n uint256 indexed characterID,\n uint256 kickedBy,\n uint256 timestamp\n );\n\n modifier characterInArena(uint256 characterID) {\n _characterInArena(characterID);\n _;\n }\n\n function _characterInArena(uint256 characterID) internal view {\n require(isCharacterInArena[characterID], \"N\");\n }\n\n modifier characterWithinDecisionTime(uint256 characterID) {\n _characterWithinDecisionTime(characterID);\n _;\n }\n\n function _characterWithinDecisionTime(uint256 characterID) internal view {\n require(\n isCharacterWithinDecisionTime(characterID),\n \"D\"\n );\n }\n\n modifier characterNotUnderAttack(uint256 characterID) {\n _characterNotUnderAttack(characterID);\n _;\n }\n\n function _characterNotUnderAttack(uint256 characterID) internal view {\n require(!isCharacterUnderAttack(characterID), \"U\");\n }\n\n modifier characterNotInDuel(uint256 characterID) {\n _characterNotInDuel(characterID);\n _;\n }\n\n function _characterNotInDuel(uint256 characterID) internal view {\n require(!isCharacterInDuel(characterID), \"Q\");\n }\n\n modifier isOwnedCharacter(uint256 characterID) {\n require(_ownerByCharacter[characterID] == msg.sender);\n _;\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender));\n }\n\n function initialize(\n address gameContract,\n address shieldsContract,\n address randomsContract,\n address pvpRankingsContract\n ) public initializer {\n __AccessControl_init_unchained();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n game = CryptoBlades(gameContract);\n characters = Characters(game.characters());\n weapons = Weapons(game.weapons());\n shields = Shields(shieldsContract);\n skillToken = IERC20(game.skillToken());\n randoms = IRandoms(randomsContract);\n pvprankings = PvpRankings(pvpRankingsContract);\n\n _baseWagerUSD = ABDKMath64x64.divu(500, 100); // $5\n _tierWagerUSD = ABDKMath64x64.divu(50, 100); // $0.5\n wageringFactor = 3;\n reRollFeePercent = 25;\n withdrawFeePercent = 25;\n decisionSeconds = 2 minutes;\n duelOffsetCost = 0.005 ether;\n }\n\n /// @dev enter the arena with a character, a weapon and optionally a shield\n function enterArena(uint256 characterID, bool tierless) external {\n\n EquipmentManager equipment = EquipmentManager(links[LINK_EQUIPMENT_MANAGER]);\n uint256 weaponID = equipment.equippedSlotID(address(characters), characterID, 1);\n uint256 shieldID = equipment.equippedSlotID(address(characters), characterID, 2);\n bool useShield = equipment.hasSlotEquipped(address(characters), characterID, 2);\n\n require(characters.ownerOf(characterID) == msg.sender\n && characters.getNftVar(characterID, 1) == 0);\n require(characters.isEquipmentReady(characterID));\n\n require((arenaAccess & 1) == 1, \"L\");\n\n uint8 tier;\n if (tierless) {\n tier = 20;\n } else {\n tier = getArenaTier(characterID);\n }\n\n uint256 wager = getEntryWagerByTier(tier);\n\n if (_ownerByCharacter[characterID] != msg.sender) {\n _ownerByCharacter[characterID] = msg.sender;\n }\n\n if (previousTierByCharacter[characterID] != tier) {\n pvprankings.changeRankingPoints(characterID, 0);\n }\n\n pvprankings.handleEnterArena(msg.sender, characterID, tier);\n\n isCharacterInArena[characterID] = true;\n characters.setNftVar(characterID, 1, 1);\n\n uint256 characterWager;\n\n if (excessWagerByCharacter[characterID] != 0) {\n characterWager = excessWagerByCharacter[characterID];\n } else {\n characterWager = fighterByCharacter[characterID].wager;\n }\n\n _matchableCharactersByTier[tier].add(characterID);\n fighterByCharacter[characterID] = Fighter(\n characterID,\n weaponID,\n shieldID,\n wager.add(characterWager),\n useShield\n );\n previousTierByCharacter[characterID] = tier;\n excessWagerByCharacter[characterID] = 0;\n\n skillToken.transferFrom(msg.sender, address(this), wager);\n }\n\n /// @dev withdraws a character and its items from the arena.\n /// if the character is in a battle, a penalty is charged\n function withdrawFromArena(uint256 characterID)\n external\n isOwnedCharacter(characterID)\n characterInArena(characterID)\n characterNotUnderAttack(characterID)\n characterNotInDuel(characterID)\n {\n Fighter storage fighter = fighterByCharacter[characterID];\n uint256 wager = fighter.wager;\n\n uint8 tier;\n \n if (previousTierByCharacter[characterID] == 20) {\n tier = 20;\n } else {\n tier = getArenaTier(characterID);\n }\n\n uint256 entryWager = getEntryWager(characterID);\n\n if (matchByFinder[characterID].createdAt != 0) {\n if (wager < entryWager.mul(withdrawFeePercent).div(100)) {\n wager = 0;\n } else {\n wager = wager.sub(entryWager.mul(withdrawFeePercent).div(100));\n }\n }\n\n _removeCharacterFromArena(characterID, tier);\n\n excessWagerByCharacter[characterID] = 0;\n fighter.wager = 0;\n\n skillToken.safeTransfer(msg.sender, wager);\n }\n\n /// @dev attempts to find an opponent for a character\n function findOpponent(uint256 characterID)\n external\n isOwnedCharacter(characterID)\n characterInArena(characterID)\n characterNotUnderAttack(characterID)\n characterNotInDuel(characterID)\n {\n require(matchByFinder[characterID].createdAt == 0, \"M\");\n\n uint8 tier;\n\n if (previousTierByCharacter[characterID] == 20) {\n tier = 20;\n } else {\n tier = getArenaTier(characterID);\n }\n\n _assignOpponent(characterID, tier);\n }\n\n /// @dev attempts to find a new opponent for a fee\n function reRollOpponent(uint256 characterID)\n external\n characterInArena(characterID)\n characterNotUnderAttack(characterID)\n isOwnedCharacter(characterID)\n characterNotInDuel(characterID)\n {\n uint256 opponentID = getOpponent(characterID);\n uint8 tier;\n \n if (previousTierByCharacter[characterID] == 20) {\n tier = 20;\n } else {\n tier = getArenaTier(characterID);\n }\n\n require(matchByFinder[characterID].createdAt != 0, \"R\");\n\n delete finderByOpponent[opponentID];\n if (isCharacterInArena[opponentID]) {\n _matchableCharactersByTier[tier].add(opponentID);\n }\n\n _assignOpponent(characterID, tier);\n\n uint256 weaponID = fighterByCharacter[characterID].weaponID;\n if(weapons.getWeaponType(weaponID) > 0 && specialWeaponRerollTimestamp[weaponID] < block.timestamp) {\n specialWeaponRerollTimestamp[weaponID] = block.timestamp + 24 hours;\n }\n else {\n skillToken.transferFrom(\n msg.sender,\n address(this),\n getDuelCostByTier(tier).mul(reRollFeePercent).div(100)\n );\n }\n }\n\n /// @dev adds a character to the duel queue\n function prepareDuel(uint256 attackerID)\n external\n payable\n isOwnedCharacter(attackerID)\n characterInArena(attackerID)\n characterWithinDecisionTime(attackerID)\n characterNotInDuel(attackerID)\n {\n require((arenaAccess & 1) == 1);\n require(msg.value == duelOffsetCost, \"O\");\n\n uint256 defenderID = getOpponent(attackerID);\n\n pvprankings.handlePrepareDuel(msg.sender, attackerID);\n\n pvprankings.handlePrepareDuel(msg.sender, defenderID);\n\n isDefending[defenderID] = true;\n\n _duelQueue.add(attackerID);\n\n pvpBotAddress.transfer(msg.value);\n }\n\n function createDuelist(uint256 id) internal view returns (Duelist memory duelist) {\n duelist.ID = id;\n\n (\n , // xp\n duelist.level,\n duelist.trait,\n , // staminaTimestamp\n , // head\n , // torso\n , // legs\n , // boots\n , // race\n ) = characters.get(id);\n }\n\n /// @dev performs a list of duels\n function performDuels(uint256[] calldata attackerIDs) external restricted {\n for (uint256 i = 0; i < attackerIDs.length; i++) {\n Duel memory duel;\n duel.attacker = createDuelist(attackerIDs[i]);\n\n if (!_duelQueue.contains(duel.attacker.ID)) continue;\n\n duel.defender = createDuelist(getOpponent(duel.attacker.ID));\n\n if (previousTierByCharacter[duel.attacker.ID] == 20) {\n duel.tier = 20;\n } else {\n duel.tier = getArenaTierForLevel(duel.attacker.level);\n }\n\n duel.cost = getDuelCostByTier(duel.tier);\n\n duel.attacker.power = getCharacterPower(duel.attacker.ID, duel.tier);\n duel.defender.power = getCharacterPower(duel.defender.ID, duel.tier);\n\n duel.attacker.roll = _getCharacterPowerRoll(duel.attacker, duel.defender.trait, duel.tier);\n duel.defender.roll = _getCharacterPowerRoll(duel.defender, duel.attacker.trait, duel.tier);\n\n // Reduce defender roll if attacker has a shield\n if (fighterByCharacter[duel.attacker.ID].useShield) {\n uint24 attackerShieldDefense = 3;\n\n uint8 attackerShieldTrait = shields.getTrait(\n fighterByCharacter[duel.attacker.ID].shieldID\n );\n\n if (\n Common.isTraitEffectiveAgainst(attackerShieldTrait, duel.defender.trait)\n ) {\n attackerShieldDefense = 10;\n }\n\n duel.defender.roll = uint24(\n (duel.defender.roll.mul(uint24(100).sub(attackerShieldDefense)))\n .div(100)\n );\n }\n\n // Reduce attacker roll if defender has a shield\n if (fighterByCharacter[duel.defender.ID].useShield) {\n uint24 defenderShieldDefense = 3;\n\n uint8 defenderShieldTrait = shields.getTrait(\n fighterByCharacter[duel.defender.ID].shieldID\n );\n\n if (\n Common.isTraitEffectiveAgainst(defenderShieldTrait, duel.attacker.trait)\n ) {\n defenderShieldDefense = 10;\n }\n\n duel.attacker.roll = uint24(\n (duel.attacker.roll.mul(uint24(100).sub(defenderShieldDefense)))\n .div(100)\n );\n }\n\n duel.attackerWon = (duel.attacker.roll >= duel.defender.roll);\n\n uint256 winnerID = duel.attackerWon\n ? duel.attacker.ID\n : duel.defender.ID;\n uint256 loserID = duel.attackerWon\n ? duel.defender.ID\n : duel.attacker.ID;\n\n if (winnerID == duel.attacker.ID && duel.attacker.power < duel.defender.power) {\n duel.bonusRank = Common.getBonusRankingPoints(duel.attacker.power, duel.defender.power);\n } else if (winnerID == duel.defender.ID && duel.attacker.power > duel.defender.power) {\n duel.bonusRank = Common.getBonusRankingPoints(duel.defender.power, duel.attacker.power); \n }\n\n emit DuelFinished(\n duel.attacker.ID,\n duel.defender.ID,\n block.timestamp,\n duel.attacker.roll,\n duel.defender.roll,\n duel.attackerWon,\n duel.bonusRank\n );\n\n (\n uint256 reward,\n uint256 poolTax\n ) = pvprankings.getDuelBountyDistribution(duel.cost);\n\n fighterByCharacter[winnerID].wager = fighterByCharacter[winnerID]\n .wager\n .add(reward);\n\n uint256 loserWager;\n\n if (\n fighterByCharacter[loserID].wager <\n duel.cost\n ) {\n loserWager = 0;\n } else {\n loserWager = fighterByCharacter[loserID].wager.sub(\n duel.cost\n );\n }\n\n fighterByCharacter[loserID].wager = loserWager;\n\n delete matchByFinder[duel.attacker.ID];\n delete finderByOpponent[duel.defender.ID];\n isDefending[duel.defender.ID] = false;\n\n if (\n fighterByCharacter[loserID].wager < duel.cost ||\n fighterByCharacter[loserID].wager <\n getEntryWagerByTier(duel.tier).mul(withdrawFeePercent).div(100)\n ) {\n _removeCharacterFromArena(loserID, duel.tier);\n emit CharacterKicked(\n loserID,\n winnerID,\n block.timestamp\n );\n } else {\n _matchableCharactersByTier[duel.tier].add(loserID);\n }\n\n _matchableCharactersByTier[duel.tier].add(winnerID);\n\n pvprankings.handlePerformDuel(winnerID, loserID, duel.bonusRank, duel.tier, poolTax);\n\n skillToken.safeTransfer(address(pvprankings), poolTax);\n\n _duelQueue.remove(duel.attacker.ID);\n }\n }\n\n /// @dev wether or not the character is still in time to start a duel\n function isCharacterWithinDecisionTime(uint256 characterID)\n internal\n view\n returns (bool)\n {\n return\n matchByFinder[characterID].createdAt.add(decisionSeconds) >\n block.timestamp;\n }\n\n /// @dev checks wether or not the character is actively someone else's opponent\n function isCharacterUnderAttack(uint256 characterID)\n public\n view\n returns (bool)\n {\n if (finderByOpponent[characterID] == 0) {\n if (matchByFinder[0].defenderID == characterID) {\n return isCharacterWithinDecisionTime(0);\n }\n return false;\n }\n\n return isCharacterWithinDecisionTime(finderByOpponent[characterID]);\n }\n\n /// @dev checks wether or not the character is currently in the duel queue\n function isCharacterInDuel(uint256 characterID)\n internal\n view\n returns (bool)\n {\n return _duelQueue.contains(characterID) || isDefending[characterID];\n }\n\n /// @dev gets the amount of SKILL required to enter the arena\n function getEntryWager(uint256 characterID) public view returns (uint256) {\n return getDuelCost(characterID).mul(wageringFactor);\n }\n\n /// @dev gets the amount of SKILL required to enter the arena by tier\n function getEntryWagerByTier(uint8 tier) public view returns (uint256) {\n return getDuelCostByTier(tier).mul(wageringFactor);\n }\n\n /// @dev gets the amount of SKILL that is risked per duel\n function getDuelCost(uint256 characterID) public view returns (uint256) {\n if (previousTierByCharacter[characterID] == 20) {\n return game.usdToSkill(_baseWagerUSD);\n }\n\n int128 tierExtra = ABDKMath64x64\n .divu(getArenaTier(characterID).mul(100), 100)\n .mul(_tierWagerUSD);\n\n return game.usdToSkill(_baseWagerUSD.add(tierExtra));\n }\n\n /// @dev gets the amount of SKILL that is risked per duel by tier\n function getDuelCostByTier(uint8 tier) internal view returns (uint256) {\n if (tier == 20) {\n return game.usdToSkill(_baseWagerUSD);\n }\n\n int128 tierExtra = ABDKMath64x64\n .divu(tier.mul(100), 100)\n .mul(_tierWagerUSD);\n\n return game.usdToSkill(_baseWagerUSD.add(tierExtra));\n }\n\n /// @dev gets the arena tier of a character (tiers are 1-10, 11-20, etc...)\n function getArenaTier(uint256 characterID) public view returns (uint8) {\n uint8 level = characters.getLevel(characterID);\n return getArenaTierForLevel(level);\n }\n\n function getArenaTierForLevel(uint8 level) internal pure returns (uint8) {\n return uint8(level.div(10));\n }\n\n /// @dev get an attacker's opponent\n function getOpponent(uint256 attackerID) public view returns (uint256) {\n return matchByFinder[attackerID].defenderID;\n }\n\n /// @dev returns the current duel queue\n function getDuelQueue() public view returns (uint256[] memory) {\n uint256 length = _duelQueue.length();\n uint256[] memory values = new uint256[](length);\n\n for (uint256 i = 0; i < length; i++) {\n values[i] = _duelQueue.at(i);\n }\n\n return values;\n }\n\n /// @dev assigns an opponent to a character\n function _assignOpponent(uint256 characterID, uint8 tier) private {\n EnumerableSet.UintSet\n storage matchableCharacters = _matchableCharactersByTier[tier];\n\n require(matchableCharacters.length() != 0, \"L\");\n\n uint256 seed = randoms.getRandomSeed(msg.sender);\n uint256 randomIndex = RandomUtil.randomSeededMinMax(\n 0,\n matchableCharacters.length() - 1,\n seed\n );\n uint256 opponentID;\n uint256 matchableCharactersCount = matchableCharacters.length();\n bool foundOpponent = false;\n\n for (uint256 i = 0; i < matchableCharactersCount; i++) {\n uint256 index = (randomIndex + i) % matchableCharactersCount;\n uint256 candidateID = matchableCharacters.at(index);\n\n if (candidateID == characterID) {\n if (matchableCharactersCount == 1) {\n break;\n }\n if (\n matchableCharacters.at(matchableCharactersCount - 1) ==\n candidateID\n ) {\n candidateID = matchableCharacters.at(0);\n } else {\n candidateID = matchableCharacters.at(index + 1);\n }\n }\n if (\n _ownerByCharacter[candidateID] == msg.sender\n ) {\n continue;\n }\n\n foundOpponent = true;\n opponentID = candidateID;\n break;\n }\n\n require(foundOpponent, \"E\");\n\n matchByFinder[characterID] = Match(\n characterID,\n opponentID,\n block.timestamp\n );\n finderByOpponent[opponentID] = characterID;\n _matchableCharactersByTier[tier].remove(characterID);\n _matchableCharactersByTier[tier].remove(opponentID);\n }\n\n /// @dev removes a character from arena and clears it's matches\n function _removeCharacterFromArena(uint256 characterID, uint8 tier)\n private\n characterInArena(characterID)\n {\n Fighter storage fighter = fighterByCharacter[characterID];\n\n uint256 weaponID = fighter.weaponID;\n uint256 shieldID = fighter.shieldID;\n\n excessWagerByCharacter[characterID] = fighter.wager;\n\n // Shield removed first before the fighter is deleted\n if (fighter.useShield) {\n isShieldInArena[shieldID] = false;\n shields.setNftVar(shieldID, 1, 0);\n }\n\n delete fighterByCharacter[characterID];\n delete matchByFinder[characterID];\n\n if (_matchableCharactersByTier[tier].contains(characterID)) {\n _matchableCharactersByTier[tier].remove(characterID);\n }\n\n isCharacterInArena[characterID] = false;\n isWeaponInArena[weaponID] = false;\n\n characters.setNftVar(characterID, 1, 0);\n weapons.setNftVar(weaponID, 1, 0);\n }\n\n function _getCharacterPowerRoll(Duelist memory character, uint8 opponentTrait, uint8 tier)\n private\n view\n returns (uint24)\n {\n uint24 playerFightPower = getCharacterPower(character.ID, tier);\n\n Fighter memory fighter = fighterByCharacter[character.ID];\n uint256 weaponID = fighter.weaponID;\n uint256 seed = randoms.getRandomSeedUsingHash(\n _ownerByCharacter[character.ID],\n blockhash(block.number - 1)\n );\n\n uint8 weaponTrait = weapons.getTrait(weaponID);\n\n int128 playerTraitBonus = _getPVPTraitBonusAgainst(\n character.trait,\n weaponTrait,\n opponentTrait\n );\n\n uint256 playerPower = RandomUtil.plusMinus10PercentSeeded(\n playerFightPower,\n seed\n );\n\n return uint24(playerTraitBonus.mulu(playerPower));\n }\n\n function getCharacterPower(uint256 characterID, uint8 tier)\n public\n view\n returns (uint24) \n {\n int128 bonusShieldStats;\n \n (\n ,\n int128 weaponMultFight,\n uint24 weaponBonusPower,\n \n ) = weapons.getFightData(fighterByCharacter[characterID].weaponID, characters.getTrait(characterID));\n\n if (fighterByCharacter[characterID].useShield) {\n // we set bonus shield stats as 0.2\n bonusShieldStats = _getShieldStats(characterID).sub(1).mul(20).div(100);\n }\n\n uint24 power;\n\n if (tier == 20) {\n power = Common.getPowerAtLevel(34);\n } else {\n power = Common.getPowerAtLevel(characters.getLevel(characterID));\n }\n\n return ( \n Common.getPlayerPowerBase100(\n power,\n (weaponMultFight.add(bonusShieldStats)),\n weaponBonusPower)\n );\n }\n\n /// @dev returns the amount of matcheable characters\n function getMatchablePlayerCount(uint256 characterID) external view returns(uint) {\n uint8 tier;\n \n if (previousTierByCharacter[characterID] == 20) {\n tier = 20;\n } else {\n tier = getArenaTier(characterID);\n }\n return _matchableCharactersByTier[tier].length(); \n }\n\n function getMatchablePlayerCountByTier(uint8 tier) external view returns (uint) {\n return _matchableCharactersByTier[tier].length();\n }\n\n function isCharacterMatchableByTier(uint256 characterID, uint8 tier) external view returns (bool) {\n return _matchableCharactersByTier[tier].contains(characterID);\n }\n\n function _getPVPTraitBonusAgainst(\n uint8 characterTrait,\n uint8 weaponTrait,\n uint8 opponentTrait\n ) private view returns (int128) {\n int128 traitBonus = ABDKMath64x64.fromUInt(1);\n int128 fightTraitBonus = game.fightTraitBonus();\n int128 charTraitFactor = ABDKMath64x64.divu(50, 100);\n if (characterTrait == weaponTrait) {\n traitBonus = traitBonus.add(fightTraitBonus.mul(3));\n }\n\n // We apply 50% of char trait bonuses because they are applied twice (once per fighter)\n if (\n Common.isTraitEffectiveAgainst(characterTrait, opponentTrait)\n ) {\n traitBonus = traitBonus.add(fightTraitBonus.mul(charTraitFactor));\n } else if (\n Common.isTraitEffectiveAgainst(opponentTrait, characterTrait)\n ) {\n traitBonus = traitBonus.sub(fightTraitBonus.mul(charTraitFactor));\n }\n return traitBonus;\n }\n\n function _getShieldStats(uint256 characterID)\n private\n view\n returns (int128)\n {\n uint8 trait = characters.getTrait(characterID);\n uint256 shieldID = fighterByCharacter[characterID].shieldID;\n int128 shieldMultFight = shields.getDefenseMultiplierForTrait(shieldID, trait);\n return (shieldMultFight);\n }\n\n function setBaseWagerInCents(uint256 cents) external restricted {\n _baseWagerUSD = ABDKMath64x64.divu(cents, 100);\n }\n\n function setTierWagerInCents(uint256 cents) external restricted {\n _tierWagerUSD = ABDKMath64x64.divu(cents, 100);\n }\n\n function setWageringFactor(uint8 factor) external restricted {\n wageringFactor = factor;\n }\n\n function setReRollFeePercent(uint256 percent) external restricted {\n reRollFeePercent = percent;\n }\n\n function setWithdrawFeePercent(uint256 percent) external restricted {\n withdrawFeePercent = percent;\n }\n\n function setDecisionSeconds(uint256 secs) external restricted {\n decisionSeconds = secs;\n }\n\n function setArenaAccess(uint256 accessFlags) external restricted {\n arenaAccess = accessFlags;\n }\n\n function setDuelOffsetCost(uint256 cost) external restricted {\n duelOffsetCost = cost;\n }\n\n function setPvpBotAddress(address payable botAddress) external restricted {\n pvpBotAddress = botAddress;\n }\n\n function setLink(uint256 linkId, address linkAddress) external restricted {\n links[linkId] = linkAddress;\n }\n\n // Note: The following are debugging functions, they can be muted to save contract size\n\n function forceRemoveCharacterFromArena(uint256 characterID)\n external \n restricted\n characterNotUnderAttack(characterID)\n characterNotInDuel(characterID)\n {\n Fighter storage fighter = fighterByCharacter[characterID];\n uint8 tier;\n \n if (previousTierByCharacter[characterID] == 20) {\n tier = 20;\n } else {\n tier = getArenaTier(characterID);\n }\n\n uint256 wager = fighter.wager;\n uint256 entryWager = getEntryWager(characterID);\n\n if (matchByFinder[characterID].createdAt != 0) {\n if (wager < entryWager.mul(withdrawFeePercent).div(100)) {\n wager = 0;\n } else {\n wager = wager.sub(entryWager.mul(withdrawFeePercent).div(100));\n }\n }\n\n _removeCharacterFromArena(characterID, tier);\n\n excessWagerByCharacter[characterID] = 0;\n fighter.wager = 0;\n\n skillToken.safeTransfer(characters.ownerOf(characterID), wager);\n }\n\n // function clearDuelQueue(uint256 length) external restricted {\n // for (uint256 i = 0; i < length; i++) {\n // if (matchByFinder[_duelQueue.at(i)].defenderID > 0) {\n // isDefending[matchByFinder[_duelQueue.at(i)].defenderID] = false;\n // }\n\n // _duelQueue.remove(_duelQueue.at(i));\n // }\n\n // isDefending[0] = false;\n // }\n}\n"
},
"contracts/PvpRankings.sol": {
"content": "pragma solidity ^0.6.0;\n// TODO: Clean unused imports after splitting contract\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"./cryptoblades.sol\";\nimport \"./characters.sol\";\nimport \"./PvpCore.sol\";\n\ncontract PvpRankings is Initializable, AccessControlUpgradeable {\n using SafeMath for uint8;\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n CryptoBlades public game;\n Characters public characters;\n IERC20 public skillToken;\n PvpCore public pvpcore;\n\n /// @dev how much of a duel's bounty is sent to the rankings pool\n uint8 private _rankingsPoolTaxPercent;\n /// @dev amount of points earned by winning a duel\n uint8 public winningPoints;\n /// @dev amount of points subtracted by losing duel\n uint8 public losingPoints;\n /// @dev max amount of top characters by tier\n uint8 private _maxTopCharactersPerTier;\n /// @dev current ranked season\n uint256 public currentRankedSeason;\n /// @dev timestamp of when the current season started\n uint256 public seasonStartedAt;\n /// @dev interval of ranked season restarts\n uint256 public seasonDuration;\n /// @dev amount of skill due for game coffers from tax\n uint256 public gameCofferTaxDue;\n /// @dev percentages of ranked prize distribution by fighter rank (represented as index)\n uint256[] public prizePercentages;\n\n /// @dev season number associated to character\n mapping(uint256 => uint256) public seasonByCharacter;\n /// @dev ranking points by character\n mapping(uint256 => uint256) public rankingPointsByCharacter;\n /// @dev accumulated skill pool per tier\n mapping(uint8 => uint256) public rankingsPoolByTier;\n /// @dev funds available for withdrawal by address\n mapping(address => uint256) private _rankingRewardsByPlayer;\n /// @dev top ranking characters by tier\n mapping(uint8 => uint256[]) private _topRankingCharactersByTier;\n /// @dev owner's address by character ID\n mapping(uint256 => address) private _ownerByCharacter;\n\n\n event SeasonRestarted(\n uint256 indexed newSeason,\n uint256 timestamp\n );\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender));\n }\n\n function initialize(\n address gameContract\n ) public initializer {\n __AccessControl_init_unchained();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n game = CryptoBlades(gameContract);\n characters = Characters(game.characters());\n skillToken = IERC20(game.skillToken());\n\n _rankingsPoolTaxPercent = 15;\n winningPoints = 5;\n losingPoints = 3;\n _maxTopCharactersPerTier = 4;\n currentRankedSeason = 1;\n seasonStartedAt = block.timestamp;\n seasonDuration = 7 days;\n prizePercentages.push(60);\n prizePercentages.push(30);\n prizePercentages.push(10);\n }\n\n function withdrawRankedRewards() external {\n uint256 amountToTransfer = _rankingRewardsByPlayer[msg.sender];\n\n if (amountToTransfer > 0) {\n _rankingRewardsByPlayer[msg.sender] = 0;\n\n skillToken.safeTransfer(msg.sender, amountToTransfer);\n }\n }\n\n function restartRankedSeason() external restricted {\n uint256[] memory duelQueue = pvpcore.getDuelQueue();\n\n if (duelQueue.length > 0) {\n pvpcore.performDuels(duelQueue);\n }\n\n // Loops over 20 tiers. Should not be reachable anytime in the foreseeable future\n for (uint8 i = 0; i <= 20; i++) {\n if (_topRankingCharactersByTier[i].length == 0) {\n continue;\n }\n\n uint256 difference = 0;\n\n if (\n _topRankingCharactersByTier[i].length <= prizePercentages.length\n ) {\n difference =\n prizePercentages.length -\n _topRankingCharactersByTier[i].length;\n }\n\n // If there are less players than top positions, excess is transferred to top 1\n if (\n _topRankingCharactersByTier[i].length < prizePercentages.length\n ) {\n uint256 excessPercentage;\n \n address topOnePlayer = _ownerByCharacter[_topRankingCharactersByTier[i][0]];\n\n // We accumulate excess percentage\n for (\n uint256 j = prizePercentages.length - difference;\n j < prizePercentages.length;\n j++\n ) {\n excessPercentage = excessPercentage.add(\n prizePercentages[j]\n );\n }\n\n // We assign excessive rewards to top 1 player\n _rankingRewardsByPlayer[topOnePlayer] = _rankingRewardsByPlayer[\n topOnePlayer\n ].add((rankingsPoolByTier[i].mul(excessPercentage)).div(100));\n }\n\n // We assign rewards normally to all possible players\n for (uint8 h = 0; h < prizePercentages.length - difference; h++) {\n _assignRewards(\n _topRankingCharactersByTier[i][h],\n h,\n rankingsPoolByTier[i]\n );\n }\n\n // We reset ranking prize pools\n rankingsPoolByTier[i] = 0;\n \n // We reset top players' scores\n for (uint256 k = 0; k < _topRankingCharactersByTier[i].length; k++) {\n rankingPointsByCharacter[_topRankingCharactersByTier[i][k]] = 0;\n }\n }\n\n currentRankedSeason = currentRankedSeason.add(1);\n seasonStartedAt = block.timestamp;\n\n emit SeasonRestarted(\n currentRankedSeason,\n seasonStartedAt\n );\n }\n\n // Note: Manual method in case of issues, remove when not needed.\n function forceAssignRewards(\n uint256 characterID,\n uint8 position,\n uint256 pool\n ) external restricted {\n uint256 percentage = prizePercentages[position];\n uint256 amountToTransfer = (pool.mul(percentage)).div(100);\n address playerToTransfer = _ownerByCharacter[characterID];\n\n _rankingRewardsByPlayer[playerToTransfer] = _rankingRewardsByPlayer[\n playerToTransfer\n ].add(amountToTransfer);\n }\n\n // Note: Manual method in case of issues, remove when not needed.\n function changeRankingRewards(\n uint256 characterID,\n uint256 amount\n ) external restricted {\n address playerToTransfer = _ownerByCharacter[characterID];\n\n _rankingRewardsByPlayer[playerToTransfer] = amount;\n }\n\n // Note: Manual method in case of issues, remove when not needed.\n function getRankingRewards(\n uint256 characterID\n ) external restricted view returns (uint256) {\n address player = _ownerByCharacter[characterID];\n\n return _rankingRewardsByPlayer[player];\n }\n\n // Note: Manual method in case of issues, remove when not needed.\n function clearTierTopCharacters(uint8 tier) external restricted {\n for (uint256 k = 0; k < _topRankingCharactersByTier[tier].length; k++) {\n rankingPointsByCharacter[_topRankingCharactersByTier[tier][k]] = 0;\n }\n delete _topRankingCharactersByTier[tier];\n rankingsPoolByTier[tier] = 0;\n }\n\n function _processWinner(uint256 winnerID, uint8 tier) private {\n uint256 rankingPoints = rankingPointsByCharacter[winnerID];\n uint256[] storage topRankingCharacters = _topRankingCharactersByTier[\n tier\n ];\n uint256 winnerPosition;\n bool winnerInRanking;\n\n // check if winner is withing the top 4\n for (uint8 i = 0; i < topRankingCharacters.length; i++) {\n if (winnerID == topRankingCharacters[i]) {\n winnerPosition = i;\n winnerInRanking = true;\n break;\n }\n }\n \n // if the winner is not in the top characters we then compare it to the last character of the top rank, swapping positions if the condition is met\n if (\n !winnerInRanking &&\n rankingPoints >=\n rankingPointsByCharacter[\n topRankingCharacters[topRankingCharacters.length - 1]\n ]\n ) {\n topRankingCharacters[topRankingCharacters.length - 1] = winnerID;\n winnerPosition = topRankingCharacters.length - 1;\n }\n\n for (winnerPosition; winnerPosition > 0; winnerPosition--) {\n if (\n rankingPointsByCharacter[\n topRankingCharacters[winnerPosition]\n ] >=\n rankingPointsByCharacter[\n topRankingCharacters[winnerPosition - 1]\n ]\n ) {\n uint256 oldCharacter = topRankingCharacters[winnerPosition - 1];\n topRankingCharacters[winnerPosition - 1] = winnerID;\n topRankingCharacters[winnerPosition] = oldCharacter;\n } else {\n break;\n }\n }\n }\n\n function _processLoser(uint256 loserID, uint8 tier) private {\n uint256 rankingPoints = rankingPointsByCharacter[loserID];\n uint256[] storage ranking = _topRankingCharactersByTier[tier];\n uint256 loserPosition;\n bool loserFound;\n\n // check if the loser is in the top 4\n for (uint8 i = 0; i < ranking.length; i++) {\n if (loserID == ranking[i]) {\n loserPosition = i;\n loserFound = true;\n break;\n }\n }\n // if the character is within the top 4, compare it to the character that precedes it and swap positions if the condition is met\n if (loserFound) {\n for (\n loserPosition;\n loserPosition < ranking.length - 1;\n loserPosition++\n ) {\n if (\n rankingPoints <\n rankingPointsByCharacter[ranking[loserPosition + 1]]\n ) {\n uint256 oldCharacter = ranking[loserPosition + 1];\n ranking[loserPosition + 1] = loserID;\n ranking[loserPosition] = oldCharacter;\n } else {\n break;\n }\n }\n }\n }\n\n function getTierTopCharacters(uint8 tier)\n external\n view\n returns (uint256[] memory)\n {\n uint256 arrayLength;\n // we return only the top 3 players, returning the array without the pivot ranker if it exists\n if (\n _topRankingCharactersByTier[tier].length == _maxTopCharactersPerTier\n ) {\n arrayLength = _topRankingCharactersByTier[tier].length - 1;\n } else {\n arrayLength = _topRankingCharactersByTier[tier].length;\n }\n uint256[] memory topRankers = new uint256[](arrayLength);\n for (uint256 i = 0; i < arrayLength; i++) {\n topRankers[i] = _topRankingCharactersByTier[tier][i];\n }\n\n return topRankers;\n }\n\n function getPlayerPrizePoolRewards() external view returns (uint256) {\n return _rankingRewardsByPlayer[msg.sender];\n }\n\n function _assignRewards(\n uint256 characterID,\n uint8 position,\n uint256 pool\n ) private {\n uint256 percentage = prizePercentages[position];\n uint256 amountToTransfer = (pool.mul(percentage)).div(100);\n address playerToTransfer = _ownerByCharacter[characterID];\n\n _rankingRewardsByPlayer[playerToTransfer] = _rankingRewardsByPlayer[\n playerToTransfer\n ].add(amountToTransfer);\n }\n\n function getDuelBountyDistribution(uint256 duelCost)\n external\n view\n returns (uint256, uint256)\n {\n uint256 bounty = duelCost.mul(2);\n uint256 poolTax = _rankingsPoolTaxPercent.mul(bounty).div(100);\n\n uint256 reward = bounty.sub(poolTax).sub(duelCost);\n\n return (reward, poolTax);\n }\n\n function fillGameCoffers() external restricted {\n skillToken.safeTransfer(address(game), gameCofferTaxDue);\n game.trackIncome(gameCofferTaxDue);\n gameCofferTaxDue = 0;\n }\n\n function increaseRankingsPool(uint8 tier, uint256 amount) external restricted {\n rankingsPoolByTier[tier] = rankingsPoolByTier[tier].add(amount);\n }\n\n function changeRankingPoints(uint256 characterID, uint256 points) external restricted {\n rankingPointsByCharacter[characterID] = points;\n }\n\n function handleEnterArena(address fighter, uint256 characterID, uint8 tier) external restricted {\n bool isCharacterInTopRanks;\n \n for (uint i = 0; i < _topRankingCharactersByTier[tier].length; i++) {\n if (characterID == _topRankingCharactersByTier[tier][i]) {\n isCharacterInTopRanks = true;\n }\n }\n\n if (\n _topRankingCharactersByTier[tier].length <\n _maxTopCharactersPerTier && !isCharacterInTopRanks\n ) {\n _topRankingCharactersByTier[tier].push(characterID);\n }\n\n if (seasonByCharacter[characterID] != currentRankedSeason) {\n rankingPointsByCharacter[characterID] = 0;\n seasonByCharacter[characterID] = currentRankedSeason;\n }\n\n if (_ownerByCharacter[characterID] != fighter) {\n _ownerByCharacter[characterID] = fighter;\n }\n }\n\n function handlePrepareDuel(address fighter, uint256 characterID) external restricted {\n if (seasonByCharacter[characterID] != currentRankedSeason) {\n rankingPointsByCharacter[characterID] = 0;\n seasonByCharacter[characterID] = currentRankedSeason;\n }\n\n if (_ownerByCharacter[characterID] != fighter) {\n _ownerByCharacter[characterID] = fighter;\n }\n }\n\n function handlePerformDuel(uint256 winnerID, uint256 loserID, uint256 bonusRank, uint8 tier, uint256 poolTax) external restricted {\n rankingPointsByCharacter[winnerID] = rankingPointsByCharacter[\n winnerID\n ].add(winningPoints.add(bonusRank));\n\n // Mute the ranking loss from users in pvpRankings\n // if (rankingPointsByCharacter[loserID] <= losingPoints) {\n // rankingPointsByCharacter[loserID] = 0;\n // } else {\n // rankingPointsByCharacter[loserID] = rankingPointsByCharacter[\n // loserID\n // ].sub(losingPoints);\n // }\n\n _processWinner(winnerID, tier);\n _processLoser(loserID, tier);\n\n rankingsPoolByTier[tier] = rankingsPoolByTier[\n tier\n ].add(poolTax / 2);\n\n gameCofferTaxDue += poolTax / 2;\n }\n\n // SETTERS\n\n function setPrizePercentage(uint256 index, uint256 value)\n external\n restricted\n {\n prizePercentages[index] = value;\n }\n\n function setRankingsPoolTaxPercent(uint8 percent) external restricted {\n _rankingsPoolTaxPercent = percent;\n }\n\n function setWinningPoints(uint8 pts) external restricted {\n winningPoints = pts;\n }\n\n function setLosingPoints(uint8 pts) external restricted {\n losingPoints = pts;\n }\n\n function setMaxTopCharactersPerTier(uint8 max) external restricted {\n _maxTopCharactersPerTier = max;\n }\n\n function setSeasonDuration(uint256 duration) external restricted {\n seasonDuration = duration;\n }\n\n function setPvpCoreAddress(address pvpCoreContract) external restricted {\n pvpcore = PvpCore(pvpCoreContract);\n }\n}"
},
"contracts/raid.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"./multiAccessUpgradeable.sol\";\nimport \"./cryptoblades.sol\";\nimport \"./characters.sol\";\nimport \"./weapons.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nabstract contract Raid is Initializable, MultiAccessUpgradeable {\n\n // THIS AND raidBasic.sol are old unused files, DO NOT USE!\n\n // Outline raids contract that we can iterate on and deploy multiple of.\n // Needs to be granted access to NFT contracts to interact with them\n\n bool internal completed;\n uint256 internal expectedFinishTime; // not a guarantee since we don't automate this (atm)\n\n CryptoBlades internal game;\n Characters internal characters;\n Weapons internal weapons;\n\n struct Raider {\n uint256 owner;\n uint256 charID;\n uint256 wepID;\n uint24 power;\n }\n Raider[] internal raiders;\n mapping(uint256 => bool) internal participation;\n\n event RaidReset();\n event RaiderJoined(address owner, uint256 character, uint256 weapon, uint24 power);\n event RaidCompleted();\n\n function initialize(address gameContract) public virtual initializer {\n MultiAccessUpgradeable.initialize();\n\n grantAccess(gameContract);\n game = CryptoBlades(gameContract);\n // maybe just use extra params for NFT addresses?\n characters = Characters(game.characters());\n weapons = Weapons(game.weapons());\n }\n\n function reset() public virtual restricted {\n for(uint i = 0; i < raiders.length; i++) {\n delete participation[raiders[i].charID]; // we cant clear all mappings in one delete call\n }\n delete raiders;\n completed = false;\n emit RaidReset();\n }\n\n function isRaider(uint256 character) public view returns(bool) {\n return participation[character];\n }\n\n function getRaiderCount() public view returns(uint256) {\n return raiders.length;\n }\n\n function getExpectedFinishTime() public view returns(uint256) {\n return expectedFinishTime;\n }\n\n function setExpectedFinishTime(uint256 time) public restricted {\n expectedFinishTime = time;\n }\n\n function addRaider(uint256 characterID, uint256 weaponID) public virtual;\n function completeRaid(uint256 seed) public virtual;\n}"
},
"contracts/multiAccessUpgradeable.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\n\ncontract MultiAccessUpgradeable is Initializable {\n\n // how about a master address that cannot be revoked?\n mapping(address => bool) private access;\n\n function initialize() public initializer {\n access[msg.sender] = true;\n }\n\n function grantAccess(address to) public restricted {\n access[to] = true;\n }\n\n function revokeAccess(address from) public restricted {\n delete access[from];\n }\n\n modifier restricted() {\n require(access[msg.sender] == true, \"Access denied\");\n _;\n }\n\n}"
},
"@openzeppelin/contracts/token/ERC20/ERC20.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"../../utils/Context.sol\";\nimport \"./IERC20.sol\";\nimport \"../../math/SafeMath.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin guidelines: functions revert instead\n * of returning `false` on failure. This behavior is nonetheless conventional\n * and does not conflict with the expectations of ERC20 applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20 {\n using SafeMath for uint256;\n\n mapping (address => uint256) private _balances;\n\n mapping (address => mapping (address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for {name} and {symbol}, initializes {decimals} with\n * a default value of 18.\n *\n * To select a different value for {decimals}, use {_setupDecimals}.\n *\n * All three of these values are immutable: they can only be set once during\n * construction.\n */\n constructor (string memory name_, string memory symbol_) public {\n _name = name_;\n _symbol = symbol_;\n _decimals = 18;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is\n * called.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual returns (uint8) {\n return _decimals;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, \"ERC20: transfer amount exceeds allowance\"));\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, \"ERC20: decreased allowance below zero\"));\n return true;\n }\n\n /**\n * @dev Moves tokens `amount` from `sender` to `recipient`.\n *\n * This is internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(address sender, address recipient, uint256 amount) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n _balances[sender] = _balances[sender].sub(amount, \"ERC20: transfer amount exceeds balance\");\n _balances[recipient] = _balances[recipient].add(amount);\n emit Transfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply = _totalSupply.add(amount);\n _balances[account] = _balances[account].add(amount);\n emit Transfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n _balances[account] = _balances[account].sub(amount, \"ERC20: burn amount exceeds balance\");\n _totalSupply = _totalSupply.sub(amount);\n emit Transfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(address owner, address spender, uint256 amount) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Sets {decimals} to a value other than the default one of 18.\n *\n * WARNING: This function should only be called from the constructor. Most\n * applications that interact with token contracts will not expect\n * {decimals} to ever change, and may work incorrectly if it does.\n */\n function _setupDecimals(uint8 decimals_) internal virtual {\n _decimals = decimals_;\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be to transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }\n}\n"
},
"@openzeppelin/contracts/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/*\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with GSN meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address payable) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes memory) {\n this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691\n return msg.data;\n }\n}\n"
},
"contracts/raidBasic.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"./raid.sol\";\n\ncontract RaidBasic is Initializable, Raid {\n\n // THIS AND raid.sol are old unused files, DO NOT USE!\n\n using ABDKMath64x64 for int128;\n using ABDKMath64x64 for uint256;\n\n uint64 internal staminaDrain;\n uint256[] weaponDrops; // given out randomly, we add them manually\n uint256 bounty; // UNUSED; KEPT FOR UPGRADE PROXY BACKWARDS COMPAT\n uint8 bossTrait; // set manually for now\n\n uint256 totalPower;\n\n event XpReward(address addr, uint256 charID, uint256 amount);\n event WeaponWinner(address addr, uint256 wepID);\n\n function initialize(address gameContract) public override initializer {\n Raid.initialize(gameContract);\n\n staminaDrain = 12 * 60 * 60;\n }\n\n function addRaider(uint256 characterID, uint256 weaponID) public override {\n require(characters.ownerOf(characterID) == msg.sender);\n require(weapons.ownerOf(weaponID) == msg.sender);\n require(participation[characterID] == false, \"This character is already part of the raid\");\n\n participation[characterID] = true;\n // we drain ~12h of stamina from the character\n // we may want to have a specific lockout in the future\n int128 traitMultiplier = 0;\n uint24 power = 0;\n totalPower += power;\n raiders.push(Raider(uint256(msg.sender), characterID, weaponID, power));\n emit RaiderJoined(msg.sender, characterID, weaponID, power);\n }\n\n function completeRaid(uint256 seed) public override restricted {\n require(completed == false, \"Raid already completed, run reset first\");\n completed = true;\n\n emit RaidCompleted();\n }\n\n function reset() public override restricted {\n totalPower = 0;\n delete weaponDrops;\n super.reset();\n }\n\n function setBossTrait(uint8 trait) public restricted {\n bossTrait = trait;\n }\n\n function getTotalPower() public view returns(uint256) {\n return totalPower;\n }\n\n function getWeaponDrops() public view returns(uint256[] memory) {\n return weaponDrops;\n }\n\n function setStaminaDrainSeconds(uint64 secs) public restricted {\n staminaDrain = secs;\n }\n\n function getStaminaDrainSeconds() public view returns(uint64) {\n return staminaDrain;\n }\n}"
},
"contracts/WaxBridge.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\n\ncontract WaxBridge is Initializable, AccessControlUpgradeable {\n\n using ABDKMath64x64 for int128;\n using SafeMath for uint256;\n\n bytes32 public constant WAX_BRIDGE = keccak256(\"WAX_BRIDGE\");\n\n uint256 public constant LIMIT_PERIOD = 1 days;\n\n function initialize() public initializer {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n latestWaxChainBlockNumberProcessed = 0;\n totalOwedBnb = 0;\n bnbLimitPerPeriod = 35779455436688300;// 0.03577 or about $10\n }\n\n uint256 public latestWaxChainBlockNumberProcessed;\n uint256 public totalOwedBnb;\n uint256 public bnbLimitPerPeriod;\n mapping(address => uint256) public bnbLimitTimestamp;\n mapping(address => uint256) public withdrawableBnb;\n mapping(address => uint256) private withdrawnBnbDuringPeriod;\n\n function getWithdrawnBnbDuringPeriod() public view returns (uint256) {\n if(bnbLimitTimestamp[msg.sender] + LIMIT_PERIOD < block.timestamp) {\n return 0;\n }\n\n return withdrawnBnbDuringPeriod[msg.sender];\n }\n\n function getRemainingWithdrawableBnbDuringPeriod() external view returns(uint256) {\n return bnbLimitPerPeriod.sub(getWithdrawnBnbDuringPeriod());\n }\n\n function getTimeUntilLimitExpires() external view returns(uint256) {\n uint256 limitExpirationTime = bnbLimitTimestamp[msg.sender] + LIMIT_PERIOD;\n (, uint256 result) = limitExpirationTime.trySub(block.timestamp);\n return result;\n }\n\n function setDailyBnbWeiLimit(uint256 _dailyBnbWeiLimit) external restricted {\n bnbLimitPerPeriod = _dailyBnbWeiLimit;\n }\n\n receive() external payable restricted {\n }\n\n function processWaxConversions(uint256 _latestWaxChainBlockNumberProcessed, address[] calldata _to, uint256[] calldata _value) external payable {\n require(hasRole(WAX_BRIDGE, msg.sender), \"Missing WAX_BRIDGE role\");\n require(_latestWaxChainBlockNumberProcessed > latestWaxChainBlockNumberProcessed, \"WAX chain block num must be gt\");\n require(_to.length == _value.length, \"Mismatched array lengths\");\n require(_to.length < 3000, \"Too many recipients\");\n\n // assume they're holding enough skill (bot will check through main contract)\n\n latestWaxChainBlockNumberProcessed = _latestWaxChainBlockNumberProcessed;\n\n uint256 _totalOwedBnb = 0;\n for (uint256 i = 0; i < _to.length; i++) {\n _totalOwedBnb = _totalOwedBnb.add(_value[i]);\n withdrawableBnb[_to[i]] = withdrawableBnb[_to[i]].add(_value[i]);\n }\n\n totalOwedBnb = totalOwedBnb.add(_totalOwedBnb);\n }\n\n function withdraw(uint256 _maxAmount) external returns (uint256) {\n if(bnbLimitTimestamp[msg.sender] + LIMIT_PERIOD < block.timestamp) {\n bnbLimitTimestamp[msg.sender] = block.timestamp;\n withdrawnBnbDuringPeriod[msg.sender] = 0;\n }\n\n uint256 withdrawableBnbDuringPeriod = bnbLimitPerPeriod.sub(withdrawnBnbDuringPeriod[msg.sender]);\n\n uint256 bnbToSend = _maxAmount;\n if(bnbToSend > withdrawableBnb[msg.sender]) {\n bnbToSend = withdrawableBnb[msg.sender];\n }\n if(bnbToSend > withdrawableBnbDuringPeriod) {\n bnbToSend = withdrawableBnbDuringPeriod;\n }\n\n // because of if statement above, 'bnbToSend' is guaranteed to be lte 'withdrawableBnb[msg.sender]'\n // this saves gas\n withdrawableBnb[msg.sender] -= bnbToSend;\n totalOwedBnb -= bnbToSend;\n withdrawnBnbDuringPeriod[msg.sender] = withdrawnBnbDuringPeriod[msg.sender].add(bnbToSend);\n\n // supposedly, send and transfer are discouraged since ether istanbul update\n (bool sent,) = msg.sender.call{value: bnbToSend}(\"\");\n require(sent, \"Failed to send BNB\");\n }\n\n modifier restricted() {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Missing DEFAULT_ADMIN_ROLE role\");\n _;\n }\n\n}"
},
"contracts/Treasury.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"./cryptoblades.sol\";\n\ncontract Treasury is Initializable, AccessControlUpgradeable {\n using SafeMath for uint256;\n using ABDKMath64x64 for int128;\n using SafeERC20 for IERC20;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n uint256 public constant GAMEUSERVAR_GEN2_UNCLAIMED = 10011;\n\n uint256 public multiplierUnit;\n\n PartnerProject[] public partneredProjects;\n mapping(uint256 => uint256) projectAddedBlockNumber;\n mapping(uint256 => uint256) public tokensClaimed;\n uint256 public skillPrice;\n\n event TreasuryClaimed(address indexed sender, uint256 indexed partnerId, uint256 claimedAmount, uint256 currentMultiplier);\n\n struct PartnerProject {\n uint256 id;\n string name;\n string tokenSymbol;\n address tokenAddress;\n uint256 tokenSupply;\n uint256 tokenPrice;\n bool isActive;\n }\n\n CryptoBlades public game;\n\n mapping(uint256 => uint256) public projectDistributionTime;\n mapping(uint256 => uint256) multiplierTimestamp;\n uint256 public defaultSlippage;\n mapping(uint256 => string) public projectLogo;\n mapping(uint256 => string) public projectDetails;\n mapping(uint256 => string) public projectWebsite;\n mapping(uint256 => string) public projectNote;\n mapping(uint256 => bool) public projectIsValor;\n\n function initialize(CryptoBlades _game) public initializer {\n __AccessControl_init_unchained();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _setupRole(GAME_ADMIN, msg.sender);\n\n game = _game;\n // multiplier increases every second by 1e18/multiplierUnit\n multiplierUnit = 1e4;\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n }\n\n // Views\n\n function getAmountOfActiveProjects() public view returns (uint256 activeCount) {\n activeCount = 0;\n for (uint i; i < partneredProjects.length; i++) {\n if (partneredProjects[i].isActive) {\n activeCount += 1;\n }\n }\n }\n\n function getActivePartnerProjectsIds() public view returns (uint256[] memory activeProjectsIds){\n uint256 activeCount = getAmountOfActiveProjects();\n uint256 counter = 0;\n\n activeProjectsIds = new uint256[](activeCount);\n for (uint i = 0; i < partneredProjects.length; i++) {\n if (partneredProjects[i].isActive) {\n activeProjectsIds[counter] = i;\n counter++;\n }\n }\n }\n\n function getProjectMultiplier(uint256 partnerId) public view returns (uint256) {\n if (block.timestamp >= multiplierTimestamp[partnerId]) {\n return uint(1e18).div(multiplierUnit).mul(block.timestamp.sub(multiplierTimestamp[partnerId])).add(1e18);\n }\n uint256 multiplierDecrease = uint(1e18).div(multiplierUnit).mul(multiplierTimestamp[partnerId].sub(block.timestamp));\n if (multiplierDecrease > 1e18) {\n return 0;\n }\n return uint(1e18).sub(multiplierDecrease);\n }\n\n function getSkillToPartnerRatio(uint256 partnerId) public view returns (int128) {\n return ABDKMath64x64.divu(skillPrice, partneredProjects[partnerId].tokenPrice);\n }\n\n function getRemainingPartnerTokenSupply(uint256 partnerId) public view returns (uint256) {\n return partneredProjects[partnerId].tokenSupply.mul(1e18).sub(tokensClaimed[partnerId]);\n }\n\n function getAmountInPartnerToken(uint256 partnerId, uint256 claimingAmount) public view returns (uint256 amountWithMultiplier) {\n uint256 baseAmount = getSkillToPartnerRatio(partnerId).mulu(claimingAmount);\n amountWithMultiplier = baseAmount.mul(getProjectMultiplier(partnerId)).div(1e18);\n }\n\n function getAmountWithAdjustedDecimals(uint256 partnerTokenAmount, uint256 partnerTokenDecimals) internal pure returns (uint256 partnerTokenAmountAdjusted) {\n if (partnerTokenDecimals > 18) {\n partnerTokenAmountAdjusted = partnerTokenAmount.mul(10 ** uint(partnerTokenDecimals - 18));\n } else {\n partnerTokenAmountAdjusted = partnerTokenAmount.div(10 ** uint(18 - partnerTokenDecimals));\n }\n }\n\n function getProjectData(uint256 partnerId) public view returns (string memory, string memory, string memory, string memory) {\n return (projectLogo[partnerId], projectDetails[partnerId], projectWebsite[partnerId], projectNote[partnerId]);\n }\n\n // Mutative\n\n function addPartnerProject(\n string memory name,\n string memory tokenSymbol,\n address tokenAddress,\n uint256 tokenSupply,\n uint256 tokenPrice,\n uint256 distributionTime,\n bool isActive,\n string memory logo,\n string memory details,\n string memory website,\n string memory note,\n bool isValor)\n public restricted {\n uint256 id = partneredProjects.length;\n multiplierTimestamp[id] = block.timestamp;\n tokensClaimed[id] = 0;\n partneredProjects.push(PartnerProject(\n id,\n name,\n tokenSymbol,\n tokenAddress,\n tokenSupply,\n tokenPrice,\n isActive\n ));\n projectDistributionTime[id] = distributionTime;\n projectLogo[id] = logo;\n projectDetails[id] = details;\n projectWebsite[id] = website;\n projectNote[id] = note;\n projectIsValor[id] = isValor;\n }\n\n function claim(uint256 partnerId) public {\n uint256 claimingAmount;\n if(projectIsValor[partnerId]) {\n claimingAmount = game.userVars(msg.sender,GAMEUSERVAR_GEN2_UNCLAIMED);\n }\n else {\n claimingAmount = game.getTokenRewardsFor(msg.sender);\n }\n claim(partnerId, claimingAmount, getProjectMultiplier(partnerId), defaultSlippage);\n }\n\n function claim(uint256 partnerId, uint256 claimingAmount, uint256 currentMultiplier, uint256 slippage) public {\n if(projectIsValor[partnerId]) {\n require(game.userVars(msg.sender,GAMEUSERVAR_GEN2_UNCLAIMED) >= claimingAmount, 'Claim amount exceeds available rewards balance');\n }\n else {\n require(game.getTokenRewardsFor(msg.sender) >= claimingAmount, 'Claim amount exceeds available rewards balance');\n }\n uint256 effectiveMultiplier = getProjectMultiplier(partnerId);\n require(currentMultiplier.mul(uint(1e18).sub(slippage)).div(1e18) < effectiveMultiplier, 'Slippage exceeded');\n require(partneredProjects[partnerId].isActive == true, 'Project inactive');\n\n uint256 partnerTokenAmount = getAmountInPartnerToken(partnerId, claimingAmount);\n uint256 remainingPartnerTokenSupply = getRemainingPartnerTokenSupply(partnerId);\n uint256 tokensToDeduct = claimingAmount;\n\n if (partnerTokenAmount > remainingPartnerTokenSupply) {\n tokensToDeduct = tokensToDeduct.mul(remainingPartnerTokenSupply).div(partnerTokenAmount);\n partnerTokenAmount = remainingPartnerTokenSupply;\n }\n\n if(projectIsValor[partnerId]) {\n game.deductValor(tokensToDeduct, msg.sender);\n }\n else {\n game.deductAfterPartnerClaim(tokensToDeduct, msg.sender);\n }\n tokensClaimed[partnerId] += partnerTokenAmount;\n if(getRemainingPartnerTokenSupply(partnerId) == 0) {\n partneredProjects[partnerId].isActive = false;\n }\n\n uint256 partnerTokenDecimals = ERC20(partneredProjects[partnerId].tokenAddress).decimals();\n IERC20(partneredProjects[partnerId].tokenAddress).safeTransfer(msg.sender, getAmountWithAdjustedDecimals(partnerTokenAmount, partnerTokenDecimals));\n\n multiplierTimestamp[partnerId] = multiplierTimestamp[partnerId].add(partnerTokenAmount.div(partneredProjects[partnerId].tokenSupply.div(projectDistributionTime[partnerId])).div(uint(1e18).div(multiplierUnit)));\n\n emit TreasuryClaimed(msg.sender, partnerId, partnerTokenAmount, effectiveMultiplier);\n }\n\n // Setters\n\n function setMultiplierUnit(uint256 unit) external restricted {\n multiplierUnit = unit;\n }\n\n function setIsActive(uint256 id, bool isActive) public restricted {\n partneredProjects[id].isActive = isActive;\n }\n\n function setIsValor(uint256 id, bool isValor) public restricted {\n projectIsValor[id] = isValor;\n }\n\n function setSkillPrice(uint256 newPrice) external restricted {\n require(newPrice > 0);\n skillPrice = newPrice;\n }\n\n function setPartnerTokenPrice(uint256 partnerId, uint256 newPrice) external restricted {\n require(newPrice > 0);\n partneredProjects[partnerId].tokenPrice = newPrice;\n }\n\n function setDistributionTime(uint256 partnerId, uint256 distributionTime) external restricted {\n projectDistributionTime[partnerId] = distributionTime;\n }\n\n function setDefaultSlippage(uint256 newSlippage) external restricted {\n defaultSlippage = newSlippage;\n }\n\n function setProjectLogo(uint256 partnerId, string calldata logo) external restricted {\n projectLogo[partnerId] = logo;\n }\n\n function setProjectDetails(uint256 partnerId, string calldata details) external restricted {\n projectDetails[partnerId] = details;\n }\n\n function setProjectWebsite(uint256 partnerId, string calldata website) external restricted {\n projectWebsite[partnerId] = website;\n }\n\n function setProjectNote(uint256 partnerId, string calldata note) external restricted {\n projectNote[partnerId] = note;\n }\n\n}\n"
},
"contracts/TokensManager.sol": {
"content": "pragma solidity ^0.6.0;\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"./cryptoblades.sol\";\n\ncontract TokensManager is Initializable, AccessControlUpgradeable {\n using SafeMath for uint256;\n using ABDKMath64x64 for int128;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n CryptoBlades public game;\n\n uint256 public combatTokenChargePercent;\n // Note: All prices are multiplied by 100\n uint256 public tokenPrice;\n uint256 public skillTokenPrice;\n uint8 public offsetSlippage;\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender));\n }\n\n function initialize(\n address gameContract\n ) public initializer {\n __AccessControl_init_unchained();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n game = CryptoBlades(gameContract);\n combatTokenChargePercent = 25;\n offsetSlippage = 5;\n }\n\n receive() external payable restricted {\n }\n\n function fight(uint256 char, uint32 target, uint8 fightMultiplier) external payable {\n (uint256 tokens, uint256 expectedTokens) = game.fight(msg.sender, char, target, fightMultiplier);\n\n uint256 offset = ABDKMath64x64.mulu(getSkillToNativeRatio(), expectedTokens.mul(combatTokenChargePercent).div(100));\n\n require(\n msg.value >= offset.mul(100 - offsetSlippage).div(100) && msg.value <= offset.mul(100 + offsetSlippage).div(100),\n 'Offset error'\n );\n\n if (tokens == 0) {\n payable(msg.sender).transfer(msg.value);\n }\n }\n\n function retrieve(address addressToTransferTo, uint256 amount) external restricted {\n payable(addressToTransferTo).transfer(amount);\n }\n\n function getSkillToNativeRatio() public view returns (int128) {\n return ABDKMath64x64.divu(skillTokenPrice, tokenPrice);\n }\n\n function setTokenPrice(uint256 price) external restricted {\n tokenPrice = price;\n }\n\n function setSkillTokenPrice(uint256 price) external restricted {\n skillTokenPrice = price;\n }\n\n function setCombatTokenChargePercent(uint256 percent) external restricted {\n combatTokenChargePercent = percent;\n }\n\n function setOffsetSlippage(uint8 slippage) external restricted {\n offsetSlippage = slippage;\n }\n}"
},
"contracts/ValorERC20BridgeProxyContract.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"./interfaces/IERC20BridgeProxy.sol\";\nimport \"./Promos.sol\";\n\n\ncontract ValorERC20BridgeProxyContract is Initializable, AccessControlUpgradeable, IERC20BridgeProxy {\n \n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n \n Promos public promos;\n bool enabled;\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"NA\");\n }\n\n function initialize(Promos _promos) public initializer {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n promos = _promos;\n }\n\n // for future use, bot will probe the returned value to know if the proxy contract has proper signature behavior\n function sigVersion() external view override returns (uint256) {\n return 1;\n }\n\n function isEnabled() external view override returns (bool) {\n return enabled;\n }\n\n function setEnabled(bool _enabled) external restricted {\n enabled = _enabled;\n }\n\n function canBridge(address wallet, uint256 amount, uint256 targetChain) external view override returns (bool) {\n return promos.getBit(msg.sender, promos.BIT_BAD_ACTOR()) == false;\n }\n}\n"
},
"contracts/interfaces/IERC20BridgeProxy.sol": {
"content": "pragma solidity ^0.6.5;\n\ninterface IERC20BridgeProxy {\n function isEnabled() external view returns (bool);\n\n function sigVersion() external view returns (uint256);\n\n function canBridge(address wallet, uint256 amount, uint256 targetChain) external view returns (bool);\n}"
},
"contracts/SkillERC20BridgeProxyContract.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"./interfaces/IERC20BridgeProxy.sol\";\nimport \"./Promos.sol\";\n\n\ncontract SkillERC20BridgeProxyContract is Initializable, AccessControlUpgradeable, IERC20BridgeProxy {\n \n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n \n Promos public promos;\n bool enabled;\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"NA\");\n }\n\n function initialize(Promos _promos) public initializer {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n promos = _promos;\n }\n\n // for future use, bot will probe the returned value to know if the proxy contract has proper signature behavior\n function sigVersion() external view override returns (uint256) {\n return 1;\n }\n\n function isEnabled() external view override returns (bool) {\n return enabled;\n }\n\n function setEnabled(bool _enabled) external restricted {\n enabled = _enabled;\n }\n\n function canBridge(address wallet, uint256 amount, uint256 targetChain) external view override returns (bool) {\n return promos.getBit(msg.sender, promos.BIT_BAD_ACTOR()) == false;\n }\n}\n"
},
"contracts/ERC20Bridge.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/utils/EnumerableSet.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"./cryptoblades.sol\";\nimport \"./interfaces/IERC20BridgeProxy.sol\";\n\ncontract ERC20Bridge is Initializable, AccessControlUpgradeable\n{\n using SafeERC20 for IERC20;\n using EnumerableSet for EnumerableSet.UintSet;\n using EnumerableSet for EnumerableSet.AddressSet;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n \n bool private _erc20BridgeEnabled;\n\n uint8 public constant BRIDGE_TOKEN_VAR_MIN_AMOUNT_INDEX = 0; // can't request bridge less than this value; value is in wei\n uint8 public constant BRIDGE_TOKEN_VAR_FEE_INDEX = 1;\n uint8 public constant BRIDGE_TOKEN_VAR_MAX_AMOUNT_INDEX = 2; // can't request bridge greater than this value; value is in wei\n uint8 public constant BRIDGE_TOKEN_VAR_OPEN_TO_PUBLIC = 3; // token is public or whitelisted only\n\n mapping(uint256 => uint256) private _bridgeVars;\n mapping(address => mapping(uint256 => uint256)) private _bridgeTokenVars;\n\n uint8 public constant TRANSFER_OUT_STATUS_NONE = 0;\n uint8 public constant TRANSFER_OUT_STATUS_PENDING = 1;\n uint8 public constant TRANSFER_OUT_STATUS_PROCESSING = 2;\n uint8 public constant TRANSFER_OUT_STATUS_DONE = 3;\n uint8 public constant TRANSFER_OUT_STATUS_ERROR = 4;\n\n struct ERC20BridgeOutRequests {\n address owner;\n address erc20Address;\n uint256 amount;\n\n uint256 chainId;\n uint8 status; // enumeration. 0 => nothing, 1 => transfer requested, 2 => processing, 3 => done\n\n uint256 blockNumber;\n }\n\n struct ERC20BridgeReceivedTokens {\n address owner;\n address erc20Address;\n uint256 amount;\n\n uint256 sourceChain;\n uint256 sourceTransferId;\n\n uint256 blockNumber;\n }\n\n\n uint256 private _transfersOutCount;\n mapping(uint256 => bool) private _bridgeEnabled; // Which chain can we go to from here?\n mapping(uint256 => ERC20BridgeOutRequests) private _transferOuts;\n\n uint256 private _transferInsCount;\n mapping(uint256 => ERC20BridgeReceivedTokens) private _transferIns;\n\n // Player stuff\n // Player => bridgeTransferId\n mapping(address => uint256) private _transferOutOfPlayers;\n mapping(address => EnumerableSet.UintSet) private _transferOutOfPlayersHistory;\n mapping(address => EnumerableSet.UintSet) private _transferInOfPlayersHistory;\n\n // address is IERC20\n EnumerableSet.AddressSet private _supportedTokenTypes;\n EnumerableSet.UintSet private _supportedChains;\n\n CryptoBlades public game;\n\n // Proxy contracts\n mapping(address => address) private _erc20ProxyContract;\n // Source chain => transfer out id of source chain => transfer in id\n mapping(uint256 => mapping(uint256 => uint256)) private _botLog;\n // ERC20 => allowed networks to bridge into\n mapping(address => EnumerableSet.UintSet) private _erc20AllowedChains;\n // Target network => allowed ERC20s\n mapping(uint256 => EnumerableSet.AddressSet) private _targetChainAllowedERC20s;\n\n // Whitelisted wallets\n mapping(address => EnumerableSet.AddressSet) private _whitelistedWallets;\n\n event BridgeIn(address indexed receiver, address indexed erc20, uint256 indexed sourceChain, uint256 sourceId, uint256 amount);\n event BridgeOut(address indexed sender, address indexed erc20, uint256 indexed destinationChain, uint256 requestId, uint256 amount);\n\n function initialize(CryptoBlades _game)\n public\n initializer\n {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n game = _game;\n }\n\n modifier restricted() {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"AO\");\n _;\n }\n\n modifier gameAdminRestricted() {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"NGA\");\n _;\n }\n\n modifier tokenNotBanned(IERC20 _tokenAddress) {\n require(\n isTokenSupported(_tokenAddress),\n \"Error\"\n );\n _;\n }\n\n modifier canBridge() {\n require(\n _erc20BridgeEnabled == true,\n \"bridge disabled\"\n );\n _;\n }\n\n modifier bridgeEnabled(uint256 targetChain) {\n require(\n _bridgeEnabled[targetChain],\n \"bridging disabled\"\n );\n _;\n }\n\n modifier noPendingBridge() {\n require(\n _transferOuts[_transferOutOfPlayers[msg.sender]].status == TRANSFER_OUT_STATUS_NONE\n || _transferOuts[_transferOutOfPlayers[msg.sender]].status == TRANSFER_OUT_STATUS_DONE,\n \"Cannot request a bridge\"\n );\n _;\n }\n\n modifier bridgeSupported(IERC20 _tokenAddress, uint256 targetChain) {\n require(_erc20AllowedChains[address(_tokenAddress)].contains(targetChain), \"BNS1\"); // We support bridging from this chain to that chain\n require(IERC20BridgeProxy(_erc20ProxyContract[address(_tokenAddress)]).isEnabled(), \"BNS2\");\n _;\n }\n \n function isTokenSupported(IERC20 _tokenAddress) public view returns (bool) {\n return _supportedTokenTypes.contains(address(_tokenAddress));\n }\n\n function getSupportedTokenTypes() public view returns (IERC20[] memory) {\n EnumerableSet.AddressSet storage set = _supportedTokenTypes;\n IERC20[] memory tokens = new IERC20[](set.length());\n\n for (uint256 i = 0; i < tokens.length; i++) {\n tokens[i] = IERC20(set.at(i));\n }\n return tokens;\n }\n\n function allowToken(IERC20 _tokenAddress) public restricted {\n _supportedTokenTypes.add(address(_tokenAddress));\n }\n\n function disallowToken(IERC20 _tokenAddress) public restricted {\n _supportedTokenTypes.remove(address(_tokenAddress));\n }\n\n function bridgeIsEnabled() public view returns (bool) {\n return _erc20BridgeEnabled;\n }\n\n function setBridgeEnabled(bool enabled) public restricted {\n _erc20BridgeEnabled = enabled;\n }\n\n // bridge stuff\n function chainBridgeEnabled(uint256 chainId) public view returns (bool) {\n return _bridgeEnabled[chainId];\n }\n\n function getEnabledChainsForBridging() public view returns (uint256[] memory chainIds) {\n uint256 amount = _supportedChains.length();\n chainIds = new uint256[](amount);\n\n for (uint256 i = 0; i < amount; i++) {\n uint256 id = _supportedChains.at(i);\n chainIds[i] = id;\n }\n }\n\n\n function toggleChainBridgeEnabled(uint256 chainId, bool enable) public restricted {\n _bridgeEnabled[chainId] = enable;\n\n if(enable) {\n _supportedChains.add(chainId);\n } else {\n _supportedChains.remove(chainId);\n }\n }\n\n // Player requests to bridge an erc20\n function bridgeOutToken(\n address _tokenAddress,\n uint256 _amount,\n uint256 targetChain\n )\n public\n tokenNotBanned(IERC20(_tokenAddress))\n bridgeSupported(IERC20(_tokenAddress), targetChain)\n bridgeEnabled(targetChain)\n noPendingBridge()\n canBridge()\n {\n require(_amount >= _bridgeTokenVars[_tokenAddress][BRIDGE_TOKEN_VAR_MIN_AMOUNT_INDEX], \"NA1\");\n require(_amount <= _bridgeTokenVars[_tokenAddress][BRIDGE_TOKEN_VAR_MAX_AMOUNT_INDEX], \"NA2\");\n require(IERC20BridgeProxy(_erc20ProxyContract[_tokenAddress]).canBridge(msg.sender, _amount, targetChain), \"NA3\");\n require(_bridgeTokenVars[_tokenAddress][BRIDGE_TOKEN_VAR_OPEN_TO_PUBLIC] == 1 \n || _whitelistedWallets[_tokenAddress].contains(msg.sender), \"NA4\");\n\n game.payContractTokenOnly(msg.sender, _bridgeTokenVars[_tokenAddress][BRIDGE_TOKEN_VAR_FEE_INDEX]);\n IERC20(_tokenAddress).transferFrom(msg.sender, address(this), _amount);\n \n _transferOuts[++_transfersOutCount] = ERC20BridgeOutRequests(msg.sender, _tokenAddress, _amount, targetChain, TRANSFER_OUT_STATUS_PENDING, block.number);\n _transferOutOfPlayers[msg.sender] = _transfersOutCount;\n _transferOutOfPlayersHistory[msg.sender].add(_transfersOutCount);\n\n emit BridgeOut(msg.sender, _tokenAddress, targetChain, _transfersOutCount, _amount);\n }\n\n function bridgeInToken(\n address receiver,\n address _tokenAddress,\n uint256 _amount,\n uint256 sourceChain,\n uint256 sourceTransferId\n ) public gameAdminRestricted\n {\n require(_botLog[sourceChain][sourceTransferId] == 0, \"Already Processed\");\n IERC20(_tokenAddress).transfer(receiver, _amount);\n _transferIns[++_transferInsCount] = ERC20BridgeReceivedTokens(receiver, _tokenAddress, _amount, sourceChain, sourceTransferId, block.number);\n _transferInOfPlayersHistory[receiver].add(_transferInsCount);\n _botLog[sourceChain][sourceTransferId] = _transferInsCount;\n emit BridgeIn(receiver, _tokenAddress, sourceChain, sourceTransferId, _amount);\n }\n \n function getBridgeTransfers() public view returns (uint256) {\n return _transfersOutCount;\n }\n\n function getBridgeVal(uint256 index) public view returns (uint256) {\n return _bridgeVars[index];\n }\n\n function setBridgeVal(uint256 index, uint256 value) public restricted {\n _bridgeVars[index] = value;\n }\n\n function getTokenBridgeVal(IERC20 token, uint256 index) public view returns (uint256) {\n return _bridgeTokenVars[address(token)][index];\n }\n\n function setTokenBridgeVal(IERC20 token, uint256 index, uint256 value) public restricted {\n _bridgeTokenVars[address(token)][index] = value;\n }\n\n function getBridgeTransferOfPlayer(address player) public view returns (uint256) {\n return _transferOutOfPlayers[player];\n }\n\n function getBridgeOutTransferOfPlayerHistory(address player) public view returns (uint256[] memory history) {\n history = new uint256[](_transferOutOfPlayersHistory[player].length());\n\n for (uint256 i = 0; i < _transferOutOfPlayersHistory[player].length(); i++) {\n uint256 id = _transferOutOfPlayersHistory[player].at(i);\n history[i] = id;\n }\n }\n\n function getBridgeInTransferOfPlayerHistory(address player) public view returns (uint256[] memory history) {\n history = new uint256[](_transferInOfPlayersHistory[player].length());\n\n for (uint256 i = 0; i < _transferInOfPlayersHistory[player].length(); i++) {\n uint256 id = _transferInOfPlayersHistory[player].at(i);\n history[i] = id;\n }\n }\n\n function getBridgeOutTransfer(uint256 bridgeTransferId) public view returns (address, address, uint256, uint256, uint8, uint256) {\n ERC20BridgeOutRequests storage transferOut = _transferOuts[bridgeTransferId];\n return (transferOut.owner,\n transferOut.erc20Address,\n transferOut.amount,\n transferOut.chainId,\n transferOut.status,\n transferOut.blockNumber);\n }\n\n function getBridgeInTransfer(uint256 bridgeTransferId) public view returns (address, address, uint256, uint256, uint256, uint256) {\n ERC20BridgeReceivedTokens storage transferIn = _transferIns[bridgeTransferId];\n return (transferIn.owner,\n transferIn.erc20Address,\n transferIn.amount,\n transferIn.sourceChain,\n transferIn.sourceTransferId,\n transferIn.blockNumber);\n }\n\n // Bot to update status of a transfer request (this chain => outside chain)\n function updateBridgeTransferStatus(uint256 bridgeTransferId, uint8 status, bool forced) public gameAdminRestricted {\n ERC20BridgeOutRequests storage transferOut = _transferOuts[bridgeTransferId];\n require(forced ||\n (transferOut.status == TRANSFER_OUT_STATUS_PENDING && status == TRANSFER_OUT_STATUS_PROCESSING)\n || (transferOut.status == TRANSFER_OUT_STATUS_PROCESSING && status == TRANSFER_OUT_STATUS_DONE)\n || status == TRANSFER_OUT_STATUS_ERROR, 'Invalid status change');\n transferOut.status = status;\n }\n\n function getTransferInFromLog(uint256 sourceChain, uint256 sourceTransferId) public view returns (uint256) {\n return _botLog[sourceChain][sourceTransferId];\n }\n\n function setProxyContract(address token, address proxy, bool forced) external restricted {\n require(forced || _erc20ProxyContract[token] == address(0), \"NA\");\n _erc20ProxyContract[token] = proxy;\n }\n\n function getProxyContract(address token) public view returns (address) {\n return _erc20ProxyContract[token];\n }\n\n function setChainSupportedForERC20(address token, uint256[] calldata chainIds, bool support) external restricted {\n for (uint256 i = 0; i < chainIds.length; i++) {\n uint256 chainId = chainIds[i];\n if(support) {\n require(!_erc20AllowedChains[token].contains(chainId), \"NA\");\n _erc20AllowedChains[token].add(chainId);\n _targetChainAllowedERC20s[chainId].add(token);\n } else {\n require(_erc20AllowedChains[token].contains(chainId), \"NA2\");\n _erc20AllowedChains[token].remove(chainId);\n _targetChainAllowedERC20s[chainId].remove(token);\n }\n }\n }\n\n function getChainsSupportingERCs(address token) public view returns (uint256[] memory chains) {\n chains = new uint256[](_erc20AllowedChains[token].length());\n\n for (uint256 i = 0; i < _erc20AllowedChains[token].length(); i++) {\n uint256 id = _erc20AllowedChains[token].at(i);\n chains[i] = id;\n }\n }\n\n function getERCsSupportedByChain(uint256 chain) public view returns (address[] memory tokens) {\n tokens = new address[](_targetChainAllowedERC20s[chain].length());\n\n for (uint256 i = 0; i < _targetChainAllowedERC20s[chain].length(); i++) {\n address token = _targetChainAllowedERC20s[chain].at(i);\n tokens[i] = token;\n }\n }\n\n function recoverToken(address token, uint256 amount) public restricted {\n IERC20(token).safeTransfer(msg.sender, amount);\n }\n\n\n function updateWhitelistedWallets(address token, address[] calldata wallets, bool enable) external gameAdminRestricted {\n for (uint256 i = 0; i < wallets.length; i++) {\n address wallet = wallets[i];\n if(enable) {\n require(!_whitelistedWallets[token].contains(wallet), \"NA\");\n _whitelistedWallets[token].add(wallet);\n } else {\n require(_whitelistedWallets[token].contains(wallet), \"NA2\");\n _whitelistedWallets[token].remove(wallet);\n }\n }\n }\n\n function getWhitelistedWallets(address token) public view returns (address[] memory wallets) {\n wallets = new address[](_whitelistedWallets[token].length());\n\n for (uint256 i = 0; i < _whitelistedWallets[token].length(); i++) {\n address wallet = _whitelistedWallets[token].at(i);\n wallets[i] = wallet;\n }\n }\n}\n"
},
"contracts/PvpArena.sol": {
"content": "pragma solidity ^0.6.0;\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/EnumerableSet.sol\";\nimport \"./interfaces/IRandoms.sol\";\nimport \"./cryptoblades.sol\";\nimport \"./characters.sol\";\nimport \"./weapons.sol\";\nimport \"./shields.sol\";\nimport \"./common.sol\";\n\ncontract PvpArena is Initializable, AccessControlUpgradeable {\n using EnumerableSet for EnumerableSet.UintSet;\n using SafeMath for uint8;\n using SafeMath for uint24;\n using SafeMath for uint256;\n using ABDKMath64x64 for int128;\n using SafeERC20 for IERC20;\n\n struct Fighter {\n uint256 characterID;\n uint256 weaponID;\n uint256 shieldID;\n uint256 wager;\n bool useShield;\n }\n\n struct Match {\n uint256 attackerID;\n uint256 defenderID;\n uint256 createdAt;\n }\n\n struct BountyDistribution {\n uint256 winnerReward;\n uint256 loserPayment;\n uint256 rankingPoolTax;\n }\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n CryptoBlades public game;\n Characters public characters;\n Weapons public weapons;\n Shields public shields;\n IERC20 public skillToken;\n IRandoms public randoms;\n\n /// @dev the base amount wagered per duel in dollars\n int128 private _baseWagerUSD;\n /// @dev how much extra USD is wagered per level tier\n int128 private _tierWagerUSD;\n /// @dev how much of a duel's bounty is sent to the rankings pool\n uint8 private _rankingsPoolTaxPercent;\n /// @dev how many times the cost of battling must be wagered to enter the arena\n uint8 public wageringFactor;\n /// @dev amount of points earned by winning a duel\n uint8 public winningPoints;\n /// @dev amount of points subtracted by losing duel\n uint8 public losingPoints;\n /// @dev max amount of top characters by tier\n uint8 private _maxTopCharactersPerTier;\n /// @dev percentage of duel cost charged when rerolling opponent\n uint256 public reRollFeePercent;\n /// @dev percentage of entry wager charged when withdrawing from arena with pending duel\n uint256 public withdrawFeePercent;\n /// @dev current ranked season\n uint256 public currentRankedSeason;\n /// @dev timestamp of when the current season started\n uint256 public seasonStartedAt;\n /// @dev interval of ranked season restarts\n uint256 public seasonDuration;\n /// @dev amount of time a match finder has to make a decision\n uint256 public decisionSeconds;\n /// @dev amount of skill due for game coffers from tax\n uint256 public gameCofferTaxDue;\n /// @dev allows or blocks entering arena (we can extend later to disable other parts such as rerolls)\n uint256 public arenaAccess; // 0 = cannot join, 1 = can join\n /// @dev percentages of ranked prize distribution by fighter rank (represented as index)\n uint256[] public prizePercentages;\n /// @dev characters by id that are on queue to perform a duel\n EnumerableSet.UintSet private _duelQueue;\n\n /// @dev Fighter by characterID\n mapping(uint256 => Fighter) public fighterByCharacter;\n /// @dev Active match by characterID of the finder\n mapping(uint256 => Match) public matchByFinder;\n /// @dev if character is currently in the arena\n mapping(uint256 => bool) public isCharacterInArena;\n /// @dev if weapon is currently in the arena\n mapping(uint256 => bool) public isWeaponInArena;\n /// @dev if shield is currently in the arena\n mapping(uint256 => bool) public isShieldInArena;\n /// @dev if defender is in a duel that has not finished processing\n mapping(uint256 => bool) public isDefending;\n /// @dev if a character is someone else's opponent\n mapping(uint256 => uint256) public finderByOpponent;\n /// @dev character's tier when it last entered arena. Used to reset rank if it changes\n mapping(uint256 => uint8) public previousTierByCharacter;\n /// @dev excess wager by character for when they re-enter the arena\n mapping(uint256 => uint256) public excessWagerByCharacter;\n /// @dev season number associated to character\n mapping(uint256 => uint256) public seasonByCharacter;\n /// @dev ranking points by character\n mapping(uint256 => uint256) public rankingPointsByCharacter;\n /// @dev accumulated skill pool per tier\n mapping(uint8 => uint256) public rankingsPoolByTier;\n /// @dev funds available for withdrawal by address\n mapping(address => uint256) private _rankingRewardsByPlayer;\n /// @dev top ranking characters by tier\n mapping(uint8 => uint256[]) private _topRankingCharactersByTier;\n /// @dev IDs of characters available for matchmaking by tier\n mapping(uint8 => EnumerableSet.UintSet) private _matchableCharactersByTier;\n\n uint256 public duelOffsetCost;\n address payable public pvpBotAddress;\n\n /// @dev owner's address by character ID\n mapping(uint256 => uint256) public specialWeaponRerollTimestamp;\n mapping(uint256 => address) private _ownerByCharacter;\n \n event DuelFinished(\n uint256 indexed attacker,\n uint256 indexed defender,\n uint256 timestamp,\n uint256 attackerRoll,\n uint256 defenderRoll,\n bool attackerWon,\n uint256 bonusRank\n );\n\n event CharacterKicked(\n uint256 indexed characterID,\n uint256 kickedBy,\n uint256 timestamp\n );\n\n event SeasonRestarted(\n uint256 indexed newSeason,\n uint256 timestamp\n );\n\n modifier characterInArena(uint256 characterID) {\n _characterInArena(characterID);\n _;\n }\n\n function _characterInArena(uint256 characterID) internal view {\n require(isCharacterInArena[characterID], \"NA\");\n }\n\n modifier characterWithinDecisionTime(uint256 characterID) {\n _characterWithinDecisionTime(characterID);\n _;\n }\n\n function _characterWithinDecisionTime(uint256 characterID) internal view {\n require(\n isCharacterWithinDecisionTime(characterID),\n \"DE\"\n );\n }\n\n modifier characterNotUnderAttack(uint256 characterID) {\n _characterNotUnderAttack(characterID);\n _;\n }\n\n function _characterNotUnderAttack(uint256 characterID) internal view {\n require(!isCharacterUnderAttack(characterID), \"UA\");\n }\n\n modifier characterNotInDuel(uint256 characterID) {\n _characterNotInDuel(characterID);\n _;\n }\n\n function _characterNotInDuel(uint256 characterID) internal view {\n require(!isCharacterInDuel(characterID), \"IQ\");\n }\n\n modifier isOwnedCharacter(uint256 characterID) {\n require(_ownerByCharacter[characterID] == msg.sender);\n _;\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender));\n }\n\n modifier enteringArenaChecks(\n uint256 characterID,\n uint256 weaponID,\n uint256 shieldID,\n bool useShield\n ) {\n require(\n characters.ownerOf(characterID) == msg.sender &&\n weapons.ownerOf(weaponID) == msg.sender\n );\n\n require(characters.getNftVar(characterID, 1) == 0 && weapons.getNftVar(weaponID, 1) == 0, \"B\");\n\n if (useShield) {\n require(shields.ownerOf(shieldID) == msg.sender);\n require(shields.getNftVar(shieldID, 1) == 0, \"SB\");\n }\n\n require((arenaAccess & 1) == 1, \"AL\");\n _;\n }\n\n function initialize(\n address gameContract,\n address shieldsContract,\n address randomsContract\n ) public initializer {\n __AccessControl_init_unchained();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n game = CryptoBlades(gameContract);\n characters = Characters(game.characters());\n weapons = Weapons(game.weapons());\n shields = Shields(shieldsContract);\n skillToken = IERC20(game.skillToken());\n randoms = IRandoms(randomsContract);\n\n // TODO: Tweak these values\n _baseWagerUSD = ABDKMath64x64.divu(500, 100); // $5\n _tierWagerUSD = ABDKMath64x64.divu(50, 100); // $0.5\n _rankingsPoolTaxPercent = 15;\n wageringFactor = 3;\n winningPoints = 5;\n losingPoints = 3;\n _maxTopCharactersPerTier = 4;\n reRollFeePercent = 25;\n withdrawFeePercent = 25;\n currentRankedSeason = 1;\n seasonStartedAt = block.timestamp;\n seasonDuration = 7 days;\n decisionSeconds = 2 minutes;\n prizePercentages.push(60);\n prizePercentages.push(30);\n prizePercentages.push(10);\n duelOffsetCost = 0.005 ether;\n }\n\n /// @dev enter the arena with a character, a weapon and optionally a shield\n function enterArena(\n uint256 characterID,\n uint256 weaponID,\n uint256 shieldID,\n bool useShield\n ) external enteringArenaChecks(characterID, weaponID, shieldID, useShield) {\n uint8 tier = getArenaTier(characterID);\n uint256 wager = getEntryWagerByTier(tier);\n\n if (_ownerByCharacter[characterID] != msg.sender) {\n _ownerByCharacter[characterID] = msg.sender;\n }\n\n if (previousTierByCharacter[characterID] != tier) {\n rankingPointsByCharacter[characterID] = 0;\n }\n\n if (\n _topRankingCharactersByTier[tier].length <\n _maxTopCharactersPerTier &&\n seasonByCharacter[characterID] != currentRankedSeason\n ) {\n _topRankingCharactersByTier[tier].push(characterID);\n }\n\n if (seasonByCharacter[characterID] != currentRankedSeason) {\n rankingPointsByCharacter[characterID] = 0;\n seasonByCharacter[characterID] = currentRankedSeason;\n }\n\n isCharacterInArena[characterID] = true;\n characters.setNftVar(characterID, 1, 1);\n\n isWeaponInArena[weaponID] = true;\n weapons.setNftVar(weaponID, 1, 1);\n\n if (useShield) {\n isShieldInArena[shieldID] = true;\n shields.setNftVar(shieldID, 1, 1);\n }\n\n uint256 characterWager;\n\n if (excessWagerByCharacter[characterID] != 0) {\n characterWager = excessWagerByCharacter[characterID];\n } else {\n characterWager = fighterByCharacter[characterID].wager;\n }\n\n _matchableCharactersByTier[tier].add(characterID);\n fighterByCharacter[characterID] = Fighter(\n characterID,\n weaponID,\n shieldID,\n wager.add(characterWager),\n useShield\n );\n previousTierByCharacter[characterID] = tier;\n excessWagerByCharacter[characterID] = 0;\n\n skillToken.transferFrom(msg.sender, address(this), wager);\n }\n\n /// @dev withdraws a character and its items from the arena.\n /// if the character is in a battle, a penalty is charged\n function withdrawFromArena(uint256 characterID)\n external\n isOwnedCharacter(characterID)\n characterInArena(characterID)\n characterNotUnderAttack(characterID)\n characterNotInDuel(characterID)\n {\n Fighter storage fighter = fighterByCharacter[characterID];\n uint256 wager = fighter.wager;\n uint8 tier = getArenaTier(characterID);\n uint256 entryWager = getEntryWager(characterID);\n\n if (matchByFinder[characterID].createdAt != 0) {\n if (wager < entryWager.mul(withdrawFeePercent).div(100)) {\n wager = 0;\n } else {\n wager = wager.sub(entryWager.mul(withdrawFeePercent).div(100));\n }\n }\n\n _removeCharacterFromArena(characterID, tier);\n\n excessWagerByCharacter[characterID] = 0;\n fighter.wager = 0;\n\n skillToken.safeTransfer(msg.sender, wager);\n }\n\n /// @dev attempts to find an opponent for a character\n function findOpponent(uint256 characterID)\n external\n isOwnedCharacter(characterID)\n characterInArena(characterID)\n characterNotUnderAttack(characterID)\n characterNotInDuel(characterID)\n {\n require(matchByFinder[characterID].createdAt == 0, \"AM\");\n\n uint8 tier = getArenaTier(characterID);\n\n _assignOpponent(characterID, tier);\n }\n\n /// @dev attempts to find a new opponent for a fee\n function reRollOpponent(uint256 characterID)\n external\n characterInArena(characterID)\n characterNotUnderAttack(characterID)\n isOwnedCharacter(characterID)\n characterNotInDuel(characterID)\n {\n uint256 opponentID = getOpponent(characterID);\n uint8 tier = getArenaTier(characterID);\n\n require(matchByFinder[characterID].createdAt != 0, \"NM\");\n\n delete finderByOpponent[opponentID];\n if (isCharacterInArena[opponentID]) {\n _matchableCharactersByTier[tier].add(opponentID);\n }\n\n _assignOpponent(characterID, tier);\n\n uint256 weaponID = fighterByCharacter[characterID].weaponID;\n if(weapons.getWeaponType(weaponID) > 0 && specialWeaponRerollTimestamp[weaponID] < block.timestamp) {\n specialWeaponRerollTimestamp[weaponID] = block.timestamp + 24 hours;\n }\n else {\n skillToken.transferFrom(\n msg.sender,\n address(this),\n getDuelCostByTier(tier).mul(reRollFeePercent).div(100)\n );\n }\n }\n\n /// @dev adds a character to the duel queue\n function prepareDuel(uint256 attackerID)\n external\n payable\n isOwnedCharacter(attackerID)\n characterInArena(attackerID)\n characterWithinDecisionTime(attackerID)\n characterNotInDuel(attackerID)\n {\n require((arenaAccess & 1) == 1);\n require(msg.value == duelOffsetCost, \"NO\");\n\n uint256 defenderID = getOpponent(attackerID);\n\n if (seasonByCharacter[attackerID] != currentRankedSeason) {\n rankingPointsByCharacter[attackerID] = 0;\n seasonByCharacter[attackerID] = currentRankedSeason;\n }\n\n if (seasonByCharacter[defenderID] != currentRankedSeason) {\n rankingPointsByCharacter[defenderID] = 0;\n seasonByCharacter[defenderID] = currentRankedSeason;\n }\n\n isDefending[defenderID] = true;\n\n _duelQueue.add(attackerID);\n\n pvpBotAddress.transfer(msg.value);\n }\n\n /// @dev allows a player to withdraw their ranking earnings\n function withdrawRankedRewards() external {\n uint256 amountToTransfer = _rankingRewardsByPlayer[msg.sender];\n\n if (amountToTransfer > 0) {\n _rankingRewardsByPlayer[msg.sender] = 0;\n\n skillToken.safeTransfer(msg.sender, amountToTransfer);\n }\n }\n\n /// @dev restarts ranked season\n function restartRankedSeason() public restricted {\n uint256[] memory duelQueue = getDuelQueue();\n\n if (duelQueue.length > 0) {\n performDuels(duelQueue);\n }\n\n // Loops over 15 tiers. Should not be reachable anytime in the foreseeable future\n for (uint8 i = 0; i <= 15; i++) {\n if (_topRankingCharactersByTier[i].length == 0) {\n continue;\n }\n\n uint256 difference = 0;\n\n if (\n _topRankingCharactersByTier[i].length <= prizePercentages.length\n ) {\n difference =\n prizePercentages.length -\n _topRankingCharactersByTier[i].length;\n }\n\n // If there are less players than top positions, excess is transferred to top 1\n if (\n _topRankingCharactersByTier[i].length < prizePercentages.length\n ) {\n uint256 excessPercentage;\n \n address topOnePlayer = _ownerByCharacter[_topRankingCharactersByTier[i][0]];\n\n // We accumulate excess percentage\n for (\n uint256 j = prizePercentages.length - difference;\n j < prizePercentages.length;\n j++\n ) {\n excessPercentage = excessPercentage.add(\n prizePercentages[j]\n );\n }\n\n // We assign excessive rewards to top 1 player\n _rankingRewardsByPlayer[topOnePlayer] = _rankingRewardsByPlayer[\n topOnePlayer\n ].add((rankingsPoolByTier[i].mul(excessPercentage)).div(100));\n }\n\n // We assign rewards normally to all possible players\n for (uint8 h = 0; h < prizePercentages.length - difference; h++) {\n _assignRewards(\n _topRankingCharactersByTier[i][h],\n h,\n rankingsPoolByTier[i]\n );\n }\n\n // We reset ranking prize pools\n rankingsPoolByTier[i] = 0;\n \n // We reset top players' scores\n for (uint256 k = 0; k < _topRankingCharactersByTier[i].length; k++) {\n rankingPointsByCharacter[_topRankingCharactersByTier[i][k]] = 0;\n }\n }\n\n currentRankedSeason = currentRankedSeason.add(1);\n seasonStartedAt = block.timestamp;\n\n emit SeasonRestarted(\n currentRankedSeason,\n seasonStartedAt\n );\n }\n\n struct Duelist {\n uint256 ID;\n uint8 level;\n uint8 trait;\n uint24 roll;\n uint256 power;\n }\n\n struct Duel {\n Duelist attacker;\n Duelist defender;\n uint8 tier;\n uint256 cost;\n bool attackerWon;\n uint256 bonusRank;\n }\n\n function createDuelist(uint256 id) internal view returns (Duelist memory duelist) {\n duelist.ID = id;\n\n (\n , // xp\n duelist.level,\n duelist.trait,\n , // staminaTimestamp\n , // head\n , // torso\n , // legs\n , // boots\n , // race\n ) = characters.get(id);\n }\n\n /// @dev performs a list of duels\n function performDuels(uint256[] memory attackerIDs) public restricted {\n for (uint256 i = 0; i < attackerIDs.length; i++) {\n Duel memory duel;\n duel.attacker = createDuelist(attackerIDs[i]);\n\n if (!_duelQueue.contains(duel.attacker.ID)) continue;\n\n duel.defender = createDuelist(getOpponent(duel.attacker.ID));\n\n duel.tier = getArenaTierForLevel(duel.attacker.level);\n duel.cost = getDuelCostByTier(duel.tier);\n\n duel.attacker.power = getCharacterPower(duel.attacker.ID);\n duel.defender.power = getCharacterPower(duel.defender.ID);\n\n duel.attacker.roll = _getCharacterPowerRoll(duel.attacker, duel.defender.trait);\n duel.defender.roll = _getCharacterPowerRoll(duel.defender, duel.attacker.trait);\n\n // Reduce defender roll if attacker has a shield\n if (fighterByCharacter[duel.attacker.ID].useShield) {\n uint24 attackerShieldDefense = 3;\n\n uint8 attackerShieldTrait = shields.getTrait(\n fighterByCharacter[duel.attacker.ID].shieldID\n );\n\n if (\n Common.isTraitEffectiveAgainst(attackerShieldTrait, duel.defender.trait)\n ) {\n attackerShieldDefense = 10;\n }\n\n duel.defender.roll = uint24(\n (duel.defender.roll.mul(uint24(100).sub(attackerShieldDefense)))\n .div(100)\n );\n }\n\n // Reduce attacker roll if defender has a shield\n if (fighterByCharacter[duel.defender.ID].useShield) {\n uint24 defenderShieldDefense = 3;\n\n uint8 defenderShieldTrait = shields.getTrait(\n fighterByCharacter[duel.defender.ID].shieldID\n );\n\n if (\n Common.isTraitEffectiveAgainst(defenderShieldTrait, duel.attacker.trait)\n ) {\n defenderShieldDefense = 10;\n }\n\n duel.attacker.roll = uint24(\n (duel.attacker.roll.mul(uint24(100).sub(defenderShieldDefense)))\n .div(100)\n );\n }\n\n duel.attackerWon = (duel.attacker.roll >= duel.defender.roll);\n\n uint256 winnerID = duel.attackerWon\n ? duel.attacker.ID\n : duel.defender.ID;\n uint256 loserID = duel.attackerWon\n ? duel.defender.ID\n : duel.attacker.ID;\n\n if (winnerID == duel.attacker.ID && duel.attacker.power < duel.defender.power) {\n duel.bonusRank = Common.getBonusRankingPoints(duel.attacker.power, duel.defender.power);\n } else if (winnerID == duel.defender.ID && duel.attacker.power > duel.defender.power) {\n duel.bonusRank = Common.getBonusRankingPoints(duel.defender.power, duel.attacker.power); \n }\n\n emit DuelFinished(\n duel.attacker.ID,\n duel.defender.ID,\n block.timestamp,\n duel.attacker.roll,\n duel.defender.roll,\n duel.attackerWon,\n duel.bonusRank\n );\n\n BountyDistribution\n memory bountyDistribution = _getDuelBountyDistribution(\n duel.cost\n );\n\n fighterByCharacter[winnerID].wager = fighterByCharacter[winnerID]\n .wager\n .add(bountyDistribution.winnerReward);\n\n uint256 loserWager;\n\n if (\n fighterByCharacter[loserID].wager <\n bountyDistribution.loserPayment\n ) {\n loserWager = 0;\n } else {\n loserWager = fighterByCharacter[loserID].wager.sub(\n bountyDistribution.loserPayment\n );\n }\n\n fighterByCharacter[loserID].wager = loserWager;\n\n delete matchByFinder[duel.attacker.ID];\n delete finderByOpponent[duel.defender.ID];\n isDefending[duel.defender.ID] = false;\n\n if (\n fighterByCharacter[loserID].wager < duel.cost ||\n fighterByCharacter[loserID].wager <\n getEntryWagerByTier(duel.tier).mul(withdrawFeePercent).div(100)\n ) {\n _removeCharacterFromArena(loserID, duel.tier);\n emit CharacterKicked(\n loserID,\n winnerID,\n block.timestamp\n );\n } else {\n _matchableCharactersByTier[duel.tier].add(loserID);\n }\n\n _matchableCharactersByTier[duel.tier].add(winnerID);\n\n // Add ranking points to the winner\n\n rankingPointsByCharacter[winnerID] = rankingPointsByCharacter[\n winnerID\n ].add(winningPoints.add(duel.bonusRank));\n\n // Check if the loser's current raking points are 'losingPoints' or less and set them to 0 if that's the case, else subtract the ranking points\n // if (rankingPointsByCharacter[loserID] <= losingPoints) {\n // rankingPointsByCharacter[loserID] = 0;\n // } else {\n // rankingPointsByCharacter[loserID] = rankingPointsByCharacter[\n // loserID\n // ].sub(losingPoints);\n // }\n\n _processWinner(winnerID, duel.tier);\n _processLoser(loserID, duel.tier);\n\n // Add to the rankings pool\n rankingsPoolByTier[duel.tier] = rankingsPoolByTier[\n duel.tier\n ].add(bountyDistribution.rankingPoolTax / 2);\n\n gameCofferTaxDue += bountyDistribution.rankingPoolTax / 2;\n\n _duelQueue.remove(duel.attacker.ID);\n }\n }\n\n /// @dev updates the rank of the winner of a duel\n function _processWinner(uint256 winnerID, uint8 tier) private {\n uint256 rankingPoints = rankingPointsByCharacter[winnerID];\n uint256[] storage topRankingCharacters = _topRankingCharactersByTier[\n tier\n ];\n uint256 winnerPosition;\n bool winnerInRanking;\n\n // check if winner is withing the top 4\n for (uint8 i = 0; i < topRankingCharacters.length; i++) {\n if (winnerID == topRankingCharacters[i]) {\n winnerPosition = i;\n winnerInRanking = true;\n break;\n }\n }\n \n // if the winner is not in the top characters we then compare it to the last character of the top rank, swapping positions if the condition is met\n if (\n !winnerInRanking &&\n rankingPoints >=\n rankingPointsByCharacter[\n topRankingCharacters[topRankingCharacters.length - 1]\n ]\n ) {\n topRankingCharacters[topRankingCharacters.length - 1] = winnerID;\n winnerPosition = topRankingCharacters.length - 1;\n }\n\n for (winnerPosition; winnerPosition > 0; winnerPosition--) {\n if (\n rankingPointsByCharacter[\n topRankingCharacters[winnerPosition]\n ] >=\n rankingPointsByCharacter[\n topRankingCharacters[winnerPosition - 1]\n ]\n ) {\n uint256 oldCharacter = topRankingCharacters[winnerPosition - 1];\n topRankingCharacters[winnerPosition - 1] = winnerID;\n topRankingCharacters[winnerPosition] = oldCharacter;\n } else {\n break;\n }\n }\n }\n\n /// @dev updates the rank of the loser of a duel\n function _processLoser(uint256 loserID, uint8 tier) private {\n uint256 rankingPoints = rankingPointsByCharacter[loserID];\n uint256[] storage ranking = _topRankingCharactersByTier[tier];\n uint256 loserPosition;\n bool loserFound;\n\n // check if the loser is in the top 4\n for (uint8 i = 0; i < ranking.length; i++) {\n if (loserID == ranking[i]) {\n loserPosition = i;\n loserFound = true;\n break;\n }\n }\n // if the character is within the top 4, compare it to the character that precedes it and swap positions if the condition is met\n if (loserFound) {\n for (\n loserPosition;\n loserPosition < ranking.length - 1;\n loserPosition++\n ) {\n if (\n rankingPoints <\n rankingPointsByCharacter[ranking[loserPosition + 1]]\n ) {\n uint256 oldCharacter = ranking[loserPosition + 1];\n ranking[loserPosition + 1] = loserID;\n ranking[loserPosition] = oldCharacter;\n } else {\n break;\n }\n }\n }\n }\n\n /// @dev wether or not the character is still in time to start a duel\n function isCharacterWithinDecisionTime(uint256 characterID)\n public\n view\n returns (bool)\n {\n return\n matchByFinder[characterID].createdAt.add(decisionSeconds) >\n block.timestamp;\n }\n\n /// @dev checks wether or not the character is actively someone else's opponent\n function isCharacterUnderAttack(uint256 characterID)\n public\n view\n returns (bool)\n {\n if (finderByOpponent[characterID] == 0) {\n if (matchByFinder[0].defenderID == characterID) {\n return isCharacterWithinDecisionTime(0);\n }\n return false;\n }\n\n return isCharacterWithinDecisionTime(finderByOpponent[characterID]);\n }\n\n /// @dev checks wether or not the character is currently in the duel queue\n function isCharacterInDuel(uint256 characterID)\n public\n view\n returns (bool)\n {\n return _duelQueue.contains(characterID) || isDefending[characterID];\n }\n\n /// @dev gets the amount of SKILL required to enter the arena\n function getEntryWager(uint256 characterID) public view returns (uint256) {\n return getDuelCost(characterID).mul(wageringFactor);\n }\n\n /// @dev gets the amount of SKILL required to enter the arena by tier\n function getEntryWagerByTier(uint8 tier) public view returns (uint256) {\n return getDuelCostByTier(tier).mul(wageringFactor);\n }\n\n /// @dev gets the amount of SKILL that is risked per duel\n function getDuelCost(uint256 characterID) public view returns (uint256) {\n int128 tierExtra = ABDKMath64x64\n .divu(getArenaTier(characterID).mul(100), 100)\n .mul(_tierWagerUSD);\n\n return game.usdToSkill(_baseWagerUSD.add(tierExtra));\n }\n\n /// @dev gets the amount of SKILL that is risked per duel by tier\n function getDuelCostByTier(uint8 tier) public view returns (uint256) {\n int128 tierExtra = ABDKMath64x64\n .divu(tier.mul(100), 100)\n .mul(_tierWagerUSD);\n\n return game.usdToSkill(_baseWagerUSD.add(tierExtra));\n }\n\n /// @dev gets the arena tier of a character (tiers are 1-10, 11-20, etc...)\n function getArenaTier(uint256 characterID) public view returns (uint8) {\n uint8 level = characters.getLevel(characterID);\n return getArenaTierForLevel(level);\n }\n\n function getArenaTierForLevel(uint8 level) public pure returns (uint8) {\n return uint8(level.div(10));\n }\n\n /// @dev get an attacker's opponent\n function getOpponent(uint256 attackerID) public view returns (uint256) {\n return matchByFinder[attackerID].defenderID;\n }\n\n /// @dev get the top ranked characters by a character's ID\n function getTierTopCharacters(uint8 tier)\n public\n view\n returns (uint256[] memory)\n {\n uint256 arrayLength;\n // we return only the top 3 players, returning the array without the pivot ranker if it exists\n if (\n _topRankingCharactersByTier[tier].length == _maxTopCharactersPerTier\n ) {\n arrayLength = _topRankingCharactersByTier[tier].length - 1;\n } else {\n arrayLength = _topRankingCharactersByTier[tier].length;\n }\n uint256[] memory topRankers = new uint256[](arrayLength);\n for (uint256 i = 0; i < arrayLength; i++) {\n topRankers[i] = _topRankingCharactersByTier[tier][i];\n }\n\n return topRankers;\n }\n\n /// @dev returns ranked prize percentages distribution\n function getPrizePercentages() external view returns (uint256[] memory) {\n return prizePercentages;\n }\n\n /// @dev returns the account's ranking prize pool earnings\n function getPlayerPrizePoolRewards() public view returns (uint256) {\n return _rankingRewardsByPlayer[msg.sender];\n }\n\n /// @dev returns the current duel queue\n function getDuelQueue() public view returns (uint256[] memory) {\n uint256 length = _duelQueue.length();\n uint256[] memory values = new uint256[](length);\n\n for (uint256 i = 0; i < length; i++) {\n values[i] = _duelQueue.at(i);\n }\n\n return values;\n }\n\n /// @dev assigns an opponent to a character\n function _assignOpponent(uint256 characterID, uint8 tier) private {\n EnumerableSet.UintSet\n storage matchableCharacters = _matchableCharactersByTier[tier];\n\n require(matchableCharacters.length() != 0, \"N1\");\n\n uint256 seed = randoms.getRandomSeed(msg.sender);\n uint256 randomIndex = RandomUtil.randomSeededMinMax(\n 0,\n matchableCharacters.length() - 1,\n seed\n );\n uint256 opponentID;\n uint256 matchableCharactersCount = matchableCharacters.length();\n bool foundOpponent = false;\n\n for (uint256 i = 0; i < matchableCharactersCount; i++) {\n uint256 index = (randomIndex + i) % matchableCharactersCount;\n uint256 candidateID = matchableCharacters.at(index);\n\n if (candidateID == characterID) {\n if (matchableCharactersCount == 1) {\n break;\n }\n if (\n matchableCharacters.at(matchableCharactersCount - 1) ==\n candidateID\n ) {\n candidateID = matchableCharacters.at(0);\n } else {\n candidateID = matchableCharacters.at(index + 1);\n }\n }\n if (\n _ownerByCharacter[candidateID] == msg.sender\n ) {\n continue;\n }\n\n foundOpponent = true;\n opponentID = candidateID;\n break;\n }\n\n require(foundOpponent, \"NE\");\n\n matchByFinder[characterID] = Match(\n characterID,\n opponentID,\n block.timestamp\n );\n finderByOpponent[opponentID] = characterID;\n _matchableCharactersByTier[tier].remove(characterID);\n _matchableCharactersByTier[tier].remove(opponentID);\n }\n\n /// @dev increases a player's withdrawable funds depending on their position in the ranked leaderboard\n function _assignRewards(\n uint256 characterID,\n uint8 position,\n uint256 pool\n ) private {\n uint256 percentage = prizePercentages[position];\n uint256 amountToTransfer = (pool.mul(percentage)).div(100);\n address playerToTransfer = _ownerByCharacter[characterID];\n\n _rankingRewardsByPlayer[playerToTransfer] = _rankingRewardsByPlayer[\n playerToTransfer\n ].add(amountToTransfer);\n }\n\n /// @dev removes a character from arena and clears it's matches\n function _removeCharacterFromArena(uint256 characterID, uint8 tier)\n private\n characterInArena(characterID)\n {\n Fighter storage fighter = fighterByCharacter[characterID];\n\n uint256 weaponID = fighter.weaponID;\n uint256 shieldID = fighter.shieldID;\n\n excessWagerByCharacter[characterID] = fighter.wager;\n\n // Shield removed first before the fighter is deleted\n if (fighter.useShield) {\n isShieldInArena[shieldID] = false;\n shields.setNftVar(shieldID, 1, 0);\n }\n\n delete fighterByCharacter[characterID];\n delete matchByFinder[characterID];\n\n if (_matchableCharactersByTier[tier].contains(characterID)) {\n _matchableCharactersByTier[tier].remove(characterID);\n }\n\n isCharacterInArena[characterID] = false;\n isWeaponInArena[weaponID] = false;\n\n // setting characters, weapons and shield NFTVAR_BUSY to 0\n characters.setNftVar(characterID, 1, 0);\n weapons.setNftVar(weaponID, 1, 0);\n }\n\n function _getCharacterPowerRoll(Duelist memory character, uint8 opponentTrait)\n private\n view\n returns (uint24)\n {\n uint24 playerFightPower = getCharacterPower(character.ID);\n\n Fighter memory fighter = fighterByCharacter[character.ID];\n uint256 weaponID = fighter.weaponID;\n uint256 seed = randoms.getRandomSeedUsingHash(\n _ownerByCharacter[character.ID],\n blockhash(block.number - 1)\n );\n\n uint8 weaponTrait = weapons.getTrait(weaponID);\n\n int128 playerTraitBonus = getPVPTraitBonusAgainst(\n character.trait,\n weaponTrait,\n opponentTrait\n );\n\n uint256 playerPower = RandomUtil.plusMinus30PercentSeeded(\n playerFightPower,\n seed\n );\n\n return uint24(playerTraitBonus.mulu(playerPower));\n }\n\n function getCharacterPower(uint256 characterID)\n public\n view\n characterInArena(characterID)\n returns (uint24) \n {\n int128 bonusShieldStats;\n \n (\n ,\n int128 weaponMultFight,\n uint24 weaponBonusPower,\n \n ) = weapons.getFightData(fighterByCharacter[characterID].weaponID, characters.getTrait(characterID));\n\n if (fighterByCharacter[characterID].useShield) {\n // we set bonus shield stats as 0.2\n // Note: hardcoded - copied in _getCharacterPowerRoll\n bonusShieldStats = _getShieldStats(characterID).sub(1).mul(20).div(100);\n }\n\n return ( \n Common.getPlayerPowerBase100(\n Common.getPowerAtLevel(characters.getLevel(characterID)),\n (weaponMultFight.add(bonusShieldStats)),\n weaponBonusPower)\n );\n }\n\n function getPVPTraitBonusAgainst(\n uint8 characterTrait,\n uint8 weaponTrait,\n uint8 opponentTrait\n ) public view returns (int128) {\n int128 traitBonus = ABDKMath64x64.fromUInt(1);\n int128 fightTraitBonus = game.fightTraitBonus();\n int128 charTraitFactor = ABDKMath64x64.divu(50, 100);\n if (characterTrait == weaponTrait) {\n traitBonus = traitBonus.add(fightTraitBonus.mul(3));\n }\n\n // We apply 50% of char trait bonuses because they are applied twice (once per fighter)\n if (\n Common.isTraitEffectiveAgainst(characterTrait, opponentTrait)\n ) {\n traitBonus = traitBonus.add(fightTraitBonus.mul(charTraitFactor));\n } else if (\n Common.isTraitEffectiveAgainst(opponentTrait, characterTrait)\n ) {\n traitBonus = traitBonus.sub(fightTraitBonus.mul(charTraitFactor));\n }\n return traitBonus;\n }\n\n function _getShieldStats(uint256 characterID)\n private\n view\n returns (int128)\n {\n uint8 trait = characters.getTrait(characterID);\n uint256 shieldID = fighterByCharacter[characterID].shieldID;\n int128 shieldMultFight = shields.getDefenseMultiplierForTrait(shieldID, trait);\n return (shieldMultFight);\n }\n\n function _getDuelBountyDistribution(uint256 duelCost)\n private\n view\n returns (BountyDistribution memory bountyDistribution)\n {\n uint256 bounty = duelCost.mul(2);\n uint256 poolTax = _rankingsPoolTaxPercent.mul(bounty).div(100);\n\n uint256 reward = bounty.sub(poolTax).sub(duelCost);\n\n return BountyDistribution(reward, duelCost, poolTax);\n }\n\n function fillGameCoffers() public restricted {\n skillToken.safeTransfer(address(game), gameCofferTaxDue);\n game.trackIncome(gameCofferTaxDue);\n gameCofferTaxDue = 0;\n }\n\n function setBaseWagerInCents(uint256 cents) external restricted {\n _baseWagerUSD = ABDKMath64x64.divu(cents, 100);\n }\n\n function setTierWagerInCents(uint256 cents) external restricted {\n _tierWagerUSD = ABDKMath64x64.divu(cents, 100);\n }\n\n function setPrizePercentage(uint256 index, uint256 value)\n external\n restricted\n {\n prizePercentages[index] = value;\n }\n\n function setWageringFactor(uint8 factor) external restricted {\n wageringFactor = factor;\n }\n\n function setReRollFeePercent(uint256 percent) external restricted {\n reRollFeePercent = percent;\n }\n\n function setWithdrawFeePercent(uint256 percent) external restricted {\n withdrawFeePercent = percent;\n }\n\n function setRankingsPoolTaxPercent(uint8 percent) external restricted {\n _rankingsPoolTaxPercent = percent;\n }\n\n function setDecisionSeconds(uint256 secs) external restricted {\n decisionSeconds = secs;\n }\n\n function setWinningPoints(uint8 pts) external restricted {\n winningPoints = pts;\n }\n\n function setLosingPoints(uint8 pts) external restricted {\n losingPoints = pts;\n }\n\n function setMaxTopCharactersPerTier(uint8 max) external restricted {\n _maxTopCharactersPerTier = max;\n }\n\n function setSeasonDuration(uint256 duration) external restricted {\n seasonDuration = duration;\n }\n\n function setArenaAccess(uint256 accessFlags) external restricted {\n arenaAccess = accessFlags;\n }\n\n function setDuelOffsetCost(uint256 cost) external restricted {\n duelOffsetCost = cost;\n }\n\n function setPvpBotAddress(address payable botAddress) external restricted {\n pvpBotAddress = botAddress;\n }\n\n function increaseRankingsPool(uint8 tier, uint256 amount) external restricted {\n rankingsPoolByTier[tier] = rankingsPoolByTier[tier].add(amount);\n }\n\n /// @dev returns the amount of matcheable characters\n function getMatchablePlayerCount(uint256 characterID) public view returns(uint){\n uint8 tier = getArenaTier(characterID);\n return _matchableCharactersByTier[tier].length(); \n }\n\n function forceRemoveCharacterFromArena(uint256 characterID)\n external \n restricted\n characterNotUnderAttack(characterID)\n characterNotInDuel(characterID)\n {\n Fighter storage fighter = fighterByCharacter[characterID];\n uint8 tier = getArenaTier(characterID);\n uint256 wager = fighter.wager;\n uint256 entryWager = getEntryWager(characterID);\n\n if (matchByFinder[characterID].createdAt != 0) {\n if (wager < entryWager.mul(withdrawFeePercent).div(100)) {\n wager = 0;\n } else {\n wager = wager.sub(entryWager.mul(withdrawFeePercent).div(100));\n }\n }\n\n _removeCharacterFromArena(characterID, tier);\n\n excessWagerByCharacter[characterID] = 0;\n fighter.wager = 0;\n\n skillToken.safeTransfer(characters.ownerOf(characterID), wager);\n }\n\n // Note: The following are debugging functions..\n\n function clearDuelQueue(uint256 length) external restricted {\n for (uint256 i = 0; i < length; i++) {\n if (matchByFinder[_duelQueue.at(i)].defenderID > 0) {\n isDefending[matchByFinder[_duelQueue.at(i)].defenderID] = false;\n }\n\n _duelQueue.remove(_duelQueue.at(i));\n }\n\n isDefending[0] = false;\n }\n\n // Note: Unmute this to test ranking interactions \n \n // function setRankingPoints(uint256 characterID, uint8 newRankingPoints)\n // public\n // restricted\n // {\n // rankingPointsByCharacter[characterID] = newRankingPoints;\n // }\n}\n"
},
"contracts/Launchpad.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/utils/EnumerableSet.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\nimport \"./common.sol\";\nimport \"./cryptoblades.sol\";\n\ncontract Launchpad is Initializable, AccessControlUpgradeable {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n using EnumerableSet for EnumerableSet.AddressSet;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n struct Launch {\n string name;\n string tokenSymbol;\n string detailsJsonUri;\n string imageUrl;\n address fundingTokenAddress;\n uint256 phase;\n bool commitOnly;\n }\n\n // VARS\n mapping(uint256 => uint256) public vars;\n uint256 public constant VAR_TIERS_AMOUNT = 1;\n uint256 public constant VAR_FUNDING_PERIOD_PHASE_1 = 2;\n uint256 public constant VAR_FUNDING_PERIOD_PHASE_2 = 3;\n uint256 public constant VAR_UNCLAIMED_TO_ALLOCATION_MULTIPLIER = 4;\n uint256 public constant VAR_UNCLAIMED_ALLOCATION_PERCENTAGE = 5;\n uint256 public constant VAR_UNCLAIMED_COMMIT_WINDOW = 6;\n uint256 public constant VAR_UNCLAIMED_COMMIT_START_OFFSET = 7;\n \n\n // TIERS INFO\n mapping(uint256 => uint256) public tierStakingRequirement;\n mapping(uint256 => uint256) public tierAllocationWeight;\n\n // LAUNCHPAD PROJECT INFO\n mapping(uint256 => Launch) public launches;\n mapping(uint256 => uint256) public launchTokenPrice;\n mapping(uint256 => uint256) public launchStartTime;\n mapping(uint256 => uint256) public launchFundsToRaise;\n mapping(uint256 => uint256) public launchBaseAllocation;\n mapping(uint256 => uint256) public launchTotalRaised;\n mapping(uint256 => address) public launchTokenAddress;\n\n // USER INFO\n mapping(uint256 => EnumerableSet.AddressSet) launchEligibleUsersSnapshot;\n mapping(uint256 => mapping(address => uint256)) public launchUserStakedAmountSnapshot; \n mapping(uint256 => mapping(address => uint256)) public launchUserInvestment;\n mapping(uint256 => mapping(address => uint256)) public launchUserUnclaimedSkillCommittedValue;\n mapping(uint256 => uint256) public launchTotalUnclaimedSkillCommittedValue;\n mapping(uint256 => mapping(address => uint256)) public launchUserTotalUnclaimedSkillCommittedValue;\n\n // VESTING INFO\n mapping(uint256 => uint256[]) launchPeriodicVestingsPercentages;\n mapping(uint256 => mapping(uint256 => uint256)) public launchPeriodicVestingsStartTimestamps;\n mapping(uint256 => uint256) public launchLinearVestingsDurations;\n mapping(uint256 => uint256) public launchLinearVestingsStartTimestamps;\n mapping(address => mapping(uint256 => uint256)) public userLinearVestingClaimTimestamp;\n mapping(address => mapping(uint256 => mapping(uint256 => bool))) public userClaimedVestingPortion;\n\n uint256 public nextLaunchId;\n uint256 public skillPrice;\n\n CryptoBlades _game;\n\n mapping(address => mapping(uint256 => bool)) public didUserCommitToLaunch;\n\n function initialize(CryptoBlades game) public initializer {\n __AccessControl_init_unchained();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _setupRole(GAME_ADMIN, msg.sender);\n\n _game = game;\n nextLaunchId = 1;\n }\n\n event LaunchAdded(uint256 indexed launchId, uint256 phase);\n event Invested(address indexed user, uint256 amount);\n event SkillCommitted(address indexed user, address indexed targetWallet, uint256 indexed launchId, uint256 amount);\n event PeriodicVestingClaimed(address indexed user, uint256 indexed launchId, uint256 indexed vestingId, uint256 amount);\n event LinearVestingClaimed(address indexed user, uint256 indexed launchId, uint256 amount);\n event FundsWithdrawn(uint256 indexed launchId, uint256 amount);\n event PeriodicVestingEnabled(uint256 indexed launchId, uint256 indexed vestingId, uint256 timestamp);\n event LinearVestingEnabled(uint256 indexed launchId, uint256 timestamp);\n\n // MODIFIERS\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n }\n\n modifier onlyPhase1(uint256 launchId) {\n _onlyPhase1(launchId);\n _;\n }\n\n function _onlyPhase1(uint256 launchId) internal pure {\n require(launchId % 2 == 1, \"Usable only on phase 1\");\n }\n\n modifier isUserWhitelistedForLaunch(address user, uint256 launchId) {\n _isUserWhitelistedForLaunch(user, launchId);\n _;\n }\n\n function _isUserWhitelistedForLaunch(address user, uint256 launchId) internal view {\n require(isUserWhitelisted(user, launchId), \"Not whitelisted\");\n }\n\n modifier isTokenAddressSet(uint256 launchId) {\n _isTokenAddressSet(launchId);\n _;\n }\n\n function _isTokenAddressSet(uint256 launchId) internal view {\n require(launchTokenAddress[launchId] != address(0), \"Token address not set\");\n }\n\n modifier launchNotStarted(uint256 launchId) {\n _launchNotStarted(launchId);\n _;\n }\n\n function _launchNotStarted(uint256 launchId) internal view {\n require(launchStartTime[launchId] == 0 || launchStartTime[launchId] > block.timestamp, \"Launch started\");\n }\n\n // VIEWS\n\n function getTierForStakedAmount(uint256 amount) public view returns (uint256 tier) {\n for (uint256 i = 1; i <= vars[VAR_TIERS_AMOUNT]; i++) {\n if (amount >= tierStakingRequirement[i]) {\n tier++;\n } else {\n break;\n }\n }\n }\n\n function getLaunchAllocationForTier(uint256 launchId, uint256 tier) public view returns (uint256) {\n return launchBaseAllocation[launchId].mul(tierAllocationWeight[tier]);\n }\n\n function getUserRemainingAllocationForLaunch(address user, uint256 launchId) public view returns (uint256 maxAllocation) {\n if(launchId % 2 == 0) {\n maxAllocation = getLaunchAllocationForTier(launchId, getTierForStakedAmount(launchUserStakedAmountSnapshot[launchId.sub(1)][user]));\n }\n else {\n maxAllocation = getLaunchAllocationForTier(launchId, getTierForStakedAmount(launchUserStakedAmountSnapshot[launchId][user]));\n }\n maxAllocation += launchUserTotalUnclaimedSkillCommittedValue[launchId][user].mul(vars[VAR_UNCLAIMED_TO_ALLOCATION_MULTIPLIER]);\n maxAllocation = maxAllocation.sub(launchUserInvestment[launchId][user]);\n }\n\n function getAvailableClaimAmount(uint256 launchId, uint256 vestingId) public onlyPhase1(launchId) view returns (uint256 claimAmount) {\n uint256 decimals = ERC20(launchTokenAddress[launchId]).decimals();\n uint256 totalUserInvestment = launchUserInvestment[launchId][msg.sender].add(launchUserInvestment[launchId + 1][msg.sender]);\n claimAmount = totalUserInvestment.mul(1e18).div(launchTokenPrice[launchId]).mul(launchPeriodicVestingsPercentages[launchId][vestingId]).div(100);\n claimAmount = Common.adjustDecimals(claimAmount, decimals);\n }\n\n function getLinearClaimAmount(uint256 launchId) public onlyPhase1(launchId) view returns (uint256 claimAmount) {\n uint256 decimals = ERC20(launchTokenAddress[launchId]).decimals();\n uint256 totalUserInvestment = launchUserInvestment[launchId][msg.sender].add(launchUserInvestment[launchId + 1][msg.sender]);\n uint256 lastClaimTimestamp = userLinearVestingClaimTimestamp[msg.sender][launchId];\n if(lastClaimTimestamp == 0) {\n lastClaimTimestamp = launchLinearVestingsStartTimestamps[launchId];\n }\n uint256 effectiveClaimTimestamp = launchLinearVestingsStartTimestamps[launchId] + launchLinearVestingsDurations[launchId];\n if(block.timestamp < effectiveClaimTimestamp) {\n effectiveClaimTimestamp = block.timestamp;\n }\n if(lastClaimTimestamp > effectiveClaimTimestamp) {\n lastClaimTimestamp = effectiveClaimTimestamp;\n }\n \n claimAmount = totalUserInvestment.mul(1e18).div(launchTokenPrice[launchId]).mul(effectiveClaimTimestamp.sub(lastClaimTimestamp)).div(launchLinearVestingsDurations[launchId]);\n claimAmount = Common.adjustDecimals(claimAmount, decimals);\n }\n\n function getLaunchDetails(uint256 launchId) public view returns (\n uint256 tokenPrice,\n uint256 startTime,\n uint256 fundsToRaise,\n uint256 maxAllocation,\n uint256 totalRaised,\n address tokenAddress) \n {\n tokenPrice = launchTokenPrice[launchId];\n startTime = launchStartTime[launchId];\n fundsToRaise = launchFundsToRaise[launchId];\n maxAllocation = getOverallMaxAllocation(launchId);\n totalRaised = launchTotalRaised[launchId];\n tokenAddress = launchTokenAddress[launchId];\n }\n\n function getOverallMaxAllocation(uint256 launchId) public view returns (uint256) {\n return getLaunchAllocationForTier(launchId, vars[VAR_TIERS_AMOUNT]);\n }\n\n function isUserWhitelisted(address user, uint256 launchId) public view returns (bool) {\n if(launchId % 2 == 0) launchId = launchId.sub(1);\n return launchEligibleUsersSnapshot[launchId].contains(user);\n }\n\n function getTotalUnlockedPercentage(uint256 launchId) public view returns (uint256 totalUnlockedPercentage) {\n for(uint i = 0; i < launchPeriodicVestingsPercentages[launchId].length; i++) {\n totalUnlockedPercentage += launchPeriodicVestingsPercentages[launchId][i];\n }\n }\n\n function getLaunchVestingPercentages(uint256 launchId) public view returns(uint256[] memory) {\n return launchPeriodicVestingsPercentages[launchId];\n }\n\n function getTotalLaunchUserInvestment(uint256 launchId) public onlyPhase1(launchId) view returns(uint256) {\n return launchUserInvestment[launchId][msg.sender].add(launchUserInvestment[launchId + 1][msg.sender]);\n }\n\n function getUnclaimedCommittedValue(uint256 launchId) public view returns(uint256) {\n return launchTotalUnclaimedSkillCommittedValue[launchId].mul(vars[VAR_UNCLAIMED_TO_ALLOCATION_MULTIPLIER]);\n }\n\n // RESTRICTED FUNCTIONS\n\n // TIERS\n\n function setBrandNewTiers(uint256[] calldata tierIds, uint256[] calldata stakingRequirements, uint256[] calldata tierWeights) external restricted {\n require(tierIds.length == stakingRequirements.length, \"Wrong input\");\n vars[VAR_TIERS_AMOUNT] = tierIds.length;\n for (uint256 i = 0; i < tierIds.length; i++) {\n require(tierIds[i] > 0 && tierIds[i] <= vars[VAR_TIERS_AMOUNT], \"Wrong id\");\n tierStakingRequirement[tierIds[i]] = stakingRequirements[i];\n tierAllocationWeight[tierIds[i]] = tierWeights[i];\n }\n }\n\n function setTiersRequirements(uint256[] calldata tierIds, uint256[] calldata stakingRequirements) external restricted {\n require(tierIds.length <= vars[VAR_TIERS_AMOUNT], \"Too many tiers\");\n require(tierIds.length == stakingRequirements.length, \"Wrong input\");\n for (uint256 i = 0; i < tierIds.length; i++) {\n require(tierIds[i] > 0 && tierIds[i] <= vars[VAR_TIERS_AMOUNT], \"Wrong id\");\n tierStakingRequirement[tierIds[i]] = stakingRequirements[i];\n }\n }\n\n function setTiersAllocationWeights(uint256[] calldata tierIds, uint256[] calldata tierWeights) external restricted {\n require(tierIds.length <= vars[VAR_TIERS_AMOUNT], \"Too many tiers\");\n require(tierIds.length == tierWeights.length, \"Wrong input\");\n for (uint256 i = 0; i < tierIds.length; i++) {\n require(tierIds[i] > 0 && tierIds[i] <= vars[VAR_TIERS_AMOUNT], \"Wrong id\");\n tierAllocationWeight[tierIds[i]] = tierWeights[i];\n }\n }\n\n // PROJECT DETAILS\n\n function addNewLaunch(\n string calldata name,\n string calldata tokenSymbol,\n string calldata detailsJsonUri,\n string calldata imageUrl,\n address fundingTokenAddress,\n bool commitOnly\n ) external restricted {\n launches[nextLaunchId] = Launch(\n name,\n tokenSymbol,\n detailsJsonUri,\n imageUrl,\n fundingTokenAddress,\n 1,\n commitOnly\n );\n\n emit LaunchAdded(nextLaunchId, 1);\n // nextLaunchId + 1 is reserved for Phase 2s\n nextLaunchId += 2;\n }\n\n function addSecondPhaseForLaunch(uint256 launchId, uint256 startTime) external onlyPhase1(launchId) restricted {\n Launch memory lp = launches[launchId];\n require(launchId < nextLaunchId, \"Wrong id\");\n require(launchStartTime[launchId + 1] == 0, \"Already added\");\n require(block.timestamp > launchStartTime[launchId] + vars[VAR_FUNDING_PERIOD_PHASE_1], \"Phase 1 not finished\");\n require(launchFundsToRaise[launchId].sub(launchTotalRaised[launchId]) > 0, \"Tokens sold out\");\n launches[launchId + 1] = Launch(\n lp.name,\n lp.tokenSymbol,\n lp.detailsJsonUri,\n lp.imageUrl,\n lp.fundingTokenAddress,\n 2,\n lp.commitOnly\n );\n \n launchStartTime[launchId + 1] = startTime;\n launchTokenPrice[launchId + 1] = launchTokenPrice[launchId];\n launchFundsToRaise[launchId + 1] = launchFundsToRaise[launchId].sub(launchTotalRaised[launchId]);\n launchBaseAllocation[launchId + 1] = launchBaseAllocation[launchId];\n launchTokenAddress[launchId + 1] = launchTokenAddress[launchId];\n\n emit LaunchAdded(launchId + 1, 2);\n }\n\n function removeLaunch(uint256 launchId) external restricted {\n delete launches[launchId];\n }\n\n // UPDATING DETAILS\n function setLaunchDetails(uint256 launchId, uint256 tokenPrice, uint256 startTime, uint256 fundsToRaise) external restricted {\n launchTokenPrice[launchId] = tokenPrice;\n launchStartTime[launchId] = startTime;\n launchFundsToRaise[launchId] = fundsToRaise;\n }\n\n function updateLaunchName(uint256 launchId, string calldata name) external restricted {\n launches[launchId].name = name;\n }\n\n function updateLaunchTokenSymbol(uint256 launchId, string calldata tokenSymbol) external restricted {\n launches[launchId].tokenSymbol = tokenSymbol;\n }\n\n function updateLaunchDetailsJsonUri(uint256 launchId, string calldata detailsJsonUri) external restricted {\n launches[launchId].detailsJsonUri = detailsJsonUri;\n }\n\n function updateLaunchImageUrl(uint256 launchId, string calldata imageUrl) external restricted {\n launches[launchId].imageUrl = imageUrl;\n }\n\n function updateLaunchFundingTokenAddress(uint256 launchId, address fundingTokenAddress) external launchNotStarted(launchId) restricted {\n launches[launchId].fundingTokenAddress = fundingTokenAddress;\n }\n\n function updateLaunchIsCommitOnly(uint256 launchId, bool commitOnly) external launchNotStarted(launchId) restricted {\n launches[launchId].commitOnly = commitOnly;\n }\n\n function updateLaunchTokenPrice(uint256 launchId, uint256 tokenPrice) external launchNotStarted(launchId) restricted {\n launchTokenPrice[launchId] = tokenPrice;\n }\n\n function updateLaunchStartTime(uint256 launchId, uint256 startTime) external restricted {\n require(launchTokenPrice[launchId] != 0 && launchFundsToRaise[launchId] != 0, \"Set token price and funds to raise first\");\n launchStartTime[launchId] = startTime;\n }\n\n function updateLaunchFundsToRaise(uint256 launchId, uint256 fundsToRaise) external launchNotStarted(launchId) restricted {\n launchFundsToRaise[launchId] = fundsToRaise;\n }\n\n function updateLaunchTokenAddress(uint256 launchId, address tokenAddress) external onlyPhase1(launchId) restricted {\n launchTokenAddress[launchId] = tokenAddress;\n launchTokenAddress[launchId + 1] = tokenAddress;\n }\n\n // VESTING\n\n function enablePeriodicVesting(uint256 launchId, uint256 percentage) external onlyPhase1(launchId) isTokenAddressSet(launchId) restricted {\n require(launchLinearVestingsDurations[launchId] == 0, \"Linear vesting enabled\");\n require(getTotalUnlockedPercentage(launchId) + percentage <= 100, \"Total percentage exceeded\");\n uint256 totalLaunchRaised = launchTotalRaised[launchId].add(launchTotalRaised[launchId + 1]);\n uint256 contractBalance = IERC20(launchTokenAddress[launchId]).balanceOf(address(this));\n uint256 decimals = ERC20(launchTokenAddress[launchId]).decimals();\n contractBalance = Common.adjustDecimals(contractBalance, decimals);\n require(contractBalance >=\n totalLaunchRaised.mul(1e18).div(launchTokenPrice[launchId]).mul(getTotalUnlockedPercentage(launchId) + percentage).div(100),\n \"Not enough balance\");\n\n launchPeriodicVestingsPercentages[launchId].push(percentage);\n uint256 newVestingId = launchPeriodicVestingsPercentages[launchId].length - 1;\n launchPeriodicVestingsStartTimestamps[launchId][newVestingId] = block.timestamp;\n emit PeriodicVestingEnabled(launchId, newVestingId, block.timestamp);\n }\n\n function enableLinearVesting(uint256 launchId, uint256 duration) external onlyPhase1(launchId) isTokenAddressSet(launchId) restricted {\n require(duration > 0);\n require(launchPeriodicVestingsPercentages[launchId].length == 0, \"Periodic vesting enabled\");\n uint256 totalLaunchRaised = launchTotalRaised[launchId].add(launchTotalRaised[launchId + 1]);\n uint256 contractBalance = IERC20(launchTokenAddress[launchId]).balanceOf(address(this));\n uint256 decimals = ERC20(launchTokenAddress[launchId]).decimals();\n contractBalance = Common.adjustDecimals(contractBalance, decimals);\n require(contractBalance >= totalLaunchRaised.mul(1e18).div(launchTokenPrice[launchId]), \"Not enough balance\");\n\n launchLinearVestingsDurations[launchId] = duration;\n launchLinearVestingsStartTimestamps[launchId] = block.timestamp;\n\n emit LinearVestingEnabled(launchId, block.timestamp);\n }\n\n // WHITELISTING\n\n function setEligibleUsersData(uint256 launchId, address[] calldata users, uint256[] calldata stakedAmounts) external onlyPhase1(launchId) launchNotStarted(launchId) restricted {\n require(nextLaunchId >= launchId, \"Wrong ID\");\n require(users.length > 0 && users.length == stakedAmounts.length, \"Bad input\");\n\n uint totalWeight = launchBaseAllocation[launchId] == 0 ? 0 : launchFundsToRaise[launchId].div(launchBaseAllocation[launchId]);\n for(uint i = 0; i < users.length; i++) {\n if(!launchEligibleUsersSnapshot[launchId].add(users[i])) continue;\n launchUserStakedAmountSnapshot[launchId][users[i]] = stakedAmounts[i];\n totalWeight += tierAllocationWeight[getTierForStakedAmount(stakedAmounts[i])];\n }\n launchBaseAllocation[launchId] = launchFundsToRaise[launchId].div(totalWeight);\n }\n\n function setTotalUnclaimedCommitted(uint256 launchId, address[] calldata users, uint256[] calldata totalCommittedValues) external onlyPhase1(launchId) launchNotStarted(launchId) restricted {\n require(nextLaunchId >= launchId, \"Wrong ID\");\n require(users.length > 0 && users.length == totalCommittedValues.length, \"Bad input\");\n uint256 unclaimedCommittedValue = 0;\n for(uint i = 0; i < users.length; i++) {\n launchUserTotalUnclaimedSkillCommittedValue[launchId][users[i]] = totalCommittedValues[i];\n unclaimedCommittedValue += totalCommittedValues[i];\n }\n launchBaseAllocation[launchId] = launchBaseAllocation[launchId].sub(launchBaseAllocation[launchId].mul(unclaimedCommittedValue).div(launchFundsToRaise[launchId]));\n }\n\n // WITHDRAW RAISED FUNDS\n\n function withdrawRaisedFunds(uint256 launchId) external onlyPhase1(launchId) {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not owner\");\n require(\n block.timestamp > launchStartTime[launchId] + vars[VAR_FUNDING_PERIOD_PHASE_1] &&\n block.timestamp > launchStartTime[launchId + 1] + vars[VAR_FUNDING_PERIOD_PHASE_2],\n \"Launch not finished\"\n );\n\n uint256 raisedFunds = launchTotalRaised[launchId].add(launchTotalRaised[launchId + 1]);\n\n IERC20(launches[launchId].fundingTokenAddress).safeTransfer(msg.sender, raisedFunds);\n emit FundsWithdrawn(launchId, raisedFunds);\n }\n\n // VARS SETTERS\n\n function setVar(uint256 varField, uint256 value) external restricted {\n vars[varField] = value;\n }\n\n function setVars(uint256[] calldata varFields, uint256[] calldata values) external restricted {\n for (uint256 i = 0; i < varFields.length; i++) {\n vars[varFields[i]] = values[i];\n }\n }\n\n function setSkillPrice(uint256 price) external restricted {\n skillPrice = price;\n }\n\n // USER FUNCTIONS\n\n function invest(uint256 launchId, uint256 amount) external isUserWhitelistedForLaunch(msg.sender, launchId) {\n Launch memory lp = launches[launchId];\n require(!lp.commitOnly, \"Invest on diff chain\");\n require(launchStartTime[launchId] != 0 && block.timestamp > launchStartTime[launchId], \"Launch not started\");\n require((lp.phase == 1 && block.timestamp < launchStartTime[launchId] + vars[VAR_FUNDING_PERIOD_PHASE_1])\n || (lp.phase == 2 && block.timestamp < launchStartTime[launchId] + vars[VAR_FUNDING_PERIOD_PHASE_2]), \"Launch ended\");\n require(launchTokenPrice[launchId] != 0, \"Token price not set\");\n require(launchTotalRaised[launchId] + amount <= launchFundsToRaise[launchId], \"Amount exceeds remaining supply\");\n require(getUserRemainingAllocationForLaunch(msg.sender, launchId) >= amount, \"Allocation allowance exceeded\");\n\n IERC20(lp.fundingTokenAddress).safeTransferFrom(msg.sender, address(this), amount);\n launchUserInvestment[launchId][msg.sender] += amount;\n launchTotalRaised[launchId] += amount;\n\n emit Invested(msg.sender, amount);\n }\n\n function claimPeriodic(uint256 launchId, uint256 vestingId) external {\n require(launchPeriodicVestingsPercentages[launchId].length >= vestingId, \"Unavailable\");\n require(!userClaimedVestingPortion[msg.sender][launchId][vestingId], \"Already claimed\");\n\n userClaimedVestingPortion[msg.sender][launchId][vestingId] = true;\n uint256 claimAmount = getAvailableClaimAmount(launchId, vestingId);\n IERC20(launchTokenAddress[launchId]).safeTransfer(msg.sender, claimAmount);\n\n emit PeriodicVestingClaimed(msg.sender, launchId, vestingId, claimAmount);\n }\n\n function claimLinear(uint256 launchId) external {\n require(launchLinearVestingsDurations[launchId] > 0, \"Unavailable\");\n\n uint256 claimAmount = getLinearClaimAmount(launchId);\n userLinearVestingClaimTimestamp[msg.sender][launchId] = block.timestamp;\n IERC20(launchTokenAddress[launchId]).safeTransfer(msg.sender, claimAmount);\n\n emit LinearVestingClaimed(msg.sender, launchId, claimAmount);\n }\n\n function commitUnclaimedSkill(uint256 launchId, uint256 amount, address whitelistedWallet) external isUserWhitelistedForLaunch(whitelistedWallet, launchId) launchNotStarted(launchId) onlyPhase1(launchId) {\n uint256 windowStartTime = launchStartTime[launchId].sub(vars[VAR_UNCLAIMED_COMMIT_START_OFFSET]);\n require(block.timestamp >= windowStartTime && block.timestamp <= windowStartTime.add(vars[VAR_UNCLAIMED_COMMIT_WINDOW]), \"Not a commit window\");\n uint256 committingValue = amount.mul(skillPrice).div(1e18);\n require((launchTotalUnclaimedSkillCommittedValue[launchId].add(committingValue)).mul(vars[VAR_UNCLAIMED_TO_ALLOCATION_MULTIPLIER]) <= vars[VAR_UNCLAIMED_ALLOCATION_PERCENTAGE].mul(launchFundsToRaise[launchId]).div(100), \"Unclaimed limit reached\");\n //_game.deductAfterPartnerClaim(amount, msg.sender); // turned off (temporarily?), supposedly shouldn't deduct unclaimed on committing\n require(!didUserCommitToLaunch[msg.sender][launchId], \"Already committed\");\n launchUserUnclaimedSkillCommittedValue[launchId][whitelistedWallet] += committingValue;\n launchTotalUnclaimedSkillCommittedValue[launchId] += committingValue;\n didUserCommitToLaunch[msg.sender][launchId] = true;\n \n emit SkillCommitted(msg.sender, whitelistedWallet, launchId, amount);\n }\n}\n"
},
"contracts/skillToken.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title SimpleToken\n * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.\n * Note they can later distribute these tokens as they wish using `transfer` and other\n * `ERC20` functions.\n * Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/examples/SimpleToken.sol\n */\ncontract SkillToken is ERC20 {\n /**\n * @dev Constructor that gives msg.sender all of existing tokens.\n */\n constructor() public ERC20(\"Skill Token\", \"SKILL\") {\n _mint(address(this), 1000000 * (10 ** uint256(decimals())));\n _approve(address(this), msg.sender, totalSupply());\n }\n}"
},
"contracts/ExperimentToken2.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract ExperimentToken2 is ERC20 {\n /**\n * @dev Constructor that gives msg.sender all of existing tokens.\n */\n constructor() public ERC20(\"Experiment Token 2\", \"EXP2\") {\n _mint(address(this), 1000000 * (10**uint256(decimals())));\n _approve(address(this), msg.sender, totalSupply());\n }\n}\n"
},
"contracts/ExperimentToken.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract ExperimentToken is ERC20 {\n /**\n * @dev Constructor that gives msg.sender all of existing tokens.\n */\n constructor() public ERC20(\"Experiment Token\", \"EXP\") {\n _mint(address(this), 1000000 * (10**uint256(decimals())));\n _approve(address(this), msg.sender, totalSupply());\n }\n}\n"
},
"contracts/Merchandise.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"./cryptoblades.sol\";\n\ncontract Merchandise is Initializable, AccessControlUpgradeable {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using SafeMath for uint64;\n\n // NOTE: It was decided to not calculate prices on-chain, so a lot of variables/functions are unused\n\n /* ========== CONSTANTS ========== */\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n uint8 public constant STATUS_PAID = 0;\n\n uint256 public constant LINK_SKILL_TOKEN = 1;\n\n uint256 public constant VAR_ORDERS_ENABLED = 1; // can orders be placed?\n uint256 public constant VAR_TRACK_INCOME = 2; // will income go to payouts?\n\n /* ========== STATE VARIABLES ========== */\n\n CryptoBlades public game;\n IPriceOracle public skillOracle;\n\n mapping(uint256 => address) public links;\n mapping(uint256 => uint256) public vars;\n\n mapping(uint256 => uint256) public itemPrices; // stored in USD cents\n\n uint256 public nextOrderID;\n uint256 public lowestUnprocessedOrderID;\n mapping(uint256 => address) public orderBuyer;\n mapping(uint256 => uint256) public orderPaidAmount;\n mapping(uint256 => uint32[]) public orderBaskets; // 8 bits amount, 24 bits itemID\n mapping(uint256 => uint256) public orderData; // 8 bits status, rest is timestamp (for now)\n mapping(uint256 => uint256) public externalOrderId;\n\n event OrderPlaced(address indexed buyer, uint256 indexed orderId, uint256 paid, uint256[] items, uint8[] amounts);\n event OrderSaved(address indexed user, uint256 indexed orderNumber, uint256 indexed internalOrderNumber, uint256 payingAmount);\n event OrderStatusChanged(uint256 indexed orderId, uint8 indexed newStatus);\n\n /* ========== INITIALIZERS AND MIGRATORS ========== */\n\n function initialize(CryptoBlades _game, IPriceOracle _skillOracle)\n public\n initializer\n {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n game = _game;\n skillOracle = _skillOracle;\n }\n\n /* ========== VIEWS ========== */\n\n function getUserAllowanceRequired(address user, uint256 charge) public view returns (uint256) {\n uint256 unclaimedSkill = game.getTokenRewardsFor(user);\n return charge < unclaimedSkill ? 0 : charge-unclaimedSkill;\n }\n\n function canUserBeCharged(address user, uint256 charge) public view returns (bool) {\n uint256 allowanceRequired = getUserAllowanceRequired(user, charge);\n return IERC20(links[LINK_SKILL_TOKEN]).allowance(user, address(game)) >= allowanceRequired;\n }\n\n /* ========== MUTATIVE FUNCTIONS ========== */\n\n function recoverToken(address tokenAddress, uint256 amount) external isAdmin {\n IERC20(tokenAddress).safeTransfer(msg.sender, amount);\n }\n\n function setLink(uint256 linkId, address linkAddress) external isAdmin {\n links[linkId] = linkAddress;\n }\n\n function setSkillOracle(IPriceOracle newOracle) external isAdmin {\n skillOracle = newOracle;\n }\n\n function setItemPrice(uint256 item, uint256 usdCents) external restricted {\n itemPrices[item] = usdCents;\n }\n\n function createOrder(uint256 orderNumber, uint256 payingAmount) external returns (uint256) {\n require(vars[VAR_ORDERS_ENABLED] != 0, \"Cannot place orders right now\");\n orderBuyer[orderNumber] = msg.sender;\n game.payContractTokenOnly(msg.sender, payingAmount, vars[VAR_TRACK_INCOME] != 0);\n orderPaidAmount[orderNumber] += payingAmount;\n externalOrderId[nextOrderID] = orderNumber;\n\n emit OrderSaved(msg.sender, orderNumber, nextOrderID, payingAmount);\n return nextOrderID++;\n }\n\n function getOrderPaidAmount(uint256 orderNumber) public view returns (uint256) {\n return orderPaidAmount[orderNumber];\n }\n\n function setVar(uint256 varField, uint256 value) external restricted {\n vars[varField] = value;\n }\n\n function setVars(uint256[] calldata varFields, uint256[] calldata values) external restricted {\n for(uint i = 0; i < varFields.length; i++) {\n vars[varFields[i]] = values[i];\n }\n }\n\n /* ========== MODIFIERS ========== */\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"Not game admin\");\n }\n\n modifier isAdmin() {\n _isAdmin();\n _;\n }\n\n function _isAdmin() internal view {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n }\n\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"../../utils/ContextUpgradeable.sol\";\nimport \"./IERC20Upgradeable.sol\";\nimport \"../../math/SafeMathUpgradeable.sol\";\nimport \"../../proxy/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin guidelines: functions revert instead\n * of returning `false` on failure. This behavior is nonetheless conventional\n * and does not conflict with the expectations of ERC20 applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable {\n using SafeMathUpgradeable for uint256;\n\n mapping (address => uint256) private _balances;\n\n mapping (address => mapping (address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for {name} and {symbol}, initializes {decimals} with\n * a default value of 18.\n *\n * To select a different value for {decimals}, use {_setupDecimals}.\n *\n * All three of these values are immutable: they can only be set once during\n * construction.\n */\n function __ERC20_init(string memory name_, string memory symbol_) internal initializer {\n __Context_init_unchained();\n __ERC20_init_unchained(name_, symbol_);\n }\n\n function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {\n _name = name_;\n _symbol = symbol_;\n _decimals = 18;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is\n * called.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual returns (uint8) {\n return _decimals;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, \"ERC20: transfer amount exceeds allowance\"));\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, \"ERC20: decreased allowance below zero\"));\n return true;\n }\n\n /**\n * @dev Moves tokens `amount` from `sender` to `recipient`.\n *\n * This is internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(address sender, address recipient, uint256 amount) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n _balances[sender] = _balances[sender].sub(amount, \"ERC20: transfer amount exceeds balance\");\n _balances[recipient] = _balances[recipient].add(amount);\n emit Transfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply = _totalSupply.add(amount);\n _balances[account] = _balances[account].add(amount);\n emit Transfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n _balances[account] = _balances[account].sub(amount, \"ERC20: burn amount exceeds balance\");\n _totalSupply = _totalSupply.sub(amount);\n emit Transfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(address owner, address spender, uint256 amount) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Sets {decimals} to a value other than the default one of 18.\n *\n * WARNING: This function should only be called from the constructor. Most\n * applications that interact with token contracts will not expect\n * {decimals} to ever change, and may work incorrectly if it does.\n */\n function _setupDecimals(uint8 decimals_) internal virtual {\n _decimals = decimals_;\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be to transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }\n uint256[44] private __gap;\n}\n"
},
"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20Upgradeable {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"
},
"contracts/JunkBridgeProxyContract.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"./items/Junk.sol\";\nimport \"./interfaces/IBridgeProxy.sol\";\n\n\ncontract JunkBridgeProxyContract is Initializable, AccessControlUpgradeable, IBridgeProxy {\n \n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n Junk junk;\n address nftStorageAddress;\n bool enabled;\n\n uint8 public constant UINT_NFT_VAR_META = 0;\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"NA\");\n }\n\n function initialize(address _nftStorageAddress, address _junk) public initializer {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n nftStorageAddress = _nftStorageAddress;\n junk = Junk(_junk);\n }\n\n \n function collectData(uint256 tokenId) external view override returns (uint256[] memory uintVars, string memory stringVar) {\n \n (uint8 _stars) = junk.get(tokenId);\n\n uintVars = new uint256[](1);\n uintVars[UINT_NFT_VAR_META] = _packJunkData(_stars);\n }\n\n // for future use, bot will probe the returned value to know if the proxy contract has proper signature behavior\n function sigVersion() external view override returns (uint256) {\n return 3;\n }\n\n function isEnabled() external view override returns (bool) {\n return enabled;\n }\n\n function setEnabled(bool _enabled) external restricted {\n enabled = _enabled;\n }\n\n function mintOrUpdate(address /*receiver*/, uint256 tokenId, uint256[] calldata uintVars, string calldata stringVar) external restricted override returns (uint256) {\n require(enabled, \"not enabled\");\n\n tokenId = \n junk.performMintJunkDetailed(nftStorageAddress, uintVars[UINT_NFT_VAR_META], tokenId);\n\n return tokenId;\n }\n\n function _packJunkData(uint8 stars) public pure returns (uint256) {\n return uint256(stars);\n }\n\n function canBridge(address wallet, uint256 tokenId, uint256 targetChain) external view override returns (bool) {\n return true;\n }\n}\n"
},
"contracts/CBKLandT3StakingRewardsUpgradeable.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./staking/NftStakingRewardsUpgradeable.sol\";\nimport \"./CBKLand.sol\";\n\ncontract CBKLandT3StakingRewardsUpgradeable is NftStakingRewardsUpgradeable {\n CBKLand internal __cbkLand;\n uint256 public stakeTier;\n\n function initialize(\n address _owner,\n address _rewardsDistribution,\n address _rewardsToken,\n address _stakingToken,\n uint256 _minimumStakeTime,\n CBKLand _cbkLand\n ) public initializer {\n super.initialize(\n _owner,\n _rewardsDistribution,\n _rewardsToken,\n _stakingToken,\n _minimumStakeTime);\n __cbkLand = _cbkLand;\n stakeTier = 3;\n }\n\n function stake(uint256 id) public virtual override {\n uint256 landTier = __cbkLand.getLandTier(id);\n require(landTier == stakeTier, 'Land tier mismatch');\n super.stake(id);\n }\n\n function bulkStake(uint256[] memory ids) public virtual override {\n for(uint i = 0; i < ids.length; i++) {\n uint256 landTier = __cbkLand.getLandTier(ids[i]);\n require(landTier == stakeTier, 'Land tier mismatch');\n }\n super.bulkStake(ids);\n }\n}"
},
"contracts/staking/NftStakingRewardsUpgradeable.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/Math.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport \"@openzeppelin/contracts/utils/EnumerableSet.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol\";\n\n// Inheritance\nimport \"./interfaces/INftStakingRewards.sol\";\nimport \"./RewardsDistributionRecipientUpgradeable.sol\";\nimport \"./FailsafeUpgradeable.sol\";\nimport \"../CBKLand.sol\";\n\n// https://docs.synthetix.io/contracts/source/contracts/stakingrewards\ncontract NftStakingRewardsUpgradeable is\n INftStakingRewards,\n IERC721ReceiverUpgradeable,\n Initializable,\n RewardsDistributionRecipientUpgradeable,\n ReentrancyGuardUpgradeable,\n FailsafeUpgradeable,\n PausableUpgradeable\n{\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n using EnumerableSet for EnumerableSet.UintSet;\n\n /* ========== STATE VARIABLES ========== */\n\n IERC20 public rewardsToken;\n IERC721 public stakingToken;\n uint256 public periodFinish;\n uint256 public override rewardRate;\n uint256 public override rewardsDuration;\n uint256 public override minimumStakeAmount;\n uint256 public override minimumStakeTime;\n uint256 public lastUpdateTime;\n uint256 public rewardPerTokenStored;\n\n mapping(address => uint256) public userRewardPerTokenPaid;\n mapping(address => uint256) public rewards;\n\n uint256 private _totalSupply;\n mapping(address => EnumerableSet.UintSet) private _userStakedNfts;\n mapping(uint256 => address) private _stakedNftOwner;\n mapping(address => uint256) private _stakeTimestamp;\n\n /* ========== CONSTRUCTOR ========== */\n\n function initialize(\n address _owner,\n address _rewardsDistribution,\n address _rewardsToken,\n address _stakingToken,\n uint256 _minimumStakeTime\n ) public virtual initializer {\n __Context_init();\n __Ownable_init_unchained();\n __Pausable_init_unchained();\n __Failsafe_init_unchained();\n __ReentrancyGuard_init_unchained();\n __RewardsDistributionRecipient_init_unchained();\n\n transferOwnership(_owner);\n\n rewardsToken = IERC20(_rewardsToken);\n stakingToken = IERC721(_stakingToken);\n rewardsDistribution = _rewardsDistribution;\n minimumStakeTime = _minimumStakeTime;\n\n periodFinish = 0;\n rewardRate = 0;\n rewardsDuration = 180 days;\n }\n\n /* ========== VIEWS ========== */\n\n function totalSupply() external view override returns (uint256) {\n return _totalSupply;\n }\n\n function stakedIdsOf(address account)\n external\n view\n override\n returns (uint256[] memory stakedIds)\n {\n uint256 amount = _userStakedNfts[account].length();\n stakedIds = new uint256[](amount);\n\n EnumerableSet.UintSet storage stakedNfts = _userStakedNfts[account];\n\n for (uint256 i = 0; i < stakedNfts.length(); i++) {\n uint256 id = stakedNfts.at(i);\n stakedIds[i] = id;\n }\n }\n\n function lastTimeRewardApplicable() public view override returns (uint256) {\n return Math.min(block.timestamp, periodFinish);\n }\n\n function rewardPerToken() public view override returns (uint256) {\n if (_totalSupply == 0) {\n return rewardPerTokenStored;\n }\n return\n rewardPerTokenStored.add(\n lastTimeRewardApplicable()\n .sub(lastUpdateTime)\n .mul(rewardRate)\n .div(_totalSupply)\n );\n }\n\n function earned(address account) public view override returns (uint256) {\n return\n _userStakedNfts[account].length()\n .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))\n .add(rewards[account]);\n }\n\n function getRewardForDuration() external view override returns (uint256) {\n return rewardRate.mul(rewardsDuration);\n }\n\n function getStakeRewardDistributionTimeLeft()\n external\n view\n override\n returns (uint256)\n {\n (bool success, uint256 diff) = periodFinish.trySub(block.timestamp);\n return success ? diff : 0;\n }\n\n function getStakeUnlockTimeLeft() external view override returns (uint256) {\n (bool success, uint256 diff) = _stakeTimestamp[msg.sender]\n .add(minimumStakeTime)\n .trySub(block.timestamp);\n return success ? diff : 0;\n }\n\n function balanceOf(address account) external view override returns (uint256) {\n return _userStakedNfts[account].length();\n }\n\n /* ========== MUTATIVE FUNCTIONS ========== */\n\n function stake(uint256 id)\n public\n virtual\n override\n normalMode\n nonReentrant\n whenNotPaused\n updateReward(msg.sender)\n {\n _totalSupply = _totalSupply.add(1);\n _userStakedNfts[msg.sender].add(id);\n _stakedNftOwner[id] = msg.sender;\n if (_stakeTimestamp[msg.sender] == 0) {\n _stakeTimestamp[msg.sender] = block.timestamp;\n }\n stakingToken.safeTransferFrom(msg.sender, address(this), id);\n emit Staked(msg.sender, id);\n }\n\n function bulkStake(uint256[] memory ids)\n public\n virtual\n override\n normalMode\n nonReentrant\n whenNotPaused\n updateReward(msg.sender)\n {\n _totalSupply = _totalSupply.add(ids.length);\n if (_stakeTimestamp[msg.sender] == 0) {\n _stakeTimestamp[msg.sender] = block.timestamp;\n }\n for(uint i = 0; i < ids.length; i++) {\n _userStakedNfts[msg.sender].add(ids[i]);\n _stakedNftOwner[ids[i]] = msg.sender;\n stakingToken.safeTransferFrom(msg.sender, address(this), ids[i]);\n emit Staked(msg.sender, ids[i]);\n }\n }\n\n function withdraw(uint256 id)\n external\n override\n normalMode\n nonReentrant\n isOwner(id)\n updateReward(msg.sender)\n {\n require(\n minimumStakeTime == 0 ||\n block.timestamp.sub(_stakeTimestamp[msg.sender]) >=\n minimumStakeTime,\n \"Cannot withdraw until minimum staking time has passed\"\n );\n _totalSupply = _totalSupply.sub(1);\n _userStakedNfts[msg.sender].remove(id);\n delete _stakedNftOwner[id];\n if (_userStakedNfts[msg.sender].length() == 0) {\n _stakeTimestamp[msg.sender] = 0;\n }\n stakingToken.safeTransferFrom(address(this), msg.sender, id);\n emit Withdrawn(msg.sender, id);\n }\n\n function bulkWithdraw(uint256[] calldata ids)\n external\n override\n normalMode\n nonReentrant\n isOwnerOfIds(ids)\n updateReward(msg.sender)\n {\n require(\n minimumStakeTime == 0 ||\n block.timestamp.sub(_stakeTimestamp[msg.sender]) >=\n minimumStakeTime,\n \"Cannot withdraw until minimum staking time has passed\"\n );\n \n _totalSupply.sub(ids.length);\n for(uint i = 0; i < ids.length; i++) {\n _userStakedNfts[msg.sender].remove(ids[i]);\n delete _stakedNftOwner[ids[i]];\n stakingToken.safeTransferFrom(address(this), msg.sender, ids[i]);\n emit Withdrawn(msg.sender, ids[i]);\n }\n if (_userStakedNfts[msg.sender].length() == 0) {\n _stakeTimestamp[msg.sender] = 0;\n }\n }\n\n function getReward()\n public\n override\n normalMode\n nonReentrant\n updateReward(msg.sender)\n {\n require(\n minimumStakeTime == 0 ||\n block.timestamp.sub(_stakeTimestamp[msg.sender]) >=\n minimumStakeTime,\n \"Cannot get reward until minimum staking time has passed\"\n );\n uint256 reward = rewards[msg.sender];\n if (reward > 0) {\n rewards[msg.sender] = 0;\n rewardsToken.safeTransfer(msg.sender, reward);\n emit RewardPaid(msg.sender, reward);\n }\n }\n\n function exit() external override normalMode {\n require(\n minimumStakeTime == 0 ||\n block.timestamp.sub(_stakeTimestamp[msg.sender]) >=\n minimumStakeTime,\n \"Cannot withdraw until minimum staking time has passed\"\n );\n\n uint256 amount = _userStakedNfts[msg.sender].length();\n _totalSupply = _totalSupply.sub(amount);\n EnumerableSet.UintSet storage stakedNfts = _userStakedNfts[msg.sender];\n\n for (uint256 i = 0; i < stakedNfts.length(); i++) {\n uint256 id = stakedNfts.at(i);\n stakedNfts.remove(id);\n delete _stakedNftOwner[id];\n stakingToken.safeTransferFrom(address(this), msg.sender, id);\n }\n\n if (_userStakedNfts[msg.sender].length() == 0) {\n _stakeTimestamp[msg.sender] = 0;\n }\n\n getReward();\n }\n\n function recoverOwnStake() external failsafeMode {\n uint256 amount = _userStakedNfts[msg.sender].length();\n _totalSupply = _totalSupply.sub(amount);\n EnumerableSet.UintSet storage stakedNfts = _userStakedNfts[msg.sender];\n\n for (uint256 i = 0; i < stakedNfts.length(); i++) {\n uint256 id = stakedNfts.at(i);\n stakedNfts.remove(id);\n delete _stakedNftOwner[id];\n stakingToken.safeTransferFrom(address(this), msg.sender, id);\n }\n }\n\n /* ========== RESTRICTED FUNCTIONS ========== */\n\n function notifyRewardAmount(uint256 reward)\n external\n override\n normalMode\n onlyRewardsDistribution\n updateReward(address(0))\n {\n if (block.timestamp >= periodFinish) {\n rewardRate = reward.div(rewardsDuration);\n } else {\n uint256 remaining = periodFinish.sub(block.timestamp);\n uint256 leftover = remaining.mul(rewardRate);\n rewardRate = reward.add(leftover).div(rewardsDuration);\n }\n\n // Ensure the provided reward amount is not more than the balance in the contract.\n // This keeps the reward rate in the right range, preventing overflows due to\n // very high values of rewardRate in the earned and rewardsPerToken functions;\n // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.\n uint256 balance = rewardsToken.balanceOf(address(this));\n require(\n rewardRate <= balance.div(rewardsDuration),\n \"Provided reward too high\"\n );\n\n lastUpdateTime = block.timestamp;\n periodFinish = block.timestamp.add(rewardsDuration);\n emit RewardAdded(reward);\n }\n\n // End rewards emission earlier\n function updatePeriodFinish(uint256 timestamp)\n external\n normalMode\n onlyOwner\n updateReward(address(0))\n {\n require(\n timestamp > lastUpdateTime,\n \"Timestamp must be after lastUpdateTime\"\n );\n periodFinish = timestamp;\n }\n\n // Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders\n function recoverERC20(address tokenAddress, uint256 tokenAmount)\n external\n onlyOwner\n {\n require(\n tokenAddress != address(stakingToken),\n \"Cannot withdraw the staking token\"\n );\n IERC20(tokenAddress).safeTransfer(owner(), tokenAmount);\n emit Recovered(tokenAddress, tokenAmount);\n }\n\n function setRewardsDuration(uint256 _rewardsDuration)\n external\n normalMode\n onlyOwner\n {\n require(\n block.timestamp > periodFinish,\n \"Previous rewards period must be complete before changing the duration for the new period\"\n );\n rewardsDuration = _rewardsDuration;\n emit RewardsDurationUpdated(rewardsDuration);\n }\n\n function setMinimumStakeAmount(uint256 _minimumStakeAmount)\n external\n normalMode\n onlyOwner\n {\n minimumStakeAmount = _minimumStakeAmount;\n emit MinimumStakeAmountUpdated(_minimumStakeAmount);\n }\n\n function setMinimumStakeTime(uint256 _minimumStakeTime)\n external\n normalMode\n onlyOwner\n {\n minimumStakeTime = _minimumStakeTime;\n emit MinimumStakeTimeUpdated(_minimumStakeTime);\n }\n\n function enableFailsafeMode() public override normalMode onlyOwner {\n minimumStakeAmount = 0;\n minimumStakeTime = 0;\n periodFinish = 0;\n rewardRate = 0;\n rewardPerTokenStored = 0;\n\n super.enableFailsafeMode();\n }\n\n function pause() external onlyOwner whenNotPaused {\n _pause();\n }\n\n function unpause() external onlyOwner whenPaused {\n _unpause();\n }\n\n /* ========== MODIFIERS ========== */\n\n modifier updateReward(address account) {\n if (!failsafeModeActive) {\n rewardPerTokenStored = rewardPerToken();\n lastUpdateTime = lastTimeRewardApplicable();\n if (account != address(0)) {\n rewards[account] = earned(account);\n userRewardPerTokenPaid[account] = rewardPerTokenStored;\n }\n }\n _;\n }\n\n /* ========== EVENTS ========== */\n\n event RewardAdded(uint256 reward);\n event Staked(address indexed user, uint256 amount);\n event Withdrawn(address indexed user, uint256 amount);\n event RewardPaid(address indexed user, uint256 reward);\n event RewardsDurationUpdated(uint256 newDuration);\n event MinimumStakeAmountUpdated(uint256 newMinimumStakeAmount);\n event MinimumStakeTimeUpdated(uint256 newMinimumStakeTime);\n event Recovered(address token, uint256 amount);\n\n /* ========== MODIFIERS ========== */\n modifier isOwner(uint256 id) {\n require(_stakedNftOwner[id] == msg.sender, \"Access denied\");\n _;\n }\n\n modifier isOwnerOfIds(uint256[] memory ids) {\n for(uint i = 0; i < ids.length; i++) {\n require(_stakedNftOwner[ids[i]] == msg.sender, \"Access denied\");\n }\n _;\n }\n\n // something\n function onERC721Received(\n address, /* operator */\n address, /* from */\n uint256 _id,\n bytes calldata /* data */\n ) external override returns (bytes4) {\n // NOTE: The contract address is always the message sender.\n address _tokenAddress = msg.sender;\n\n require(\n address(stakingToken) ==_tokenAddress &&\n _stakedNftOwner[_id] != address(0),\n \"Token ID not listed\"\n );\n\n return IERC721ReceiverUpgradeable.onERC721Received.selector;\n }\n}\n"
},
"contracts/staking/interfaces/INftStakingRewards.sol": {
"content": "pragma solidity >=0.4.24;\n\n// https://docs.synthetix.io/contracts/source/interfaces/istakingrewards\ninterface INftStakingRewards {\n // Views\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function earned(address account) external view returns (uint256);\n\n function getRewardForDuration() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function stakedIdsOf(address account) external view returns (uint256[] memory);\n\n function minimumStakeAmount() external view returns (uint256);\n\n function minimumStakeTime() external view returns (uint256);\n\n function getStakeRewardDistributionTimeLeft() external view returns (uint256);\n\n function getStakeUnlockTimeLeft() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function rewardsDuration() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n // Mutative\n function stake(uint256 id) external;\n\n function bulkStake(uint256[] calldata ids) external;\n\n function withdraw(uint256 id) external;\n\n function bulkWithdraw(uint256[] calldata ids) external;\n\n function getReward() external;\n\n function exit() external;\n\n // Events\n event RewardAdded(uint256 reward);\n\n event Staked(address indexed user, uint256 amount);\n\n event Withdrawn(address indexed user, uint256 amount);\n\n event RewardPaid(address indexed user, uint256 reward);\n\n event RewardsDurationUpdated(uint256 newDuration);\n\n event MinimumStakeTimeUpdated(uint256 newMinimumStakeTime);\n}\n"
},
"contracts/CBKLandT2StakingRewardsUpgradeable.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./staking/NftStakingRewardsUpgradeable.sol\";\nimport \"./CBKLand.sol\";\n\ncontract CBKLandT2StakingRewardsUpgradeable is NftStakingRewardsUpgradeable {\n CBKLand internal __cbkLand;\n uint256 public stakeTier;\n\n function initialize(\n address _owner,\n address _rewardsDistribution,\n address _rewardsToken,\n address _stakingToken,\n uint256 _minimumStakeTime,\n CBKLand _cbkLand\n ) public initializer {\n super.initialize(\n _owner,\n _rewardsDistribution,\n _rewardsToken,\n _stakingToken,\n _minimumStakeTime);\n __cbkLand = _cbkLand;\n stakeTier = 2;\n }\n\n function stake(uint256 id) public virtual override {\n uint256 landTier = __cbkLand.getLandTier(id);\n require(landTier == stakeTier, 'Land tier mismatch');\n super.stake(id);\n }\n\n function bulkStake(uint256[] memory ids) public virtual override {\n for(uint i = 0; i < ids.length; i++) {\n uint256 landTier = __cbkLand.getLandTier(ids[i]);\n require(landTier == stakeTier, 'Land tier mismatch');\n }\n super.bulkStake(ids);\n }\n}"
},
"contracts/ValorToken.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\n\n/**\n * @title SimpleToken\n * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.\n * Note they can later distribute these tokens as they wish using `transfer` and other\n * `ERC20` functions.\n * Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/examples/SimpleToken.sol\n */\ncontract ValorToken is ERC20Upgradeable, OwnableUpgradeable {\n function initialize()\n public\n initializer\n {\n __ERC20_init(\"Valor Token\", \"VALOR\");\n __Ownable_init();\n _mint(address(this), 100000000 * (10 ** uint256(decimals())));\n _approve(address(this), msg.sender, totalSupply());\n }\n\n function mint(address account, uint256 amount) external onlyOwner {\n _mint(account, amount);\n }\n}"
},
"contracts/HasMain.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\n\ncontract HasMain is Initializable, OwnableUpgradeable {\n address public main;\n\n function __HasMain_init() internal initializer {\n __Ownable_init();\n __HasMain_init_unchained();\n }\n\n function __HasMain_init_unchained() internal initializer {\n main = address(0);\n }\n\n function setMain(address newMain) external onlyOwner {\n main = newMain;\n }\n\n modifier restrictedToMain() {\n require(msg.sender == main || msg.sender == address(this), \"Can only be called by main address or self\");\n _;\n }\n}\n"
},
"contracts/DummyRandoms.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"./interfaces/IRandoms.sol\";\nimport \"./HasMain.sol\";\n\ncontract DummyRandoms is IRandoms, HasMain {\n // UNUSED; KEPT FOR UPGRADEABILITY PROXY COMPATIBILITY\n mapping(address => bool) private alreadyRequestedTestingFlag;\n // UNUSED; KEPT FOR UPGRADEABILITY PROXY COMPATIBILITY\n mapping(address => bool) private seedAvailable;\n // UNUSED; KEPT FOR UPGRADEABILITY PROXY COMPATIBILITY\n mapping(address => uint256) private seeds;\n\n uint256 private seed;\n\n function initialize() public initializer {\n __HasMain_init();\n }\n\n // Views\n function getRandomSeed(address user) external view override returns (uint256) {\n return uint256(keccak256(abi.encodePacked(user, seed, block.timestamp)));\n }\n\n function getRandomSeedUsingHash(address user, bytes32 hash) external view override returns (uint256) {\n return uint256(keccak256(abi.encodePacked(user, seed, hash, block.timestamp)));\n }\n\n // Mutative\n function setRandomNumberForTestingPurposes(uint256 randomValue) external {\n seed = randomValue;\n }\n}\n"
},
"contracts/ChainlinkRandoms.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts/access/AccessControl.sol\";\nimport \"@openzeppelin/contracts/utils/Pausable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@chainlink/contracts/src/v0.6/VRFConsumerBase.sol\";\nimport \"./interfaces/IRandoms.sol\";\n\nstruct SeedState {\n bytes32 requestId;\n uint256 seed;\n bool isAvailable;\n}\n\ncontract ChainlinkRandoms is IRandoms, Pausable, AccessControl, VRFConsumerBase {\n using SafeERC20 for IERC20;\n\n uint256 constant VRF_MAGIC_SEED = uint256(keccak256(\"CryptoBlades\"));\n\n bytes32 public constant RANDOMNESS_REQUESTER = keccak256(\"RANDOMNESS_REQUESTER\");\n\n bytes32 private keyHash;\n uint256 private fee;\n\n uint256 private seed;\n\n // BSC testnet details:\n // LINK token: 0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06\n // VRF Coordinator: 0xa555fC018435bef5A13C6c6870a9d4C11DEC329C\n // Key Hash: 0xcaf3c3727e033261d383b315559476f48034c13b18f8cafed4d871abe5049186\n // Fee: 0.1 * 10 ** 18 // 0.1 LINK\n\n constructor(address _vrfCoordinator, address _link, bytes32 _keyHash, uint256 _fee)\n VRFConsumerBase(\n _vrfCoordinator, // VRF Coordinator\n _link // LINK Token\n ) public\n {\n keyHash = _keyHash;\n fee = _fee;\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n }\n\n // Views\n function getRandomSeed(address user) external override view returns (uint256) {\n return getRandomSeedUsingHash(user, blockhash(block.number - 1));\n }\n\n function getRandomSeedUsingHash(address user, bytes32 hash) public override view returns (uint256) {\n return uint256(keccak256(abi.encodePacked(user, seed, hash)));\n }\n\n // Mutative\n\n /**\n * Requests randomness from a user-provided seed\n */\n function requestRandomNumber() external whenNotPaused {\n require(hasRole(RANDOMNESS_REQUESTER, msg.sender), \"Sender cannot request seed\");\n require(LINK.balanceOf(address(this)) >= fee, \"Not enough LINK\");\n\n // the user-provided seed is not necessary, as per the docs\n // hence we set it to an arbitrary constant\n requestRandomness(keyHash, fee, VRF_MAGIC_SEED);\n }\n\n function pause() external onlyOwner {\n _pause();\n }\n\n function unpause() external onlyOwner {\n _unpause();\n }\n\n /**\n * Callback function used by VRF Coordinator\n */\n function fulfillRandomness(bytes32 /* requestId */, uint256 randomness) internal override {\n seed = randomness;\n }\n\n function withdrawLink(uint256 tokenAmount) external onlyOwner {\n // very awkward - but should be safe given that the LINK token is ERC20-compatible\n IERC20(address(LINK)).safeTransfer(msg.sender, tokenAmount);\n }\n\n // Modifiers\n modifier onlyOwner() {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n _;\n }\n}\n"
},
"@openzeppelin/contracts/access/AccessControl.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"../utils/EnumerableSet.sol\";\nimport \"../utils/Address.sol\";\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context {\n using EnumerableSet for EnumerableSet.AddressSet;\n using Address for address;\n\n struct RoleData {\n EnumerableSet.AddressSet members;\n bytes32 adminRole;\n }\n\n mapping (bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view returns (bool) {\n return _roles[role].members.contains(account);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view returns (uint256) {\n return _roles[role].members.length();\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view returns (address) {\n return _roles[role].members.at(index);\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual {\n require(hasRole(_roles[role].adminRole, _msgSender()), \"AccessControl: sender must be an admin to grant\");\n\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual {\n require(hasRole(_roles[role].adminRole, _msgSender()), \"AccessControl: sender must be an admin to revoke\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);\n _roles[role].adminRole = adminRole;\n }\n\n function _grantRole(bytes32 role, address account) private {\n if (_roles[role].members.add(account)) {\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n function _revokeRole(bytes32 role, address account) private {\n if (_roles[role].members.remove(account)) {\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n"
},
"@openzeppelin/contracts/utils/Pausable.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"./Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor () internal {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n"
},
"@chainlink/contracts/src/v0.6/VRFConsumerBase.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.6.0;\n\nimport \"./vendor/SafeMathChainlink.sol\";\n\nimport \"./interfaces/LinkTokenInterface.sol\";\n\nimport \"./VRFRequestIDBase.sol\";\n\n/** ****************************************************************************\n * @notice Interface for contracts using VRF randomness\n * *****************************************************************************\n * @dev PURPOSE\n *\n * @dev Reggie the Random Oracle (not his real job) wants to provide randomness\n * @dev to Vera the verifier in such a way that Vera can be sure he's not\n * @dev making his output up to suit himself. Reggie provides Vera a public key\n * @dev to which he knows the secret key. Each time Vera provides a seed to\n * @dev Reggie, he gives back a value which is computed completely\n * @dev deterministically from the seed and the secret key.\n *\n * @dev Reggie provides a proof by which Vera can verify that the output was\n * @dev correctly computed once Reggie tells it to her, but without that proof,\n * @dev the output is indistinguishable to her from a uniform random sample\n * @dev from the output space.\n *\n * @dev The purpose of this contract is to make it easy for unrelated contracts\n * @dev to talk to Vera the verifier about the work Reggie is doing, to provide\n * @dev simple access to a verifiable source of randomness.\n * *****************************************************************************\n * @dev USAGE\n *\n * @dev Calling contracts must inherit from VRFConsumerBase, and can\n * @dev initialize VRFConsumerBase's attributes in their constructor as\n * @dev shown:\n *\n * @dev contract VRFConsumer {\n * @dev constuctor(<other arguments>, address _vrfCoordinator, address _link)\n * @dev VRFConsumerBase(_vrfCoordinator, _link) public {\n * @dev <initialization with other arguments goes here>\n * @dev }\n * @dev }\n *\n * @dev The oracle will have given you an ID for the VRF keypair they have\n * @dev committed to (let's call it keyHash), and have told you the minimum LINK\n * @dev price for VRF service. Make sure your contract has sufficient LINK, and\n * @dev call requestRandomness(keyHash, fee, seed), where seed is the input you\n * @dev want to generate randomness from.\n *\n * @dev Once the VRFCoordinator has received and validated the oracle's response\n * @dev to your request, it will call your contract's fulfillRandomness method.\n *\n * @dev The randomness argument to fulfillRandomness is the actual random value\n * @dev generated from your seed.\n *\n * @dev The requestId argument is generated from the keyHash and the seed by\n * @dev makeRequestId(keyHash, seed). If your contract could have concurrent\n * @dev requests open, you can use the requestId to track which seed is\n * @dev associated with which randomness. See VRFRequestIDBase.sol for more\n * @dev details. (See \"SECURITY CONSIDERATIONS\" for principles to keep in mind,\n * @dev if your contract could have multiple requests in flight simultaneously.)\n *\n * @dev Colliding `requestId`s are cryptographically impossible as long as seeds\n * @dev differ. (Which is critical to making unpredictable randomness! See the\n * @dev next section.)\n *\n * *****************************************************************************\n * @dev SECURITY CONSIDERATIONS\n *\n * @dev A method with the ability to call your fulfillRandomness method directly\n * @dev could spoof a VRF response with any random value, so it's critical that\n * @dev it cannot be directly called by anything other than this base contract\n * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method).\n *\n * @dev For your users to trust that your contract's random behavior is free\n * @dev from malicious interference, it's best if you can write it so that all\n * @dev behaviors implied by a VRF response are executed *during* your\n * @dev fulfillRandomness method. If your contract must store the response (or\n * @dev anything derived from it) and use it later, you must ensure that any\n * @dev user-significant behavior which depends on that stored value cannot be\n * @dev manipulated by a subsequent VRF request.\n *\n * @dev Similarly, both miners and the VRF oracle itself have some influence\n * @dev over the order in which VRF responses appear on the blockchain, so if\n * @dev your contract could have multiple VRF requests in flight simultaneously,\n * @dev you must ensure that the order in which the VRF responses arrive cannot\n * @dev be used to manipulate your contract's user-significant behavior.\n *\n * @dev Since the ultimate input to the VRF is mixed with the block hash of the\n * @dev block in which the request is made, user-provided seeds have no impact\n * @dev on its economic security properties. They are only included for API\n * @dev compatability with previous versions of this contract.\n *\n * @dev Since the block hash of the block which contains the requestRandomness\n * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful\n * @dev miner could, in principle, fork the blockchain to evict the block\n * @dev containing the request, forcing the request to be included in a\n * @dev different block with a different hash, and therefore a different input\n * @dev to the VRF. However, such an attack would incur a substantial economic\n * @dev cost. This cost scales with the number of blocks the VRF oracle waits\n * @dev until it calls responds to a request.\n */\nabstract contract VRFConsumerBase is VRFRequestIDBase {\n\n using SafeMathChainlink for uint256;\n\n /**\n * @notice fulfillRandomness handles the VRF response. Your contract must\n * @notice implement it. See \"SECURITY CONSIDERATIONS\" above for important\n * @notice principles to keep in mind when implementing your fulfillRandomness\n * @notice method.\n *\n * @dev VRFConsumerBase expects its subcontracts to have a method with this\n * @dev signature, and will call it once it has verified the proof\n * @dev associated with the randomness. (It is triggered via a call to\n * @dev rawFulfillRandomness, below.)\n *\n * @param requestId The Id initially returned by requestRandomness\n * @param randomness the VRF output\n */\n function fulfillRandomness(bytes32 requestId, uint256 randomness)\n internal virtual;\n\n /**\n * @notice requestRandomness initiates a request for VRF output given _seed\n *\n * @dev The fulfillRandomness method receives the output, once it's provided\n * @dev by the Oracle, and verified by the vrfCoordinator.\n *\n * @dev The _keyHash must already be registered with the VRFCoordinator, and\n * @dev the _fee must exceed the fee specified during registration of the\n * @dev _keyHash.\n *\n * @dev The _seed parameter is vestigial, and is kept only for API\n * @dev compatibility with older versions. It can't *hurt* to mix in some of\n * @dev your own randomness, here, but it's not necessary because the VRF\n * @dev oracle will mix the hash of the block containing your request into the\n * @dev VRF seed it ultimately uses.\n *\n * @param _keyHash ID of public key against which randomness is generated\n * @param _fee The amount of LINK to send with the request\n * @param _seed seed mixed into the input of the VRF.\n *\n * @return requestId unique ID for this request\n *\n * @dev The returned requestId can be used to distinguish responses to\n * @dev concurrent requests. It is passed as the first argument to\n * @dev fulfillRandomness.\n */\n function requestRandomness(bytes32 _keyHash, uint256 _fee, uint256 _seed)\n internal returns (bytes32 requestId)\n {\n LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, _seed));\n // This is the seed passed to VRFCoordinator. The oracle will mix this with\n // the hash of the block containing this request to obtain the seed/input\n // which is finally passed to the VRF cryptographic machinery.\n uint256 vRFSeed = makeVRFInputSeed(_keyHash, _seed, address(this), nonces[_keyHash]);\n // nonces[_keyHash] must stay in sync with\n // VRFCoordinator.nonces[_keyHash][this], which was incremented by the above\n // successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest).\n // This provides protection against the user repeating their input seed,\n // which would result in a predictable/duplicate output, if multiple such\n // requests appeared in the same block.\n nonces[_keyHash] = nonces[_keyHash].add(1);\n return makeRequestId(_keyHash, vRFSeed);\n }\n\n LinkTokenInterface immutable internal LINK;\n address immutable private vrfCoordinator;\n\n // Nonces for each VRF key from which randomness has been requested.\n //\n // Must stay in sync with VRFCoordinator[_keyHash][this]\n mapping(bytes32 /* keyHash */ => uint256 /* nonce */) private nonces;\n\n /**\n * @param _vrfCoordinator address of VRFCoordinator contract\n * @param _link address of LINK token contract\n *\n * @dev https://docs.chain.link/docs/link-token-contracts\n */\n constructor(address _vrfCoordinator, address _link) public {\n vrfCoordinator = _vrfCoordinator;\n LINK = LinkTokenInterface(_link);\n }\n\n // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF\n // proof. rawFulfillRandomness then calls fulfillRandomness, after validating\n // the origin of the call\n function rawFulfillRandomness(bytes32 requestId, uint256 randomness) external {\n require(msg.sender == vrfCoordinator, \"Only VRFCoordinator can fulfill\");\n fulfillRandomness(requestId, randomness);\n }\n}\n"
},
"@chainlink/contracts/src/v0.6/vendor/SafeMathChainlink.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.6.0;\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations with added overflow\n * checks.\n *\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\n * in bugs, because programmers usually assume that an overflow raises an\n * error, which is the standard behavior in high level programming languages.\n * `SafeMath` restores this intuition by reverting the transaction when an\n * operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeMathChainlink {\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n uint256 c = a + b;\n require(c >= a, \"SafeMath: addition overflow\");\n\n return c;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n require(b <= a, \"SafeMath: subtraction overflow\");\n uint256 c = a - b;\n\n return c;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522\n if (a == 0) {\n return 0;\n }\n\n uint256 c = a * b;\n require(c / a == b, \"SafeMath: multiplication overflow\");\n\n return c;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers. Reverts on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n // Solidity only automatically asserts when dividing by 0\n require(b > 0, \"SafeMath: division by zero\");\n uint256 c = a / b;\n // assert(a == b * c + a % b); // There is no case in which this doesn't hold\n\n return c;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * Reverts when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n require(b != 0, \"SafeMath: modulo by zero\");\n return a % b;\n }\n}\n"
},
"@chainlink/contracts/src/v0.6/interfaces/LinkTokenInterface.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.6.0;\n\ninterface LinkTokenInterface {\n function allowance(address owner, address spender) external view returns (uint256 remaining);\n function approve(address spender, uint256 value) external returns (bool success);\n function balanceOf(address owner) external view returns (uint256 balance);\n function decimals() external view returns (uint8 decimalPlaces);\n function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);\n function increaseApproval(address spender, uint256 subtractedValue) external;\n function name() external view returns (string memory tokenName);\n function symbol() external view returns (string memory tokenSymbol);\n function totalSupply() external view returns (uint256 totalTokensIssued);\n function transfer(address to, uint256 value) external returns (bool success);\n function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success);\n function transferFrom(address from, address to, uint256 value) external returns (bool success);\n}\n"
},
"@chainlink/contracts/src/v0.6/VRFRequestIDBase.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.6.0;\n\ncontract VRFRequestIDBase {\n\n /**\n * @notice returns the seed which is actually input to the VRF coordinator\n *\n * @dev To prevent repetition of VRF output due to repetition of the\n * @dev user-supplied seed, that seed is combined in a hash with the\n * @dev user-specific nonce, and the address of the consuming contract. The\n * @dev risk of repetition is mostly mitigated by inclusion of a blockhash in\n * @dev the final seed, but the nonce does protect against repetition in\n * @dev requests which are included in a single block.\n *\n * @param _userSeed VRF seed input provided by user\n * @param _requester Address of the requesting contract\n * @param _nonce User-specific nonce at the time of the request\n */\n function makeVRFInputSeed(bytes32 _keyHash, uint256 _userSeed,\n address _requester, uint256 _nonce)\n internal pure returns (uint256)\n {\n return uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce)));\n }\n\n /**\n * @notice Returns the id for this request\n * @param _keyHash The serviceAgreement ID to be used for this request\n * @param _vRFInputSeed The seed to be passed directly to the VRF\n * @return The id for this request\n *\n * @dev Note that _vRFInputSeed is not the seed passed by the consuming\n * @dev contract, but the one generated by makeVRFInputSeed\n */\n function makeRequestId(\n bytes32 _keyHash, uint256 _vRFInputSeed) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed));\n }\n}\n"
},
"contracts/CharactersBridgeProxyContract.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"./CharacterCosmetics.sol\";\nimport \"./CharacterRenameTagConsumables.sol\";\nimport \"./characters.sol\";\nimport \"./interfaces/IBridgeProxy.sol\";\nimport \"./Promos.sol\";\nimport \"./EquipmentManager.sol\";\n\n\ncontract CharactersBridgeProxyContract is Initializable, AccessControlUpgradeable, IBridgeProxy {\n \n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n Characters characters;\n CharacterCosmetics characterCosmetics;\n CharacterRenameTagConsumables characterRenameTagConsumables;\n address nftStorageAddress;\n bool enabled;\n bool giveawayGen2Enabled;\n\n uint8 public constant UINT_NFT_VAR_META = 0;\n uint8 public constant UINT_NFT_VAR_SEED3DCOSMETIC = 1;\n\n Promos promos;\n EquipmentManager equipmentManager;\n\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"NA\");\n }\n\n function initialize(address _nftStorageAddress, address _characters, address _characterCosmetics, address _characterRenameTagConsumables) public initializer {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n nftStorageAddress = _nftStorageAddress;\n characters = Characters(_characters);\n characterCosmetics = CharacterCosmetics(_characterCosmetics);\n characterRenameTagConsumables = CharacterRenameTagConsumables(_characterRenameTagConsumables);\n }\n\n function migrate_c906001(Promos _newPromos) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n promos = _newPromos;\n }\n\n function migrate_68c6936(EquipmentManager _equipmentManager) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n equipmentManager = _equipmentManager;\n }\n\n \n function collectData(uint256 tokenId) external view override returns (uint256[] memory uintVars, string memory stringVar) {\n string memory rename = characterRenameTagConsumables.getCharacterRename(tokenId);\n uintVars = new uint256[](2);\n (uintVars[UINT_NFT_VAR_META], uintVars[UINT_NFT_VAR_SEED3DCOSMETIC], stringVar) = _packedCharacterData(tokenId);\n\n stringVar = rename;\n }\n\n // for future use, bot will probe the returned value to know if the proxy contract has proper signature behavior\n function sigVersion() external view override returns (uint256) {\n return 3;\n }\n\n function isEnabled() external view override returns (bool) {\n return enabled;\n }\n\n function setEnabled(bool _enabled) external restricted {\n enabled = _enabled;\n }\n\n function mintOrUpdate(address receiver, uint256 tokenId, uint256[] calldata uintVars, string calldata stringVar) external restricted override returns (uint256) {\n require(enabled, \"not enabled\");\n\n (uint32 appliedCosmetic, uint16 xp, uint8 level, uint16 traitAndVersion, uint24 bonusPower, uint16 reputation) = _unpackCharactersData(uintVars[UINT_NFT_VAR_META]); \n\n tokenId = _mintOrUpdate(tokenId, xp, level, traitAndVersion, uintVars[UINT_NFT_VAR_SEED3DCOSMETIC], bonusPower, reputation);\n \n if(appliedCosmetic > 0) {\n characterCosmetics.setCharacterCosmetic(tokenId, uint32(appliedCosmetic));\n }\n\n if(bytes(stringVar).length > 0) {\n characterRenameTagConsumables.setName(tokenId, stringVar);\n }\n\n return tokenId;\n }\n\n function _mintOrUpdate(uint256 tokenId, uint16 xp, uint8 level, uint16 traitAndVersion, uint256 seed, uint24 bonusPower, uint16 reputation) internal returns (uint256) {\n tokenId = \n characters.customMint(nftStorageAddress, xp, level, uint8(traitAndVersion & 0xFF), seed, tokenId, bonusPower, reputation, uint8((traitAndVersion >> 8) & 0xFF));\n\n return tokenId;\n }\n\n function _unpackCharactersData(uint256 metaData) internal pure returns (uint32 appliedCosmetic, uint16 xp, uint8 level, uint16 traitAndVersion, uint24 bonusPower, uint16 reputation) {\n traitAndVersion = uint16((metaData) & 0xFFFF);\n level = uint8((metaData >> 16) & 0xFF);\n xp = uint16(metaData >> 24 & 0xFFFF);\n appliedCosmetic = uint32((metaData >> 40) & 0xFFFFFFFF);\n bonusPower = uint24((metaData >> 72) & 0xFFFFFF);\n reputation = uint16((metaData >> 96) & 0xFFFF);\n }\n\n\n function _packedCharacterData(uint256 characterId) internal view returns (uint256 packedData, uint256 seed3dCosmetics, string memory rename) {\n (uint16 xp, uint8 level, uint8 trait,,,,,,,) = characters.get(characterId);\n uint32 appliedCosmetic = characterCosmetics.getCharacterCosmetic(characterId);\n rename = characterRenameTagConsumables.getCharacterRename(characterId);\n seed3dCosmetics = characters.getCosmeticsSeed(characterId);\n uint24 bonusPower = uint24(characters.getNftVar(characterId, 2)); // 2 => bonus Power\n uint16 reputation = uint16(characters.getNftVar(characterId, 103)); // 103 => reputation\n uint8 version = uint8(characters.getNftVar(characterId, 3)); // 3 => version\n packedData = _packCharactersData(appliedCosmetic, xp, level, uint16(trait | (version << 8)), bonusPower, reputation);\n }\n\n function _packCharactersData(uint32 appliedCosmetic, uint16 xp, uint8 level, uint16 traitAndVersion, uint24 bonusPower, uint16 reputation) internal pure returns (uint256) {\n return uint256(uint256(traitAndVersion) | (uint256(level) << 16) | (uint256(xp) << 24) | (uint256(appliedCosmetic) << 40) | (uint256(bonusPower) << 72) | (uint256(reputation) << 96));\n }\n\n function canBridge(address wallet, uint256 tokenId, uint256 targetChain) external view override returns (bool) {\n return characters.getNftVar(tokenId, 3) == 0 // Only gen 1 is allowed\n && equipmentManager.getNftVar(address(characters), tokenId, 1) == 0; // Nothing equipped\n }\n}\n"
},
"contracts/CharacterWaterTraitChangeConsumables.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"./Consumables.sol\";\nimport \"./characters.sol\";\n\ncontract CharacterWaterTraitChangeConsumables is Consumables {\n\n Characters characters;\n\n event CharacterTraitChangedToWater(address indexed owner, uint256 indexed character, uint8 from);\n\n function initialize(Characters _characters)\n public\n initializer\n {\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n _enabled = true;\n\n characters = _characters;\n }\n\n function changeCharacterTrait(uint256 characterId) public {\n require(characters.ownerOf(characterId) == msg.sender, \"Not the character owner\");\n consumeItem(1);\n emit CharacterTraitChangedToWater(msg.sender, characterId, characters.getTrait(characterId));\n characters.setTrait(characterId, 3);\n }\n}\n"
},
"contracts/CharacterLightningTraitChangeConsumables.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"./Consumables.sol\";\nimport \"./characters.sol\";\n\ncontract CharacterLightningTraitChangeConsumables is Consumables {\n\n Characters characters;\n\n event CharacterTraitChangedToLightning(address indexed owner, uint256 indexed character, uint8 from);\n\n function initialize(Characters _characters)\n public\n initializer\n {\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n _enabled = true;\n\n characters = _characters;\n }\n\n function changeCharacterTrait(uint256 characterId) public {\n require(characters.ownerOf(characterId) == msg.sender, \"Not the character owner\");\n consumeItem(1);\n emit CharacterTraitChangedToLightning(msg.sender, characterId, characters.getTrait(characterId));\n characters.setTrait(characterId, 2);\n }\n}\n"
},
"contracts/CharacterFireTraitChangeConsumables.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"./Consumables.sol\";\nimport \"./characters.sol\";\n\ncontract CharacterFireTraitChangeConsumables is Consumables {\n\n Characters characters;\n\n event CharacterTraitChangedToFire(address indexed owner, uint256 indexed character, uint8 from);\n\n function initialize(Characters _characters)\n public\n initializer\n {\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n _enabled = true;\n\n characters = _characters;\n }\n\n function changeCharacterTrait(uint256 characterId) public {\n require(characters.ownerOf(characterId) == msg.sender, \"Not the character owner\");\n consumeItem(1);\n emit CharacterTraitChangedToFire(msg.sender, characterId, characters.getTrait(characterId));\n characters.setTrait(characterId, 0);\n }\n}\n"
},
"contracts/CharacterEarthTraitChangeConsumables.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"./Consumables.sol\";\nimport \"./characters.sol\";\n\ncontract CharacterEarthTraitChangeConsumables is Consumables {\n\n Characters characters;\n\n event CharacterTraitChangedToEarth(address indexed owner, uint256 indexed character, uint8 from);\n\n function initialize(Characters _characters)\n public\n initializer\n {\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n _enabled = true;\n\n characters = _characters;\n }\n\n function changeCharacterTrait(uint256 characterId) public {\n require(characters.ownerOf(characterId) == msg.sender, \"Not the character owner\");\n consumeItem(1);\n emit CharacterTraitChangedToEarth(msg.sender, characterId, characters.getTrait(characterId));\n characters.setTrait(characterId, 1);\n }\n}\n"
},
"contracts/ShieldBridgeProxyContract.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"./shields.sol\";\nimport \"./interfaces/IBridgeProxy.sol\";\n\n\ncontract ShieldBridgeProxyContract is Initializable, AccessControlUpgradeable, IBridgeProxy {\n \n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n Shields shields;\n address nftStorageAddress;\n bool enabled;\n\n uint8 public constant UINT_NFT_VAR_META = 0;\n uint8 public constant UINT_NFT_VAR_SEED3DCOSMETIC = 1;\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"NA\");\n }\n\n function initialize(address _nftStorageAddress, address _shields) public initializer {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n nftStorageAddress = _nftStorageAddress;\n shields = Shields(_shields);\n }\n\n \n function collectData(uint256 tokenId) external view override returns (uint256[] memory uintVars, string memory stringVar) {\n \n (uint16 _properties, uint16 _stat1, uint16 _stat2, uint16 _stat3) = shields.get(tokenId);\n uint8 _type = uint8(shields.getNftVar(tokenId, 2)); // 2 => shield type\n uint256 seed3dCosmetics = shields.getCosmeticsSeed(tokenId);\n\n uintVars = new uint256[](2);\n uintVars[UINT_NFT_VAR_META] = _packShieldsData(0, _properties, _stat1, _stat2, _stat3, _type);\n uintVars[UINT_NFT_VAR_SEED3DCOSMETIC] = seed3dCosmetics;\n }\n\n // for future use, bot will probe the returned value to know if the proxy contract has proper signature behavior\n function sigVersion() external view override returns (uint256) {\n return 3;\n }\n\n function isEnabled() external view override returns (bool) {\n return enabled;\n }\n\n function setEnabled(bool _enabled) external restricted {\n enabled = _enabled;\n }\n\n function mintOrUpdate(address /*receiver*/, uint256 tokenId, uint256[] calldata uintVars, string calldata stringVar) external restricted override returns (uint256) {\n require(enabled, \"not enabled\");\n\n tokenId = \n shields.performMintShieldDetailed(nftStorageAddress, uintVars[UINT_NFT_VAR_META], uintVars[UINT_NFT_VAR_SEED3DCOSMETIC], tokenId);\n\n return tokenId;\n }\n\n function _packShieldsData(uint32 appliedCosmetic, uint16 properties, uint16 stat1, uint16 stat2, uint16 stat3, uint8 shieldType) public pure returns (uint256) {\n return uint256(uint256(shieldType) | uint256(stat3) << 16| (uint256(stat2) << 32) | (uint256(stat1) << 48) | (uint256(properties) << 64) | (uint256(appliedCosmetic) << 80));\n }\n\n function canBridge(address wallet, uint256 tokenId, uint256 targetChain) external view override returns (bool) {\n return true;\n }\n}\n"
},
"contracts/partner-giveaways/PartnerGiveaways.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport \"../SpecialWeaponsManager.sol\";\n\ncontract PartnerGiveaways is Initializable, AccessControlUpgradeable {\n using SafeMath for uint256;\n using ABDKMath64x64 for int128;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n \n mapping(uint256 => uint256) vars;\n uint256 public constant VAR_SHARDS_REWARD = 1;\n\n SpecialWeaponsManager public specialWeaponsManager;\n\n mapping(address => mapping(uint256 => bool)) public userClaimedAtGiveaway;\n mapping(uint256 => mapping(uint256 => bool)) public nftClaimedAtGiveaway;\n\n mapping(uint256 => address) public giveawayIdToAddress;\n mapping(uint256 => uint256) giveawayIdToSpecialEventId;\n mapping(uint256 => uint256) public giveawayEndTime;\n\n uint256 public nextGiveawayId;\n\n function initialize(SpecialWeaponsManager _specialWeaponsManager)\n public\n initializer\n {\n __AccessControl_init();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _setupRole(GAME_ADMIN, msg.sender);\n\n specialWeaponsManager = _specialWeaponsManager;\n vars[VAR_SHARDS_REWARD] = 125;\n }\n\n // MODIFIERS\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"NGA\");\n }\n\n // VIEWS\n\n function getGiveawayClaimInfoForUser(uint256 giveawayId, uint256 nftId) external view returns(bool didUserClaim, bool didNftClaim){\n didUserClaim = userClaimedAtGiveaway[msg.sender][giveawayId];\n didNftClaim = nftClaimedAtGiveaway[giveawayId][nftId];\n }\n\n // FUNCTIONS\n \n function createGiveawayForNft(address requiredNftAddress, uint256 specialEventId, uint256 giveawayPeriod) external restricted {\n giveawayIdToAddress[nextGiveawayId] = requiredNftAddress;\n giveawayIdToSpecialEventId[nextGiveawayId] = specialEventId;\n giveawayEndTime[nextGiveawayId] = block.timestamp + giveawayPeriod;\n nextGiveawayId++; \n }\n\n function claimReward(uint256 giveawayId, uint256 nftId) external {\n require(msg.sender == IERC721(giveawayIdToAddress[giveawayId]).ownerOf(nftId), \"Nft not owned\");\n require(!userClaimedAtGiveaway[msg.sender][giveawayId], \"Reward for this wallet already claimed\");\n require(!nftClaimedAtGiveaway[giveawayId][nftId], \"Reward for this NFT already claimed\");\n require(block.timestamp <= giveawayEndTime[giveawayId], \"Giveaway ended\");\n userClaimedAtGiveaway[msg.sender][giveawayId] = true;\n nftClaimedAtGiveaway[giveawayId][nftId] = true;\n specialWeaponsManager.addShards(msg.sender, giveawayIdToSpecialEventId[giveawayId], vars[VAR_SHARDS_REWARD]);\n }\n\n // VAR SETTER\n function setVar(uint256 varField, uint256 value) external restricted {\n vars[varField] = value;\n }\n\n function setVars(uint256[] calldata varFields, uint256[] calldata values) external restricted {\n for(uint i = 0; i < varFields.length; i++) {\n vars[varFields[i]] = values[i];\n }\n }\n}\n"
},
"contracts/ValorStakingRewardsUpgradeable.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./staking/StakingRewardsUpgradeable.sol\";\n\ncontract ValorStakingRewardsUpgradeable is StakingRewardsUpgradeable {\n}\n"
},
"contracts/SkillStakingRewardsUpgradeable.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./staking/StakingRewardsUpgradeable.sol\";\nimport \"./interfaces/IStakeFromGame.sol\";\nimport \"./cryptoblades.sol\";\n\ncontract SkillStakingRewardsUpgradeable is\n IStakeFromGame,\n StakingRewardsUpgradeable\n{\n\n SpecialWeaponsManager private specialWeaponsManager;\n\n /* ========== VIEWS ========== */\n\n function game() external view returns (CryptoBlades) {\n return __game;\n }\n\n /* ========== RESTRICTED FUNCTIONS ========== */\n\n function migrateTo_23b3a8b(CryptoBlades _game) external onlyOwner {\n __game = _game;\n }\n\n function migrateTo_e1fe97c(SpecialWeaponsManager _swm) external onlyOwner {\n specialWeaponsManager = _swm;\n }\n\n function stakeFromGame(address player, uint256 amount)\n external\n override\n normalMode\n nonReentrant\n whenNotPaused\n onlyGame\n updateReward(player)\n {\n _stake(player, amount);\n stakingToken.safeTransferFrom(address(__game), address(this), amount);\n }\n\n function unstakeToGame(address player, uint256 amount)\n external\n override\n normalMode\n nonReentrant\n whenNotPaused\n onlyGame\n updateReward(player)\n {\n _unstake(player, amount);\n stakingToken.transfer(address(__game), amount);\n }\n\n function stake(uint256 amount)\n external\n override\n normalMode\n nonReentrant\n whenNotPaused\n updateReward(msg.sender)\n {\n _stake(msg.sender, amount);\n stakingToken.safeTransferFrom(msg.sender, address(this), amount);\n specialWeaponsManager.updateStakingReward(msg.sender, _balances[msg.sender]);\n }\n\n function withdraw(uint256 amount)\n public\n override\n normalMode\n nonReentrant\n updateReward(msg.sender)\n {\n require(\n minimumStakeTime == 0 ||\n block.timestamp.sub(_stakeTimestamp[msg.sender]) >=\n minimumStakeTime ||\n periodFinish <= block.timestamp,\n \"Cannot withdraw until minimum staking time has passed\"\n );\n _unstake(msg.sender, amount);\n stakingToken.safeTransfer(msg.sender, amount);\n specialWeaponsManager.updateStakingReward(msg.sender, _balances[msg.sender]);\n }\n\n function exit() external override normalMode {\n withdraw(_balances[msg.sender]);\n getReward();\n }\n\n function recoverOwnStake() external override failsafeMode {\n uint256 amount = _balances[msg.sender];\n if (amount > 0) {\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n stakingToken.safeTransfer(msg.sender, amount);\n specialWeaponsManager.updateStakingReward(msg.sender, _balances[msg.sender]);\n }\n }\n\n function recoverERC20(address tokenAddress, uint256 tokenAmount)\n external\n virtual\n override\n onlyOwner\n {\n IERC20(tokenAddress).safeTransfer(owner(), tokenAmount);\n emit Recovered(tokenAddress, tokenAmount);\n }\n\n /* ========== MODIFIERS ========== */\n\n modifier onlyGame() {\n require(msg.sender == address(__game), \"Only callable by game\");\n _;\n }\n}\n"
},
"contracts/SkillStakingRewardsUpgradeable90.sol": {
"content": "pragma solidity ^0.6.2;\nimport \"./SkillStakingRewardsUpgradeable.sol\";\n\ncontract SkillStakingRewardsUpgradeable90 is SkillStakingRewardsUpgradeable\n{\n}\n"
},
"contracts/SkillStakingRewardsUpgradeable60.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./SkillStakingRewardsUpgradeable.sol\";\n\ncontract SkillStakingRewardsUpgradeable60 is SkillStakingRewardsUpgradeable {\n function recoverERC20(address tokenAddress, uint256 tokenAmount)\n external\n override\n onlyOwner\n {\n IERC20(tokenAddress).safeTransfer(owner(), tokenAmount);\n emit Recovered(tokenAddress, tokenAmount);\n }\n}\n"
},
"contracts/SkillStakingRewardsUpgradeable180.sol": {
"content": "pragma solidity ^0.6.2;\nimport \"./SkillStakingRewardsUpgradeable.sol\";\n\ncontract SkillStakingRewardsUpgradeable180 is SkillStakingRewardsUpgradeable\n{\n}\n"
},
"contracts/LPStakingRewardsUpgradeableValor.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./staking/StakingRewardsUpgradeable.sol\";\n\ncontract LPStakingRewardsUpgradeableValor is StakingRewardsUpgradeable {\n}\n"
},
"contracts/LPStakingRewardsUpgradeable.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./staking/StakingRewardsUpgradeable.sol\";\n\ncontract LPStakingRewardsUpgradeable is StakingRewardsUpgradeable {\n function recoverERC20(address tokenAddress, uint256 tokenAmount)\n external\n override\n onlyOwner\n {\n IERC20(tokenAddress).safeTransfer(owner(), tokenAmount);\n emit Recovered(tokenAddress, tokenAmount);\n }\n}\n"
},
"contracts/LP2StakingRewardsUpgradeableValor.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./staking/StakingRewardsUpgradeable.sol\";\n\ncontract LP2StakingRewardsUpgradeableValor is StakingRewardsUpgradeable {\n}\n"
},
"contracts/LP2StakingRewardsUpgradeable.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./staking/StakingRewardsUpgradeable.sol\";\n\ncontract LP2StakingRewardsUpgradeable is StakingRewardsUpgradeable {\n function recoverERC20(address tokenAddress, uint256 tokenAmount)\n external\n override\n onlyOwner\n {\n IERC20(tokenAddress).safeTransfer(owner(), tokenAmount);\n emit Recovered(tokenAddress, tokenAmount);\n }\n}\n"
},
"contracts/KingStakingRewardsUpgradeable.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./staking/StakingRewardsUpgradeable.sol\";\n\ncontract KingStakingRewardsUpgradeable is StakingRewardsUpgradeable {\n function withdrawWithoutFee(uint256 amount) public {\n super.withdraw(amount);\n uint256 feeCompensation = amount.mul(1e16).div(1e18);\n stakingToken.safeTransfer(msg.sender, feeCompensation);\n }\n\n function getRewardWithoutFee() public {\n uint256 feeCompensation = rewards[msg.sender].mul(1e16).div(1e18);\n super.getReward();\n if(feeCompensation > 0) {\n rewardsToken.safeTransfer(msg.sender, feeCompensation);\n }\n }\n}"
},
"contracts/KingStakingRewardsUpgradeable90.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./KingStakingRewardsUpgradeable.sol\";\n\ncontract KingStakingRewardsUpgradeable90 is KingStakingRewardsUpgradeable {\n}"
},
"contracts/KingStakingRewardsUpgradeable180.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./KingStakingRewardsUpgradeable.sol\";\n\ncontract KingStakingRewardsUpgradeable180 is KingStakingRewardsUpgradeable {\n}"
},
"contracts/staking/StakingRewards.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts/math/Math.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/ReentrancyGuard.sol\";\n\n// Inheritance\nimport \"./interfaces/IStakingRewards.sol\";\nimport \"./RewardsDistributionRecipient.sol\";\nimport \"./SynthetixPausable.sol\";\nimport \"./Failsafe.sol\";\n\n// https://docs.synthetix.io/contracts/source/contracts/stakingrewards\ncontract StakingRewards is\n IStakingRewards,\n RewardsDistributionRecipient,\n ReentrancyGuard,\n Failsafe,\n SynthetixPausable\n{\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n /* ========== STATE VARIABLES ========== */\n\n IERC20 public rewardsToken;\n IERC20 public stakingToken;\n uint256 public periodFinish = 0;\n uint256 public override rewardRate = 0;\n uint256 public override rewardsDuration = 180 days;\n uint256 public override minimumStakeAmount;\n uint256 public override minimumStakeTime;\n uint256 public lastUpdateTime;\n uint256 public rewardPerTokenStored;\n\n mapping(address => uint256) public userRewardPerTokenPaid;\n mapping(address => uint256) public rewards;\n\n uint256 private _totalSupply;\n mapping(address => uint256) private _balances;\n mapping(address => uint256) private _stakeTimestamp;\n\n /* ========== CONSTRUCTOR ========== */\n\n constructor(\n address _owner,\n address _rewardsDistribution,\n address _rewardsToken,\n address _stakingToken,\n uint256 _minimumStakeAmount,\n uint256 _minimumStakeTime\n ) public Owned(_owner) {\n rewardsToken = IERC20(_rewardsToken);\n stakingToken = IERC20(_stakingToken);\n rewardsDistribution = _rewardsDistribution;\n minimumStakeAmount = _minimumStakeAmount;\n minimumStakeTime = _minimumStakeTime;\n }\n\n /* ========== VIEWS ========== */\n\n function totalSupply() external view override returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account)\n external\n view\n override\n returns (uint256)\n {\n return _balances[account];\n }\n\n function lastTimeRewardApplicable() public view override returns (uint256) {\n return Math.min(block.timestamp, periodFinish);\n }\n\n function rewardPerToken() public view override returns (uint256) {\n if (_totalSupply == 0) {\n return rewardPerTokenStored;\n }\n return\n rewardPerTokenStored.add(\n lastTimeRewardApplicable()\n .sub(lastUpdateTime)\n .mul(rewardRate)\n .mul(1e18)\n .div(_totalSupply)\n );\n }\n\n function earned(address account) public view override returns (uint256) {\n return\n _balances[account]\n .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))\n .div(1e18)\n .add(rewards[account]);\n }\n\n function getRewardForDuration() external view override returns (uint256) {\n return rewardRate.mul(rewardsDuration);\n }\n\n function getStakeRewardDistributionTimeLeft()\n external\n override\n view\n returns (uint256)\n {\n (bool success, uint256 diff) = periodFinish.trySub(block.timestamp);\n return success ? diff : 0;\n }\n\n function getStakeUnlockTimeLeft() external override view returns (uint256) {\n (bool success, uint256 diff) =\n _stakeTimestamp[msg.sender].add(minimumStakeTime).trySub(\n block.timestamp\n );\n return success ? diff : 0;\n }\n\n /* ========== MUTATIVE FUNCTIONS ========== */\n\n function stake(uint256 amount)\n external\n override\n normalMode\n nonReentrant\n notPaused\n updateReward(msg.sender)\n {\n require(amount >= minimumStakeAmount, \"Minimum stake amount required\");\n _totalSupply = _totalSupply.add(amount);\n _balances[msg.sender] = _balances[msg.sender].add(amount);\n if (_stakeTimestamp[msg.sender] == 0) {\n _stakeTimestamp[msg.sender] = block.timestamp;\n }\n stakingToken.safeTransferFrom(msg.sender, address(this), amount);\n emit Staked(msg.sender, amount);\n }\n\n function withdraw(uint256 amount)\n public\n override\n normalMode\n nonReentrant\n updateReward(msg.sender)\n {\n require(amount > 0, \"Cannot withdraw 0\");\n require(\n minimumStakeTime == 0 ||\n block.timestamp.sub(_stakeTimestamp[msg.sender]) >=\n minimumStakeTime,\n \"Cannot withdraw until minimum staking time has passed\"\n );\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n if (_balances[msg.sender] == 0) {\n _stakeTimestamp[msg.sender] = 0;\n }\n stakingToken.safeTransfer(msg.sender, amount);\n emit Withdrawn(msg.sender, amount);\n }\n\n function getReward()\n public\n override\n normalMode\n nonReentrant\n updateReward(msg.sender)\n {\n require(\n minimumStakeTime == 0 ||\n block.timestamp.sub(_stakeTimestamp[msg.sender]) >=\n minimumStakeTime,\n \"Cannot get reward until minimum staking time has passed\"\n );\n uint256 reward = rewards[msg.sender];\n if (reward > 0) {\n rewards[msg.sender] = 0;\n rewardsToken.safeTransfer(msg.sender, reward);\n emit RewardPaid(msg.sender, reward);\n }\n }\n\n function exit() external override normalMode {\n withdraw(_balances[msg.sender]);\n getReward();\n }\n\n function recoverOwnStake() external failsafeMode {\n uint256 amount = _balances[msg.sender];\n if (amount > 0) {\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n stakingToken.safeTransfer(msg.sender, amount);\n }\n }\n\n /* ========== RESTRICTED FUNCTIONS ========== */\n\n function notifyRewardAmount(uint256 reward)\n external\n override\n normalMode\n onlyRewardsDistribution\n updateReward(address(0))\n {\n if (block.timestamp >= periodFinish) {\n rewardRate = reward.div(rewardsDuration);\n } else {\n uint256 remaining = periodFinish.sub(block.timestamp);\n uint256 leftover = remaining.mul(rewardRate);\n rewardRate = reward.add(leftover).div(rewardsDuration);\n }\n\n // Ensure the provided reward amount is not more than the balance in the contract.\n // This keeps the reward rate in the right range, preventing overflows due to\n // very high values of rewardRate in the earned and rewardsPerToken functions;\n // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.\n uint256 balance = rewardsToken.balanceOf(address(this));\n require(\n rewardRate <= balance.div(rewardsDuration),\n \"Provided reward too high\"\n );\n\n lastUpdateTime = block.timestamp;\n periodFinish = block.timestamp.add(rewardsDuration);\n emit RewardAdded(reward);\n }\n\n // End rewards emission earlier\n function updatePeriodFinish(uint256 timestamp)\n external\n normalMode\n onlyOwner\n updateReward(address(0))\n {\n require(\n timestamp > lastUpdateTime,\n \"Timestamp must be after lastUpdateTime\"\n );\n periodFinish = timestamp;\n }\n\n // Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders\n function recoverERC20(address tokenAddress, uint256 tokenAmount)\n external\n onlyOwner\n {\n require(\n tokenAddress != address(stakingToken),\n \"Cannot withdraw the staking token\"\n );\n IERC20(tokenAddress).safeTransfer(owner, tokenAmount);\n emit Recovered(tokenAddress, tokenAmount);\n }\n\n function setRewardsDuration(uint256 _rewardsDuration)\n external\n normalMode\n onlyOwner\n {\n require(\n block.timestamp > periodFinish,\n \"Previous rewards period must be complete before changing the duration for the new period\"\n );\n rewardsDuration = _rewardsDuration;\n emit RewardsDurationUpdated(rewardsDuration);\n }\n\n function setMinimumStakeAmount(uint256 _minimumStakeAmount)\n external\n normalMode\n onlyOwner\n {\n minimumStakeAmount = _minimumStakeAmount;\n emit MinimumStakeAmountUpdated(_minimumStakeAmount);\n }\n\n function setMinimumStakeTime(uint256 _minimumStakeTime)\n external\n normalMode\n onlyOwner\n {\n minimumStakeTime = _minimumStakeTime;\n emit MinimumStakeTimeUpdated(_minimumStakeTime);\n }\n\n function enableFailsafeMode() public override normalMode onlyOwner {\n minimumStakeAmount = 0;\n minimumStakeTime = 0;\n periodFinish = 0;\n rewardRate = 0;\n rewardPerTokenStored = 0;\n\n super.enableFailsafeMode();\n }\n\n function recoverExtraStakingTokensToOwner() external onlyOwner {\n // stake() and withdraw() should guarantee that\n // _totalSupply <= stakingToken.balanceOf(this)\n uint256 stakingTokenAmountBelongingToOwner =\n stakingToken.balanceOf(address(this)).sub(_totalSupply);\n\n if (stakingTokenAmountBelongingToOwner > 0) {\n stakingToken.safeTransfer(\n owner,\n stakingTokenAmountBelongingToOwner\n );\n }\n }\n\n /* ========== MODIFIERS ========== */\n\n modifier updateReward(address account) {\n if (!failsafeModeActive) {\n rewardPerTokenStored = rewardPerToken();\n lastUpdateTime = lastTimeRewardApplicable();\n if (account != address(0)) {\n rewards[account] = earned(account);\n userRewardPerTokenPaid[account] = rewardPerTokenStored;\n }\n }\n _;\n }\n\n /* ========== EVENTS ========== */\n\n event RewardAdded(uint256 reward);\n event Staked(address indexed user, uint256 amount);\n event Withdrawn(address indexed user, uint256 amount);\n event RewardPaid(address indexed user, uint256 reward);\n event RewardsDurationUpdated(uint256 newDuration);\n event MinimumStakeAmountUpdated(uint256 newMinimumStakeAmount);\n event MinimumStakeTimeUpdated(uint256 newMinimumStakeTime);\n event Recovered(address token, uint256 amount);\n}\n"
},
"@openzeppelin/contracts/utils/ReentrancyGuard.sol": {
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n constructor () internal {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n // On the first call to nonReentrant, _notEntered will be true\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n}\n"
},
"contracts/staking/RewardsDistributionRecipient.sol": {
"content": "pragma solidity ^0.6.2;\n\n// Inheritance\nimport \"./Owned.sol\";\n\n// https://docs.synthetix.io/contracts/source/contracts/rewardsdistributionrecipient\nabstract contract RewardsDistributionRecipient is Owned {\n address public rewardsDistribution;\n\n function notifyRewardAmount(uint256 reward) external virtual;\n\n modifier onlyRewardsDistribution() {\n require(\n msg.sender == rewardsDistribution,\n \"Caller is not RewardsDistribution contract\"\n );\n _;\n }\n\n function setRewardsDistribution(address _rewardsDistribution)\n external\n onlyOwner\n {\n rewardsDistribution = _rewardsDistribution;\n }\n}\n"
},
"contracts/staking/SynthetixPausable.sol": {
"content": "pragma solidity ^0.6.2;\n\n// Inheritance\nimport \"./Owned.sol\";\n\n// https://docs.synthetix.io/contracts/source/contracts/pausable\nabstract contract SynthetixPausable is Owned {\n uint256 public lastPauseTime;\n bool public paused;\n\n constructor() internal {\n // This contract is abstract, and thus cannot be instantiated directly\n require(owner != address(0), \"Owner must be set\");\n // Paused will be false, and lastPauseTime will be 0 upon initialisation\n }\n\n /**\n * @notice Change the paused state of the contract\n * @dev Only the contract owner may call this.\n */\n function setPaused(bool _paused) external onlyOwner {\n // Ensure we're actually changing the state before we do anything\n if (_paused == paused) {\n return;\n }\n\n // Set our paused state.\n paused = _paused;\n\n // If applicable, set the last pause time.\n if (paused) {\n lastPauseTime = now;\n }\n\n // Let everyone know that our pause state has changed.\n emit PauseChanged(paused);\n }\n\n event PauseChanged(bool isPaused);\n\n modifier notPaused {\n require(\n !paused,\n \"This action cannot be performed while the contract is paused\"\n );\n _;\n }\n}\n"
},
"contracts/staking/Failsafe.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./Owned.sol\";\n\nabstract contract Failsafe is Owned {\n bool public failsafeModeActive = false;\n\n constructor() internal {\n require(owner != address(0), \"Owner must be set\");\n }\n\n function enableFailsafeMode() public virtual onlyOwner {\n failsafeModeActive = true;\n emit FailsafeModeEnabled();\n }\n\n event FailsafeModeEnabled();\n\n modifier normalMode {\n require(\n !failsafeModeActive,\n \"This action cannot be performed while the contract is in Failsafe Mode\"\n );\n _;\n }\n\n modifier failsafeMode {\n require(\n failsafeModeActive,\n \"This action can only be performed while the contract is in Failsafe Mode\"\n );\n _;\n }\n}\n"
},
"contracts/staking/Owned.sol": {
"content": "pragma solidity ^0.6.2;\n\n// https://docs.synthetix.io/contracts/source/contracts/owned\ncontract Owned {\n address public owner;\n address public nominatedOwner;\n\n constructor(address _owner) public {\n require(_owner != address(0), \"Owner address cannot be 0\");\n owner = _owner;\n emit OwnerChanged(address(0), _owner);\n }\n\n function nominateNewOwner(address _owner) external onlyOwner {\n nominatedOwner = _owner;\n emit OwnerNominated(_owner);\n }\n\n function acceptOwnership() external {\n require(\n msg.sender == nominatedOwner,\n \"You must be nominated before you can accept ownership\"\n );\n emit OwnerChanged(owner, nominatedOwner);\n owner = nominatedOwner;\n nominatedOwner = address(0);\n }\n\n modifier onlyOwner {\n _onlyOwner();\n _;\n }\n\n function _onlyOwner() private view {\n require(\n msg.sender == owner,\n \"Only the contract owner may perform this action\"\n );\n }\n\n event OwnerNominated(address newOwner);\n event OwnerChanged(address oldOwner, address newOwner);\n}\n"
},
"contracts/SkillStakingRewards.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./staking/StakingRewards.sol\";\n\ncontract SkillStakingRewards is StakingRewards {\n constructor(\n address _owner,\n address _rewardsDistribution,\n address _rewardsToken,\n address _stakingToken,\n uint256 _minimumStakeAmount,\n uint256 _minimumStakeTime\n )\n public\n StakingRewards(\n _owner,\n _rewardsDistribution,\n _rewardsToken,\n _stakingToken,\n _minimumStakeAmount,\n _minimumStakeTime\n )\n {}\n}\n"
},
"contracts/LPStakingRewards.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./staking/StakingRewards.sol\";\n\ncontract LPStakingRewards is StakingRewards {\n constructor(\n address _owner,\n address _rewardsDistribution,\n address _rewardsToken,\n address _stakingToken,\n uint256 _minimumStakeAmount,\n uint256 _minimumStakeTime\n )\n public\n StakingRewards(\n _owner,\n _rewardsDistribution,\n _rewardsToken,\n _stakingToken,\n _minimumStakeAmount,\n _minimumStakeTime\n )\n {}\n}\n"
},
"contracts/CBKLandT1StakingRewardsUpgradeable.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"./staking/NftStakingRewardsUpgradeable.sol\";\nimport \"./CBKLand.sol\";\n\ncontract CBKLandT1StakingRewardsUpgradeable is NftStakingRewardsUpgradeable {\n CBKLand internal __cbkLand;\n uint256 public stakeTier;\n\n function initialize(\n address _owner,\n address _rewardsDistribution,\n address _rewardsToken,\n address _stakingToken,\n uint256 _minimumStakeTime,\n CBKLand _cbkLand\n ) public initializer {\n super.initialize(\n _owner,\n _rewardsDistribution,\n _rewardsToken,\n _stakingToken,\n _minimumStakeTime);\n __cbkLand = _cbkLand;\n stakeTier = 1;\n }\n\n function stake(uint256 id) public virtual override {\n uint256 landTier = __cbkLand.getLandTier(id);\n require(landTier == stakeTier, 'Land tier mismatch');\n super.stake(id);\n }\n\n function bulkStake(uint256[] memory ids) public virtual override {\n for(uint i = 0; i < ids.length; i++) {\n uint256 landTier = __cbkLand.getLandTier(ids[i]);\n require(landTier == stakeTier, 'Land tier mismatch');\n }\n super.bulkStake(ids);\n }\n}"
},
"contracts/CBKLandBridgeProxyContract.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"./CBKLand.sol\";\nimport \"./interfaces/IBridgeProxy.sol\";\n\n\ncontract CBKLandBridgeProxyContract is Initializable, AccessControlUpgradeable, IBridgeProxy {\n \n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n CBKLand private _cbkLand;\n address private _nftStorageAddress;\n bool private _enabled;\n\n uint8 public constant UINT_NFT_VAR_META = 0;\n uint8 public constant UINT_NFT_VAR_ADDRESS = 1;\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"NA\");\n }\n\n function initialize(address nftStorageAddress, address cbkLand) public initializer {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n _nftStorageAddress = nftStorageAddress;\n _cbkLand = CBKLand(cbkLand);\n }\n\n \n // tier, chunkid, x, y, reseller address\n function collectData(uint256 tokenId) external view override returns (uint256[] memory uintVars, string memory stringVar) {\n \n (uint256 tier, uint256 chunkId, uint256 x, uint256 y, address reseller) = _cbkLand.get(tokenId);\n\n uintVars = new uint256[](2);\n uintVars[UINT_NFT_VAR_META] = _packCBKLandData(tier, chunkId, x, y);\n uintVars[UINT_NFT_VAR_ADDRESS] = uint256(uint160(reseller));\n }\n\n // for future use, bot will probe the returned value to know if the proxy contract has proper signature behavior\n function sigVersion() external view override returns (uint256) {\n return 3;\n }\n\n function isEnabled() external view override returns (bool) {\n return _enabled;\n }\n\n function setEnabled(bool enabled) external restricted {\n _enabled = enabled;\n }\n\n function mintOrUpdate(address /*receiver*/, uint256 tokenId, uint256[] calldata uintVars, string calldata stringVar) external restricted override returns (uint256) {\n require(_enabled, \"not enabled\");\n\n (uint256 tier, uint256 chunkId, uint256 x, uint256 y) = _unpackCBKLandData(uintVars[UINT_NFT_VAR_META]);\n address reseller = address(uint160(uintVars[UINT_NFT_VAR_ADDRESS]));\n\n tokenId = \n _cbkLand.mintOrUpdate(tokenId, _nftStorageAddress, tier, chunkId, x, y, reseller);\n\n return tokenId;\n }\n\n // tier => 1, 2 or 3 => 8 bits more than enough\n // chunkid => max is 9999 => 16 bits more than enough\n // x => max is 5000 => 16 bits more than enough\n // y => max is 5000 => 16 bits more than enough\n function _packCBKLandData(uint256 tier, uint256 chunkId, uint256 x, uint256 y) internal pure returns (uint256) {\n return tier | chunkId << 8 | x << 24 | y << 40;\n }\n\n function _unpackCBKLandData(uint256 metaData) internal pure returns (uint256 tier, uint256 chunkId, uint256 x, uint256 y) {\n tier = metaData & 0xFF;\n chunkId = (metaData >> 8) & 0xFFFF;\n x = (metaData >> 24) & 0xFFFF;\n y = (metaData >> 40) & 0xFFFF;\n }\n\n function canBridge(address wallet, uint256 tokenId, uint256 targetChain) external view override returns (bool) {\n return true;\n }\n}\n"
},
"contracts/Dex.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.6.5;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport \"./common.sol\";\n\ncontract Dex is Initializable, AccessControlUpgradeable {\n using SafeMath for uint256;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n uint public constant FEE_DENOMINATOR = 10000;\n\n uint8 public constant VAR_FEE = 0;\n uint8 public constant VAR_CONTRACT_ENABLED = 1;\n\n struct TokenPair {\n address token1;\n uint token1Amount;\n address token2;\n uint token2Amount;\n }\n\n mapping(uint => TokenPair) public tokenPairs;\n mapping(address => uint) public collectedFees;\n mapping(uint256 => uint256) public vars;\n\n event TokenPairSwapped(address indexed token1, uint token1Amount, address indexed token2, uint token2Amount);\n event TokenPairAdded(uint id, address indexed token1, address indexed token2);\n event TokenPairRemoved(uint id, address indexed token1, address indexed token2);\n event LiquidityAdded(address indexed token1, uint token1Amount, address indexed token2, uint token2Amount);\n event LiquiditySet(address indexed token1, uint token1Amount, address indexed token2, uint token2Amount);\n event LiquidityTaken(address indexed token1, uint token1Amount, address indexed token2, uint token2Amount);\n event ERC20Withdrawal(address indexed token, uint amount);\n\n function initialize() virtual public initializer {\n __AccessControl_init_unchained();\n _setupRole(DEFAULT_ADMIN_ROLE, tx.origin);\n _setupRole(GAME_ADMIN, tx.origin);\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"NA\");\n }\n\n modifier contractEnabled() {\n _contractEnabled();\n _;\n }\n\n function _contractEnabled() internal view {\n require(vars[VAR_CONTRACT_ENABLED] == 1, \"Contract disabled\");\n }\n\n // FUNCTIONS\n\n function swap(address tokenA, address tokenB, uint amountA) external contractEnabled {\n uint id = getTokenPairId(tokenA, tokenB);\n TokenPair memory pair = tokenPairs[id];\n require((pair.token1 == tokenA && pair.token2 == tokenB) || (pair.token2 == tokenA && pair.token1 == tokenB), \"Invalid pair\");\n amountA = transferWithAdditionalAmountCheck(tokenA, msg.sender, address(this), amountA);\n uint amountB = getAmountOut(tokenA, tokenB, amountA);\n require(amountB > 0, \"Invalid amount\");\n if (tokenA == pair.token1) {\n pair.token1Amount += amountA;\n pair.token2Amount -= amountB;\n } else {\n pair.token1Amount -= amountB;\n pair.token2Amount += amountA;\n }\n tokenPairs[id] = pair;\n uint fee = amountB.mul(vars[VAR_FEE]).div(FEE_DENOMINATOR);\n collectedFees[tokenB] = collectedFees[tokenB].add(fee);\n IERC20(tokenB).transfer(msg.sender, Common.adjustDecimals(amountB - fee, ERC20(tokenB).decimals()));\n emit TokenPairSwapped(tokenA, amountA, tokenB, amountB);\n }\n\n // VIEWS\n\n function getTokenPairId(address tokenA, address tokenB) public pure returns (uint id) {\n (address token1, address token2) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);\n id = uint(keccak256(abi.encodePacked(token1, token2)));\n }\n\n function getAmountOut(address tokenA, address tokenB, uint amountA) public view returns (uint amountTo) {\n uint id = getTokenPairId(tokenA, tokenB);\n TokenPair memory pair = tokenPairs[id];\n require(amountA > 0, \"Invalid amount\");\n if (tokenA == pair.token1) {\n amountTo = pair.token2Amount.mul(amountA).div(pair.token1Amount);\n } else {\n amountTo = pair.token1Amount.mul(amountA).div(pair.token2Amount);\n }\n }\n\n function getVars(uint256[] calldata varFields) external view returns (uint256[] memory) {\n uint256[] memory result = new uint256[](varFields.length);\n for (uint i = 0; i < varFields.length; i++) {\n result[i] = vars[varFields[i]];\n }\n return result;\n }\n\n // ADMIN\n\n function setVar(uint256 varField, uint256 value) external restricted {\n vars[varField] = value;\n }\n\n function addPair(address tokenA, uint amountA, address tokenB, uint amountB) external restricted {\n (address token1, uint amount1, address token2, uint amount2) = tokenA < tokenB ? (tokenA, amountA, tokenB, amountB) : (tokenB, amountB, tokenA, amountA);\n uint id = getTokenPairId(token1, token2);\n require(tokenPairs[id].token1 == address(0) && tokenPairs[id].token2 == address(0), \"Pair already exists\");\n amount1 = transferWithAdditionalAmountCheck(token1, msg.sender, address(this), amount1);\n amount2 = transferWithAdditionalAmountCheck(token2, msg.sender, address(this), amount2);\n tokenPairs[id] = TokenPair(token1, amount1, token2, amount2);\n emit TokenPairAdded(id, token1, token2);\n }\n\n function addPairWithoutLiquidity(address tokenA, address tokenB) external restricted {\n (address token1, address token2) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);\n uint id = getTokenPairId(token1, token2);\n require(tokenPairs[id].token1 == address(0) && tokenPairs[id].token2 == address(0), \"Pair already exists\");\n tokenPairs[id] = TokenPair(token1, 0, token2, 0);\n emit TokenPairAdded(id, token1, token2);\n }\n\n function deletePair(address tokenA, address tokenB) external restricted {\n (address token1, address token2) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);\n uint id = getTokenPairId(token1, token2);\n require(tokenPairs[id].token1 != address(0) && tokenPairs[id].token2 != address(0), \"Pair doesn't exist\");\n delete tokenPairs[id];\n emit TokenPairRemoved(id, token1, token2);\n }\n\n function addLiquidity(address tokenA, uint amountA, address tokenB, uint amountB) external restricted {\n (address token1, uint amount1, address token2, uint amount2) = tokenA < tokenB ? (tokenA, amountA, tokenB, amountB) : (tokenB, amountB, tokenA, amountA);\n uint id = getTokenPairId(token1, token2);\n TokenPair memory pair = tokenPairs[id];\n require(pair.token1 == token1 && pair.token2 == token2, \"Invalid pair\");\n require(amount1 > 0 && amount2 > 0, \"Invalid amount\");\n amount1 = transferWithAdditionalAmountCheck(token1, msg.sender, address(this), amount1);\n amount2 = transferWithAdditionalAmountCheck(token2, msg.sender, address(this), amount2);\n pair.token1Amount += amount1;\n pair.token2Amount += amount2;\n tokenPairs[id] = pair;\n emit LiquidityAdded(token1, amount1, token2, amount2);\n }\n\n function setLiquidityInWei(address tokenA, uint amountA, address tokenB, uint amountB) external restricted {\n (address token1, uint amount1, address token2, uint amount2) = tokenA < tokenB ? (tokenA, amountA, tokenB, amountB) : (tokenB, amountB, tokenA, amountA);\n uint id = getTokenPairId(token1, token2);\n TokenPair memory pair = tokenPairs[id];\n require(pair.token1 == token1 && pair.token2 == token2, \"Invalid pair\");\n pair.token1Amount = amount1;\n pair.token2Amount = amount2;\n tokenPairs[id] = pair;\n emit LiquiditySet(token1, amount1, token2, amount2);\n }\n\n function takeLiquidityInWei(address tokenA, uint amountA, address tokenB, uint amountB) external restricted {\n (address token1, uint amount1, address token2, uint amount2) = tokenA < tokenB ? (tokenA, amountA, tokenB, amountB) : (tokenB, amountB, tokenA, amountA);\n uint id = getTokenPairId(token1, token2);\n TokenPair memory pair = tokenPairs[id];\n require(pair.token1 == token1 && pair.token2 == token2, \"Invalid pair\");\n IERC20(token1).transfer(msg.sender, amount1);\n IERC20(token2).transfer(msg.sender, amount2);\n pair.token1Amount = pair.token1Amount.sub(amount1);\n pair.token2Amount = pair.token2Amount.sub(amount2);\n tokenPairs[id] = pair;\n emit LiquidityTaken(token1, amount1, token2, amount2);\n }\n\n function withdrawERC20Wei(address token, uint256 amount) external restricted {\n IERC20(token).transfer(msg.sender, amount);\n emit ERC20Withdrawal(token, amount);\n }\n\n function collectFees(address token) external restricted {\n uint fee = collectedFees[token];\n IERC20(token).transfer(msg.sender, Common.adjustDecimals(fee, ERC20(token).decimals()));\n collectedFees[token] = 0;\n }\n\n // PRIVATE\n\n function transferWithAdditionalAmountCheck(address token, address from, address to, uint amount) private returns (uint) {\n uint amountBefore = IERC20(token).balanceOf(address(this));\n IERC20(token).transferFrom(from, to, Common.adjustDecimals(amount, ERC20(token).decimals()));\n uint amountAfter = IERC20(token).balanceOf(address(this));\n return amountAfter.sub(amountBefore);\n }\n\n}"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers"
],
"": ["ast"]
}
}
}
}
This file has been truncated, but you can view the full file.
{
"settings": {
"evmVersion": "istanbul",
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [],
"outputSelection": {
"contracts/BurningManager.sol": {
"BurningManager": [
"evm.bytecode.object",
"evm.deployedBytecode.object",
"metadata"
]
}
}
},
"sources": {
"contracts/Blacksmith.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/SafeERC20.sol\";\nimport \"./interfaces/IPriceOracle.sol\";\nimport \"./interfaces/IRandoms.sol\";\nimport \"./shields.sol\";\nimport \"./Consumables.sol\";\nimport \"./Cosmetics.sol\";\nimport \"./weapons.sol\";\nimport \"./cryptoblades.sol\";\nimport \"./CBKLandSale.sol\";\n\ncontract Blacksmith is Initializable, AccessControlUpgradeable {\n using SafeERC20 for IERC20;\n /* ========== CONSTANTS ========== */\n\n bytes32 public constant GAME = keccak256(\"GAME\");\n bytes32 public constant SHIELD_SEED = keccak256(\"SHIELD_SEED\");\n\n uint256 public constant ITEM_WEAPON_RENAME = 1;\n uint256 public constant ITEM_CHARACTER_RENAME = 2;\n uint256 public constant ITEM_CHARACTER_TRAITCHANGE_FIRE = 3;\n uint256 public constant ITEM_CHARACTER_TRAITCHANGE_EARTH = 4;\n uint256 public constant ITEM_CHARACTER_TRAITCHANGE_WATER = 5;\n uint256 public constant ITEM_CHARACTER_TRAITCHANGE_LIGHTNING = 6;\n uint256 public constant ITEM_COSMETIC_WEAPON = 7; // series\n uint256 public constant ITEM_COSMETIC_CHARACTER = 8; // series\n uint256 public constant ITEM_SHIELD = 9;\n\n uint256 public constant VAR_PURCHASE_SHIELD_TYPE = 1;\n uint256 public constant VAR_PURCHASE_SHIELD_SUPPLY = 2; // only for non-0 type shields\n\n uint256 public constant LINK_SKILL_ORACLE_2 = 1; // technically second skill oracle (it's separate)\n uint256 public constant LINK_KING_ORACLE = 2;\n uint256 public constant LINK_SAFE_RANDOMS = 3;\n\n uint256 public constant CURRENCY_SKILL = 0;\n //uint256 public constant CURRENCY_KING = 1; // not referenced atm\n\n /* ========== STATE VARIABLES ========== */\n\n Weapons public weapons;\n IRandoms public randoms;\n\n mapping(address => uint32) public tickets;\n\n Shields public shields;\n CryptoBlades public game;\n\n\n // keys: ITEM_ constant\n mapping(uint256 => address) public itemAddresses;\n mapping(uint256 => uint256) public itemFlatPrices;\n\n mapping(uint256 => uint256) public numberParameters; // AKA \"vars\"\n\n mapping(uint256 => mapping(uint256 => uint256)) public itemSeriesFlatPrices;\n CBKLandSale public cbkLandSale;\n // ERC20 => tier => price\n mapping(uint256 => mapping(uint256 => uint256)) public landPrices;\n mapping(uint256 => address) currencies;\n\n mapping(uint256 => address) public links;\n\n /* ========== INITIALIZERS AND MIGRATORS ========== */\n\n function initialize(Weapons _weapons, IRandoms _randoms)\n public\n initializer\n {\n __AccessControl_init();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n weapons = _weapons;\n randoms = _randoms;\n }\n\n function migrateRandoms(IRandoms _newRandoms) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n randoms = _newRandoms;\n }\n\n function migrateTo_61c10da(Shields _shields, CryptoBlades _game) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n shields = _shields;\n game = _game;\n }\n\n function migrateTo_16884dd(\n address _characterRename,\n address _weaponRename,\n address _charFireTraitChange,\n address _charEarthTraitChange,\n address _charWaterTraitChange,\n address _charLightningTraitChange\n ) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n itemAddresses[ITEM_WEAPON_RENAME] = _weaponRename;\n itemAddresses[ITEM_CHARACTER_RENAME] = _characterRename;\n itemAddresses[ITEM_CHARACTER_TRAITCHANGE_FIRE] = _charFireTraitChange;\n itemAddresses[ITEM_CHARACTER_TRAITCHANGE_EARTH] = _charEarthTraitChange;\n itemAddresses[ITEM_CHARACTER_TRAITCHANGE_WATER] = _charWaterTraitChange;\n itemAddresses[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING] = _charLightningTraitChange;\n\n itemFlatPrices[ITEM_WEAPON_RENAME] = 0.1 ether;\n itemFlatPrices[ITEM_CHARACTER_RENAME] = 0.1 ether;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE] = 0.2 ether;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH] = 0.2 ether;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER] = 0.2 ether;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING] = 0.2 ether;\n }\n\n function migrateTo_bcdf4c(CBKLandSale _cbkLandSale) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n cbkLandSale = _cbkLandSale;\n }\n\n /* ========== VIEWS ========== */\n\n /* ========== MUTATIVE FUNCTIONS ========== */\n\n function recoverToken(address tokenAddress, uint256 amount) public {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n\n IERC20(tokenAddress).safeTransfer(msg.sender, amount);\n }\n\n function getSeed(uint seedId, uint shieldType) internal pure returns (uint256 seed) {\n uint[] memory seeds = new uint[](3);\n seeds[0] = seedId;\n seeds[1] = uint(1);\n seeds[2] = shieldType;\n seed = RandomUtil.combineSeeds(seeds);\n }\n\n function hasSeed(uint seedId, uint shieldType) public view returns (bool) {\n return SafeRandoms(links[LINK_SAFE_RANDOMS]).hasSingleSeedRequest(msg.sender, getSeed(seedId, shieldType));\n }\n\n function generateShieldSeed() external {\n require(itemFlatPrices[ITEM_SHIELD] > 0);\n uint256 shieldType = numberParameters[VAR_PURCHASE_SHIELD_TYPE];\n payCurrency(msg.sender, itemFlatPrices[ITEM_SHIELD], CURRENCY_SKILL);\n SafeRandoms(links[LINK_SAFE_RANDOMS]).requestSingleSeed(msg.sender, getSeed(uint(SHIELD_SEED), shieldType));\n }\n\n function claimShield() external {\n uint256 shieldType = numberParameters[VAR_PURCHASE_SHIELD_TYPE];\n if(shieldType != 0) {\n require(numberParameters[VAR_PURCHASE_SHIELD_SUPPLY] > 0);\n numberParameters[VAR_PURCHASE_SHIELD_SUPPLY] -= 1;\n }\n uint256 seed = SafeRandoms(links[LINK_SAFE_RANDOMS]).popSingleSeed(msg.sender, getSeed(uint(SHIELD_SEED), shieldType), true, false);\n shields.mint(msg.sender, shieldType, seed);\n }\n\n /* ========== MODIFIERS ========== */\n\n modifier onlyGame() {\n require(hasRole(GAME, msg.sender), \"Only game\");\n _;\n }\n\n modifier isAdmin() {\n _isAdmin();\n _;\n }\n\n function _isAdmin() internal view {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n }\n\n /* ========== Generic Getters ========== */\n\n function getAddressOfItem(uint256 itemIndex) public view returns(address) {\n return itemAddresses[itemIndex];\n }\n\n function getFlatPriceOfItem(uint256 itemIndex) public view returns(uint256) {\n return itemFlatPrices[itemIndex];\n }\n\n function getFlatPriceOfSeriesItem(uint256 itemIndex, uint256 seriesIndex) public view returns(uint256) {\n return itemSeriesFlatPrices[itemIndex][seriesIndex];\n }\n\n function getCurrency(uint256 currency) public view returns (address) {\n return currencies[currency];\n }\n\n function vars(uint256 varField) public view returns (uint256) {\n return numberParameters[varField];\n }\n\n /* ========== Generic Setters ========== */\n\n function setAddressOfItem(uint256 itemIndex, address to) external isAdmin {\n itemAddresses[itemIndex] = to;\n }\n\n function setFlatPriceOfItem(uint256 itemIndex, uint256 flatWeiPrice) external isAdmin {\n itemFlatPrices[itemIndex] = flatWeiPrice;\n }\n\n function setFlatPriceOfItemSeries(uint256 itemIndex,\n uint256[] calldata seriesIndices,\n uint256[] calldata seriesPrices\n ) external isAdmin {\n for(uint i = 0; i < seriesIndices.length; i++) {\n itemSeriesFlatPrices[itemIndex][seriesIndices[i]] = seriesPrices[i];\n }\n }\n\n function setCurrency(uint256 currency, address currencyAddress, bool forced) external isAdmin {\n require(currency > 0 && (forced || currencies[currency] == address(0)), 'used');\n currencies[currency] = currencyAddress;\n }\n\n function setLink(uint256 linkId, address linkAddress) external isAdmin {\n links[linkId] = linkAddress;\n }\n\n function setVar(uint256 varField, uint256 value) external isAdmin {\n numberParameters[varField] = value;\n }\n\n function setVars(uint256[] calldata varFields, uint256[] calldata values) external isAdmin {\n for(uint i = 0; i < varFields.length; i++) {\n numberParameters[varFields[i]] = values[i];\n }\n }\n\n /* ========== Character Rename ========== */\n\n function setCharacterRenamePrice(uint256 newPrice) external isAdmin {\n require(newPrice > 0, 'invalid price');\n itemFlatPrices[ITEM_CHARACTER_RENAME] = newPrice;\n }\n\n function characterRenamePrice() public view returns (uint256){\n return itemFlatPrices[ITEM_CHARACTER_RENAME];\n }\n\n function purchaseCharacterRenameTag(uint256 paying) public {\n require(paying == itemFlatPrices[ITEM_CHARACTER_RENAME], 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_CHARACTER_RENAME], CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_CHARACTER_RENAME]).giveItem(msg.sender, 1);\n }\n\n function purchaseCharacterRenameTagDeal(uint256 paying) public { // 4 for the price of 3\n require(paying == itemFlatPrices[ITEM_CHARACTER_RENAME] * 3, 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_CHARACTER_RENAME] * 3, CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_CHARACTER_RENAME]).giveItem(msg.sender, 4);\n }\n\n /* ========== Weapon Rename ========== */\n\n function setWeaponRenamePrice(uint256 newPrice) external isAdmin {\n require(newPrice > 0, 'invalid price');\n itemFlatPrices[ITEM_WEAPON_RENAME] = newPrice;\n }\n\n function weaponRenamePrice() public view returns (uint256){\n return itemFlatPrices[ITEM_WEAPON_RENAME];\n }\n\n function purchaseWeaponRenameTag(uint256 paying) public {\n require(paying == itemFlatPrices[ITEM_WEAPON_RENAME], 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_WEAPON_RENAME], CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_WEAPON_RENAME]).giveItem(msg.sender, 1);\n }\n\n function purchaseWeaponRenameTagDeal(uint256 paying) public { // 4 for the price of 3\n require(paying == itemFlatPrices[ITEM_WEAPON_RENAME] * 3, 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_WEAPON_RENAME] * 3, CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_WEAPON_RENAME]).giveItem(msg.sender, 4);\n }\n\n /* ========== Character Trait Change ========== */\n\n function setCharacterTraitChangePrice(uint256 newPrice) external isAdmin {\n require(newPrice > 0, 'invalid price');\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE] = newPrice;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH] = newPrice;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER] = newPrice;\n itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING] = newPrice;\n }\n\n function characterTraitChangePrice() public view returns (uint256){\n return itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE];\n }\n\n function purchaseCharacterFireTraitChange(uint256 paying) public {\n require(paying == itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE], 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE], CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_CHARACTER_TRAITCHANGE_FIRE]).giveItem(msg.sender, 1);\n }\n\n function purchaseCharacterEarthTraitChange(uint256 paying) public {\n require(paying == itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH], 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH], CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_CHARACTER_TRAITCHANGE_EARTH]).giveItem(msg.sender, 1);\n }\n\n function purchaseCharacterWaterTraitChange(uint256 paying) public {\n require(paying == itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER], 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER], CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_CHARACTER_TRAITCHANGE_WATER]).giveItem(msg.sender, 1);\n }\n\n function purchaseCharacterLightningTraitChange(uint256 paying) public {\n require(paying == itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING], 'Invalid price');\n payCurrency(msg.sender, itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING], CURRENCY_SKILL);\n Consumables(itemAddresses[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING]).giveItem(msg.sender, 1);\n }\n\n\n /* ========== Weapon cosmetics ========== */\n function setWeaponCosmeticPrice(uint32 cosmetic, uint256 newPrice) external isAdmin {\n require(cosmetic > 0 && newPrice > 0, 'invalid request');\n itemSeriesFlatPrices[ITEM_COSMETIC_WEAPON][cosmetic] = newPrice;\n }\n\n function getWeaponCosmeticPrice(uint32 cosmetic) public view returns (uint256){\n return itemSeriesFlatPrices[ITEM_COSMETIC_WEAPON][cosmetic];\n }\n\n function purchaseWeaponCosmetic(uint32 cosmetic, uint256 paying) public {\n require(paying > 0 && paying == itemSeriesFlatPrices[ITEM_COSMETIC_WEAPON][cosmetic], 'Invalid price');\n payCurrency(msg.sender, itemSeriesFlatPrices[ITEM_COSMETIC_WEAPON][cosmetic], CURRENCY_SKILL);\n Cosmetics(itemAddresses[ITEM_COSMETIC_WEAPON]).giveCosmetic(msg.sender, cosmetic, 1);\n }\n\n /* ========== Character cosmetics ========== */\n function setCharacterCosmeticPrice(uint32 cosmetic, uint256 newPrice) external isAdmin {\n require(cosmetic > 0 && newPrice > 0, 'invalid request');\n itemSeriesFlatPrices[ITEM_COSMETIC_CHARACTER][cosmetic] = newPrice;\n }\n\n function getCharacterCosmeticPrice(uint32 cosmetic) public view returns (uint256){\n return itemSeriesFlatPrices[ITEM_COSMETIC_CHARACTER][cosmetic];\n }\n\n function purchaseCharacterCosmetic(uint32 cosmetic, uint256 paying) public {\n require(paying > 0 && paying == itemSeriesFlatPrices[ITEM_COSMETIC_CHARACTER][cosmetic], 'Invalid price');\n payCurrency(msg.sender, itemSeriesFlatPrices[ITEM_COSMETIC_CHARACTER][cosmetic], CURRENCY_SKILL);\n Cosmetics(itemAddresses[ITEM_COSMETIC_CHARACTER]).giveCosmetic(msg.sender, cosmetic, 1);\n }\n\n /* ========== CBK Land sale ========== */\n\n event CBKLandPurchased(address indexed owner, uint256 tier, uint256 price, uint256 currency);\n\n function purchaseT1CBKLand(uint256 paying, uint256 currency) public {\n uint256 price = getCBKLandPrice(cbkLandSale.TIER_ONE(), currency);\n require(paying > 0 && price == paying, 'Invalid price');\n payCurrency(msg.sender, price, currency);\n cbkLandSale.giveT1Land(msg.sender);\n emit CBKLandPurchased(msg.sender, cbkLandSale.TIER_ONE(), price, currency);\n }\n\n function purchaseT2CBKLand(uint256 paying, uint256 chunkId, uint256 currency) public {\n uint256 price = getCBKLandPrice(cbkLandSale.TIER_TWO(), currency);\n require(paying > 0 && price == paying, 'Invalid price');\n payCurrency(msg.sender, price, currency);\n cbkLandSale.giveT2Land(msg.sender, chunkId);\n emit CBKLandPurchased(msg.sender, cbkLandSale.TIER_TWO(), price, currency);\n }\n\n function purchaseT3CBKLand(uint256 paying, uint256 chunkId, uint256 currency) public {\n uint256 price = getCBKLandPrice(cbkLandSale.TIER_THREE(), currency);\n require(paying > 0 && price == paying, 'Invalid price');\n payCurrency(msg.sender, price, currency);\n cbkLandSale.giveT3Land(msg.sender, chunkId);\n emit CBKLandPurchased(msg.sender, cbkLandSale.TIER_THREE(), price, currency);\n }\n\n function getCBKLandPrice(uint256 tier, uint256 currency) public view returns (uint256){\n return landPrices[currency][tier] * getOracledTokenPerUSD(currency);\n }\n\n function getOracledTokenPerUSD(uint256 currency) public view returns (uint256) {\n if(currency == CURRENCY_SKILL) {\n return IPriceOracle(links[LINK_SKILL_ORACLE_2]).currentPrice();\n }\n else {\n return IPriceOracle(links[LINK_KING_ORACLE]).currentPrice();\n }\n }\n\n function setCBKLandPrice(uint256 tier, uint256 newPrice, uint256 currency) external isAdmin {\n require(newPrice > 0, 'invalid price');\n require(tier >= cbkLandSale.TIER_ONE() && tier <= cbkLandSale.TIER_THREE(), \"Invalid tier\");\n landPrices[currency][tier] = newPrice;\n }\n\n function payCurrency(address payer, uint256 paying, uint256 currency) internal {\n if(currency == CURRENCY_SKILL){\n game.payContractConvertedSupportingStaked(payer, paying);\n }\n else {\n IERC20(currencies[currency]).transferFrom(payer, address(this), paying);\n }\n }\n}\n"
},
"contracts/BurningManager.sol": {
"content": "pragma solidity ^0.6.2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"abdk-libraries-solidity/ABDKMath64x64.sol\";\nimport \"./Promos.sol\";\nimport \"./util.sol\";\nimport \"./Garrison.sol\";\nimport \"./cryptoblades.sol\";\nimport \"./EquipmentManager.sol\";\n\ncontract BurningManager is Initializable, AccessControlUpgradeable {\n using SafeMath for uint256;\n using ABDKMath64x64 for int128;\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n bytes32 public constant BURNER_ROLE = keccak256(\"BURNER_ROLE\");\n\n // STATE\n Characters public characters;\n CryptoBlades public game;\n Garrison public garrison;\n\n mapping(address => mapping(uint256 => uint256)) public userVars;\n uint256 public constant USERVAR_SOUL_SUPPLY = 1;\n uint256 public constant USERVAR_NON_GENESIS_SOUL_SUPPLY = 2;\n mapping(uint256 => uint256) public vars;\n uint256 public constant VAR_ROI_DAYS = 1;\n uint256 public constant VAR_BURN_POWER_MULTIPLIER = 2;\n\n int128 public burnWeaponFee;\n int128 public reforgeWeaponWithDustFee;\n int128 public reforgeWeaponFee;\n int128 public constant PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT = 14757395258967641292; // 0.8 in fixed-point 64x64 format\n\n\n Weapons public weapons;\n IPriceOracle public priceOracleSkillPerUsd;\n\n mapping(uint256 => address) public links;\n uint256 public constant LINK_EQUIPMENT_MANAGER = 1;\n\n function initialize(Characters _characters, Garrison _garrison, CryptoBlades _game)\n public\n initializer\n {\n __AccessControl_init();\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _setupRole(GAME_ADMIN, msg.sender);\n\n characters = _characters;\n garrison = _garrison;\n game = _game;\n }\n\n function migrateTo_e1fe97c(Weapons _weapons, IPriceOracle _priceOracleSkillPerUsd) external {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));\n weapons = _weapons;\n priceOracleSkillPerUsd = _priceOracleSkillPerUsd;\n burnWeaponFee = ABDKMath64x64.divu(2, 10); // 0.2 USD\n reforgeWeaponWithDustFee = ABDKMath64x64.divu(3, 10); // 0.3 USD\n reforgeWeaponFee = burnWeaponFee + reforgeWeaponWithDustFee; // 0.5 USD\n }\n\n // MODIFIERS\n\n modifier isCharactersOwner(uint256[] memory burnIds) {\n _isCharactersOwner(burnIds);\n _;\n }\n\n function _isCharactersOwner(uint256[] memory burnIds) internal view {\n for(uint i = 0; i < burnIds.length; i++) {\n require(characters.ownerOf(burnIds[i]) == msg.sender || garrison.characterOwner(burnIds[i]) == msg.sender, 'Not owner');\n }\n }\n\n modifier isWeaponOwner(uint256 weapon) {\n _isWeaponOwner(weapon);\n _;\n }\n\n function _isWeaponOwner(uint256 weapon) internal view {\n require(weapons.ownerOf(weapon) == msg.sender);\n }\n\n modifier isWeaponsOwner(uint256[] memory weaponArray) {\n _isWeaponsOwner(weaponArray);\n _;\n }\n\n function _isWeaponsOwner(uint256[] memory weaponArray) internal view {\n for(uint i = 0; i < weaponArray.length; i++) {\n require(weapons.ownerOf(weaponArray[i]) == msg.sender);\n }\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender), \"NGA\");\n }\n\n modifier burningEnabled() {\n _burningEnabled();\n _;\n }\n\n function _burningEnabled() internal view {\n require(vars[VAR_BURN_POWER_MULTIPLIER] > 0, \"Burning disabled\");\n }\n\n // VIEWS\n\n function burnCharactersFee(uint256[] memory burnIds) public view returns (uint256) {\n uint256 burnFee = 0;\n for(uint i = 0; i < burnIds.length; i++) {\n burnFee += burnCharacterFee(burnIds[i]);\n }\n return burnFee;\n }\n\n function burnCharacterFee(uint256 burnId) public view returns (uint256) {\n 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];\n }\n\n function usdToSkill(int128 usdAmount) public view returns (uint256) {\n return usdAmount.mulu(priceOracleSkillPerUsd.currentPrice());\n }\n\n //FUNCTIONS\n\n // Characters burning\n function burnCharacterFromMarket(uint256 burnId) external burningEnabled {\n require(hasRole(BURNER_ROLE, msg.sender), 'Not burner');\n require(EquipmentManager(links[LINK_EQUIPMENT_MANAGER])\n .hasNothingEquipped(address(characters), burnId), 'Has equips');\n game.payContractTokenOnly(tx.origin, burnCharacterFee(burnId));\n uint256[] memory burnIds = new uint256[](1);\n burnIds[0] = burnId;\n (uint256 genesisSoul, uint256 nonGenesisSoul) = characters.getSoulForBurns(burnIds);\n userVars[tx.origin][USERVAR_SOUL_SUPPLY] += genesisSoul.mul(vars[VAR_BURN_POWER_MULTIPLIER]).div(1e18);\n userVars[tx.origin][USERVAR_NON_GENESIS_SOUL_SUPPLY] += nonGenesisSoul.mul(vars[VAR_BURN_POWER_MULTIPLIER]).div(1e18);\n characters.burnIntoSoul(burnIds);\n }\n\n function burnCharactersIntoCharacter(uint256[] memory burnIds, uint256 targetId) public isCharactersOwner(burnIds) burningEnabled {\n require(EquipmentManager(links[LINK_EQUIPMENT_MANAGER])\n .haveNothingEquipped(address(characters), burnIds), 'Have equips');\n game.payContractTokenOnly(msg.sender, burnCharactersFee(burnIds));\n characters.burnIntoCharacter(burnIds, targetId, vars[VAR_BURN_POWER_MULTIPLIER]);\n }\n\n function burnCharactersIntoSoul(uint256[] memory burnIds) public isCharactersOwner(burnIds) burningEnabled {\n require(EquipmentManager(links[LINK_EQUIPMENT_MANAGER])\n .haveNothingEquipped(address(characters), burnIds), 'Have equips');\n game.payContractTokenOnly(msg.sender, burnCharactersFee(burnIds));\n (uint256 genesisSoul, uint256 nonGenesisSoul) = characters.getSoulForBurns(burnIds);\n userVars[msg.sender][USERVAR_SOUL_SUPPLY] += genesisSoul.mul(vars[VAR_BURN_POWER_MULTIPLIER]).div(1e18);\n userVars[msg.sender][USERVAR_NON_GENESIS_SOUL_SUPPLY] += nonGenesisSoul.mul(vars[VAR_BURN_POWER_MULTIPLIER]).div(1e18);\n characters.burnIntoSoul(burnIds);\n }\n\n function transferSoul(address targetAddress, uint256 soulAmount) public {\n userVars[msg.sender][USERVAR_SOUL_SUPPLY] = userVars[msg.sender][USERVAR_SOUL_SUPPLY].sub(soulAmount);\n userVars[targetAddress][USERVAR_SOUL_SUPPLY] = userVars[targetAddress][USERVAR_SOUL_SUPPLY].add(soulAmount);\n }\n\n function transferNonGenesisSoul(address targetAddress, uint256 soulAmount) public {\n userVars[msg.sender][USERVAR_NON_GENESIS_SOUL_SUPPLY] = userVars[msg.sender][USERVAR_NON_GENESIS_SOUL_SUPPLY].sub(soulAmount);\n userVars[targetAddress][USERVAR_NON_GENESIS_SOUL_SUPPLY] = userVars[targetAddress][USERVAR_NON_GENESIS_SOUL_SUPPLY].add(soulAmount);\n }\n\n function upgradeCharacterWithSoul(uint256 targetId, uint256 soulAmount) public burningEnabled {\n userVars[msg.sender][USERVAR_SOUL_SUPPLY] = userVars[msg.sender][USERVAR_SOUL_SUPPLY].sub(soulAmount);\n characters.upgradeWithSoul(targetId, soulAmount, true);\n }\n\n function upgradeNonGenesisCharacterWithSoul(uint256 targetId, uint256 soulAmount) public burningEnabled {\n userVars[msg.sender][USERVAR_NON_GENESIS_SOUL_SUPPLY] = userVars[msg.sender][USERVAR_NON_GENESIS_SOUL_SUPPLY].sub(soulAmount);\n characters.upgradeWithSoul(targetId, soulAmount, false);\n }\n\n // Weapons burning\n\n function burnWeapons(uint256[] calldata burnIDs) external isWeaponsOwner(burnIDs) {\n game.payContractConvertedSupportingStaked(msg.sender, game.usdToSkill(burnWeaponFee.mul(ABDKMath64x64.fromUInt(burnIDs.length))));\n\n _burnWeaponsLogic(burnIDs);\n }\n\n function reforgeWeapon(uint256 reforgeID, uint256 burnID) external isWeaponOwner(reforgeID) isWeaponOwner(burnID) {\n game.payContractConvertedSupportingStaked(msg.sender, usdToSkill(burnWeaponFee));\n\n _reforgeWeaponLogic(reforgeID, burnID);\n }\n\n function reforgeWeaponWithDust(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) external isWeaponOwner(reforgeID) {\n game.payContractConvertedSupportingStaked(msg.sender, usdToSkill(reforgeWeaponWithDustFee));\n\n _reforgeWeaponWithDustLogic(reforgeID, amountLB, amount4B, amount5B);\n }\n\n function burnWeaponsUsingStakedSkill(uint256[] calldata burnIDs) external isWeaponsOwner(burnIDs) {\n int128 discountedBurnWeaponFee =\n burnWeaponFee\n .mul(ABDKMath64x64.fromUInt(burnIDs.length))\n .mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT);\n game.payContractStakedOnly(msg.sender, usdToSkill(discountedBurnWeaponFee));\n\n _burnWeaponsLogic(burnIDs);\n }\n\n function reforgeWeaponUsingStakedSkill(uint256 reforgeID, uint256 burnID) external isWeaponOwner(reforgeID) isWeaponOwner(burnID) {\n int128 discountedReforgeWeaponFee =\n reforgeWeaponFee\n .mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT);\n game.payContractStakedOnly(msg.sender, usdToSkill(discountedReforgeWeaponFee));\n\n _reforgeWeaponLogic(reforgeID, burnID);\n }\n\n function reforgeWeaponWithDustUsingStakedSkill(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) external isWeaponOwner(reforgeID) {\n int128 discountedReforgeWeaponWithDustFee =\n reforgeWeaponWithDustFee\n .mul(PAYMENT_USING_STAKED_SKILL_COST_AFTER_DISCOUNT);\n game.payContractStakedOnly(msg.sender, usdToSkill(discountedReforgeWeaponWithDustFee));\n\n _reforgeWeaponWithDustLogic(reforgeID, amountLB, amount4B, amount5B);\n }\n\n function _burnWeaponLogic(uint256 burnID) internal {\n weapons.burn(burnID);\n }\n\n function _burnWeaponsLogic(uint256[] memory burnIDs) internal {\n for(uint i = 0; i < burnIDs.length; i++) {\n weapons.burn(burnIDs[i]);\n }\n }\n\n function _reforgeWeaponLogic(uint256 reforgeID, uint256 burnID) internal {\n weapons.reforge(reforgeID, burnID);\n }\n\n function _reforgeWeaponWithDustLogic(uint256 reforgeID, uint8 amountLB, uint8 amount4B, uint8 amount5B) internal {\n weapons.reforgeWithDust(reforgeID, amountLB, amount4B, amount5B);\n }\n\n function giveAwaySoul(address user, uint256 soulAmount) external restricted {\n userVars[user][USERVAR_SOUL_SUPPLY] += soulAmount;\n }\n\n function burnSoul(address user, uint256 soulAmount) external restricted {\n userVars[user][USERVAR_SOUL_SUPPLY] = userVars[user][USERVAR_SOUL_SUPPLY].sub(soulAmount);\n }\n\n // VARS SETTER\n\n function setVar(uint256 varField, uint256 value) external restricted {\n vars[varField] = value;\n }\n\n function setLink(uint256 linkId, address linkAddress) external restricted {\n links[linkId] = linkAddress;\n }\n\n function setBurnWeaponValue(uint256 cents) public restricted {\n burnWeaponFee = ABDKMath64x64.divu(cents, 100);\n }\n\n function setReforgeWeaponValue(uint256 cents) public restricted {\n int128 newReforgeWeaponFee = ABDKMath64x64.divu(cents, 100);\n require(newReforgeWeaponFee > burnWeaponFee);\n reforgeWeaponWithDustFee = newReforgeWeaponFee - burnWeaponFee;\n reforgeWeaponFee = newReforgeWeaponFee;\n }\n\n function setReforgeWeaponWithDustValue(uint256 cents) public restricted {\n reforgeWeaponWithDustFee = ABDKMath64x64.divu(cents, 100);\n reforgeWeaponFee = burnWeaponFee + reforgeWeaponWithDustFee;\n }\n \n}\n"
},
"contracts/CBKLand.sol": {
"content": "pragma solidity ^0.6.0;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\nimport \"./util.sol\";\n\ncontract CBKLand is Initializable, ERC721Upgradeable, AccessControlUpgradeable {\n\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n bytes32 public constant NO_OWNED_LIMIT = keccak256(\"NO_OWNED_LIMIT\");\n\n // Land specific\n uint256 public constant LT = 0; // Land Tier\n uint256 public constant LC = 1; // Land Chunk Id\n uint256 public constant LX = 2; // Land Coordinate X\n uint256 public constant LY = 3; // Land Coordinate Y\n \n\n event LandMinted(address indexed minter, uint256 id, uint256 tier, uint256 chunkId);\n event LandTransfered(address indexed from, address indexed to, uint256 id);\n event LandTokenMinted(address indexed reseller, address indexed minter, uint256 id, uint256 tier);\n event LandMintedWithReseller(address indexed minter, uint256 id, uint256 tier, uint256 chunkId, address reseller);\n event LandChunkIdUpdated(uint256 indexed id, uint256 chunkId);\n\n // TotalLand\n uint256 landMinted;\n // Avoiding structs for stats\n mapping(uint256 => mapping(uint256 => uint256)) landData;\n\n mapping(uint256 => mapping(uint256 => string)) landStrData;\n\n uint256 public constant LBT = 0; // Land is a Token, it will have its chunkId updated later\n mapping(uint256 => mapping(uint256 => bool)) landBoolData;\n\n uint256 public constant LAR = 0; // Land Reseller, the one who minted the token\n mapping(uint256 => mapping(uint256 => address)) landAddressData;\n\n uint256 public constant TSU = 0; // URI of a tier. Will put this in land NFT because it kinda belongs here\n mapping(uint256 => mapping(uint256 => string)) tierStrData;\n\n function initialize () public initializer {\n __ERC721_init(\"CryptoBladesKingdoms Land\", \"CBKL\");\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n }\n\n modifier restricted() {\n _restricted();\n _;\n }\n\n function _restricted() internal view {\n require(hasRole(GAME_ADMIN, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"NA\");\n }\n\n // tier, chunkid, x, y, reseller address\n function get(uint256 id) public view returns (uint256, uint256, uint256, uint256, address) {\n return (landData[id][LT], landData[id][LC], landData[id][LX], landData[id][LY], landAddressData[id][LAR]);\n }\n\n function getOwned(address owner) public view returns (uint256[] memory ownedIds) {\n uint256 ownedLandCount = balanceOf(owner);\n ownedIds = new uint256[](ownedLandCount);\n for(uint256 i = 0; i < ownedLandCount; i++) {\n ownedIds[i] = tokenOfOwnerByIndex(owner, i);\n }\n }\n\n function getLandReseller(uint256 land) public view returns (address) {\n return landAddressData[land][LAR];\n }\n\n // DO NOT call directly outside the logic of CBKLandSale to avoid breaking tier and chunk logic\n function mint(address minter, uint256 tier, uint256 chunkId) public restricted {\n uint256 tokenID = landMinted++;\n \n landData[tokenID][LT] = tier;\n landData[tokenID][LC] = chunkId;\n //landData[tokenID][LX] = x; // not yet\n //landData[tokenID][LY] = y; // not yet\n \n _mint(minter, tokenID);\n emit LandMinted(minter, tokenID, tier, chunkId);\n }\n\n function mint(address minter, uint256 tier, uint256 chunkId, address reseller) public restricted {\n uint256 tokenID = landMinted++;\n \n landData[tokenID][LT] = tier;\n landData[tokenID][LC] = chunkId;\n //landData[tokenID][LX] = x; // not yet\n //landData[tokenID][LY] = y; // not yet\n \n landAddressData[tokenID][LAR] = reseller;\n\n _mint(minter, tokenID);\n emit LandMintedWithReseller(minter, tokenID, tier, chunkId, reseller);\n }\n\n // To be used by the NFT bridge. This is fine as the logic will still be maintend by CBKLandSale deployed to BSC.\n // ChunkId set on global T1s or reseller T1s will include BE server which has access to BSC CBKLandSale\n function mintOrUpdate(uint256 tokenID, address minter, uint256 tier, uint256 chunkId, uint256 x, uint256 y, address reseller) public restricted returns (uint256) {\n \n if(tokenID == 0) {\n tokenID = landMinted++;\n _mint(minter, tokenID);\n landData[tokenID][LT] = tier;\n landAddressData[tokenID][LAR] = reseller;\n }\n \n // Unlike vars above, these may change at some point (or multiple) \n landData[tokenID][LX] = x;\n landData[tokenID][LY] = y;\n landData[tokenID][LC] = chunkId;\n\n return tokenID;\n }\n\n function massMint(address minter, uint256 tier, uint256 chunkId, address reseller, uint256 quantity) public restricted {\n for(uint256 i = 0; i < quantity; i++) {\n mint(minter, tier, chunkId, reseller);\n }\n }\n\n function updateChunkId(uint256 id, uint256 chunkId) public restricted {\n landData[id][LC] = chunkId;\n emit LandChunkIdUpdated(id, chunkId);\n }\n\n function updateChunkId(uint256[] memory ids, uint256 chunkId) public restricted {\n for(uint256 i = 0; i < ids.length; i++) {\n updateChunkId(ids[i], chunkId);\n }\n }\n\n // Helper function for bulk moving land without having to jump chains\n function landsBelongToChunk(uint256[] memory ids, uint256 chunkId) public view returns (bool) {\n for(uint256 i = 0; i < ids.length; i++) {\n if(landData[ids[i]][LC] != chunkId) {\n return false;\n }\n\n if(ids[i] > landMinted) {\n return false;\n }\n }\n\n return true;\n }\n\n function getLandTierURI(uint256 id) public view returns (string memory uri) {\n (uint256 tier,,,,) = get(id);\n return getTierURI(tier);\n }\n\n function tokenURI(uint256 id) public view override returns (string memory) {\n return getLandTierURI(id);\n }\n\n function getTierURI(uint256 tier) public view returns (string memory uri) {\n return tierStrData[tier][TSU];\n }\n\n function setTierStr(uint256 tier, uint256 index, string memory val) public restricted {\n tierStrData[tier][index] = val;\n }\n\n function getLandTier(uint256 id) public view returns (uint256) {\n return landData[id][LT];\n }\n}\n"
},
"contracts/CBKLandSale.sol": {
"content": "pragma solidity ^0.6.5;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport \"@openzeppelin/contracts/utils/EnumerableSet.sol\";\nimport \"./CBKLand.sol\";\n\ncontract CBKLandSale is Initializable, AccessControlUpgradeable {\n\n using EnumerableSet for EnumerableSet.UintSet;\n bytes32 public constant GAME_ADMIN = keccak256(\"GAME_ADMIN\");\n\n CBKLand public cbkLand;\n /* ========== EVENTS ========== */\n event T1Given(address indexed owner, uint256 stamp);\n event T2Given(address indexed owner, uint256 chunkId);\n event T3Given(address indexed owner, uint256 chunkId);\n event T1GivenFree(address indexed owner, uint256 stamp);\n event T2GivenFree(address indexed owner, uint256 chunkId);\n event T3GivenFree(address indexed owner, uint256 chunkId);\n event T1GivenReserved(address indexed reseller, address indexed owner, uint256 chunkId);\n event T2GivenReserved(address indexed reseller, address indexed owner, uint256 chunkId);\n event T3GivenReserved(address indexed reseller, address indexed owner, uint256 chunkId);\n event LandTokenGiven(address indexed reseller, address indexed owner, uint256 tier);\n\n event ReservedLandClaimed(uint256 indexed reservation, address indexed reseller, address indexed owner, uint256 tier, uint256 chunkId);\n event MassMintReservedLand(address indexed player, address indexed reseller, uint256 chunkId, uint256 tier1, uint256 tier2, bool giveTier3);\n event MassMintReservedChunklessLand(address indexed player, address indexed reseller, uint256 tier1, uint256 tier2, uint256 tier3);\n event ReservedLandClaimedForPlayer(uint256 indexed reservation, address indexed reseller, address indexed owner, uint256 tier, uint256 chunkId);\n \n /* ========== LAND SALE INFO ========== */\n uint256 private constant NO_LAND = 0;\n uint256 public constant TIER_ONE = 1;\n uint256 public constant TIER_TWO = 2;\n uint256 public constant TIER_THREE = 3;\n uint256 private constant MAX_CHUNK_ID = 9999; // 100 x 100\n\n struct purchaseInfo {\n address buyer;\n uint256 purchasedTier;\n uint256 stamp; // chunkId or roundrobin stamp\n bool free;\n }\n\n uint256 private totalSales;\n mapping(uint256 => purchaseInfo) public sales; // Put all sales in an mapping for easier tracking\n mapping(address => purchaseInfo) public purchaseAddressMapping;\n mapping(uint256 => uint256) public availableLand; // Land that is up for sale. \n mapping(uint256 => uint256) public chunkZoneLandSales;\n\n /* ========== T1 LAND SALE INFO ========== */\n // T1 land is sold with no exact coordinates commitment and assigned based on round robin\n // once minting is done. For now the player gets a stamp which can reflect PROJECTED land coordinates\n // should it need be.\n uint256 private t1LandsSold;\n\n\n\n /* ========== T2 LAND SALE INFO ========== */\n uint256 private t2LandsSold;\n uint256 private chunksWithT2Land;\n\n uint256 private _allowedLandSalePerChunk;\n uint256 private _allowedLandOffset; // Max allowed deviation allowed from theoretical average\n\n // T2 sold per chunk\n mapping(uint256 => uint256) public chunkT2LandSales;\n\n\n /* ========== T3 LAND SALE INFO ========== */\n uint256 private t3LandsSold;\n mapping(uint256 => address) public chunkT3LandSoldTo;\n\n\n /* ========== RESERVED CHUNKS SALE INFO ========== */\n EnumerableSet.UintSet private reservedChunkIds;\n\n bool internal _enabled;\n bool internal _reservedEnabled;\n \n mapping(address => EnumerableSet.UintSet) private reservedChunks;\n mapping(address => uint256) private reservedChunksCounter;\n mapping(uint256 => address) private chunksReservedFor;\n\n // reseller address => land tier => budget\n mapping(address => mapping(uint256 => uint256)) private resellerLandBudget;\n\n // player reserved land\n uint256 private playerReservedLandAt;\n mapping(address => EnumerableSet.UintSet) private playerReservedLands;\n mapping(uint256 => uint256) private playerReservedLandTier;\n mapping(uint256 => address) private playerReservedLandReseller;\n mapping(uint256 => address) private playerReservedLandForPlayer;\n mapping(uint256 => bool) private playerReservedLandClaimed;\n \n EnumerableSet.UintSet private takenT3Chunks;\n\n function initialize(CBKLand _cbkLand)\n public\n initializer\n {\n __AccessControl_init_unchained();\n\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n _enabled = false;\n\n _allowedLandOffset = 2;\n _allowedLandSalePerChunk = 99; // At least 1 reserved for T3\n availableLand[TIER_ONE] = 1000; // Placeholder value\n availableLand[TIER_TWO] = 100; // Placeholder value\n availableLand[TIER_THREE] = 10; // Placeholder value\n\n cbkLand = _cbkLand;\n }\n\n modifier isAdmin() {\n require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), \"Not admin\");\n _;\n }\n\n modifier saleAllowed() {\n require(_enabled, \"Sales disabled\");\n _;\n }\n\n modifier reservedSaleAllowed() {\n require(_reservedEnabled, \"Sales disabled\");\n _;\n }\n\n modifier canPurchase(address buyer) {\n require(purchaseAddressMapping[buyer].purchasedTier == 0, \"Already purchased\");\n _;\n }\n\n modifier chunkAvailable(uint256 chunkId) {\n require(chunkId <= MAX_CHUNK_ID, \"Chunk not valid\");\n require(!reservedChunkIds.contains(chunkId), \"Chunk reserved\");\n require(chunkT2LandSales[chunkId] < _allowedLandSalePerChunk, \"Chunk not available\");\n require(_chunkAvailableForT2(chunkId), \"Chunk overpopulated\");\n _;\n }\n\n // modifier reservedChunkAvailable(uint256 chunkId) {\n // require(chunkI
View raw

(Sorry about that, but we can’t show files that are this big right now.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment