Il programma DvP è il programma standard per la consegna contro pagamento su Solana,
mantenuto dalla Solana Foundation e sottoposto ad audit da Cantina. Due parti concordano
un'operazione, ciascuna invia la propria gamba a un account escrow con un normale
trasferimento di token, e un'autorità di regolamento regola entrambe le gambe in un'unica transazione, così
o si muovono entrambe o nessuna delle due. L'autorità di regolamento è un terzo indirizzo
indicato nell'operazione, come la sede di negoziazione o l'agente che l'ha organizzata. Nessuna delle parti
scrive o distribuisce un programma, e un wallet o un custode in grado di inviare un trasferimento di
token standard (TransferChecked) può finanziare una gamba senza alcuna integrazione specifica per DvP. Fino al regolamento, ciascuna parte può riprendersi la propria gamba o annullare
l'operazione.
Leggi il record prima di agire
La creazione di un record di operazione è permissionless, quindi un record non è una prova di accordo. Ogni parte legge i termini memorizzati prima di finanziare, e l'autorità di regolamento prima di regolare (vedi verifica).
Come funziona
Ogni operazione ottiene un record di proprietà del programma e due account escrow, uno per gamba. La
parte della gamba di asset (user_a) e la parte della gamba di cassa (user_b) finanziano ciascuna una gamba
trasferendo token nell'account escrow di quella gamba. L'autorità di
regolamento è l'unico firmatario che può regolare. Il regolamento trasferisce l'importo
concordato di ciascuna gamba all'altra parte, restituisce l'eventuale eccedenza alla parte che possiede
la gamba e chiude il record e entrambi gli escrow. L'atomicità deriva dalle
transazioni di Solana: entrambi i trasferimenti sono in un'unica transazione,
quindi o hanno effetto entrambi o nessuno dei due.
Ogni rimborso, recupero e restituzione di eccedenza va all'associated
token account della parte stessa: quello di user_a per mint_a, quello di user_b per mint_b. Un
custode o un sistema di tesoreria può finanziare una gamba dal proprio account, ma tutto ciò che
viene restituito finisce nell'account della parte, non torna al custode. Ogni operazione
lascia inoltre un account marcatore permanente (vedi Stato).
Guide
Le pagine dei passaggi seguono un'operazione dalla creazione al regolamento, con TypeScript e Rust per ogni istruzione.
Crea un'operazione
Installa i client e registra i termini dell'operazione con CreateDvp
Finanzia le gambe
Verifica i termini memorizzati, poi finanzia ogni gamba con un semplice trasferimento
Regola l'operazione
Regola entrambe le gambe in un'unica transazione come autorità di regolamento
Annulla un'operazione
Riprendi una gamba, annulla o rifiuta l'operazione e recupera i depositi tardivi
Una demo su devnet esegue un'operazione dall'inizio alla fine: crea l'operazione, finanzia entrambi gli escrow con trasferimenti di token standard e regola entrambe le gambe in un'unica transazione.
Scegliere tra il programma e la delega
Delivery vs Payment (DvP) con delega regola lo stesso scambio senza un programma: ciascuna parte delega la propria posizione a un agente di regolamento, che muove entrambe le gambe in un'unica transazione.
- Il programma non richiede delega, quindi il limite di un delegato per token account non si applica, e l'autorità di regolamento non può dirottare i proventi. Ogni operazione dipende da un programma aggiornabile e dalla sua upgrade authority.
- Il pattern di delega non ha dipendenze da programmi e funziona con i mint che
usano Scaled UI Amount. Il programma rifiuta quei mint, il che esclude qualsiasi
mint creato dal template
tokenized-securitydi Mosaic (vedi vincoli di configurazione del mint).
Ruoli
Il programma è simmetrico. Nella sua interfaccia le due parti sono user_a, che
consegna mint_a, e user_b, che consegna mint_b. Il README e i client
generati chiamano mint_a la gamba di asset e mint_b la gamba di cassa, e
queste pagine seguono tale convenzione, ma nulla nel programma richiede che la gamba di cassa
sia uno strumento di cassa. Due asset, o due stablecoin, si regolano allo stesso
modo.
| Ruolo | Chi | Firma | Note |
|---|---|---|---|
| Parte della gamba di asset | user_a, consegna mint_a | Il proprio trasferimento di finanziamento; Reclaim, Reject, Recover | Un wallet keypair, o uno smart wallet basato su PDA come un vault multisig. Un account multisig SPL Token o un program account non può essere una parte: Create fallisce con PartyNotSignerCapable |
| Parte della gamba di cassa | user_b, consegna mint_b | Il proprio trasferimento di finanziamento; Reclaim, Reject, Recover | Stesso requisito |
| Autorità di regolamento | Un terzo indirizzo indicato alla creazione | Settle, Cancel | L'unico firmatario che può regolare. Non può dirottare i proventi, che sono fissati alla creazione. Riceve il rent degli account chiusi quando regola o annulla |
| Pagatore del rent | Chiunque firmi CreateDvp | Create | Finanzia il rent (il deposito in SOL che mantiene aperto un account) per il record, il tombstone del nonce e entrambi gli escrow. Non è registrato come destinatario del rent: il rent degli account chiusi va a chi chiude l'operazione |
ID del programma
dvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZq
| Cluster | Programma | Autorità di aggiornamento | Ultimo slot di deploy |
|---|---|---|---|
| mainnet-beta | dvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZq | DXtFpbPjcn2hxPnw79x1Pfoj35vXh5AsWBkS37YnXMVv | 452.058.374 |
| devnet | dvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZq | 5EX1zFeJWj4rGYZv432WnYw8CbaSUeTdxPx4r573UgYU | 479.376.863 |
Il programma è aggiornabile su entrambi i cluster. I valori sopra sono quelli riportati da
solana program show il 2 ottobre 2026; esegui lo stesso comando per i
valori attuali. La build su devnet è più vecchia del commit su cui sono scritte le pagine dei passaggi
(vedi Installazione),
con lo stesso insieme di istruzioni ed errori.
Ciclo di vita
Un'operazione è aperta da CreateDvp finché una tra Settle, Cancel o Reject non la chiude. Il finanziamento non ha un'istruzione propria: una gamba è finanziata quando il suo account escrow contiene almeno l'importo concordato. Reclaim agisce su una singola gamba e lascia
l'operazione aperta, quindi un problema con una gamba non costringe l'altra parte ad annullare
l'intera operazione.
Stato
Ogni operazione ha un account SwapDvp di 458 byte, di proprietà del programma. Il suo
indirizzo è derivato dall'autorità di regolamento, dalle due parti, dai due
mint e da un nonce (i seed
["dvp", settlement_authority, user_a, user_b, mint_a, mint_b, nonce]). Gli
importi e i tempi sono memorizzati all'interno dell'account e non influiscono sull'indirizzo,
motivo per cui devono essere letti anziché dedotti. Un piccolo account marcatore,
il tombstone del nonce in ["nonce", swap_dvp], viene creato con l'operazione e
non viene mai chiuso, quindi ogni combinazione di parti, mint, autorità e nonce può essere
usata per una sola operazione.
| Campo | Tipo | Significato |
|---|---|---|
bump | u8 | Bump della PDA |
user_a, user_b | Pubkey | Le due parti |
mint_a, mint_b | Pubkey | I mint della gamba di asset e della gamba di cassa |
settlement_authority | Pubkey | L'unico firmatario che può regolare o annullare |
token_program_a, token_program_b | Pubkey | Il token program proprietario di ciascun mint; un'operazione può combinare gambe SPL Token e Token-2022 |
amount_a, amount_b | u64 | L'importo concordato di ciascuna gamba, nell'unità più piccola del mint (unità di base) |
expiry_timestamp | i64 | Dopo questo orario di rete (il clock onchain), Settle viene rifiutata. Reclaim, Cancel e Reject continuano a funzionare |
nonce | u64 | Distingue le operazioni che condividono gli altri seed. Monouso |
ref_string | [u8; 64] | Un riferimento opaco del client, riempito con zeri; il programma non lo legge mai |
user_a_settlement_destination | Pubkey | Riceve la gamba di cassa al momento di Settle; per impostazione predefinita è user_a |
user_b_settlement_destination | Pubkey | Riceve la gamba di asset al momento di Settle; per impostazione predefinita è user_b |
mint_a_authority, mint_b_authority | Pubkey | Le mint authority acquisite alla creazione; Settle fallisce se una delle due è cambiata |
earliest_settlement_timestamp | Option<i64> | Se impostato, Settle viene rifiutata prima di questo orario di rete |
L'elenco dei campi è tratto dall'IDL del programma al commit indicato in Crea un'operazione.
Istruzioni
| Istruzione | Firmatario | Effetto |
|---|---|---|
CreateDvp | Qualsiasi pagatore | Crea il record SwapDvp, il relativo tombstone del nonce e entrambi gli account escrow. Non sposta alcun token |
ReclaimDvp | user_a o user_b | Restituisce dall'escrow la gamba del firmatario. L'operazione resta aperta e la gamba può essere finanziata di nuovo |
SettleDvp | Autorità di regolamento | Trasferisce amount_a alla destinazione di user_b e amount_b alla destinazione di user_a, restituisce l'eventuale eccedenza alla parte della gamba, chiude il record e entrambi gli escrow |
CancelDvp | Autorità di regolamento | Rimborsa a user_a quanto contiene l'escrow A e a user_b quanto contiene l'escrow B, chiude il record e entrambi gli escrow |
RejectDvp | user_a o user_b | Stesso effetto di Cancel, firmata da una parte |
RecoverDvp | user_a o user_b | Dopo la chiusura dell'operazione: svuota un escrow ricreato verso il token account del firmatario e lo chiude. Copre un deposito arrivato dopo Settle, Cancel o Reject |
Ogni rimborso va all'associated token account della parte per il mint di quella gamba, indipendentemente da chi abbia finanziato l'escrow.
Gli account e gli argomenti di ciascuna istruzione si trovano nella pagina del relativo passaggio.
Errori
| Codice | Errore | Quando |
|---|---|---|
| 0 | SignerNotParty | Firma di Reclaim, Reject o Recover da parte di un indirizzo che non è user_a o user_b |
| 1 | DvpExpired | Settle dopo expiry_timestamp |
| 2 | SettlementAuthorityMismatch | Firma di Settle o Cancel da parte di un indirizzo diverso dall'autorità di regolamento |
| 3 | SettlementTooEarly | Settle prima di earliest_settlement_timestamp |
| 4 | LegNotFunded | Settle mentre un escrow contiene meno dell'importo concordato |
| 5 | ExpiryNotInFuture | Create con una scadenza pari o precedente all'orario di rete corrente |
| 6 | EarliestAfterExpiry | Create con un orario di regolamento più anticipato successivo alla scadenza |
| 7 | SelfDvp | Create con user_a uguale a user_b |
| 8 | SameMint | Create con mint_a uguale a mint_b |
| 9 | ZeroAmount | Create con un importo pari a zero su una delle due gambe |
| 10 | BlockedMintExtension | Create o Settle con un mint che usa TransferFee, InterestBearing, ScaledUiAmount o NonTransferable |
| 11 | SettlementAuthorityIsParty | Create con l'autorità di regolamento uguale a una delle parti |
| 12 | SettlementAuthorityExecutable | Create con un'autorità di regolamento eseguibile |
| 13 | NonceAlreadyUsed | Create che riutilizza una (seeds, nonce) che ha già un tombstone |
| 14 | ExpiryTooFarInFuture | Create con una scadenza oltre un anno nel futuro |
| 15 | RefStringTooLong | Create con un riferimento più lungo di 64 byte |
| 16 | DvpStillOpen | Recover mentre l'operazione è ancora aperta; usa Reclaim |
| 17 | DvpNeverCreated | Recover con seed privi di tombstone |
| 18 | EscrowPreloadedWithLamports | Create quando un escrow non nativo detiene lamports oltre il proprio minimo rent-exempt |
| 19 | SettlementDestinationIsSwapDvp | Create con una destinazione di regolamento uguale al record dello scambio |
| 20 | SwapDvpPreloadedWithLamports | Create quando l'indirizzo del record detiene lamports oltre la propria riserva di rent |
| 21 | RecipientAtaMismatch | Un token account del destinatario o di rimborso non appartiene più al wallet e al mint previsti |
| 22 | PartyNotSignerCapable | Create con una parte che non è un account di proprietà del sistema e non eseguibile |
| 23 | MintAuthorityChanged | Settle quando la mint authority di un leg differisce dal valore acquisito alla creazione |
Eventi e indicizzazione
Il programma non emette eventi on-chain. Un indexer o un sistema di riconciliazione legge
lo stato dell'account SwapDvp e i saldi di token dei due escrow, e usa
ref_string per correlare uno scambio al relativo ordine off-chain. Poiché il record
viene chiuso al regolamento, un sistema che ha bisogno dei termini a posteriori li
memorizza al momento della lettura, oppure li ricostruisce dalla transazione che ha creato
lo scambio.
Token supportati
Uno scambio può abbinare un leg SPL Token a un leg Token-2022; ciascun leg riporta il proprio token program. I leg con Wrapped SOL sono supportati.
| Estensione Token-2022 sul mint di un leg | Comportamento del programma |
|---|---|
| TransferFee, InterestBearing, ScaledUiAmount, NonTransferable | Rifiutata in Create e Settle con BlockedMintExtension |
| PermanentDelegate, Pausable, DefaultAccountState, una freeze authority, MintCloseAuthority | Accettate; ciascuna authority può agire sui token in escrow |
| ConfidentialTransfer | Accettata; gli importi su quel leg sono pubblici |
| TransferHook | Supportata, fino a 32 account aggiuntivi per leg |
| MemoTransfer su un account di destinazione o di rimborso | Accettata; il programma emette il memo richiesto prima del trasferimento |
Una commissione di trasferimento lascerebbe la parte ricevente al di sotto dell'importo concordato. Interest Bearing e Scaled UI Amount non modificano gli importi grezzi, ma il loro tasso o moltiplicatore può cambiare tra la creazione e il regolamento di uno scambio, e ciò modifica il modo in cui vengono visualizzati gli importi concordati. NonTransferable bloccherebbe entrambi i leg.
Le authority accettate possono spostare, congelare o bloccare i token in escrow per tutta la durata dello scambio (vedi sotto). L'escrow non può essere configurato per ricevere trasferimenti confidenziali, quindi gli importi in escrow e regolati su un leg confidenziale sono pubblici.
Per un mint con hook, il client risolve gli account aggiuntivi dell'hook e li accoda a Settle, Cancel, Reject, Reclaim e Recover. Il limite di 32 per leg conteggia il programma dell'hook e il relativo account di validazione. Se la hook authority amplia l'elenco di account dell'hook oltre tale limite, ogni trasferimento su quel leg fallisce, inclusi Reclaim, Cancel, Reject e Recover, finché la authority non annulla la modifica. Il limite di account della transazione può diventare vincolante prima del limite per leg quando entrambi i leg hanno hook.
Quando un account di destinazione o di rimborso richiede memo, il programma chiama il programma
Memo prima del trasferimento, motivo per cui ogni istruzione che sposta token
fuori dall'escrow richiede un account memo_program.
Vincoli di configurazione del mint
Scaled UI Amount. Il template tokenized-security in
Mosaic, il toolkit di emissione della Solana Foundation,
aggiunge sempre Scaled UI Amount, quindi il programma non accetta come leg un mint
creato da quel template. Un mint destinato a questo programma può
essere creato senza l'estensione (vedi
Token Extensions);
DvP con delega regola tramite autorità
delegata e non applica questo controllo.
Mint congelati per impostazione predefinita. L'escrow di ogni scambio è un associated token account di proprietà di un program-derived address nuovo per quello scambio. Su un mint con Default Account State impostato su frozen, l'escrow viene creato congelato e un trasferimento di finanziamento verso di esso fallisce finché non viene sbloccato. Con Token ACL ciò significa che l'emittente o il suo transfer agent ammette l'escrow dello scambio prima del finanziamento: aggiungendo l'indirizzo del record dello scambio (il proprietario dell'escrow) alla allowlist, in modo che chiunque possa sbloccare l'escrow se la configurazione Token ACL del mint abilita lo sblocco senza permessi, oppure facendolo sbloccare direttamente alla freeze authority. Un escrow congelato blocca anche Settle, Reclaim, Cancel, Reject e Recover su quel leg, quindi la freeze authority, e su un mint con hook la hook authority, è una parte fidata per tutta la durata dello scambio. Il percorso dell'escrow congelato è descritto qui e non viene eseguito negli snippet su devnet.
Limiti
- Nessun matching, price discovery o order book. Le parti concordano i termini fuori dal ledger e una di esse li registra.
- Nessun netting, e solo bilaterale: un record è uno scambio tra due parti.
- Nessun riempimento parziale. Settle sposta esattamente l'importo concordato per ciascun leg e restituisce l'eventuale eccedenza alla parte del leg.
- Nessun leg off-ledger. Entrambi i leg sono token account su Solana; un leg in contanti su canali esistenti viene regolato separatamente e riconciliato.
- Nessun controllo di idoneità, allowlist o KYC. I controlli propri del mint impongono l'idoneità dei detentori.
- Nessun regolamento automatico. La settlement authority deve firmare.
- Nessun evento e nessuna commissione di protocollo oltre alle commissioni di transazione e al rent.
Il programma elimina il rischio di capitale tra le due parti: nessun leg si muove a meno che non si muovano entrambi. Non elimina il rischio di credito o di rimborso dell'emittente su nessuno dei due token, la possibilità per la freeze authority, la pause authority o la permanent delegate authority di un mint di agire sui token in escrow (vedi token supportati), né la dipendenza dalla disponibilità della settlement authority a firmare.
Verifica prima del finanziamento e del regolamento
CreateDvp è senza permessi: firma solo il payer, mentre le parti e la
settlement authority non firmano. Chiunque può quindi creare un record che indichi qualsiasi
parte e qualsiasi termine. Prima del finanziamento, e di nuovo prima del regolamento, leggi
l'account SwapDvp e conferma che:
- L'account sia di proprietà del programma DvP e sia esattamente di 458 byte. Un account non di proprietà del programma può essere riempito con byte che si decodificano come un record reale.
user_a,user_b,mint_a,mint_besettlement_authoritycorrispondano allo scambio concordato.amount_a,amount_b,expiry_timestampeearliest_settlement_timestampcorrispondano ai termini concordati.- Entrambe le destinazioni di regolamento siano gli indirizzi concordati. Un record contraffatto può reindirizzare i proventi.
Finanziare i leg elenca i lettori con controllo dei client generati e mostra il controllo nel codice.
Considera uno scambio regolato quando la transazione Settle raggiunge
finalized. Si tratta di un livello di commitment
della rete; se segni anche la definitività del regolamento a fini legali
dipende dagli accordi tra le parti e dai regimi applicabili.
Versioning
L'account SwapDvp non include un campo di versione. Il suo layout è fisso a 458 byte
(SwapDvp::LEN) e i lettori con controllo rifiutano un account di qualsiasi altra lunghezza.
L'IDL ha una propria versione, 0.1.0 al 2 ottobre 2026.
Il programma è aggiornabile su entrambi i cluster (vedi Program ID). Un record di scambio esiste solo finché non viene regolato, annullato o rifiutato, quindi consulta il repository per sapere come un aggiornamento tratta gli scambi aperti e rigenera i client dall'IDL della build distribuita.
Repository e audit
Sorgente, IDL, client generati e test si trovano in
solana-foundation/dvp con licenza
MIT. Il programma è un programma Pinocchio no_std; i client sono
generati dall'IDL con Codama. Le segnalazioni di vulnerabilità passano per il link degli
security advisory del repository, come indicato in SECURITY.md.
Il programma è stato sottoposto ad audit da Cantina.
Correlati
- Delivery vs Payment (DvP) con delega: il pattern di delega senza un programma
- Token ACL: come un mint con accesso controllato interagisce con gli account escrow
Is this page helpful?