Liquidar la operación

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:

CuentaRecibePropietario
userADestinationAtaBEl tramo de efectivo (amount_b)user_a_settlement_destination
userBDestinationAtaAEl tramo de activos (amount_a)user_b_settlement_destination
userAAtaACualquier excedente del tramo de activosuser_a
userBAtaBCualquier excedente del tramo de efectivouser_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 userA
const destinationB = t.userBSettlementDestination; // defaults to userB
const [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

Is this page helpful?

Tabla de Contenidos

Editar Página