Hic Et Nunc Smart Contracts (Part 1)
The HicEtNunc developer shut down the hicetnunc.xyz site. You can use one of the alternative marketplaces such as https://teia.art/.
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.
To create an NFT on HEN, an artist needs to go through a minting process which creates a record of the art information in 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.
Hackers recently stole digital artworks from HEN due to an exploit in one of the smart contracts. Attempting to understand the exploit got me interested in how these smart contracts work, but I couldn’t find any detailed explanations. I decided to document the HEN smart contracts so that anybody with some technical background can understand the design and features of these contracts.
Source code
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.
The GitHub source code consists of these main repositories:
- hicetnunc — the website and its graphical user interface (GUI).
- hicetnunc-api — application programming interface (API) backend for the website.
- smart-contracts — the smart contracts.
Tezos smart contracts
A smart contract is programming logic that defines rules and protocols for various use cases. After being deployed, a smart contract resides in the Tezos blockchain, decentralized and distributed on a network of Tezos nodes. A copy of each deployed smart contract resides on each node.
A smart contract can have one or more entry points, which can be called to invoke transactions. These entry points are similar to invoking a function in a high-level programming language. The transactions are confirmed by being added to a block in the blockchain.
Each Tezos smart contract has the following features:
- An account potentially holding tez.
- With an attached, typed, storage.
- With attached, typed parameters.
- With an attached, typed, script, representing a function returning:
- A new storage value.
- A list of operations to be executed after the script completes.
Smart contracts on the Tezos blockchain are written in the Michelson language. Since Michelson is very low-level and has a steep learning curve, it is easier for developers to use other popular programming languages like Python to declare the smart contract logic and then convert the Python code into the Michelson language format.
Note: Even though invoking entry points behave like functions and can be passed parameters, Michelson contracts do not return any custom values.
The HEN smart contracts use SmartPy, which is a high-level smart contracts Python library. SmartPy also provides an online development tool and a command-line interface (CLI) tool.
The HEN smart contracts follow the FA2-SmartPy implementation of the FA2 (TZIP-12) token standard contract specification. The FA2 standard is based on the ERC-20 (fungible), ERC-721 (non-fungible), ERC-1238 (non-transferable), and ERC-1155 (multi-asset contracts) token standards, enabling NFTs for digital assets on Ethereum.
An FA2 contract maintains a table that maps the NFT identifiers with their associated metadata. HEN stores the metadata online, and so the table contains the address of that metadata.
FA2 provides a standard API for token transfer operations. FA2 contracts manage a ledger that maps owner identities to token balances. Each FA2 contract also provides approval for external contracts or accounts to transfer the tokens.
HEN smart contracts
The Tezos blockchain has the following HEN smart contracts deployed:
- NFT tokens (aka OBJKTs).
- User profiles (aka SUBJKTs).
- Hic et Nunc Minter (OBJKT Swap v1).
- V2 marketplace (OBJKT Swap v2).
There are also three smart contracts for the media governance token (hDAO, OBJKT-hDAO Curation, and hDAO marketplace), but the use of this token isn’t widespread and is still under discussion.
Note: The Tezos smart contracts are not upgradeable. If a smart contract contains bugs, then deploy a new smart contract with a fix, and all marketplace code has to be adjusted to use the address of the new smart contract. All tokens must also be transferred to the new smart contract or swapped back to the previous owner’s wallets.
User flow
I recently wrote an article about how I minted some of my 3D art as NFTs on HEN. Here are the features supported by HEN for artists and collectors:
- Mint: Minting is the process of creating an NFT and recording its data into a smart contract.
- Swap: To put the artwork up for sale to collectors, the artist has to swap the minted artwork. The artist needs to specify how many minted editions will be swapped and the price in Tezos.
- Collect: With the artwork swapped, collectors can buy editions of the artwork.
- Cancel: Canceling swapped artwork will remove them from sale and send their tokens back to your wallet.
- Burn: Burning is the process of deleting the data associated with an NFT from a smart contract.
- Resell: Collectors can resell any NFT artwork they own by swapping their editions at their set price. The artists that created the artwork will receive their royalties once the artwork sells on the secondary market.
- Edit the user profile: Artists and collectors on HEN can configure their profiles using the
Settings
menu.
Contract design
Now that we have a high-level understanding of how artists create NFTs and how collectors buy and sell artwork on HEN let’s look under the hood at how the smart contracts make this work.
Minting
The GUI for minting is implemented in hicetnunc/src/pages/mint/index.js
using the React JavaScript library.
There are three steps to minting the digital artwork:
- Upload the digital assets to persistent storage.
- Request funds from a Tezos wallet to cover the blockchain fees.
- Create the NFT token in the blockchain.
Uploading assets
The GUI code uploads the artwork digital file and metadata to the Interplanetary File System (IPFS). IPFS provides decentralized storage of files with worldwide access.
Adding the artwork file to IPFS produces a content identifier (CID) directly derived from the file and links to the file in the IPFS network. The CID can only ever refer to one piece of content, and nobody can replace or alter the content, which ensures that the digital assets associated with a HEN NFT are unique.
The smart contract requires an IPFS metadata URI, which should resolve a JSON object describing the artwork. The HEN metadata schema follows the TZIP-21 standard for rich metadata. Here is an example of the JSON used by HEN for artwork in JPEG format:
{
"name": "HEN-Mesh",
"description": "Mesh design of the HicEtNunc logo",
"tags": [
"CG",
"3D",
"B3D",
"GEO",
"WIREFRAME",
"B&W",
"COMPLEX"
],
"symbol": "OBJKT",
"artifactUri":
"ipfs://QmS1r91dZadi4XQ6TuFcUVTUBnhphS7B428YdfETfjs6yL",
"displayUri":
"ipfs://QmXNMJVFtnCiZuLDgRFgoMCLG3JyMZXX4JP8qQ9cKtvG8g",
"thumbnailUri":
"ipfs://QmNrhZHUaEqxhyLfqoq1mtHSipkWHeT31LNHb1QEbDHgnc",
"creators": [
"tz1XtjZTzEM6EQ3TnUPUQviCD6WfcsZRHXbj"
],
"formats": [
{
"uri":
"ipfs://QmS1r91dZadi4XQ6TuFcUVTUBnhphS7B428YdfETfjs6yL",
"mimeType": "image/jpeg"
}
],
"decimals": 0,
"isBooleanAmount": false,
"shouldPreferSymbol": false
}
The metadata includes the URI’s of the following resources also stored on IPFS:
artifactUri
— the original artwork digital file.displayUri
— a smaller-scale version of the artwork used for the HEN website feed.thumbnailUri
— currently, this points to the same image for all of the artwork on HEN.
Note: The symbol for HEN NFT tokens is OBJKT. It’s common in the HEN community to refer to NFTs as OBJKTs.
The original digital file, the smaller-scale version, and the metadata are uploaded and pinned using the Infura IPFS node with the ipfs-http-client
client library. Pinning ensures the uploaded files are available all the time and globally by using servers tuned for high volume and reliability.
Requesting funds
The GUI code to request funds from a Tezos wallet and mint the artwork is src/context/HicetnuncContext.js
in the mint
method. The GUI uses the Taquito library Wallet API to communicate with your Tezos wallet of choice. In particular, HEN uses Taquito’s support for Beacon wallets which implements the TZIP-10 standard (for example, the Beacon extension, Temple wallet, or Kukai wallet).
When minting an OBJKT, you will notice two transactions in the blockchain:
- The first is for invoking the
mint_OBJKT
entry point on the minter smart contract. - The second is for
mint_OBJKT
subsequently invoking themint
entry point on the NFT token smart contract.
Each of these transactions has associated fees. Both require payment from your wallet for the following:
- Gas fee — the fee that you pay bakers who run Tezos nodes to include and propagate transactions into blocks of the blockchain.
- Storage fee — the fee to pay when the storage of a contract increases. Minting adds new data to the contract, which increases the storage size of the contract.
Transaction fees on Tezos are very low compared to other blockchains. Currently, minting on HEN costs only ~0.06 tez (~0.20 USD).
Creating the token
Even though the V2 HEN smart contracts are deployed, the deployed V1 contract code in objkt_swap_v1.py
is still used for minting only.
Note: This contract doesn’t include any access controls to limit which accounts are allowed to call the mint_OBJKT
function (which allows other 3rd party marketplaces like objkt.com to reuse the HEN minting). Also, HEN doesn’t charge any platform fee for doing the minting.
The V1 smart contract Python code imports the SmartPy package as sp
and extends the sp.Contract
class:
import smartpy as sp
class OBJKTSwap(sp.Contract):
The OBJKTSwap
constructor (init
method) is used to initialize new instances of the class. The smart contract initializes storage values and data structures used in other methods of the class.
def __init__(self, objkt, hdao, manager, metadata, curate):
self.fee = 0
self.amount = 0
self.royalties = 0
self.init(
swaps = sp.big_map(tkey=sp.TNat,
tvalue=sp.TRecord(issuer=sp.TAddress,
xtz_per_objkt=sp.TMutez,
objkt_id=sp.TNat, objkt_amount=sp.TNat)),
royalties = sp.big_map(tkey=sp.TNat,
tvalue=sp.TRecord(issuer=sp.TAddress,
royalties=sp.TNat)),
swap_id = 0,
objkt_id = 152,
objkt = objkt,
hdao = hdao,
manager = manager,
metadata = metadata,
genesis = sp.timestamp(0),
curate = curate,
locked = False
)
The HEN V1 smart contract doesn’t enable the NFT setting (non_fungible
) in the SmartPy FA2 configuration, which assumes that the total supply must be one. This configuration allows for multiple editions of OBJKTs on HEN.
The constructor has several variables, although the mint_OBJKT
method only uses objkt_id
and objkt
directly:
objkt_id
: A contract storage field for the ID of the next minted token.objkt
: A contract storage field for the address of the NFT token contract.
Here is the SmartPy Python code for the mint_OBJKT
method:
@sp.entry_point
def mint_OBJKT(self, params):
sp.verify((params.amount > 0) & ((params.royalties >= 0) &
(params.royalties <= 250)) & (params.amount <= 10000))
c = sp.contract(
sp.TRecord(
address=sp.TAddress,
amount=sp.TNat,
token_id=sp.TNat,
token_info=sp.TMap(sp.TString, sp.TBytes)
),
self.data.objkt,
entry_point = "mint"
).open_some()
sp.transfer(
sp.record(
address=params.address,
amount=params.amount,
token_id=self.data.objkt_id,
token_info={ '' : params.metadata }
),
sp.mutez(0),
c
)
self.data.royalties[self.data.objkt_id] =
sp.record(issuer=sp.sender, royalties=params.royalties)
self.data.objkt_id += 1
The @sp.entry_point
decorator marks the entry point.
The following parameter values are passed to the mint_OBJKT
function by the HEN GUI code:
address
— the artist Tezos wallet address.amount
— the number of editions.metadata
— the IPFS URI (“ipfs://”
+ CID) of the metadata JSON, hexadecimal encoded.royalties
— the royalties multiplied by 10.
At execution, operations in the body of the method evaluate in their natural order. Evaluation of the entry point is atomic, and if anything fails, all the operations reverse.
The code uses the sp.verify
command to prevent the entry point from proceeding with the following conditions:
amount
must be greater than 0 and less than or equal to 10000.royalties
must be greater or equal than 0 and less or equal than 250 (25%).
The code uses sp.contract
to reference the mint entry point of the NFT token smart contract. The sp.transfer
command invokes the mint
entry point with the parameter values. The FA2 code for the mint
entry point updates the following maps in the storage of the NFT token smart contract:
ledger
— associates the token identifiers created in the contract with their owner using the combination ofaddress
andobjkt_id
as the key and the amount as the value. The ledger follows the FA2 standard for tracking balance updates of multi-asset contracts.token_metadata
— records the metadata associated with every token stored in the contract using theobjkt_id
as the key and a record consisting ofobjkt_id
andmetadata
as the value. This map follows the FA2 standard for token metadata storage and access.
The rest of the code for mint_OBJKT
updates a royalties map in storage (V2 swaps don’t use this map). The objkt_id
value increments for the next mint operation.
The GUI code invoked the mint entry point has to wait for the blockchain to confirm the transaction. There is a chance that the transaction might fail due to insufficient funds for the storage required or due to a baker not processing the transaction.
If the transaction is confirmed, the NFT token smart contract’s ledger
will record the minted NFT.
Note: Your Tezos account doesn’t keep track of any of the tokens minted or owned. You query blockchain transactions to determine what tokens you own. Since this is a laborious and slow process, blockchain indexers such as TzKT or hicdex provide convenient tools and APIs to determine the list of tokens minted and owned. These indexers maintain their own metadata about the transactions and stay in sync with blockchain updates. Tezos wallet apps will also typically use an indexer to show all the tokens associated with your account.
Conclusion
This concludes the first part of exploring the HEN smart contracts. In the second part, I will go over the other top-level features that make HEN work. These are implemented using the V2 HEN smart contracts. I will also discuss the V1 smart contracts exploit and how V2 improved the design.
You can follow my 3D art on HEN.