/*
* Crypto stamp 2
* digital-physical collectible postage stamp
*
* Developed by Capacity Blockchain Solutions GmbH <capacity.at>
* for Österreichische Post AG <post.at>
*/
// File: @openzeppelin/contracts/GSN/Context.sol
pragma solidity ^0.6.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
contract Context {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
constructor () internal { }
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
// File: @openzeppelin/contracts/introspection/IERC165.sol
pragma solidity ^0.6.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// File: @openzeppelin/contracts/token/ERC721/IERC721.sol
pragma solidity ^0.6.2;
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transfered from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from`, `to` cannot be zero.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
*
* Requirements:
*
* - `from`, `to` cannot be zero.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from`, `to` cannot be zero.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}
// File: @openzeppelin/contracts/token/ERC721/IERC721Metadata.sol
pragma solidity ^0.6.2;
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}
// File: @openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol
pragma solidity ^0.6.2;
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}
// File: @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol
pragma solidity ^0.6.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
abstract contract IERC721Receiver {
/**
* @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient
* after a {IERC721-safeTransferFrom}. This function MUST return the function selector,
* otherwise the caller will revert the transaction. The selector to be
* returned can be obtained as `this.onERC721Received.selector`. This
* function MAY throw to revert and reject the transfer.
* Note: the ERC721 contract address is always the message sender.
* @param operator The address which called `safeTransferFrom` function
* @param from The address which previously owned the token
* @param tokenId The NFT identifier which is being transferred
* @param data Additional data with no specified format
* @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
public virtual returns (bytes4);
}
// File: @openzeppelin/contracts/introspection/ERC165.sol
pragma solidity ^0.6.0;
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts may inherit from this and call {_registerInterface} to declare
* their support of an interface.
*/
contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () internal {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See {IERC165-supportsInterface}.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See {IERC165-supportsInterface}.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal virtual {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
// File: @openzeppelin/contracts/math/SafeMath.sol
pragma solidity ^0.6.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
// File: @openzeppelin/contracts/utils/Address.sol
pragma solidity ^0.6.2;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != accountHash && codehash != 0x0);
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
}
// File: @openzeppelin/contracts/utils/EnumerableSet.sol
pragma solidity ^0.6.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
* (`UintSet`) are supported.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) { // Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(value)));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(value)));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(value)));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint256(_at(set._inner, index)));
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}
// File: contracts/EnumerableRandomMapSimple.sol
pragma solidity ^0.6.0;
library EnumerableRandomMapSimple {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Map type with
// bytes32 keys and values.
// The Map implementation uses private functions, and user-facing
// implementations (such as Uint256ToAddressMap) are just wrappers around
// the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit
// in bytes32.
struct Map {
// Storage of map keys and values
mapping(bytes32 => bytes32) _entries;
uint256 _length;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
bool newVal;
if (map._entries[key] == bytes32("")) { // add new entry
map._length++;
newVal = true;
}
else if (value == bytes32("")) { // remove entry
map._length--;
newVal = false;
}
else {
newVal = false;
}
map._entries[key] = value;
return newVal;
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function _remove(Map storage /*map*/, bytes32 /*key*/) private pure returns (bool) {
revert("No removal supported");
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function _contains(Map storage map, bytes32 key) private view returns (bool) {
return map._entries[key] != bytes32("");
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function _length(Map storage map) private view returns (uint256) {
return map._length;
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
bytes32 key = bytes32(index);
require(_contains(map, key), "EnumerableMap: index out of bounds");
return (key, map._entries[key]);
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function _get(Map storage map, bytes32 key) private view returns (bytes32) {
return _get(map, key, "EnumerableMap: nonexistent key");
}
/**
* @dev Same as {_get}, with a custom error message when `key` is not in the map.
*/
function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
require(_contains(map, key), errorMessage); // Equivalent to contains(map, key)
return map._entries[key];
}
// UintToAddressMap
struct UintToAddressMap {
Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return _set(map._inner, bytes32(key), bytes32(uint256(value)));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return _remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return _contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return _length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the set. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = _at(map._inner, index);
return (uint256(key), address(uint256(value)));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint256(_get(map._inner, bytes32(key))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*/
function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {
return address(uint256(_get(map._inner, bytes32(key), errorMessage)));
}
}
// File: @openzeppelin/contracts/utils/Strings.sol
pragma solidity ^0.6.0;
/**
* @dev String operations.
*/
library Strings {
/**
* @dev Converts a `uint256` to its ASCII `string` representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
uint256 index = digits - 1;
temp = value;
while (temp != 0) {
buffer[index--] = byte(uint8(48 + temp % 10));
temp /= 10;
}
return string(buffer);
}
}
// File: contracts/OZ_Clone/ERC721_simplemaps.sol
pragma solidity ^0.6.0;
// Clone of OpenZeppelin 3.0.0 token/ERC721/ERC721.sol with just imports adapted and EnumerableMap exchanged for EnumerableMapSimple,
// and totalSupply() marked as virtual.
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {
using SafeMath for uint256;
using Address for address;
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableRandomMapSimple for EnumerableRandomMapSimple.UintToAddressMap;
using Strings for uint256;
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
// which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
// Mapping from holder address to their (enumerable) set of owned tokens
mapping (address => EnumerableSet.UintSet) private _holderTokens;
// Enumerable mapping from token ids to their owners
EnumerableRandomMapSimple.UintToAddressMap private _tokenOwners;
// Mapping from token ID to approved address
mapping (uint256 => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) private _operatorApprovals;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Optional mapping for token URIs
mapping(uint256 => string) private _tokenURIs;
// Base URI
string private _baseURI;
/*
* bytes4(keccak256('balanceOf(address)')) == 0x70a08231
* bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
* bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
* bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
* bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
* bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
* bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
*
* => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
* 0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
*/
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
/*
* bytes4(keccak256('name()')) == 0x06fdde03
* bytes4(keccak256('symbol()')) == 0x95d89b41
* bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
*
* => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
*/
bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
/*
* bytes4(keccak256('totalSupply()')) == 0x18160ddd
* bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
* bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
*
* => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
*/
bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
constructor (string memory name, string memory symbol) public {
_name = name;
_symbol = symbol;
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721);
_registerInterface(_INTERFACE_ID_ERC721_METADATA);
_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
}
/**
* @dev Gets the balance of the specified address.
* @param owner address to query the balance of
* @return uint256 representing the amount owned by the passed address
*/
function balanceOf(address owner) public view override returns (uint256) {
require(owner != address(0), "ERC721: balance query for the zero address");
return _holderTokens[owner].length();
}
/**
* @dev Gets the owner of the specified token ID.
* @param tokenId uint256 ID of the token to query the owner of
* @return address currently marked as the owner of the given token ID
*/
function ownerOf(uint256 tokenId) public view override returns (address) {
return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
}
/**
* @dev Gets the token name.
* @return string representing the token name
*/
function name() public view override returns (string memory) {
return _name;
}
/**
* @dev Gets the token symbol.
* @return string representing the token symbol
*/
function symbol() public view override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the URI for a given token ID. May return an empty string.
*
* If a base URI is set (via {_setBaseURI}), it is added as a prefix to the
* token's own URI (via {_setTokenURI}).
*
* If there is a base URI but no token URI, the token's ID will be used as
* its URI when appending it to the base URI. This pattern for autogenerated
* token URIs can lead to large gas savings.
*
* .Examples
* |===
* |`_setBaseURI()` |`_setTokenURI()` |`tokenURI()`
* | ""
* | ""
* | ""
* | ""
* | "token.uri/123"
* | "token.uri/123"
* | "token.uri/"
* | "123"
* | "token.uri/123"
* | "token.uri/"
* | ""
* | "token.uri/<tokenId>"
* |===
*
* Requirements:
*
* - `tokenId` must exist.
*/
function tokenURI(uint256 tokenId) public view override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
// If there is no base URI, return the token URI.
if (bytes(_baseURI).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(_baseURI, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(_baseURI, tokenId.toString()));
}
/**
* @dev Returns the base URI set via {_setBaseURI}. This will be
* automatically added as a prefix in {tokenURI} to each token's URI, or
* to the token ID if no specific URI is set for that token ID.
*/
function baseURI() public view returns (string memory) {
return _baseURI;
}
/**
* @dev Gets the token ID at a given index of the tokens list of the requested owner.
* @param owner address owning the tokens list to be accessed
* @param index uint256 representing the index to be accessed of the requested tokens list
* @return uint256 token ID at the given index of the tokens list owned by the requested address
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view override returns (uint256) {
return _holderTokens[owner].at(index);
}
/**
* @dev Gets the total amount of tokens stored by the contract.
* @return uint256 representing the total amount of tokens
*/
function totalSupply() public view override virtual returns (uint256) {
// _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
return _tokenOwners.length();
}
/**
* @dev Gets the token ID at a given index of all the tokens in this contract
* Reverts if the index is greater or equal to the total number of tokens.
* @param index uint256 representing the index to be accessed of the tokens list
* @return uint256 token ID at the given index of the tokens list
*/
function tokenByIndex(uint256 index) public view override returns (uint256) {
(uint256 tokenId, ) = _tokenOwners.at(index);
return tokenId;
}
/**
* @dev Approves another address to transfer the given token ID
* The zero address indicates there is no approved address.
* There can only be one approved address per token at a given time.
* Can only be called by the token owner or an approved operator.
* @param to address to be approved for the given token ID
* @param tokenId uint256 ID of the token to be approved
*/
function approve(address to, uint256 tokenId) public virtual override {
address owner = ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not owner nor approved for all"
);
_approve(to, tokenId);
}
/**
* @dev Gets the approved address for a token ID, or zero if no address set
* Reverts if the token ID does not exist.
* @param tokenId uint256 ID of the token to query the approval of
* @return address currently approved for the given token ID
*/
function getApproved(uint256 tokenId) public view override returns (address) {
require(_exists(tokenId), "ERC721: approved query for nonexistent token");
return _tokenApprovals[tokenId];
}
/**
* @dev Sets or unsets the approval of a given operator
* An operator is allowed to transfer all tokens of the sender on their behalf.
* @param operator operator address to set the approval
* @param approved representing the status of the approval to be set
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
require(operator != _msgSender(), "ERC721: approve to caller");
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev Tells whether an operator is approved by a given owner.
* @param owner owner address which you want to query the approval of
* @param operator operator address which you want to query the approval of
* @return bool whether the given operator is approved by the given owner
*/
function isApprovedForAll(address owner, address operator) public view override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev Transfers the ownership of a given token ID to another address.
* Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
* Requires the msg.sender to be the owner, approved, or operator.
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual override {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_transfer(from, to, tokenId);
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement {IERC721Receiver-onERC721Received},
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* Requires the msg.sender to be the owner, approved, or operator
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement {IERC721Receiver-onERC721Received},
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* Requires the _msgSender() to be the owner, approved, or operator
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes data to send along with a safe transfer check
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_safeTransfer(from, to, tokenId, _data);
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* Requires the msg.sender to be the owner, approved, or operator
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes data to send along with a safe transfer check
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Returns whether the specified token exists.
* @param tokenId uint256 ID of the token to query the existence of
* @return bool whether the token exists
*/
function _exists(uint256 tokenId) internal view returns (bool) {
return _tokenOwners.contains(tokenId);
}
/**
* @dev Returns whether the given spender can transfer a given token ID.
* @param spender address of the spender to query
* @param tokenId uint256 ID of the token to be transferred
* @return bool whether the msg.sender is approved for the given token ID,
* is an operator of the owner, or is the owner of the token
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
require(_exists(tokenId), "ERC721: operator query for nonexistent token");
address owner = ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
}
/**
* @dev Internal function to safely mint a new token.
* Reverts if the given token ID already exists.
* If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* @param to The address that will own the minted token
* @param tokenId uint256 ID of the token to be minted
*/
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
/**
* @dev Internal function to safely mint a new token.
* Reverts if the given token ID already exists.
* If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* @param to The address that will own the minted token
* @param tokenId uint256 ID of the token to be minted
* @param _data bytes data to send along with a safe transfer check
*/
function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
_mint(to, tokenId);
require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Internal function to mint a new token.
* Reverts if the given token ID already exists.
* @param to The address that will own the minted token
* @param tokenId uint256 ID of the token to be minted
*/
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
emit Transfer(address(0), to, tokenId);
}
/**
* @dev Internal function to burn a specific token.
* Reverts if the token does not exist.
* @param tokenId uint256 ID of the token being burned
*/
function _burn(uint256 tokenId) internal virtual {
address owner = ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId);
// Clear approvals
_approve(address(0), tokenId);
// Clear metadata (if any)
if (bytes(_tokenURIs[tokenId]).length != 0) {
delete _tokenURIs[tokenId];
}
_holderTokens[owner].remove(tokenId);
_tokenOwners.remove(tokenId);
emit Transfer(owner, address(0), tokenId);
}
/**
* @dev Internal function to transfer ownership of a given token ID to another address.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function _transfer(address from, address to, uint256 tokenId) internal virtual {
require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
// Clear approvals from the previous owner
_approve(address(0), tokenId);
_holderTokens[from].remove(tokenId);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
emit Transfer(from, to, tokenId);
}
/**
* @dev Internal function to set the token URI for a given token.
*
* Reverts if the token ID does not exist.
*
* TIP: If all token IDs share a prefix (for example, if your URIs look like
* `https://api.myproject.com/token/<id>`), use {_setBaseURI} to store
* it and save gas.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
/**
* @dev Internal function to set the base URI for all token IDs. It is
* automatically added as a prefix to the value returned in {tokenURI},
* or to the token ID if {tokenURI} is empty.
*/
function _setBaseURI(string memory baseURI_) internal virtual {
_baseURI = baseURI_;
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
* The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
private returns (bool)
{
if (!to.isContract()) {
return true;
}
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = to.call(abi.encodeWithSelector(
IERC721Receiver(to).onERC721Received.selector,
_msgSender(),
from,
tokenId,
_data
));
if (!success) {
if (returndata.length > 0) {
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert("ERC721: transfer to non ERC721Receiver implementer");
}
} else {
bytes4 retval = abi.decode(returndata, (bytes4));
return (retval == _ERC721_RECEIVED);
}
}
function _approve(address to, uint256 tokenId) private {
_tokenApprovals[tokenId] = to;
emit Approval(ownerOf(tokenId), to, tokenId);
}
/**
* @dev Hook that is called before any token transfer. This includes minting
* and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, ``from``'s `tokenId` will be
* transferred to `to`.
* - when `from` is zero, `tokenId` will be minted for `to`.
* - when `to` is zero, ``from``'s `tokenId` will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
}
// File: contracts/ERC721SimpleMapsURI.sol
pragma solidity ^0.6.0;
/**
* @title ERC721 With a nicer simple token URI
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721SimpleMapsURI is ERC721 {
// Similar to ERC1155 URI() event, but without a token ID.
event BaseURI(string value);
constructor (string memory name, string memory symbol, string memory baseURI)
ERC721(name, symbol)
public
{
_setBaseURI(baseURI);
}
/**
* @dev Internal function to set the base URI for all token IDs. It is
* automatically added as a prefix to the value returned in {tokenURI}.
*/
function _setBaseURI(string memory baseURI_) internal override virtual {
super._setBaseURI(baseURI_);
emit BaseURI(baseURI());
}
}
// File: contracts/CS1ColorsI.sol
/*
Color store interface for Crypto stamp 1
*/
pragma solidity ^0.6.0;
interface CS1ColorsI {
enum Colors {
Black,
Green,
Blue,
Yellow,
Red
}
// Returns the color of a given token ID
function getColor(uint256 tokenId) external view returns (Colors);
}
// File: contracts/CS2PropertiesI.sol
/*
Interface for CS2 properties.
*/
pragma solidity ^0.6.0;
interface CS2PropertiesI {
enum AssetType {
Honeybadger,
Llama,
Panda,
Doge
}
enum Colors {
Black,
Green,
Blue,
Yellow,
Red
}
function getType(uint256 tokenId) external view returns (AssetType);
function getColor(uint256 tokenId) external view returns (Colors);
}
// File: contracts/ENSReverseRegistrarI.sol
/*
* Interfaces for ENS Reverse Registrar
* See https://github.com/ensdomains/ens/blob/master/contracts/ReverseRegistrar.sol for full impl
* Also see https://github.com/wealdtech/wealdtech-solidity/blob/master/contracts/ens/ENSReverseRegister.sol
*
* Use this as follows (registryAddress is the address of the ENS registry to use):
* -----
* // This hex value is caclulated by namehash('addr.reverse')
* bytes32 public constant ENS_ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;
* function registerReverseENS(address registryAddress, string memory calldata) external {
* require(registryAddress != address(0), "need a valid registry");
* address reverseRegistrarAddress = ENSRegistryOwnerI(registryAddress).owner(ENS_ADDR_REVERSE_NODE)
* require(reverseRegistrarAddress != address(0), "need a valid reverse registrar");
* ENSReverseRegistrarI(reverseRegistrarAddress).setName(name);
* }
* -----
* or
* -----
* function registerReverseENS(address reverseRegistrarAddress, string memory calldata) external {
* require(reverseRegistrarAddress != address(0), "need a valid reverse registrar");
* ENSReverseRegistrarI(reverseRegistrarAddress).setName(name);
* }
* -----
* ENS deployments can be found at https://docs.ens.domains/ens-deployments
* E.g. Etherscan can be used to look up that owner on those contracts.
* namehash.hash("addr.reverse") == "0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2"
* Ropsten: ens.owner(namehash.hash("addr.reverse")) == "0x6F628b68b30Dc3c17f345c9dbBb1E483c2b7aE5c"
* Mainnet: ens.owner(namehash.hash("addr.reverse")) == "0x084b1c3C81545d370f3634392De611CaaBFf8148"
*/
pragma solidity ^0.6.0;
interface ENSRegistryOwnerI {
function owner(bytes32 node) external view returns (address);
}
interface ENSReverseRegistrarI {
function setName(string calldata name) external returns (bytes32 node);
}
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol
pragma solidity ^0.6.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: contracts/AchievementsUpgradingI.sol
/*
Interface for CS2 color upgrading support by achievements.
*/
pragma solidity ^0.6.0;
interface AchievementsUpgradingI is IERC165 {
/*
* Calculate the interface ID for ERC 165:
*
* bytes4(keccak256('onContractAdded(bool)')) == 0x58cac597
*/
/**
* @notice Notify about changing a CS2 color as done by the "upgrading" mechanism
* @dev The Cryptostamp2 smart contract calls this function when changing the color of any asset,
* esp. as the result of upgrading. This function MUST return the function selector,
* otherwise the caller will revert the transaction. The selector to be
* returned can be obtained as `this.onCS2ColorChanged.selector`. This
* function MAY throw to revert and reject the transfer.
* Note: the Collection contract address is always the message sender.
* @param tokenId The token identifier which is being changed
* @param previousColor The previous color held by the token
* @param newColor The new color assigned to the token, which MUST match the current color at time of this call
* @return bytes4 `bytes4(keccak256("onCS2ColorChanged(uint256,uint8,uint8)"))`
*/
function onCS2ColorChanged(uint256 tokenId, CS2PropertiesI.Colors previousColor, CS2PropertiesI.Colors newColor)
external returns (bytes4);
}
// File: @openzeppelin/contracts/cryptography/ECDSA.sol
pragma solidity ^0.6.0;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
// Check the signature length
if (signature.length != 65) {
revert("ECDSA: invalid signature length");
}
// Divide the signature in r, s and v variables
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
revert("ECDSA: invalid signature 's' value");
}
if (v != 27 && v != 28) {
revert("ECDSA: invalid signature 'v' value");
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
require(signer != address(0), "ECDSA: invalid signature");
return signer;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* replicates the behavior of the
* https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
* JSON-RPC method.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
}
// File: @openzeppelin/contracts/cryptography/MerkleProof.sol
pragma solidity ^0.6.0;
/**
* @dev These functions deal with verification of Merkle trees (hash trees),
*/
library MerkleProof {
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
// Hash(current computed hash + current element of the proof)
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
// Hash(current element of the proof + current computed hash)
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
}
// Check if the computed hash (root) is equal to the provided root
return computedHash == root;
}
}
// File: contracts/Cryptostamp2.sol
/*
Implements ERC 721 Token standard: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
pragma solidity ^0.6.0;
/* The inheritance is very much the same as OpenZeppelin's ERC721Full,
* but using simplified (and cheaper) versions of ERC721Enumerable and ERC721Metadata */
contract Cryptostamp2 is ERC721SimpleMapsURI, CS2PropertiesI {
using SafeMath for uint256;
bytes4 private constant _INTERFACE_ID_ACHIEVEMENTS_UPGRADING = 0x58cac597;
uint8 private constant COLORBITS = 4;
uint8 private constant COLORMOD = uint8(2) ** COLORBITS;
address public createControl;
address public tokenAssignmentControl;
address public CS1Address;
address public CS1ColorsAddress;
AchievementsUpgradingI public achievementsContract;
uint256 immutable finalSupply;
uint256 constant maxSupportedSupply = 2 ** 24;
bool public allowPublicMinting = false;
bytes32 public dataRoot;
uint256 public upgradesDone = 0;
uint256 public upgradeMaximum;
uint8[maxSupportedSupply] private properties;
uint256[5][4] public typeColorSupply; // Note that numbers are reversed in Solidity compared to other languages!
mapping(uint256 => bool) public usedInUpgrade;
mapping(uint256 => bool) public usedInUpgradeCS1;
mapping(address => uint256) public signedTransferNonce;
event CreateControlTransferred(address indexed previousCreateControl, address indexed newCreateControl);
event TokenAssignmentControlTransferred(address indexed previousTokenAssignmentControl, address indexed newTokenAssignmentControl);
event AchievementsContractSet(address indexed achievementsContractAddress);
event UpgradeMaximumChanged(uint256 previousUpgradeMaximum, uint256 newUpgradeMaximum);
event DataRootSet(bytes32 dataRoot);
event PublicMintingEnabled();
event PublicMintingDisabled();
event MintedWithProof(address operator, uint256 indexed tokenId, address indexed owner, AssetType aType, Colors color);
event SignedTransfer(address operator, address indexed from, address indexed to, uint256 indexed tokenId, uint256 signedTransferNonce);
event StampUpgraded(uint256 indexed changedTokenId, Colors previousColor, Colors newColor, bool withJoker, uint256 usedTokedId1, uint256 usedTokenId2);
// TestAchievementsUpgrading event - never emitted in this contract but helpful for running our tests.
event SeenCS2ColorChanged(uint256 tokenId, Colors previousColor, Colors newColor);
constructor(address _createControl, address _tokenAssignmentControl, address _CS1Address, address _CS1ColorsAddress, uint256 _finalSupply, uint256 _upgradeMaximum)
ERC721SimpleMapsURI("Crypto stamp Edition 2", "CS2", "https://test.crypto.post.at/CS2/meta/")
public
{
createControl = _createControl;
require(address(createControl) != address(0x0), "You need to provide an actual createControl address.");
tokenAssignmentControl = _tokenAssignmentControl;
require(address(tokenAssignmentControl) != address(0x0), "You need to provide an actual tokenAssignmentControl address.");
CS1ColorsAddress = _CS1ColorsAddress;
require(address(CS1ColorsAddress) != address(0x0), "You need to provide an actual CS1 colors address.");
CS1Address = _CS1Address;
require(address(CS1Address) != address(0x0), "You need to provide an actual CS1 address.");
finalSupply = _finalSupply;
require(_finalSupply <= maxSupportedSupply, "The final supply is too high.");
upgradeMaximum = _upgradeMaximum;
}
modifier onlyCreateControl()
{
require(msg.sender == createControl, "createControl key required for this function.");
_;
}
modifier onlyTokenAssignmentControl() {
require(msg.sender == tokenAssignmentControl, "tokenAssignmentControl key required for this function.");
_;
}
modifier requireMinting() {
require(mintingFinished() == false, "This call only works when minting is not finished.");
_;
}
/*** Enable adjusting variables after deployment ***/
function transferTokenAssignmentControl(address _newTokenAssignmentControl)
public
onlyTokenAssignmentControl
{
require(_newTokenAssignmentControl != address(0), "tokenAssignmentControl cannot be the zero address.");
emit TokenAssignmentControlTransferred(tokenAssignmentControl, _newTokenAssignmentControl);
tokenAssignmentControl = _newTokenAssignmentControl;
}
function transferCreateControl(address _newCreateControl)
public
onlyCreateControl
{
require(_newCreateControl != address(0), "createControl cannot be the zero address.");
emit CreateControlTransferred(createControl, _newCreateControl);
createControl = _newCreateControl;
}
// Set an achievements contract.
function setAchievementsContract(address _achievementsAddress)
public
onlyCreateControl
{
achievementsContract = AchievementsUpgradingI(_achievementsAddress);
emit AchievementsContractSet(_achievementsAddress);
if (_achievementsAddress != address(0)) {
require(IERC165(achievementsContract).supportsInterface(_INTERFACE_ID_ACHIEVEMENTS_UPGRADING),
"Need to implement the achievements upgrading interface!");
}
}
function setUpgradeMaximum(uint256 _newUpgradeMaximum)
public
onlyCreateControl
{
require(upgradesDone <= _newUpgradeMaximum, "Already more upgrades done than the requested limit.");
emit UpgradeMaximumChanged(upgradeMaximum, _newUpgradeMaximum);
upgradeMaximum = _newUpgradeMaximum;
}
function setDataRoot(bytes32 _newDataRoot)
public
onlyCreateControl
{
require(dataRoot == bytes32(""), "Can only set root once.");
emit DataRootSet(_newDataRoot);
dataRoot = _newDataRoot;
}
/*** Base functionality: minting, URI, property getters, etc. ***/
// Override totalSupply() so it reports the target final supply.
/**
* @dev Gets the total amount of tokens stored by the contract.
* @return uint256 representing the total amount of tokens
*/
function totalSupply()
public view override
returns (uint256) {
return finalSupply;
}
/**
* @dev Gets the actually minted amount of tokens in the contract.
* @return uint256 representing the minted amount of tokens
*/
function mintedSupply()
public view
returns (uint256) {
return super.totalSupply();
}
// Issue a new crypto stamp asset, giving it to a specific owner address.
// As appending the ID into a URI in Solidity is complicated, generate both
// externally and hand them over to the asset here.
function create(uint256 _tokenId, address _owner, AssetType _type, Colors _color)
public
onlyCreateControl
requireMinting
{
// _mint already ends up checking if owner != 0 and that tokenId doesn't exist yet.
_mintWithProperties(_tokenId, _owner, _type, _color);
}
// Batch-issue multiple crypto stamps with adjacent IDs.
function createMulti(uint256 _tokenIdStart, address[] memory _owners, AssetType[] memory _types, Colors[] memory _colors)
public
onlyCreateControl
requireMinting
{
require(_owners.length == _types.length && _owners.length == _colors.length, "All given arrays need to be the same length.");
uint256 addrcount = _owners.length;
for (uint256 i = 0; i < addrcount; i++) {
// Make sure this is in sync with what create() does.
_mintWithProperties(_tokenIdStart + i, _owners[i], _types[i], _colors[i]);
}
}
// Issue a crypto stamp with a merkle proof.
function createWithProof(bytes32 tokenData, bytes32[] memory merkleProof)
public
requireMinting
returns (uint256)
{
require(publicMintingAllowed(), "Public minting needs to be allowed.");
(uint256 tokenId, address owner, AssetType aType, Colors color) = getDataFieldsFromProof(tokenData, merkleProof);
require(!_exists(tokenId), "Token already exists.");
emit MintedWithProof(msg.sender, tokenId, owner, aType, color);
_mintWithProperties(tokenId, owner, aType, color);
return tokenId;
}
/**
* @dev Gets the token ID of the specified token ID
* @param tokenData bytes32 token data : 11 bytes TokenId | 1 byte properties split in 4 bits + 4 bits | 20 bytes owner
* @param merkleProof bytes32[] memory array of a storage proof
* @return address currently marked as the owner of the given token ID, if manifested explicitly or if the proof matches
*/
function getTokenIdFromProof(bytes32 tokenData, bytes32[] memory merkleProof)
public view
returns (uint256) {
(uint256 tokenId, /*address owner*/, /*AssetType aType*/, /*Colors color*/) = getDataFieldsFromProof(tokenData, merkleProof);
return tokenId;
}
/**
* @dev Gets the owner of the specified token ID
* @param tokenData bytes32 token data : 11 bytes TokenId | 1 byte properties split in 4 bits + 4 bits | 20 bytes owner
* @param merkleProof bytes32[] memory array of a storage proof
* @return address currently marked as the owner of the given token ID, if manifested explicitly or if the proof matches
*/
function getOwnerFromProof(bytes32 tokenData, bytes32[] memory merkleProof)
public view
returns (address) {
(uint256 tokenId, address owner, /*AssetType aType*/, /*Colors color*/) = getDataFieldsFromProof(tokenData, merkleProof);
if (_exists(tokenId)) return ownerOf(tokenId);
return owner;
}
/**
* @dev Gets the owner of the specified token ID
* @param tokenData bytes32 token data : 11 bytes TokenId | 1 byte properties split in 4 bits + 4 bits | 20 bytes owner
* @param merkleProof bytes32[] memory array of a storage proof
* @return AssetType of the given token.
*/
function getTypeFromProof(bytes32 tokenData, bytes32[] memory merkleProof)
public view
returns (AssetType) {
(uint256 tokenId, /*address owner*/, AssetType aType, /*Colors color*/) = getDataFieldsFromProof(tokenData, merkleProof);
if (_exists(tokenId)) return getType(tokenId);
return aType;
}
/**
* @dev Gets the color of the specified token ID even if not manifested yet.
* @param tokenData bytes32 token data : 11 bytes TokenId | 1 byte properties split in 4 bits + 4 bits | 20 bytes owner
* @param merkleProof bytes32[] memory array of a storage proof
* @return color of the given token.
*/
function getColorFromProof(bytes32 tokenData, bytes32[] memory merkleProof)
public view
returns (Colors) {
(uint256 tokenId, /*address owner*/, /*AssetType aType*/, Colors color) = getDataFieldsFromProof(tokenData, merkleProof);
if (_exists(tokenId)) return getColor(tokenId);
return color;
}
/**
* @dev Gets the tokenId, owner, type and color from the specified token proof.
* @param tokenData bytes32 token data : 11 bytes TokenId | 1 byte properties split in 4 bits + 4 bits | 20 bytes owner
* @param merkleProof bytes32[] memory array of a storage proof
* @return tokenId, owner, type and color of the given token.
*/
function getDataFieldsFromProof(bytes32 tokenData, bytes32[] memory merkleProof)
public view
returns (uint256, address, AssetType, Colors)
{
require(dataRoot != bytes32(""), "Root needs to be set.");
require(MerkleProof.verify(merkleProof, dataRoot, tokenData), "Verification failed.");
uint256 tokenId = uint256(tokenData >> 168); // shift by 20 bytes for address and 1 byte for properties
AssetType aType = AssetType(uint8(tokenData[11]) >> COLORBITS);
Colors color = Colors(uint8(tokenData[11]) % COLORMOD);
address owner = address(uint256(tokenData)); // address() cuts off everything except the lower 160 bits
require(owner != address(0), "tokenData needs to contain an owner.");
return (tokenId, owner, aType, color);
}
// Actually issue a crypto stamp with its properties, or just check the properties if it already exists.
function _mintWithProperties(uint256 _tokenId, address _owner, AssetType _type, Colors _color)
internal
{
if (_exists(_tokenId)) {
require(getType(_tokenId) == _type, "Type mismatch with existing token.");
if (upgradesDone == 0) {
// Once any upgrades have been done, we don't know if the minted color is still set necessarily.
require(getColor(_tokenId) == _color, "Color mismatch with existing token.");
}
}
else {
_mint(_owner, _tokenId);
// Store the type in the higher bits of the unit and the color in the lower bits.
properties[_tokenId] = uint8(_type) * COLORMOD + uint8(_color);
typeColorSupply[uint(_type)][uint(_color)] = typeColorSupply[uint(_type)][uint(_color)].add(1);
}
}
// Determine if the creation/minting process is done.
function mintingFinished()
public view
returns (bool)
{
return (super.totalSupply() >= finalSupply);
}
// Allow creation/minting by anyone who can show a valid merkle proof.
function enablePublicMinting()
public
onlyCreateControl
{
require(dataRoot != bytes32(""), "Root needs to be set.");
allowPublicMinting = true;
emit PublicMintingEnabled();
}
// deactive public creation/minting.
function disablePublicMinting()
public
onlyCreateControl
{
allowPublicMinting = false;
emit PublicMintingDisabled();
}
// Determine if the creation/minting process is done.
function publicMintingAllowed()
public view
returns (bool)
{
return (!mintingFinished() && allowPublicMinting);
}
// Set new base for the token URI.
function setBaseURI(string memory _newBaseURI)
public
onlyCreateControl
{
super._setBaseURI(_newBaseURI);
}
// Get type of the given token.
function getType(uint256 tokenId)
public view override
returns (AssetType) {
require(_exists(tokenId), "Token ID needs to exist.");
return AssetType(properties[tokenId] >> COLORBITS);
}
// Get color of the given token.
function getColor(uint256 tokenId)
public view override
returns (Colors) {
require(_exists(tokenId), "Token ID needs to exist.");
return Colors(properties[tokenId] % COLORMOD);
}
// Returns whether the specified token exists
function exists(uint256 tokenId)
public view
returns (bool) {
return _exists(tokenId);
}
function typeSupply(AssetType _type)
public view
returns (uint256) {
uint256 thisTypeSupply = 0;
for (uint256 i = 0; i < typeColorSupply[uint(_type)].length; i++) {
thisTypeSupply = thisTypeSupply.add(typeColorSupply[uint(_type)][i]);
}
return thisTypeSupply;
}
function colorSupply(Colors _color)
public view
returns (uint256) {
uint256 thisColorSupply = 0;
for (uint256 i = 0; i < typeColorSupply.length; i++) {
thisColorSupply = thisColorSupply.add(typeColorSupply[i][uint(_color)]);
}
return thisColorSupply;
}
/*** Allows any user to initiate a transfer with the signature of the current stamp owner ***/
// Outward-facing function for signed transfer: assembles the expected data and then calls the internal function to do the rest.
// Can called by anyone knowing about the right signature, but can only transfer to the given specific target.
function signedTransfer(uint256 _tokenId, address _to, bytes memory _signature)
public
{
address currentOwner = ownerOf(_tokenId);
// The signed data contains the token ID, the transfer target and a nonce.
bytes32 data = keccak256(abi.encodePacked(address(this), this.signedTransfer.selector, currentOwner, _to, _tokenId, signedTransferNonce[currentOwner]));
_signedTransferInternal(currentOwner, data, _tokenId, _to, _signature);
}
// Outward-facing function for operator-driven signed transfer: assembles the expected data and then calls the internal function to do the rest.
// Can transfer to any target, but only be called by the trusted operator contained in the signature.
function signedTransferWithOperator(uint256 _tokenId, address _to, bytes memory _signature)
public
{
address currentOwner = ownerOf(_tokenId);
// The signed data contains the operator, the token ID, and a nonce. Note that we use the selector of the external function here!
bytes32 data = keccak256(abi.encodePacked(address(this), this.signedTransferWithOperator.selector, msg.sender, currentOwner, _tokenId, signedTransferNonce[currentOwner]));
_signedTransferInternal(currentOwner, data, _tokenId, _to, _signature);
}
// Actually check the signature and perform a signed transfer.
function _signedTransferInternal(address _currentOwner, bytes32 _data, uint256 _tokenId, address _to, bytes memory _signature)
internal
{
bytes32 hash = ECDSA.toEthSignedMessageHash(_data);
address signer = ECDSA.recover(hash, _signature);
require(signer == _currentOwner, "Signature needs to match parameters, nonce, and current owner.");
// Now that we checked that the signature is correct, do the actual transfer and increase the nonce.
emit SignedTransfer(msg.sender, _currentOwner, _to, _tokenId, signedTransferNonce[_currentOwner]);
signedTransferNonce[_currentOwner] = signedTransferNonce[_currentOwner].add(1);
_safeTransfer(_currentOwner, _to, _tokenId, "");
}
// Mint token and transfer it in the same transaction.
// Can called by anyone knowing about the right signature (and proof), but can only transfer to the given specific target.
function signedTransferWithMintProof(bytes32 tokenData, address _to, bytes calldata _signature, bytes32[] calldata merkleProof)
external
{
uint256 tokenId = createWithProof(tokenData, merkleProof);
signedTransfer(tokenId, _to, _signature);
}
// Mint token and transfer it in the same transaction.
// Can transfer to any target, but only be called by the trusted operator contained in the signature.
function signedTransferWithOperatorAndMintProof(bytes32 tokenData, address _to, bytes calldata _signature, bytes32[] calldata merkleProof)
external
{
uint256 tokenId = createWithProof(tokenData, merkleProof);
signedTransferWithOperator(tokenId, _to, _signature);
}
/*** Upgrading functionality: upgrade color of the stamp using other stamps of the same color ***/
// Returns whether upgrading is possible at this time.
function upgradesAllowed() public view returns (bool) {
return (address(achievementsContract) != address(0) && upgradesDone < upgradeMaximum);
}
// Upgrade 1 CS2 stamp by "upgrading" with 2 other CS2 of same type and color.
function upgradeStamp(uint256 _upgradeTokenId, uint256 _helperTokenId1, uint256 _helperTokenId2)
public
{
require(address(achievementsContract) != address(0), "No achievements contract set");
require(upgradesDone < upgradeMaximum, "Maximum upgrades reached");
require(_upgradeTokenId != _helperTokenId1 &&
_upgradeTokenId != _helperTokenId2 &&
_helperTokenId1 != _helperTokenId2,
"You actually need to use 3 different tokens to upgrade");
require(msg.sender == ownerOf(_upgradeTokenId) &&
msg.sender == ownerOf(_helperTokenId1) &&
msg.sender == ownerOf(_helperTokenId2),
"Caller has to be owner of all tokens.");
require(usedInUpgrade[_upgradeTokenId] == false &&
usedInUpgrade[_helperTokenId1] == false &&
usedInUpgrade[_helperTokenId2] == false,
"Cannot used stamps already used in a upgrade.");
Colors previousColor = getColor(_upgradeTokenId);
AssetType aType = getType(_upgradeTokenId);
require(getType(_helperTokenId1) == aType && getColor(_helperTokenId1) == previousColor &&
getType(_helperTokenId2) == aType && getColor(_helperTokenId2) == previousColor,
"All tokens involved must have the same type and color");
usedInUpgrade[_helperTokenId1] = true;
usedInUpgrade[_helperTokenId2] = true;
// Now, actually upgrade the color and notify the achievements contract.
Colors newColor = _upgradeColor(_upgradeTokenId, aType, previousColor);
emit StampUpgraded(_upgradeTokenId, previousColor, newColor, false, _helperTokenId1, _helperTokenId2);
}
// Upgrade 1 CS2 stamp by "upgrading" with 1 other CS2 of same type and color and 1 CS1 of same color (as a "joker").
function upgradeStampWithJoker(uint256 _upgradeTokenId, uint256 _helperTokenId, uint256 _helperCS1TokenId)
public
{
require(address(achievementsContract) != address(0), "No achievements contract set");
require(upgradesDone < upgradeMaximum, "Maximum upgrades reached");
require(_upgradeTokenId != _helperTokenId, "You actually need to use different tokens to upgrade");
IERC721 CS1 = IERC721(CS1Address);
CS1ColorsI CS1Colors = CS1ColorsI(CS1ColorsAddress);
require(msg.sender == ownerOf(_upgradeTokenId) &&
msg.sender == ownerOf(_helperTokenId) &&
msg.sender == CS1.ownerOf(_helperCS1TokenId),
"Caller has to be owner of all tokens.");
require(usedInUpgrade[_upgradeTokenId] == false &&
usedInUpgrade[_helperTokenId] == false &&
usedInUpgradeCS1[_helperCS1TokenId] == false,
"Cannot used stamps already used in a upgrade.");
Colors previousColor = getColor(_upgradeTokenId);
AssetType aType = getType(_upgradeTokenId);
// Note: The second line below ONLY works because the Colors enums are the same on both contracts!
require(getType(_helperTokenId) == aType && getColor(_helperTokenId) == previousColor &&
CS1Colors.getColor(_helperCS1TokenId) == CS1ColorsI.Colors(uint8(previousColor)),
"All tokens involved must have the same color, and all CS2 tokens the same type");
usedInUpgrade[_helperTokenId] = true;
usedInUpgradeCS1[_helperCS1TokenId] = true;
// Now, actually upgrade the color and notify the achievements contract.
Colors newColor = _upgradeColor(_upgradeTokenId, aType, previousColor);
emit StampUpgraded(_upgradeTokenId, previousColor, newColor, true, _helperTokenId, _helperCS1TokenId);
}
function _upgradeColor(uint256 _upgradeTokenId, AssetType _assetType, Colors _previousColor)
private
returns(Colors)
{
Colors newColor = Colors(uint256(_previousColor).add(1));
properties[_upgradeTokenId] = uint8(_assetType) * COLORMOD + uint8(newColor);
typeColorSupply[uint(_assetType)][uint(_previousColor)] = typeColorSupply[uint(_assetType)][uint(_previousColor)].sub(1);
typeColorSupply[uint(_assetType)][uint(newColor)] = typeColorSupply[uint(_assetType)][uint(newColor)].add(1);
upgradesDone = upgradesDone.add(1);
require(
achievementsContract.onCS2ColorChanged(_upgradeTokenId, _previousColor, newColor) ==
achievementsContract.onCS2ColorChanged.selector,
"Achievements upgrading: got unknown value from onCS2ColorChanged"
);
return newColor;
}
/*** Enable reverse ENS registration ***/
// Call this with the address of the reverse registrar for the respecitve network and the ENS name to register.
// The reverse registrar can be found as the owner of 'addr.reverse' in the ENS system.
// See https://docs.ens.domains/ens-deployments for address of ENS deployments, e.g. Etherscan can be used to look up that owner on those.
// namehash.hash("addr.reverse") == "0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2"
// Ropsten: ens.owner(namehash.hash("addr.reverse")) == "0x6F628b68b30Dc3c17f345c9dbBb1E483c2b7aE5c"
// Mainnet: ens.owner(namehash.hash("addr.reverse")) == "0x084b1c3C81545d370f3634392De611CaaBFf8148"
function registerReverseENS(address _reverseRegistrarAddress, string calldata _name)
external
onlyTokenAssignmentControl
{
require(_reverseRegistrarAddress != address(0), "need a valid reverse registrar");
ENSReverseRegistrarI(_reverseRegistrarAddress).setName(_name);
}
/*** Make sure currency doesn't get stranded in this contract ***/
// If this contract gets a balance in some ERC20 contract after it's finished, then we can rescue it.
function rescueToken(IERC20 _foreignToken, address _to)
external
onlyTokenAssignmentControl
{
_foreignToken.transfer(_to, _foreignToken.balanceOf(address(this)));
}
// If this contract gets a balance in some ERC721 contract after it's finished, then we can rescue it.
function approveNFTrescue(IERC721 _foreignNFT, address _to)
external
onlyTokenAssignmentControl
{
_foreignNFT.setApprovalForAll(_to, true);
}
// Make sure this contract cannot receive ETH.
receive()
external payable
{
revert("The contract cannot receive ETH payments.");
}
}
{
"compilationTarget": {
"Cryptostamp2.sol": "Cryptostamp2"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_createControl","type":"address"},{"internalType":"address","name":"_tokenAssignmentControl","type":"address"},{"internalType":"address","name":"_CS1Address","type":"address"},{"internalType":"address","name":"_CS1ColorsAddress","type":"address"},{"internalType":"uint256","name":"_finalSupply","type":"uint256"},{"internalType":"uint256","name":"_upgradeMaximum","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"achievementsContractAddress","type":"address"}],"name":"AchievementsContractSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"}],"name":"BaseURI","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousCreateControl","type":"address"},{"indexed":true,"internalType":"address","name":"newCreateControl","type":"address"}],"name":"CreateControlTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"dataRoot","type":"bytes32"}],"name":"DataRootSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"enum CS2PropertiesI.AssetType","name":"aType","type":"uint8"},{"indexed":false,"internalType":"enum CS2PropertiesI.Colors","name":"color","type":"uint8"}],"name":"MintedWithProof","type":"event"},{"anonymous":false,"inputs":[],"name":"PublicMintingDisabled","type":"event"},{"anonymous":false,"inputs":[],"name":"PublicMintingEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"enum CS2PropertiesI.Colors","name":"previousColor","type":"uint8"},{"indexed":false,"internalType":"enum CS2PropertiesI.Colors","name":"newColor","type":"uint8"}],"name":"SeenCS2ColorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"signedTransferNonce","type":"uint256"}],"name":"SignedTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"changedTokenId","type":"uint256"},{"indexed":false,"internalType":"enum CS2PropertiesI.Colors","name":"previousColor","type":"uint8"},{"indexed":false,"internalType":"enum CS2PropertiesI.Colors","name":"newColor","type":"uint8"},{"indexed":false,"internalType":"bool","name":"withJoker","type":"bool"},{"indexed":false,"internalType":"uint256","name":"usedTokedId1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usedTokenId2","type":"uint256"}],"name":"StampUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousTokenAssignmentControl","type":"address"},{"indexed":true,"internalType":"address","name":"newTokenAssignmentControl","type":"address"}],"name":"TokenAssignmentControlTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousUpgradeMaximum","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newUpgradeMaximum","type":"uint256"}],"name":"UpgradeMaximumChanged","type":"event"},{"inputs":[],"name":"CS1Address","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CS1ColorsAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"achievementsContract","outputs":[{"internalType":"contract AchievementsUpgradingI","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowPublicMinting","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_foreignNFT","type":"address"},{"internalType":"address","name":"_to","type":"address"}],"name":"approveNFTrescue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum CS2PropertiesI.Colors","name":"_color","type":"uint8"}],"name":"colorSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"enum CS2PropertiesI.AssetType","name":"_type","type":"uint8"},{"internalType":"enum CS2PropertiesI.Colors","name":"_color","type":"uint8"}],"name":"create","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"createControl","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenIdStart","type":"uint256"},{"internalType":"address[]","name":"_owners","type":"address[]"},{"internalType":"enum CS2PropertiesI.AssetType[]","name":"_types","type":"uint8[]"},{"internalType":"enum CS2PropertiesI.Colors[]","name":"_colors","type":"uint8[]"}],"name":"createMulti","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tokenData","type":"bytes32"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"createWithProof","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dataRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"disablePublicMinting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"enablePublicMinting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getColor","outputs":[{"internalType":"enum CS2PropertiesI.Colors","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tokenData","type":"bytes32"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"getColorFromProof","outputs":[{"internalType":"enum CS2PropertiesI.Colors","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tokenData","type":"bytes32"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"getDataFieldsFromProof","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"enum CS2PropertiesI.AssetType","name":"","type":"uint8"},{"internalType":"enum CS2PropertiesI.Colors","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tokenData","type":"bytes32"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"getOwnerFromProof","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tokenData","type":"bytes32"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"getTokenIdFromProof","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getType","outputs":[{"internalType":"enum CS2PropertiesI.AssetType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tokenData","type":"bytes32"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"getTypeFromProof","outputs":[{"internalType":"enum CS2PropertiesI.AssetType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintedSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintingFinished","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicMintingAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_reverseRegistrarAddress","type":"address"},{"internalType":"string","name":"_name","type":"string"}],"name":"registerReverseENS","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_foreignToken","type":"address"},{"internalType":"address","name":"_to","type":"address"}],"name":"rescueToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_achievementsAddress","type":"address"}],"name":"setAchievementsContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_newBaseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_newDataRoot","type":"bytes32"}],"name":"setDataRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newUpgradeMaximum","type":"uint256"}],"name":"setUpgradeMaximum","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"signedTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"signedTransferNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tokenData","type":"bytes32"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"bytes","name":"_signature","type":"bytes"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"signedTransferWithMintProof","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"signedTransferWithOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tokenData","type":"bytes32"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"bytes","name":"_signature","type":"bytes"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"signedTransferWithOperatorAndMintProof","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenAssignmentControl","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newCreateControl","type":"address"}],"name":"transferCreateControl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newTokenAssignmentControl","type":"address"}],"name":"transferTokenAssignmentControl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"typeColorSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum CS2PropertiesI.AssetType","name":"_type","type":"uint8"}],"name":"typeSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"upgradeMaximum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_upgradeTokenId","type":"uint256"},{"internalType":"uint256","name":"_helperTokenId1","type":"uint256"},{"internalType":"uint256","name":"_helperTokenId2","type":"uint256"}],"name":"upgradeStamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_upgradeTokenId","type":"uint256"},{"internalType":"uint256","name":"_helperTokenId","type":"uint256"},{"internalType":"uint256","name":"_helperCS1TokenId","type":"uint256"}],"name":"upgradeStampWithJoker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upgradesAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"upgradesDone","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"usedInUpgrade","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"usedInUpgradeCS1","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]