Delivery vs Payment (DvP) op Solana

Delivery vs Payment (DvP) is een methode voor de afwikkeling van effecten die ervoor zorgt dat de overdracht van effecten gelijktijdig plaatsvindt met de betaling. Dit elimineert tegenpartijrisico door te garanderen dat beide onderdelen van de transactie atomisch worden uitgevoerd — ofwel beide slagen, ofwel geen van beide.

Het Probleem

Wanneer u obligaties koopt (zoals commercial paper), moeten er traditioneel twee dingen gebeuren:

  1. U stuurt geld → Verkoper
  2. Verkoper stuurt obligaties → U

Als deze afzonderlijk plaatsvinden, is er een risico — wat als u betaalt maar de obligaties nooit ontvangt? Of andersom?

De Oplossing

(DvP) "Delivery vs Payment" betekent dat beide overdrachten op exact hetzelfde moment plaatsvinden, of geen van beide. Het is als een ruil waarbij beide partijen gelijktijdig uitwisselen.

┌─────────────────────────────────────────────────────┐
│ ONE ATOMIC TRANSACTION │
├─────────────────────────────────────────────────────┤
│ │
│ Investor ──── $95,000 USDC ────→ Issuer │
│ │
│ Issuer ─────── 100 Bonds ──────→ Investor │
│ │
│ ✅ Both happen together, or neither happens │
│ │
└─────────────────────────────────────────────────────┘

Deze handleiding demonstreert hoe u een complete DvP-workflow op Solana kunt implementeren met behulp van SPL Token 2022-extensies voor conforme obligatie-uitgifte en standaard USDC voor afwikkeling, zonder aangepaste Rust-programma's te schrijven.

Educatieve Referentie-implementatie

U kunt de broncode van deze implementatie gebruiken om een implementatie van DvP lokaal uit te proberen.

Deze handleiding biedt een referentie-implementatie uitsluitend voor verkenning en educatieve doeleinden. Gebruik deze code NIET rechtstreeks in productie zonder:

  • Uitgebreide beveiligingsaudits
  • Correcte sleutelbeheerssystemen
  • Beoordeling van regelgevende naleving
  • Juridisch advies
  • Uitgebreide tests en aanpassingen

Waarom Solana voor DvP?

De architectuur van Solana biedt aanzienlijke voordelen ten opzichte van traditionele effectenafwikkeling:

AspectTraditioneel (T+2)Solana
AfwikkelingslogicaClearinghousesAtomische transactiebundeling
Afwikkelingstijd2 dagen<1 seconde
Transactiekosten$50-500<$0,01
TegenpartijrisicoHoog (tussenpersonen)Nul (atomische uitvoering)
FinaliteitEinde van de dag~400ms

De atomische transactiebundeling van Solana elimineert tussenpersonen en biedt tegelijkertijd directe, goedkope en veilige afwikkelingen.

Architectuuroverzicht

Het DvP-systeem bestaat uit de volgende kerncomponenten:

  1. Obligatietoken (Commercial Paper): SPL Token 2022 met token extensions
  2. Afwikkelingsvaluta: Standaard USDC (bestaand SPL-token)
  3. Afwikkelingsagent: Orkestreert atomische swaps via gedelegeerde bevoegdheid
  4. Whitelistsysteem: Beheert welke adressen obligaties mogen bezitten

Belangrijke Ontwerpprincipes

  • Geen Aangepaste Programma's: Gebruikt alleen Token Extensions Program en standaard USDC
  • Atomische Afwikkeling: Eén transactie zorgt ervoor dat beide overdrachten slagen of samen mislukken
  • Standaard Bevroren Toestand: Obligaties vereisen expliciete whitelisting voor naleving van regelgeving
  • Gedelegeerde Bevoegdheid: Afwikkelingsagent coördineert zonder bewaring over te nemen
  • Netwerktoestandcommunicatie: Geen point-to-point API-connectiviteit vereist
┌─────────────────────────────────────────────────────┐
│ DvP Settlement Flow │
├─────────────────────────────────────────────────────┤
│ │
│ 1. Bond Creation (Token-2022 + Extensions) │
│ └─> Default State: FROZEN │
│ └─> Freeze Authority: Settlement Agent/Issuer │
│ └─> Token metadata: Bond information │
│ │
│ 2. Whitelist Participants │
│ └─> Whitelist issuer, mint bonds │
│ └─> Whitelist investor for trading │
│ │
│ 3. Delegate Authority to settlement agent │
│ ├─> Issuer delegates bonds │
│ └─> Investor delegates USDC │
│ │
│ 4. Atomic Settlement │
│ ├─> Transfer bonds: Issuer → Investor │
│ └─> Transfer USDC: Investor → Issuer │
│ (Both or neither - atomic) │
│ │
└─────────────────────────────────────────────────────┘

