Toimitus vastaan maksu (DvP) Solanassa

Toimitus vastaan maksu (DvP) on arvopapereiden selvitysmenetelmä, joka varmistaa, että arvopapereiden siirto tapahtuu samanaikaisesti maksun siirron kanssa. Tämä poistaa vastapuoliriskin takaamalla, että transaktion molemmat osat suoritetaan atomisesti – joko molemmat onnistuvat tai kumpikaan ei.

Ongelma

Kun ostat joukkovelkakirjoja (kuten yritystodistuksia), perinteisesti täytyy tapahtua kaksi asiaa:

  1. Lähetät rahat → Myyjä
  2. Myyjä lähettää joukkovelkakirjat → Sinulle

Jos nämä tapahtuvat erikseen, syntyy riski — entä jos maksat mutta et koskaan saa joukkovelkakirjoja? Tai päinvastoin?

Ratkaisu

(DvP) "Toimitus vastaan maksu" tarkoittaa, että molemmat siirrot tapahtuvat täsmälleen samalla hetkellä, tai kumpikaan ei tapahdu. Se on kuin vaihtokauppa, jossa molemmat osapuolet vaihtavat samanaikaisesti.

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

Tämä opas havainnollistaa, kuinka toteuttaa täydellinen DvP-työnkulku Solanassa käyttämällä SPL Token 2022 -laajennuksia sääntöjenmukaista joukkovelkakirjojen liikkeeseenlaskua varten sekä tavallista USDC:tä selvitykseen – ilman mukautettuja Rust-ohjelmia.

Koulutuksellinen viiteimplementaatio

Voit käyttää tämän toteutuksen lähdekoodia kokeillaksesi DvP-implementaatiota paikallisesti.

Tämä opas tarjoaa viiteimplementaation tutkimista ja koulutustarkoituksia varten. ÄLÄ käytä tätä koodia suoraan tuotannossa ilman:

  • Kattavia tietoturva-auditointeja
  • Asianmukaisia avaintenhallintajärjestelmiä
  • Sääntelymääräystenmukaisuuden tarkistusta
  • Oikeudellista konsultaatiota
  • Laajaa testausta ja muutoksia

Miksi Solana DvP:hen?

Solanan arkkitehtuuri tarjoaa merkittäviä etuja perinteiseen arvopapereiden selvitykseen verrattuna:

OminaisuusPerinteinen (T+2)Solana
SelvityslogiikkaSelvitysyhteisötAtominen transaktiopaketointi
Selvitysaika2 päivää<1 sekunti
Transaktiokustannus50–500 $<0,01 $
VastapuoliriskiKorkea (välikädet)Nolla (atominen suoritus)
LopullisuusPäivän päätteeksi~400 ms

Solanan atominen transaktiopaketointi poistaa välikädet tarjoten samalla välittömän, edullisen ja turvallisen selvityksen.

Arkkitehtuurin yleiskatsaus

DvP-järjestelmä koostuu seuraavista ydinkomponenteista:

  1. Joukkovelkakirjatoken (yritystodistus): SPL Token 2022 Token Extensions -laajennuksilla
  2. Selvitysvaluutta: Tavallinen USDC (olemassa oleva SPL-token)
  3. Selvitysagentti: Orkestroi atomisia vaihtoja delegoidun valtuutuksen kautta
  4. Sallittujen lista -järjestelmä: Hallinnoi, mitkä osoitteet voivat pitää hallussaan joukkovelkakirjoja

Keskeiset suunnitteluperiaatteet

  • Ei mukautettuja ohjelmia: Käyttää ainoastaan SPL Token 2022 -laajennuksia ja tavallista USDC:tä
  • Atominen selvitys: Yksittäinen transaktio varmistaa, että molemmat siirrot onnistuvat tai epäonnistuvat yhdessä
  • Oletuksena jäädytetty tila: Joukkovelkakirjat edellyttävät nimenomaista sallittujen listalle lisäämistä sääntelymääräystenmukaisuuden vuoksi
  • Delegoitu valtuutus: Selvitysagentti koordinoi ottamatta haltuunsa
  • Verkkotilaviestintä: Pisteestä pisteeseen -API-yhteyttä ei tarvita
