ETH Price: $2,101.58 (+1.75%)

Transaction Decoder

Block:
12238203 at Apr-14-2021 12:28:00 PM +UTC
Transaction Fee:
0.021617596346594204 ETH $45.43
Gas Used:
237,556 Gas / 91.000001459 Gwei

Account State Difference:

  Address   Before After State Difference Code
(F2Pool Old)
2,842.161440055857211296 Eth2,842.1830576522038055 Eth0.021617596346594204
0xaA8330FB...8505C6B37
0xB20217bf...6CcD570C8
0xfa12612A...1C92F70b4
0.195627540765006564 Eth
Nonce: 912
0.17400994441841236 Eth
Nonce: 913
0.021617596346594204

Execution Trace

POLCToken.approveAndCall( _spender=0x9A0043bb4566363D507bD8e18a52B828460431D9, _value=0, _extraData=0x000000000000000000000000000000000000000000000000000000000000001C0000000000000000000000000000000000000000000000000000000001B02F04 ) => ( success=True )
  • 0x9a0043bb4566363d507bd8e18a52b828460431d9.8f4ffcb1( )
    • ERC721.assetsByType( 28 ) => ( maxAmount=5, mintedAmount=2, coinIndex=2, copyright=Standard Copyright License. Creator: rfarencibia )
    • ERC721.tradeCoins( 2 ) => ( tokenAddress=0xaA8330FB2B4D5D07ABFE7A72262752a8505C6B37, symbol=POLC, name=Polka City )
    • POLCToken.transferFrom( _from=0xfa12612A0c2eF2E88861794422d379F1C92F70b4, _to=0xAD334543437EF71642Ee59285bAf2F4DAcBA613F, _value=0 ) => ( success=True )
    • ERC721.mint( to=0xfa12612A0c2eF2E88861794422d379F1C92F70b4, _assetType=28, _value=0, _customDetails=28323588 ) => ( success=True )
      File 1 of 2: POLCToken
      // SPDX-License-Identifier: MIT
      
      pragma solidity ^0.8.0;
      
      library SafeMath {
      
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, "SafeMath: addition overflow");
      
              return c;
          }
      
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, "SafeMath: subtraction overflow");
          }
      
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              uint256 c = a - b;
      
              return c;
          }
              
      }
      
      interface ItokenRecipient { 
          function receiveApproval(address _from, uint256 _value, address _token, bytes calldata _extraData) external returns (bool); 
      }
      
      interface IERC20Token {
          function totalSupply() external view returns (uint256 supply);
          function transfer(address _to, uint256 _value) external  returns (bool success);
          function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
          function balanceOf(address _owner) external view returns (uint256 balance);
          function approve(address _spender, uint256 _value) external returns (bool success);
          function allowance(address _owner, address _spender) external view returns (uint256 remaining);
      }
      
      contract Ownable {
      
          address private owner;
          
          event OwnerSet(address indexed oldOwner, address indexed newOwner);
          
          modifier onlyOwner() {
              require(msg.sender == owner, "Caller is not owner");
              _;
          }
      
          constructor() {
              owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor
              emit OwnerSet(address(0), owner);
          }
      
      
          function changeOwner(address newOwner) public onlyOwner {
              emit OwnerSet(owner, newOwner);
              owner = newOwner;
          }
      
          function getOwner() external view returns (address) {
              return owner;
          }
      }
      
      contract StandardToken is IERC20Token {
          
          using SafeMath for uint256;
          mapping (address => uint256) public balances;
          mapping (address => mapping (address => uint256)) public allowed;
          uint256 public _totalSupply;
          
          event Transfer(address indexed _from, address indexed _to, uint256 _value);
          event Approval(address indexed _owner, address indexed _spender, uint256 _value);
          
          function totalSupply() override public view returns (uint256 supply) {
              return _totalSupply;
          }
      
          function transfer(address _to, uint256 _value) override virtual public returns (bool success) {
              require(_to != address(0x0), "Use burn function instead");                              
      		require(_value >= 0, "Invalid amount"); 
      		require(balances[msg.sender] >= _value, "Not enough balance");
      		balances[msg.sender] = balances[msg.sender].sub(_value);
      		balances[_to] = balances[_to].add(_value);
      		emit Transfer(msg.sender, _to, _value);
              return true;
          }
      
          function transferFrom(address _from, address _to, uint256 _value) override virtual public returns (bool success) {
              require(_to != address(0x0), "Use burn function instead");                               
      		require(_value >= 0, "Invalid amount"); 
      		require(balances[_from] >= _value, "Not enough balance");
      		require(allowed[_from][msg.sender] >= _value, "You need to increase allowance");
      		balances[_from] = balances[_from].sub(_value);
      		balances[_to] = balances[_to].add(_value);
      		emit Transfer(_from, _to, _value);
              return true;
          }
      
          function balanceOf(address _owner) override public view returns (uint256 balance) {
              return balances[_owner];
          }
      
          function approve(address _spender, uint256 _value) override public returns (bool success) {
              allowed[msg.sender][_spender] = _value;
              emit Approval(msg.sender, _spender, _value);
              return true;
          }
      
          function allowance(address _owner, address _spender) override public view returns (uint256 remaining) {
              return allowed[_owner][_spender];
          }
          
      }
      
      contract POLCToken is Ownable, StandardToken {
      
          using SafeMath for uint256;
          string public name = "Polka City";
          uint8 public decimals = 18;
          string public symbol = "POLC";
      
          // Time lock for progressive release of team, marketing and platform balances
          struct TimeLock {
              uint256 totalAmount;
              uint256 lockedBalance;
              uint128 baseDate;
              uint64 step;
              uint64 tokensStep;
          }
          mapping (address => TimeLock) public timeLocks; 
      
          // Prevent Bots - If true, limits transactions to 1 transfer per block (whitelisted can execute multiple transactions)
          bool public limitTransactions;
          mapping (address => bool) public contractsWhiteList;
          mapping (address => uint) public lastTXBlock;
          event Burn(address indexed from, uint256 value);
      
      // token sale
      
          // Wallet for the tokens to be sold, and receive ETH
          address payable public salesWallet;
          uint256 public soldOnCSale;
          uint256 public constant CROWDSALE_START = 1613926800;
          uint256 public constant CROWDSALE_END = 1614556740;
          uint256 public constant CSALE_WEI_FACTOR = 15000;
          uint256 public constant CSALE_HARDCAP = 7500000 ether;
          
          constructor() {
              _totalSupply = 250000000 ether;
              
              // Base date to calculate team, marketing and platform tokens lock
              uint256 lockStartDate = 1613494800;
              
              // Team wallet - 10000000 tokens
              // 0 tokens free, 10000000 tokens locked - progressive release of 5% every 30 days (after 180 days of waiting period)
              address team = 0x4ef5B3d10fD217AC7ddE4DDee5bF319c5c356723;
              balances[team] = 10000000 ether;
              timeLocks[team] = TimeLock(10000000 ether, 10000000 ether, uint128(lockStartDate + (180 days)), 30 days, 500000);
              emit Transfer(address(0x0), team, balances[team]);
      
              // Marketing wallet - 5000000 tokens
              // 1000000 tokens free, 4000000 tokens locked - progressive release of 5% every 30 days
              address marketingWallet = 0x056F878d4Ac07E66C9a46a8db4918E827c6fD71c;
              balances[marketingWallet] = 5000000 ether;
              timeLocks[marketingWallet] = TimeLock(4000000 ether, 4000000 ether, uint128(lockStartDate), 30 days, 200000);
              emit Transfer(address(0x0), marketingWallet, balances[marketingWallet]);
              
              // Private sale wallet - 2500000 tokens
              address privateWallet = 0xED854fCF86efD8473F174d6dE60c8A5EBDdCc37A;
              balances[privateWallet] = 2500000 ether;
              emit Transfer(address(0x0), privateWallet, balances[privateWallet]);
              
              // Sales wallet, holds Pre-Sale balance - 7500000 tokens
              salesWallet = payable(0x4bb74E94c1EB133a6868C53aA4f6BD437F99c347);
              balances[salesWallet] = 7500000 ether;
              emit Transfer(address(0x0), salesWallet, balances[salesWallet]);
              
              // Exchanges - 25000000 tokens
              address exchanges = 0xE50d4358425a93702988eCd8B66c2EAD8b41CE5d;  
              balances[exchanges] = 25000000 ether;
              emit Transfer(address(0x0), exchanges, balances[exchanges]);
              
              // Platform wallet - 200000000 tokens
              // 50000000 tokens free, 150000000 tokens locked - progressive release of 25000000 every 90 days
              address platformWallet = 0xAD334543437EF71642Ee59285bAf2F4DAcBA613F;
              balances[platformWallet] = 200000000 ether;
              timeLocks[platformWallet] = TimeLock(150000000 ether, 150000000 ether, uint128(lockStartDate), 90 days, 25000000);
              emit Transfer(address(0x0), platformWallet, balances[platformWallet]);
              
      
      
          }
          
          function transfer(address _to, uint256 _value) override public returns (bool success) {
              require(checkTransferLimit(), "Transfers are limited to 1 per block");
              require(_value <= (balances[msg.sender] - timeLocks[msg.sender].lockedBalance));
              return super.transfer(_to, _value);
          }
          
          function transferFrom(address _from, address _to, uint256 _value) override public returns (bool success) {
              require(checkTransferLimit(), "Transfers are limited to 1 per block");
              require(_value <= (balances[_from] - timeLocks[_from].lockedBalance));
              return super.transferFrom(_from, _to, _value);
          }
          
          function burn(uint256 _value) public returns (bool success) {
              require(balances[msg.sender] >= _value, "Not enough balance");
      		require(_value >= 0, "Invalid amount"); 
              balances[msg.sender] = balances[msg.sender].sub(_value);
              _totalSupply = _totalSupply.sub(_value);
              emit Burn(msg.sender, _value);
              return true;
          }
          
          function approveAndCall(address _spender, uint256 _value, bytes memory _extraData) public returns (bool success) {
              allowed[msg.sender][_spender] = _value;
              emit Approval(msg.sender, _spender, _value);
              ItokenRecipient recipient = ItokenRecipient(_spender);
              require(recipient.receiveApproval(msg.sender, _value, address(this), _extraData));
              return true;
          }
          
      
          function releaseTokens(address _account) public {
              uint256 timeDiff = block.timestamp - uint256(timeLocks[_account].baseDate);
              require(timeDiff > uint256(timeLocks[_account].step), "Unlock point not reached yet");
              uint256 steps = (timeDiff / uint256(timeLocks[_account].step));
              uint256 unlockableAmount = ((uint256(timeLocks[_account].tokensStep) * 1 ether) * steps);
              if (unlockableAmount >=  timeLocks[_account].totalAmount) {
                  timeLocks[_account].lockedBalance = 0;
              } else {
                  timeLocks[_account].lockedBalance = timeLocks[_account].totalAmount - unlockableAmount;
              }
          }
             
          function checkTransferLimit() internal returns (bool txAllowed) {
              address _caller = msg.sender;
              if (limitTransactions == true && contractsWhiteList[_caller] != true) {
                  if (lastTXBlock[_caller] == block.number) {
                      return false;
                  } else {
                      lastTXBlock[_caller] = block.number;
                      return true;
                  }
              } else {
                  return true;
              }
          }
          
          function enableTXLimit() public onlyOwner {
              limitTransactions = true;
          }
          
          function disableTXLimit() public onlyOwner {
              limitTransactions = false;
          }
          
          function includeWhiteList(address _contractAddress) public onlyOwner {
              contractsWhiteList[_contractAddress] = true;
          }
          
          function removeWhiteList(address _contractAddress) public onlyOwner {
              contractsWhiteList[_contractAddress] = false;
          }
          
          function getLockedBalance(address _wallet) public view returns (uint256 lockedBalance) {
              return timeLocks[_wallet].lockedBalance;
          }
          
          function buy() public payable {
              require((block.timestamp > CROWDSALE_START) && (block.timestamp < CROWDSALE_END), "Contract is not selling tokens");
              uint weiValue = msg.value;
              require(weiValue >= (5 * (10 ** 16)), "Minimum amount is 0.05 eth");
              require(weiValue <= (20 ether), "Maximum amount is 20 eth");
              uint amount = CSALE_WEI_FACTOR * weiValue;
              require((soldOnCSale) <= (CSALE_HARDCAP), "That quantity is not available");
              soldOnCSale += amount;
              balances[salesWallet] = balances[salesWallet].sub(amount);
              balances[msg.sender] = balances[msg.sender].add(amount);
              require(salesWallet.send(weiValue));
              emit Transfer(salesWallet, msg.sender, amount);
      
          }
          
          function burnUnsold() public onlyOwner {
              require(block.timestamp > CROWDSALE_END);
              uint currentBalance = balances[salesWallet];
              balances[salesWallet] = 0;
              _totalSupply = _totalSupply.sub(currentBalance);
              emit Burn(salesWallet, currentBalance);
          }
      }

      File 2 of 2: ERC721
      /**
       *Submitted for verification at Etherscan.io on 2021-02-23
      */
      
      // SPDX-License-Identifier: MIT
      
      pragma solidity ^0.8.0;
      
      
      interface IERC165 {
          /**
           * @dev Returns true ifa 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);
      }
      interface IERC721 is IERC165 {
          /**
           * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
           */
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
      
          /**
           * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
           */
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
      
          /**
           * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
           */
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
      
          /**
           * @dev Returns the number of tokens in ``owner``'s account.
           */
          function balanceOf(address owner) external view returns (uint256 balance);
      
          /**
           * @dev Returns the owner of the `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function ownerOf(uint256 tokenId) external view returns (address owner);
      
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
           * are aware of the ERC721 protocol to prevent tokens from being forever locked.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(address from, address to, uint256 tokenId) external;
      
          /**
           * @dev Transfers `tokenId` token from `from` to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address from, address to, uint256 tokenId) external;
      
          /**
           * @dev Gives permission to `to` to transfer `tokenId` token to another account.
           * The approval is cleared when the token is transferred.
           *
           * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
           *
           * Requirements:
           *
           * - The caller must own the token or be an approved operator.
           * - `tokenId` must exist.
           *
           * Emits an {Approval} event.
           */
          function approve(address to, uint256 tokenId) external;
      
          /**
           * @dev Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
      
          /**
           * @dev Approve or remove `operator` as an operator for the caller.
           * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
           *
           * Requirements:
           *
           * - The `operator` cannot be the caller.
           *
           * Emits an {ApprovalForAll} event.
           */
          function setApprovalForAll(address operator, bool _approved) external;
      
          /**
           * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
           *
           * See {setApprovalForAll}
           */
          function isApprovedForAll(address owner, address operator) external view returns (bool);
      
          /**
            * @dev Safely transfers `tokenId` token from `from` to `to`.
            *
            * Requirements:
            *
            * - `from` cannot be the zero address.
            * - `to` cannot be the zero address.
            * - `tokenId` token must exist and be owned by `from`.
            * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
            * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
            *
            * Emits a {Transfer} event.
            */
          function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
      }
      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);
      }
      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);
      }
      interface IERC721Receiver {
          /**
           * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
           * by `operator` from `from`, this function is called.
           *
           * It must return its Solidity selector to confirm the token transfer.
           * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
           *
           * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
           */
          function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
      }
      abstract contract ERC165 is IERC165 {
          /**
           * @dev Mapping of interface ids to whether or not it's supported.
           */
          mapping(bytes4 => bool) private _supportedInterfaces;
      
          constructor () {
              // Derived contracts need only register support for their own interfaces,
              // we register support for ERC165 itself here
              _registerInterface(type(IERC165).interfaceId);
          }
      
          /**
           * @dev See {IERC165-supportsInterface}.
           *
           * Time complexity O(1), guaranteed to always use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
              return _supportedInterfaces[interfaceId];
          }
      
          /**
           * @dev Registers the contract as an implementer of the interface defined by
           * `interfaceId`. Support of the actual ERC165 interface is automatic and
           * registering its interface id is not required.
           *
           * See {IERC165-supportsInterface}.
           *
           * Requirements:
           *
           * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
           */
          function _registerInterface(bytes4 interfaceId) internal virtual {
              require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
              _supportedInterfaces[interfaceId] = true;
          }
      }
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
      
              uint256 size;
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
      
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
      
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
      
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: value }(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.staticcall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
      
          function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
      
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      library EnumerableSet {
          // To implement this library for multiple types with as little code
          // repetition as possible, we write it in terms of a generic Set type with
          // bytes32 values.
          // The Set implementation uses private functions, and user-facing
          // implementations (such as AddressSet) are just wrappers around the
          // underlying Set.
          // This means that we can only create new EnumerableSets for types that fit
          // in bytes32.
      
          struct Set {
              // Storage of set values
              bytes32[] _values;
      
              // Position of the value in the `values` array, plus 1 because index 0
              // means a value is not in the set.
              mapping (bytes32 => uint256) _indexes;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function _add(Set storage set, bytes32 value) private returns (bool) {
              if (!_contains(set, value)) {
                  set._values.push(value);
                  // The value is stored at length-1, but we add 1 to all indexes
                  // and use 0 as a sentinel value
                  set._indexes[value] = set._values.length;
                  return true;
              } else {
                  return false;
              }
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function _remove(Set storage set, bytes32 value) private returns (bool) {
              // We read and store the value's index to prevent multiple reads from the same storage slot
              uint256 valueIndex = set._indexes[value];
      
              if (valueIndex != 0) { // Equivalent to contains(set, value)
                  // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                  // the array, and then remove the last element (sometimes called as 'swap and pop').
                  // This modifies the order of the array, as noted in {at}.
      
                  uint256 toDeleteIndex = valueIndex - 1;
                  uint256 lastIndex = set._values.length - 1;
      
                  // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
                  // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
      
                  bytes32 lastvalue = set._values[lastIndex];
      
                  // Move the last value to the index where the value to delete is
                  set._values[toDeleteIndex] = lastvalue;
                  // Update the index for the moved value
                  set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
      
                  // Delete the slot where the moved value was stored
                  set._values.pop();
      
                  // Delete the index for the deleted slot
                  delete set._indexes[value];
      
                  return true;
              } else {
                  return false;
              }
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function _contains(Set storage set, bytes32 value) private view returns (bool) {
              return set._indexes[value] != 0;
          }
      
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function _length(Set storage set) private view returns (uint256) {
              return set._values.length;
          }
      
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function _at(Set storage set, uint256 index) private view returns (bytes32) {
              require(set._values.length > index, "EnumerableSet: index out of bounds");
              return set._values[index];
          }
      
          // Bytes32Set
      
          struct Bytes32Set {
              Set _inner;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _add(set._inner, value);
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _remove(set._inner, value);
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
              return _contains(set._inner, value);
          }
      
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(Bytes32Set storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
      
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
              return _at(set._inner, index);
          }
      
          // AddressSet
      
          struct AddressSet {
              Set _inner;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(AddressSet storage set, address value) internal returns (bool) {
              return _add(set._inner, bytes32(uint256(uint160(value))));
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(AddressSet storage set, address value) internal returns (bool) {
              return _remove(set._inner, bytes32(uint256(uint160(value))));
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(AddressSet storage set, address value) internal view returns (bool) {
              return _contains(set._inner, bytes32(uint256(uint160(value))));
          }
      
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(AddressSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
      
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(AddressSet storage set, uint256 index) internal view returns (address) {
              return address(uint160(uint256(_at(set._inner, index))));
          }
      
      
          // UintSet
      
          struct UintSet {
              Set _inner;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(UintSet storage set, uint256 value) internal returns (bool) {
              return _add(set._inner, bytes32(value));
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(UintSet storage set, uint256 value) internal returns (bool) {
              return _remove(set._inner, bytes32(value));
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(UintSet storage set, uint256 value) internal view returns (bool) {
              return _contains(set._inner, bytes32(value));
          }
      
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function length(UintSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
      
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(UintSet storage set, uint256 index) internal view returns (uint256) {
              return uint256(_at(set._inner, index));
          }
      }
      library EnumerableMap {
          // To implement this library for multiple types with as little code
          // repetition as possible, we write it in terms of a generic Map type with
          // bytes32 keys and values.
          // The Map implementation uses private functions, and user-facing
          // implementations (such as Uint256ToAddressMap) are just wrappers around
          // the underlying Map.
          // This means that we can only create new EnumerableMaps for types that fit
          // in bytes32.
      
          struct MapEntry {
              bytes32 _key;
              bytes32 _value;
          }
      
          struct Map {
              // Storage of map keys and values
              MapEntry[] _entries;
      
              // Position of the entry defined by a key in the `entries` array, plus 1
              // because index 0 means a key is not in the map.
              mapping (bytes32 => uint256) _indexes;
          }
      
          /**
           * @dev Adds a key-value pair to a map, or updates the value for an existing
           * key. O(1).
           *
           * Returns true if the key was added to the map, that is if it was not
           * already present.
           */
          function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
              // We read and store the key's index to prevent multiple reads from the same storage slot
              uint256 keyIndex = map._indexes[key];
      
              if (keyIndex == 0) { // Equivalent to !contains(map, key)
                  map._entries.push(MapEntry({ _key: key, _value: value }));
                  // The entry is stored at length-1, but we add 1 to all indexes
                  // and use 0 as a sentinel value
                  map._indexes[key] = map._entries.length;
                  return true;
              } else {
                  map._entries[keyIndex - 1]._value = value;
                  return false;
              }
          }
      
          /**
           * @dev Removes a key-value pair from a map. O(1).
           *
           * Returns true if the key was removed from the map, that is if it was present.
           */
          function _remove(Map storage map, bytes32 key) private returns (bool) {
              // We read and store the key's index to prevent multiple reads from the same storage slot
              uint256 keyIndex = map._indexes[key];
      
              if (keyIndex != 0) { // Equivalent to contains(map, key)
                  // To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one
                  // in the array, and then remove the last entry (sometimes called as 'swap and pop').
                  // This modifies the order of the array, as noted in {at}.
      
                  uint256 toDeleteIndex = keyIndex - 1;
                  uint256 lastIndex = map._entries.length - 1;
      
                  // When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs
                  // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
      
                  MapEntry storage lastEntry = map._entries[lastIndex];
      
                  // Move the last entry to the index where the entry to delete is
                  map._entries[toDeleteIndex] = lastEntry;
                  // Update the index for the moved entry
                  map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based
      
                  // Delete the slot where the moved entry was stored
                  map._entries.pop();
      
                  // Delete the index for the deleted slot
                  delete map._indexes[key];
      
                  return true;
              } else {
                  return false;
              }
          }
      
          /**
           * @dev Returns true if the key is in the map. O(1).
           */
          function _contains(Map storage map, bytes32 key) private view returns (bool) {
              return map._indexes[key] != 0;
          }
      
          /**
           * @dev Returns the number of key-value pairs in the map. O(1).
           */
          function _length(Map storage map) private view returns (uint256) {
              return map._entries.length;
          }
      
         /**
          * @dev Returns the key-value pair stored at position `index` in the map. O(1).
          *
          * Note that there are no guarantees on the ordering of entries inside the
          * array, and it may change when more entries are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
              require(map._entries.length > index, "EnumerableMap: index out of bounds");
      
              MapEntry storage entry = map._entries[index];
              return (entry._key, entry._value);
          }
      
          /**
           * @dev Tries to returns the value associated with `key`.  O(1).
           * Does not revert if `key` is not in the map.
           */
          function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) {
              uint256 keyIndex = map._indexes[key];
              if (keyIndex == 0) return (false, 0); // Equivalent to contains(map, key)
              return (true, map._entries[keyIndex - 1]._value); // All indexes are 1-based
          }
      
          /**
           * @dev Returns the value associated with `key`.  O(1).
           *
           * Requirements:
           *
           * - `key` must be in the map.
           */
          function _get(Map storage map, bytes32 key) private view returns (bytes32) {
              uint256 keyIndex = map._indexes[key];
              require(keyIndex != 0, "EnumerableMap: nonexistent key"); // Equivalent to contains(map, key)
              return map._entries[keyIndex - 1]._value; // All indexes are 1-based
          }
      
          /**
           * @dev Same as {_get}, with a custom error message when `key` is not in the map.
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {_tryGet}.
           */
          function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
              uint256 keyIndex = map._indexes[key];
              require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
              return map._entries[keyIndex - 1]._value; // All indexes are 1-based
          }
      
          // UintToAddressMap
      
          struct UintToAddressMap {
              Map _inner;
          }
      
          /**
           * @dev Adds a key-value pair to a map, or updates the value for an existing
           * key. O(1).
           *
           * Returns true if the key was added to the map, that is if it was not
           * already present.
           */
          function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
              return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the key was removed from the map, that is if it was present.
           */
          function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
              return _remove(map._inner, bytes32(key));
          }
      
          /**
           * @dev Returns true if the key is in the map. O(1).
           */
          function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
              return _contains(map._inner, bytes32(key));
          }
      
          /**
           * @dev Returns the number of elements in the map. O(1).
           */
          function length(UintToAddressMap storage map) internal view returns (uint256) {
              return _length(map._inner);
          }
      
         /**
          * @dev Returns the element stored at position `index` in the set. O(1).
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
              (bytes32 key, bytes32 value) = _at(map._inner, index);
              return (uint256(key), address(uint160(uint256(value))));
          }
      
          /**
           * @dev Tries to returns the value associated with `key`.  O(1).
           * Does not revert if `key` is not in the map.
           *
           * _Available since v3.4._
           */
          function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
              (bool success, bytes32 value) = _tryGet(map._inner, bytes32(key));
              return (success, address(uint160(uint256(value))));
          }
      
          /**
           * @dev Returns the value associated with `key`.  O(1).
           *
           * Requirements:
           *
           * - `key` must be in the map.
           */
          function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
              return address(uint160(uint256(_get(map._inner, bytes32(key)))));
          }
      
          /**
           * @dev Same as {get}, with a custom error message when `key` is not in the map.
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {tryGet}.
           */
          function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {
              return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage))));
          }
      }
      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;
              temp = value;
              while (temp != 0) {
                  buffer[--index] = bytes1(uint8(48 + uint256(temp % 10)));
                  temp /= 10;
              }
              return string(buffer);
          }
      }
      library Counters {
          struct Counter {
              // This variable should never be directly accessed by users of the library: interactions must be restricted to
              // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
              // this feature: see https://github.com/ethereum/solidity/issues/4637
              uint256 _value; // default: 0
          }
      
          function current(Counter storage counter) internal view returns (uint256) {
              return counter._value;
          }
      
          function increment(Counter storage counter) internal {
              unchecked {
                  counter._value += 1;
              }
          }
      
          function decrement(Counter storage counter) internal {
              uint256 value = counter._value;
              require(value > 0, "Counter: decrement overflow");
              unchecked {
                  counter._value = value - 1;
              }
          }
      }
      contract Ownable {
      
          address private owner;
          
          event OwnerSet(address indexed oldOwner, address indexed newOwner);
          
          modifier onlyOwner() {
              require(msg.sender == owner, "Caller is not owner");
              _;
          }
      
          constructor() {
              owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor
              emit OwnerSet(address(0), owner);
          }
      
      
          function changeOwner(address newOwner) public onlyOwner {
              emit OwnerSet(owner, newOwner);
              owner = newOwner;
          }
      
          function getOwner() external view returns (address) {
              return owner;
          }
      }
      
      contract ERC721 is ERC165, IERC721, IERC721Metadata, IERC721Enumerable, Ownable {
          using Address for address;
          using EnumerableSet for EnumerableSet.UintSet;
          using EnumerableMap for EnumerableMap.UintToAddressMap;
          using Strings for uint256;
          using Counters for Counters.Counter;
      
          // Map the selling contracts that can mint tokens
          mapping (address => bool) public minters;
          
          // Contract that calculates the stake profits
          address public profitsContract;
          
          // 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
          EnumerableMap.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;
          
          struct Coin {
              address tokenAddress;
              string symbol;
              string name;
          }
          
          mapping (uint256 => Coin) public tradeCoins;
          
          struct assetType {
              uint64 maxAmount;
              uint64 mintedAmount;
              uint64 coinIndex;
              string copyright;
          }
          
          mapping (uint256 => assetType) public assetsByType;
          
          struct assetDetail {
              uint256 value;
              uint32 lastTrade;
              uint32 lastPayment;
              uint32 typeDetail;
              uint32 customDetails;
          }
          
          mapping (uint256 => assetDetail) assetsDetails;
          address public sellingContract;
          uint256 public polkaCitizens = 0;
      
          // Token name
          string private _name;
      
          // Token symbol
          string private _symbol;
      
          // Base URI
          string private _baseURI;
          
          Counters.Counter private _tokenIdTracker;
      
          /**
           * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
           */
          constructor () {
              _name ="Polka City 3D Asset";
              _symbol = "PC3D";
              _baseURI = "https://polkacity.app/3dnftassets/";
              
              // include ETH as coin
              
              tradeCoins[1].tokenAddress = address(0x0);
              tradeCoins[1].symbol = "ETH";
              tradeCoins[1].name = "Ethereum";
              
              // include POLC as coin
              
              tradeCoins[2].tokenAddress = 0x0daD676DA510e71e31464A765a8548b47f47bdad;
              tradeCoins[2].symbol = "POLC";
              tradeCoins[2].name = "Polka City Token";
              
              // register the supported interfaces to conform to ERC721 via ERC165
              _registerInterface(type(IERC721).interfaceId);
              _registerInterface(type(IERC721Metadata).interfaceId);
              _registerInterface(type(IERC721Enumerable).interfaceId);
          }
      
          function initAssets(uint64 _assetType, uint64 _maxAmount, uint64 _coinIndex, string memory _copyright) private {
              assetsByType[_assetType].maxAmount = _maxAmount;
              assetsByType[_assetType].mintedAmount = 0;
              assetsByType[_assetType].coinIndex = _coinIndex;
              assetsByType[_assetType].copyright = _copyright;
          }
          
          /**
           * @dev See {IERC721-balanceOf}.
           */
          function balanceOf(address _owner) public view virtual override returns (uint256) {
              require(_owner != address(0), "ERC721: balance query for the zero address");
              return _holderTokens[_owner].length();
          }
      
          /**
           * @dev See {IERC721-ownerOf}.
           */
          function ownerOf(uint256 tokenId) public view virtual override returns (address) {
              return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
          }
      
          /**
           * @dev See {IERC721Metadata-name}.
           */
          function name() public view virtual override returns (string memory) {
              return _name;
          }
      
          /**
           * @dev See {IERC721Metadata-symbol}.
           */
          function symbol() public view virtual override returns (string memory) {
              return _symbol;
          }
      
          /**
           * @dev See {IERC721Metadata-tokenURI}.
           */
          function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
              require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
              string memory base = baseURI();
              return string(abi.encodePacked(base,( uint256(assetsDetails[tokenId].customDetails).toString())));
          }
      
          function baseURI() public view virtual returns (string memory) {
              return _baseURI;
          }
      
          /**
           * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
           */
          function tokenOfOwnerByIndex(address _owner, uint256 index) public view virtual override returns (uint256) {
              return _holderTokens[_owner].at(index);
          }
      
          /**
           * @dev See {IERC721Enumerable-totalSupply}.
           */
          function totalSupply() public view virtual override returns (uint256) {
              // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
              return _tokenOwners.length();
          }
      
          /**
           * @dev See {IERC721Enumerable-tokenByIndex}.
           */
          function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
              (uint256 tokenId, ) = _tokenOwners.at(index);
              return (tokenId);
          }
          
          function getTokenDetails(uint256 index) public view returns (uint32 aType, uint32 customDetails, uint32 lastTx, uint32 lastPayment, uint256 initialvalue, string memory coin) {
              uint256 coinIndex = uint256(assetsByType[(assetsDetails[index].typeDetail)].coinIndex);
              
              return ( 
              assetsDetails[index].typeDetail, 
              assetsDetails[index].customDetails, 
              assetsDetails[index].lastTrade, 
              assetsDetails[index].lastPayment,
              assetsDetails[index].value,
              tradeCoins[coinIndex].symbol
              );
          }
          
      
          /**
           * @dev See {IERC721-approve}.
           */
          function approve(address to, uint256 tokenId) public virtual override {
              address _owner = ERC721.ownerOf(tokenId);
              require(to != _owner, "ERC721: approval to current owner");
      
              require(msg.sender == _owner || ERC721.isApprovedForAll(_owner, msg.sender),
                  "ERC721: approve caller is not owner nor approved for all"
              );
      
              _approve(to, tokenId);
          }
      
          /**
           * @dev See {IERC721-getApproved}.
           */
          function getApproved(uint256 tokenId) public view virtual override returns (address) {
              require(_exists(tokenId), "ERC721: approved query for nonexistent token");
      
              return _tokenApprovals[tokenId];
          }
      
          /**
           * @dev See {IERC721-setApprovalForAll}.
           */
          function setApprovalForAll(address operator, bool approved) public virtual override {
              require(operator != msg.sender, "ERC721: approve to caller");
      
              _operatorApprovals[msg.sender][operator] = approved;
              emit ApprovalForAll(msg.sender, operator, approved);
          }
      
          /**
           * @dev See {IERC721-isApprovedForAll}.
           */
          function isApprovedForAll(address _owner, address operator) public view virtual override returns (bool) {
              return _operatorApprovals[_owner][operator];
          }
      
          /**
           * @dev See {IERC721-transferFrom}.
           */
          function transferFrom(address from, address to, uint256 tokenId) public virtual override {
              //solhint-disable-next-line max-line-length
              require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
      
              _transfer(from, to, tokenId);
          }
      
          /**
           * @dev See {IERC721-safeTransferFrom}.
           */
          function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
              safeTransferFrom(from, to, tokenId, "");
          }
      
          /**
           * @dev See {IERC721-safeTransferFrom}.
           */
          function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
              require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
              _safeTransfer(from, to, tokenId, _data);
          }
      
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
           * are aware of the ERC721 protocol to prevent tokens from being forever locked.
           *
           * `_data` is additional data, it has no specified format and it is sent in call to `to`.
           *
           * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
           * implement alternative mechanisms to perform token transfer, such as signature-based.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
              _transfer(from, to, tokenId);
              require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
          }
      
          /**
           * @dev Returns whether `tokenId` exists.
           *
           * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
           *
           * Tokens start existing when they are minted (`_mint`),
           * and stop existing when they are burned (`_burn`).
           */
          function _exists(uint256 tokenId) internal view virtual returns (bool) {
              return _tokenOwners.contains(tokenId);
          }
      
          /**
           * @dev Returns whether `spender` is allowed to manage `tokenId`.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
              require(_exists(tokenId), "ERC721: operator query for nonexistent token");
              address _owner = ERC721.ownerOf(tokenId);
              return (spender == _owner || getApproved(tokenId) == spender || ERC721.isApprovedForAll(_owner, spender));
          }
      
          /**
           * @dev Safely mints `tokenId` and transfers it to `to`.
           *
           * Requirements:
           d*
           * - `tokenId` must not exist.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function _safeMint(address to, uint256 tokenId) internal virtual {
              _safeMint(to, tokenId, "");
          }
      
          /**
           * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
           * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
           */
          function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
              _mint(to, tokenId);
              require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
          }
      
          /**
           * @dev Mints `tokenId` and transfers it to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
           *
           * Requirements:
           *
           * - `tokenId` must not exist.
           * - `to` cannot be the zero address.
           *
           * Emits a {Transfer} event.
           */
          function _mint(address to, uint256 tokenId) internal virtual {
              require(to != address(0), "ERC721: mint to the zero address");
              require(!_exists(tokenId), "ERC721: token already minted");
      
              _beforeTokenTransfer(address(0), to, tokenId);
      
              _holderTokens[to].add(tokenId);
      
              _tokenOwners.set(tokenId, to);
      
              emit Transfer(address(0), to, tokenId);
          }
      
      
          /**
           * @dev Transfers `tokenId` from `from` to `to`.
           *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - `tokenId` token must be owned by `from`.
           *
           * Emits a {Transfer} event.
           */
          function _transfer(address from, address to, uint256 tokenId) internal virtual {
              require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); // internal owner
              require(to != address(0), "ERC721: transfer to the zero address");
      
              _beforeTokenTransfer(from, to, tokenId);
      
              // Clear approvals from the previous owner
              _approve(address(0), tokenId);
      
              _holderTokens[from].remove(tokenId);
              _holderTokens[to].add(tokenId);
      
              _tokenOwners.set(tokenId, to);
              assetsDetails[tokenId].lastTrade = uint32(block.timestamp);
              assetsDetails[tokenId].lastPayment = uint32(block.timestamp);
              checkCitizen(to, true);
              checkCitizen(from, false);
      
              emit Transfer(from, to, tokenId);
          }
      
      
          function setBaseURI(string memory baseURI_) public onlyOwner {
              _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()) {
                  try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data) returns (bytes4 retval) {
                      return retval == IERC721Receiver(to).onERC721Received.selector;
                  } catch (bytes memory reason) {
                      if (reason.length == 0) {
                          revert("ERC721: transfer to non ERC721Receiver implementer");
                      } else {
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              revert(add(32, reason), mload(reason))
                          }
                      }
                  }
              } else {
                  return true;
              }
          }
      
          function _approve(address to, uint256 tokenId) private {
              _tokenApprovals[tokenId] = to;
              emit Approval(ERC721.ownerOf(tokenId), to, tokenId); // internal owner
          }
      
          /**
           * @dev Hook that is called before any token transfer. This includes minting
           * and burning.
           *
           * Calling conditions:
           *
           * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
           * transferred to `to`.
           * - When `from` is zero, `tokenId` will be minted for `to`.
           * - When `to` is zero, ``from``'s `tokenId` will be burned.
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
          
          function checkCitizen(address _citizen, bool _addAsset) private {
              uint256 citizenBalance = _holderTokens[_citizen].length();
              if (citizenBalance > 0) {
                  if (_addAsset == true && citizenBalance == 1) {
                      polkaCitizens++;
                  }
              } else {
                  polkaCitizens--;
              }
          }
          
          function mint(address to, uint32 _assetType, uint256 _value, uint32 _customDetails) public virtual returns (bool success) {
              require(minters[msg.sender] == true, "Not allowed");
              require(assetsByType[_assetType].maxAmount > assetsByType[_assetType].mintedAmount, "Max mintable amount reached for this asset" );
              uint256 curIndex = _tokenIdTracker.current();
              _mint(to, curIndex);
              assetsDetails[curIndex].typeDetail = _assetType;
              assetsDetails[curIndex].value = _value;
              assetsDetails[curIndex].lastTrade = uint32(block.timestamp);
              assetsDetails[curIndex].lastPayment = uint32(block.timestamp);
              assetsDetails[curIndex].customDetails = _customDetails;
              assetsByType[_assetType].mintedAmount += 1;
              _tokenIdTracker.increment();
              checkCitizen(to, true);
              return true;
          }
          
          function setMinter(address _minterAddress, bool _canMint) public onlyOwner {
              minters[_minterAddress] = _canMint;
          }
          
          function setProfitsContract(address _contract) public onlyOwner {
              profitsContract = _contract;
          }
          
          function setPaymentDate(uint256 _asset) public {
              require(msg.sender == profitsContract);
              assetsDetails[_asset].lastPayment = uint32(block.timestamp);
          }
          
          function addAssetType(uint64 _assetType, uint64 _maxAmount, uint64 _coinIndex, string memory _copyright) public onlyOwner {
              require(_maxAmount > 0);
              initAssets( _assetType, _maxAmount, _coinIndex, _copyright);
          }
          
          function modifyAssetType(uint64 _typeId, uint64 _maxAmount, uint64 _coinIndex, string memory _copyright) public onlyOwner {
              assetsByType[_typeId].copyright = _copyright;
              assetsByType[_typeId].maxAmount = _maxAmount;
              assetsByType[_typeId].coinIndex = _coinIndex;
          }
          
          function fixAsset(uint256 _assetId, uint32 _customDetails) public {
              require(minters[msg.sender] == true, "Not allowed");
              assetsDetails[_assetId].customDetails = _customDetails;
          }
          
          function addCoin(uint256 _coinIndex, address _tokenAddress, string memory _tokenSymbol, string memory _tokenName) public onlyOwner {
              tradeCoins[_coinIndex].tokenAddress = _tokenAddress;
              tradeCoins[_coinIndex].symbol = _tokenSymbol;
              tradeCoins[_coinIndex].name = _tokenName;
          }
      
          
      }