Een vertrouwde partij die de handel orkestreert. Beide partijen delegeren bevoegdheid aan de afwikkelingsagent, die vervolgens de atomische swap uitvoert.

┌─────────┐ delegates ┌──────────────────┐ delegates ┌──────────┐
│ Issuer │ ─────────────────→ │ Settlement Agent │ ←──────────────── │ Investor │
└─────────┘ (bonds) │ (trusted) │ (USDC) └──────────┘
│ │
│ executes atomic │
│ transaction │
└──────────────────┘

Obligatietoken met Token 2022 Extensions

SPL Token 2022 biedt krachtige extensies die conforme effecten-uitgifte mogelijk maken zonder aangepaste programma's:

Essentiële Extensies voor Obligaties

  1. Default Account State Extension: Stelt alle nieuwe token accounts standaard in op bevroren, wat expliciete whitelisting vereist
  2. Metadata-extensie: Slaat obligatiedetails on-chain op (ISIN, vervaldatum, couponrente, enz.)
  3. Permanente Gedelegeerde (optioneel): Maakt geautoriseerd herstel of terugvordering mogelijk indien vereist door regelgeving

Configuratie van Bevoegdheden

  • Mint-bevoegdheid: Uitgever (beheert aanmaken van aanbod)
  • Bevriezingsbevoegdheid: Afwikkelingsagent (beheert whitelist)
  • Updatebevoegdheid: Afwikkelingsagent (kan metadata bijwerken)

De standaard bevroren toestand is essentieel voor naleving van regelgeving. Het zorgt ervoor dat alleen expliciet gewhiteliste adressen de effecten kunnen ontvangen en bezitten, waarmee wordt voldaan aan KYC/AML-vereisten.

Volledige DvP-implementatie

Het DvP-systeem Opzetten

Maak eerst de kern DvP-engineklasse aan die alle bewerkingen afhandelt:

