Last Updated: 2021-08-18

Hic Et Nunc introduction

Hic Et Nunc (HEN) is an experimental non-fungible token (NFT) marketplace covering a wide range of visual art, interactive art, music, and animations from a diverse group of artists worldwide.

HEN has a diverse community of well-known and less-known artists with a wide range of backgrounds. HEN allows artists to sell their artwork for much lower fees and with significantly less environmental impact than other NFT marketplaces. On both Twitter and Discord, you will find artists that support each other by buying and promoting each other's work.

HEN is what is referred to as a DApp (decentralized application) and is built on the Tezos blockchain. HEN utilizes smart contracts to manage the minting, selling, and buying processes to ensure the artist is paid for each sale and receives royalties for secondary sales. The smart contracts store information about each NFT in the Tezos blockchain.

A unique aspect of HEN as an NFT marketplace is that it is open-sourced on GitHub. The code provides an insight into the workings of a marketplace that isn't possible with other closed source platforms.

There is also a growing number of tools made by the HEN community, many open-sourced.

What you'll build

In this codelab, you will build a web app that displays an image from the HEN marketplace. This web app will be a much simpler version of what the HEN marketplace website provides to view the available artworks for sale.

What you'll learn

This codelab focuses on web apps. Non-relevant concepts and code blocks are glossed over and are provided for you to simply copy and paste.

What you'll need

Familiarity with JavaScript (ES6) is strongly recommended to help you understand the code used in this codelab.

Optional: Get the complete app code

In this codelab, you build the app step-by-step from an incomplete version. If you prefer, you can also get the completed app from the GitHub repository.

In this section, you will set up your development environment. You will use a simple HTTP web server on your local machine using Node.js for doing development. The webserver runs on the http-server npm package, a simple zero-configuration http server for serving static files to the browser. This web server starts from the command line.

Download and install Node

This codelab uses the node package manager (npm) to install various packages. Make sure to install npm, which typically comes with Node.js.

If you haven't installed Node yet, download the latest stable release of Node.js and install it using all the default options.

Install the http-server package from npm

Install the http-server globally on your machine using the npm command-line tool. This will allow you to run a web server from anywhere on your computer.

Open a terminal window and enter the following npm command:

npm install -g http-server

Clone the repository

In this section, you clone the files you need for this codelab.

Download source code

Open a terminal and change to a folder where you usually store coding projects. If you don't have one, change it to your home folder. Unzip the downloaded zip file into your coding folder.

Open the start folder. This folder contains the following essential files that you'll be working with:

Let npm download any dependencies for the project by running this command:

npm install

Start a web server

Change to the start folder containing in the command line window, e.g.:

cd \codelab1\start

Start the server with this command:

http-server

You should see something like the following:

Starting up http-server, serving ./
Available on:
  http://192.168.86.36:8080
  http://127.0.0.1:8080
Hit CTRL-C to stop the server

Open your browser

Open a browser tab to http://localhost:8000. You should see a web page with the message: "Ready for the codelab!".

The HTTP server console should display something like this:

[2021-08-16T15:05:20.493Z]  "GET /" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
(node:26604) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated
(Use `node --trace-deprecation ...` to show where the warning was created)

In this section, you will create a query for the HEN marketplace data. You send a query to hicdex, a Tezos indexer focused on indexing HENs smart contracts activity.

The indexed data is made available by exposing a public GraphQL endpoint. You can interactively create GraphQL queries for hicdex using its GraphQL explorer.

You will use hicdex to query for an NFT minted on HEN and then display the image for that NFT.

You will use the following hicdex queries:

Query for token ID

Here is the query syntax you will use to get the NFT token ID:

query MyQuery {
  hic_et_nunc_swap(limit: 1, 
     where: {token: {mime: {_like: "image/%"}},
       contract_version: {_eq: "2"}}, order_by: {token_id: desc}) {
    token_id
  }
}

