Drops Protocol documentation

Application layer · marker drops-pact

Drop Pacts: agreements that keep their receipts

A Pact records an agreement's identity and the fingerprints of its rules on Bitcoin, so that any party can later show which rules were agreed and which outcome followed from them. It is deliberately honest about how much of that Bitcoin itself enforces.

Three people in iridescent evening wear around a lit glass table, studying a chrome padlock and a translucent panel of linked nodes above a night city.
Parties, terms, and a record that outlives the interface that produced it.

Read this before anything else: Pacts is reference-only today

The reference indexer publishes a machine-readable capability boundary. Its mode is reference. Five things are supported, and twelve authority fields are all false.

Pacts capability contract, schema version 1
GroupFieldsValue
supported verifiedDrops, recordedPactSeeds, pactsReferences, deterministicPrimitives, localProofPackValidation true
authority pactCell, liveCells, transaction, authorization, signature, signatures, broadcast, custody, contractExecution, enforceableOffChainPolicy, valueBearingPacts, valueBearingAuthority false
recovery.status Guidance returned with every capability response reference_only

In the indexer's own words: Pact Seeds and Pacts references are recorded evidence only. They are not live Pact Cells, transaction requests, authorization, signatures, custody records, enforceable off-chain policy, or value-bearing authority.

If a screen tells you a Pact transaction is pending

No funds or live Pact state are held by the reference service. Do not sign and do not send funds. Keep the full blueprint and the plan hash together, verify any Bitcoin transaction independently in a wallet or explorer you trust, and contact support with the plan hash. No Pacts interface ever needs a seed phrase or a private key.

Everything below the capability contract describes the record formats and the validation rules that are implemented and testable today. Where a structure belongs to the future execution model rather than to what the reference service will act on, this page says so in the section itself.

1. The Pact Seed

A Pact Seed is a Drop. It uses the ordinary Drops leaf, with marker drops-pact and content type application/vnd.drops.pact-seed. Its body is a fixed 184-byte binary record. Nothing about it is free-form, which is why two implementations cannot disagree about what an agreement's identity is.

Byte layout of the 184-byte Pact Seed record Offset zero: four ASCII bytes, D P S E, the record magic. Offset four: one reserved zero byte. Offset five: one network tag byte. Offset six: one enforcement floor tag byte. Offset seven: one reserved zero byte. Offset eight through twenty-four: sixteen bytes of engine identifier, ASCII, zero padded. Then five thirty-two byte hashes in order: ruleset hash at offset twenty-four, ABI hash at fifty-six, genesis state root at eighty-eight, policy root at one hundred and twenty, and data availability policy hash at one hundred and fifty-two. The record ends at offset one hundred and eighty-four. DPSE 4 bytes 0 00 rsv 4 net 1 byte 5 floor 1 byte 6 00 rsv 7 engineId 16 bytes, zero padded 8 rulesetHash 32 bytes 24 abiHash 32 bytes 56 genesis 32 bytes 88 policyRoot 32 bytes 120 daPolicy 32 bytes 152 Total: exactly 184 bytes, which fits inside the 256-byte Drops body limit with room to spare. Network tags: mainnet 0, testnet 1, signet 2, regtest 3. Enforcement floor tags: recorded 0, co-signed 1, template-enforced 2. Engine id grammar: one to sixteen characters matching a lowercase letter followed by letters, digits or hyphens. Reserved bytes must be zero, engine padding must be zero, and re-encoding the decoded record must reproduce it exactly.
A fixed-width record with no optional fields. Decoding is a length check followed by five slices.

1.1 Seed fields

What each Pact Seed field pins
FieldSizeMeaning
network1 byte tagThe Bitcoin network this Pact belongs to. A Pact does not exist on another network.
enforcementFloor1 byte tagHow much of the agreement Bitcoin enforces by itself. See enforcement floors.
engineId16 bytesWhich rule engine interprets the ruleset. Identifies the interpreter, not the agreement.
rulesetHash32 bytesThe agreement's rules. Change one clause and this hash changes.
abiHash32 bytesThe shape of the actions the ruleset accepts.
genesisStateRoot32 bytesThe agreement's opening state, before any transition.
policyRoot32 bytesThe policy in force at genesis. Policy can move; the ruleset cannot.
dataAvailabilityPolicyHash32 bytesWhere the parties agreed the off-chain material will be kept, and on what terms.

1.2 Pact identity

A Pact's identity is derived, not chosen. It is a tagged hash over the Seed's own Drop identity plus the three hashes that cannot change during the Pact's life.

pactId = taggedHash("Drops/Pact",
             networkTag                    (1 byte)
          || txid                          (32 bytes, internal byte order)
          || seedInputIndex                (4 bytes, big-endian)
          || rulesetHash                   (32 bytes)
          || abiHash                       (32 bytes)
          || policyRoot                    (32 bytes))