import {
Connection,
Keypair,
PublicKey,
Transaction,
SystemProgram,
sendAndConfirmTransaction,
LAMPORTS_PER_SOL
} from "@solana/web3.js";
import {
approve,
thawAccount,
freezeAccount,
getAccount,
getAssociatedTokenAddress,
getOrCreateAssociatedTokenAccount,
createTransferCheckedInstruction,
TOKEN_2022_PROGRAM_ID,
TOKEN_PROGRAM_ID,
ExtensionType,
getMintLen,
createInitializeMintInstruction,
createInitializeDefaultAccountStateInstruction,
createInitializeMetadataPointerInstruction,
AccountState,
LENGTH_SIZE,
TYPE_SIZE
} from "@solana/spl-token";
import {
pack,
createInitializeInstruction,
createUpdateFieldInstruction,
type TokenMetadata
} from "@solana/spl-token-metadata";
interface BondTokenConfig {
name: string;
symbol: string;
decimals: number;
maturityDate: Date;
couponRate: number;
isin?: string;
description?: string;
}
interface DvPParams {
bondMint: PublicKey;
usdcMint: PublicKey;
bondAmount: number;
usdcAmount: number;
issuer: PublicKey;
investor: PublicKey;
}
interface DvPResult {
signature: string;
bondAmount: number;
usdcAmount: number;
timestamp: Date;
bondsSent: boolean;
usdcReceived: boolean;
}
/**
* DvP Engine - Reference Implementation
*
* This implementation demonstrates Delivery vs Payment (DvP) on Solana using:
* - SPL Token 2022 with Default Account State extension for bonds
* - Standard USDC for settlement
* - Atomic transactions for settlement
* - Delegated authority pattern for settlement agent
*
* ⚠️ IMPORTANT: This is a reference implementation for educational purposes.
* Do NOT use in production without proper audits and security reviews.
*/
export class DvPEngine {
private connection: Connection;
private settlementAgent: Keypair;
constructor(connection: Connection, settlementAgent: Keypair) {
this.connection = connection;
this.settlementAgent = settlementAgent;
}
/**
* Creates a bond token using Token-2022 with Default Account State and Metadata extensions
* Bonds are frozen by default and require whitelisting
* Metadata is stored onchain using the TokenMetadata extension
*/
async createBondToken(
issuer: Keypair,
config: BondTokenConfig
): Promise<PublicKey> {
console.log("\n🏗️ Creating bond token with Token-2022 + Metadata...");
console.log(` Name: ${config.name}`);
console.log(` Symbol: ${config.symbol}`);
console.log(` Coupon Rate: ${config.couponRate}%`);
console.log(
` Maturity: ${config.maturityDate.toISOString().split("T")[0]}`
);
// Generate new keypair for the mint
const mintKeypair = Keypair.generate();
// Create the metadata object to get EXACT size
const metadata: TokenMetadata = {
mint: mintKeypair.publicKey,
name: config.name,
symbol: config.symbol,
uri: config.description || "",
additionalMetadata: [
["couponRate", config.couponRate.toString()],
["maturityDate", config.maturityDate.toISOString()],
["isin", config.isin || ""]
]
};
// Size of metadata using pack() - this gives us the EXACT size
const metadataLen = pack(metadata).length;
// Size of MetadataExtension: 2 bytes for type, 2 bytes for length
const metadataExtension = TYPE_SIZE + LENGTH_SIZE;
// Calculate space for mint with extensions (without metadata)
const extensions = [
ExtensionType.DefaultAccountState,
ExtensionType.MetadataPointer
];
const spaceWithoutMetadataExtension = getMintLen(extensions);
// Calculate rent for FULL space (mint + metadata + TLV overhead)
const lamports = await this.connection.getMinimumBalanceForRentExemption(
spaceWithoutMetadataExtension + metadataLen + metadataExtension
);
// Build transaction following the official docs pattern
const transaction = new Transaction().add(
// 1. Create account with just base space, but rent for full space
SystemProgram.createAccount({
fromPubkey: issuer.publicKey,
newAccountPubkey: mintKeypair.publicKey,
space: spaceWithoutMetadataExtension, // Just base space
lamports, // But rent for full space (includes metadata + TLV)
programId: TOKEN_2022_PROGRAM_ID
}),
// 2. Initialize metadata pointer (before mint!)
createInitializeMetadataPointerInstruction(
mintKeypair.publicKey,
issuer.publicKey, // authority
mintKeypair.publicKey, // metadata address (self)
TOKEN_2022_PROGRAM_ID
),
// 3. Initialize default account state (frozen)
createInitializeDefaultAccountStateInstruction(
mintKeypair.publicKey,
AccountState.Frozen,
TOKEN_2022_PROGRAM_ID
),
// 4. Initialize mint
createInitializeMintInstruction(
mintKeypair.publicKey,
config.decimals,
issuer.publicKey, // mint authority
this.settlementAgent.publicKey, // freeze authority
TOKEN_2022_PROGRAM_ID
),
// 5. Initialize metadata
createInitializeInstruction({
programId: TOKEN_2022_PROGRAM_ID,
mint: mintKeypair.publicKey,
metadata: mintKeypair.publicKey,
name: config.name,
symbol: config.symbol,
uri: config.description || "",
mintAuthority: issuer.publicKey,
updateAuthority: this.settlementAgent.publicKey
})
);
// 6. Add custom metadata fields
for (const [field, value] of metadata.additionalMetadata) {
if (value) {
transaction.add(
createUpdateFieldInstruction({
programId: TOKEN_2022_PROGRAM_ID,
metadata: mintKeypair.publicKey,
updateAuthority: this.settlementAgent.publicKey,
field: field,
value: value
})
);
}
}
// Send transaction
await sendAndConfirmTransaction(
this.connection,
transaction,
[issuer, mintKeypair, this.settlementAgent],
{ commitment: "confirmed" }
);
console.log(`✅ Bond token created: ${mintKeypair.publicKey.toBase58()}`);
console.log(` Mint Authority: ${issuer.publicKey.toBase58()}`);
console.log(
` Freeze Authority: ${this.settlementAgent.publicKey.toBase58()}`
);
console.log(
` Update Authority: ${this.settlementAgent.publicKey.toBase58()}`
);
console.log(` Default State: FROZEN (requires whitelisting)`);
console.log(` ✨ Metadata: ON-CHAIN`);
return mintKeypair.publicKey;
}
/**
* Whitelists a participant by creating their bond account and thawing it
*/
async whitelist(
bondMint: PublicKey,
participant: PublicKey,
payer: Keypair
): Promise<PublicKey> {
console.log(`\n🔓 Whitelisting participant: ${participant.toBase58()}`);
// Get or create token account (will be frozen by default if new)
const bondAccount = await getOrCreateAssociatedTokenAccount(
this.connection,
payer,
bondMint,
participant,
false,
"confirmed",
{ commitment: "confirmed" },
TOKEN_2022_PROGRAM_ID
);
console.log(` Account: ${bondAccount.address.toBase58()}`);
// Only thaw if the account is frozen
if (bondAccount.isFrozen) {
await thawAccount(
this.connection,
this.settlementAgent,
bondAccount.address,
bondMint,
this.settlementAgent,
[],
{ commitment: "confirmed" },
TOKEN_2022_PROGRAM_ID
);
console.log(`✅ Participant whitelisted and account thawed`);
} else {
console.log(
`✅ Participant already whitelisted (account was not frozen)`
);
}
return bondAccount.address;
}
/**
* Removes an investor from whitelist by freezing their account
*/
async removeFromWhitelist(
bondMint: PublicKey,
investorBondAccount: PublicKey
): Promise<void> {
console.log(
`\n🔒 Removing from whitelist: ${investorBondAccount.toBase58()}`
);
await freezeAccount(
this.connection,
this.settlementAgent,
investorBondAccount,
bondMint,
this.settlementAgent,
[],
{ commitment: "confirmed" },
TOKEN_2022_PROGRAM_ID
);
console.log(`✅ Account frozen and removed from whitelist`);
}
/**
* Delegates authority to settlement agent for a token account
*/
async delegateAuthority(
owner: Keypair,
tokenAccount: PublicKey,
amount: number,
decimals: number,
programId: PublicKey
): Promise<void> {
const amountWithDecimals = amount * Math.pow(10, decimals);
console.log(`\n🤝 Delegating authority...`);
console.log(` Account: ${tokenAccount.toBase58()}`);
console.log(` Amount: ${amount}`);
console.log(` Delegate: ${this.settlementAgent.publicKey.toBase58()}`);
await approve(
this.connection,
owner,
tokenAccount,
this.settlementAgent.publicKey,
owner.publicKey,
amountWithDecimals,
[],
{ commitment: "confirmed" },
programId
);
console.log(`✅ Authority delegated`);
}
/**
* Executes atomic DvP settlement
* Both bond and USDC transfers happen in a single transaction
*/
async executeDvP(params: DvPParams): Promise<DvPResult> {
console.log(`\n⚡ Executing atomic DvP settlement...`);
console.log(` Bonds: ${params.bondAmount}`);
console.log(` USDC: ${params.usdcAmount}`);
console.log(` Issuer: ${params.issuer.toBase58()}`);
console.log(` Investor: ${params.investor.toBase58()}`);
// Get all associated token account addresses (deterministically)
const issuerBondAccount = await getAssociatedTokenAddress(
params.bondMint,
params.issuer,
false, // allowOwnerOffCurve
TOKEN_2022_PROGRAM_ID
);
const investorBondAccount = await getAssociatedTokenAddress(
params.bondMint,
params.investor,
false,
TOKEN_2022_PROGRAM_ID
);
const investorUSDCAccount = await getAssociatedTokenAddress(
params.usdcMint,
params.investor,
false,
TOKEN_PROGRAM_ID
);
const issuerUSDCAccount = await getAssociatedTokenAddress(
params.usdcMint,
params.issuer,
false,
TOKEN_PROGRAM_ID
);
// Build atomic transaction
const transaction = new Transaction();
// Add bond transfer instruction (Issuer → Investor)
transaction.add(
this.createTransferCheckedIx(
issuerBondAccount,
params.bondMint,
investorBondAccount,
params.bondAmount,
0, // bonds have 0 decimals
TOKEN_2022_PROGRAM_ID
)
);
// Add USDC transfer instruction (Investor → Issuer)
transaction.add(
this.createTransferCheckedIx(
investorUSDCAccount,
params.usdcMint,
issuerUSDCAccount,
params.usdcAmount * 1e6, // USDC has 6 decimals
6,
TOKEN_PROGRAM_ID
)
);
// Send atomic transaction
console.log(`\n📡 Sending atomic transaction...`);
const signature = await sendAndConfirmTransaction(
this.connection,
transaction,
[this.settlementAgent],
{ commitment: "confirmed" }
);
console.log(`✅ DvP SETTLED ATOMICALLY`);
console.log(` Signature: ${signature}`);
console.log(` Bonds transferred: ${params.bondAmount}`);
console.log(` USDC transferred: ${params.usdcAmount}`);
return {
signature,
bondAmount: params.bondAmount,
usdcAmount: params.usdcAmount,
timestamp: new Date(),
bondsSent: true,
usdcReceived: true
};
}
/**
* Helper to create a transferChecked instruction using delegated authority
*/
private createTransferCheckedIx(
source: PublicKey,
mint: PublicKey,
destination: PublicKey,
amount: number,
decimals: number,
programId: PublicKey
) {
return createTransferCheckedInstruction(
source,
mint,
destination,
this.settlementAgent.publicKey, // Settlement agent acts via delegation
amount,
decimals,
[],
programId
);
}
/**
* Gets account information for inspection
*/
async getAccountInfo(tokenAccount: PublicKey, programId: PublicKey) {
const account = await getAccount(
this.connection,
tokenAccount,
"confirmed",
programId
);
return {
address: tokenAccount,
mint: account.mint,
owner: account.owner,
amount: account.amount,
isFrozen: account.isFrozen
};
}
/**
* Airdrops SOL for testing (devnet/testnet only)
*/
async airdropSol(publicKey: PublicKey, amount: number): Promise<void> {
console.log(`\n💰 Airdropping ${amount} SOL to ${publicKey.toBase58()}`);
const signature = await this.connection.requestAirdrop(
publicKey,
amount * LAMPORTS_PER_SOL
);
await this.connection.confirmTransaction(signature, "confirmed");
console.log(`✅ Airdrop complete`);
}
}

