Skip to content

Proposed Specification for Asynchronous Sell Offers for Inscriptions #4291

@joshdoman

Description

@joshdoman

This issue is a cousin to issue #4290 and proposes a specification for asynchronous sell offers for inscriptions.

PSBT Format (v1)

Sell Offer PSBT

A PSBT sell offer consists of a single input and output. The input UTXO holds the offered inscription, and the output value equals the input value plus the value required to sell the inscription. The input UTXO is signed using the sighash SINGLE|ANYONECANPAY.

Finalized PSBT

Input 0: The buyer's smallest input (signed with ALL)
Input 1: The seller's input (signed with SINGLE|ANYONECANPAY)
Input 2+: Additional buyer inputs needed to fund the transaction

Output 0: The buyer's receive output (value of input 0 + input 1, plus any sats required to meet minimal postage)
Output 1: The seller's output
Output 2: The buyer's change output (if non-dust)

Inscriptions transfer by sat, so this structure ensures that the seller's inscription lands on the buyer's receive output.

Optimization 1(a)

This implementation can be improved by tracking the exact sat position of the inscription in input 1 and ensuring output 0 contains only:

  1. That specific sat (and the preceding sats from input 1)
  2. Enough sats to meet the minimum postage

This approach eliminates the risk of unnecessarily inflating output 0, if input 1 has a high postage.

Analysis

The chief benefit of this approach is its simplicity. In theory, a user could accept an offer with a single cardinal UTXO in the ord wallet. If Optimization 1(a) is implemented, for instance, there might exist enough sats in the seller's input post-inscription to pay the fees for the whole transaction (conceivably, sellers might even have an economic incentive to intentionally provide post-inscription sats in their input - ord could help with this).

Alternative PSBT Format (v2)

Sell Offer PSBT

Same.

Finalized PSBT

Input 0: The buyer's smallest input (signed with ALL)
Input 1: The buyer's second smallest input (signed with ALL)
Input 2: The seller's input (signed with SINGLE|ANYONECANPAY)
Input 3+: Additional buyer inputs needed to fund the transaction

Output 0: The buyer's cardinal output (value of input 0 + input 1)
Output 1: The buyer's receive output (value of input 2, plus any sats required to meet minimal postage)
Output 2: The seller's output
Output 3: The buyer's change output (may not be dust)

The user must have at least three cardinal UTXOs in their wallet if --fee-rate is non-zero. If they do not, ord should throw an error.

Optimization 2(a)

If there are sufficient UTXOs to purchase the inscription but insufficient UTXOs to construct the funded PSBT and pay the necessary fees, package relay could be used to broadcast the funded PSBT alongside a preparation transaction, which creates the necessary inputs.

Optimization 2(b)

It is desirable for the user's wallet to have at least 3 cardinal UTXO's after accepting the offer (so that they can accept additional offers and pay the transaction fee). If all UTXOs are consumed as inputs, the user will only have 2 cardinal UTXO's after accepting the offer (output 0 and output 3). In that case, the finalized funded PSBT should be modified to add an output 4, which splits the change output between it and output 3.

Here is what the outputs of the funded PSBT should look like with format v2 if all cardinal UTXOs are used in the inputs:

Output 0: The buyer's cardinal output (value of input 0 + input 1)
Output 1: The buyer's receive output (value of input 2, plus any sats required to meet minimal postage)
Output 2: The seller's output
Output 3: The buyer's first change output (50% of total change, may not be dust)
Output 4: The buyer's second change output (50% of total change, may not be dust)

Analysis

The chief benefit of this approach is it reduces the probability two transactions are needed to accept the PSBT offer. As long as three UTXOs exist, the offer can be accepted in a single transaction. The downside is that you need to sign at least three inputs vs. one in format v1 (or two if you need a second transaction to CFPB the first via package relay). For this reason, format v2 is not necessarily more fee efficient vs. format v1. It merely increases the probability only one transaction is needed and increases the probability the buyer's receive output is reasonably sized.*

*this probability can be further increased if the equivalent of Optimization 1(a) is used to.

CLI Interface

This feature would use the subcommand async-offer proposed in #4290 .

Creating an Inscription Sell Offer

A user can offer to sell the inscription <INSCRIPTION_ID> for AMOUNT using:

ord wallet async-offer create --outgoing <INSCRIPTION_ID> --amount <AMOUNT>

This will generate a partially signed bitcoin transaction (PSBT), which the user can broadcast for anyone to fund and sign.

Accepting an Inscription Sell Offer

A user can accept the offer in PSBT to sell <INSCRIPTION_ID> for AMOUNT using:

ord wallet async-offer accept --outgoing <INSCRIPTION_ID> --amount <AMOUNT> --fee-rate <FEE_RATE>

Request for Feedback

  1. How do the proposed PSBT formats compare to other formats that have been proposed? Format v2 is partially inspired by the two input bumping format proposed in Offer PSBT design #2706, but it streamlines "recycling" and eliminates the assumption that dedicated bump UTXOs exist in the user's wallet.
  2. Between the first and second proposals, which would be more appropriate for ord? Conceivably, both could be implemented and used depending on what's most fee efficient, but that may add unnecessary complexity.
  3. Do we like the use of --outgoing over separate arguments for --runes and --inscription? The idea would be to remain consistent with how commands like send and burn work and prepare the interface for support for all outgoing types. Adding support for Outgoing::Sat and Outgoing::SatPoint should be trivial once support for inscriptions is added, but I'm not sure how to balance that with the principle of not being YAGNI.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions