ordinals / ord

πŸ‘β€πŸ—¨ Rare and exotic sats
https://ordinals.com
Creative Commons Zero v1.0 Universal
3.8k stars 1.33k forks source link

Offers #802

Open casey opened 1 year ago

casey commented 1 year ago

This doesn't need to be done for mainnet, but we need to make sure that it can be done eventually.

Ask: Offer to sell sats

Inputs

Outputs

Steps

Bid: Offer to buy sats

Inputs

Outputs

Steps

Notes:

Related:

casey commented 1 year ago

In offers to buy, can people include change so they an use input uxos of any size?

cryptoquick commented 1 year ago

This but also for inscriptions

casey commented 1 year ago

@cryptoquick This actually covers inscriptions! Since inscriptions are on sats, which transfer using the ordinals protocol, offers to buy and sell individual sats work for inscriptions. (UX obviously needs to be there too!)

cryppadotta commented 1 year ago

One of the things I think is critical for the design of this feature is that 3rd-parties can be optionally designated as recipients in the transaction. Most notably:

Ordinals will have the same issues as Ethereum in enforcing these, but both of those fees are massive forces for good and we should design offers to support them from the get-go

Because if tool-builders and artists know they can participate in the ongoing economy, you'll see an incredible amount of work poured into the ecosystem

casey commented 1 year ago

@cryppadotta As far as I can tell, there is no way to implement royalties in an enforcable, technically reasonable way. See https://github.com/casey/ord/discussions/773 for some discussion we've been having on the topic.

casey commented 1 year ago

Offers could certainly include a "tip" output to a third-partty, and venues (marketplaces, artist home pages, discords), etc could require that an offer file contain a tip in order to show it one the homepage, etc. I think this would be great. Offers are simple and flexible enough that we can add this in a v2 version. So I think no need to worry about it in the MVP. Offers are off-chain, and easy to upgrade!

gnoblin commented 1 year ago

What are the options for writing more protocols around Ordinals - bitcoin script only? Or is it possible to use sapio, stacks/clarity? (or anything else like that). Thanks!

SlowestTimelord commented 1 year ago

I know you've taken a look at Chia and ChiaLisp @casey but not sure if you've dived into how Offers are implemented there that may offer some ideas: https://chialisp.com/offers/

The defined NFT1 standard also enforces royalties on-chain via the transfer function part of the standard NFT1 program @cryppadotta

There are differences between Chia's coin set model vs Bitcoin's UTXO namely Chia's use of BLS enables signature aggregation in a better way than Schnorr but I'm not sure if there are other shortcomings in Bitcoin Script that makes a similar implementation not possible...

casey commented 1 year ago

What are the options for writing more protocols around Ordinals - bitcoin script only? Or is it possible to use sapio, stacks/clarity? (or anything else like that). Thanks!

Stacks is has a separate token, and is only thematically related to Bitcoin, so it and clarity will never be supported.

Sapio is an excellent project, and although I believe that @JeremyRubin is no longer working on it, it is open source and someone dedicated could pick up the torch. Sapio would be a great fit for filling in the gaps in Bitcoin Script for markets and services related to ordinals.

casey commented 1 year ago

I know you've taken a look at Chia and ChiaLisp @casey but not sure if you've dived into how Offers are implemented there that may offer some ideas: https://chialisp.com/offers/

Thanks for the link! I have looked at offers, and they definitely seem similar to what we want to do here.

The defined NFT1 standard also enforces royalties on-chain via the transfer function part of the standard NFT1 program @cryppadotta

Royalties cannot be enforced on-chain, because the buyer and the seller can simply use another channel to perform the actual transfer of funds, and lie on chain about the actual sale price. Until I see a good implementation proposed that does not have this and other problems, I do not plan to add any royalty related functionality to ord.

There are differences between Chia's coin set model vs Bitcoin's UTXO namely Chia's use of BLS enables signature aggregation in a better way than Schnorr but I'm not sure if there are other shortcomings in Bitcoin Script that makes a similar implementation not possible...

Offers as sketched out don't really use script, only input and output selection, but I would be curious if something is missing. The coin set model and the UTXO model are pretty much the same, so I often actually use the "coin set" terminology when describing bitcoin, because "output" is a pretty unfamiliar term to most people πŸ˜…

