ETH Price: $2,083.09 (-3.02%)

Transaction Decoder

Block:
10352223 at Jun-28-2020 04:55:16 AM +UTC
Transaction Fee:
0.004509246 ETH $9.39
Gas Used:
214,726 Gas / 21 Gwei

Emitted Events:

146 PoapDelegatedMint.VerifiedSignature( _signedMessage=0xAA48B9ABFA6E5212BC0E03CA7CBF2976FB2647795001FE6811E8407F4469E08A174AFA0CF69CC813E6C2BE5DF39BD04D3458FBD5D8B69F195A121CE5BF2A82341B )
147 AdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x00000000000000000000000083bb781a2a2ca1fec0350f178c911848811cc440, 0x000000000000000000000000000000000000000000000000000000000000351c )
148 AdminUpgradeabilityProxy.0x4b3711cd7ece062b0828c1b6e08d814a72d4c003383a016c833cbb1b45956e34( 0x4b3711cd7ece062b0828c1b6e08d814a72d4c003383a016c833cbb1b45956e34, 0000000000000000000000000000000000000000000000000000000000000105, 000000000000000000000000000000000000000000000000000000000000351c )

Account State Difference:

  Address   Before After State Difference Code
0x22C1f605...fEf83B415
(Spark Pool)
73.059567278370952674 Eth73.064076524370952674 Eth0.004509246
0x83bb781a...8811cC440
0.206806229164688242 Eth
Nonce: 16437
0.202296983164688242 Eth
Nonce: 16438
0.004509246
0xAac24971...db8a05337

Execution Trace

