SettleDvp lo firma la autoridad de liquidación indicada en el registro de la operación. En
una sola transacción, mueve amount_a del tramo de activos al destino de la parte
del tramo de efectivo y amount_b del tramo de efectivo al destino de la parte del tramo de activos,
devuelve cualquier excedente de cualquiera de los escrows a la parte de ese tramo y cierra el registro
y ambos escrows. Si alguna de esas transferencias no puede completarse, la instrucción
falla y no se mueve nada.
Esta página continúa desde crear una operación
y financiar los tramos; el cliente, los firmantes,
trade y los token accounts de las partes se mantienen.
Revalidar antes de firmar
La autoridad de liquidación firma la transacción que mueve ambos tramos, así que realiza la misma lectura que cada parte antes de firmar: propietario, tamaño, partes, mints, importes, vencimiento y ambos destinos (consulta financiar los tramos). El propio programa vuelve a comprobar en la liquidación que cada mint sigue perteneciendo al token program registrado en la creación, no incluye ninguna extensión bloqueada y tiene la misma autoridad de mint que cuando se creó la operación, de modo que un mint recreado con una extensión bloqueada, un token program distinto o una autoridad de mint diferente no puede liquidarse.
Crear las cuentas de destino
Settle escribe en cuatro token accounts que el programa no crea:
| Cuenta | Recibe | Propietario |
|---|---|---|
userADestinationAtaB | El tramo de efectivo (amount_b) | user_a_settlement_destination |
userBDestinationAtaA | El tramo de activos (amount_a) | user_b_settlement_destination |
userAAtaA | Cualquier excedente del tramo de activos | user_a |
userBAtaB | Cualquier excedente del tramo de efectivo | user_b |
Las cuentas de excedente solo se usan cuando un escrow contiene más que el importe acordado, pero cualquiera que posea el mint puede generar un excedente enviando unas pocas unidades base al escrow. Si la cuenta de reembolso no existe, toda la liquidación falla, así que crea las cuatro de forma idempotente en la misma transacción.
import {findAssociatedTokenPda,getCreateAssociatedTokenIdempotentInstruction,} from "@solana-program/token-2022";const destinationA = t.userASettlementDestination; // defaults to userAconst destinationB = t.userBSettlementDestination; // defaults to userBconst [userADestinationAtaB] = await findAssociatedTokenPda({mint: mintB,owner: destinationA,tokenProgram: tokenProgramB,});const [userBDestinationAtaA] = await findAssociatedTokenPda({mint: mintA,owner: destinationB,tokenProgram: tokenProgramA,});// userAAtaA and userBAtaB are from fund the legs.const createRecipientAccounts = [{ ata: userADestinationAtaB, owner: destinationA, mint: mintB, tokenProgram: tokenProgramB },{ ata: userBDestinationAtaA, owner: destinationB, mint: mintA, tokenProgram: tokenProgramA },{ ata: userAAtaA, owner: userA, mint: mintA, tokenProgram: tokenProgramA },{ ata: userBAtaB, owner: userB, mint: mintB, tokenProgram: tokenProgramB },].map(({ ata, owner, mint, tokenProgram }) =>getCreateAssociatedTokenIdempotentInstruction({payer: authoritySigner,ata,owner,mint,tokenProgram,}),);
Liquidar
legAExtrasCount indica al programa cuántas de las cuentas añadidas después de la
lista fija pertenecen al transfer hook del tramo de activos; el resto corresponden al del tramo de
efectivo. Para mints sin transfer hooks, pasa cero y no añadas nada. El memo
program account es requerido por la instrucción y solo se usa para destinos
que exigen un memo.
import { MEMO_PROGRAM_ADDRESS } from "@solana-program/memo";import { getSettleDvpInstruction } from "./dvp";const settleInstruction = getSettleDvpInstruction({settlementAuthority: authoritySigner,swapDvp,mintA,mintB,dvpAtaA: escrowA,dvpAtaB: escrowB,userADestinationAtaB,userBDestinationAtaA,userAAtaA,userBAtaB,tokenProgramA,tokenProgramB,memoProgram: MEMO_PROGRAM_ADDRESS,legAExtrasCount: 0,});const { context: settleContext } = await client.sendTransaction([...createRecipientAccounts,settleInstruction,]);const settleSignature = settleContext.signature;console.log("Submitted:", settleSignature);// sendTransaction returns at the client's commitment (confirmed by default).// Wait for finalized before recording the trade as settled.let finalized = false;for (let attempt = 0; attempt < 30 && !finalized; attempt++) {const {value: [status],} = await client.rpc.getSignatureStatuses([settleSignature]).send();if (status?.err) {throw new Error("Settle transaction failed");}finalized = status?.confirmationStatus === "finalized";if (!finalized) {await new Promise((resolve) => setTimeout(resolve, 2_000));}}if (!finalized) {// A closed record means it settled; an open one is safe to settle again.throw new Error("Settle not finalized after 60s; read the trade record");}console.log("Settled:", settleSignature);
Transfer hooks
El IDL generado enumera solo las cuentas fijas, por lo que los builders generados
no conocen las cuentas de los hooks. Resuelve los extra account metas de cada mint con hook
de la misma manera que lo harías para una transferencia directa de ese token y luego añádelos a la
instrucción: primero las cuentas del tramo de activos y después las del tramo de efectivo, con
legAExtrasCount establecido en el número del primer grupo. En TypeScript, expande
los metas resueltos en el array accounts de la instrucción; en Rust, usa
add_remaining_accounts en el builder. El límite es de 32 cuentas por tramo,
incluidos el programa del hook y la cuenta de validación; el límite de cuentas de la transacción
puede alcanzarse antes cuando ambos tramos tienen hooks. El programa elimina el indicador de firmante
de cada cuenta reenviada, por lo que un hook no puede obtener la firma de la
autoridad de liquidación a través de esta vía.
Registrar el resultado
Considera la operación liquidada cuando la transacción de liquidación alcance finalized. El
registro y ambos escrows dejan de existir tras la liquidación, por lo que un sistema que necesite
los términos más adelante los almacena a partir de su lectura previa a la liquidación o de la
transacción de creación. El rent de las cuentas cerradas ha pasado a la autoridad
de liquidación.
Errores de liquidación
LegNotFunded significa que un escrow contiene menos de su importe acordado; DvpExpired
y SettlementTooEarly significan que se perdió la ventana de tiempo; MintAuthorityChanged
y BlockedMintExtension significan que un mint cambió después de la creación;
RecipientAtaMismatch significa que un token account de destino o de reembolso falta o
ya no pertenece al propietario y al mint esperados, normalmente porque se omitió el paso
de creación de cuentas anterior. En todos los casos no se ha movido nada, y las
partes pueden deshacer la operación, sujetas a las autoridades del mint (consulta
deshacer una operación).
Próximos pasos
- Deshacer una operación: recuperar los tramos financiados cuando una operación no se liquida
Is this page helpful?