SlowestTimelord commented 1 year ago

Royalties cannot be enforced on-chain, because the buyer and the seller can simply use another channel to perform the actual transfer of funds, and lie on chain about the actual sale price. Until I see a good implementation proposed that does not have this and other problems, I do not plan to add any royalty related functionality to ord.

Well of course one can always get around royalties if you are okay with some level of trust. At the extreme I can sell you my private key to my wallet containing the NFT in exchange for cash and no information will take place on chain. The point with on-chain royalties (on Chia) is to create a standard where royalties are enforced when trustless Offer Files are used to exchange assets that follow that standard.

Offers as sketched out don't really use script, only input and output selection, but I would be curious if something is missing. The coin set model and the UTXO model are pretty much the same, so I often actually use the "coin set" terminology when describing bitcoin, because "output" is a pretty unfamiliar term to most people πŸ˜…

Fair point. BLS aggregation also opens the door to things like aggregation of Offers so multiple can be taken simultaneously.

A bit off topic but I heard your interview on Bankless and was intrigued by how you think tokens (like ERC-20 or CAT1 on Chia) can be implemented in a similar way. I think that's what would make Offers very exciting if its utility can go beyond just BTC for ordinal trades (or ordinals for ordinals).

casey commented 1 year ago

Well of course one can always get around royalties if you are okay with some level of trust. At the extreme I can sell you my private key to my wallet containing the NFT in exchange for cash and no information will take place on chain. The point with on-chain royalties (on Chia) is to create a standard where royalties are enforced when trustless Offer Files are used to exchange assets that follow that standard.

Someone can probably figure out trustless atomic swaps to remove trust and still use offer files. I want to avoid a scenario where we advertise that we have royalties to artists, but then as the amount of the value in the system increases, it becomes a confusing and chaotic race to the bottom like we're seeing in Ethereum.

A bit off topic but I heard your interview on Bankless and was intrigued by how you think tokens (like ERC-20 or CAT1 on Chia) can be implemented in a similar way. I think that's what would make Offers very exciting if its utility can go beyond just BTC for ordinal trades (or ordinals for ordinals).

I think there's an issue open for fungible ordinals. I'm not sure how to support that, but we could add a metadata field to an inscription that said "the next N sats after this inscription" are fungible tokens. Support in ord would be hard, because special tracking for all those sat ranges would be difficult, but it's such a simple protocol-level extension that I would be open to adding it, if only so that others could experiment with it in other repos.

casey commented 1 year ago

@SlowestTimelord Check out #787, I just copied my last comment there.

JeremyRubin commented 1 year ago

Sapio would only need minor changes to support basic ord functionality, but the biggest issue would be convention around fees probably.

dannydeezy commented 1 year ago

question about incentives: if i'm selling a rare sat, i set a price in my Ask offer isn't there now an interesting dynamic where two buyers may compete with each other to fulfill it, and the one who pays a higher mining fee will win?

casey commented 1 year ago

question about incentives: if i'm selling a rare sat, i set a price in my Ask offer isn't there now an interesting dynamic where two buyers may compete with each other to fulfill it, and the one who pays a higher mining fee will win?

Yup, that is correct. Seller should set the price such that it's close to what sellers are willing to pay. If it's below what sellers are willing to pay, then they will increase their "bid" by adding fees, which will go to miners. There are probably a bunch of different auction mechanism, where the value is split in different ways between miners and sellers.

On the one hand, you have the #1611 method, where miners get all bids. On the other hand, you have a descending price auction where the seller continually releases PBSTs with lower and lower prices until someone takes it and pays EV - FEE to the seller, where EV is expected value, and FEE is what's required to get it mined, which goes to the miner.

MuffinsThaCat commented 1 year ago

Something like this:?

const bitcoin = require("bitcoinjs-lib"); const bip32 = require("bip32"); const bip39 = require("bip39");

const mnemonic = "a random mnemonic phrase used as seed for BIP32"; const seed = bip39.mnemonicToSeed(mnemonic); const node = bip32.fromSeed(seed); const keyPairB = node.derivePath("m/44'/0'/0'/0/1").keyPair; const keyPairC = node.derivePath("m/44'/0'/0'/0/2").keyPair;