The Seed's Drop identity supplies the transaction id and input index. Because genesisStateRoot and dataAvailabilityPolicyHash are not in the preimage, a Pact keeps its identity across state changes, while a different ruleset, a different action shape, or a different genesis policy produces a different Pact.

2. Enforcement floors

This is the field that stops a Pact from pretending. It states the weakest guarantee a reader may assume, in one word.

The three enforcement floors
FloorTagWhat Bitcoin guaranteesWhat still depends on people
recorded0 That these exact terms were committed at this height, and nobody edited them afterwards. Everything else. Performance, delivery, payment, and any consequence of breach.
co-signed1 The above, plus that a spend needs the signatures the script requires. Whether the signers were entitled to sign, and whether the off-chain obligation was met.
template-enforced2 The above, plus the script constraints of a known template: timelocks, required keys, and which outputs a spend may create. Anything the template does not encode. A template constrains spends; it does not read the agreement.
Read the floor as a ceiling on your trust

A recorded Pact is a notarised document, not an escrow. Bitcoin can enforce signatures, timelocks, and whether a particular output has already been spent. It cannot enforce that a shipment arrived. An interface that blurs those two things is the failure this field exists to prevent.

3. Pact lifecycle

A Pact has three structural pieces. The Seed is recorded today. The Cell and the transition belong to the execution model, and the reference service validates them locally without acting on them.

The Pact lifecycle from Seed to successor Cell Stage one, the Pact Seed, a Drop with marker drops-pact, fixes the agreement identity. Stage two, a Pact Cell, is a separate Tapscript leaf with marker drops-cell carrying a one hundred and seventy-six byte descriptor that holds the pact id, a sequence number, a state root, a policy root, a state attachment hash and a transition commitment. Stage three, a transition, spends the parent cell and creates a successor cell whose sequence is one higher; an OP_RETURN output tagged D P C 1 carries the thirty-two byte transition commitment, and a proof pack carries the evidence. Arrows run left to right, and a note says the successor cell must prove its own Taproot commitment. STAGE 01 · RECORDED TODAY Pact Seed A Drop, marker drops-pact 184-byte record, 5 pinned hashes Fixes the agreement identity pactId = taggedHash(...) STAGE 02 · EXECUTION MODEL Pact Cell Separate leaf, marker drops-cell 176-byte descriptor, magic DPCL pactId, sequence, stateRoot, policyRoot, attachment, commitment STAGE 03 · EXECUTION MODEL Transition Spends the parent cell, creates a successor successor.sequence = parent.sequence + 1 OP_RETURN 6a24 "DPC1" <32-byte commitment> Proof pack carries the supporting evidence Every stage is checked against Bitcoin, never against a database label. The successor cell must prove its own Taproot commitment, the transition preimage must hash to the commitment the successor descriptor carries, and the DPC1 anchor must carry that same value. An anchor on its own proves nothing. It marks a candidate transition; the proof pack is what decides whether the candidate is real. The reference service validates all of this locally and acts on none of it. There is no live Cell and no settlement authority.
Seed, Cell, transition. The reference deployment records the first and validates the other two without granting them authority.

3.1 The Cell descriptor

176-byte Cell descriptor, magic DPCL
OffsetSizeFieldNotes
04magic DPCLASCII
41reservedMust be zero
51flagsOne byte
62reservedBoth bytes must be zero
832pactIdTies the cell to its Pact
408sequenceUnsigned 64-bit, big-endian
4832stateRootThe state this cell asserts
8032policyRootThe policy in force at this cell
11232stateAttachmentHashBinds the off-chain state material
14432transitionCommitmentThe transition that produced this cell

The Cell leaf that carries the descriptor is deliberately tiny: a push of the marker drops-cell, OP_DROP, a push of the 176-byte descriptor, OP_DROP, then OP_0. It is a commitment carrier, not a spending condition, and it is not a Drops leaf.

3.2 The transition preimage

A transition is committed by hashing a fixed 245-byte preimage. The commitment appears in two places, and both must agree.

transitionCommitment = taggedHash("Drops/PactTransition", preimage)

preimage (245 bytes, big-endian integers)
  0    1   networkTag
  1   32   pactId
  33  32   parent txid, internal byte order
  65   4   parent output index
  69   4   parent input index
  73   4   successor output index
  77   8   parent sequence
  85  32   parentStateRoot
  117 32   nextStateRoot
  149 32   nextPolicyRoot
  181 32   proofPackHash
  213 32   opDropEffectHash

If a transition has no OP_DROP token effect, opDropEffectHash is 32 zero bytes. Otherwise it is the SHA-256 of the effect payload. That single field is how a Pact and a token movement are tied together without either protocol reaching into the other.