┌─────────────────────────────────────────────────────┐
│ 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) │
│ │
└─────────────────────────────────────────────────────┘

Luotettu osapuoli, joka orkestroi kaupankäynnin. Molemmat osapuolet delegoivat valtuutuksen selvitysagentille, joka sitten suorittaa atomisen vaihdon.

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

Joukkovelkakirjatoken Token 2022 Extensions -laajennuksilla

SPL Token 2022 tarjoaa tehokkaita laajennuksia, jotka mahdollistavat sääntöjenmukaisen arvopaperien liikkeeseenlaskun ilman mukautettuja ohjelmia:

Joukkovelkakirjojen olennaiset laajennukset

  1. Default Account State -laajennus: Asettaa kaikki uudet token accountit oletuksena jäädytetyiksi, edellyttäen nimenomaista sallittujen listalle lisäämistä
  2. Metadata-laajennus: Tallentaa joukkovelkakirjan tiedot ketjuun (ISIN, eräpäivä, kuponkikorko jne.)
  3. Pysyvä valtuutettu (valinnainen): Sallii valtuutetun palautuksen tai takaisinperimisen, jos sääntelyviranomainen niin vaatii

Valtuutuksen konfigurointi

  • Liikkeeseenlaskuvaltuutus: Liikkeeseenlaskija (hallitsee tarjonnan luomista)
  • Jäädytysvaltuutus: Selvitysagentti (hallinnoi sallittujen listaa)
  • Päivitysvaltuutus: Selvitysagentti (voi päivittää metatietoja)

Jäädytetty oletustila on kriittinen sääntelymääräystenmukaisuuden kannalta. Se varmistaa, että vain nimenomaisesti sallittujen listalle lisätyt osoitteet voivat vastaanottaa ja pitää hallussaan arvopapereita, täyttäen KYC/AML-vaatimukset.

Täydellinen DvP-implementaatio

DvP-moottorin käyttöönotto

Luo ensin ydinluokka DvP-moottorille, joka käsittelee kaikki toiminnot:

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

Täydellinen käyttöesimerkki

Tässä on täydellinen esimerkki, joka havainnollistaa koko DvP-työnkulkua:

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

Lisäominaisuudet

Metatietojen päivittäminen

Selvitysagentti (jolla on päivitysvaltuutus) voi päivittää metatietokenttiä:

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

Sallittujen listalta poistaminen

Poista sijoittajan mahdollisuus pitää hallussaan joukkovelkakirjoja jäädyttämällä heidän tilinsä käyttämällä sisäänrakennettua removeFromWhitelist-metodia:

await dvp.removeFromWhitelist(bondMint, investorBondAccount);

Jäädyttäminen vs. polttaminen

Tilin jäädyttäminen estää siirrot, mutta säilyttää tilin ja saldon. Täydelliseksi poistamiseksi saatat haluta ensin siirtää joukkovelkakirjat takaisin liikkeeseenlaskijalle ja sen jälkeen jäädyttää tilin.

Moniosapuolinen selvitys

Monimutkaisempia useita osapuolia koskevia skenaarioita varten voit paketoida useita siirtoja yhdeksi atomiseksi transaktioksi:

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

Transaktion kokorajoitukset

Solana-transaktioilla on kokorajoitus (~1232 tavua). Jokainen siirto-instruktio lisää noin 200 tavua. Hyvin suurissa moniosapuolisissa selvityksissä harkitse Address Lookup Tables -taulukoiden käyttöä tilien osoitteiden tiivistämiseksi ja useampien siirtojen mahduttamiseksi yhdelle transaktiolle.

Tuotantoa koskevat huomiot

