Hey again, fellow Simplicity devs! New month, new research paper. Will be happy to hear your feedback.
Link to the original post.
Abstract
We propose a decentralized protocol for managing regulated Liquid assets. Unlike an offchain cosigning service, ordinary transfers require no issuer signature. A holder signs the spend of the asset and supplies a policy proof, enforced by the Simplicity covenant onchain in the same transaction. In case the issuer’s service goes offline, holders who retained access to the latest policy snapshot can continue transacting under those rules. The design preserves holder custody and confines the asset to compliant covenants, still allowing the issuer to update the rules dynamically. The protocol supports mutable blacklists, whitelists, and transfer limits.
1. Introduction
Liquid assets are native ledger assets with optional reissuance authority [1, 2]. However, an issuer may still need to restrict their use. Common controls include sanctions, court-ordered freezes, account approval, and transaction limits. Circle (USDC) contracts include pause and blacklist roles [3]. Tether (USDT) also states that it may freeze or burn tokens in specified circumstances [4].
Blockstream AMP2 enforces issuer rules via a 2-of-2 wallet, with the user and the platform each holding one key. Every asset transfer requires a second signature from the platform [2]. This gives the issuer a simple enforcement point. However, it also makes transfer availability depend on the cosigning service being constantly online.
We replace that with policy evaluation enforced fully onchain. An issuer posts a policy snapshot and commits to it in a Simplicity verifier covenant [5]. Any asset holder with the snapshot can construct the required proofs and initiate the transfer without the issuer’s participation. In this paper, decentralized describes transfer execution and policy data distribution. However, it does not describe policy governance – the issuer still chooses and updates the rules.
The protocol combines three ideas. A programmable signature authorizes the holder [6]. A verifier output identifies the applicable rules. A confinement covenant returns every regulated output to the same ownership form. Sections 2 and 3 introduce these ideas before giving their Taproot construction. Section 4 presents example policies. Section 5 states the resulting guarantees and limits.
2. Protocol Model
2.1. Assets and roles
The issuer creates a regulated Liquid asset A and controls an issuer key I. Liquid also allows the creation of reissuance tokens whose holders may mint more units of A [2]. This authority is separate from the protocol. The issuer may destroy the reissuance token for a fixed supply of A or retain it under its governance. Nevertheless, any later issuance must enter the regulated covenant. Otherwise, the issuer has created unregulated units of A.
The issuer also creates a distinct verifier asset V. A valid verifier output carries a fixed amount q of V. The issuer may destroy V’s reissuance token for a deployment with only one verifier output. It may instead retain this token and create more verifier outputs as demand grows – this reduces contention and increases throughput. Each unspent verifier output C_V(π) identifies the policy π enforced on its spending path.
A holder controls an x-only public key X and receives A in a user covenant C_U(X). A recipient is simply the holder selected for a new output. The two covenants have different roles:
- C_U(X) proves authorization by X and requires the verifier output at transaction input zero.
- C_V(π) enforces the current policy π on its primary path and confines every new A output to some C_U(Y).
Thus neither program trusts the other as data. The same transaction joins them. The user covenant requires q units of V. The verifier output can move on its primary path only by executing the verifier covenant. Coordination among several such outputs is outside this paper’s scope.
2.2. Setup
The issuer performs four setup steps:
- Issue A and place its circulating supply in user covenants.
- Issue q units of V and choose whether to retain V’s reissuance authority.
- Choose an initial policy snapshot D_{π_0} and publish enough data to reproduce the proofs.
- Lock these q units of V in the verifier covenant C_V(π_0).
The commitment π_0 authenticates D_{π_0} but does not make the data available. Holders or independent mirrors must retain the full snapshot if they are willing to spend without the issuer’s service.
2.3. Holder transfer
Suppose X pays Y. The transaction spends C_V(π) at input zero with one or more C_U(X) inputs afterwards. Each user input requires its owner to provide a BIP-340 signature over Simplicity’s sig_all_hash [7, 8], committing to the entire transaction. The design follows the programmable signature pattern described in the companion work [6].
The verifier input then performs two tasks. First, it requires every output carrying A to use the user covenant, where the intended recipient key becomes a parameter of that covenant. Second, the verifier evaluates the issuer’s policy using transaction data and proofs supplied by the holder. The transaction is valid only if both the owner authorization and the verifier checks succeed.
Figure 1 shows the primary spending flow. Output zero recreates the same verifier covenant. Payments and change return to user covenants.
2.4. Issuer update
The verifier output uses a NUMS internal key and has two script spending paths. The primary path enforces holder transfers, and an issuer path verifies a signature under I over the entire transaction. This allows the issuer to move V to any successor script. They can use the secret key behind I to move V to C_V(π'), changing the policy commitments or the verifier logic. Additionally, the issuer path may also be used to halt all transfers by moving V elsewhere. No hidden Taproot leaf stores separate policy state. Instead, π is a parameter of the primary path verifier program and the resulting TapLeaf hash P(π) commits to that policy. Hence, the locking script C_V(π) also commits to the same policy. The issuer spends the old verifier output through the issuer path. It then creates a successor locked to C_V(π') and sets the primary path of the successor script to use π'.
This authority does not reveal a holder key, and it does not satisfy a user covenant. The issuer alone therefore cannot spend a holder’s balance. It can only change the rules for future transfers authorized by holders.
3. Covenant Construction
3.1. User covenant state
We use the Taproot state construction from the Simplicity documentation [9, 10]. Let U be the Elements TapLeaf hash of the fixed Simplicity user program. Let K be a NUMS internal key. For a tag t, define the tagged hash as
H_t(m) = SHA256(SHA256(t)\ ||\ SHA256(t)\ ||\ m).
For an owner key X, define
D(X) = H_{TapData}(X),
B(a, b) = H_{TapBranch/elements}(min(a, b)\ ||\ max(a, b)),
R_U(X) = B(U, D(X)),
C_U(X) = P2TR(K, R_U(X)).
Here, P2TR(K, r) is the Elements Taproot locking script obtained by tweaking the internal key K with the script root r. Elements uses its own tagged hash domains for Taproot [11].
Figure 2 contains one executable leaf and one unspendable TapData leaf. Changing X changes the output without changing the program U. This lets the verifier covenant embed U and derive a recipient covenant from a supplied public key.
3.2. User authorization
To spend C_U(X) through U, the witness reveals X and a signature σ_X. The Simplicity program recomputes C_U(X) and compares it with its current locking script. A witness therefore cannot substitute another key. The program then checks
BIP340Verify(X, sigAllHash(T), σ_X) = 1,
where T is the spending transaction. The program also checks three facts. Its current input must contain explicit asset A. This input cannot be zero. Input zero must contain the explicit verifier asset V in amount q.
These checks establish custody – they do not duplicate policy logic. Policy is enforced when the same transaction spends a verifier output.
3.3. Verifier covenant
Let P(π) be the primary spending path of the Simplicity verifier program instantiated with policy commitment π. Let G(I) be the issuer spending path. This path requires a BIP-340 signature under I over sig_all_hash and allows any successor script for V. Using the NUMS internal key K, define
R_V(π) = CommitmentMerkleRoot(P(π), G(I)),
C_V(π) = P2TR(K, D(R_V(π))).
Spending through P(π) is the primary transfer path. Spending through G(I) is the issuer update path. The NUMS key provides no key-path spend.
On the primary path, P(π) performs the following checks:
- Its input is transaction input zero and contains q units of V.
- Output zero returns q units of V to the same C_V(π).
- At least one regulated input is a C_U(X) input. This input independently requires its owner signature. Only holders can therefore advance the verifier output.
- Every output exposes its asset identifier. Every output of A has a locking script C_U(Y) for its supplied recipient key Y.
- All enabled policy predicates hold.
The output scan advances until the transaction has no output at the next index. A confidential asset commitment would hide which outputs need confinement. Hence, asset identifiers must be explicit. Value commitments may remain confidential unless a policy requires their amounts.
The fixed-index input may be functionally limiting. Research on arbitrary-index positioning is considered future work.
3.4. State transitions
An ordinary transfer has the abstract form
T_{in} = [(V, q, C_V(π)), (A, a_1, C_U(X_1)), ...],
T_{out} = [(V, q, C_V(π)), (A, b_1, C_U(Y_1)), ..., fees].
Every user input supplies its owner’s signature, and the verifier input supplies the policy proofs. Liquid’s asset conservation and the two covenant checks preserve both the verifier output and the confinement of A.
An issuer update is instead
(V, q, C_V(π)) → (V, q, C_V(π')).
The issuer authorizes this transition through G(I) with a signature under I, updating the policy.
4. Example Policy Checks
The verifier can implement any predicate expressible from committed policy data, transaction data, and witness proofs. The following examples are common rather than exhaustive. Sparse Merkle trees [12] and Cartesian Merkle trees [13] provide compact membership and non-membership proofs. We use that standard abstraction instead of specifying a concrete tree implementation.
4.1. Blacklist
For an input outpoint (t, v), define a policy key
k_{out}(t, v) = SHA256(t\ ||\ BE_{32}(v)).
The issuer commits to a sparse Merkle tree containing blacklisted outpoints. Each regulated input supplies a non-membership proof for its key against the committed root. A listed UTXO cannot satisfy the verifier. An unlisted UTXO can be spent without contacting the issuer if the holder has the current tree data.
4.2. Whitelist
A whitelist uses membership proofs. Keying it by stable owner public keys permits repeated transfers among already approved holders. The verifier checks the owners of regulated inputs and the recipients of regulated outputs. An issuer may instead key the list by outpoint when every individual UTXO requires approval. However, that stronger model requires a policy update after creating a new outpoint and therefore restores issuer availability as a prerequisite for the next spend.
4.3. Transfer limit
Let X be the authenticated sender and ℓ the current limit. The verifier checks three conditions. All regulated inputs in the limited transfer must belong to X. Payments to other owners must use explicit values. Their sum must satisfy
\sum_{asset(o)=A, owner(o)≠X} value(o) ≤ ℓ.
Outputs back to X are change and may not need to be counted toward ℓ. Their values may remain confidential. The requirement for one sender prevents a recipient from adding a small input and mislabeling a large payment as change. This limits the payment amount in one transaction. However, it does not limit the size of a spendable UTXO.
4.4. Extensions
The same mechanism can combine several Merkle roots, or it can enforce other rules. Examples include jurisdiction sets, credential expiry bounds, and transaction type restrictions. New logic is activated by moving V to a new verifier program.
5. Security Considerations
5.1. Custody and confinement
We assume that BIP-340 is unforgeable and that Taproot and policy hashes are binding. We assume that no one knows the discrete logarithm of K. We also assume that Liquid conserves assets. The issuer alone controls any retained reissuance authority for V. Every accepted verifier output carries q units of V. We also assume that the issuer places every newly issued unit of A in a user covenant. If V cannot be reissued, one verifier output exists. If the issuer retains its reissuance authority, the following claims apply to each verifier output separately. The following claims hold after setup and every primary verifier spend:
- The spent q units of V are recreated in a successor verifier output under the same policy.
- Every new A output has the form C_U(X) for some owner X.
- Spending such an output requires X’s signature and either the current verifier program or deliberate issuer participation through G(I).
Setup establishes the claims. Verifier recreation and conservation preserve the first claim. The complete output scan preserves the second claim. The user signature and verifier asset checks preserve the third claim. The issuer can freeze or replace future rules, yet it cannot sign for a holder.
5.2. Availability
Ordinary transfers require no issuer signature and no designated API. If the issuer service goes offline, existing verifier outputs remain usable. This guarantee depends on the availability of policy data. A Merkle root authenticates a proof but cannot generate one. Holders should therefore keep or mirror every active snapshot. Loss of that data can halt the primary path until it is recovered or the issuer publishes an update.
The issuer remains a governance authority. Possession of the secret key behind I permits arbitrary verifier replacement or a global halt through G(I). The protocol therefore decentralizes enforcement but not policy selection.
5.3. Confidentiality
The verifier must distinguish A from other assets. Asset identifiers are therefore explicit. Policy commitments and spend proofs are public. A proof may also reveal the queried key and path. If no policy depends on values, confidential value commitments and range proofs remain available. With the transfer limit above, recipient amounts are explicit while sender change may remain confidential. Policies keyed by public identity reveal more links than policies keyed by outpoints.
5.4. Contention
A single verifier output creates a spending race condition, limiting throughput. If the issuer retains V’s reissuance authority, it can create parallel verifier outputs with q units each. These outputs can serve more demand and increase per-block throughput. Keeping policy versions consistent across those outputs is outside this paper’s scope. An optional coordinator can also increase throughput by ordering transactions without becoming an authorization service, yet potentially introducing MEV.
5.5. Issuer key and redemption
Compromise of I permits policy revocation or denial of service. Threshold Schnorr, MuSig, or a native multisig governance covenant can distribute that authority without changing the user covenant [6].
The primary transfer path has no redemption exit. However, the issuer path G(I) still permits an informal exit. Spending V at input zero through that path satisfies the user covenants spent in the same transaction without running P(π). A collaborating holder and the issuer can therefore move A outside confinement. A holder who does not consent is unaffected. Every user input still requires its owner’s signature.
A dedicated redemption branch could impose fixed rules. It would retain the holder signature requirement and add an issuer signature over the entire transaction. It would permit explicit A outputs only to a fixed redemption covenant or a provably unspendable burn script. Both forms of redemption require agreement from the holder and the issuer – the issuer therefore cannot confiscate funds on its own.
5.6. User-controlled UTXO discovery
Discovering UTXOs locked behind Simplicity scripts remains an open problem in the Liquid ecosystem at the time of writing. We propose to use script public keys alone to discover the necessary UTXOs. The Liquid Wallet Kit implements this approach as a descriptor variant [14].
6. Future Work
Future work should specify a policy snapshot publication format and the construction of policy commitments. Update race conditions need a formal treatment, and simultaneous use of multiple verifier covenants requires evaluation. A user covenant may be made stateless via a Simplicity composition pattern. Private policy proofs deserve further study. A redemption path authorized by both parties remains to be developed. UTXO discovery for user-controlled balances remains a wider Liquid ecosystem problem.
7. Conclusion
Simplicity can move regulatory enforcement from an issuer’s online cosigner into a Liquid covenant. The issuer keeps the authority to set rules while holders keep their self-custody. Holders can transfer under the published policy whenever its authenticated data remains available.
AI Disclosure
Parts of this work were prepared with an AI assistant, Claude by Anthropic. The assistant helped draft and revise the paper under the authors’ direction. The protocol design, the paper’s structure, and all final decisions are the authors’. The authors reviewed the generated content and take full responsibility for the final output.
References
[1] Jonas Nick, Andrew Poelstra, and Gregory Sanders. Liquid: A Bitcoin sidechain. https://blockstream.com/assets/downloads/pdf/liquid-whitepaper.pdf, 2020.
[2] Blockstream. Liquid Assets.
[3] Circle Internet Financial. USDC stablecoin smart contracts. GitHub - circlefin/stablecoin-evm at 7acbe662898945f1a989ad74e28cfbc8897f4a4b · GitHub. See contracts/v1/Pausable.sol and Blacklistable.sol.
[4] Tether International, S.A. de C.V. Relevant information document. Tether – Official Home of Tether, 2026. Version dated 20 February 2026.
[5] Russell O’Connor. Simplicity: A new language for blockchains. https://blockstream.com/simplicity.pdf, 2017.
[6] Blockstream Research. Programmable signatures with Simplicity. simplicity-native-multisig/programmable-signature-tex at 94dfdcca505f13718cd3c933bfc1504cfb7c1a01 · BlockstreamResearch/simplicity-native-multisig · GitHub, 2026. Companion manuscript in the Blockstream Research native multisig repository.
[7] Pieter Wuille, Jonas Nick, and Tim Ruffing. BIP 340: Schnorr signatures for secp256k1. bips/bip-0340.mediawiki at e7263a4cfe500c89e4269889244606953691ca33 · bitcoin/bips · GitHub, 2020.
[8] Andrew Poelstra and Russell O’Connor. Simplicity: Taproot and universal sighashes. Blockstream, 2021.
[9] Simplicity contributors. Covenants & State Management - Simplicity.
[10] Pieter Wuille, Jonas Nick, and Anthony Towns. BIP 341: Taproot: SegWit version 1 spending rules. bips/bip-0341.mediawiki at e7263a4cfe500c89e4269889244606953691ca33 · bitcoin/bips · GitHub, 2020.
[11] rust-elements contributors. Elements Taproot tagged hash domains. rust-elements/src/taproot.rs at daf9d835ad6f392aad77a27930784ad1ae9265ca · ElementsProject/rust-elements · GitHub.
[12] Faraz Haider. Compact sparse Merkle trees. Technical Report 2018/955, Cryptology ePrint Archive, 2018. https://eprint.iacr.org/2018/955.pdf.
[13] Artem Chystiakov, Oleh Komendant, and Kyrylo Riabov. Cartesian Merkle tree. [2504.10944] Cartesian Merkle Tree, 2025.
[14] Blockstream. Script public key descriptors in the Liquid Wallet Kit. lwk/lwk_wollet/src/descriptor.rs at 87d4920e33ff7662610d9e96276586a9dfb60adc · Blockstream/lwk · GitHub.