Volledig Gebruiksvoorbeeld

Hier is een volledig voorbeeld dat de gehele DvP-workflow demonstreert:

import { Connection, Keypair, clusterApiUrl, PublicKey } from "@solana/web3.js";
import {
getOrCreateAssociatedTokenAccount,
mintTo,
TOKEN_2022_PROGRAM_ID,
TOKEN_PROGRAM_ID
} from "@solana/spl-token";
// Standard USDC mint address on mainnet
const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
async function runDvPExample() {
// 1. Initialize connection and keypairs
const connection = new Connection(clusterApiUrl("devnet"));
const settlementAgent = Keypair.generate();
const issuer = Keypair.generate();
const investor = Keypair.generate();
console.log("🚀 Starting DvP Workflow\n");
// 2. Initialize DvP engine
const dvp = new DvPEngine(connection, settlementAgent);
// 3. Airdrop SOL for transaction fees (devnet only)
await dvp.airdropSol(settlementAgent.publicKey, 2);
await dvp.airdropSol(issuer.publicKey, 2);
await dvp.airdropSol(investor.publicKey, 2);
// 4. Create commercial paper (bond) token with metadata
const bondMint = await dvp.createBondToken(issuer, {
name: "ACME Commercial Paper Series A",
symbol: "ACME-CP-A",
decimals: 0, // Bonds are whole units
maturityDate: new Date("2026-12-31"),
couponRate: 4.5, // 4.5% annual coupon
isin: "US0000000001",
description: "https://acme.com/bonds/series-a"
});
// 5. Whitelist issuer and mint bonds
console.log("\n🏦 Whitelisting issuer for bond holding...");
const issuerBondAccount = await dvp.whitelist(
bondMint,
issuer.publicKey,
settlementAgent
);
console.log("\n💰 Minting 100 bonds to issuer...");
await mintTo(
connection,
issuer,
bondMint,
issuerBondAccount,
issuer,
100, // 100 bonds
[],
{ commitment: "confirmed" },
TOKEN_2022_PROGRAM_ID
);
// 6. Whitelist investor (KYC/AML approved)
await dvp.whitelist(bondMint, investor.publicKey, settlementAgent);
// 7. Setup USDC for investor
console.log("\n💵 Setting up USDC for investor...");
const investorUSDCAccount = await getOrCreateAssociatedTokenAccount(
connection,
investor,
USDC_MINT,
investor.publicKey,
false,
"confirmed",
{ commitment: "confirmed" },
TOKEN_PROGRAM_ID
);
// In production, investor would acquire USDC from exchange/market
// For this example, assume they have 95,000 USDC
// 8. Delegate authority to settlement agent
console.log("\n🔐 Delegating authority to settlement agent...");
// Issuer delegates bonds
await dvp.delegateAuthority(
issuer,
issuerBondAccount,
100, // 100 bonds
0, // 0 decimals
TOKEN_2022_PROGRAM_ID
);
// Investor delegates USDC
await dvp.delegateAuthority(
investor,
investorUSDCAccount.address,
95000, // $95,000
6, // USDC decimals
TOKEN_PROGRAM_ID
);
// 9. Execute atomic DvP settlement
console.log("\n⚡ Executing atomic DvP settlement...");
console.log(" Terms: 100 bonds @ $950 each = $95,000\n");
const result = await dvp.executeDvP({
bondMint,
usdcMint: USDC_MINT,
bondAmount: 100,
usdcAmount: 95000,
issuer: issuer.publicKey,
investor: investor.publicKey
});
console.log("\n✨ Settlement complete!");
console.log(` Transaction: ${result.signature}`);
console.log(` Timestamp: ${result.timestamp.toISOString()}`);
console.log(
` View on explorer: https://explorer.solana.com/tx/${result.signature}?cluster=devnet`
);
// 10. Verify balances
console.log("\n🔍 Verifying final balances...");
const issuerBondInfo = await dvp.getAccountInfo(
issuerBondAccount,
TOKEN_2022_PROGRAM_ID
);
console.log(` Issuer bonds: ${issuerBondInfo.amount}`);
const investorBondInfo = await dvp.getAccountInfo(
await getAssociatedTokenAddress(
bondMint,
investor.publicKey,
false,
TOKEN_2022_PROGRAM_ID
),
TOKEN_2022_PROGRAM_ID
);
console.log(` Investor bonds: ${investorBondInfo.amount}`);
console.log(` Investor frozen: ${investorBondInfo.isFrozen}`);
}
// Run the example
runDvPExample().catch(console.error);

