In our previous article, we showed a way to integrate ordinals with smart contract, by placing an ordinal satoshi and a contract in the same UTXO. Today we introduce an alternative to integrate them, where they are in separate UTXOs.
As a showcase, we developed a smart contract that enables the on-chain auctioning of an ordinal. The smart contract guarantees that the auctioneer will receive the highest bid, while the bidder will obtain the ordinal.
In a UTXO model, a transaction can contain multiple inputs, each consuming a separate UTXO. In one of the UTXOs is our smart contract, another is the ordinal locked in a non-contract (i.e., P2PKH) UTXO. Bitcoin smart contract has the capability to access sibling inputs, called ScriptContext in sCrypt. Since Ordinal is also based on UTXO, a smart contract can access ordinal spent in a neighboring input and thus dictate its transfer.
To demonstrate how the idea manifests, we build a contract to auction an ordinal. It is open and transparent, where everyone can participate and the highest bidder wins when the bidding is over after a specified deadline.
There are two ways to interact with the contract. Let’s take a look at how they are implemented.
When there is a higher bid, the current highest bidder is updated and the previous highest bidder is refunded.
The bid method is pretty straightforward. It first checks if the bid is sufficiently large. If so, it updates the highest bidders. In the rest, it checks the new transaction’s outputs. The first output is just the next instance of the auction with updated state. The value locked in this output will be equal to the new bid. The second output will pay back the last highest bidder with the amount they were bidding. Lastly it adds change output.
when the auction expires, the auctioneer can close it and take the offer. The auctioneer must also transfers the ordinal to the highest bidder. This is where the contract controls
The above diagram shows a close transaction on the right. It differs from a bidding transaction in inputs and outputs.
- There is an additional input (first input) containing the ordinal we are auctioning off
- There is an output (first output) transfering the ordinal to the winning bidder.
The contract is called in the second input, while the ordinal is referenced in the first. They are in separate UTXOs, yet the contract can control the transfer of the ordinal.
The close method is slightly more involved. First, it checks that the call is made after the deadline using a typical time-lock pattern at Line 8. It then authenticates the auctioneer’s signature at Line 14, the only one allowed to close the auction.
this.prevouts in ScriptContext contains all pointers to the UTXOs referenced in the inputs, called outpoints. An outpoint contains two parts:
- TXID: 32 bytes
- Output index: 4 bytes.
A UTXO is in the output of a transaction uniquely identified by such an outpoint.
At Line 21, we extract the outpoint of the first input (first 36 bytes) and compare it against the actual ordinal’s UTXO, hardcoded when the auction starts and the contract is deployed. This ensures the authenticity of the ordinal and it cannot be fabricated.
We then construct and confirm the outputs as before. The first output is a regular P2PKH transfer to the highest bidder. The second output pays the auctioneer. Finally, we add change outputs if necessary.
Note the contract ensures the ordinal comes in the first input, so it ends up in the first output and gets transferred to the winner.
Here’s an example transaction executing the closing of an ordinal auction on Bitcoin:
This ordinal, inscribed with the text “Hello, sCrypt!”, was auctioned for a whopping 8 satoshis! The ordinal was transferred to the highest bidder with address “1NHJoK2ANVb8MtK7Er1uEqBckgbpZK7QUz”.
CoinGeek Roundtable with Joshua Henslee: 1Sat Ordinals on Bitcoin
New to blockchain? Check out CoinGeek’s Blockchain for Beginners section, the ultimate resource guide to learn more about blockchain technology.