ETH Price: $4,408.77 (-1.53%)

Transaction Decoder

Block:
15003150 at Jun-21-2022 04:19:47 PM +UTC
Transaction Fee:
0.00234598052980344 ETH $10.34
Gas Used:
55,590 Gas / 42.201484616 Gwei

Emitted Events:

211 AdminUpgradeabilityProxy.0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31( 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31, 0x000000000000000000000000b6b7e606916038e28b3668919c014e838fe5eba5, 0x0000000000000000000000001e0049783f008a0085193e00003d00cd54003c71, 0000000000000000000000000000000000000000000000000000000000000001 )

Account State Difference:

  Address   Before After State Difference Code
0x22C1f605...fEf83B415
0xb6B7E606...38Fe5EBA5
0.020578982535768718 Eth
Nonce: 916
0.018233002005965278 Eth
Nonce: 917
0.00234598052980344
(Ethermine)
2,641.552649407521081339 Eth2,641.552788382521081339 Eth0.000138975

Execution Trace

AdminUpgradeabilityProxy.a22cb465( )
  • Poap.setApprovalForAll( to=0x1E0049783F008A0085193E00003D00cd54003c71, approved=True )
    setApprovalForAll[Poap (ln:116)]
    File 1 of 2: 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 2 of 2: Poap
    // SPDX-License-Identifier: AGPL-3.0
    pragma solidity ^0.5.2;
    import "zos-lib/contracts/Initializable.sol";
    import "openzeppelin-eth/contracts/token/ERC721/ERC721.sol";
    import "openzeppelin-eth/contracts/token/ERC721/ERC721Enumerable.sol";
    import "openzeppelin-eth/contracts/token/ERC721/IERC721Metadata.sol";
    import "./PoapRoles.sol";
    import "./PoapPausable.sol";
    /**
     * @title POAP contract in Ethereum
     * @dev Mainnet point of interaction with POAP
     * - Users can:
     *   # Add Event Organizer
     *   # Mint token for an event
     *   # Batch Mint
     *   # Burn Tokens if admin
     *   # Pause contract if admin
     *   # Unpause contract if admin
     *   # ERC721 full interface (base, metadata, enumerable)
     * - To be covered by a proxy contract
     * @author POAP
     * - Developers:
     *   # Agustin Lavarello
     *   # Rodrigo Manuel Navarro Lajous
     *   # Ramiro Gonzales
    **/
    contract Poap is Initializable, ERC721, ERC721Enumerable, PoapRoles, PoapPausable {
       
        /**
         * @dev Emmited when token is created
         */
        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;
        // Event Id 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;
        }
        /**
         * @dev Gets the Event Id for the token
         * @param tokenId ( uint256 ) The Token Id you want to query
         * @return uint256 representing the Event id for the token
         */
        function tokenEvent(uint256 tokenId) public view returns (uint256) {
            return _tokenEvent[tokenId];
        }
        /**
         * @dev Gets the Token Id and Event Id for a given index of the tokens list of the requested owner
         * @param owner ( address ) Owner address of the token list to be queried
         * @param index ( uint256 ) Index to be accessed of the requested tokens list
         * @return ( uint256, uint256 ) Token Id and Event Id for 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 URI for the token metadata
         * @param tokenId ( uint256 ) The Token Id you want to get the URI
         * @return ( string ) URI for the token metadata 
         */
        function tokenURI(uint256 tokenId) external view returns (string memory) {
            uint eventId = _tokenEvent[tokenId];
            return _strConcat(_baseURI, _uint2str(eventId), "/", _uint2str(tokenId), "");
        }
        /**
         * @dev Sets Base URI for the token metadata.
         * Requires 
         * - The msg sender to be the admin
         * - The contract does not have to be paused
         * @param baseURI ( string ) The base URI to change
         */
        function setBaseURI(string memory baseURI) public onlyAdmin whenNotPaused {
            _baseURI = baseURI;
        }
        /**
         * @dev Approves another address to transfer the given token ID (Implements ERC71)
         * Wrapper for function extended from ERC721 (  https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol )
         * Requires 
         * - The msg sender to be the owner, approved, or operator
         * - The contract does not have to be paused
         * @param to ( address ) The addres 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 whenNotPaused {
            super.approve(to, tokenId);
        }
        /**
         * @dev Sets or unsets the approval of a given operator (Implements ERC71)
         * Wrapper for function extended from ERC721 (  https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol )
         * Requires 
         * - The msg sender to be the owner, approved, or operator
         * - The contract does not have to be paused
         * @param to ( address ) The address of the operator to set the approval
         * @param approved ( bool ) Represents the status of the approval to be set
         */
        function setApprovalForAll(address to, bool approved) public whenNotPaused {
            super.setApprovalForAll(to, approved);
        }
        /**
         * @dev Transfers the ownership of a given token ID to another address
         * Wrapper for function extended from ERC721 (  https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol )
         * Requires 
         * - The msg sender to be the owner, approved, or operator
         * - Contract not paused
         * @param from ( address ) The address of the current owner of the token
         * @param to ( address ) The 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 whenNotPaused {
            super.transferFrom(from, to, tokenId);
        }
        /**
         * @dev Safely transfers the ownership of a given token ID to another address (Implements ERC71)
         * Wrapper for function extended from ERC721 (  https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol )
         * Requires 
         * - The msg sender to be the owner, approved, or operator
         * - The contract does not have to be paused
         * @param from ( address ) The address of the current owner of the token
         * @param to ( address ) The 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 whenNotPaused {
            super.safeTransferFrom(from, to, tokenId);
        }
        /**
         * @dev Safely transfers the ownership of a given token ID to another address (Implements ERC71)
         * Wrapper for function extended from ERC721 (  https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol )
         * Requires 
         * - The msg sender to be the owner, approved, or operator
         * - The contract does not have to be paused
         * @param from ( address ) The address of the current owner of the token
         * @param to ( address ) The 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 whenNotPaused {
            super.safeTransferFrom(from, to, tokenId, _data);
        }
        /**
         * @dev Mint token to address
         * @param eventId ( uint256 ) EventId for the new token
         * @param to ( address ) 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 Mint specific token to address.
         * Requires 
         * - The msg sender to be the admin, or event minter for the specific event Id
         * - The contract does not have to be paused
         * @param eventId ( uint256 ) EventId for the new token
         * @param tokenId ( uint256 ) Token Id for the new token
         * @param to ( address ) 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)
        public whenNotPaused onlyEventMinter(eventId) returns (bool)
        {
            return _mintToken(eventId, tokenId, to);
        }
        /**
         * @dev Mint token to many addresses.
         * Requires 
         * - The msg sender to be the admin, or event minter for the specific event Id
         * - The contract does not have to be paused
         * @param eventId ( uint256 ) EventId for the new token
         * @param to ( array of address ) The addresses 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 Mint many tokens to address.
         * Requires 
         * - The msg sender to be the admin
         * - The contract does not have to be paused
         * @param eventIds ( array uint256 ) Event Ids to assing to user
         * @param to ( address ) 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.
         * Requires 
         * - The msg sender to be the owner, approved, or admin
         * @param tokenId ( uint256 ) Id of the ERC721 token to be burned.
         */
        function burn(uint256 tokenId) public {
            require(_isApprovedOrOwner(msg.sender, tokenId) || isAdmin(msg.sender), "Sender doesn't have permission");
            _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 ( addreess ) The 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 Internal function to mint tokens
         * @param eventId ( uint256 ) EventId for the new token
         * @param tokenId ( uint256 ) The token id to mint.
         * @param to ( address ) 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 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);
        }
        /**
         * @dev Admin can remove other Admin.
         * @param account ( address ) Address of the admin to be removed
         */
        function removeAdmin(address account) public onlyAdmin {
            _removeAdmin(account);
        }
    }
    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;
    }
    pragma solidity ^0.5.2;
    import "zos-lib/contracts/Initializable.sol";
    import "./IERC721.sol";
    import "./IERC721Receiver.sol";
    import "../../math/SafeMath.sol";
    import "../../utils/Address.sol";
    import "../../drafts/Counters.sol";
    import "../../introspection/ERC165.sol";
    /**
     * @title ERC721 Non-Fungible Token Standard basic implementation
     * @dev see https://eips.ethereum.org/EIPS/eip-721
     */
    contract ERC721 is Initializable, 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 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].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));
            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 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) {
            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].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(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].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);
            require(to != address(0));
            _clearApproval(tokenId);
            _ownedTokensCount[from].decrement();
            _ownedTokensCount[to].increment();
            _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 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;
            }
            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;
    }
    pragma solidity ^0.5.2;
    import "zos-lib/contracts/Initializable.sol";
    import "./IERC721Enumerable.sol";
    import "./ERC721.sol";
    import "../../introspection/ERC165.sol";
    /**
     * @title ERC-721 Non-Fungible Token with optional enumeration extension logic
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    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 ERC721Enumerable 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 occupied by
            // lastTokenId, 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;
    }
    pragma solidity ^0.5.2;
    import "zos-lib/contracts/Initializable.sol";
    import "./IERC721.sol";
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    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);
    }
    // SPDX-License-Identifier: agpl-3.0
    pragma solidity ^0.5.0;
    import "zos-lib/contracts/Initializable.sol";
    import "openzeppelin-eth/contracts/access/Roles.sol";
     /**
     * @title Roles contract
     * @dev Base contract which allows children to implement role system.
     * - Users can:
     *   # Add event minter if Event Minter or Admin
     *   # Add Admin if Admin
     *   # Check if addr is Admin
     *   # Check if addr is Event Minter for Event ID
     *   # Renounce Admin role
     *   # Renounce Event Minter role
     *   # Remove Event Minter if Admin
     * @author POAP
     * - Developers:
     *   # Agustin Lavarello
     *   # Rodrigo Manuel Navarro Lajous
     *   # Ramiro Gonzales
     **/
    contract PoapRoles is Initializable {
        using Roles for Roles.Role;
        /**
         * @dev Emmited when an Admin is added
         */
        event AdminAdded(address indexed account);
        /**
         * @dev Emmited when an Admin is removed
         */
        event AdminRemoved(address indexed account);
        
        /**
         * @dev Emmited when an Event Minter is added
         */
        event EventMinterAdded(uint256 indexed eventId, address indexed account);
        
        /**
         * @dev Emmited when an Event Minter is removed
         */
        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);
            }
        }
        /**
         * @dev Modifier to make a function callable only by the Admin.
         */
        modifier onlyAdmin() {
            require(isAdmin(msg.sender), "Sender is not Admin");
            _;
        }
        /**
         * @dev Modifier to make a function callable only by the Event Minter for especific Event Id.
         * @param eventId ( uint256 ) The Event Id to check.
         */
        modifier onlyEventMinter(uint256 eventId) {
            require(isEventMinter(eventId, msg.sender), "Sender is not Event Minter");
            _;
        }
        /**
         * @dev Checks if address is Admin.
         * @param account ( address ) The address to be checked.
         * @return bool representing if the adddress is admin.
         */
        function isAdmin(address account) public view returns (bool) {
            return _admins.has(account);
        }
        /**
         * @dev Checks if address is Event Minter for especific Event.
         * @param eventId ( uint256 ) The Event ID to check.
         * @param account ( address ) The address to be checked.
         * @return bool representing if the adddress is Event Minter.
         */
        function isEventMinter(uint256 eventId, address account) public view returns (bool) {
            return isAdmin(account) || _minters[eventId].has(account);
        }
        /**
         * @dev Function to add an Event Minter for especefic Event ID
         * Requires 
         * - The msg sender to be the admin or Event Minter for the especific Event ID
         * @param eventId ( uint256 ) The ID of the Event.
         * @param account ( address ) The Address that will be granted permissions on the Event.
         */
        function addEventMinter(uint256 eventId, address account) public onlyEventMinter(eventId) {
            _addEventMinter(eventId, account);
        }
        /**
         * @dev Function to add an Admin.
         * Requires 
         * - The msg sender to be the admin.
         * @param account ( address ) The Address that will be granted permissions as Admin.
         */
        function addAdmin(address account) public onlyAdmin {
            _addAdmin(account);
        }
        /**
         * @dev Function renounce as Event Minter for especefic Event ID
         * Requires 
         * - The msg sender to be an Event Minter for the especific Event ID
         * @param eventId ( uint256 ) The ID of the Event.
         */
        function renounceEventMinter(uint256 eventId) public {
            _removeEventMinter(eventId, msg.sender);
        }
        /**
         * @dev Function renounce as Admin
         * Requires 
         * - The msg sender to be an Admin
         */
        function renounceAdmin() public {
            _removeAdmin(msg.sender);
        }
        /**
         * @dev Function remove Event Minter for especif Event ID
         * Requires 
         * - The msg sender to be an Admin
         * @param eventId ( uint256 ) The ID of the Event.
         * @param account ( address ) The Address that will be removed permissions as Event Minter for especific ID.
         */
        function removeEventMinter(uint256 eventId, address account) public onlyAdmin {
            _removeEventMinter(eventId, account);
        }
        /**
         * @dev Internal function to add Event Minter
         * @param eventId ( uint256 ) The ID of the Event.
         * @param account ( address ) The Address that will be granted permissions on the Event.
         */
        function _addEventMinter(uint256 eventId, address account) internal {
            _minters[eventId].add(account);
            emit EventMinterAdded(eventId, account);
        }
        /**
         * @dev Internal function to add Admin
         * @param account ( address ) The Address that will be granted permissions as Admin.
         */
        function _addAdmin(address account) internal {
            _admins.add(account);
            emit AdminAdded(account);
        }
        /**
         * @dev Internal function to remove Event Minter for especif Event ID
         * @param eventId ( uint256 ) The ID of the Event.
         * @param account ( address ) The Address that will be removed permissions as Event Minter for especific ID.
         */
        function _removeEventMinter(uint256 eventId, address account) internal {
            _minters[eventId].remove(account);
            emit EventMinterRemoved(eventId, account);
        }
        /**
         * @dev Internal function to remove an Admin
         * @param account ( address ) The Address that will be removed permissions as Admin.
         */
        function _removeAdmin(address account) internal {
            _admins.remove(account);
            emit AdminRemoved(account);
        }
        // For future extensions
        uint256[50] private ______gap;
    }
    // SPDX-License-Identifier: agpl-3.0
    pragma solidity ^0.5.0;
    import "zos-lib/contracts/Initializable.sol";
    import "./PoapRoles.sol";
     /**
     * @title Pausable contract
     * @dev Base contract which allows children to implement an emergency stop mechanism.
     * - Users can:
     *   # Pause contract if admin
     *   # Unpause contract if admin
     * @author POAP
     * - Developers:
     *   # Agustin Lavarello
     *   # Rodrigo Manuel Navarro Lajous
     *   # Ramiro Gonzales
     **/
    contract PoapPausable is Initializable, PoapRoles {
        /**
         * @dev Emmited when contract is paused
         */
        event Paused(address account);
        /**
         * @dev Emmited when contract is unpaused
         */
        event Unpaused(address account);
        // Boolean to save if contract is paused
        bool private _paused;
        function initialize() public initializer {
            _paused = false;
        }
        /**
         * @dev Get if contract is paused
         * @return ( bool ) If contract is paused
         */
        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, "Contract is Paused");
            _;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         */
        modifier whenPaused() {
            require(_paused, "Contract is not Paused");
            _;
        }
        /**
         * @dev Called by the owner to pause, triggers stopped state.
         * Requires 
         * - The msg sender to be the admin
         * - The contract does not have to be paused
         */
        function pause() public onlyAdmin whenNotPaused {
            _paused = true;
            emit Paused(msg.sender);
        }
         /**
         * @dev Called by the owner to pause, triggers unstopped state.
         * Requires 
         * - The msg sender to be the admin
         * - The contract does not have to be paused
         */
        function unpause() public onlyAdmin whenPaused {
            _paused = false;
            emit Unpaused(msg.sender);
        }
        // For future extensions
        uint256[50] private ______gap;
    }
    pragma solidity ^0.5.2;
    import "zos-lib/contracts/Initializable.sol";
    import "../../introspection/IERC165.sol";
    /**
     * @title ERC721 Non-Fungible Token Standard basic interface
     * @dev see https://eips.ethereum.org/EIPS/eip-721
     */
    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;
    }
    pragma solidity ^0.5.2;
    /**
     * @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 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
         */
        function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
        public returns (bytes4);
    }
    pragma solidity ^0.5.2;
    /**
     * @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;
        }
    }
    pragma solidity ^0.5.2;
    /**
     * 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;
        }
    }
    pragma solidity ^0.5.2;
    import "../math/SafeMath.sol";
    /**
     * @title Counters
     * @author Matt Condon (@shrugs)
     * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
     * of elements in a mapping, issuing ERC721 ids, or counting request ids
     *
     * Include with `using Counters for Counters.Counter;`
     * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath
     * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
     * directly accessed.
     */
    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 {
            counter._value += 1;
        }
        function decrement(Counter storage counter) internal {
            counter._value = counter._value.sub(1);
        }
    }
    pragma solidity ^0.5.2;
    import "zos-lib/contracts/Initializable.sol";
    import "./IERC165.sol";
    /**
     * @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;
    }
    pragma solidity ^0.5.2;
    /**
     * @title IERC165
     * @dev https://eips.ethereum.org/EIPS/eip-165
     */
    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);
    }
    pragma solidity ^0.5.2;
    import "zos-lib/contracts/Initializable.sol";
    import "./IERC721.sol";
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    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);
    }
    pragma solidity ^0.5.2;
    /**
     * @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];
        }
    }