This query asks hicdex to return the token ID (token_id) of a single (limit: 1) token:

The result of this query will return the token ID as JSON:

{
  "data": {
    "hic_et_nunc_swap": [
      {
        "token_id": 1
      }
    ]
  }
}

Query for token information

Here is the query syntax you will use to get the token information:

query MyQuery($token_id: bigint) {
  hic_et_nunc_token(where: {id: {_eq: $token_id}}) {
    artifact_uri
  }
}

This query asks hicdex to return the token image URL (artifact_uri) for an NFT with an ID that equals (_eq) the given token ID (token_id).

The result of this query will return the token ID as JSON:

{
  "data": {
    "hic_et_nunc_token": [
      {
        "artifact_uri": "ipfs://..."
      }
    ]
  }
}

In this section, you will learn how to invoke the hicdex public API, which allows developers to invoke queries against the indexed HEN data it maintains.

Invoke the API

To query hicdex, you have to use its GraphQL endpoint at:

https://api.hicdex.com/v1/graphql

This involves making an HTTP POST request to that URL with a payload that consists of these values:

These three values need to pass in the body of the request in JSON format:

{
  query: "query MyQuery {...}",
  variables: {"name": value},
  operationName: "MyQuery"
}

For the query to get the NFT token ID, there aren't any parameter values to be passed along in the request, so the variables value will be the JSON empty value of {}.

To invoke the endpoint using JavaScript, you will use the Fetch API, which allows you to make HTTP requests.

Add the following JavaScript code between the script tags in the HTML:

const data = {
    query: `query MyQuery {
      hic_et_nunc_swap(limit: 1, 
        where: {token: {mime: {_like: "image/%"}},
         contract_version: {_eq: "2"}}, order_by: {token_id: desc}) {
        token_id
      }
    }`,
    variables: {},
    operationName: "MyQuery"
}

fetch("https://api.hicdex.com/v1/graphql", {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(data),
    })
    .then(response => response.json())
    .then(data => {
        console.log('Success: ', data)
    })
    .catch((error) => {
        console.error('Error: ', error)
    })

Save the index.html file and force a reload of the index.html file in your browser (in Chrome, use Ctrl+F5).

DevTools Detour

DevTools provides a quick, easy way to check your index.html file logging. Open up DevTools by using the Ctrl+Shift+I keyboard shortcut. Click on the Console tab to see the logging output from the JavaScript code.

You should see something like this for the console output:

The result of this query returns the token ID as JSON:

{
  "data": {
    "hic_et_nunc_swap": [
      {
        "token_id": 200165
      }
    ]
  }
}

Adding logging statements to your JavaScript code is an easy way to debug the logic and confirm that the data is in the format you expect.

Organize the code

Since we will be doing the same kind of fetch operations to query hicdex, it makes sense to create a reusable function instead of adding more fetch calls. Also, instead of using the Promise notation (then, catch), which can make the code deeply nested, we will make the code read more synchronously by using async functions.

Remove the current JavaScript code between the script tags and replace it with:

async function fetchHicdex(query, variables, operationName) {
    const result = await fetch(
        "https://api.hicdex.com/v1/graphql", {
            method: "POST",
            body: JSON.stringify({
                query: query,
                variables: variables,
                operationName: operationName
            })
        }
    )
    const json = await result.json()
    console.log(JSON.stringify(json))
    return json
}

async function querySwap() {
    const {
        errors,
        data
    } = await fetchHicdex(`query MyQuery {
          hic_et_nunc_swap(limit: 1, 
            where: {token: {mime: {_like: "image/%"}},
             contract_version: {_eq: "2"}},
             order_by: {token_id: desc}) {
               token_id
          }
        }`, {}, "MyQuery")
    if (errors) {
        console.error(errors)
        return false
    }
    const result = data.hic_et_nunc_swap
    if (result.length == 0) {
        return false
    }
    console.log(result)
    return result
}
querySwap()

