contract ERC1155Tradable is ERC1155, ERC1155MintBurn, ERC1155Metadata, Ownable, MinterRole, WhitelistAdminRole {

	uint256 internal _currentTokenID = 0;
	mapping(uint256 => address) public creators;
	mapping(uint256 => uint256) public tokenSupply;
	mapping(uint256 => uint256) public tokenMaxSupply;
	// Contract name
	string public name;
	// Contract symbol
	string public symbol;

	constructor(
		string memory _name,
		string memory _symbol
	) public {
		name = _name;
		symbol = _symbol;
	}

  function addWhitelistAdmin(address account) public onlyOwner {
    _addWhitelistAdmin(account);
  }

	function removeWhitelistAdmin(address account) public onlyOwner {
		_removeWhitelistAdmin(account);
	}

	function addMinter(address account) public onlyOwner {
		_addMinter(account);
	}

	function removeMinter(address account) public onlyOwner {
		_removeMinter(account);
	}

	function uri(uint256 _id) public view returns (string memory) {
		require(_exists(_id), "ERC1155Tradable#uri: NONEXISTENT_TOKEN");
    return string(abi.encodePacked(baseMetadataURI, _uint2str(_id), ".json"));
	}

	/**
	 * @dev Returns the total quantity for a token ID
	 * @param _id uint256 ID of the token to query
	 * @return amount of token in existence
	 */
	function totalSupply(uint256 _id) public view returns (uint256) {
		return tokenSupply[_id];
	}

	/**
	 * @dev Returns the max quantity for a token ID
	 * @param _id uint256 ID of the token to query
	 * @return amount of token in existence
	 */
	function maxSupply(uint256 _id) public view returns (uint256) {
		return tokenMaxSupply[_id];
	}

	/**
	 * @dev Will update the base URL of token's URI
	 * @param _newBaseMetadataURI New base URL of token's URI
	 */
	function setBaseMetadataURI(string memory _newBaseMetadataURI) public onlyWhitelistAdmin {
		_setBaseMetadataURI(_newBaseMetadataURI);
    if (_currentTokenID > 0) {
      _logURIs(_currentTokenID);
    }
	}

	/**
	 * @dev Creates a new token type and assigns _initialSupply to an address
	 * @param _maxSupply max supply allowed
	 * @param _initialSupply Optional amount to supply the first owner
	 * @param _data Optional data to pass if receiver is contract
	 * @return The newly created token ID
	 */
	function create(
		uint256 _maxSupply,
		uint256 _initialSupply,
		bytes calldata _data
	) external onlyWhitelistAdmin returns (uint256 tokenId) {
		require(_initialSupply <= _maxSupply, "ERC1155Tradable#create: Initial supply cannot be more than max supply");
		uint256 _id = _getNextTokenID();
		_incrementTokenTypeId();
		creators[_id] = msg.sender;

    if (bytes(baseMetadataURI).length > 0) {
      _logURI(_id);
    }

		if (_initialSupply != 0) _mint(msg.sender, _id, _initialSupply, _data);
		tokenSupply[_id] = _initialSupply;
		tokenMaxSupply[_id] = _maxSupply;
		return _id;
	}

	/**
	 * @dev Mints some amount of tokens to an address
	 * @param _to          Address of the future owner of the token
	 * @param _id          Token ID to mint
	 * @param _quantity    Amount of tokens to mint
	 * @param _data        Data to pass if receiver is contract
	 */
	function mint(
		address _to,
		uint256 _id,
		uint256 _quantity,
		bytes memory _data
	) public onlyMinter {
    require(_exists(_id), "ERC1155Tradable#mint: NONEXISTENT_TOKEN");
    require(_to != address(0), "ERC1155Tradable#mint: INVALID_RECIPIENT");
		require(tokenSupply[_id].add(_quantity) <= tokenMaxSupply[_id], "ERC1155Tradable#mint: mint supply cannot be more than max supply");

		_mint(_to, _id, _quantity, _data);
		tokenSupply[_id] = tokenSupply[_id].add(_quantity);
	}

  /**
	 * @dev Burns some amount of tokens from an address
	 * @param _from        Address of the owner of the token
	 * @param _id          Token ID to burn
	 * @param _quantity    Amount of tokens to burn
	 */
  function burn(address _from, uint256 _id, uint256 _quantity) public {
    require(_exists(_id), "ERC1155Tradable#burn: NONEXISTENT_TOKEN");
    require(_from == _msgSender() || isApprovedForAll(_from, _msgSender()), "ERC1155Tradable#burn: caller is not owner nor approved");

    _burn(_from, _id, _quantity);
    tokenSupply[_id] = tokenSupply[_id].sub(_quantity);
  }

	/**
	 * @dev Returns whether the specified token exists by checking to see if it has a creator
	 * @param _id uint256 ID of the token to query the existence of
	 * @return bool whether the token exists
	 */
	function _exists(uint256 _id) internal view returns (bool) {
		return creators[_id] != address(0);
	}

	/**
	 * @dev calculates the next token ID based on value of _currentTokenID
	 * @return uint256 for the next token ID
	 */
	function _getNextTokenID() private view returns (uint256) {
		return _currentTokenID.add(1);
	}

	/**
	 * @dev increments the value of _currentTokenID
	 */
	function _incrementTokenTypeId() private {
		_currentTokenID++;
	}
}
/**
 * @title ERC1155Tradable
 * ERC1155Tradable - ERC1155 contract that whitelists an operator address, 
 * has create and mint functionality, and supports useful standards from OpenZeppelin,
 like _exists(), name(), symbol(), and totalSupply()
 */
function _getNextTokenID() private view returns (uint256) {
	return _currentTokenID.add(1);
}

/**
 	 * @dev calculates the next token ID based on value of _currentTokenID
 	 * @return uint256 for the next token ID
 	 */