const network = bitcoin.networks.testnet; const p2pkhB = bitcoin.payments.p2pkh({ pubkey: keyPairB.publicKey, network }); const p2pkhC = bitcoin.payments.p2pkh({ pubkey: keyPairC.publicKey, network }); const p2pkhMaker = "the address for the maker"; const p2pkhTaker = "the address for the taker";

let psbt = new bitcoin.Psbt({ network });

psbt.addInput({ hash: "the hash of transaction A", index: 0, nonWitnessUtxo: Buffer.from("00", "hex"), });

psbt.addInput({ hash: "the hash of transaction B", index: 0, witnessUtxo: { script: p2pkhB.output, value: N, }, });

psbt.addInput({ hash: "the hash of transaction C", index: 0, witnessUtxo: { script: p2pkhC.output, value: M, }, });

psbt.addOutput({ address: p2pkhMaker, value: N, });

psbt.signInput(2, keyPairC, null, bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY);

const partiallySignedPsbt = psbt.toBase64(); console.log("Partially signed PSBT:", partiallySignedPsbt);

// Maker broadcasts PSBT

psbt = bitcoin.Psbt.fromBase64(partiallySignedPsbt);

psbt.addOutput({ address: p2pkhTaker, value: M, });

psbt.signAllInputs(keyPairB);

const signedTransaction = psbt.finalizeAllInputs().extractTransaction().toHex(); console.log("Signed transaction:", signedTransaction);

// Taker broadcasts transaction

// When transaction is mined, maker receives ordinal sats in Y for payment, taker receives cardinal sats in X

dajuguan commented 1 year ago

I think the script should use "p2tr" because ordinals are inscribed in the p2tr scripts.

cyclomancer commented 1 year ago

In offers to buy, can people include change so they an use input uxos of any size?

@casey, one way to support this is for the maker to include output Y, paying to the same address as the desired ordinal UTXO, output Z paying themselves change, and sign with SIGHASH_ALL. If the taker doesn't want to reuse the address, they can respond with an updated PSBT paying their preferred destination, also signed with SIGHASH_ALL. The added interactivity in that case isn't ideal, but I haven't thought of a better way to do it without SIGHASH_GROUP.

rot13maxi commented 1 year ago

how would we do the zero-value dummy input? The rest of this seems pretty straightforward: construct the psbt with the psbt module in rust-bitcoin, sign with the processpsbtwithwallet rpc

AlexanderPavlenko commented 1 year ago

This actually covers inscriptions!

@casey does that mean an inscription can be created in the same "sell offer" PSBT so that it's zero-cost for a maker to create and sell inscriptions?

kevinfaveri commented 1 year ago

In the "Ask: Offer to sell sats", I fail to see how it is possible to guarantee these two:

This way you can enforce both a minimum payment value AND that you know the buyer public key beforehand

kevinfaveri commented 1 year ago

Just created a topic on Discord #tech-support https://discord.com/channels/987504378242007100/1076920008065679500 in case we wanna discuss in a real time chat environment

cyclomancer commented 1 year ago

@kevinfaveri the seller's signature commits to (only) the input spending the ordinal and the output paying himself the desired price, via SIGHASH_SINGLE|ANYONECANPAY. Anyone in possession of that PSBT can add an input spending at least the already-committed output's value and an output sending the ordinal to themselves, sign their input, and broadcast.

Having trouble verifying on Discord but will get in the chat when possible.

cyclomancer commented 1 year ago

It occurs to me there's a MEV-like dynamic that arises with miners and fees in a hot inscription market. Since a miner can always break the transaction apart and try to snipe the purchase from the buyer, the incentive is to pay a fee premium equal to the expected appreciation of the NFT over whatever timescale a miner is likely willing to stay illiquid for.

Not a severe issue by any means but will be interesting to observe if/how this happens in practice.

kevinfaveri commented 1 year ago

@kevinfaveri the seller's signature commits to (only) the input spending the ordinal and the output paying himself the desired price, via SIGHASH_SINGLE|ANYONECANPAY. Anyone in possession of that PSBT can add an input spending at least the already-committed output's value and an output sending the ordinal to themselves, sign their input, and broadcast.