PoapDelegatedMint.mintToken( event_id=261, receiver=0x83bb781a2a2Ca1fec0350F178C911848811cC440, signedMessage=0xAA48B9ABFA6E5212BC0E03CA7CBF2976FB2647795001FE6811E8407F4469E08A174AFA0CF69CC813E6C2BE5DF39BD04D3458FBD5D8B69F195A121CE5BF2A82341B ) => ( True )
  • Null: 0x000...001.e67b1aeb( )
  • AdminUpgradeabilityProxy.a140ae23( )
    • Poap.mintToken( eventId=261, to=0x83bb781a2a2Ca1fec0350F178C911848811cC440 ) => ( True )
      File 1 of 3: PoapDelegatedMint
      pragma solidity ^0.5.15;
      
      contract Context {
          // Empty internal constructor, to prevent people from mistakenly deploying
          // an instance of this contract, which should be used via inheritance.
          constructor () internal { }
          // solhint-disable-previous-line no-empty-blocks
      
          function _msgSender() internal view returns (address payable) {
              return msg.sender;
          }
      
          function _msgData() internal view returns (bytes memory) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
      
      library Counters {
          using SafeMath for uint256;
      
          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 {
              // The {SafeMath} overflow check can be skipped here, see the comment at the top
              counter._value += 1;
          }
      
          function decrement(Counter storage counter) internal {
              counter._value = counter._value.sub(1);
          }
      }
      
      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);
      }
      
      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) external view 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 {
              require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
              _supportedInterfaces[interfaceId] = true;
          }
      }
      
      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.
           *
           * _Available since v2.4.0._
           */
          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.
           *
           * _Available since v2.4.0._
           */
          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.
           *
           * _Available since v2.4.0._
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      
      contract IERC721 is IERC165 {
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
      
          /**
           * @dev Returns the number of NFTs in `owner`'s account.
           */
          function balanceOf(address owner) public view returns (uint256 balance);
      
          /**
           * @dev Returns the owner of the NFT specified by `tokenId`.
           */
          function ownerOf(uint256 tokenId) public view returns (address owner);
      
          /**
           * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
           * another (`to`).
           *
           *
           *
           * Requirements:
           * - `from`, `to` cannot be zero.
           * - `tokenId` must be owned by `from`.
           * - If the caller is not `from`, it must be have been allowed to move this
           * NFT by either {approve} or {setApprovalForAll}.
           */
          function safeTransferFrom(address from, address to, uint256 tokenId) public;
          /**
           * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
           * another (`to`).
           *
           * Requirements:
           * - If the caller is not `from`, it must be approved to move this NFT by
           * either {approve} or {setApprovalForAll}.
           */
          function transferFrom(address from, address to, uint256 tokenId) public;
          function approve(address to, uint256 tokenId) public;
          function getApproved(uint256 tokenId) public view returns (address operator);
      
          function setApprovalForAll(address operator, bool _approved) public;
          function isApprovedForAll(address owner, address operator) public view returns (bool);
      
      
          function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
      }
      
      contract ERC721 is Context, ERC165, IERC721 {
          using SafeMath for uint256;
          using Address for address;
          using Counters for Counters.Counter;
      
          // 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 token ID to owner
          mapping (uint256 => address) private _tokenOwner;
      
          // Mapping from token ID to approved address
          mapping (uint256 => address) private _tokenApprovals;
      
          // Mapping from owner to number of owned token
          mapping (address => Counters.Counter) private _ownedTokensCount;
      
          // Mapping from owner to operator approvals
          mapping (address => mapping (address => bool)) private _operatorApprovals;
      
          /*
           *     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;
      
          constructor () public {
              // register the supported interfaces to conform to ERC721 via ERC165
              _registerInterface(_INTERFACE_ID_ERC721);
          }
      
          /**
           * @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 returns (uint256) {
              require(owner != address(0), "ERC721: balance query for the zero address");
      
              return _ownedTokensCount[owner].current();
          }
      
          /**
           * @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 returns (address) {
              address owner = _tokenOwner[tokenId];
              require(owner != address(0), "ERC721: owner query for nonexistent token");
      
              return owner;
          }
      
          /**
           * @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 {
              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"
              );
      
              _tokenApprovals[tokenId] = to;
              emit Approval(owner, 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 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 to operator address to set the approval
           * @param approved representing the status of the approval to be set
           */
          function setApprovalForAll(address to, bool approved) public {
              require(to != _msgSender(), "ERC721: approve to caller");
      
              _operatorApprovals[_msgSender()][to] = approved;
              emit ApprovalForAll(_msgSender(), to, 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 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 {
              //solhint-disable-next-line max-line-length
              require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
      
              _transferFrom(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 {
              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 {
              require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
              _safeTransferFrom(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 _safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) internal {
              _transferFrom(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) {
              address owner = _tokenOwner[tokenId];
              return owner != address(0);
          }
      
          /**
           * @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 {
              _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 {
              _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 {
              require(to != address(0), "ERC721: mint to the zero address");
              require(!_exists(tokenId), "ERC721: token already minted");
      
              _tokenOwner[tokenId] = to;
              _ownedTokensCount[to].increment();
      
              emit Transfer(address(0), to, tokenId);
          }
      
          /**
           * @dev Internal function to burn a specific token.
           * Reverts if the token does not exist.
           * Deprecated, use {_burn} instead.
           * @param owner owner of the token to burn
           * @param tokenId uint256 ID of the token being burned
           */
          function _burn(address owner, uint256 tokenId) internal {
              require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own");
      
              _clearApproval(tokenId);
      
              _ownedTokensCount[owner].decrement();
              _tokenOwner[tokenId] = address(0);
      
              emit Transfer(owner, address(0), 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 {
              _burn(ownerOf(tokenId), 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 _transferFrom(address from, address to, uint256 tokenId) internal {
              require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
              require(to != address(0), "ERC721: transfer to the zero address");
      
              _clearApproval(tokenId);
      
              _ownedTokensCount[from].decrement();
              _ownedTokensCount[to].increment();
      
              _tokenOwner[tokenId] = to;
      
              emit Transfer(from, to, tokenId);
          }
      
          /**
           * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
           * The call is not executed if the target address is not a contract.
           *
           * This is an internal detail of the `ERC721` contract and its use is deprecated.
           * @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)
              internal 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);
              }
          }
      
          /**
           * @dev Private function to clear current approval of a given token ID.
           * @param tokenId uint256 ID of the token to be transferred
           */
          function _clearApproval(uint256 tokenId) private {
              if (_tokenApprovals[tokenId] != address(0)) {
                  _tokenApprovals[tokenId] = address(0);
              }
          }
      }
      
      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 returns (bytes4);
      }
      
      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 Converts an `address` into `address payable`. Note that this is
           * simply a type cast: the actual underlying value is not changed.
           *
           * _Available since v2.4.0._
           */
          function toPayable(address account) internal pure returns (address payable) {
              return address(uint160(account));
          }
      
          /**
           * @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].
           *
           * _Available since v2.4.0._
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
      
              // solhint-disable-next-line avoid-call-value
              (bool success, ) = recipient.call.value(amount)("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
      }
      
      contract Poap is ERC721 {
          event EventToken(uint256 eventId, uint256 tokenId);
      
          // Last Used id (used to generate new ids)
          uint256 private lastId;
      
          // EventId for each token
          mapping(uint256 => uint256) private _tokenEvent;
      
          /**
           * @dev Function to mint tokens
           * @param eventId EventId for the new token
           * @param tokenId The token id to mint.
           * @param to The address that will receive the minted tokens.
           * @return A boolean that indicates if the operation was successful.
           */
          function _mintToken(uint256 eventId, uint256 tokenId, address to) internal returns (bool) {
              _mint(to, tokenId);
              _tokenEvent[tokenId] = eventId;
              emit EventToken(eventId, tokenId);
              return true;
          }
      
          /**
           * @dev Function to mint tokens
           * @param eventId EventId for the new token
           * @param to The address that will receive the minted tokens.
           * @return A boolean that indicates if the operation was successful.
           */
          function mintToken(uint256 eventId, address to) public returns (bool) {
              lastId += 1;
              return _mintToken(eventId, lastId, to);
          }
      }
      
      contract PoapDelegatedMint {
      
          event VerifiedSignature(
              bytes _signedMessage
          );
      
          string public name = "POAP Delegated Mint";
      
          // POAP Contract - Only Mint Token function
          Poap POAPToken;
      
          // Contract creator
          address public owner;
      
          // POAP valid token minter
          address public validSigner;
      
          // Processed signatures
          mapping(bytes => bool) public processed;
      
          constructor (address _poapContractAddress, address _validSigner) public{
              validSigner = _validSigner;
              POAPToken = Poap(_poapContractAddress);
              owner = msg.sender;
          }
      
          function _recoverSigner(bytes32 message, bytes memory signature) private pure returns (address) {
              uint8 v;
              bytes32 r;
              bytes32 s;
      
              (v, r, s) = _splitSignature(signature);
              return ecrecover(message, v, r, s);
          }
      
          function _splitSignature(bytes memory signature) private pure returns (uint8, bytes32, bytes32) {
              require(signature.length == 65);
      
              bytes32 r;
              bytes32 s;
              uint8 v;
      
              assembly {
              // first 32 bytes, after the length prefix
                  r := mload(add(signature, 32))
              // second 32 bytes
                  s := mload(add(signature, 64))
              // final byte (first byte of the next 32 bytes)
                  v := byte(0, mload(add(signature, 96)))
              }
      
              return (v, r, s);
          }
      
          function _isValidData(uint256 _event_id, address _receiver, bytes memory _signed_message) private view returns(bool) {
              bytes32 message = keccak256(abi.encodePacked(_event_id, _receiver));
              return (_recoverSigner(message, _signed_message) == validSigner);
          }
      
          function mintToken(uint256 event_id, address receiver, bytes memory signedMessage) public returns (bool) {
              // Check that the signature is valid
              require(_isValidData(event_id, receiver, signedMessage), "Invalid signed message");
              // Check that the signature was not already processed
              require(processed[signedMessage] == false, "Signature already processed");
      
              processed[signedMessage] = true;
              emit VerifiedSignature(signedMessage);
              return POAPToken.mintToken(event_id, receiver);
          }
      }

      File 2 of 3: AdminUpgradeabilityProxy
      /**
       * @title ImplementationProvider
       * @dev Interface for providing implementation addresses for other contracts by name.
       */
      interface ImplementationProvider {
        /**
         * @dev Abstract function to return the implementation address of a contract.
         * @param contractName Name of the contract.
         * @return Implementation address of the contract.
         */
        function getImplementation(string contractName) public view returns (address);
      }
      
      // File: zos-lib/contracts/ownership/Ownable.sol
      
      
      /**
       * @title Ownable
       * @dev The Ownable contract has an owner address, and provides basic authorization control
       * functions, this simplifies the implementation of "user permissions".
       *
       * Source: https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.0.0/contracts/ownership/Ownable.sol
       * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
       * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
       * build/artifacts folder) as well as the vanilla Ownable implementation from an openzeppelin version.
       */
      contract ZOSLibOwnable {
        address private _owner;
      
        event OwnershipTransferred(
          address indexed previousOwner,
          address indexed newOwner
        );
      
        /**
         * @dev The Ownable constructor sets the original `owner` of the contract to the sender
         * account.
         */
        constructor() internal {
          _owner = msg.sender;
          emit OwnershipTransferred(address(0), _owner);
        }
      
        /**
         * @return the address of the owner.
         */
        function owner() public view returns(address) {
          return _owner;
        }
      
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
          require(isOwner());
          _;
        }
      
        /**
         * @return true if `msg.sender` is the owner of the contract.
         */
        function isOwner() public view returns(bool) {
          return msg.sender == _owner;
        }
      
        /**
         * @dev Allows the current owner to relinquish control of the contract.
         * @notice Renouncing to ownership will leave the contract without an owner.
         * It will not be possible to call the functions with the `onlyOwner`
         * modifier anymore.
         */
        function renounceOwnership() public onlyOwner {
          emit OwnershipTransferred(_owner, address(0));
          _owner = address(0);
        }
      
        /**
         * @dev Allows the current owner to transfer control of the contract to a newOwner.
         * @param newOwner The address to transfer ownership to.
         */
        function transferOwnership(address newOwner) public onlyOwner {
          _transferOwnership(newOwner);
        }
      
        /**
         * @dev Transfers control of the contract to a newOwner.
         * @param newOwner The address to transfer ownership to.
         */
        function _transferOwnership(address newOwner) internal {
          require(newOwner != address(0));
          emit OwnershipTransferred(_owner, newOwner);
          _owner = newOwner;
        }
      }
      
      // File: zos-lib/contracts/application/Package.sol
      
      
      
      /**
       * @title Package
       * @dev A package is composed by a set of versions, identified via semantic versioning,
       * where each version has a contract address that refers to a reusable implementation,
       * plus an optional content URI with metadata. Note that the semver identifier is restricted
       * to major, minor, and patch, as prerelease tags are not supported.
       */
      contract Package is ZOSLibOwnable {
        /**
         * @dev Emitted when a version is added to the package.
         * @param semanticVersion Name of the added version.
         * @param contractAddress Contract associated with the version.
         * @param contentURI Optional content URI with metadata of the version.
         */
        event VersionAdded(uint64[3] semanticVersion, address contractAddress, bytes contentURI);
      
        struct Version {
          uint64[3] semanticVersion;
          address contractAddress;
          bytes contentURI; 
        }
      
        mapping (bytes32 => Version) internal versions;
        mapping (uint64 => bytes32) internal majorToLatestVersion;
        uint64 internal latestMajor;
      
        /**
         * @dev Returns a version given its semver identifier.
         * @param semanticVersion Semver identifier of the version.
         * @return Contract address and content URI for the version, or zero if not exists.
         */
        function getVersion(uint64[3] semanticVersion) public view returns (address contractAddress, bytes contentURI) {
          Version storage version = versions[semanticVersionHash(semanticVersion)];
          return (version.contractAddress, version.contentURI); 
        }
      
        /**
         * @dev Returns a contract for a version given its semver identifier.
         * This method is equivalent to `getVersion`, but returns only the contract address.
         * @param semanticVersion Semver identifier of the version.
         * @return Contract address for the version, or zero if not exists.
         */
        function getContract(uint64[3] semanticVersion) public view returns (address contractAddress) {
          Version storage version = versions[semanticVersionHash(semanticVersion)];
          return version.contractAddress;
        }
      
        /**
         * @dev Adds a new version to the package. Only the Owner can add new versions.
         * Reverts if the specified semver identifier already exists. 
         * Emits a `VersionAdded` event if successful.
         * @param semanticVersion Semver identifier of the version.
         * @param contractAddress Contract address for the version, must be non-zero.
         * @param contentURI Optional content URI for the version.
         */
        function addVersion(uint64[3] semanticVersion, address contractAddress, bytes contentURI) public onlyOwner {
          require(contractAddress != address(0), "Contract address is required");
          require(!hasVersion(semanticVersion), "Given version is already registered in package");
          require(!semanticVersionIsZero(semanticVersion), "Version must be non zero");
      
          // Register version
          bytes32 versionId = semanticVersionHash(semanticVersion);
          versions[versionId] = Version(semanticVersion, contractAddress, contentURI);
          
          // Update latest major
          uint64 major = semanticVersion[0];
          if (major > latestMajor) {
            latestMajor = semanticVersion[0];
          }
      
          // Update latest version for this major
          uint64 minor = semanticVersion[1];
          uint64 patch = semanticVersion[2];
          uint64[3] latestVersionForMajor = versions[majorToLatestVersion[major]].semanticVersion;
          if (semanticVersionIsZero(latestVersionForMajor) // No latest was set for this major
             || (minor > latestVersionForMajor[1]) // Or current minor is greater 
             || (minor == latestVersionForMajor[1] && patch > latestVersionForMajor[2]) // Or current patch is greater
             ) { 
            majorToLatestVersion[major] = versionId;
          }
      
          emit VersionAdded(semanticVersion, contractAddress, contentURI);
        }
      
        /**
         * @dev Checks whether a version is present in the package.
         * @param semanticVersion Semver identifier of the version.
         * @return true if the version is registered in this package, false otherwise.
         */
        function hasVersion(uint64[3] semanticVersion) public view returns (bool) {
          Version storage version = versions[semanticVersionHash(semanticVersion)];
          return address(version.contractAddress) != address(0);
        }
      
        /**
         * @dev Returns the version with the highest semver identifier registered in the package.
         * For instance, if `1.2.0`, `1.3.0`, and `2.0.0` are present, will always return `2.0.0`, regardless 
         * of the order in which they were registered. Returns zero if no versions are registered.
         * @return Semver identifier, contract address, and content URI for the version, or zero if not exists.
         */
        function getLatest() public view returns (uint64[3] semanticVersion, address contractAddress, bytes contentURI) {
          return getLatestByMajor(latestMajor);
        }
      
        /**
         * @dev Returns the version with the highest semver identifier for the given major.
         * For instance, if `1.2.0`, `1.3.0`, and `2.0.0` are present, will return `1.3.0` for major `1`, 
         * regardless of the order in which they were registered. Returns zero if no versions are registered
         * for the specified major.
         * @param major Major identifier to query
         * @return Semver identifier, contract address, and content URI for the version, or zero if not exists.
         */
        function getLatestByMajor(uint64 major) public view returns (uint64[3] semanticVersion, address contractAddress, bytes contentURI) {
          Version storage version = versions[majorToLatestVersion[major]];
          return (version.semanticVersion, version.contractAddress, version.contentURI); 
        }
      
        function semanticVersionHash(uint64[3] version) internal pure returns (bytes32) {
          return keccak256(abi.encodePacked(version[0], version[1], version[2]));
        }
      
        function semanticVersionIsZero(uint64[3] version) internal pure returns (bool) {
          return version[0] == 0 && version[1] == 0 && version[2] == 0;
        }
      }
      
      // File: zos-lib/contracts/upgradeability/Proxy.sol
      
      
      /**
       * @title Proxy
       * @dev Implements delegation of calls to other contracts, with proper
       * forwarding of return values and bubbling of failures.
       * It defines a fallback function that delegates all calls to the address
       * returned by the abstract _implementation() internal function.
       */
      contract Proxy {
        /**
         * @dev Fallback function.
         * Implemented entirely in `_fallback`.
         */
        function () payable external {
          _fallback();
        }
      
        /**
         * @return The Address of the implementation.
         */
        function _implementation() internal view returns (address);
      
        /**
         * @dev Delegates execution to an implementation contract.
         * This is a low level function that doesn't return to its internal call site.
         * It will return to the external caller whatever the implementation returns.
         * @param implementation Address to delegate.
         */
        function _delegate(address implementation) internal {
          assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize)
      
            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
      
            // Copy the returned data.
            returndatacopy(0, 0, returndatasize)
      
            switch result
            // delegatecall returns 0 on error.
            case 0 { revert(0, returndatasize) }
            default { return(0, returndatasize) }
          }
        }
      
        /**
         * @dev Function that is run as the first thing in the fallback function.
         * Can be redefined in derived contracts to add functionality.
         * Redefinitions must call super._willFallback().
         */
        function _willFallback() internal {
        }
      
        /**
         * @dev fallback implementation.
         * Extracted to enable manual triggering.
         */
        function _fallback() internal {
          _willFallback();
          _delegate(_implementation());
        }
      }
      
      // File: zos-lib/contracts/utils/Address.sol
      
      
      /**
       * Utility library of inline functions on addresses
       *
       * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.0.0/contracts/utils/Address.sol
       * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
       * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
       * build/artifacts folder) as well as the vanilla Address implementation from an openzeppelin version.
       */
      library ZOSLibAddress {
      
        /**
         * Returns whether the target address is a contract
         * @dev This function will return false if invoked during the constructor of a contract,
         * as the code is not actually created until after the constructor finishes.
         * @param account address of the account to check
         * @return whether the target address is a contract
         */
        function isContract(address account) internal view returns (bool) {
          uint256 size;
          // XXX Currently there is no better way to check if there is a contract in an address
          // than to check the size of the code at that address.
          // See https://ethereum.stackexchange.com/a/14016/36603
          // for more details about how this works.
          // TODO Check this again before the Serenity release, because all addresses will be
          // contracts then.
          // solium-disable-next-line security/no-inline-assembly
          assembly { size := extcodesize(account) }
          return size > 0;
        }
      
      }
      
      // File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol
      
      
      
      
      /**
       * @title UpgradeabilityProxy
       * @dev This contract implements a proxy that allows to change the
       * implementation address to which it will delegate.
       * Such a change is called an implementation upgrade.
       */
      contract UpgradeabilityProxy is Proxy {
        /**
         * @dev Emitted when the implementation is upgraded.
         * @param implementation Address of the new implementation.
         */
        event Upgraded(address indexed implementation);
      
        /**
         * @dev Storage slot with the address of the current implementation.
         * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
         * validated in the constructor.
         */
        bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
      
        /**
         * @dev Contract constructor.
         * @param _implementation Address of the initial implementation.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _implementation, bytes _data) public payable {
          assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
          _setImplementation(_implementation);
          if(_data.length > 0) {
            require(_implementation.delegatecall(_data));
          }
        }
      
        /**
         * @dev Returns the current implementation.
         * @return Address of the current implementation
         */
        function _implementation() internal view returns (address impl) {
          bytes32 slot = IMPLEMENTATION_SLOT;
          assembly {
            impl := sload(slot)
          }
        }
      
        /**
         * @dev Upgrades the proxy to a new implementation.
         * @param newImplementation Address of the new implementation.
         */
        function _upgradeTo(address newImplementation) internal {
          _setImplementation(newImplementation);
          emit Upgraded(newImplementation);
        }
      
        /**
         * @dev Sets the implementation address of the proxy.
         * @param newImplementation Address of the new implementation.
         */
        function _setImplementation(address newImplementation) private {
          require(ZOSLibAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
      
          bytes32 slot = IMPLEMENTATION_SLOT;
      
          assembly {
            sstore(slot, newImplementation)
          }
        }
      }
      
      // File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol
      
      
      
      /**
       * @title AdminUpgradeabilityProxy
       * @dev This contract combines an upgradeability proxy with an authorization
       * mechanism for administrative tasks.
       * All external functions in this contract must be guarded by the
       * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
       * feature proposal that would enable this to be done automatically.
       */
      contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
        /**
         * @dev Emitted when the administration has been transferred.
         * @param previousAdmin Address of the previous admin.
         * @param newAdmin Address of the new admin.
         */
        event AdminChanged(address previousAdmin, address newAdmin);
      
        /**
         * @dev Storage slot with the admin of the contract.
         * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is
         * validated in the constructor.
         */
        bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;
      
        /**
         * @dev Modifier to check whether the `msg.sender` is the admin.
         * If it is, it will run the function. Otherwise, it will delegate the call
         * to the implementation.
         */
        modifier ifAdmin() {
          if (msg.sender == _admin()) {
            _;
          } else {
            _fallback();
          }
        }
      
        /**
         * Contract constructor.
         * @param _implementation address of the initial implementation.
         * @param _admin Address of the proxy administrator.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _implementation, address _admin, bytes _data) UpgradeabilityProxy(_implementation, _data) public payable {
          assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
      
          _setAdmin(_admin);
        }
      
        /**
         * @return The address of the proxy admin.
         */
        function admin() external view ifAdmin returns (address) {
          return _admin();
        }
      
        /**
         * @return The address of the implementation.
         */
        function implementation() external view ifAdmin returns (address) {
          return _implementation();
        }
      
        /**
         * @dev Changes the admin of the proxy.
         * Only the current admin can call this function.
         * @param newAdmin Address to transfer proxy administration to.
         */
        function changeAdmin(address newAdmin) external ifAdmin {
          require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
          emit AdminChanged(_admin(), newAdmin);
          _setAdmin(newAdmin);
        }
      
        /**
         * @dev Upgrade the backing implementation of the proxy.
         * Only the admin can call this function.
         * @param newImplementation Address of the new implementation.
         */
        function upgradeTo(address newImplementation) external ifAdmin {
          _upgradeTo(newImplementation);
        }
      
        /**
         * @dev Upgrade the backing implementation of the proxy and call a function
         * on the new implementation.
         * This is useful to initialize the proxied contract.
         * @param newImplementation Address of the new implementation.
         * @param data Data to send as msg.data in the low level call.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         */
        function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin {
          _upgradeTo(newImplementation);
          require(newImplementation.delegatecall(data));
        }
      
        /**
         * @return The admin slot.
         */
        function _admin() internal view returns (address adm) {
          bytes32 slot = ADMIN_SLOT;
          assembly {
            adm := sload(slot)
          }
        }
      
        /**
         * @dev Sets the address of the proxy admin.
         * @param newAdmin Address of the new proxy admin.
         */
        function _setAdmin(address newAdmin) internal {
          bytes32 slot = ADMIN_SLOT;
      
          assembly {
            sstore(slot, newAdmin)
          }
        }
      
        /**
         * @dev Only fall back when the sender is not the admin.
         */
        function _willFallback() internal {
          require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
          super._willFallback();
        }
      }
      
      // File: zos-lib/contracts/application/App.sol
      
      
      
      
      
      
      /**
       * @title App
       * @dev Contract for upgradeable applications.
       * It handles the creation of proxies.
       */
      contract App is ZOSLibOwnable {
        /**
         * @dev Emitted when a new proxy is created.
         * @param proxy Address of the created proxy.
         */
        event ProxyCreated(address proxy);
      
        /**
         * @dev Emitted when a package dependency is changed in the application.
         * @param providerName Name of the package that changed.
         * @param package Address of the package associated to the name.
         * @param version Version of the package in use.
         */
        event PackageChanged(string providerName, address package, uint64[3] version);
      
        /**
         * @dev Tracks a package in a particular version, used for retrieving implementations
         */
        struct ProviderInfo {
          Package package;
          uint64[3] version;
        }
      
        /**
         * @dev Maps from dependency name to a tuple of package and version
         */
        mapping(string => ProviderInfo) internal providers;
      
        /**
         * @dev Constructor function.
         */
        constructor() public { }
      
        /**
         * @dev Returns the provider for a given package name, or zero if not set.
         * @param packageName Name of the package to be retrieved.
         * @return The provider.
         */
        function getProvider(string packageName) public view returns (ImplementationProvider provider) {
          ProviderInfo storage info = providers[packageName];
          if (address(info.package) == address(0)) return ImplementationProvider(0);
          return ImplementationProvider(info.package.getContract(info.version));
        }
      
        /**
         * @dev Returns information on a package given its name.
         * @param packageName Name of the package to be queried.
         * @return A tuple with the package address and pinned version given a package name, or zero if not set
         */
        function getPackage(string packageName) public view returns (Package, uint64[3]) {
          ProviderInfo storage info = providers[packageName];
          return (info.package, info.version);
        }
      
        /**
         * @dev Sets a package in a specific version as a dependency for this application.
         * Requires the version to be present in the package.
         * @param packageName Name of the package to set or overwrite.
         * @param package Address of the package to register.
         * @param version Version of the package to use in this application.
         */
        function setPackage(string packageName, Package package, uint64[3] version) public onlyOwner {
          require(package.hasVersion(version), "The requested version must be registered in the given package");
          providers[packageName] = ProviderInfo(package, version);
          emit PackageChanged(packageName, package, version);
        }
      
        /**
         * @dev Unsets a package given its name.
         * Reverts if the package is not set in the application.
         * @param packageName Name of the package to remove.
         */
        function unsetPackage(string packageName) public onlyOwner {
          require(address(providers[packageName].package) != address(0), "Package to unset not found");
          delete providers[packageName];
          emit PackageChanged(packageName, address(0), [uint64(0), uint64(0), uint64(0)]);
        }
      
        /**
         * @dev Returns the implementation address for a given contract name, provided by the `ImplementationProvider`.
         * @param packageName Name of the package where the contract is contained.
         * @param contractName Name of the contract.
         * @return Address where the contract is implemented.
         */
        function getImplementation(string packageName, string contractName) public view returns (address) {
          ImplementationProvider provider = getProvider(packageName);
          if (address(provider) == address(0)) return address(0);
          return provider.getImplementation(contractName);
        }
      
        /**
         * @dev Creates a new proxy for the given contract and forwards a function call to it.
         * This is useful to initialize the proxied contract.
         * @param packageName Name of the package where the contract is contained.
         * @param contractName Name of the contract.
         * @param admin Address of the proxy administrator.
         * @param data Data to send as msg.data to the corresponding implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         * @return Address of the new proxy.
         */
         function create(string packageName, string contractName, address admin, bytes data) payable public returns (AdminUpgradeabilityProxy) {
           address implementation = getImplementation(packageName, contractName);
           AdminUpgradeabilityProxy proxy = (new AdminUpgradeabilityProxy).value(msg.value)(implementation, admin, data);
           emit ProxyCreated(proxy);
           return proxy;
        }
      }

      File 3 of 3: Poap
      // File: zos-lib/contracts/Initializable.sol
      
      pragma solidity >=0.4.24 <0.6.0;
      
      
      /**
       * @title Initializable
       *
       * @dev Helper contract to support initializer functions. To use it, replace
       * the constructor with a function that has the `initializer` modifier.
       * WARNING: Unlike constructors, initializer functions must be manually
       * invoked. This applies both to deploying an Initializable contract, as well
       * as extending an Initializable contract via inheritance.
       * WARNING: When used with inheritance, manual care must be taken to not invoke
       * a parent initializer twice, or ensure that all initializers are idempotent,
       * because this is not dealt with automatically as with constructors.
       */
      contract Initializable {
      
        /**
         * @dev Indicates that the contract has been initialized.
         */
        bool private initialized;
      
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool private initializing;
      
        /**
         * @dev Modifier to use in the initializer function of a contract.
         */
        modifier initializer() {
          require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
      
          bool wasInitializing = initializing;
          initializing = true;
          initialized = true;
      
          _;
      
          initializing = wasInitializing;
        }
      
        /// @dev Returns true if and only if the function is running in the constructor
        function isConstructor() private view returns (bool) {
          // extcodesize checks the size of the code stored in an address, and
          // address returns the current address. Since the code is still not
          // deployed when running a constructor, any checks on its code size will
          // yield zero, making it an effective way to detect if a contract is
          // under construction or not.
          uint256 cs;
          assembly { cs := extcodesize(address) }
          return cs == 0;
        }
      
        // Reserved storage space to allow for layout changes in the future.
        uint256[50] private ______gap;
      }
      
      // File: openzeppelin-eth/contracts/introspection/IERC165.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @title IERC165
       * @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
       */
      interface IERC165 {
          /**
           * @notice Query if a contract implements an interface
           * @param interfaceId The interface identifier, as specified in ERC-165
           * @dev Interface identification is specified in ERC-165. This function
           * uses less than 30,000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      
      // File: openzeppelin-eth/contracts/token/ERC721/IERC721.sol
      
      pragma solidity ^0.5.0;
      
      
      
      /**
       * @title ERC721 Non-Fungible Token Standard basic interface
       * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
       */
      contract IERC721 is Initializable, IERC165 {
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
      
          function balanceOf(address owner) public view returns (uint256 balance);
          function ownerOf(uint256 tokenId) public view returns (address owner);
      
          function approve(address to, uint256 tokenId) public;
          function getApproved(uint256 tokenId) public view returns (address operator);
      
          function setApprovalForAll(address operator, bool _approved) public;
          function isApprovedForAll(address owner, address operator) public view returns (bool);
      
          function transferFrom(address from, address to, uint256 tokenId) public;
          function safeTransferFrom(address from, address to, uint256 tokenId) public;
      
          function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
      }
      
      // File: openzeppelin-eth/contracts/token/ERC721/IERC721Receiver.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @title ERC721 token receiver interface
       * @dev Interface for any contract that wants to support safeTransfers
       * from ERC721 asset contracts.
       */
      contract IERC721Receiver {
          /**
           * @notice Handle the receipt of an NFT
           * @dev The ERC721 smart contract calls this function on the recipient
           * after a `safeTransfer`. 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(keccak256("onERC721Received(address,address,uint256,bytes)"))`
           */
          function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
          public returns (bytes4);
      }
      
      // File: openzeppelin-eth/contracts/math/SafeMath.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @title SafeMath
       * @dev Unsigned math operations with safety checks that revert on error
       */
      library SafeMath {
          /**
          * @dev Multiplies two unsigned integers, reverts on 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-solidity/pull/522
              if (a == 0) {
                  return 0;
              }
      
              uint256 c = a * b;
              require(c / a == b);
      
              return c;
          }
      
          /**
          * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
          */
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0);
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
      
              return c;
          }
      
          /**
          * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
          */
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              require(b <= a);
              uint256 c = a - b;
      
              return c;
          }
      
          /**
          * @dev Adds two unsigned integers, reverts on overflow.
          */
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a);
      
              return c;
          }
      
          /**
          * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
          * reverts when dividing by zero.
          */
          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              require(b != 0);
              return a % b;
          }
      }
      
      // File: openzeppelin-eth/contracts/utils/Address.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * Utility library of inline functions on addresses
       */
      library Address {
          /**
           * Returns whether the target address is a contract
           * @dev This function will return false if invoked during the constructor of a contract,
           * as the code is not actually created until after the constructor finishes.
           * @param account address of the account to check
           * @return whether the target address is a contract
           */
          function isContract(address account) internal view returns (bool) {
              uint256 size;
              // XXX Currently there is no better way to check if there is a contract in an address
              // than to check the size of the code at that address.
              // See https://ethereum.stackexchange.com/a/14016/36603
              // for more details about how this works.
              // TODO Check this again before the Serenity release, because all addresses will be
              // contracts then.
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
      }
      
      // File: openzeppelin-eth/contracts/introspection/ERC165.sol
      
      pragma solidity ^0.5.0;
      
      
      
      /**
       * @title ERC165
       * @author Matt Condon (@shrugs)
       * @dev Implements ERC165 using a lookup table.
       */
      contract ERC165 is Initializable, IERC165 {
          bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
          /**
           * 0x01ffc9a7 ===
           *     bytes4(keccak256('supportsInterface(bytes4)'))
           */
      
          /**
           * @dev a mapping of interface id to whether or not it's supported
           */
          mapping(bytes4 => bool) private _supportedInterfaces;
      
          /**
           * @dev A contract implementing SupportsInterfaceWithLookup
           * implement ERC165 itself
           */
          function initialize() public initializer {
              _registerInterface(_INTERFACE_ID_ERC165);
          }
      
          /**
           * @dev implement supportsInterface(bytes4) using a lookup table
           */
          function supportsInterface(bytes4 interfaceId) public view returns (bool) {
              return _supportedInterfaces[interfaceId];
          }
      
          /**
           * @dev internal method for registering an interface
           */
          function _registerInterface(bytes4 interfaceId) internal {
              require(interfaceId != 0xffffffff);
              _supportedInterfaces[interfaceId] = true;
          }
      
          uint256[50] private ______gap;
      }
      
      // File: openzeppelin-eth/contracts/token/ERC721/ERC721.sol
      
      pragma solidity ^0.5.0;
      
      
      
      
      
      
      
      /**
       * @title ERC721 Non-Fungible Token Standard basic implementation
       * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
       */
      contract ERC721 is Initializable, ERC165, IERC721 {
          using SafeMath for uint256;
          using Address for address;
      
          // 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 token ID to owner
          mapping (uint256 => address) private _tokenOwner;
      
          // Mapping from token ID to approved address
          mapping (uint256 => address) private _tokenApprovals;
      
          // Mapping from owner to number of owned token
          mapping (address => uint256) private _ownedTokensCount;
      
          // Mapping from owner to operator approvals
          mapping (address => mapping (address => bool)) private _operatorApprovals;
      
          bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
          /*
           * 0x80ac58cd ===
           *     bytes4(keccak256('balanceOf(address)')) ^
           *     bytes4(keccak256('ownerOf(uint256)')) ^
           *     bytes4(keccak256('approve(address,uint256)')) ^
           *     bytes4(keccak256('getApproved(uint256)')) ^
           *     bytes4(keccak256('setApprovalForAll(address,bool)')) ^
           *     bytes4(keccak256('isApprovedForAll(address,address)')) ^
           *     bytes4(keccak256('transferFrom(address,address,uint256)')) ^
           *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
           *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
           */
      
          function initialize() public initializer {
              ERC165.initialize();
      
              // register the supported interfaces to conform to ERC721 via ERC165
              _registerInterface(_INTERFACE_ID_ERC721);
          }
      
          function _hasBeenInitialized() internal view returns (bool) {
              return supportsInterface(_INTERFACE_ID_ERC721);
          }
      
          /**
           * @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 returns (uint256) {
              require(owner != address(0));
              return _ownedTokensCount[owner];
          }
      
          /**
           * @dev Gets the owner of the specified token ID
           * @param tokenId uint256 ID of the token to query the owner of
           * @return owner address currently marked as the owner of the given token ID
           */
          function ownerOf(uint256 tokenId) public view returns (address) {
              address owner = _tokenOwner[tokenId];
              require(owner != address(0));
              return owner;
          }
      
          /**
           * @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 {
              address owner = ownerOf(tokenId);
              require(to != owner);
              require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
      
              _tokenApprovals[tokenId] = to;
              emit Approval(owner, 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 returns (address) {
              require(_exists(tokenId));
              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 to operator address to set the approval
           * @param approved representing the status of the approval to be set
           */
          function setApprovalForAll(address to, bool approved) public {
              require(to != msg.sender);
              _operatorApprovals[msg.sender][to] = approved;
              emit ApprovalForAll(msg.sender, to, 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 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 {
              require(_isApprovedOrOwner(msg.sender, tokenId));
      
              _transferFrom(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 `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 {
              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 `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 safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
              transferFrom(from, to, tokenId);
              require(_checkOnERC721Received(from, to, tokenId, _data));
          }
      
          /**
           * @dev Returns whether the specified token exists
           * @param tokenId uint256 ID of the token to query the existence of
           * @return whether the token exists
           */
          function _exists(uint256 tokenId) internal view returns (bool) {
              address owner = _tokenOwner[tokenId];
              return owner != address(0);
          }
      
          /**
           * @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) {
              address owner = ownerOf(tokenId);
              return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
          }
      
          /**
           * @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 {
              require(to != address(0));
              require(!_exists(tokenId));
      
              _tokenOwner[tokenId] = to;
              _ownedTokensCount[to] = _ownedTokensCount[to].add(1);
      
              emit Transfer(address(0), to, tokenId);
          }
      
          /**
           * @dev Internal function to burn a specific token
           * Reverts if the token does not exist
           * Deprecated, use _burn(uint256) instead.
           * @param owner owner of the token to burn
           * @param tokenId uint256 ID of the token being burned
           */
          function _burn(address owner, uint256 tokenId) internal {
              require(ownerOf(tokenId) == owner);
      
              _clearApproval(tokenId);
      
              _ownedTokensCount[owner] = _ownedTokensCount[owner].sub(1);
              _tokenOwner[tokenId] = address(0);
      
              emit Transfer(owner, address(0), 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 {
              _burn(ownerOf(tokenId), 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 _transferFrom(address from, address to, uint256 tokenId) internal {
              require(ownerOf(tokenId) == from);
              require(to != address(0));
      
              _clearApproval(tokenId);
      
              _ownedTokensCount[from] = _ownedTokensCount[from].sub(1);
              _ownedTokensCount[to] = _ownedTokensCount[to].add(1);
      
              _tokenOwner[tokenId] = to;
      
              emit Transfer(from, to, tokenId);
          }
      
          /**
           * @dev Internal function to invoke `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 whether the call correctly returned the expected magic value
           */
          function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
              internal returns (bool)
          {
              if (!to.isContract()) {
                  return true;
              }
      
              bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
              return (retval == _ERC721_RECEIVED);
          }
      
          /**
           * @dev Private function to clear current approval of a given token ID
           * @param tokenId uint256 ID of the token to be transferred
           */
          function _clearApproval(uint256 tokenId) private {
              if (_tokenApprovals[tokenId] != address(0)) {
                  _tokenApprovals[tokenId] = address(0);
              }
          }
      
          uint256[50] private ______gap;
      }
      
      // File: openzeppelin-eth/contracts/token/ERC721/IERC721Enumerable.sol
      
      pragma solidity ^0.5.0;
      
      
      
      /**
       * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
       * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
       */
      contract IERC721Enumerable is Initializable, IERC721 {
          function totalSupply() public view returns (uint256);
          function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256 tokenId);
      
          function tokenByIndex(uint256 index) public view returns (uint256);
      }
      
      // File: openzeppelin-eth/contracts/token/ERC721/ERC721Enumerable.sol
      
      pragma solidity ^0.5.0;
      
      
      
      
      
      /**
       * @title ERC-721 Non-Fungible Token with optional enumeration extension logic
       * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
       */
      contract ERC721Enumerable is Initializable, ERC165, ERC721, IERC721Enumerable {
          // Mapping from owner to list of owned token IDs
          mapping(address => uint256[]) private _ownedTokens;
      
          // Mapping from token ID to index of the owner tokens list
          mapping(uint256 => uint256) private _ownedTokensIndex;
      
          // Array with all token ids, used for enumeration
          uint256[] private _allTokens;
      
          // Mapping from token id to position in the allTokens array
          mapping(uint256 => uint256) private _allTokensIndex;
      
          bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
          /**
           * 0x780e9d63 ===
           *     bytes4(keccak256('totalSupply()')) ^
           *     bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^
           *     bytes4(keccak256('tokenByIndex(uint256)'))
           */
      
          /**
           * @dev Constructor function
           */
          function initialize() public initializer {
              require(ERC721._hasBeenInitialized());
      
              // register the supported interface to conform to ERC721 via ERC165
              _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
          }
      
          function _hasBeenInitialized() internal view returns (bool) {
              return supportsInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
          }
      
          /**
           * @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 returns (uint256) {
              require(index < balanceOf(owner));
              return _ownedTokens[owner][index];
          }
      
          /**
           * @dev Gets the total amount of tokens stored by the contract
           * @return uint256 representing the total amount of tokens
           */
          function totalSupply() public view returns (uint256) {
              return _allTokens.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 returns (uint256) {
              require(index < totalSupply());
              return _allTokens[index];
          }
      
          /**
           * @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 _transferFrom(address from, address to, uint256 tokenId) internal {
              super._transferFrom(from, to, tokenId);
      
              _removeTokenFromOwnerEnumeration(from, tokenId);
      
              _addTokenToOwnerEnumeration(to, tokenId);
          }
      
          /**
           * @dev Internal function to mint a new token
           * Reverts if the given token ID already exists
           * @param to address the beneficiary that will own the minted token
           * @param tokenId uint256 ID of the token to be minted
           */
          function _mint(address to, uint256 tokenId) internal {
              super._mint(to, tokenId);
      
              _addTokenToOwnerEnumeration(to, tokenId);
      
              _addTokenToAllTokensEnumeration(tokenId);
          }
      
          /**
           * @dev Internal function to burn a specific token
           * Reverts if the token does not exist
           * Deprecated, use _burn(uint256) instead
           * @param owner owner of the token to burn
           * @param tokenId uint256 ID of the token being burned
           */
          function _burn(address owner, uint256 tokenId) internal {
              super._burn(owner, tokenId);
      
              _removeTokenFromOwnerEnumeration(owner, tokenId);
              // Since tokenId will be deleted, we can clear its slot in _ownedTokensIndex to trigger a gas refund
              _ownedTokensIndex[tokenId] = 0;
      
              _removeTokenFromAllTokensEnumeration(tokenId);
          }
      
          /**
           * @dev Gets the list of token IDs of the requested owner
           * @param owner address owning the tokens
           * @return uint256[] List of token IDs owned by the requested address
           */
          function _tokensOfOwner(address owner) internal view returns (uint256[] storage) {
              return _ownedTokens[owner];
          }
      
          /**
           * @dev Private function to add a token to this extension's ownership-tracking data structures.
           * @param to address representing the new owner of the given token ID
           * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
           */
          function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
              _ownedTokensIndex[tokenId] = _ownedTokens[to].length;
              _ownedTokens[to].push(tokenId);
          }
      
          /**
           * @dev Private function to add a token to this extension's token tracking data structures.
           * @param tokenId uint256 ID of the token to be added to the tokens list
           */
          function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
              _allTokensIndex[tokenId] = _allTokens.length;
              _allTokens.push(tokenId);
          }
      
          /**
           * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
           * while the token is not assigned a new owner, the _ownedTokensIndex mapping is _not_ updated: this allows for
           * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
           * This has O(1) time complexity, but alters the order of the _ownedTokens array.
           * @param from address representing the previous owner of the given token ID
           * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
           */
          function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
              // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
              // then delete the last slot (swap and pop).
      
              uint256 lastTokenIndex = _ownedTokens[from].length.sub(1);
              uint256 tokenIndex = _ownedTokensIndex[tokenId];
      
              // When the token to delete is the last token, the swap operation is unnecessary
              if (tokenIndex != lastTokenIndex) {
                  uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
      
                  _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
                  _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
              }
      
              // This also deletes the contents at the last position of the array
              _ownedTokens[from].length--;
      
              // Note that _ownedTokensIndex[tokenId] hasn't been cleared: it still points to the old slot (now occcupied by
              // lasTokenId, or just over the end of the array if the token was the last one).
          }
      
          /**
           * @dev Private function to remove a token from this extension's token tracking data structures.
           * This has O(1) time complexity, but alters the order of the _allTokens array.
           * @param tokenId uint256 ID of the token to be removed from the tokens list
           */
          function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
              // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
              // then delete the last slot (swap and pop).
      
              uint256 lastTokenIndex = _allTokens.length.sub(1);
              uint256 tokenIndex = _allTokensIndex[tokenId];
      
              // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
              // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
              // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
              uint256 lastTokenId = _allTokens[lastTokenIndex];
      
              _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
              _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
      
              // This also deletes the contents at the last position of the array
              _allTokens.length--;
              _allTokensIndex[tokenId] = 0;
          }
      
          uint256[50] private ______gap;
      }
      
      // File: openzeppelin-eth/contracts/token/ERC721/IERC721Metadata.sol
      
      pragma solidity ^0.5.0;
      
      
      
      /**
       * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
       * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
       */
      contract IERC721Metadata is Initializable, IERC721 {
          function name() external view returns (string memory);
          function symbol() external view returns (string memory);
          function tokenURI(uint256 tokenId) external view returns (string memory);
      }
      
      // File: openzeppelin-eth/contracts/access/Roles.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @title Roles
       * @dev Library for managing addresses assigned to a Role.
       */
      library Roles {
          struct Role {
              mapping (address => bool) bearer;
          }
      
          /**
           * @dev give an account access to this role
           */
          function add(Role storage role, address account) internal {
              require(account != address(0));
              require(!has(role, account));
      
              role.bearer[account] = true;
          }
      
          /**
           * @dev remove an account's access to this role
           */
          function remove(Role storage role, address account) internal {
              require(account != address(0));
              require(has(role, account));
      
              role.bearer[account] = false;
          }
      
          /**
           * @dev check if an account has this role
           * @return bool
           */
          function has(Role storage role, address account) internal view returns (bool) {
              require(account != address(0));
              return role.bearer[account];
          }
      }
      
      // File: contracts/PoapRoles.sol
      
      pragma solidity ^0.5.0;
      
      
      
      contract PoapRoles is Initializable {
          using Roles for Roles.Role;
      
          event AdminAdded(address indexed account);
          event AdminRemoved(address indexed account);
          event EventMinterAdded(uint256 indexed eventId, address indexed account);
          event EventMinterRemoved(uint256 indexed eventId, address indexed account);
      
          Roles.Role private _admins;
          mapping(uint256 => Roles.Role) private _minters;
      
          function initialize(address sender) public initializer {
              if (!isAdmin(sender)) {
                  _addAdmin(sender);
              }
          }
      
          modifier onlyAdmin() {
              require(isAdmin(msg.sender));
              _;
          }
      
          modifier onlyEventMinter(uint256 eventId) {
              require(isEventMinter(eventId, msg.sender));
              _;
          }
      
          function isAdmin(address account) public view returns (bool) {
              return _admins.has(account);
          }
      
          function isEventMinter(uint256 eventId, address account) public view returns (bool) {
              return isAdmin(account) || _minters[eventId].has(account);
          }
      
          function addEventMinter(uint256 eventId, address account) public onlyEventMinter(eventId) {
              _addEventMinter(eventId, account);
          }
      
          function addAdmin(address account) public onlyAdmin {
              _addAdmin(account);
          }
      
          function renounceEventMinter(uint256 eventId) public {
              _removeEventMinter(eventId, msg.sender);
          }
      
          function renounceAdmin() public {
              _removeAdmin(msg.sender);
          }
      
          function removeEventMinter(uint256 eventId, address account) public onlyAdmin {
              _removeEventMinter(eventId, account);
          }
      
          function _addEventMinter(uint256 eventId, address account) internal {
              _minters[eventId].add(account);
              emit EventMinterAdded(eventId, account);
          }
      
          function _addAdmin(address account) internal {
              _admins.add(account);
              emit AdminAdded(account);
          }
      
          function _removeEventMinter(uint256 eventId, address account) internal {
              _minters[eventId].remove(account);
              emit EventMinterRemoved(eventId, account);
          }
      
          function _removeAdmin(address account) internal {
              _admins.remove(account);
              emit AdminRemoved(account);
          }    
      
          // For future extensions
          uint256[50] private ______gap;
      }
      
      // File: contracts/PoapPausable.sol
      
      pragma solidity ^0.5.0;
      
      
      
      /**
       * @title Pausable
       * @dev Base contract which allows children to implement an emergency stop mechanism.
       */
      contract PoapPausable is Initializable, PoapRoles {
          event Paused(address account);
          event Unpaused(address account);
      
          bool private _paused;
      
          function initialize() public initializer {
              _paused = false;
          }
      
          /**
           * @return true if the contract is paused, false otherwise.
           */
          function paused() public view returns (bool) {
              return _paused;
          }
      
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           */
          modifier whenNotPaused() {
              require(!_paused);
              _;
          }
      
          /**
           * @dev Modifier to make a function callable only when the contract is paused.
           */
          modifier whenPaused() {
              require(_paused);
              _;
          }
      
          /**
           * @dev called by the owner to pause, triggers stopped state
           */
          function pause() public onlyAdmin whenNotPaused {
              _paused = true;
              emit Paused(msg.sender);
          }
      
          /**
           * @dev called by the owner to unpause, returns to normal state
           */
          function unpause() public onlyAdmin whenPaused {
              _paused = false;
              emit Unpaused(msg.sender);
          }
      
          // For future extensions
          uint256[50] private ______gap;
      }
      
      // File: contracts/Poap.sol
      
      pragma solidity ^0.5.0;
      
      
      
      
      
      
      
      
          // Desired Features
          // - Add Event
          // - Add Event Organizer
          // - Mint token for an event
          // - Batch Mint
          // - Burn Tokens (only admin?)
          // - Pause contract (only admin)
          // - ERC721 full interface (base, metadata, enumerable)
      
      contract Poap is Initializable, ERC721, ERC721Enumerable, PoapRoles, PoapPausable {
          event EventToken(uint256 eventId, uint256 tokenId);
      
          // Token name
          string private _name;
      
          // Token symbol
          string private _symbol;
      
          // Base token URI
          string private _baseURI;
      
          // Last Used id (used to generate new ids)
          uint256 private lastId;
      
          // EventId for each token
          mapping(uint256 => uint256) private _tokenEvent;
      
      
          bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
          
          /**
           * @dev Gets the token name
           * @return string representing the token name
           */
          function name() external view returns (string memory) {
              return _name;
          }
      
          /**
           * @dev Gets the token symbol
           * @return string representing the token symbol
           */
          function symbol() external view returns (string memory) {
              return _symbol;
          }
      
          function tokenEvent(uint256 tokenId) public view returns (uint256) {
              return _tokenEvent[tokenId];
          }
      
          /**
           * @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 tokenDetailsOfOwnerByIndex(address owner, uint256 index) public view returns (uint256 tokenId, uint256 eventId) {
              tokenId = tokenOfOwnerByIndex(owner, index);
              eventId = tokenEvent(tokenId);
          }
      
          /**
           * @dev Gets the token uri
           * @return string representing the token uri
           */
          function tokenURI(uint256 tokenId) external view returns (string memory) {
              uint eventId = _tokenEvent[tokenId];
              return _strConcat(_baseURI, _uint2str(eventId), "/", _uint2str(tokenId), "");
          }
      
          function setBaseURI(string memory baseURI) public onlyAdmin whenNotPaused {
              _baseURI = baseURI;
          }
      
          function approve(address to, uint256 tokenId) public whenNotPaused {
              super.approve(to, tokenId);
          }
      
          function setApprovalForAll(address to, bool approved) public whenNotPaused {
              super.setApprovalForAll(to, approved);
          }
      
          function transferFrom(address from, address to, uint256 tokenId) public whenNotPaused {
              super.transferFrom(from, to, tokenId);
          }
      
          /**
           * @dev Function to mint tokens
           * @param eventId EventId for the new token
           * @param to The address that will receive the minted tokens.
           * @return A boolean that indicates if the operation was successful.
           */
          function mintToken(uint256 eventId, address to) 
              public whenNotPaused onlyEventMinter(eventId) returns (bool) 
          {
              lastId += 1;
              return _mintToken(eventId, lastId, to);
          }
      
          /**
           * @dev Function to mint tokens
           * @param eventId EventId for the new token
           * @param to The address that will receive the minted tokens.
           * @return A boolean that indicates if the operation was successful.
           */
          function mintEventToManyUsers(uint256 eventId, address[] memory to) 
              public whenNotPaused onlyEventMinter(eventId) returns (bool) 
          {
              for (uint256 i = 0; i < to.length; ++i) {
                  _mintToken(eventId, lastId + 1 + i, to[i]);
              }        
              lastId += to.length;
              return true;
          }
      
          /**
           * @dev Function to mint tokens
           * @param eventIds EventIds to assing to user
           * @param to The address that will receive the minted tokens.
           * @return A boolean that indicates if the operation was successful.
           */
          function mintUserToManyEvents(uint256[] memory eventIds, address to) 
              public whenNotPaused onlyAdmin() returns (bool) 
          {
              for (uint256 i = 0; i < eventIds.length; ++i) {
                  _mintToken(eventIds[i], lastId + 1 + i, to);
              }        
              lastId += eventIds.length;
              return true;
          }
      
          /**
           * @dev Burns a specific ERC721 token.
           * @param tokenId uint256 id of the ERC721 token to be burned.
           */
          function burn(uint256 tokenId) public {
              require(_isApprovedOrOwner(msg.sender, tokenId) || isAdmin(msg.sender));
              _burn(tokenId);
          }    
      
          function initialize(string memory __name, string memory __symbol, string memory __baseURI, address[] memory admins) 
              public initializer 
          {
              ERC721.initialize();
              ERC721Enumerable.initialize();
              PoapRoles.initialize(msg.sender); 
              PoapPausable.initialize();
      
              // Add the requested admins
              for (uint256 i = 0; i < admins.length; ++i) {
                  _addAdmin(admins[i]);
              }
      
              _name = __name;
              _symbol = __symbol;
              _baseURI = __baseURI;
              
              // register the supported interfaces to conform to ERC721 via ERC165
              _registerInterface(_INTERFACE_ID_ERC721_METADATA);
          }
      
          /**
           * @dev Internal function to burn a specific token
           * Reverts if the token does not exist
           * 
           * @param owner owner of the token to burn
           * @param tokenId uint256 ID of the token being burned by the msg.sender
           */
          function _burn(address owner, uint256 tokenId) internal {
              super._burn(owner, tokenId);
      
              delete _tokenEvent[tokenId];
          }    
      
          /**
           * @dev Function to mint tokens
           * @param eventId EventId for the new token
           * @param tokenId The token id to mint.
           * @param to The address that will receive the minted tokens.
           * @return A boolean that indicates if the operation was successful.
           */
          function _mintToken(uint256 eventId, uint256 tokenId, address to) internal returns (bool) {
              // TODO Verify that the token receiver ('to') do not have already a token for the event ('eventId')
              _mint(to, tokenId);
              _tokenEvent[tokenId] = eventId;
              emit EventToken(eventId, tokenId);
              return true;
          }
      
          /**
           * @dev Function to convert uint to string
           * Taken from https://github.com/oraclize/ethereum-api/blob/master/oraclizeAPI_0.5.sol
           */
          function _uint2str(uint _i) internal pure returns (string memory _uintAsString) {
              if (_i == 0) {
                  return "0";
              }
              uint j = _i;
              uint len;
              while (j != 0) {
                  len++;
                  j /= 10;
              }
              bytes memory bstr = new bytes(len);
              uint k = len - 1;
              while (_i != 0) {
                  bstr[k--] = byte(uint8(48 + _i % 10));
                  _i /= 10;
              }
              return string(bstr);
          }
      
          /**
           * @dev Function to concat strings
           * Taken from https://github.com/oraclize/ethereum-api/blob/master/oraclizeAPI_0.5.sol
           */
          function _strConcat(string memory _a, string memory _b, string memory _c, string memory _d, string memory _e) 
              internal pure returns (string memory _concatenatedString) 
          {
              bytes memory _ba = bytes(_a);
              bytes memory _bb = bytes(_b);
              bytes memory _bc = bytes(_c);
              bytes memory _bd = bytes(_d);
              bytes memory _be = bytes(_e);
              string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length);
              bytes memory babcde = bytes(abcde);
              uint k = 0;
              uint i = 0;
              for (i = 0; i < _ba.length; i++) {
                  babcde[k++] = _ba[i];
              }
              for (i = 0; i < _bb.length; i++) {
                  babcde[k++] = _bb[i];
              }
              for (i = 0; i < _bc.length; i++) {
                  babcde[k++] = _bc[i];
              }
              for (i = 0; i < _bd.length; i++) {
                  babcde[k++] = _bd[i];
              }
              for (i = 0; i < _be.length; i++) {
                  babcde[k++] = _be[i];
              }
              return string(babcde);
          }
      
      }