券款对付(DvP)是一种证券结算方式,确保证券转让与款项支付同步进行。通过保证交易的两腿原子性执行——要么同时完成,要么同时取消——从而消除交易对手风险。
问题所在
当您购买债券(如商业票据)时,传统上需要完成两件事:
- 您汇款 → 卖方
- 卖方转让债券 → 您
如果这两步分开进行,就存在风险——如果您已付款却始终收不到债券怎么办?反之亦然?
解决方案
(DvP)"券款对付"意味着两笔转让在同一时刻发生,否则均不发生。就像一次双方同时交换的互换交易。
┌─────────────────────────────────────────────────────┐│ ONE ATOMIC TRANSACTION │├─────────────────────────────────────────────────────┤│ ││ Investor ──── $95,000 USDC ────→ Issuer ││ ││ Issuer ─────── 100 Bonds ──────→ Investor ││ ││ ✅ Both happen together, or neither happens ││ │└─────────────────────────────────────────────────────┘
本指南演示如何在 Solana 上使用 SPL Token 2022 扩展实现完整的 DvP 工作流,用于合规债券发行,并以标准 USDC 进行结算,全程无需编写自定义 Rust 程序。
教育参考实现
您可以使用本实现的 源代码 在本地试验 DvP 的实现方式。
本指南仅提供供探索和学习之用的参考实现。请勿将此代码直接用于生产环境,除非您已完成:
- 全面的安全审计
- 完善的密钥管理系统
- 合规性审查
- 法律咨询
- 充分的测试与改造
为何选择 Solana 实现 DvP?
Solana 的架构相较于传统证券结算具有显著优势:
| 维度 | 传统方式(T+2) | Solana |
|---|---|---|
| 结算逻辑 | 清算所 | 原子交易打包 |
| 结算时间 | 2 天 | <1 秒 |
| 交易成本 | $50–$500 | <$0.01 |
| 交易对手风险 | 高(依赖中间方) | 零(原子执行) |
| 最终确认 | 当日收盘 | 约 400 毫秒 |
Solana 的原子交易打包机制消除了中间方,同时提供即时、低成本且安全的结算。
架构概览
DvP 系统由以下核心组件构成:
- 债券代币(商业票据):带有 token extensions 的 SPL Token 2022
- 结算货币:标准 USDC(现有 SPL 代币)
- 结算代理:通过委托授权编排原子互换
- 白名单系统:控制哪些地址可以持有债券
核心设计原则
- 无自定义程序:仅使用 Token Extensions Program 和标准 USDC
- 原子结算:单笔交易确保两笔转让同时成功或同时失败
- 默认冻结状态:债券需要明确的白名单授权以满足监管合规要求
- 委托授权:结算代理在不托管资产的情况下进行协调
- 网络状态通信:无需点对点 API 连接
┌─────────────────────────────────────────────────────┐│ 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) ││ │└─────────────────────────────────────────────────────┘
一个可信方,负责编排交易。双方均向结算代理授予委托权,由其执行原子互换。
┌─────────┐ delegates ┌──────────────────┐ delegates ┌──────────┐│ Issuer │ ─────────────────→ │ Settlement Agent │ ←──────────────── │ Investor │└─────────┘ (bonds) │ (trusted) │ (USDC) └──────────┘│ ││ executes atomic ││ transaction │└──────────────────┘
使用 Token 2022 Extensions 的债券代币
SPL Token 2022 提供了强大的扩展功能,无需自定义程序即可实现合规的证券发行:
债券所需的核心扩展
- 默认账户状态扩展:将所有新 token account 的默认状态设为冻结,需要明确的白名单授权才能解冻
- 元数据扩展:将债券详情(ISIN、到期日、票面利率等)存储在链上
- 永久委托(可选):在监管要求的情况下,允许授权方进行追回或撤销操作
权限配置
- 铸造权限:发行方(控制供应量的创建)
- 冻结权限:结算代理(管理白名单)
- 更新权限:结算代理(可更新元数据)
默认冻结状态对于监管合规至关重要。它确保只有明确列入白名单的地址才能接收和持有证券,从而满足 KYC/AML 要求。
完整 DvP 实现
搭建 DvP 引擎
首先,创建处理所有操作的核心 DvP 引擎类:
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 mintconst mintKeypair = Keypair.generate();// Create the metadata object to get EXACT sizeconst 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 sizeconst metadataLen = pack(metadata).length;// Size of MetadataExtension: 2 bytes for type, 2 bytes for lengthconst 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 patternconst transaction = new Transaction().add(// 1. Create account with just base space, but rent for full spaceSystemProgram.createAccount({fromPubkey: issuer.publicKey,newAccountPubkey: mintKeypair.publicKey,space: spaceWithoutMetadataExtension, // Just base spacelamports, // But rent for full space (includes metadata + TLV)programId: TOKEN_2022_PROGRAM_ID}),// 2. Initialize metadata pointer (before mint!)createInitializeMetadataPointerInstruction(mintKeypair.publicKey,issuer.publicKey, // authoritymintKeypair.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 mintcreateInitializeMintInstruction(mintKeypair.publicKey,config.decimals,issuer.publicKey, // mint authoritythis.settlementAgent.publicKey, // freeze authorityTOKEN_2022_PROGRAM_ID),// 5. Initialize metadatacreateInitializeInstruction({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 fieldsfor (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 transactionawait 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 frozenif (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, // allowOwnerOffCurveTOKEN_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 transactionconst transaction = new Transaction();// Add bond transfer instruction (Issuer → Investor)transaction.add(this.createTransferCheckedIx(issuerBondAccount,params.bondMint,investorBondAccount,params.bondAmount,0, // bonds have 0 decimalsTOKEN_2022_PROGRAM_ID));// Add USDC transfer instruction (Investor → Issuer)transaction.add(this.createTransferCheckedIx(investorUSDCAccount,params.usdcMint,issuerUSDCAccount,params.usdcAmount * 1e6, // USDC has 6 decimals6,TOKEN_PROGRAM_ID));// Send atomic transactionconsole.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 delegationamount,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`);}}
完整使用示例
以下是演示整个 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 mainnetconst USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");async function runDvPExample() {// 1. Initialize connection and keypairsconst 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 engineconst 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 metadataconst bondMint = await dvp.createBondToken(issuer, {name: "ACME Commercial Paper Series A",symbol: "ACME-CP-A",decimals: 0, // Bonds are whole unitsmaturityDate: new Date("2026-12-31"),couponRate: 4.5, // 4.5% annual couponisin: "US0000000001",description: "https://acme.com/bonds/series-a"});// 5. Whitelist issuer and mint bondsconsole.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 investorconsole.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 agentconsole.log("\n🔐 Delegating authority to settlement agent...");// Issuer delegates bondsawait dvp.delegateAuthority(issuer,issuerBondAccount,100, // 100 bonds0, // 0 decimalsTOKEN_2022_PROGRAM_ID);// Investor delegates USDCawait dvp.delegateAuthority(investor,investorUSDCAccount.address,95000, // $95,0006, // USDC decimalsTOKEN_PROGRAM_ID);// 9. Execute atomic DvP settlementconsole.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 balancesconsole.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 examplerunDvPExample().catch(console.error);
附加功能
更新元数据
结算代理(拥有更新权限)可以更新元数据字段:
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]);}
从白名单中移除
通过使用内置的 removeFromWhitelist 方法冻结投资者账户,撤销其持有债券的资格:
await dvp.removeFromWhitelist(bondMint, investorBondAccount);
冻结与销毁的区别
冻结账户会阻止转账,但保留账户及其余额。若需完全移除,您可能需要先将债券转回发行方,再冻结该账户。
多方结算
对于涉及多方的复杂场景,您可以将多笔转账打包进单笔原子交易:
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 transactionfor (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 authoritytransaction.add(createTransferCheckedInstruction(fromAccount,leg.mint,toAccount,settlementAgent.publicKey, // Uses delegated authorityleg.amount * Math.pow(10, leg.decimals),leg.decimals,[],leg.programId));}// All legs settle atomically - either all succeed or all failconst signature = await sendAndConfirmTransaction(connection,transaction,[settlementAgent],{ commitment: "confirmed" });console.log(`✅ Multi-party DvP settled: ${legs.length} legs`);return signature;}
交易大小限制
Solana 交易有大小限制(约 1232 字节),每条转账指令约占 200 字节。对于规模较大的多方结算,建议使用 地址查找表 压缩账户地址,以便在单笔交易中容纳更多转账。
生产环境注意事项
在部署至生产环境之前,请确保您已妥善处理以下事项:
- 安全性:专业的密钥管理基础设施、多签控制,以及对代码库的全面审计
- 监管合规:证券注册、KYC/AML 系统、转让限制及法律框架
- 密钥管理:具备完善备份与恢复流程的专业托管解决方案
- 交易处理:优先费用、重试逻辑、确认处理,以及用于可靠结算执行的 RPC 冗余
- 运营管理:结算调度、失败交易处理、对账及客户支持
- 监控:实时追踪、告警及自动化监管报告
SPL Token 限制
SPL Token 账户一次只能有一个委托方。这意味着在多方场景中,您可能需要通过多次顺序委托或不同的架构模式来规避此限制。
检查账户状态
DvPEngine 提供了一个用于检查 token account 状态的辅助方法,可用于验证白名单状态、确认结算完成情况、调试转账问题以及审计账户状态。
// Get detailed account informationconst 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}`);
后续步骤
在了解 Solana 上 DvP 的基本原理之后:
-
探索 Token Extensions:了解其他 Token 2022 扩展,例如用于增强合规功能的 Transfer Hooks
-
实现 Token ACL:对于更复杂的合规需求,可考虑使用 Token ACL 实现允许/阻止列表,并支持无许可解冻功能。这使用户可以自助完成白名单验证,同时保持完整的合规控制。
-
深入了解监管要求:就您所在司法管辖区的证券法规咨询法律专家
-
生产架构设计:构建稳健的密钥管理、监控及灾难恢复系统
结语
Solana 的原子交易模型与 Token 2022 扩展为实现合规的证券 DvP 结算提供了强大的基础。以下特性的结合:
- 原生原子执行(无智能合约风险)
- 亚秒级最终确认
- 近乎零成本的交易费用
- 内置合规功能(默认冻结、元数据)
使 Solana 成为推动证券结算基础设施现代化的理想平台。
然而,从本教育参考实现迈向生产环境,还需要在安全性、合规性、托管及运营等方面投入大量额外工作。在涉及证券代币化时,请务必与具备资质的法律、监管及技术专家紧密协作。
Is this page helpful?