Delivery vs Payment (DvP) program

The DvP program is the standard program for delivery versus payment on Solana, maintained by the Solana Foundation and audited by Cantina. Two parties agree on a trade, each sends its leg to an escrow account with an ordinary token transfer, and a settlement authority settles both legs in one transaction, so either both move or neither does. The settlement authority is a third address named in the trade, such as the venue or agent that arranged it. Neither party writes or deploys a program, and a wallet or custodian that can send a standard token transfer (TransferChecked) can fund a leg with no DvP-specific integration. Until settlement, either party can take its own leg back or unwind the trade.

Read the record before acting on it

Creating a trade record is permissionless, so a record is not proof of agreement. Each party reads the stored terms before it funds, and the settlement authority before it settles (see verification).

How it works

Each trade gets a program-owned record and two escrow accounts, one per leg. The asset-leg party (user_a) and the cash-leg party (user_b) each fund a leg by transferring tokens into the escrow account for that leg. The settlement authority is the only signer that can settle. Settlement transfers the agreed amount of each leg to the other side, returns any surplus to the party that owns the leg, and closes the record and both escrows. The atomicity comes from Solana transactions: both transfers are in one transaction, so either both take effect or neither does.

Every refund, reclaim, and surplus return goes to the party's own associated token account: user_a's account for mint_a, user_b's for mint_b. A custodian or treasury system can fund a leg from its own account, but anything returned lands in the party's account, not back with the custodian. Each trade also leaves a permanent marker account (see State).

Guides

The step pages take one trade from creation to settlement, with TypeScript and Rust for each instruction.

A devnet demo runs a trade end to end: it creates the trade, funds both escrows with standard token transfers, and settles both legs in one transaction.

Choosing between the program and delegation

Delivery vs Payment (DvP) using delegation settles the same exchange without a program: each side delegates its position to a settlement agent, which moves both legs in one transaction.

  • The program needs no delegation, so the limit of one delegate per token account does not apply, and the settlement authority cannot redirect proceeds. Each trade depends on an upgradeable program and its upgrade authority.
  • The delegation pattern has no program dependency and works with mints that carry Scaled UI Amount. The program rejects those mints, which rules out any mint created from Mosaic's tokenized-security template (see mint configuration constraints).

Roles

The program is symmetric. In its interface the two parties are user_a, who delivers mint_a, and user_b, who delivers mint_b. The README and the generated clients call mint_a the asset leg and mint_b the cash leg, and these pages follow that convention, but nothing in the program requires the cash leg to be a cash instrument. Two assets, or two stablecoins, settle the same way.

RoleWhoSignsNotes
Asset-leg partyuser_a, delivers mint_aIts own funding transfer; Reclaim, Reject, RecoverA keypair wallet, or a PDA-based smart wallet such as a multisig vault. An SPL Token multisig account or a program account cannot be a party: Create fails with PartyNotSignerCapable
Cash-leg partyuser_b, delivers mint_bIts own funding transfer; Reclaim, Reject, RecoverSame requirement
Settlement authorityA third address named at creationSettle, CancelThe only signer that can settle. It cannot redirect proceeds, which are fixed at creation. It receives the rent of the closed accounts when it settles or cancels
Rent payerWhoever signs CreateDvpCreateFunds the rent (the SOL deposit that keeps an account open) for the record, the nonce tombstone, and both escrows. Not recorded as a rent recipient: closed-account rent goes to whoever closes the trade

Program ID

dvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZq
ClusterProgramUpgrade authorityLast deployed slot
mainnet-betadvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZqDXtFpbPjcn2hxPnw79x1Pfoj35vXh5AsWBkS37YnXMVv452,058,374
devnetdvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZq5EX1zFeJWj4rGYZv432WnYw8CbaSUeTdxPx4r573UgYU479,376,863

The program is upgradeable on both clusters. The figures above are what solana program show reported on 2 October 2026; run the same command for current values. The devnet build is older than the commit the step pages are written against (see Install), with the same instruction and error set.

Lifecycle

A trade is open from CreateDvp until one of Settle, Cancel, or Reject closes it. Funding has no instruction of its own: a leg is funded when its escrow account holds at least the agreed amount. Reclaim is per leg and leaves the trade open, so a problem with one leg does not force the other party to cancel the whole trade.

State

Each trade has one SwapDvp account of 458 bytes, owned by the program. Its address is derived from the settlement authority, the two parties, the two mints, and a nonce (the seeds ["dvp", settlement_authority, user_a, user_b, mint_a, mint_b, nonce]). The amounts and times are stored inside the account and do not affect the address, which is why they have to be read rather than inferred. A small marker account, the nonce tombstone at ["nonce", swap_dvp], is created with the trade and never closed, so each combination of parties, mints, authority, and nonce can be used for one trade only.

FieldTypeMeaning
bumpu8PDA bump
user_a, user_bPubkeyThe two parties
mint_a, mint_bPubkeyThe asset-leg and cash-leg mints
settlement_authorityPubkeyThe only signer that can settle or cancel
token_program_a, token_program_bPubkeyThe token program that owns each mint; a trade can mix SPL Token and Token-2022 legs
amount_a, amount_bu64The agreed amount of each leg, in the mint's smallest unit (base units)
expiry_timestampi64After this network time (the onchain clock), Settle is rejected. Reclaim, Cancel, and Reject still work
nonceu64Disambiguates trades that share the other seeds. Single-use
ref_string[u8; 64]An opaque client reference, zero-padded; the program never reads it
user_a_settlement_destinationPubkeyReceives the cash leg at Settle; defaults to user_a
user_b_settlement_destinationPubkeyReceives the asset leg at Settle; defaults to user_b
mint_a_authority, mint_b_authorityPubkeyThe mint authorities captured at creation; Settle fails if either has changed
earliest_settlement_timestampOption<i64>If set, Settle is rejected before this network time

The field list is taken from the program's IDL at the commit named under Create a trade.

Instructions

InstructionSignerEffect
CreateDvpAny payerCreates the SwapDvp record, its nonce tombstone, and both escrow accounts. Moves no tokens
ReclaimDvpuser_a or user_bReturns the signer's own leg from escrow. The trade stays open and the leg can be funded again
SettleDvpSettlement authorityTransfers amount_a to user_b's destination and amount_b to user_a's destination, returns any surplus to the leg's party, closes the record and both escrows
CancelDvpSettlement authorityRefunds whatever escrow A holds to user_a and escrow B to user_b, closes the record and both escrows
RejectDvpuser_a or user_bSame effect as Cancel, signed by a party
RecoverDvpuser_a or user_bAfter the trade is closed: drains a recreated escrow to the signer's own token account and closes it. Covers a deposit that landed after Settle, Cancel, or Reject

Every refund goes to the party's own associated token account for that leg's mint, whoever funded the escrow.

Each instruction's accounts and arguments are on the page for that step.

Errors

CodeErrorWhen
0SignerNotPartyReclaim, Reject, or Recover signed by an address that is not user_a or user_b
1DvpExpiredSettle after expiry_timestamp
2SettlementAuthorityMismatchSettle or Cancel signed by an address other than the settlement authority
3SettlementTooEarlySettle before earliest_settlement_timestamp
4LegNotFundedSettle while an escrow holds less than its agreed amount
5ExpiryNotInFutureCreate with an expiry at or before the current network time
6EarliestAfterExpiryCreate with an earliest settlement time after the expiry
7SelfDvpCreate with user_a equal to user_b
8SameMintCreate with mint_a equal to mint_b
9ZeroAmountCreate with a zero amount on either leg
10BlockedMintExtensionCreate or Settle with a mint carrying TransferFee, InterestBearing, ScaledUiAmount, or NonTransferable
11SettlementAuthorityIsPartyCreate with the settlement authority equal to a party
12SettlementAuthorityExecutableCreate with an executable settlement authority
13NonceAlreadyUsedCreate reusing a (seeds, nonce) that already has a tombstone
14ExpiryTooFarInFutureCreate with an expiry more than one year ahead
15RefStringTooLongCreate with a reference longer than 64 bytes
16DvpStillOpenRecover while the trade is still open; use Reclaim
17DvpNeverCreatedRecover with seeds that have no tombstone
18EscrowPreloadedWithLamportsCreate when a non-native escrow holds lamports above its rent-exempt minimum
19SettlementDestinationIsSwapDvpCreate with a settlement destination equal to the trade record
20SwapDvpPreloadedWithLamportsCreate when the record address holds lamports above its rent reserve
21RecipientAtaMismatchA recipient or refund token account no longer belongs to the expected wallet and mint
22PartyNotSignerCapableCreate with a party that is not a system-owned, non-executable account
23MintAuthorityChangedSettle when a leg's mint authority differs from the value captured at creation

Events and indexing

The program emits no onchain events. An indexer or reconciliation system reads the SwapDvp account state and the token balances of the two escrows, and uses ref_string to correlate a trade with its off-chain order. Because the record is closed at settlement, a system that needs the terms after the fact stores them when it reads them, or reconstructs them from the transaction that created the trade.

Supported tokens

A trade can pair an SPL Token leg with a Token-2022 leg; each leg carries its own token program. Wrapped SOL legs are supported.

Token-2022 extension on a leg's mintProgram behavior
TransferFee, InterestBearing, ScaledUiAmount, NonTransferableRejected at Create and Settle with BlockedMintExtension
PermanentDelegate, Pausable, DefaultAccountState, a freeze authority, MintCloseAuthorityAccepted; each authority can act on the escrowed tokens
ConfidentialTransferAccepted; amounts on that leg are public
TransferHookSupported, up to 32 extra accounts per leg
MemoTransfer on a destination or refund accountAccepted; the program emits the required memo before the transfer

A transfer fee would leave the receiving side short of the agreed amount. Interest Bearing and Scaled UI Amount do not change raw amounts, but their rate or multiplier can change between creating and settling a trade, which changes how the agreed amounts display. NonTransferable would strand both legs.

The accepted authorities can move, freeze, or halt the escrowed tokens for the life of the trade (see below). The escrow cannot be configured to receive confidential transfers, so the escrowed and settled amounts on a confidential leg are public.

For a hooked mint, the client resolves the hook's extra accounts and appends them to Settle, Cancel, Reject, Reclaim, and Recover. The cap of 32 per leg counts the hook program and its validation account. If the hook authority enlarges the hook's account list past that cap, every transfer on that leg fails, including Reclaim, Cancel, Reject and Recover, until the authority reverses it. The transaction account limit can bind before the per-leg cap when both legs carry hooks.

When a destination or refund account requires memos, the program calls the Memo program before the transfer, which is why every instruction that moves tokens out of escrow takes a memo_program account.

Mint configuration constraints

Scaled UI Amount. The tokenized-security template in Mosaic, the Solana Foundation's issuance toolkit, always adds Scaled UI Amount, so the program does not accept a mint created from that template as a leg. A mint intended for this program can be created without the extension (see Token Extensions); DvP using delegation settles through delegated authority and does not apply this check.

Frozen-by-default mints. Each trade's escrow is an associated token account owned by a program-derived address that is new for that trade. On a mint with Default Account State set to frozen, the escrow is created frozen, and a funding transfer into it fails until it is thawed. Under Token ACL that means the issuer or its transfer agent admits the trade's escrow before funding: either by adding the trade record's address (the escrow's owner) to the allowlist, so that anyone can thaw the escrow where the mint's Token ACL config enables permissionless thaw, or by having the freeze authority thaw it directly. A frozen escrow also blocks Settle, Reclaim, Cancel, Reject and Recover on that leg, so the freeze authority, and on a hooked mint the hook authority, is a trusted party for the trade's lifetime. The frozen-escrow path is described here and is not exercised in the devnet snippets.

Limits

  • No matching, price discovery, or order book. The parties agree the terms off the ledger and one of them records them.
  • No netting, and bilateral only: one record is one trade between two parties.
  • No partial fills. Settle moves exactly the agreed amount of each leg and returns any surplus to the leg's party.
  • No off-ledger leg. Both legs are token accounts on Solana; a cash leg on existing rails settles separately and is reconciled.
  • No eligibility check, allowlist, or KYC. The mint's own controls enforce holder eligibility.
  • No automatic settlement. The settlement authority has to sign.
  • No events, and no protocol fee beyond transaction fees and rent.

The program removes principal risk between the two parties: neither leg moves unless both do. It does not remove issuer credit or redemption risk on either token, the ability of a mint's freeze, pause, or permanent delegate authority to act on escrowed tokens (see supported tokens), or the dependence on the settlement authority being available to sign.

Verification before funding and settlement

CreateDvp is permissionless: only the payer signs, and the parties and the settlement authority do not. Anyone can therefore create a record naming any parties and any terms. Before funding, and again before settling, read the SwapDvp account and confirm:

  1. The account is owned by the DvP program and is exactly 458 bytes. An account that is not owned by the program can be filled with bytes that decode like a real record.
  2. user_a, user_b, mint_a, mint_b, and settlement_authority match the agreed trade.
  3. amount_a, amount_b, expiry_timestamp, and earliest_settlement_timestamp match the agreed terms.
  4. Both settlement destinations are the agreed addresses. A forged record can redirect proceeds.

Fund the legs lists the generated clients' checked readers and shows the check in code.

Treat a trade as settled when the Settle transaction reaches finalized. That is a network commitment level; whether it also marks settlement finality for legal purposes depends on the parties' agreements and the regimes that apply.

Versioning

The SwapDvp account carries no version field. Its layout is fixed at 458 bytes (SwapDvp::LEN), and the checked readers reject an account of any other length. The IDL carries its own version, 0.1.0 as of 2 October 2026.

The program is upgradeable on both clusters (see Program ID). A trade record exists only until it is settled, cancelled, or rejected, so check the repository for how an upgrade treats open trades, and regenerate clients from the IDL of the deployed build.

Repository and audit

Source, IDL, generated clients, and tests are in solana-foundation/dvp under the MIT license. The program is a no_std Pinocchio program; the clients are generated from the IDL with Codama. Vulnerability reports go through the repository's security advisory link, per its SECURITY.md.

The program has been audited by Cantina.

Is this page helpful?

İçindekiler

Sayfayı Düzenle
© 2026 Solana Vakfı. Tüm hakları saklıdır.