Ennen tuotantoon ottamista varmista, että käsittelet seuraavat asiat:

  1. Tietoturva: Ammattimainen avaintenhallinnan infrastruktuuri, moniallekirjoituksen hallinta, koodipohjan kattavat auditoinnit
  2. Sääntelymääräystenmukaisuus: Arvopapereiden rekisteröinti, KYC/AML-järjestelmät, siirtorajoitukset ja oikeudellinen kehys
  3. Avaintenhallinta: Ammattimainen säilytysratkaisu asianmukaisella varmuuskopio- ja palautusmenettelyllä
  4. Transaktioiden käsittely: Prioriteettimaksut, uudelleenyrityslogiikka, vahvistustenkäsittely ja RPC-redundanssi luotettavaa selvityssuoritusta varten
  5. Toiminnot: Selvitysaikataulutus, epäonnistuneiden kauppojen käsittely, täsmäytys ja asiakastuki
  6. Seuranta: Reaaliaikainen seuranta, hälytykset ja automatisoitu sääntelyraportointi

SPL Token -rajoitus

SPL Token accounteilla voi olla vain yksi valtuutettu kerrallaan. Tämä tarkoittaa, että moniosapuolisissa skenaarioissa sinun on ehkä suunniteltava tämän rajoitteen ympärille käyttämällä useita peräkkäisiä delegointeja tai erilaisia arkkitehtuurimalleja.

Tilin tilan tarkastelu

DvPEngine sisältää apuvälineen token account -tilan tarkastelua varten. Tämä on hyödyllistä sallittujen listan tilan tarkistamiseen, selvityksen valmistumisen vahvistamiseen, siirto-ongelmien virheenkorjaukseen ja tilien tilojen auditointiin.

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

Seuraavat vaiheet

Kun olet ymmärtänyt DvP:n perusteet Solanassa:

  1. Tutustu Token Extensions -laajennuksiin: Lue lisää muista Token 2022 -laajennuksista, kuten Transfer Hooks -koukuista lisäsääntöjenmukaisuusominaisuuksia varten

  2. Toteuta Token ACL: Kehittyneempiä sääntelytarpeita varten harkitse Token ACL:n käyttöä salli/estä-listojen toteuttamiseksi luvattoman sulautumisen mahdollisuuksilla. Tämä antaa käyttäjille mahdollisuuden itse palvella sallittujen listan vahvistusta säilyttäen samalla täyden sääntöjenmukaisuuden hallinnan.

  3. Sääntelysyventyminen: Konsultoi oikeusasiantuntijoita arvopapereiden sääntelystä lainkäyttöalueellasi

  4. Tuotantoarkkitehtuuri: Suunnittele kestävä avaintenhallinta, seuranta ja katastrofista palautumisjärjestelmät

Yhteenveto

Solanan atominen transaktiomalli ja Token 2022 Extensions -laajennukset tarjoavat tehokkaan perustan sääntöjenmukaisten DvP-selvitysten toteuttamiseksi arvopapereille. Yhdistelmä:

  • Natiivi atominen suoritus (ei älysopimuksen riskiä)
  • Alle sekunnin lopullisuus
  • Lähes nollan transaktiokustannukset
  • Sisäänrakennetut sääntöjenmukaisuusominaisuudet (jäädytetyt oletukset, metatiedot)

Tekee Solanasta ihanteellisen alustan arvopapereiden selvitysinfrastruktuurin modernisoimiseen.

Siirtyminen tästä koulutuksellisesta viiteimplementaatiosta tuotantoon vaatii kuitenkin merkittävää lisätyötä tietoturvan, sääntöjenmukaisuuden, säilytyksen ja toimintojen osalta. Tee aina yhteistyötä pätevien oikeudellisten, sääntelyllisten ja teknisten asiantuntijoiden kanssa arvopapereiden tokenisaatioon liittyvissä asioissa.

Is this page helpful?

© 2026 Solana Foundation. Kaikki oikeudet pidätetään.