4. Outcomes and how they are checked

An outcome is a claim that the Pact moved from one state to the next. Checking it means checking that the recorded successor is exactly the successor the stated terms produce. Nine conditions must all hold.

  1. P-1

    The parent descriptor, the successor descriptor and the transition must all carry the same pactId.

    PACTS_INVALID_TRANSITION: Pact IDs do not match

  2. P-2

    parent.sequence must equal the transition's parentSequence, and successor.sequence must equal parent.sequence + 1. No gaps, no rewinds.

    PACTS_INVALID_TRANSITION: cell sequence is invalid

  3. P-3

    parent.stateRoot must equal parentStateRoot, successor.stateRoot must equal nextStateRoot, and successor.policyRoot must equal nextPolicyRoot.

    PACTS_INVALID_TRANSITION: cell roots do not match the transition

  4. P-4

    The ruleset result recorded in the proof pack must equal nextStateRoot. The outcome must be the outcome the rules produced.

    PACTS_INVALID_TRANSITION: ruleset result does not match next state root

  5. P-5

    The OP_DROP effect hash must equal SHA256(effect) when an effect is present, and 32 zero bytes when it is not.

    PACTS_INVALID_TRANSITION: op-drop effect hash does not match

  6. P-6

    proofPackHash must equal the hash of the proof pack payload actually supplied.

    PACTS_INVALID_TRANSITION: proof pack payload hash does not match

  7. P-7

    successor.transitionCommitment must equal taggedHash("Drops/PactTransition", preimage).

    PACTS_INVALID_TRANSITION: successor descriptor does not commit to the transition

  8. P-8

    The successor Cell leaf must prove its own Taproot commitment against the successor output script, using the same control-block rules as a Drop.

    PACTS_INVALID_SUCCESSOR_CELL

  9. P-9

    The DPC1 anchor must decode to that same transition commitment. The anchor script is exactly 38 bytes: 6a 24 "DPC1" <32-byte commitment>.

    PACTS_INVALID_DPC1: DPC1 commitment does not match the proof pack transition

The pact verifier on this site runs rules P-1, P-2, P-3, P-7 and P-9 in your browser from descriptor and preimage hex, and explains exactly which one failed.

4.1 The proof pack

The proof pack is a deterministic CBOR structure holding the transition, both descriptors, the successor control block and output script, and the ruleset material: rulesetInput, stateWitness, stateDelta, rulesetResult, an optional opDropEffect, and a list of availability hashes. Its encoding is deterministic on purpose, so that a proof pack has one serialization and therefore one hash.

5. The Pacts reference profile

Not every Pact needs the full execution model. The lightest useful record is a Pacts reference: a Drop that ties a template to the two hashes a party keeps locally, so they can prove later which plan and which blueprint they agreed to.

Pacts reference profile
Content typeapplication/vnd.drops.pacts-reference+json
BodyUTF-8 JSON, exactly four keys, serialized in sorted key order with no whitespace
Keysbh blueprint hash, p always pacts, ph plan hash, t template id
Hash format64 lowercase hex characters each
Template grammar^[a-z][a-z0-9-]{0,40}$
SizeAlways well under the 256-byte body limit
{"bh":"<64 hex>","p":"pacts","ph":"<64 hex>","t":"escrow-basic"}

A body that decodes to the right values but was serialized differently is rejected. Re-encoding the parsed reference must reproduce the body byte for byte, exactly as it must for the leaf itself. Unknown keys, missing keys, reordered keys, added whitespace and non-string values are all rejections.

The plan hash and the blueprint hash reveal nothing on their own. They let a party show that the document they kept is the document the Pact committed to.

A dark studio still life: a glass-fronted vault of coins on the left, an illuminated padlock in the centre, and a rising row of glass blocks on the right, connected by thin light lines.
Shared custody, escrow, vesting and asset policy are the shapes people reach for. What Bitcoin proves about each is the enforcement floor, not the label.

6. Agreement shapes

These are the agreement structures the Pact record format is designed to describe. They are descriptions of the model, not services offered by the reference deployment.

Shared custody
Controllers, recovery expectations, and each approved change to the agreement state. A co-signed or template-enforced floor is what makes the signing rules real rather than described.
Escrow
Release and refund paths recorded before value moves, so both parties can point at the same terms afterwards.
Vesting
A release schedule tied to block height. Timelocks are one of the few things Bitcoin enforces directly, which makes this shape a good fit for template-enforced.
Asset policy
An OP_DROP token policy pointed at a durable public agreement record. The opDropEffectHash field is the join.
Community treasury
Shared policy, current state, and the history of approved changes, all readable from chain data rather than from an application's database.

Check an outcome against its terms or decode a Pact Seed body.