Aanvullende Functies

Metadata Bijwerken

De afwikkelingsagent (met updatebevoegdheid) kan metadatavelden bijwerken:

import { createUpdateFieldInstruction } from "@solana/spl-token-metadata";
async function updateBondMetadata(
connection: Connection,
settlementAgent: Keypair,
bondMint: PublicKey,
field: string,
value: string
): Promise<void> {
const transaction = new Transaction().add(
createUpdateFieldInstruction({
programId: TOKEN_2022_PROGRAM_ID,
metadata: bondMint,
updateAuthority: settlementAgent.publicKey,
field: field,
value: value
})
);
await sendAndConfirmTransaction(connection, transaction, [settlementAgent]);
}

Verwijderen uit Whitelist

Verwijder de mogelijkheid van een investeerder om obligaties te bezitten door hun account te bevriezen met behulp van de ingebouwde removeFromWhitelist-methode:

await dvp.removeFromWhitelist(bondMint, investorBondAccount);

Bevriezen vs Verbranden

Het bevriezen van een account voorkomt overdrachten, maar behoudt het account en het saldo. Voor volledige verwijdering kunt u eerst obligaties terugboeken naar de uitgever en vervolgens het account bevriezen.

Meerpartijenafwikkeling

Voor complexere scenario's met meerdere partijen kunt u meerdere overdrachten bundelen in één atomische transactie:

import { sendAndConfirmTransaction } from "@solana/web3.js";
import { createTransferCheckedInstruction } from "@solana/spl-token";
interface TransferLeg {
from: PublicKey;
to: PublicKey;
mint: PublicKey;
amount: number;
decimals: number;
programId: PublicKey;
}
async function executeMultiPartyDvP(
connection: Connection,
settlementAgent: Keypair,
legs: TransferLeg[]
): Promise<string> {
const transaction = new Transaction();
// Add all transfer legs to single transaction
for (const leg of legs) {
const fromAccount = await getAssociatedTokenAddress(
leg.mint,
leg.from,
false,
leg.programId
);
const toAccount = await getAssociatedTokenAddress(
leg.mint,
leg.to,
false,
leg.programId
);
// Add transfer instruction using delegated authority
transaction.add(
createTransferCheckedInstruction(
fromAccount,
leg.mint,
toAccount,
settlementAgent.publicKey, // Uses delegated authority
leg.amount * Math.pow(10, leg.decimals),
leg.decimals,
[],
leg.programId
)
);
}
// All legs settle atomically - either all succeed or all fail
const signature = await sendAndConfirmTransaction(
connection,
transaction,
[settlementAgent],
{ commitment: "confirmed" }
);
console.log(`✅ Multi-party DvP settled: ${legs.length} legs`);
return signature;
}

