Both Obsidian.btc (my co-founder at Byzantion) and I got involved in Stacks during the Blockstack 1.0 days through the initial offering. We are strong believers in Bitcoin as a base layer for money, which made us very interested in the way Stacks was building on top of it.
We have followed the ecosystem’s development ever since, and after Stacks 2.0 mainnet launch last year we noticed the vast improvements in dev tooling and growth in the developer community. We decided it was finally time to build on Stacks.
Around that time, the summer of 2021, we were trading NFTs on Ethereum and thinking about the idea of developing a marketplace on Stacks, but there were no NFT collections on Stacks yet. In the latter half of the summer, we noticed an NFT collection come out on Stacks. It was the “hello world” of NFT projects, a copy of CryptoPunks on Stacks. That sparked an idea: creating an NFT collection to learn the whole process, so we could then help others launch theirs with our ultimate goal of building up an ecosystem that could support an NFT marketplace.
This led us to launch a generative art collection called Mandelbrots, which entailed:
- Creating the art
- Creating standardized metadata in accordance with SIP-009
- Storing the images and metadata on decentralized storage (IPFS)
- Writing a smart contract
- Building a launch site
After we did all of this and launched our NFT project, artists started reaching out asking us if we would help them launch their projects too. This is what gave rise to Byzantion as a platform.
The Problem With NFT Demand
Across all NFT ecosystems on ETH, SOL, and STX, there exists a similar dynamic at play between supply and demand. How this plays out in most cases is that there is a finite number of NFTs in a collection. The owners of those will list them for sale at various prices based on what they think they are worth. This process will create the “floor,” or the lowest price at which you could buy one of these NFTs. If the floor is close to demand, pieces listed at “floor” prices will be relatively liquid and move off the market quickly.
The issue with this is sellers have no clear view into how many buyers there are at the floor price, not to mention how many buyers there are below floor price at different price points.
This problem is painfully obvious to anyone who has witnessed an undercutting battle. This occurs when several sellers in a collection want to sell their items quickly and start lowering prices below the floor. Because they have no idea at what price there is demand, they keep undercutting until they sell their item, causing the floor to drop sharply.
Creating an NFT Order Book
The first step to solving this problem is the concept of a “collection bid,” popularized by LooksRare, which essentially allows a user to place a bid on an entire collection. Anyone who owns any item in this collection can then accept that bid and sell their NFT to the bidder. This gives a way for a buyer to purchase a “floor item” in a given collection, without specifying which specific NFT they want to purchase. This increases liquidity because it allows the bidder to express interest without the seller having to list their item first.
The second step in solving this issue is where we come in: allowing for any number of collection bids at any price and clearly displaying those to sellers in an order book.
The clear display of the order book is the real differentiator from existing solutions on other marketplaces. It allows traders to clearly see where demand exists and at what price points.
No bidding contract would be complete without the traditional feature of bidding on a specific item. The ability to bid on a single item was our first iteration of bidding functionality. In this contract, we upgraded that feature by allowing multiple bids to exist on a single item and for a single bidder to bid multiple times on an item (even though there’s not much of a point in that).
In addition to giving users a way to bid on items that would likely be floor items, we also wanted an order book to be able to exist on certain subsets of a collection partitioned by attributes. Sometimes a buyer does not want just any floor NFT, they want any NFT in a collection with a particular trait (a crown, for example). They don’t care which exact NFT they buy, as long as it is one with a crown. We felt this functionality was necessary to complete the order book process, so in addition to collection bids, you can place “trait bids” - a bid on a set of items with certain traits. While collection offers existed on LooksRare, trait bids were a new feature that we hadn’t seen elsewhere.
While these 3 types of bids have different mechanics and are not all relevant to every holder in a collection, we wanted to tie together a cohesive order book. So we ended up building the order book in such a way that only bids that applied to pieces in your portfolio show up and can be accepted, meaning you don’t have to look at different order books for the collection, certain traits, and certain items - it’s all there in one view!
Writing the Smart Contract
The high level challenges with this were less at the smart contract level and more at the UI and data infrastructure level. Hats off to our frontend and backend team on getting this across the finish line!
But there was still a good amount of work in creating the contract for the order book. We had both individual item bidding and collection bidding (one bid per item or per collection) built into our previous marketplace contract, so we used that as a jumping off point and modified it to create the order book. In the process, we mapped out the functions we would need including:
- Bid-item: allowing a user to bid on a specific NFT
- Collection-bid: allowing a user to bid on an NFT collection
- Multiple-bid (trait bid): allowing a user to bid on any NFTs in a collection with one or more selected traits
Breaking Down the Bid-Item Function
This function allows a user to bid on a specific item. Say this user loves the newly released Marbling collection and wants to make a bid on the rarest item in the collection—this function allows them to do so. Let’s take a look at the code.
As you can see in the snippet above, the bid function takes in a few parameters in the first line: the collection ID, the ID of the specific NFT being bid on, and the amount the user is wanting to bid. Bid-item then defines some local variables that will be used later in the function.
We then check to make sure that the bid is greater than 0 and that the contract has not been “turned off” with the shutoff-valve variable. This shutoff variable would only really be utilized in case of an emergency if an exploit was discovered. This would allow the contract owner to shut down everything in the contract except for the admin functions and return assets (both NFTs and bids in escrow) to owners.
After these checks are passed, the contract transfers STX from the tx-sender (the bidder) into the contract to be held in escrow. We then have a section that could charge the user the bid-cost amount if Byzantion decided to charge a small fee for bidding functionality. We also update the item-bids map to include the bid from this user on this item (Marbling #236 in this case). In Clarity, maps are datamaps comprised of a key and a value, with the value typically being a tuple.
The item-bids map includes information about the collection and the item being bid on, the buyer that’s bidding, and a unique identifier to track that bid (the order variable). Importantly, after the STX are sent into escrow and all of the relevant maps are updated, we print out the information about this bid to be logged in both the API and the Stacks Explorer.
The print function simply evaluates and returns its input expression. This print statement is an integral part of how Byzantion’s backend picks up the information needed to keep up with these bids. It allows us to track the order number of the bid, the item, and the function type.
You can see the code for the full function in lines 167-197 of the smart contract here. If you want to dive deeper into the code, download a free breakdown where I walk through the collection-bid and multiple-bid functions, as well as the three functions for accepting those bids.
The Impact of the Order Book
This new contract and feature set happened to launch just before we had a large drawdown across all of crypto, accelerated by the recent fall of LUNA/UST. Since NFTs had become more widely adopted we haven’t seen many sharp drawdowns like this, so it was interesting to monitor trader behavior.
It seems when we have a fast drawdown in prices on fungible tokens, NFTs lose value too, not only in fiat terms but in the currency they are priced in (meaning, for example, if STX dropped from $1 to $0.50, an NFT priced at 100 STX may simultaneously fall to a price of 60 STX), as people start looking for liquidity. This new order book gave people the ability to find liquidity instantly when they were looking for it.
You can see in the chart below that many of the days with sharp negative daily price changes for STX corresponded with spikes in collection bids being accepted. This could also be true for large positive spikes in price, as traders typically try to exit to liquidity when the underlying crypto asset prices experience greater volatility.
The order book we created, which allows bids on single items, groups of items sharing specific traits, and entire collections, is unlike anything else we have seen in NFT marketplaces. We were able to accomplish this through fully on-chain transactions enabled by Clarity.
Due to costs, marketplaces built on Ethereum do not use on-chain listings or bids. This makes NFT activity more difficult for third parties to keep up with. With low enough costs for listings and bids to occur on the Stacks chain and Clarity’s human-readable, non-compiled syntax, we were able to not only build our order book, but to make that order book open for any other party to hook into and read. Byzantion’s order book now gives us a more complete view of the demand that exists at different price ranges below the floor.
As traders ourselves, we are excited to bring a feature set to Stacks that creates a more robust experience for buyers and sellers and enables a more liquid market for NFT assets. We hope that it makes trading JPEGs on Stacks even more fun!
We would love for you to go check out our order book bidding features. If you own an NFT, navigate to its collection on Byzantion.xyz, and then head to the order book tab. See if there are any bids that pique your interest! Similarly, if you’ve been wanting to buy a new NFT in a particular collection, you can try to pick up an item below floor price by placing a collection bid or a trait bid on the same tab!
If you want to dive deeper into the code, download a free breakdown where I walk through the code behind the order book and give you more insight into the powers of Clarity.