This code logically does the same as the previous code, but sets up the code to be more reusable and readable as we add new JavaScript logic. There is also better error handling by explicitly checking for hicdex errors and verifying that we got the results we expected. The output in the DevTools console should be similar to before.

In this section, you will query hicdex for the information associated with a particular token ID.

Replace the previous querySwap() call (not the function declaration) with the following JavaScript code:

async function queryTokenInfo(token_id) {
    const {
        errors,
        data
    } = await fetchHicdex(`query MyQuery($token_id: bigint) {
              hic_et_nunc_token(where: {id: {_eq: $token_id}}) {
                artifact_uri
              }
          }`, {
        token_id: token_id
    }, "MyQuery")
    if (errors) {
        console.error(errors)
        return false
    }
    const result = data.hic_et_nunc_token
    if (result.length == 0) {
        return false
    }
    console.log(result)
    return result
}

async function displayNft() {
    const swap = await querySwap()
    if (swap) {
        const info = await queryTokenInfo(swap[0].token_id)
    }
}
displayNft()

Save the index.html file and force a reload of the index.html file in your browser.

In DevTools, you will see the result of this query returns the token artifact URL as JSON:

{
  "data": {
    "hic_et_nunc_token": [
      {
        "artifact_uri": 
            "ipfs://QmS1r91dZadi4XQ6TuFcUVTUBnhphS7B428YdfETfjs6yL"
      }
    ]
  }
}

Now that you have the URL for the image of a HEN NFT, the next step is to display that image using HTML.

In this section, you will use the artifact URL for the token to display the image of an NFT in HTML.

Configure CSS

Add the following CSS styling inside the head tag:

<style>
    body,
    html {
        margin: 0;
        padding: 0;
        overflow: hidden;
    }
    
    img {
        width: 100vw;
        height: 100vh;
        object-fit: contain;
    }
</style>

This CSS styling will display an image along the entire width of the browser window.

Configure HTML

To display the image we can use the img tag. Change the contents of the root div tag to:

<div id="root">
    <img id="image" />
</div>

The img tag has an id of "image" which we can use to reference it in JavaScript code.

Working with IPFS images

The "artifact_uri" provided by the hicdex query result starts with the IPFS protocol:

ipfs://QmZSXELDJEhEJ6BsVdPQozetm34LDWTApyW6wQu1zv2LUg

Browsers like Chrome cannot directly display images with this protocol. To display an image with the img tag, you need an HTTP URL. An IPFS HTTP gateway such as ipfs.io allows browsers to access files stored on the IPFS network using the HTTP protocol.

To convert a URL with the IPFS protocol to the HTTP protocol, you must append the content identifier (CID) to the gateway address. The CID is the large alphanumeric ID after the ipfs:// protocol.

To convert the IPFS address to an HTTP address, add the following JavaScript:

function ipfsToHttp(url) {
    return url.replace('ipfs://', 'https://ipfs.io/ipfs/')
}

This code updates the URL string value by replacing the IPFS protocol prefix with the HTTP gateway address.

Updating the image

Replace the existing displayNft function declaration with the following code to display the NFT image:

async function displayNft() {
    const swap = await querySwap()
    if (swap) {
        const info = await queryTokenInfo(swap[0].token_id)
        if (info) {
            image.src = ipfsToHttp(info[0].artifact_uri)
        }
    }
}
displayNft()

This code converts the artifact_uri value to an HTTP gateway URL and then updates the img tag src value so that the browser will display the image.

Ideas for improvement

The code for this web app is quite simple. Various aspects could be improved, for example:

Congratulations, you've successfully built your first HEN web app!!

You invoked the hicdex API to query for NFT token information and then used that to display the image of a HEN NFT. You learned how to use DevTools to view the logs of your web app.

You now know some of the basics of how the HEN website works.

Further reading

Reference docs