Delivery vs Payment (DvP) di Solana

Delivery vs Payment (DvP) adalah metode penyelesaian sekuritas yang memastikan transfer sekuritas terjadi secara bersamaan dengan transfer pembayaran. Hal ini menghilangkan risiko pihak lawan dengan menjamin bahwa kedua sisi transaksi dieksekusi secara atomik — keduanya selesai atau keduanya tidak terjadi.

Masalah

Ketika Anda membeli obligasi (seperti surat berharga komersial), secara tradisional dua hal perlu terjadi:

  1. Anda mengirim uang → Penjual
  2. Penjual mengirim obligasi → Anda

Jika ini terjadi secara terpisah, ada risiko — bagaimana jika Anda membayar tetapi tidak pernah mendapatkan obligasinya? Atau sebaliknya?

Solusi

(DvP) "Delivery vs Payment" berarti kedua transfer terjadi pada saat yang sama persis, atau tidak ada yang terjadi. Ini seperti pertukaran di mana kedua pihak bertukar secara bersamaan.

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

Panduan ini mendemonstrasikan cara mengimplementasikan alur kerja DvP lengkap di Solana menggunakan extension SPL Token 2022 untuk penerbitan obligasi yang patuh dan USDC standar untuk penyelesaian, semuanya tanpa menulis program Rust kustom.

Referensi Implementasi Edukatif

Anda dapat menggunakan kode sumber dari implementasi ini untuk mencoba implementasi DvP secara lokal.

Panduan ini menyediakan referensi implementasi untuk eksplorasi dan tujuan edukasi saja. JANGAN gunakan kode ini langsung di produksi tanpa:

  • Audit keamanan yang komprehensif
  • Sistem manajemen kunci yang tepat
  • Tinjauan kepatuhan regulasi
  • Konsultasi hukum
  • Pengujian dan modifikasi yang ekstensif

Mengapa Solana untuk DvP?

Arsitektur Solana memberikan keunggulan signifikan dibandingkan penyelesaian sekuritas tradisional:

AspekTradisional (T+2)Solana
Logika PenyelesaianLembaga KliringPenggabungan transaksi atomik
Waktu Penyelesaian2 hari<1 detik
Biaya Transaksi$50-500<$0.01
Risiko Pihak LawanTinggi (perantara)Nol (eksekusi atomik)
FinalitasAkhir hari~400ms

Penggabungan transaksi atomik Solana menghilangkan perantara sambil memberikan penyelesaian yang instan, murah, dan aman.

Gambaran Arsitektur

Sistem DvP terdiri dari komponen-komponen inti berikut:

  1. Token Obligasi (Surat Berharga Komersial): SPL Token 2022 dengan Token Extensions
  2. Mata Uang Penyelesaian: USDC standar (SPL token yang sudah ada)
  3. Agen Penyelesaian: Mengorkestrasi pertukaran atomik melalui otoritas yang didelegasikan
  4. Sistem Whitelist: Mengontrol alamat mana yang dapat memegang obligasi

Prinsip Desain Utama

  • Tanpa Program Kustom: Hanya menggunakan Token Extensions SPL Token 2022 dan USDC standar
  • Penyelesaian Atomik: Satu transaksi memastikan kedua transfer berhasil atau gagal bersama
  • Status Beku Default: Obligasi memerlukan whitelisting eksplisit untuk kepatuhan regulasi
  • Otoritas yang Didelegasikan: Agen penyelesaian berkoordinasi tanpa mengambil kustodi
  • Komunikasi Status Jaringan: Tidak diperlukan konektivitas API titik-ke-titik
┌─────────────────────────────────────────────────────┐
│ 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) │
│ │
└─────────────────────────────────────────────────────┘

Pihak tepercaya yang mengorkestrasikan perdagangan. Kedua pihak mendelegasikan otoritas kepada agen penyelesaian, yang kemudian mengeksekusi pertukaran atomik.

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

Token Obligasi dengan Token 2022 Extensions

SPL Token 2022 menyediakan extension yang powerful untuk memungkinkan penerbitan sekuritas yang patuh tanpa program kustom:

Extension Penting untuk Obligasi

  1. Default Account State Extension: Menetapkan semua token account baru ke status beku secara default, memerlukan whitelisting eksplisit
  2. Metadata Extension: Menyimpan detail obligasi di onchain (ISIN, tanggal jatuh tempo, tingkat kupon, dll.)
  3. Permanent Delegate (opsional): Mengizinkan pemulihan atau clawback yang diotorisasi jika diperlukan oleh regulasi

Konfigurasi Otoritas

  • Mint Authority: Penerbit (mengontrol pembuatan pasokan)
  • Freeze Authority: Agen Penyelesaian (mengelola whitelist)
  • Update Authority: Agen Penyelesaian (dapat memperbarui metadata)

Status beku default sangat penting untuk kepatuhan regulasi. Ini memastikan bahwa hanya alamat yang telah di-whitelist secara eksplisit yang dapat menerima dan memegang sekuritas, memenuhi persyaratan KYC/AML.