Having trouble verifying on Discord but will get in the chat when possible.

Thank you so much sir! So, in the "Ask: Offer to sell sats" theoretical algo, anyone can buy. Makes sense, but how to enforce that the buyer will pay the specific price of cardinal sats? e.g. will not try to pay less? Because maker only signs for the ordinal sat in the theoretical algo from @casey. Just the match between inputs and outputs should guarantee the minimum expected price, right?

If so, how to use the dummy output to match input and output "sizes"?

I think it is possible to workaround this (enforce a listing price) using OP_CODES that will validate the second input (from the taker, the payment) against a specific min price, right?


I have found other challenges here already, but this is the core to validate the first scenario here.

Also, what do you guys use to sandbox these PSBTs easily? I suppose there isnt an easy way other than doing manually....

kevinfaveri commented 1 year ago

@orenyomtov just released an open source NFT marketplace base layer protocol. Let's hack and validate it.

https://twitter.com/orenyomtov/status/1628106067119833107

https://github.com/orenyomtov/openordex

Congrats on the release and thanks on the choice of going OSS @orenyomtov, wish you good luck on your endeavor

orenyomtov commented 1 year ago

Thanks! Would also love to cleanly abstract and export the capability as a library down the road

I'm also looking forward to creating a V2 that accomplishes this without the need for dummy UTXOs

niftynei commented 1 year ago

I'm also looking forward to creating a V2 that accomplishes this without the need for dummy UTXOs

