O programa DvP é o programa padrão de entrega contra pagamento na Solana,
mantido pela Solana Foundation e auditado pela Cantina. Duas partes combinam
uma operação, cada uma envia sua perna para uma conta de escrow com uma
transferência de token comum, e uma autoridade de liquidação liquida as duas
pernas em uma única transação, de modo que ou ambas se movem ou nenhuma se move.
A autoridade de liquidação é um terceiro endereço indicado na operação, como a
plataforma ou o agente que a intermediou. Nenhuma das partes escreve ou implanta
um programa, e uma carteira ou custodiante capaz de enviar uma transferência de
token padrão (TransferChecked) pode financiar uma perna sem nenhuma integração
específica com o DvP. Até a liquidação, qualquer uma das partes pode retirar a
sua própria perna ou desfazer a operação.
Leia o registro antes de agir com base nele
A criação de um registro de operação é sem permissão, portanto um registro não é prova de acordo. Cada parte lê os termos armazenados antes de financiar, e a autoridade de liquidação antes de liquidar (veja verificação).
Como funciona
Cada operação recebe um registro pertencente ao programa e duas contas de
escrow, uma por perna. A parte da perna de ativo (user_a) e a parte da perna
de dinheiro (user_b) financiam cada uma sua perna transferindo tokens para a
conta de escrow dessa perna. A autoridade de liquidação é o único signatário que
pode liquidar. A liquidação transfere o valor acordado de cada perna para a
outra parte, devolve qualquer excedente à parte dona da perna e encerra o
registro e ambos os escrows. A atomicidade vem das transações da
Solana: as duas transferências estão em uma única transação, então ou ambas
entram em vigor ou nenhuma entra.
Todo reembolso, resgate e devolução de excedente vai para a associated token
account da própria parte: a conta de user_a para mint_a, a de user_b para
mint_b. Um custodiante ou sistema de tesouraria pode financiar uma perna a
partir de sua própria conta, mas tudo o que for devolvido cai na conta da parte,
não de volta ao custodiante. Cada operação também deixa uma conta marcadora
permanente (veja Estado).
Guias
As páginas passo a passo conduzem uma operação da criação à liquidação, com TypeScript e Rust para cada instrução.
Criar uma operação
Instale os clientes e registre os termos da operação com CreateDvp
Financiar as pernas
Verifique os termos armazenados e depois financie cada perna com uma transferência simples
Liquidar a operação
Liquide as duas pernas em uma única transação como autoridade de liquidação
Desfazer uma operação
Recupere uma perna, cancele ou rejeite a operação e recupere depósitos tardios
Uma demonstração em devnet executa uma operação de ponta a ponta: ela cria a operação, financia os dois escrows com transferências de token padrão e liquida as duas pernas em uma única transação.
Como escolher entre o programa e a delegação
Entrega contra Pagamento (DvP) usando delegação liquida a mesma troca sem um programa: cada lado delega sua posição a um agente de liquidação, que move as duas pernas em uma única transação.
- O programa não exige delegação, então o limite de um delegado por token account não se aplica, e a autoridade de liquidação não pode redirecionar os recursos. Cada operação depende de um programa atualizável e de sua upgrade authority.
- O padrão de delegação não tem dependência de programa e funciona com mints
que usam Scaled UI Amount. O programa rejeita esses mints, o que exclui
qualquer mint criado a partir do modelo
tokenized-securitydo Mosaic (veja restrições de configuração de mint).
Papéis
O programa é simétrico. Em sua interface, as duas partes são user_a, que
entrega mint_a, e user_b, que entrega mint_b. O README e os clientes
gerados chamam mint_a de perna de ativo e mint_b de perna de dinheiro, e
estas páginas seguem essa convenção, mas nada no programa exige que a perna de
dinheiro seja um instrumento financeiro em dinheiro. Dois ativos, ou duas
stablecoins, são liquidados da mesma forma.
| Papel | Quem | Assina | Observações |
|---|---|---|---|
| Parte da perna de ativo | user_a, entrega mint_a | Sua própria transferência de financiamento; Reclaim, Reject, Recover | Uma carteira keypair ou uma carteira inteligente baseada em PDA, como um cofre multisig. Uma conta multisig do SPL Token ou uma program account não pode ser uma parte: Create falha com PartyNotSignerCapable |
| Parte da perna de dinheiro | user_b, entrega mint_b | Sua própria transferência de financiamento; Reclaim, Reject, Recover | Mesmo requisito |
| Autoridade de liquidação | Um terceiro endereço indicado na criação | Settle, Cancel | O único signatário que pode liquidar. Não pode redirecionar os recursos, que são fixados na criação. Recebe o rent das contas encerradas quando liquida ou cancela |
| Pagador de rent | Quem assinar CreateDvp | Create | Financia o rent (o depósito em SOL que mantém uma conta aberta) do registro, do tombstone do nonce e dos dois escrows. Não é registrado como destinatário de rent: o rent das contas encerradas vai para quem encerrar a operação |
ID do programa
dvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZq
| Cluster | Programa | Upgrade authority | Último slot implantado |
|---|---|---|---|
| mainnet-beta | dvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZq | DXtFpbPjcn2hxPnw79x1Pfoj35vXh5AsWBkS37YnXMVv | 452.058.374 |
| devnet | dvp34bdbcEm4f4FCUjGV4mDAkDshaQR4LkK8fdcsyZq | 5EX1zFeJWj4rGYZv432WnYw8CbaSUeTdxPx4r573UgYU | 479.376.863 |
O programa é atualizável nos dois clusters. Os valores acima são o que
solana program show informou em 2 de outubro de 2026; execute o mesmo comando
para obter os valores atuais. O build da devnet é mais antigo que o commit com
base no qual as páginas passo a passo foram escritas (veja
Instalação), com o mesmo conjunto de instruções e erros.
Ciclo de vida
Uma operação fica aberta desde CreateDvp até que uma das instruções Settle,
Cancel ou Reject a encerre. O financiamento não tem instrução própria: uma
perna é considerada financiada quando sua conta de escrow contém pelo menos o
valor acordado. Reclaim funciona por perna e mantém a operação aberta, de modo
que um problema em uma perna não obriga a outra parte a cancelar toda a
operação.
Estado
Cada operação tem uma conta SwapDvp de 458 bytes, pertencente ao programa. Seu
endereço é derivado da autoridade de liquidação, das duas partes, dos dois
mints e de um nonce (as seeds
["dvp", settlement_authority, user_a, user_b, mint_a, mint_b, nonce]). Os
valores e os horários são armazenados dentro da conta e não afetam o endereço,
razão pela qual precisam ser lidos em vez de presumidos. Uma pequena conta
marcadora, o tombstone do nonce em ["nonce", swap_dvp], é criada junto com a
operação e nunca é encerrada, de modo que cada combinação de partes, mints,
autoridade e nonce só pode ser usada em uma única operação.
| Campo | Tipo | Significado |
|---|---|---|
bump | u8 | Bump do PDA |
user_a, user_b | Pubkey | As duas partes |
mint_a, mint_b | Pubkey | Os mints da perna de ativo e da perna de dinheiro |
settlement_authority | Pubkey | O único signatário que pode liquidar ou cancelar |
token_program_a, token_program_b | Pubkey | O token program dono de cada mint; uma operação pode misturar pernas SPL Token e Token-2022 |
amount_a, amount_b | u64 | O valor acordado de cada perna, na menor unidade do mint (unidades base) |
expiry_timestamp | i64 | Após esse horário da rede (o relógio onchain), Settle é rejeitada. Reclaim, Cancel e Reject continuam funcionando |
nonce | u64 | Diferencia operações que compartilham as demais seeds. Uso único |
ref_string | [u8; 64] | Uma referência opaca do cliente, preenchida com zeros; o programa nunca a lê |
user_a_settlement_destination | Pubkey | Recebe a perna de dinheiro em Settle; o padrão é user_a |
user_b_settlement_destination | Pubkey | Recebe a perna de ativo em Settle; o padrão é user_b |
mint_a_authority, mint_b_authority | Pubkey | As mint authorities capturadas na criação; Settle falha se qualquer uma delas tiver mudado |
earliest_settlement_timestamp | Option<i64> | Se definido, Settle é rejeitada antes desse horário da rede |
A lista de campos foi extraída do IDL do programa no commit indicado em Criar uma operação.
Instruções
| Instrução | Signatário | Efeito |
|---|---|---|
CreateDvp | Qualquer pagador | Cria o registro SwapDvp, seu tombstone do nonce e as duas contas de escrow. Não move tokens |
ReclaimDvp | user_a ou user_b | Devolve do escrow a perna do próprio signatário. A operação permanece aberta e a perna pode ser financiada novamente |
SettleDvp | Autoridade de liquidação | Transfere amount_a para o destino de user_b e amount_b para o destino de user_a, devolve qualquer excedente à parte da perna e encerra o registro e os dois escrows |
CancelDvp | Autoridade de liquidação | Reembolsa a user_a o que o escrow A contiver e a user_b o que o escrow B contiver, e encerra o registro e os dois escrows |
RejectDvp | user_a ou user_b | Mesmo efeito de Cancel, assinada por uma das partes |
RecoverDvp | user_a ou user_b | Após o encerramento da operação: esvazia um escrow recriado para a token account do próprio signatário e o encerra. Cobre um depósito que chegou depois de Settle, Cancel ou Reject |
Todo reembolso vai para a associated token account da própria parte referente ao mint daquela perna, independentemente de quem financiou o escrow.
As contas e os argumentos de cada instrução estão na página da respectiva etapa.
Erros
| Código | Erro | Quando |
|---|---|---|
| 0 | SignerNotParty | Reclaim, Reject ou Recover assinada por um endereço que não é user_a nem user_b |
| 1 | DvpExpired | Settle após expiry_timestamp |
| 2 | SettlementAuthorityMismatch | Settle ou Cancel assinada por um endereço diferente da autoridade de liquidação |
| 3 | SettlementTooEarly | Settle antes de earliest_settlement_timestamp |
| 4 | LegNotFunded | Settle enquanto um escrow contém menos que o valor acordado |
| 5 | ExpiryNotInFuture | Create com uma expiração igual ou anterior ao horário atual da rede |
| 6 | EarliestAfterExpiry | Create com um horário mínimo de liquidação posterior à expiração |
| 7 | SelfDvp | Create com user_a igual a user_b |
| 8 | SameMint | Create com mint_a igual a mint_b |
| 9 | ZeroAmount | Create com valor zero em qualquer uma das pernas |
| 10 | BlockedMintExtension | Create ou Settle com um mint que possui TransferFee, InterestBearing, ScaledUiAmount ou NonTransferable |
| 11 | SettlementAuthorityIsParty | Create com a autoridade de liquidação igual a uma das partes |
| 12 | SettlementAuthorityExecutable | Create com uma autoridade de liquidação executável |
| 13 | NonceAlreadyUsed | Create reutilizando um (seeds, nonce) que já possui um tombstone |
| 14 | ExpiryTooFarInFuture | Create com uma expiração superior a um ano no futuro |
| 15 | RefStringTooLong | Create com uma referência maior que 64 bytes |
| 16 | DvpStillOpen | Recover enquanto a operação ainda está aberta; use Reclaim |
| 17 | DvpNeverCreated | Recover com seeds que não têm tombstone |
| 18 | EscrowPreloadedWithLamports | Create quando um escrow não nativo mantém lamports acima do seu mínimo isento de rent |
| 19 | SettlementDestinationIsSwapDvp | Create com um destino de liquidação igual ao registo da operação |
| 20 | SwapDvpPreloadedWithLamports | Create quando o endereço do registo mantém lamports acima da sua reserva de rent |
| 21 | RecipientAtaMismatch | Uma token account de destinatário ou de reembolso deixou de pertencer à carteira e à mint esperadas |
| 22 | PartyNotSignerCapable | Create com uma parte que não é uma conta não executável pertencente ao sistema |
| 23 | MintAuthorityChanged | Settle quando a mint authority de uma perna difere do valor capturado na criação |
Eventos e indexação
O programa não emite eventos on-chain. Um indexador ou sistema de reconciliação lê
o estado da conta SwapDvp e os saldos de tokens dos dois escrows, e usa
ref_string para correlacionar uma operação com a respetiva ordem off-chain. Como o registo
é encerrado na liquidação, um sistema que precise dos termos posteriormente guarda-os
quando os lê, ou reconstrói-os a partir da transação que criou
a operação.
Tokens suportados
Uma operação pode combinar uma perna SPL Token com uma perna Token-2022; cada perna transporta o seu próprio token program. As pernas de Wrapped SOL são suportadas.
| Extensão Token-2022 na mint de uma perna | Comportamento do programa |
|---|---|
| TransferFee, InterestBearing, ScaledUiAmount, NonTransferable | Rejeitada em Create e Settle com BlockedMintExtension |
| PermanentDelegate, Pausable, DefaultAccountState, uma freeze authority, MintCloseAuthority | Aceite; cada authority pode atuar sobre os tokens em escrow |
| ConfidentialTransfer | Aceite; os montantes dessa perna são públicos |
| TransferHook | Suportado, até 32 contas adicionais por perna |
| MemoTransfer numa conta de destino ou de reembolso | Aceite; o programa emite o memo necessário antes da transferência |
Uma taxa de transferência deixaria o lado recetor aquém do montante acordado. Interest Bearing e Scaled UI Amount não alteram os montantes brutos, mas a sua taxa ou multiplicador pode mudar entre a criação e a liquidação de uma operação, o que altera a forma como os montantes acordados são apresentados. NonTransferable deixaria ambas as pernas bloqueadas.
As authorities aceites podem mover, congelar ou suspender os tokens em escrow durante toda a vida da operação (ver abaixo). O escrow não pode ser configurado para receber transferências confidenciais, pelo que os montantes em escrow e liquidados numa perna confidencial são públicos.
Para uma mint com hook, o cliente resolve as contas adicionais do hook e acrescenta-as a Settle, Cancel, Reject, Reclaim e Recover. O limite de 32 por perna inclui o programa do hook e a respetiva conta de validação. Se a hook authority aumentar a lista de contas do hook para além desse limite, todas as transferências nessa perna falham, incluindo Reclaim, Cancel, Reject e Recover, até que a authority reverta a alteração. O limite de contas por transação pode ser atingido antes do limite por perna quando ambas as pernas têm hooks.
Quando uma conta de destino ou de reembolso exige memos, o programa chama o programa Memo
antes da transferência, razão pela qual todas as instruções que movem tokens
para fora do escrow recebem uma conta memo_program.
Restrições de configuração da mint
Scaled UI Amount. O modelo tokenized-security no
Mosaic, o kit de ferramentas de
emissão da Solana Foundation, adiciona sempre Scaled UI Amount, pelo que o programa não aceita uma
mint criada a partir desse modelo como perna. Uma mint destinada a este programa pode
ser criada sem a extensão (ver
Token Extensions);
DvP com delegação liquida através de
autoridade delegada e não aplica esta verificação.
Mints congeladas por defeito. O escrow de cada operação é uma associated token account pertencente a um program-derived address novo para essa operação. Numa mint com Default Account State definido como congelado, o escrow é criado congelado, e uma transferência de financiamento para o mesmo falha até ser descongelado. Com Token ACL, isso significa que o emitente ou o seu agente de transferência admite o escrow da operação antes do financiamento: quer adicionando o endereço do registo da operação (o owner do escrow) à allowlist, para que qualquer pessoa possa descongelar o escrow quando a configuração Token ACL da mint ativa o descongelamento sem permissões, quer fazendo com que a freeze authority o descongele diretamente. Um escrow congelado também bloqueia Settle, Reclaim, Cancel, Reject e Recover nessa perna, pelo que a freeze authority, e numa mint com hook a hook authority, é uma parte de confiança durante toda a vida da operação. O caminho do escrow congelado é descrito aqui e não é exercitado nos snippets da devnet.
Limites
- Sem correspondência de ordens, descoberta de preços ou livro de ordens. As partes acordam os termos fora do ledger e uma delas regista-os.
- Sem compensação (netting) e apenas bilateral: um registo é uma operação entre duas partes.
- Sem execuções parciais. Settle move exatamente o montante acordado de cada perna e devolve qualquer excedente à parte dessa perna.
- Sem perna fora do ledger. Ambas as pernas são token accounts na Solana; uma perna em dinheiro nos canais existentes é liquidada separadamente e reconciliada.
- Sem verificação de elegibilidade, allowlist ou KYC. Os controlos da própria mint impõem a elegibilidade dos detentores.
- Sem liquidação automática. A settlement authority tem de assinar.
- Sem eventos e sem taxa de protocolo para além das taxas de transação e da rent.
O programa elimina o risco de principal entre as duas partes: nenhuma perna se move a menos que ambas se movam. Não elimina o risco de crédito do emitente nem o risco de resgate em qualquer um dos tokens, a capacidade de a freeze, pause ou permanent delegate authority de uma mint atuar sobre os tokens em escrow (ver tokens suportados), nem a dependência de a settlement authority estar disponível para assinar.
Verificação antes do financiamento e da liquidação
CreateDvp não requer permissões: apenas o payer assina, e as partes e a
settlement authority não. Por isso, qualquer pessoa pode criar um registo que nomeie quaisquer
partes e quaisquer termos. Antes de financiar, e novamente antes de liquidar, leia a
conta SwapDvp e confirme:
- A conta pertence ao programa DvP e tem exatamente 458 bytes. Uma conta que não pertença ao programa pode ser preenchida com bytes que se descodificam como um registo real.
user_a,user_b,mint_a,mint_besettlement_authoritycorrespondem à operação acordada.amount_a,amount_b,expiry_timestampeearliest_settlement_timestampcorrespondem aos termos acordados.- Ambos os destinos de liquidação são os endereços acordados. Um registo forjado pode redirecionar os fundos.
Financiar as pernas lista os leitores verificados dos clientes gerados e mostra a verificação em código.
Considere uma operação liquidada quando a transação Settle atinge
finalized. Trata-se de um
nível de commitment da rede; se também marca a finalidade da liquidação para efeitos legais
depende dos acordos das partes e dos regimes aplicáveis.
Versionamento
A conta SwapDvp não tem campo de versão. O seu layout é fixo em 458 bytes
(SwapDvp::LEN), e os leitores verificados rejeitam uma conta de qualquer outro tamanho.
O IDL tem a sua própria versão, 0.1.0 a 2 de outubro de 2026.
O programa é atualizável em ambos os clusters (ver Program ID). Um registo de operação existe apenas até ser liquidado, cancelado ou rejeitado, pelo que consulte o repositório para saber como uma atualização trata as operações em aberto, e regenere os clientes a partir do IDL da build implementada.
Repositório e auditoria
O código-fonte, o IDL, os clientes gerados e os testes encontram-se em
solana-foundation/dvp sob a
licença MIT. O programa é um programa Pinocchio no_std; os clientes são
gerados a partir do IDL com Codama. Os relatórios de vulnerabilidades são feitos através da
ligação de security advisory do repositório, conforme o respetivo SECURITY.md.
O programa foi auditado pela Cantina.
Relacionado
- Delivery vs Payment (DvP) com delegação: o padrão de delegação sem um programa
- Token ACL: como uma mint com acesso restrito interage com as contas de escrow
Is this page helpful?