Implementasi DvP Lengkap

Menyiapkan DvP Engine

Pertama, buat kelas DvP engine inti yang menangani semua operasi:

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`);
}
}

Contoh Penggunaan Lengkap

Berikut adalah contoh lengkap yang mendemonstrasikan seluruh alur kerja DvP:

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);

Fitur Tambahan

Memperbarui Metadata

Agen penyelesaian (dengan update authority) dapat memperbarui field metadata:

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]);
}

Menghapus dari Whitelist

Hapus kemampuan investor untuk memegang obligasi dengan membekukan akun mereka menggunakan metode removeFromWhitelist bawaan:

await dvp.removeFromWhitelist(bondMint, investorBondAccount);

Pembekuan vs Pembakaran

Membekukan akun mencegah transfer tetapi mempertahankan akun dan saldo. Untuk penghapusan menyeluruh, Anda mungkin ingin terlebih dahulu mentransfer obligasi kembali ke penerbit, lalu membekukan akun tersebut.

Penyelesaian Multi-Pihak

Untuk skenario yang lebih kompleks yang melibatkan beberapa pihak, Anda dapat menggabungkan beberapa transfer ke dalam satu transaksi atomik:

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;
}

Batas Ukuran Transaksi

Transaksi Solana memiliki batas ukuran (~1232 byte). Setiap instruksi transfer menambahkan ~200 byte. Untuk penyelesaian multi-pihak yang sangat besar, pertimbangkan untuk menggunakan Address Lookup Tables untuk mengompresi alamat akun dan memasukkan lebih banyak transfer per transaksi.

Pertimbangan Produksi

Sebelum melakukan deployment ke produksi, pastikan Anda menangani:

  1. Keamanan: Infrastruktur manajemen kunci profesional, kontrol multi-sig, audit komprehensif atas basis kode
  2. Kepatuhan Regulasi: Pendaftaran sekuritas, sistem KYC/AML, pembatasan transfer, dan kerangka hukum
  3. Manajemen Kunci: Solusi kustodi profesional dengan prosedur pencadangan dan pemulihan yang tepat
  4. Pemrosesan Transaksi: Biaya prioritas, logika percobaan ulang, penanganan konfirmasi, dan redundansi RPC untuk eksekusi penyelesaian yang andal
  5. Operasi: Penjadwalan penyelesaian, penanganan perdagangan yang gagal, rekonsiliasi, dan dukungan pelanggan
  6. Pemantauan: Pelacakan real-time, peringatan, dan pelaporan regulasi otomatis

Keterbatasan SPL Token

token account SPL Token hanya dapat memiliki satu delegate pada satu waktu. Ini berarti untuk skenario multi-pihak, Anda mungkin perlu merancang sesuai batasan ini dengan menggunakan banyak delegasi berurutan atau pola arsitektur yang berbeda.

Memeriksa Status Akun

DvPEngine menyertakan helper untuk memeriksa status token account. Ini berguna untuk memverifikasi status whitelist, mengonfirmasi penyelesaian yang selesai, melakukan debugging masalah transfer, dan mengaudit status akun.

// 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}`);

Langkah Berikutnya

Setelah memahami dasar-dasar DvP di Solana:

  1. Jelajahi Token Extensions: Pelajari tentang Token 2022 extensions lainnya seperti Transfer Hooks untuk fitur kepatuhan tambahan

  2. Implementasikan Token ACL: Untuk kebutuhan kepatuhan yang lebih canggih, pertimbangkan menggunakan Token ACL untuk mengimplementasikan daftar izin/blokir dengan kemampuan thaw tanpa izin. Ini memungkinkan pengguna untuk melakukan verifikasi whitelist secara mandiri sambil mempertahankan kontrol kepatuhan penuh.

  3. Pendalaman Regulasi: Konsultasikan dengan para ahli hukum mengenai regulasi sekuritas di yurisdiksi Anda

  4. Arsitektur Produksi: Rancang manajemen kunci, pemantauan, dan sistem pemulihan bencana yang kuat

Kesimpulan

Model transaksi atomik Solana dan Token 2022 extensions menyediakan fondasi yang kuat untuk mengimplementasikan penyelesaian DvP yang patuh untuk sekuritas. Kombinasi dari:

  • Eksekusi atomik native (tanpa risiko smart contract)
  • Finalitas di bawah satu detik
  • Biaya transaksi hampir nol
  • Fitur kepatuhan bawaan (beku default, metadata)

Menjadikan Solana platform ideal untuk memodernisasi infrastruktur penyelesaian sekuritas.

Namun, berpindah dari referensi edukatif ini ke produksi memerlukan pekerjaan tambahan yang signifikan seputar keamanan, kepatuhan, kustodi, dan operasi. Selalu bekerja sama dengan para ahli hukum, regulasi, dan teknis yang berkualifikasi saat menangani tokenisasi sekuritas.

Is this page helpful?

© 2026 Yayasan Solana. Semua hak dilindungi.