Have you looked into the psbtv2 protocol update yet? Curious is this solves the problem (i suspect it'll require more rounds of communication tho)

orenyomtov commented 1 year ago

I'm also looking forward to creating a V2 that accomplishes this without the need for dummy UTXOs

Have you looked into the psbtv2 protocol update yet? Curious is this solves the problem (i suspect it'll require more rounds of communication tho)

Do you know which wallets support psbtv2?

niftynei commented 1 year ago

The latest version of libwally has it, if iiuc. https://github.com/ElementsProject/libwally-core/releases/tag/release_0.8.8

niftynei commented 1 year ago

Oh that's not consumer grade wallets tho. Yeah no, it'll probably be a good bit before they update.

niftynei commented 1 year ago

If you wanted to play around with it though, that'd be a decent way to try it out.

niftynei commented 1 year ago

There's a really bad bug in the Bid flow fyi, cc @casey.

This line is problematic.

Maker creates PSBT providing a signature for C, which commits to A, B, C, and X, using SIGHASH_SINGLE

Given

Inputs
A: Zero-value dummy output, provided by taker
B: Value N output containing ordinal sats to be bought, provided by taker
C: Value M output containing cardinal sats for payment, provided by maker
Outputs
X: Value N output with maker's address
Y: Value M output with taker's address

SIGHASH_SINGLE on C is equivalent to SIGHASH_NONE. Yikes...

The transaction should instead be constructed as follows:

Inputs
C: Value M output containing cardinal sats for payment, provided by maker
B: Value N output containing ordinal sats to be bought, provided by taker
A: Zero-value dummy output, provided by taker

Outputs
X: Value N output with maker's address
Y: Value M output with taker's address

This makes the original instruction correct.

Edit: Just realized this doesn't work at all, you'd have to use SIGHASH_SINGLE|SIGHASH_ANYONECANPAY on the signature for C. Otherwise A can't be updated later.

Oh just noticed the "Offer to buy is wrong but can be fixed."... except I don't think it can with existing sighash flags and zero-rounds of communication; multiround PSBTv2 or an input version of SIGHASH_GROUP (or OP_TXHASH) i think will fix.

Gonna use this as an opportunity to shill @base58btc classes, we cover how SIGHASH works in our in-person classes ;) https://base58.info

there's a MEV-like dynamic that arises with miners and fees in a hot inscription market. Since a miner can always break the transaction apart and try to snipe the purchase from the buyer,

PSBTv2 and +1 round trip fixes this iiuc; you separate the compose phase from the sign phase, which lets you use SIGHASH_ALL to sign. This prevents anyone from outbidding you once broadcast.

roadbuilder commented 1 year ago
  • Taker adds inputs A and C and output Y and finalizes transaction

In the Asks sections for this shouldn't it say that : Taker adds inputs A and C and output X and finalizes transaction?

Being that: X: Value M output with taker's address Y: Value N output with maker's address

Also, could someone please explain why the Dummy UTXO is needed and why it needs to be empty or have less than 1000 Sats?

Any help great appreciated!

niftynei commented 1 year ago

Also, could someone please explain why the Dummy UTXO is needed and why it needs to be empty or have less than 1000 Sats?

Dummy UTXO is required for SIGHASH_SINGLE, so the inputs/output indexes of the maker get lined up/covered for their signature (B + Y have same index position in their respective lists)

roadbuilder commented 1 year ago

Thank you but I still don't understand this, need to study this more; some diagrams would help!

ghost commented 1 year ago

Did an inscription swap without dummy UTXO:

  1. Created PSBT 1 using createpsbt in which input is inscription UTXO and output is sell amount

    cHNidP8BAF4CAAAAAf6QBbK23/kX2b6+TwOMD+5K/3f/jev+O2ISBwUFJkjpAAAAAAD9////AUANAwAAAAAAIlEgggRYhIl33bjKgdv+G5AXrcTRKGyWQXBY/93IME8F4EYAAAAAAAAA
  2. Created PSBT 2 using createpsbt in which input is some UTXO from wallet (cardinal) and output is change left after deducting fees and output amount used in 1

    cHNidP8BAIkCAAAAAXm7jKQnPdFALnyfAMZ/4LktlmM2ae/x1zClF3eZFHh3AQAAAAD9////AvgqAAAAAAAAIlEgmeAiGFUfme2tsJ+HAT89PjkFYrYsn37nwN/Uckj23wpgrgoAAAAAACJRIObgyCXaE9gvj1wPqzq/SO1DVitTmnl2XYV3Y4dK8N0bAAAAAAAAAAA=
    1. Joined both PSBTs using joinpsbts RPC
    cHNidP8BAN0CAAAAAv6QBbK23/kX2b6+TwOMD+5K/3f/jev+O2ISBwUFJkjpAAAAAAD9////ebuMpCc90UAufJ8Axn/guS2WYzZp7/HXMKUXd5kUeHcBAAAAAP3///8D+CoAAAAAAAAiUSCZ4CIYVR+Z7a2wn4cBPz0+OQVitiyffufA39RySPbfCkANAwAAAAAAIlEgggRYhIl33bjKgdv+G5AXrcTRKGyWQXBY/93IME8F4EZgrgoAAAAAACJRIObgyCXaE9gvj1wPqzq/SO1DVitTmnl2XYV3Y4dK8N0bAAAAAAAAAAAAAA==
    1. Signed PSBT using walletprocesspsbt and finalized using finalizepsbt
    2. Broadcasted the transaction using sendrawtransaction: https://mempool.space/signet/tx/1e34d8c00eae244a725ca0beecb9ff63f42d70072c49908ca17b9f98df08463d

    Note: Order of inputs and outputs is important however joinpsbts RPC randomizes the order to improve privacy. So I had to keep trying until I get the right order. This can be fixed by either compiling core and remove the changes made in #16512 or open a pull request in bitcoin core to add another argument in joinspsbts RPC that is true by default and could be change to false for not shuffling inputs and outputs.

AdamISZ commented 1 year ago

@niftynei

Does this not work (for the "buy sats" case discussed above)?

0v = zero value dummy utxo

Inputs:
A: N, ordinal, taker
B: M, cardinal, maker

Outputs:
X: 0v
Y: N, ordinal, maker
Z: M, cardinal, taker.

Then maker can SINGLE|ACP sign over input B with safety right?

PSBTv2 and +1 round trip fixes this iiuc; you separate the compose phase from the sign phase, which lets you use SIGHASH_ALL to sign. This prevents anyone from outbidding you once broadcast.

Yeah I haven't thought it through at all but I could well imagine all this "jigsaw piece" stuff with single|acp is academic if you start thinking about markets.

niftynei commented 1 year ago

Yeah I think that works nicely. You'd want to use just SIGHASH_SINGLE as originally suggested though, otherwise anyone could swap out the other ordinal input, and change what you're buying.

(I hadn't realized it til I saw this tweet, but the ordinal input/output needs to go at the top to make it work out)

You can make SIGHASH_ALL work for it and remove the 0v dummy output if the maker's pay-to address is known at the time you're constructing the offer to buy.

AdamISZ commented 1 year ago

You'd want to use just SIGHASH_SINGLE as originally suggested though, otherwise anyone could swap out the other ordinal input, and change what you're buying.

Oh yes, good catch! Huh, interesting that it's different this way round, because the "special" ordinal input isn't owned by the signer at the start.

It's interesting how many dimensions these setups have.

You can make SIGHASH_ALL work for it and remove the 0v dummy output if the maker's pay-to address is known at the time you're constructing the offer to buy.

Still struggling with this one :smile: Did you mean taker, not maker there (i.e. the guy who has the ordinals and is accepting the offer to buy them?). I guess in case where all inputs and outputs are known in advance then none of this shenanigans is necessary right? You just ALL all of it in the usual way like a traditional coinjoin (except, where the ordering is fixed and no privacy is achieved).

niftynei commented 1 year ago

in case where all inputs and outputs are known in advance then none of this shenanigans is necessary right?

Yeah exactly :D

ghost commented 1 year ago

I am trying to write a spec for PSBT to be used in any inscription swap:

Let me know if you have any feedback or opinions

Cc: @casey @rot13maxi @niftynei @AdamISZ @JeremyRubin

ghost commented 1 year ago

https://gist.github.com/1440000bytes/a7deeb3f1740bc533a61fbcc1fe58d77

Christewart commented 1 year ago

Also, could someone please explain why the Dummy UTXO is needed and why it needs to be empty or have less than 1000 Sats?

Dummy UTXO is required for SIGHASH_SINGLE, so the inputs/output indexes of the maker get lined up/covered for their signature (B + Y have same index position in their respective lists)

I don't understand why this is required.

Ask: Offer to sell sats
Inputs

    A: Zero-value dummy output, provided by taker
    B: Value M output containing ordinals to be sold, provided by maker
    C: Value N output containing cardinal sats to be used for payment, provided by taker

Outputs

    X: Value M output with taker's address
    Y: Value N output with maker's address

Why not just reorganize to be

Inputs
    B: Value M output containing ordinals to be sold, provided by maker
    C: Value N output containing cardinal sats to be used for payment, provided by taker
Outputs
    Y: Value N output with maker's address
    X: Value M output with taker's address

Now B and Y are sharing the same index (0) which should be valid for SINGLE|ANYONECANPAY. This allows the taker to add his input C at index 1 to fund the payment for the ordinal? This removes A from the transaction which simplifies things. Am i missing something?

EDIT:

Explained by @niftynei and @rot13maxi . Thank you! For other readers i highly suggest reading the ordinal BIP

niftynei commented 1 year ago

Am i missing something?

iiuc you're missing how ordinals flow/are reassigned to the outputs from the inputs. The person buying the ordinals needs their address to be at output 0, so they get the sat that the inscription is associated with.

rot13maxi commented 1 year ago

@niftynei has it. Sats get assigned to outputs in the same order that they are provided as inputs. So if the first sat of the first input is an inscription or otherwise desirable sat (rare/exotic?), then it will go to the first sat of the first output. If the seller provides the first input and the first output with SINGLE|ANYONECANPAY, they will just be sending that sat to themselves.

0xankylosaurus commented 1 year ago

@rot13maxi thanks for the helpful answer... did you get the answer to your earlier question re. zero-value dummy output? Could we actually put a zero-value input in A? Currently I'm using a non-zero dummy output (1000 sats) to get around the same index position requirement.

AdamISZ commented 1 year ago

Yes, 0 value OP_RETURN outputs are valid and standard.

ep150de commented 1 year ago

Hey folks. What's the safest way to parse out sats without messing up dummy utxo for ordinals ? So that you can inscribe a specific art to a specific sat and sell it eventually. What's the best known method for creating dummy UTXO's today?