HIP-0006: Trustless Live Auctions for Handshake
We propose an ascending-price auction system for HNS domain names without trust or
third-party custody. A name owner initiates an auction by locking their TLD with
a verifiable script. Bidders place bids by partially-signing
and the name owner eventually picks the highest bid and atomically swaps the name
ownership for the payment. The protocol requires more interactivity and centralization
than the current Shakedex-style HIP-0001 auction format, but with some compelling trade-offs.
In addition, the limits on collaborative transaction construction (like atomic swaps)
mean that the buyer can not add a change output to the final transaction. Because there is
no change output, the name owner must pay the miner fee out of their sale price in the
This proposal is based on HIP-0001, and
in particular the Swap Script, Transaction Structure, and Swap Proof. Handshake
SIGHASH_SINGLEREVERSE are also required.
Recall the HIP-0001 Swap Script only allows two covenant actions be applied to the name:
The name owner can sign a TRANSFER with their private key
Anyone can FINALIZE without a signature
The auction technically begins when a name owner FINALIZEs a transfer of their name to a HIP-0001 locking script. The name owner must then publicly reveal the redeem script for the address that owns the name, so it can be verified. The locking script prevents the name owner from canceling an in-progress name ownership transfer. Since there is a small amount of interactivity required in the protocol, the name owner must also publish “where” the auction will take place. The auction “location” is just some internet endpoint where bidders can submit bids. It can be a private email address (in which case the name owner may choose to keep all bids confidential or truly blind), or it can be a public server that announces every new bid as they arrive.
A user that wants to bid on the name constructs a partially-signed transaction in the following format:
vin: 0: current name owner, encumbered by Swap Script 1: A UTXO controlled by the bidder of some value V vout: 0: TRANSFER to the bidder's address
Input 1 is signed with
SIGHASH_ANYONECANPAY | SIGHASH_SINGLEREVERSE. This commits
the incoming payment to the name transfer. Note that Input 0 is not signed yet,
but it must be the current name-owning UTXO or the linked output 0 would be invalid.
Note that this transaction has no change output. Because the bidder is signing
with single-reverse, they can’t specify a change output in addition to the name
transfer output. If they were to sign with
SIGHASH_ALL then the next step of
the protocol would not be possible. If they signed with
SIGHASH_SINGLE they could
commit to a change address, but not the name transfer.
Having no change output means the INPUT coin they are spending MUST BE THE VALUE OF THE BID THEY INTEND TO PLACE ON THE NAME. This is a big inconvenience that can be solved in at least these two ways:
The user prepares their wallet in advance and collects UTXOs with a range of values (1 HNS, 10 HNS, 100 HNS, 1000 HNS, etc).
When the user wants to bid, their wallet creates a “perfect” UTXO by spending to itself, creating a coin with the exact value desired. This output can be used (even before it is confirmed) in this protocol.
The name owner collects partially-signed bids and after some time, declares the highest value bid the winner. The name owner completes the auction by adding a payment output to the partially-signed transaction, signing and broadcasting:
vin: 0: current name owner, encumbered by Swap Script 1: A UTXO controlled by the bidder of some value V vout: 0: TRANSFER to the bidder's address 1: payment to the name-owner
The name owner pays themselves from the fully funded transaction, and signs input 0
SIGHASH_ALL. This transaction is a valid atomic swap and can be broadcast to the network.
Because the locking script prevents all other name actions, the auction winner can
FINALIZE this name transfer any time after the 288-block lockout without any other
Note that since the bidder creates the INPUT and the name owner creates the OUTPUT, the name owner MUST also pay the miner fee for this transaction. They do so by reducing the value of output 1, effectively paying the miner fee out of the money they earned by the sale.
Bids must remain valid off-chain
The partially-signed bid transactions are collected by the name owner and commit to spending a UTXO of some value V from the blockchain. In the simplest construction, nothing prevents the bidder from invalidating this off-chain transaction by double-spending the coin they used to bid. For this reason, the name owner must routinely check that all submitted bids are still valid. They can try finalizing the highest bid and if that fails, try to finalize the second-highest bid, etc.
More advanced constructions may be possible to more effectively prevent this, see the next section for one possible extension.
“Locked bids” mode
To ensure that bids are funded by reliable outputs, the bidder and name owner can engage in a time-lock multisig contract that will ensure the bids are valid until the end of the auction. Bidders will effectively be “locking up” their bids for the duration of the auction. After the auction expiry time, bidders can “redeem” their losing bids with (almost) no potential grief. This pattern should be very familiar to Handshake users!
OPEN: Name owners must include extra metadata in their auction announcement:
An indication that all bids must be submitted using this mode, or they will be ignored.
An auction expiration time, set by blockchain height:
A public key from the name owner’s wallet that they can sign transactions with:
BID: When a user wants to submit a bid for this name, they follow these steps:
Derive a public key from their wallet that they can sign with:
Combine their key and the auction announcement data into the script below:
Derive the corresponding address for that script and send their bid amount to that address:
Construct a partially-signed TX
bid_submit_txas defined in Auction BID but with the following modifications:
Input 1 of this transaction will spend the UTXO created by
The redeem script in the witness of input 1 will be a partially-signed multisig spend defined below as
bid_submit_script, with a single
0x00byte as a placeholder for the name owner’s signature.
Bidder submits the following data to the name owner:
bid_lock_scriptwith all data inserted such that the name owner can verify it matches the address in the output of
bid_submit_tx(which will explicitly indicate the outpoint of the UTXO created by
FINALIZE: The name owner must finalize the atomic swap BEFORE
Name owner chooses the highest bid and verifies that its
bid_lock_txis confirmed on chain, and that the output address is spendable by
bid_lock_script. They construct a final TX as defined in Auction FINALIZE:
Name owner inserts their self-payment (minus miner fee) at output 1 like normal.
Name owner signs the name-owning UTXO into a TRANSFER at input 0 like normal.
Name owner must now also COMPLETE THE MULTISIG in input 1 to fund the transaction. This is accomplished by filling in
owner_sigin the incomplete
bid_submit_scriptthat the bidder has included in the witness of
REDEEM: If the bidder loses the auction or if for any reason the
reached without their
bid_lock_tx output being spent into an atomic swap, they can
refund their bid value using
bid_redeem_script below. That refund transaction MUST
have a locktime set at
expiration_height and of course can not be submitted until that time.
The only potential grief attack is a race condition after
expiration_height has been reached
in which both spending paths are valid: the name owner can still complete the atomic swap, and the
bidder can now submit a refund. However, if the name owner has already completed the auction
and finalized a “winner” then only the refund path is valid.
OP_IF OP_2 <owner_pk> <bidder_pk> OP_2 OP_CHECKMULTISIG OP_ELSE <expiration_height> OP_CHECKLOCKTIMEVERIFY OP_DROP <bidder_pk> OP_CHECKSIG OP_ENDIF
OP_1 0x00 # placeholder for <owner_sig> <bidder_sig>
A semi-custodial platform could help ensure auction finality
One possible scenario with this protocol is that the name owner never finalizes any bids. They might have decided a “reserve price” was not met or just changed their mind about the sale. One technique that may be used to ensure bidders that a winner will be chosen by a certain time is to include a trusted moderator.
Auctions could be held on a platform like Shakedex or run by an agency with a good reputation like Namebase or Kyokan. This agent would publish a static public key for their platform, and name owners would have to incorporate that key along with a relative locktime into the HIP-0001 locking script like so:
OP_TYPE 0x09 // TRANSFER OP_EQUAL OP_IF OP_IF <name-owner's public key> OP_ELSE <timespan of auction> OP_CSV OP_DROP <platform's public key> OP_ENDIF OP_CHECKSIG OP_ELSE OP_TYPE 0x0a // FINALIZE OP_EQUAL OP_ENDIF
What this does is add a rule to the locking script such that, after a certain amount of time (i.e. 5 days) the platform operator can finalize the auction with their own private key instead of waiting for the name owner to do so. Technically this means that if the name owner does not finalize the auction in time (or simply transfer the name back to themselves – publicly ending the auction on the blockchain) the platform can steal the name OR the value of the highest bid. In some cases, this may be the actual terms-of-service. In more common cases we can assume a platform with interest in maintaining a good reputation would award the highest bidder with the name and pay the name owner from output 1 of the final transaction.
A commercial platform could also improve auction quality by regularly performing the offline bid-validity checks mentioned in the previous section. The platform would have to be trusted (via reputation) to perform this job correctly or some bids may be censored.
Where & When
Because this auction protocol is interactive, bidders need a place to send their partially-signed transactions where they are accessible to the name owner to finalize. A name owner could therefore open an auction by adding TXT records to their zone announcing the endpoint to send bids, along with the redeem script to prove the name is locked by HIP-0001! Once this UPDATE has been sent, users just need to wait for the name owner to FINALIZE the transfer to the HIP-0001 address, and the auction is LIVE!
Edit on GitHub