Transactiegroottebeperkingen

Solana-transacties hebben een groottelimiet (~1232 bytes). Elke overdrachtsinstrictie voegt ~200 bytes toe. Voor zeer grote meerpartijenafwikkelingen kunt u overwegen om Address Lookup Tables te gebruiken om accountadressen te comprimeren en meer overdrachten per transactie te verwerken.

Productieoverwegingen

Zorg ervoor dat u het volgende aanpakt voordat u naar productie implementeert:

  1. Beveiliging: Professionele sleutelbeheerinfrastructuur, multi-sig-controles, uitgebreide audits van de codebase
  2. Naleving van Regelgeving: Effectenregistratie, KYC/AML-systemen, overdrachtsrestricties en juridisch kader
  3. Sleutelbeheer: Professionele bewaaroplossingen met correcte back-up- en herstelprocedures
  4. Transactieverwerking: Prioriteitsvergoedingen, logica voor opnieuw proberen, bevestigingsafhandeling en RPC-redundantie voor betrouwbare afwikkelingsuitvoering
  5. Operaties: Afwikkelingsplanning, afhandeling van mislukte transacties, reconciliatie en klantenondersteuning
  6. Monitoring: Real-time tracking, waarschuwingen en geautomatiseerde regelgevende rapportage

SPL Token-beperking

SPL Token accounts kunnen slechts één gedelegeerde tegelijk hebben. Dit betekent dat u voor meerpartijenscenario's mogelijk het ontwerp moet aanpassen aan deze beperking door meerdere opeenvolgende delegaties of andere architectuurpatronen te gebruiken.

Accountstatus Inspecteren

De DvPEngine bevat een hulpfunctie om de status van token accounts te inspecteren. Dit is handig voor het verifiëren van de witliststatus, het bevestigen van de voltooiing van afwikkeling, het oplossen van overdrachtsfouten en het controleren van accountstatussen.

// Get detailed account information
const accountInfo = await dvp.getAccountInfo(
investorBondAccount,
TOKEN_2022_PROGRAM_ID
);
console.log("Account Information:");
console.log(` Address: ${accountInfo.address.toBase58()}`);
console.log(` Mint: ${accountInfo.mint.toBase58()}`);
console.log(` Owner: ${accountInfo.owner.toBase58()}`);
console.log(` Balance: ${accountInfo.amount}`);
console.log(` Frozen: ${accountInfo.isFrozen}`);

Volgende Stappen

Na het begrijpen van de basisprincipes van DvP op Solana:

  1. Token Extensions Verkennen: Meer informatie over andere Token 2022-extensies zoals Transfer Hooks voor aanvullende nalevingsfuncties

  2. Token ACL Implementeren: Voor meer geavanceerde nalevingsbehoeften kunt u overwegen Token ACL te gebruiken om allow/block-lijsten te implementeren met permissieloze ontdooicapaciteiten. Hierdoor kunnen gebruikers zelf de whitelistverificatie uitvoeren met behoud van volledige nalevingscontrole.

  3. Regelgevende Verdieping: Raadpleeg juridische experts over effectenregelgeving in uw jurisdictie

  4. Productiearchitectuur: Ontwerp robuuste systemen voor sleutelbeheer, monitoring en noodherstel

Conclusie

Het atomische transactiemodel van Solana en Token 2022 extensions bieden een krachtige basis voor het implementeren van conforme DvP-afwikkelingen voor effecten. De combinatie van:

  • Native atomische uitvoering (geen slimme contractrisico's)
  • Sub-seconde finaliteit
  • Vrijwel nul transactiekosten
  • Ingebouwde nalevingsfuncties (bevroren standaarden, metadata)

Maakt Solana een ideaal platform voor het moderniseren van de afwikkelingsinfrastructuur voor effecten.

De overgang van deze educatieve referentie naar productie vereist echter aanzienlijk aanvullend werk op het gebied van beveiliging, naleving, bewaring en operaties. Werk altijd samen met gekwalificeerde juridische, regelgevende en technische experts bij het omgaan met effectentokenisering.

Is this page helpful?

© 2026 Solana Foundation. Alle rechten voorbehouden.