The Engineering Behind DLC Markets: Crafting a Secure and Simple Trading Platform
The choice of our current technical stack reflects and answers DLC Markets commitment to provide the utmost security for our users given their practical constraints.
DLC Markets is now mature enough to fly out of signet nest to mainnet jungle! We are really proud to finally release a work that took us a year and a half. From the mere idea to use Discreet Log Contracts to enable non-custodial trading on Bitcoin to a fully fledged product for OTC derivatives trading desks, it has been a long road! In this article, we want to share the technical steps we took to make what we think is a simple, secure and efficient product.
It all starts with an Oracle
Our DLC adventures obviously start by a bibliography on the current state of the art of DLC. In order to develop a non-custodial trading platform for Bitcoin derivatives, we first needed an oracle to testify BTCUSD price.
We reviewed the available oracle implementations at the time and were disappointed to discover that they are not all compatible with each other and decided we had to follow the specifications implemented by p2p-derivatives and used by 10101.finance. We wanted to minimize the variety of programming languages used and therefore introduced Rust in our code base using the reference implementation Rust-Dlc (while p2p-derivatives oracle is coded in Go).
We discovered Sybils, an oracle implementing p2p-derivative specification in Rust by lava-xyz. We forked it to use Postgres as database backend and ended up rewriting almost all its code to allow a more flexible attestation scheduling, web-sockets to broadcast attestations and query batched announcement data.
This led to the oracle implementation we use today in production: Pythia. We are going to release our implementation open-source so that other institutions can attest the price of Bitcoin and hopefully create a resilient enough network of oracles.
Building an institutional OTC trading platform with Rust and WebAssembly
Given the current state of Discreet Log Contracts, a peer-to-peer bet protocol designed for financial transactions, we thought the best use case would be institutional OTC (Over The Counter) trading. Counterparties must agree on price, quantity and collateral management: not an easy feat for retail users but a well-known practice for institutions used to trade bilaterally bespoke products.
This reasoning led us to start building DLC Markets: A non-custodial booking and settlement platform for bitcoin-native companies.
We want institutional users to be able to sign and verify adaptor signatures of DLCs by themselves, remaining the sole owners of their private key and with no need to download and update a separate software. The choice of our tech stack was made accordingly, we integrated the Rust-Dlc library in a web app.
From a technical point of view, there is only one reliable solution to make this efficient: WebAssembly. The code to sign and verify signature is compiled by Rust in WebAssembly and inlined in a JavaScript file, allowing us to use it everywhere. The same WebAssembly byte code is used by all users and the coordinator server!
Collateral optimization with liquidations
We started to implement the popular BTC/USD inverse future (whilst developing vanilla options) and the first challenge we thought to address is the collateral optimization. Institutions cannot be expected to fully collateralize trades by locking up an excessive quantity of bitcoins in a DLC. Capital efficiency and liquidity are critical. So, DLCs are not practical unless they support some degree of leverage.
This brought us to a key issue: most DLC designs overlook the fact that a DLC locks one party's funds even when the other party’s collateral is not sufficient to cover potential losses. This impacts pricing: it turns the product into something closer to a call/put spread, which is not ideal for inverse futures. This setup makes hedging difficult and reduces capital efficiency. We needed a way to liquidate the trade if a party's collateral balance hits zero.
One approach used by the 10101 app was to implement short-expiry DLCs to increase the likelihood of a planned renewal occurring before a party's collateral reaches zero. This method also allows the derivative to be perpetual by resetting the expiry with each renewal. However, this solution is inefficient: leverage must be limited, and a party close to liquidation may be tempted to let their collateral drop to zero in order to take advantage of a small option. To address this issue, we began developing a coordinator aimed at minimizing the number of interactions required to set up and renew a DLC, to match traditional daily margin calls.
While implementing the DLC setup protocol with a coordinator, we were surprised by a design decision in Rust-Dlc which allows several contracts to fit in a single DLC. Digging the dlcspec changelog, we discovered a discussion on the concept of disjoint union DLC as something which could help implementing liquidations. Unfortunately, there had not been any progress made for a long time to demonstrate how this could be done exactly.
This feature in the Rust-Dlc library felt like a hidden gem waiting to be used, but there was a catch: disjoint union does not work for liquidations. Each contract in a DLC has to provide a payout for every price the oracle might sign off at settlement time. The current DLC specifications do not allow a contract to skip certain price ranges.
After this discovery, we decided to fork the Rust-Dlc library to add a missing feature: a “no payout” range for disjoint union contracts. This addition makes it possible to create contracts that cannot be settled within certain price ranges. With this change, we can implement inverse futures with liquidations by adding multiple intermediary contracts, where the “no payout” range applies whenever both parties have enough collateral. This acts as a liquidation check, allowing the contract to continue as long as the price remains within this safe range at each checkpoint.
This feature is incredibly powerful. It lets users set very long DLC expiries without needing to pre-arrange margin calls to manage collateral. If a party’s collateral runs low, they will be asked to top up to avoid liquidation, or they can choose to take the loss instead. Funds are no longer locked unnecessarily, offering much more flexibility in collateral management—a core innovation DLC Markets is bringing to the DLC space.
Improving user experience with parallel computing
But it is not over yet! We now have to sign many many MANY contracts just to set up ONE trade with a long enough expiry.
Each liquidation represents a contract, just like the final settlement does. However, the liquidation check does not include signatures for the price range where a liquidation contract should not be used to settle the DLC. Additionally, the other signatures cover very broad price ranges that offer the same payout. This redundancy can be efficiently minimized to a smaller number of signatures through numerical compression techniques.
A casual future often has several months of expiry and liquidation must be checked regularly enough so that there is no opportunity to capture a time value by the liquidated party. Performing a liquidation check every hour of a future with 6 months expiry requires to sign almost 4 500 contracts with often 30 signatures. Each user has to sign and verify at least 120 000 adaptor signatures!
Needless to say that is way too much: it would take around 15 minutes to sign such a contract on a regular grade laptop. But there is very good news: browsers are all single-threaded by default! Considering that most laptops now have at least 8 cores, if we use web workers we can take advantage of all cores in parallel to reduce this time to the scale of 1-2 minutes. We spent a long time working on how to spawn web workers managed by our WebAssembly module to sign and verify adaptor signatures in parallel using the well known rayon rust library, so that all the computing power of the user machine is used as efficiently as possible. It also enables to speed up the process just by upgrading to a bigger machine!
…and buffer addresses
Furthermore, DLCM initially required users to sign the funding transaction through a Partially Signed Bitcoin Transaction (PSBT) with their usual wallet in order to reduce interactions needed to set up a DLC. PSBT is a quite unique standard of the Bitcoin ecosystem that allows to coordinate the signatures of collaborative transactions. But from the feedback we collected from our potential users, we found out that they cannot easily interact with the PSBT standard using the usual custody solutions, such as Fireblocks.
The solution to this issue is to ask users who cannot sign PSBT to send the collateral before each DLC setup in a buffer address controlled by the DLCM user's account wallet, use it as input in the DLC protocol and sign the PSBT directly in his browser. While it works, it has drawbacks. The first one being that it requires one more interaction between users to set up a contract.
Initially, the wallet private key manipulated in the browser only protected funds in DLCs, which are naturally in a multi party multisig. Stealing these funds requires the attacker to know both users' keys, deeply complicating theft. Introducing singlesig buffer addresses changes the security model, as funds are fully controlled by the in-browser key for the duration of the setup. To mitigate this risk, the setup is time-limited and funds can be withdrawn from these buffer addresses at any time.
DLC Markets operates on a non-custodial model, meaning we do not take custody of users' funds at any time, even during the brief period required to set up a trade. This approach places the responsibility on users to balance practicality and security.
We anticipate that the institutional adoption of Partially Signed Bitcoin Transactions (PSBT) will grow alongside the use of DLC Markets, as our platform offers a more practical solution with fewer interactions needed to set up contracts, while also enhancing security by eliminating the need for a buffer by the in-browser key.
In the meantime, we are actively exploring ways to secure users' private keys while they are accessed within their browsers.
Security at the forefront of DLCM
The main attack that can be performed to extract secrets in a web page is cross-site scripting (XSS): the victim browses on another website and this website injects JavaScript code on DLCM web page that extracts the plain text private key somewhere else. Other attacks are possible but often require that the user installs a malicious program or extension running on its machine while cross-site scripting does not.
WebAssembly also provides a partial solution to counter such attacks. A WebAssembly module exposes a limited set of methods to the browser script. By keeping the private key exclusively within the module's memory and eliminating any interface to export it, extracting it becomes significantly more challenging. An attacker attempting an XSS exploit would need to transfer the entire memory contents of the module, and then painstakingly sift through the raw bytes to locate the private key.
To further segregate scopes, the WebAssembly module is actually not running in the main browser’s thread but in its own web worker. Only the methods needed to sign the DLCs and the funding transaction from the buffer address are exposed. This prevents a successful XSS attack to even get a reference to the memory as it simply does not exist in the main browser thread. This worker can then spawn and manage other web workers used to perform the heavy computations required.
The idea is not to entrust the main browser’s thread with secrets handling by doing all the sensitive computation in that separate worker. This requires to create a secure encrypted communication channel between the server and the WebAssembly module so the main thread never even knows the password-encrypted private key. This can be done using Diffie-Hellman key exchange or the Secure Remote Password protocol (SRP), which is notably used by Proton. Reusing libsecp256k1 primitives with SRP is still an alternative we are considering.
This would also allow the DLCM server to authenticate all its signing requests, which would prevent an even more sophisticated attacker from tricking users into signing fake contracts, should the DLCM server not be coping with the attacker.
A summary of our current tech stack
When the users sign and verify Contract Execution Transactions (CETs), the WebAssembly module runs in its own web worker and manages a pool of workers through the rust rayon library. We chose this architecture because it allows us to use as much as possible the computing power available on users computers while providing the best security possible when the private key of DLC contracts and buffer address will be stored only in the WebAssembly module.
This is summarized in the following schema (with future enhancements highlighted in orange):
By establishing a secure encrypted connection between the main WebAssembly worker and the DLCM server, the private key can be decrypted and stored in the WebAssembly memory. This allows it to be used without transmitting any sensitive information in plain text through the user's browser, which could potentially be exploited by an attacker. Additionally, a trusted public key for the DLCM server can be stored to verify the authenticity of all the requests it makes.
The choice of our current technical stack reflects and answers DLC Markets commitment to provide the utmost security for our users given their practical constraints.