Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offer PSBT design #2706

Open
casey opened this issue Nov 20, 2023 · 16 comments
Open

Offer PSBT design #2706

casey opened this issue Nov 20, 2023 · 16 comments

Comments

@casey
Copy link
Collaborator

casey commented Nov 20, 2023

Before adding commands to create and accept offers, we should design and agree on the right structure for offer PSBTs. There are a number of tricky issues that I don't fully understand. Namely, making sure everything works with the sighash flags, it isn't possible to accept an offer without providing the right amount of BTC, and the number and order of padding inputs. (Some PSBT implementations include extra padding inputs, I think for alignment, but I don't fully understand the tradeoffs there.)

This design will make it easier to review PRs adding the commands. As is, reading the code in #2697 to figure out the PSBT structure is pretty hard. It would be better to have a documented design first, and then review the code to make sure that it's doing what's in the design.

In order to close this issue:

  • figure out how a seller creates an offer, including the number and order of inputs, the number and order of outputs, and which inputs and outputs are covered by which sighash flags
  • figure out how a buyer accepts an offer
  • figure out why this design is secure
  • document all of the above in a file in docs/src/inscriptions/offers.md

Then we can move on to actually implementing and reviewing the sell and accept commands, updating the design if needed.

@ayo-dan
Copy link

ayo-dan commented Nov 21, 2023

This sounds like a great model to me and is something that could be incorporated into the Node Monkeys bounty.

I do think it would be good to create a standard design for how PSBTs could also be used for trxs that involve the creation of inscriptions.

For example how an artist could create a PSBT where a buyer is providing BTC and in exchange the creator is inscribing and sending an Inscription to the buyer.

This could also incorporate standards on how Parent Inscriptions could be involved.

@utxo-detective
Copy link

utxo-detective commented Nov 21, 2023

When it comes to atomic swaps for inscriptions, there are two patterns that are desired. One where you want to sell an inscription you own for a set price, and another where you may solicit offers for an inscription you own from many buyers and then accept the one you want. For the purposes of this explanation im going to call them sell/buy and offer/accept,

Sell/Buy

sell

+----------------------------------------+--------------------------------+
|                 Inputs                 |            Outputs             |
+----------------------------------------+--------------------------------+
| SINGLE|ANYONECANPAY <inscription_utxo> | <seller_address> <seller_sats> |
+----------------------------------------+--------------------------------+

buy w/ 2 bump

+--------------------------------------------------+--------------------------------+
|                      Inputs                      |            Outputs             |
+--------------------------------------------------+--------------------------------+
| SIGHASH_ALL <bump_utxo> 600sat                   | <buyer_address> 1200sat        |
| SIGHASH_ALL <bump_utxo> 600sat                   | <buyer_address> <postage>      |
| SINGLE|ANYONECANPAY <inscription_utxo> <postage> | <seller_address> <seller_sats> |
| SIGHASH_ALL <fund_utxo> <fund_sats>              | <buyer_address> <change_sats>  |
+--------------------------------------------------+--------------------------------+

buy w/ 2 bump and recycle

+--------------------------------------------------+--------------------------------+
|                      Inputs                      |            Outputs             |
+--------------------------------------------------+--------------------------------+
| SIGHASH_ALL <bump_utxo> 600sat                   | <buyer_address> 1200sat        |
| SIGHASH_ALL <bump_utxo> 600sat                   | <buyer_address> <postage>      |
| SINGLE|ANYONECANPAY <inscription_utxo> <postage> | <seller_address> <seller_sats> |
| SIGHASH_ALL <fund_utxo> <fund_sats>              | <buyer_address> <change_sats>  |
| .                                                | <buyer_address> 600sat         |
| .                                                | <buyer_address> 600sat         |
+--------------------------------------------------+--------------------------------+

In the first pattern where you want to sell an inscription for a specific amount of sats, you create a psbt with one input and one output. The input is the current location/utxo of your inscription and the output is the amount of sats you want to receive. You sign the psbt SIGHASH_SINGLE|ANYONECANPAY. This will guarantee that the only way for a buyer to spend the inscription utxo is to fund a transaction that pays the seller the exact amount they signed for. Once a seller has signed, they can share the unbroadcasted psbt privately or publicly. To keep this design simple, we should not try to engineer how the psbts are shared, we can tackle that later. If you share your psbt publicly, but no longer wish for it to be purchased, you must transfer the inscription to yourself, which will spend the utxo and invalidate any signatures that may be floating around. The sell psbt is not broadcasted and is you dont need any cardinal utxos to create it.

When the buyer want to purchase, they construct a transaction that uses ordinal theory to transfer the inscription into an output that the buyer controls. The buyer signs this transaction SIGHASH_ALL and then takes the signature from the sellers psbt and inserts it into their purchasing psbt. They then broadcast this transaction to the network, finalizing the swap. There are multiple methods to construct these purchase psbts and ill do my best to explain the tradeoff of each.

  1. 0 bump utxo - Openordex created a way that does not use any bump utxos, but it results in a sat offset in the middle of a utxo which is not ideal.
  2. 1 bump utxo - Using 1 bump utxo is also a possibility, but it requires you to first create a utxo with size of the purchase amount + some fixed amount. The Sat offset doesnt change when buying, but a setup transaction is always required before purchase resulting in 2 transactions ever purchase.
  3. 2 bump utxos - in this method you can use 2 fixed size utxos (most marketplaces are using this method with 600 sat utxos). Using a 2 bump method, you keep the inscription in 0 offset as well as simplify the math logic when creating the psbt. There is also an optimization where you can recycle these 2 utxos so that only on your first purchase you need to make a setup tx, but then any subsequent purchases you already have 2 bump utxos at your disposal. This 2 bump method is what i had implemented in Buy and Sell Inscriptions/Rare Sats #2697

ThesIn this sell/buy pattern, selling is very straightforward to understand the security risks and implementation. Buying is where all the complexity comes in. The buy methods defined here transfer the entire postage of a utxo and do not attempt to try and split certain sats out a a utxo

Offer/Accept Offer

+-------------------------------------------------+----------------------------------+
|                     Inputs                      |             Outputs              |
+-------------------------------------------------+----------------------------------+
| SIGHASH_ALL <seller_inscription_utxo> <postage> | <buyer_address> <postage>        |
| SIGHASH_ALL <buyer_funding_utxo> <funding_sats> | <seller_address> <purchase_sats> |
| .                                               | <buyer_address> <change_sats>    |
+-------------------------------------------------+----------------------------------+

This pattern is a little less complex as it requires no bump utxos. A buyer constructs a transaction to purchase an inscription and signs it SIGHASH_ALL. The first input is the inscription that a buyer wants to purchase, and the first output is the buyers address with postage the same exact size as the inscription currently contains. All other inputs of this transaction are the buyers used to fund the purchase. The 2nd output is the inscription owners address with the amount of sats the buyer decided on, 3rd output is buyers change address. After the buyer signs this transaction, they give it to the seller.

The seller inspects the psbt and see what inscription is being purchased and for what amount. If a seller likes what they see, they can sign SIGHASH_ALL on the 1st input and then broadcast the transaction. This method is nice for running a trustless auction where after some set amount of time, the seller accepts the highest offer. Its also useful if you wanted to only sell an inscription to certain address/addresses, particularly if you wanted to run a trustless claim for a collection

@ayo-dan
Copy link

ayo-dan commented Nov 21, 2023

Great breakdown of how listings and offers can be done with PSBTs @utxo-detective

Follow-up questions (from a non-dev):

  1. With the model you propose, is it possible for the seller or offerer to update the amount of BTC needed for the PSBT to be fulfilled? Essentially changing the sale price of the listed Inscription or offer amount. Or would it be simplest to spend the UTxO-referenced in the initial PSBT and then create a new one with the updated amount?

  2. Would this same model be feasible if the method of payment was another inscription? For example, the seller creates a PSBT for their Bitcoin Frog that can be purchased with a OCM Dimension or in a different case a Rune/BRC20. This could be difficult as defining what meets the sale conditions beyond the number of sats could be tricky.

Appreciate it!

@utxo-detective
Copy link

utxo-detective commented Nov 21, 2023

With the model you propose, is it possible for the seller or offerer to update the amount of BTC needed for the PSBT to be fulfilled? Essentially changing the sale price of the listed Inscription or offer amount. Or would it be simplest to spend the UTxO-referenced in the initial PSBT and then create a new one with the updated amount?

If you want to list at a lower price than originally, you can just create a psbt at a lower price, no need to transfer and create a new. If you want to increase the price, you should transfer the inscription to yourself and then create a new psbt at the higher amount

Would this same model be feasible if the method of payment was another inscription? For example, the seller creates a PSBT for their Bitcoin Frog that can be purchased with a OCM Dimension or in a different case a Rune/BRC20. This could be difficult as defining what meets the sale conditions beyond the number of sats could be tricky.

Unfortunately not. SIGHASH_SINGLE only makes a promise of some amount of satoshis, not some specfic sats. I beleive there is a proposed SIGHASH_ANYPREVOUT that is not yet added to bitcoincore but it may be able to accomplish this sort of thing, but i dont know much about it other than i saw it somewhere on twitter

@casey
Copy link
Collaborator Author

casey commented Nov 22, 2023

Is the only downside of the 0 bump utxo method that it requires the buyer to split the ordinal out in a separate transaction after purchase? If so, I think we should consider it. Needing to keep bump UTXOs around will add more complexity to the wallet's utxo management, which is already complex. Also, if you need bump UTXOs to buy, you're limited to one buy in-flight at a time, or having more bump utxos available to make multiple purchases at the same time.

@rayonx
Copy link
Contributor

rayonx commented Nov 22, 2023

You don't need dummy/padding inputs, not sure why the community kept on building using this scheme

Edit: one reason I can think of is that existing third-party wallets are reusing addresses, and that's really bad for hygiene.


Transaction Mapping

All Initial Asks are Submited as PSBTs with:

Inputs Outputs
From maker > Inscription Ask > To Maker

Signed and submitted to the order book by the maker

The taker finalizes the PSBT based on available UTXOs and fee market conditions:

Inputs Outputs
From Taker > Payment* Change* > To Taker
From Maker > Inscription Ask > To Maker
From Taker > Additional** Inscription > To Taker
Change** > To Taker

Credits: @p0stcapone

@utxo-detective
Copy link

Is the only downside of the 0 bump utxo method that it requires the buyer to split the ordinal out in a separate transaction after purchase?

If you only have one cardinal, this method does not work and you will still need to create a setup transaction. If you only have large cardinals then one will now be inscription postage. The ideal case for 0 bump is you have 1 small and 1 large cardinal, but this may not always be the case.

Needing to keep bump UTXOs around will add more complexity to the wallet's utxo management, which is already complex.

This is a fair assessment but you are trading off complexity in transaction construction for complexity in ux. It just kicking the can to users who will need to manage their cardinals. Also, i found that the utxo selection on 0 bump was more complex than just maintaining a few 600 sat utxos.

You don't need dummy/padding inputs, not sure why the community kept on building using this scheme

Primary reason is cheaper since you didnt need to split inscriptions out after each purchase and it also made bulk purchase easier. The 2 bump method is generalized as n + 1 bump where n is the number of inscriptions you are buying in a single transaction. You can see this here where 6 inscriptions were purchased in 1 transaction

@casey
Copy link
Collaborator Author

casey commented Nov 22, 2023

If you only have one cardinal, this method does not work and you will still need to create a setup transaction.

True, but the user is more likely to have multiple cardinal outputs vs having bump outputs of a specific size.

If you only have large cardinals then one will now be inscription postage.

Is that an issue if you split afterwards? The transaction builder will strip postage over 20,000 sats automatically when sending inscriptions.

@utxo-detective
Copy link

True, but the user is more likely to have multiple cardinal outputs vs having bump outputs of a specific size.

My thinking was that if you may need to split anyway, that the logic would be simpler if they were fixed size

Is that an issue if you split afterwards? The transaction builder will strip postage over 20,000 sats automatically when sending inscriptions

Its not an issue after you split, just means for most purchases youll have to do purchase tx + split tx and sometimes setup tx, purchase tx and then split tx

@casey
Copy link
Collaborator Author

casey commented Nov 22, 2023

Its not an issue after you split, just means for most purchases youll have to do purchase tx + split tx and sometimes setup tx, purchase tx and then split tx

In the ord wallet, I don't think you'll have to do the split tx manually. The inscription will be in your wallet with extra postage, and then when you send it, it'll be stripped automatically. However it will lock that postage up for other uses, and then you'll have to manually send the inscription back to yourself if you want to use it.

I'm leaning towards two bump utxos, like you suggest. Could you make a diagram for the no-bump case? I just want to make sure I'm understanding the tradeoffs.

And thanks for writing this up and providing the diagrams! It's super helpful.

@utxo-detective
Copy link

utxo-detective commented Nov 22, 2023

I'm leaning towards two bump utxos, like you suggest. Could you make a diagram for the no-bump case? I just want to make sure I'm understanding the tradeoffs.

Will create them in a next day or so, need to dig into my old code and find the openordex version

@victorkirov
Copy link
Contributor

victorkirov commented Nov 23, 2023

Why not split a utxo into more usable chunks? If you split into 2 x 600 sat utxos you'll end up with a 1200 sat UTXO which is basically dust with current fee rates. That's a big issue with market places, that they keep on adding dust utxos each time you purchase on top of creating new utxos worth 600 each for subsequent transactions and they never reuse those 1200 sat utxos.

@raphjaph raphjaph added this to Tracker Nov 29, 2023
@raphjaph raphjaph moved this to Backlog in Tracker Nov 29, 2023
@casey
Copy link
Collaborator Author

casey commented Nov 30, 2023

@utxo-detective Any update? No worries if not, just wanted to ping this issue, since this is a great feature.

@riverrun46
Copy link

Why not split a utxo into more usable chunks? If you split into 2 x 600 sat utxos you'll end up with a 1200 sat UTXO which is basically dust with current fee rates. That's a big issue with market places, that they keep on adding dust utxos each time you purchase on top of creating new utxos worth 600 each for subsequent transactions and they never reuse those 1200 sat utxos.

Actually most marketplace is now using a 600-600-1200 (or 300-300-600 if using taproot) 3 bump rotation scheme. Using this design you just do setup once and keep using 3 bump utxos forever. The downside is a bigger tx size tho.

@riverrun46
Copy link

Another idea is that you can "mark" every utxos with 600x (600/1200/1800...) as bumps, then you can keep reusing those bumps since that even if they "grow" after each transaction, they are still 600x. You don't deem a 1200-sat UTXO as a waste, instead it's another bump that you can use for future transactions.

@victorkirov
Copy link
Contributor

Another idea is that you can "mark" every utxos with 600x (600/1200/1800...) as bumps, then you can keep reusing those bumps since that even if they "grow" after each transaction, they are still 600x. You don't deem a 1200-sat UTXO as a waste, instead it's another bump that you can use for future transactions.

Yeah, I'm thinking this will be best, then you can decrease the amount of dust you have and do "free" consolidation of your dust.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants