Skip to content

Solana DvP Program USDC Settlement: How to Settle a Tokenized Asset (2026)

Settle a tokenized asset for USDC with the Solana DvP program: Tokenkeg cash leg, verified terms, four recipient accounts, finalized settlement, and Reclaim/Cancel/Reject.

Table of Contents

A tokenized bond, fund share, or commercial paper note only becomes useful for institutions when the asset and the cash change hands together. On Solana, the open standard for that is the Solana DvP Program, and in most real trades the cash leg is USDC.

This Stablecoin Insider guide walks through Solana DvP program USDC settlement end to end: agreeing terms, creating the trade with CreateDvp, verifying the stored record, funding the asset leg and the USDC leg with plain TransferChecked transfers, settling both legs in one SettleDvp transaction, and unwinding a trade that does not settle. For the news on why the standard matters and what J.P. Morgan did and did not contribute, read Stablecoin Insider's report: Solana Publishes an Open Settlement Standard That Settles in USDC.

Key Takeaways

  • The DvP program escrows each leg and settles both in one transaction or neither moves.
  • USDC on Solana is a legacy SPL Token mint, so the cash leg uses the Tokenkeg program, not Token-2022.
  • Both parties and the settlement authority must verify the stored terms before funding or settling.
  • Create all four recipient token accounts in the Settle transaction or a dusted escrow reverts it.
  • Record a trade as settled only when SettleDvp reaches finalized commitment.
🧭
What this guide is: a docs and repository walkthrough of the Solana Foundation DvP program, written against the official step pages (commit df9919e, IDL 0.1.0) and the solana-foundation/dvp README, with a USDC-specific worked example. Stablecoin Insider also read the program and the USDC mint on Solana mainnet-beta over public RPC on 13 October 2026 to confirm the deployment details below. No mainnet trade was executed and no funds moved.
Solana Publishes an Open Settlement Standard That Settles in USDC
Stablecoin Insider's report on the Solana DvP release: what it does, the J.P. Morgan role, and why the payment leg is a stablecoin.

What the Solana DvP Program does in a USDC trade

According to the Solana DvP program documentation, each trade gets one program-owned record (a SwapDvp account) and two escrow accounts, one per leg. The asset-leg party (user_a) sends the tokenized asset into escrow A. The cash-leg party (user_b) sends USDC into escrow B. A third address, the settlement authority, is the only signer that can settle.

When the settlement authority signs SettleDvp, the program moves the agreed asset amount to the buyer, the agreed USDC amount to the seller, refunds any surplus, and closes the record and both escrows. Because both transfers sit inside one Solana transaction, either both happen or neither does. That is the whole point of delivery versus payment: neither side carries principal risk while the other side's leg is in flight.

Three properties make the program easy to plug into existing USDC operations:

  • Funding is a normal transfer. There is no funding instruction. A wallet or custodian that can send TransferChecked can fund the USDC leg with no DvP-specific integration.
  • Proceeds are fixed at creation. The settlement authority cannot redirect money. Settlement destinations are written into the record when the trade is created.
  • Either party can walk away before settlement. ReclaimDvp returns your own leg and keeps the trade open; RejectDvp refunds both legs and closes it.

Deployment facts Stablecoin Insider checked on 13 October 2026

The docs list the program and upgrade authority as reported on 2 October 2026. Stablecoin Insider re-read both accounts on mainnet-beta on 13 October 2026, along with the USDC mint, because the USDC leg's token program and decimals decide how every instruction in this guide is built.

ItemValueSource
DvP program ID (mainnet-beta and devnet)dvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZqSolana DvP docs
Mainnet upgrade authorityDXtFpbPjcn2hxPnw79x1Pfoj35vXh5AsWBkS37YnXMVv (unchanged on 13 Oct 2026)Program ID table
Mainnet last deployed slot452,058,374 (unchanged on 13 Oct 2026)Program ID table
Docs and clients pinned tocommit df9919e, IDL 0.1.0GitHub commit df9919e
AuditCantinaCantina portfolio
USDC mint on SolanaEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1vCircle USDC addresses
USDC token programLegacy SPL Token (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA), 6 decimals, freeze authority setMainnet RPC read, 13 Oct 2026

Sources: Solana DvP program overview (program ID, upgrade authority, and last deployed slot as of 2 October 2026), Circle's USDC contract addresses page for the mint, and Stablecoin Insider's own getAccountInfo reads for the 13 October values.

Before you start: what you need

  • Three addresses. The asset seller (user_a), the USDC buyer (user_b), and a settlement authority that is neither party. Parties must be system-owned wallets or smart-wallet PDAs such as a multisig vault; an SPL Token multisig account fails with PartyNotSignerCapable.
  • Agreed terms in base units. Asset amount, USDC amount (USDC has 6 decimals), expiry, optional earliest settlement time, optional settlement destinations, and a reference string of up to 64 bytes.
  • The generated client. No DvP client package was published as of 2 October 2026, so you clone solana-foundation/dvp, check out df9919e, and generate the TypeScript or Rust client from the IDL.
  • SOL for rent and fees. Whoever signs CreateDvp pays rent for the record, a permanent nonce tombstone, and both escrows. Closed-account rent goes to whoever closes the trade, not back to the payer.
  • USDC on Solana. If the buyer's USDC sits on another chain, bridge it first. Stablecoin Insider's guide to bridging USDC from Ethereum to Solana covers the native CCTP route.
git clone https://github.com/solana-foundation/dvp.git
cd dvp && git checkout df9919ed02c25a93620e7f6820107c9050ce3b92
pnpm install && pnpm generate-clients && pnpm build
# TypeScript client: clients/typescript/src (link it into your project as ./dvp)
pnpm add @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-signer \
  @solana-program/token @solana-program/token-2022 @solana-program/memo

The worked example: 1,000 fund units for 998.50 USDC

Stablecoin Insider uses the same trade size as the official create-a-trade page so the numbers can be checked against the docs: 1,000 units of a 6-decimal tokenized asset for 998.50 units of a 6-decimal cash token. Here the cash token is USDC, so the base-unit amounts are 1,000,000,000 (asset) and 998,500,000 (USDC).

FieldAsset leg (mint_a)Cash leg (mint_b = USDC)
Partyuser_a, the seller (for example a fund's transfer agent wallet)user_b, the buyer (for example a treasury desk)
Token programUsually Token-2022 for a regulated assetLegacy SPL Token (Tokenkeg)
Decimals6 in this example6
Agreed amount (base units)1,000,000,000998,500,000
Transfer hook accountsWhatever the issuer's hook requires (up to 32)None: USDC on Solana has no Token-2022 extensions
Settlement destinationReceives USDC; defaults to user_aReceives the asset; defaults to user_b

Base-unit figures follow the example on the Solana DvP create-a-trade page (amountA 1_000_000_000n, amountB 998_500_000n).

⚠️
The USDC pitfall most teams will hit: the official snippets set both legs to TOKEN_2022_PROGRAM_ADDRESS. USDC on Solana is owned by the legacy SPL Token program. If you leave tokenProgramB as Token-2022, the escrow address for the USDC leg derives to the wrong account and the CreateDvp, funding, and settle instructions will not line up. Set tokenProgramB to TOKEN_PROGRAM_ADDRESS (Tokenkeg) and pass decimals 6 on every USDC TransferChecked.

Step 1: Agree the terms off the ledger

The program does no matching, price discovery, or netting. The two parties agree the trade first and one of them records it. Write down every field the record will store: parties, mints, both token programs, both amounts in base units, expiry, earliest settlement time, both settlement destinations, the settlement authority, and an order reference for ref_string. These are the values each side will check on chain in Step 4.

Pick the expiry with network time in mind. The program reads the onchain clock, which the DvP README says can lag wall-clock time by roughly 10 to 30 seconds and jump forward, so budget about a minute of drift and do not schedule funding or settlement close to either boundary. Expiry can be at most one year after creation.

Step 2: Derive the trade and escrow addresses

The record address is a program-derived address over the settlement authority, both parties, both mints, and a nonce. Each escrow is the record's associated token account for one mint. Use a cryptographically random 64-bit nonce per trade, passed as a bigint, because creation is permissionless and a predictable nonce lets a third party occupy your address first.

import { address } from "@solana/kit";
import { TOKEN_PROGRAM_ADDRESS } from "@solana-program/token";
import { TOKEN_2022_PROGRAM_ADDRESS } from "@solana-program/token-2022";
import { findSwapDvpEscrowAta, findSwapDvpPda } from "./dvp";

const mintA = address("ASSET_MINT_ADDRESS_HERE");
const mintB = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); // USDC
const tokenProgramA = TOKEN_2022_PROGRAM_ADDRESS; // typical regulated asset
const tokenProgramB = TOKEN_PROGRAM_ADDRESS;      // USDC is legacy SPL Token

const nonce = new DataView(
  crypto.getRandomValues(new Uint8Array(8)).buffer,
).getBigUint64(0);

const [swapDvp] = await findSwapDvpPda({
  settlementAuthority, userA, userB, mintA, mintB, nonce,
});
const [escrowA] = await findSwapDvpEscrowAta({ swapDvp, mint: mintA, tokenProgram: tokenProgramA });
const [escrowB] = await findSwapDvpEscrowAta({ swapDvp, mint: mintB, tokenProgram: tokenProgramB });

Step 3: Record the trade with CreateDvp

CreateDvp writes the terms, creates the nonce tombstone, and creates both escrows. It moves no tokens. Only the payer signs, which is why the record proves nothing about agreement on its own. Per the create-a-trade guide, amounts are in base units, settlement destinations default to each party's own address, and refunds always go back to the party's own token account, never to a destination.

const createInstruction = getCreateDvpInstruction({
  payer: payerSigner,
  swapDvp,
  nonceTombstone,             // PDA ["nonce", swapDvp]
  settlementAuthority, userA, userB, mintA, mintB,
  dvpAtaA: escrowA,
  dvpAtaB: escrowB,
  tokenProgramA, tokenProgramB,
  amountA: 1_000_000_000n,    // 1,000 asset units (6 decimals)
  amountB: 998_500_000n,      // 998.50 USDC (6 decimals)
  expiryTimestamp: nowSeconds + 60n * 60n * 24n, // one day
  nonce,
  refString: "ORDER-2026-10-13-0001",
  userASettlementDestination: null, // USDC lands with userA
  userBSettlementDestination: null, // asset lands with userB
  earliestSettlementTimestamp: null,
});

Who should pay? The payer funds rent but the closer receives it, so if the settlement authority will close the trade at Settle, the cleanest arrangement is usually for the settlement authority's operator to send CreateDvp too. Otherwise one party quietly subsidizes the venue.

Step 4: Verify the stored terms before anyone funds

This is the step teams skip and the one that protects the USDC. Escrow addresses derive from the record and the mint, never from the terms, so a transfer can land against terms you never agreed to, including a settlement destination that sends the seller's USDC proceeds to a third address. The fund-the-legs guide says to read the record with the checked reader verifySwapDvp, which confirms the account is owned by the DvP program, is exactly 458 bytes, and sits at the address its own fields derive.

const trade = await verifySwapDvp(client.rpc, swapDvp);
const t = trade.data;
if (
  t.userA !== userA || t.userB !== userB ||
  t.mintA !== mintA || t.mintB !== mintB ||
  t.settlementAuthority !== settlementAuthority ||
  t.tokenProgramA !== tokenProgramA || t.tokenProgramB !== tokenProgramB ||
  t.amountA !== 1_000_000_000n || t.amountB !== 998_500_000n ||
  t.expiryTimestamp !== expectedExpiry ||
  t.userASettlementDestination !== userA ||
  t.userBSettlementDestination !== userB
) {
  throw new Error("Stored terms do not match the agreed trade; do not fund");
}

Do not use the plain fetchSwapDvp or decodeSwapDvp readers for this check; they verify none of the three properties. Read at confirmed commitment, since a record created moments ago is not yet visible at finalized.

Step 5: Fund the asset leg

The seller sends exactly amountA of the asset into escrow A with TransferChecked. Regulated assets usually carry Token-2022 controls, and two of them change the funding step:

  • Frozen-by-default mints. If the asset uses Default Account State frozen, the new escrow starts frozen and the transfer fails until the issuer admits it, either by allowlisting the trade record's address under Token ACL or by having the freeze authority thaw the escrow.
  • Transfer hooks. Resolve the hook's extra accounts exactly as you would for any transfer of that token. Fund hook-bearing escrows at exactly the agreed amount, because a surplus refund at Settle reuses the hook accounts with a different destination and some hooks reject that.

Step 6: Fund the USDC leg

The buyer sends exactly 998,500,000 base units of USDC into escrow B. Because USDC has no transfer hook or Token-2022 extension on Solana, this is the simplest leg: one TransferChecked against the legacy token program with decimals set to 6, as the fund-the-legs page shows for any leg.

import { findAssociatedTokenPda, getTransferCheckedInstruction } from "@solana-program/token";

const [userBAtaB] = await findAssociatedTokenPda({
  mint: mintB, owner: userB, tokenProgram: tokenProgramB,
});

const fundUsdcLeg = getTransferCheckedInstruction({
  source: userBAtaB,
  mint: mintB,           // USDC
  destination: escrowB,
  authority: userBSigner,
  amount: t.amountB,     // 998_500_000n
  decimals: 6,
});

await client.sendTransaction([fundUsdcLeg]);

Three USDC-specific cautions. First, never send SOL to the USDC escrow; the program cannot return it and it goes to whoever closes the account. Second, a funding transfer seen at confirmed can still be superseded, so treat the leg as funded only at finalized. Third, a custodian can fund from its own account, but any refund lands in user_b's own USDC account, not back with the custodian, which matters for omnibus reconciliation.

Step 7: Settle both legs with SettleDvp

The settlement authority repeats the Step 4 read, then builds one transaction. The settle-the-trade guide requires four recipient token accounts that the program does not create: the seller's USDC destination, the buyer's asset destination, and both parties' own accounts for surplus refunds. Anyone holding USDC can dust escrow B with a few base units, which forces a surplus refund, so create all four idempotently in the same transaction or the whole Settle fails.

const settleInstruction = getSettleDvpInstruction({
  settlementAuthority: authoritySigner,
  swapDvp, mintA, mintB,
  dvpAtaA: escrowA,
  dvpAtaB: escrowB,
  userADestinationAtaB,  // seller's USDC account (Tokenkeg ATA)
  userBDestinationAtaA,  // buyer's asset account
  userAAtaA,             // seller's asset account, for surplus
  userBAtaB,             // buyer's USDC account, for surplus
  tokenProgramA, tokenProgramB,
  memoProgram: MEMO_PROGRAM_ADDRESS,
  legAExtrasCount: assetHookAccounts.length,
});
// Append the asset leg's resolved hook accounts; the USDC leg adds none.
const settleWithHooks = {
  ...settleInstruction,
  accounts: [...settleInstruction.accounts, ...assetHookAccounts],
};

await client.sendTransaction([...createRecipientAccounts, settleWithHooks]);

The legAExtrasCount field tells the program how many appended accounts belong to the asset leg's hook; the rest go to the cash leg. With USDC as the cash leg there is nothing after the asset leg's group, so legAExtrasCount equals the full number of appended accounts. The program strips the signer flag from every forwarded hook account, so a hostile hook cannot borrow the settlement authority's signature.

🔍
Legacy SPL detail that applies to USDC: a legacy SPL token account's owner can be reassigned with SetAuthority without changing its address. The program therefore re-reads every recipient account's owner and mint before paying out and fails with RecipientAtaMismatch if they no longer match. Token-2022 ATAs carry ImmutableOwner, so this check mostly bites on the USDC side. If Settle fails with RecipientAtaMismatch, check that the seller's USDC destination still belongs to the expected wallet.

Step 8: Wait for finalized and record the result

sendTransaction returns at confirmed by default. The docs say to treat the trade as settled only when SettleDvp reaches finalized, and add that whether that also marks legal settlement finality depends on the parties' agreements and the regimes that apply. Poll getSignatureStatuses until confirmationStatus is finalized; if it never gets there, read the record: a closed record means it settled, an open one is safe to settle again.

The program emits no events and closes the record at settlement, so a reconciliation system must store the terms from its pre-settlement read or from the creating transaction, and match trades to orders through ref_string. Closed-account rent goes to the settlement authority.

Step 9: Unwind a trade that does not settle

None of the unwind paths check expiry, so an expired but funded trade never strands USDC. The unwind-a-trade guide lists four instructions:

InstructionWho signsWhat happens to the USDC legTrade afterwards
ReclaimDvpuser_a or user_b (own leg)Buyer pulls its USDC back; can re-fund laterStill open
CancelDvpSettlement authorityWhatever escrow B holds goes back to user_bClosed
RejectDvpEither partySame as Cancel, signed by a partyClosed
RecoverDvpuser_a or user_bDrains a recreated escrow after close (late deposit)Already closed

Source: Solana DvP unwind-a-trade page. Recovery needs the original seed inputs (settlement authority, parties, mints, nonce), so store them with each order until the trade has closed and both escrows are confirmed empty.

Settle errors you will actually see

  • LegNotFunded (4): an escrow holds less than its agreed amount, often because Settle raced a funding transfer that never finalized.
  • DvpExpired (1) and SettlementTooEarly (3): the network-time window was missed.
  • MintAuthorityChanged (23): a leg's mint authority differs from the value captured at creation. That includes the USDC mint authority, so keep expiries short rather than leaving USDC trades open for months.
  • BlockedMintExtension (10): a leg carries TransferFee, InterestBearing, ScaledUiAmount, or NonTransferable.
  • RecipientAtaMismatch (21): a destination or refund account is missing or no longer belongs to the expected owner and mint.

Error codes are from the errors table in the DvP docs. In every Settle failure nothing has moved, and the parties can unwind subject to the mint authorities.

DvP program vs delegation vs a bespoke escrow

Solana documents a second way to do the same exchange: DvP using delegation, where each side approves a settlement agent as delegate and the agent moves both legs in one transaction. Stablecoin Insider's comparison for a USDC cash leg:

QuestionDvP program (escrow)Delegation patternBespoke escrow contract
Can the operator redirect USDC proceeds?No, destinations fixed at creationDelegate controls the approved amountDepends on the contract
Program dependencyUpgradeable program and its upgrade authorityNone beyond the token programsYour own program, your own audit
One-delegate-per-account limitDoes not applyApplies to every USDC accountDepends
Scaled UI Amount assetsRejectedSupportedDepends
Custodian integrationPlain TransferCheckedApprove plus agent signatureCustom instruction
Audit statusAudited by CantinaReference code, not for production as-isPer deal

Sources: DvP program overview (choosing between the program and delegation) and Solana tokenization DvP guide.

💡
Stablecoin Insider's take: for a USDC cash leg, the escrow program is the better default for anyone settling with a counterparty they do not fully control. The proceeds lock is what a treasury or compliance team will ask about first, and funding by plain transfer means a custodian needs no new integration. Delegation still wins for issuers whose asset uses Scaled UI Amount (including anything minted from Mosaic's tokenized-security template). The weak point of the program is not the code; it is operations: verify every record, create all four recipient accounts, and wait for finalized.

What the program does not do

  • No matching, order book, netting, or partial fills: one record is one bilateral trade at exact amounts.
  • No off-ledger cash leg: a fiat leg on bank rails settles separately and is reconciled.
  • No KYC or allowlist of its own: the asset mint's controls enforce holder eligibility.
  • No automatic settlement: the settlement authority has to sign.
  • No removal of issuer risk: USDC's freeze authority and an asset's pause, freeze, or permanent delegate can still act on escrowed tokens.

For market context on how much USDC is available to serve as a cash leg, see Stablecoin Insider's Q3 2026 stablecoin report. If you need to move USDC between chains before a trade, compare routes in the best crypto cross-chain bridges in 2026 and the best cross-chain aggregators for 2026. For consumer-side USDC payments on Solana, see Stablecoin Insider's Solana Pay USDC guide.

Conclusion

Solana DvP program USDC settlement comes down to nine steps: agree terms, derive addresses with a random nonce and the right token program for USDC, CreateDvp, verify the record, fund both legs with TransferChecked, settle with all four recipient accounts in one transaction, wait for finalized, and keep the seed inputs until the escrows are empty. The program removes principal risk between the two parties. It does not remove issuer risk on either token or the need for a settlement authority that shows up to sign.

For why the Solana Foundation published the standard and why its payment leg is a stablecoin, return to Stablecoin Insider's report on the Solana DvP atomic settlement standard.


New to the standard itself? Start with Stablecoin Insider's news report on the Solana DvP release and the J.P. Morgan advisory role.

Read the Solana DvP standard report
Settle the trade | Solana DvP program docs
Official step page for SettleDvp: recipient accounts, transfer hooks, finalized commitment, and settle errors.

FAQ

What is the Solana DvP Program?

An open-source, audited Solana Foundation program for delivery versus payment. Two parties fund escrows with ordinary token transfers and a named settlement authority settles both legs in one transaction, so either both move or neither does. Its program ID is dvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZq on mainnet-beta and devnet.

Does the Solana DvP Program support USDC?

Yes. A trade can mix a legacy SPL Token leg with a Token-2022 leg, and USDC on Solana is a legacy SPL Token mint with 6 decimals and no blocked extensions. Set the cash leg's token program to Tokenkeg rather than Token-2022.

Who can settle a DvP trade?

Only the settlement authority named when the trade was created. It cannot be either party, it cannot redirect proceeds, and it receives the closed-account rent when it settles or cancels.

What happens to my USDC if the trade expires?

Settle is rejected after expiry, but ReclaimDvp, CancelDvp, and RejectDvp still work, so the buyer can always pull its USDC back. A deposit that lands after the trade closes is recoverable with RecoverDvp.

Why must I verify the trade record before funding?

CreateDvp is permissionless and only the payer signs. Anyone can create a record naming you with different amounts or settlement destinations. Read it with verifySwapDvp and compare every field with the agreed terms before sending USDC.

When is a Solana DvP trade final?

The docs say to treat it as settled when the SettleDvp transaction reaches finalized commitment. Whether that also counts as legal settlement finality depends on the parties' agreements and applicable regimes.

Does the program charge a fee?

No protocol fee. Costs are Solana transaction fees plus rent for the record, the permanent nonce tombstone, and two escrows, with closed-account rent paid to whoever closes the trade.


This content is provided for informational and educational purposes only and does not constitute financial, investment, legal, or tax advice; it is not financial advice, and no material herein should be interpreted as a recommendation, endorsement, or solicitation to buy or sell any asset or token.

Latest