Deadcat: A binary prediction market built on Simplicity

I, and my team at Resolvr, have been working on a SimplicityHL-based binary prediction market that we’re calling Deadcat. At the time of writing, the website still needs to be hooked up to our backend, but the end product will be a binary prediction market web app that supports connection with Apogee, our new Liquid self-custody wallet Chrome extension.

Deadcat currently contains two SimplicityHL contracts: A market contract and a limit order contract, which I’ll describe briefly below:

The market contract is the base layer for an individual binary market. It has one job: uphold the solvency of the market. Or, put another way, ensure that no matter whether the market resolves to YES or to NO, it has enough money to pay out all holders of a winning position token. To achieve this, the contract consists of three different UTXOs:

  • A YES re-issuance token (the faucet of YES tokens for the given market)
  • A NO re-issuance token (the faucet of NO tokens for the given market)
  • The market’s collateral, which backs the outstanding supply of YES and NO tokens

Pre-resolution, it allows for anyone to permissionlessly create YES and NO tokens, iff the number of YES and NO tokens being created are equal, and the right amount of collateral is locked up into the market contract in exchange. This is called an issuance. The contract also allows for the reverse of this: burn an equal amount of YES and NO tokens, while co-spending the market contract (so it can witness this burn) and release the collateral that was backing those tokens before the market resolves. This is called a cancellation. Together, issuances and cancellations allow for the volume of a given market to freely expand and contract to meet demand without needlessly locking up collateral. Eventually, the outcome for the market is known, and an oracle provides a signature which moves the contract into the resolved state (which burns the YES and NO re-issuance tokens, leaving only the collateral UTXO). In this state, the contract now allows for the winning tokens to be redeemed at full value, and deems the losing tokens worthless. Importantly, the market contract does not manage the pre-resolution price at all. It simply asserts that pre-resolution, YES+NO=100%, and post-resolution, either YES=100% and NO=0% or YES=0% and NO=100%. Pre-resolution pricing is handled by the limit order contract and, in the future, possibly an automated market maker contract or central trading server.

The limit order contract is pretty much what it sounds like. It’s a contract that contains either a position token, or the collateral asset for a given market, and allows for anyone to permissionlessly swap the opposing asset for the one in the contract at a fixed price. So a limit order could contain some YES tokens at a price of 1,000 sats, which would let anyone take some or all of those YES tokens if they lock up 1,000 sats per YES token they take into an address specified in the limit order contract (which, presumably, is spendable by the person who created the limit order). The limit order contract also has a spend path that allows the person who created it to cancel the order. Assuming a maker has knowledge of many or all active limit orders for a given market, they can derive the past and present limit order book purely from on-chain data.

I’ve been toying around with different ideas for an automated market maker (AMM) contract as well. I want one that provides price coherency (i.e. it always prices YES and NO such that YES+NO=100%, so that there’s never any arbitrage opportunity against the market contract) and uses liquidity efficiently, which led me towards an Logarithmic Market Scoring Rule (LMSR). However, this requires transcendental math operations, which Simplicity doesn’t have jets for. It’s still possible to implement an LMSR in Simplicity by having the contract commit to a hash of the price curve, and have any spends of the contract reveal the price on the curve that they are moving the pool to, but that introduces a trade-off of witness data and price accuracy/resolution. There are other options such as a fixed product AMM adapted to a binary market. I still need to research this more though.

Source code for the backend node, which handles all processing of the Deadcat Simplicity contracts, can be found here: https://github.com/Resolvr-io/deadcat-node

3 Likes

Great stuff! :raising_hands:

I was trying to check the source code but the link appears to be broken..

Ahh whoops, I forgot to make the repo public. It’s fixed now!

1 Like

What’s the difference between that and the older repository at GitHub - Resolvr-io/deadcat: A desktop application for trading prediction markets over Nostr using Bitcoin's Liquid Network. · GitHub ?

Ahh yes, apologies for the confusion there! The initial vision with the repo you linked was for the SimplicityHL contract code, chain walker, validator, and the app itself to all live in the same repo. I’ve since realized that might be too ambitious, so the deadcat-node repo specifically contains the deadcat protocol-level code, as a node server binary, alongside a rust client library and CLI. The idea is that, similar to how you can run a bitcoin core node to validate the layer 1 chain and then run a lightning node that connects to it and understands the lightning layer 2, a deadcat node would sit atop a Liquid chain data source and speak the protocol of the deadcat market contract. Then, you can inperact with deadcat using a web app, a desktop app, the included CLI, or build whatever interface you’d like using the provided rust client library. The deadcat-node repo makes no opinionated decision.

A bit of a tangent, but one cool thing I realized here is that as an end user, if I’m interested in interacting with a given market, I probably don’t care about any past states. I only want to know that the market I’m interacting with is guaranteed to be solvent, and how to interact with it. To know these two things, I only need to know the most recent version of the market, and to validate that the transaction which created the market did so correctly (i.e. it created exactly 1 YES reissuance token and 1 NO reissuance token, immediately locking them to the market contract to guarantee market solvency). So a client doesn’t need to trust a deadcat node to be honest about a given market. The node can share the first and latest txids for a given market, and then the client can fetch them using its own trusted chain data source to validate it.