Os strikes de NAV (Valor Líquido dos Ativos) são horários fixos durante o dia de negociação em que as cotas do fundo são precificadas e as ordens são executadas. Os fundos tradicionais geralmente têm um único strike diário no fechamento do mercado, mas a velocidade e os baixos custos da Solana permitem múltiplos strikes intradiários, oferecendo mais flexibilidade aos investidores.
O que é NAV?
Um fundo é uma carteira de ativos que muitos investidores possuem em conjunto. O NAV (Valor Líquido dos Ativos) é simplesmente o preço de uma cota em um fundo. Pense assim:
NAV = (Total Value of Everything in the Fund - liabilities) ÷ Number of Shares
Por exemplo, se um fundo possui $10 milhões em ativos e tem 10 milhões de cotas em circulação, cada cota vale $1,00.
Por que o NAV é importante?
- Compra de cotas (subscrição): Você paga o NAV atual. Se o NAV for $1,02 e você investir $102, receberá 100 cotas.
- Venda de cotas (resgate): Você recebe o NAV atual. Se você resgatar 100 cotas ao NAV de $1,02, receberá $102.
Os fundos do mercado monetário geralmente mantêm um NAV estável em torno de $1,00, com pequenas variações com base nos juros recebidos.
O Problema
Ao investir em um fundo do mercado monetário, tradicionalmente duas coisas acontecem em momentos diferentes:
- Você envia uma ordem → Aguarda o fechamento do mercado
- NAV calculado → Cotas emitidas/resgatadas em T+1 ou T+2
Isso gera vários problemas:
- Execução atrasada: Ordens enviadas pela manhã aguardam até as 16h
- Atraso na liquidação: Seu dinheiro fica em suspenso por 1 a 2 dias
- Flexibilidade limitada: Apenas uma oportunidade por dia para transacionar
- Preços desatualizados: O NAV pode estar desatualizado entre o envio e a execução da ordem
A Solução
Os Strikes de NAV na Solana permitem múltiplas janelas de liquidação diárias com execução atômica. As ordens são enfileiradas e executadas instantaneamente em cada horário de strike com o NAV atual.
┌─────────────────────────────────────────────────────────────┐│ MULTIPLE DAILY NAV STRIKES │├─────────────────────────────────────────────────────────────┤│ ││ 9:30 AM 12:00 PM 2:30 PM 4:00 PM ││ │ │ │ │ ││ ▼ ▼ ▼ ▼ ││ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ││ │STRIKE│ │STRIKE│ │STRIKE│ │STRIKE│ ││ │NAV=$1│ │NAV=$1│ │NAV=$1│ │NAV=$1│ ││ │.0012 │ │.0015 │ │.0018 │ │.0020 │ ││ └─────┘ └─────┘ └─────┘ └─────┘ ││ │ │ │ │ ││ ▼ ▼ ▼ ▼ ││ Process Process Process Process ││ Orders Orders Orders Orders ││ ││ ✅ Atomic settlement at each strike ││ ✅ NAV stored onchain in token metadata ││ ✅ Less stale prices ││ │└─────────────────────────────────────────────────────────────┘
Este guia demonstra como implementar strikes de NAV na Solana usando as extensões SPL Token 2022 para emissão de cotas de fundos em conformidade e USDC padrão para liquidação, tudo sem a necessidade de criar programas Rust personalizados.
Implementação de Referência Educacional
Você pode usar o código-fonte desta implementação para experimentar os strikes de NAV localmente.
Este guia fornece uma implementação de referência apenas para fins de exploração e educação. NÃO utilize este código diretamente em produção sem:
- Auditorias de segurança abrangentes
- Sistemas adequados de gerenciamento de chaves
- Revisão de conformidade regulatória
- Consulta jurídica
- Testes extensivos e modificações
Visão Geral da Arquitetura
O sistema de Strikes de NAV é composto pelos seguintes componentes principais:
- Token de Cota do Fundo: SPL Token 2022 com metadados armazenando o NAV onchain
- Moeda de Liquidação: USDC padrão (token SPL existente)
- Administrador do Fundo: Orquestra os strikes por meio de autoridade delegada
- Sistema de Whitelist: Controla quais endereços podem deter cotas do fundo
Princípios-Chave de Design
- Sem Programas Personalizados: Utiliza apenas Token Extensions Program e USDC padrão
- Liquidação Atômica: Uma única transação garante que a subscrição/resgate seja concluída ou falhe em conjunto
- Estado Congelado por Padrão: As cotas exigem inclusão explícita na whitelist para conformidade regulatória
- Autoridade Delegada: O administrador do fundo opera sem assumir a custódia
- NAV Onchain: Preço atual armazenado nos metadados do token, verificável publicamente
Etapa 1: Configuração do Fundo
- Criar token de cota do fundo com extensões de metadados
- Inicializar o NAV em $1,00
- Definir o calendário diário de strikes
- Adicionar investidores institucionais à whitelist
Etapa 2: Período Pré-Strike
- Investidores enviam solicitações de subscrição/resgate
- As ordens são enfileiradas para o próximo horário de strike
- O fundo calcula o NAV preliminar
- Verificações de risco são realizadas
Etapa 3: Execução do Strike de NAV
At Strike Time (e.g., 14:30):├── Calculate final NAV from underlying assets├── Update onchain NAV in metadata├── Process all pending subscriptions atomically│ └── USDC → Fund Shares at exact NAV├── Process all pending redemptions atomically│ └── Fund Shares → USDC at exact NAV└── Emit strike completion event✅ STRIKE COMPLETE (all trades at same NAV)
Etapa 4: Pós-Strike
- Gerar confirmações de operação
- Atualizar a composição do fundo
- Preparar para o próximo strike
- Reconciliar com o custodiante
Padrão de Autoridade Delegada
Os investidores pré-autorizam o administrador do fundo a movimentar seus tokens e, em seguida, o administrador executa no horário do strike sem necessidade das assinaturas dos investidores:
┌──────────┐ delegates ┌────────────────────┐ delegates ┌──────────┐│ Investor │ ───────────────→ │ Fund Administrator │ ←────────────── │ Investor ││ A │ (USDC) │ (trusted) │ (shares) │ B │└──────────┘ │ │ └──────────┘│ executes strike ││ (atomic txns) │└────────────────────┘
Fluxo de Subscrição (USDC → Cotas do Fundo)
Investor wants to invest $100,000 USDCCurrent NAV = $1.000234 per share────────────────────────────────────1. Investor approves $100,000 USDC delegation2. At strike time, atomic transaction:├── Transfer $100,000 USDC from investor├── Calculate shares: 100,000 / 1.000234 = 99,976.61└── Mint 99,976.61 fund shares to investor3. Settlement complete in <1 second
Fluxo de Resgate (Cotas do Fundo → USDC)
Investor wants to redeem 50,000 sharesCurrent NAV = $1.000234 per share────────────────────────────────────1. Investor approves 50,000 shares delegation2. At strike time, atomic transaction:├── Burn 50,000 fund shares from investor├── Calculate USDC: 50,000 × 1.000234 = $50,011.70└── Transfer $50,011.70 USDC to investor3. Settlement complete in <1 second
Por que Solana para Strikes de NAV?
A arquitetura da Solana oferece vantagens significativas em relação à liquidação tradicional de fundos:
| Aspecto | Tradicional (T+1) | Strikes de NAV na Solana |
|---|---|---|
| Lógica de Liquidação | Agentes de transferência | Agrupamento atômico de transações |
| Tempo de Liquidação | 1 a 2 dias | <1 segundo |
| Custo por Transação | $50–200 | <$0,01 |
| Strikes por Dia | 1 | 4+ (configurável) |
| Risco de preços desatualizados | Alto | Menor |
| Transparência do NAV | Relatório no fim do dia | Onchain, em tempo real |
As transações atômicas da Solana eliminam intermediários, proporcionando liquidações instantâneas, econômicas e seguras com transparência de NAV onchain.
Token do Fundo com Token Extensions
O SPL Token 2022 oferece extensões poderosas que permitem a emissão de cotas de fundos em conformidade sem a necessidade de programas personalizados:
Extensões Essenciais para Cotas de Fundos
- Extensão Default Account State: Define todos os novos token accounts como congelados por padrão, exigindo inclusão explícita na whitelist (conformidade com KYC/AML)
- Extensão de Metadados: Armazena o NAV, o calendário de strikes e o AUM onchain para transparência
Configuração de Autoridade
- Mint Authority: Administrador do Fundo (controla a emissão de cotas)
- Freeze Authority: Administrador do Fundo (gerencia a whitelist)
- Update Authority: Administrador do Fundo (atualiza os metadados do NAV)
O estado congelado por padrão é fundamental para a conformidade regulatória. Ele garante que apenas endereços explicitamente incluídos na whitelist (verificados por KYC) possam receber e deter cotas do fundo.
Campos de Metadados Onchain
{"name": "Example Money Market Fund","symbol": "EX-MMF","currentNAV": "1.000234","lastStrikeTime": "2024-01-15T14:30:00Z","strikeSchedule": "[\"09:30\", \"12:00\", \"14:30\", \"16:00\"]","totalAUM": "50000000.00","fundType": "Money Market Fund"}
Implementação Completa dos Strikes de NAV
Configurando o Motor de Strike de NAV
Observe que neste guia utilizamos a biblioteca @solana/kit para criar o Motor de Strike de NAV. Você também pode encontrar implementações em web3.js no
código-fonte. Primeiro, crie
a classe do motor principal que gerencia todas as operações do fundo:
/*** NAV Strikes Engine - Solana Kit Reference Implementation** This implementation demonstrates NAV strikes for money market funds on Solana using:* - SPL Token 2022 with Default Account State extension for fund shares* - Standard USDC for settlement* - Atomic transactions for subscription/redemption* - Delegated authority pattern for fund administrator** Built with @solana/kit (web3.js 2.0)** ⚠️ IMPORTANT: This is a reference implementation for educational purposes.* Do NOT use in production without proper audits and security reviews.*/import {Address,airdropFactory,appendTransactionMessageInstructions,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,generateKeyPairSigner,getSignatureFromTransaction,KeyPairSigner,lamports,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners,Rpc,RpcSubscriptions,SolanaRpcApi,SolanaRpcSubscriptionsApi} from "@solana/kit";import { getCreateAccountInstruction } from "@solana-program/system";import {TOKEN_PROGRAM_ADDRESS,getApproveInstruction,getTransferCheckedInstruction,getMintToInstruction,getBurnInstruction,findAssociatedTokenPda,getCreateAssociatedTokenInstruction} from "@solana-program/token";import {TOKEN_2022_PROGRAM_ADDRESS,getInitializeMintInstruction,getInitializeTokenMetadataInstruction,getUpdateTokenMetadataFieldInstruction,tokenMetadataField,getThawAccountInstruction,getFreezeAccountInstruction,AccountState,getMintSize,extension,getPreInitializeInstructionsForMintExtensions,fetchMaybeToken} from "@solana-program/token-2022";import { pack } from "@solana/spl-token-metadata";import { PublicKey } from "@solana/web3.js";// TLV (Type-Length-Value) sizes for Token-2022 extensionsconst TYPE_SIZE = 2;const LENGTH_SIZE = 2;import type {FundTokenConfig,FundState,SubscriptionParams,RedemptionParams,SubscriptionResult,RedemptionResult,StrikeResult,StrikeOrder,SolanaClient} from "./types";/*** Cluster type for explorer links*/export type ClusterType = "mainnet-beta" | "devnet" | "testnet" | "localnet";/*** Generate Solana Explorer link for a transaction*/export function getExplorerLink(signature: string,cluster: ClusterType = "localnet"): string {const baseUrl = "https://explorer.solana.com/tx";if (cluster === "localnet") {return `${baseUrl}/${signature}?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899`;}return `${baseUrl}/${signature}?cluster=${cluster}`;}/*** Generate Solana Explorer link for an account/address*/export function getAddressExplorerLink(addr: string,cluster: ClusterType = "localnet"): string {const baseUrl = "https://explorer.solana.com/address";if (cluster === "localnet") {return `${baseUrl}/${addr}?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899`;}return `${baseUrl}/${addr}?cluster=${cluster}`;}/*** Create a Solana client for Kit*/export async function createSolanaClient(rpcUrl: string = "http://127.0.0.1:8899",wsUrl: string = "ws://127.0.0.1:8900"): Promise<SolanaClient> {const rpc = createSolanaRpc(rpcUrl);const rpcSubscriptions = createSolanaRpcSubscriptions(wsUrl);const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({rpc,rpcSubscriptions});return {rpc,rpcSubscriptions,sendAndConfirmTransaction};}/*** NAV Strike Engine - Solana Kit Version** Manages the lifecycle of a money market fund on Solana:* - Creates fund tokens with Token 2022 extensions* - Updates NAV at scheduled strike times* - Processes subscription and redemption orders atomically*/export class NAVStrikeEngine {private client: SolanaClient;private fundAdministrator: KeyPairSigner;private cluster: ClusterType;// Fund stateprivate currentNAV: number = 1.0;private totalAUM: number = 0;private totalSharesOutstanding: number = 0;private strikeSchedule: string[] = [];private lastStrikeTime: Date = new Date();// Order queueprivate pendingOrders: StrikeOrder[] = [];private orderCounter: number = 0;constructor(client: SolanaClient,fundAdministrator: KeyPairSigner,cluster: ClusterType = "localnet") {this.client = client;this.fundAdministrator = fundAdministrator;this.cluster = cluster;}/*** Helper to send and confirm transactions with correct typing* Works around stricter types in @solana/kit v5+*/// eslint-disable-next-line @typescript-eslint/no-explicit-anyprivate async sendTransaction(signedTx: any,commitment: "confirmed" | "finalized" = "confirmed"): Promise<void> {await this.client.sendAndConfirmTransaction(signedTx, { commitment });}/*** Get explorer link for a transaction signature*/getTxExplorerLink(signature: string): string {return getExplorerLink(signature, this.cluster);}/*** Get explorer link for an address*/getAddressLink(addr: string): string {return getAddressExplorerLink(addr, this.cluster);}/*** Creates a fund share token using Token-2022 with Default Account State and Metadata extensions* Fund shares are frozen by default and require whitelisting* Metadata stores NAV and fund information onchain*/async createFundToken(issuer: KeyPairSigner,config: FundTokenConfig): Promise<Address> {console.log("\n╔══════════════════════════════════════════════════════════════╗");console.log("║ NAV STRIKE - FUND CREATION (Kit) ║");console.log("╚══════════════════════════════════════════════════════════════╝");console.log(`\n🏗️ Creating fund token with Token-2022 + Metadata...`);console.log(` Name: ${config.name}`);console.log(` Symbol: ${config.symbol}`);console.log(` Initial NAV: $${config.initialNAV.toFixed(6)}`);console.log(` Strike Schedule: ${config.strikeSchedule.join(", ")}`);// Generate new keypair for the mintconst mint = await generateKeyPairSigner();const decimals = config.decimals ?? 6;// Define extensionsconst defaultAccountStateExtension = extension("DefaultAccountState", {state: AccountState.Frozen});const metadataPointerExtension = extension("MetadataPointer", {authority: this.fundAdministrator.address,metadataAddress: mint.address});const extensions = [defaultAccountStateExtension, metadataPointerExtension];// Calculate mint size without metadataconst baseMintSize = getMintSize(extensions);// Create metadata object to calculate EXACT size using pack()// Note: PublicKey is only used here for size calculation, not for transaction buildingconst metadataForSizing = {mint: new PublicKey(mint.address),name: config.name,symbol: config.symbol,uri: config.uri || "",additionalMetadata: [["icon-uri", "link to icon"], // Max NAV format["currentNAV", "999999.999999"], // Max NAV format["lastStrikeTime", "2099-12-31T23:59:59.999Z"], // Max ISO timestamp["strikeSchedule", JSON.stringify(config.strikeSchedule)],["totalAUM", "999999999999999.99"], // Max AUM (quadrillions)["fundType", "Money Market Fund"]] as [string, string][]};// Calculate exact metadata size using pack()const metadataLen = pack(metadataForSizing).length;// MetadataExtension TLV overhead: 2 bytes for type, 2 bytes for lengthconst metadataExtensionOverhead = TYPE_SIZE + LENGTH_SIZE;// Add safety buffer for any encoding overheadconst totalSpace =baseMintSize + metadataLen + metadataExtensionOverhead + 100;// Get rent for total spaceconst mintRent = await this.client.rpc.getMinimumBalanceForRentExemption(BigInt(totalSpace)).send();// Build create account instruction (with base size, but extra rent for metadata)const createAccountIx = getCreateAccountInstruction({payer: issuer,newAccount: mint,lamports: lamports(mintRent),space: baseMintSize,programAddress: TOKEN_2022_PROGRAM_ADDRESS});// Get extension initialization instructionsconst preInitIxs = getPreInitializeInstructionsForMintExtensions(mint.address,extensions);// Initialize mint instruction (Token 2022)const initMintIx = getInitializeMintInstruction({mint: mint.address,decimals,mintAuthority: this.fundAdministrator.address,freezeAuthority: this.fundAdministrator.address});// Initialize metadata instructionconst initMetadataIx = getInitializeTokenMetadataInstruction({metadata: mint.address,updateAuthority: this.fundAdministrator.address,mint: mint.address,mintAuthority: this.fundAdministrator,name: config.name,symbol: config.symbol,uri: ""});// Build custom metadata field update instructionsconst initialMetadata: [string, string][] = [["currentNAV", config.initialNAV.toFixed(6)],["lastStrikeTime", new Date().toISOString()],["strikeSchedule", JSON.stringify(config.strikeSchedule)],["totalAUM", "0.00"],["fundType", "Money Market Fund"]];const updateFieldIxs = initialMetadata.map(([key, value]) =>getUpdateTokenMetadataFieldInstruction({metadata: mint.address,updateAuthority: this.fundAdministrator,field: tokenMetadataField("Key", [key]),value}));// Get latest blockhashconst { value: latestBlockhash } = await this.client.rpc.getLatestBlockhash().send();// Build transaction messageconst transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(issuer, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) =>appendTransactionMessageInstructions([createAccountIx,...preInitIxs,initMintIx,initMetadataIx,...updateFieldIxs],tx));// Sign and sendconst signedTransaction =await signTransactionMessageWithSigners(transactionMessage);const signature = getSignatureFromTransaction(signedTransaction);await this.sendTransaction(signedTransaction);// Update internal statethis.currentNAV = config.initialNAV;this.strikeSchedule = config.strikeSchedule;this.lastStrikeTime = new Date();console.log(`\n✅ Fund token created: ${mint.address}`);console.log(` Mint Authority: ${this.fundAdministrator.address}`);console.log(` Freeze Authority: ${this.fundAdministrator.address}`);console.log(` Default State: FROZEN (requires whitelisting)`);console.log(` ✨ Metadata: ON-CHAIN`);console.log(` - NAV: $${config.initialNAV.toFixed(6)}`);console.log(` - Schedule: ${config.strikeSchedule.join(", ")}`);console.log(` 🔗 Token: ${this.getAddressLink(mint.address)}`);console.log(` 🔗 Tx: ${this.getTxExplorerLink(signature)}`);return mint.address;}
Atualizações Dinâmicas de Metadados
Para as cotas do fundo, os campos currentNAV, lastStrikeTime e totalAUM são
atualizados onchain a cada strike de NAV usando
getUpdateTokenMetadataFieldInstruction. Isso proporciona:
- Visibilidade do NAV em tempo real para todos os participantes
- Trilha de auditoria de todas as atualizações de NAV onchain
- Integração com protocolos DeFi que podem ler metadados onchain
/*** Updates NAV onchain in token metadata*/async updateNAV(fundMint: Address, newNAV: number): Promise<string> {const previousNAV = this.currentNAV;this.currentNAV = newNAV;this.lastStrikeTime = new Date();// Build update field instructionsconst updateNavIx = getUpdateTokenMetadataFieldInstruction({metadata: fundMint,updateAuthority: this.fundAdministrator,field: tokenMetadataField("Key", ["currentNAV"]),value: newNAV.toFixed(6)});const updateTimeIx = getUpdateTokenMetadataFieldInstruction({metadata: fundMint,updateAuthority: this.fundAdministrator,field: tokenMetadataField("Key", ["lastStrikeTime"]),value: this.lastStrikeTime.toISOString()});const updateAumIx = getUpdateTokenMetadataFieldInstruction({metadata: fundMint,updateAuthority: this.fundAdministrator,field: tokenMetadataField("Key", ["totalAUM"]),value: this.totalAUM.toFixed(2)});const { value: latestBlockhash } = await this.client.rpc.getLatestBlockhash().send();const transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(this.fundAdministrator, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) =>appendTransactionMessageInstructions([updateNavIx, updateTimeIx, updateAumIx],tx));const signedTransaction =await signTransactionMessageWithSigners(transactionMessage);const signature = getSignatureFromTransaction(signedTransaction);await this.sendTransaction(signedTransaction);const navChange = ((newNAV - previousNAV) / previousNAV) * 100;const changeSymbol = navChange >= 0 ? "▲" : "▼";console.log(` NAV Updated: $${previousNAV.toFixed(6)} → $${newNAV.toFixed(6)} (${changeSymbol}${Math.abs(navChange).toFixed(4)}%)`);console.log(` 🔗 Explorer: ${this.getTxExplorerLink(signature)}`);return signature;}/*** Whitelists an investor by creating their fund account and thawing it*/async whitelistInvestor(fundMint: Address,investor: Address,payer: KeyPairSigner): Promise<Address> {console.log(`\n🔓 Whitelisting investor: ${investor.slice(0, 20)}...`);// Find the ATAconst [ata] = await findAssociatedTokenPda({mint: fundMint,owner: investor,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const maybeToken = await fetchMaybeToken(this.client.rpc, ata, {commitment: "confirmed"});const { value: latestBlockhash } = await this.client.rpc.getLatestBlockhash().send();if (!maybeToken.exists) {// Create ATAconst createAtaIx = getCreateAssociatedTokenInstruction({payer,owner: investor,mint: fundMint,ata,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const createAtaMsg = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(payer, tx),(tx) =>setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions([createAtaIx], tx));const signedCreateAta =await signTransactionMessageWithSigners(createAtaMsg);await this.sendTransaction(signedCreateAta);}// Thaw the accountconst thawIx = getThawAccountInstruction({account: ata,mint: fundMint,owner: this.fundAdministrator});const thawMsg = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(payer, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions([thawIx], tx));const signedThaw = await signTransactionMessageWithSigners(thawMsg);await this.sendTransaction(signedThaw);console.log(` Account: ${ata}`);console.log(`✅ Investor whitelisted and account thawed`);return ata;}/*** Removes an investor from whitelist by freezing their account*/async removeFromWhitelist(fundMint: Address,investorFundAccount: Address,payer: KeyPairSigner): Promise<void> {console.log(`\n🔒 Removing from whitelist: ${investorFundAccount}`);const { value: latestBlockhash } = await this.client.rpc.getLatestBlockhash().send();const freezeIx = getFreezeAccountInstruction({account: investorFundAccount,mint: fundMint,owner: this.fundAdministrator});const freezeMsg = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(payer, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions([freezeIx], tx));const signedFreeze = await signTransactionMessageWithSigners(freezeMsg);await this.sendTransaction(signedFreeze);console.log(`✅ Account frozen and removed from whitelist`);}/*** Delegates USDC authority to fund administrator for subscription*/async delegateUSDCForSubscription(investor: KeyPairSigner,usdcMint: Address,amount: number): Promise<void> {const [investorUSDC] = await findAssociatedTokenPda({mint: usdcMint,owner: investor.address,tokenProgram: TOKEN_PROGRAM_ADDRESS});console.log(`\n🤝 Delegating USDC for subscription...`);console.log(` Amount: $${amount.toLocaleString()}`);const { value: latestBlockhash } = await this.client.rpc.getLatestBlockhash().send();const approveIx = getApproveInstruction({source: investorUSDC,delegate: this.fundAdministrator.address,owner: investor,amount: BigInt(amount * 1e6)});const approveMsg = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(investor, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions([approveIx], tx));const signedApprove = await signTransactionMessageWithSigners(approveMsg);await this.sendTransaction(signedApprove);console.log(`✅ USDC delegation approved`);}/*** Delegates fund shares authority to administrator for redemption*/async delegateSharesForRedemption(investor: KeyPairSigner,fundMint: Address,shareAmount: number): Promise<void> {const [investorShares] = await findAssociatedTokenPda({mint: fundMint,owner: investor.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});console.log(`\n🤝 Delegating shares for redemption...`);console.log(` Shares: ${shareAmount.toLocaleString()}`);const { value: latestBlockhash } = await this.client.rpc.getLatestBlockhash().send();const approveIx = getApproveInstruction({source: investorShares,delegate: this.fundAdministrator.address,owner: investor,amount: BigInt(Math.floor(shareAmount * 1e6))},{ programAddress: TOKEN_2022_PROGRAM_ADDRESS });const approveMsg = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(investor, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions([approveIx], tx));const signedApprove = await signTransactionMessageWithSigners(approveMsg);await this.sendTransaction(signedApprove);console.log(`✅ Share delegation approved`);}/*** Executes atomic subscription: USDC → Fund Shares at current NAV*/async processSubscription(params: SubscriptionParams): Promise<SubscriptionResult> {const { fundMint, usdcMint, investor, usdcAmount } = params;// Calculate shares at current NAVconst sharesToMint = usdcAmount / this.currentNAV;const shortAddr = `${investor.slice(0, 4)}...${investor.slice(-4)}`;console.log(`\n⚡ Processing subscription...`);console.log(` Investor: ${shortAddr}`);console.log(` USDC Amount: $${usdcAmount.toLocaleString()}`);console.log(` NAV: $${this.currentNAV.toFixed(6)}`);console.log(` Shares to mint: ${sharesToMint.toFixed(6)}`);// Get token accountsconst [investorUSDC] = await findAssociatedTokenPda({mint: usdcMint,owner: investor,tokenProgram: TOKEN_PROGRAM_ADDRESS});const [fundUSDC] = await findAssociatedTokenPda({mint: usdcMint,owner: this.fundAdministrator.address,tokenProgram: TOKEN_PROGRAM_ADDRESS});const [investorShares] = await findAssociatedTokenPda({mint: fundMint,owner: investor,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const { value: latestBlockhash } = await this.client.rpc.getLatestBlockhash().send();// Build atomic transaction with both instructions// 1. Transfer USDC from investor to fund (using delegated authority)const transferUSDCIx = getTransferCheckedInstruction({source: investorUSDC,mint: usdcMint,destination: fundUSDC,authority: this.fundAdministrator,amount: BigInt(Math.floor(usdcAmount * 1e6)),decimals: 6});// 2. Mint fund shares to investorconst mintSharesIx = getMintToInstruction({mint: fundMint,token: investorShares,mintAuthority: this.fundAdministrator,amount: BigInt(Math.floor(sharesToMint * 1e6))},{ programAddress: TOKEN_2022_PROGRAM_ADDRESS });const txMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(this.fundAdministrator, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) =>appendTransactionMessageInstructions([transferUSDCIx, mintSharesIx], tx) // <- Both happen at the same time);const signedTx = await signTransactionMessageWithSigners(txMessage);const signature = getSignatureFromTransaction(signedTx);await this.sendTransaction(signedTx);// Update fund statethis.totalAUM += usdcAmount;this.totalSharesOutstanding += sharesToMint;console.log(`✅ SUBSCRIPTION SETTLED ATOMICALLY`);console.log(` Shares issued: ${sharesToMint.toFixed(6)}`);console.log(` New AUM: $${this.totalAUM.toLocaleString()}`);console.log(` 🔗 Explorer: ${this.getTxExplorerLink(signature)}`);return {signature,usdcAmount,sharesIssued: sharesToMint,executionNAV: this.currentNAV,timestamp: new Date()};}/*** Executes atomic redemption: Fund Shares → USDC at current NAV*/async processRedemption(params: RedemptionParams): Promise<RedemptionResult> {const { fundMint, usdcMint, investor, shareAmount } = params;// Calculate USDC at current NAVconst usdcToPay = shareAmount * this.currentNAV;const shortAddr = `${investor.slice(0, 4)}...${investor.slice(-4)}`;console.log(`\n⚡ Processing redemption...`);console.log(` Investor: ${shortAddr}`);console.log(` Shares to redeem: ${shareAmount.toLocaleString()}`);console.log(` NAV: $${this.currentNAV.toFixed(6)}`);console.log(` USDC to pay: $${usdcToPay.toFixed(2)}`);// Get token accountsconst [investorShares] = await findAssociatedTokenPda({mint: fundMint,owner: investor,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const [investorUSDC] = await findAssociatedTokenPda({mint: usdcMint,owner: investor,tokenProgram: TOKEN_PROGRAM_ADDRESS});const [fundUSDC] = await findAssociatedTokenPda({mint: usdcMint,owner: this.fundAdministrator.address,tokenProgram: TOKEN_PROGRAM_ADDRESS});const { value: latestBlockhash } = await this.client.rpc.getLatestBlockhash().send();// Build atomic transaction with both instructions// 1. Burn fund shares from investor (using delegated authority)const burnSharesIx = getBurnInstruction({account: investorShares,mint: fundMint,authority: this.fundAdministrator,amount: BigInt(Math.floor(shareAmount * 1e6))},{ programAddress: TOKEN_2022_PROGRAM_ADDRESS });// 2. Transfer USDC from fund to investorconst transferUSDCIx = getTransferCheckedInstruction({source: fundUSDC,mint: usdcMint,destination: investorUSDC,authority: this.fundAdministrator,amount: BigInt(Math.floor(usdcToPay * 1e6)),decimals: 6});const txMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(this.fundAdministrator, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) =>appendTransactionMessageInstructions([burnSharesIx, transferUSDCIx], tx));const signedTx = await signTransactionMessageWithSigners(txMessage);const signature = getSignatureFromTransaction(signedTx);await this.sendTransaction(signedTx);// Update fund statethis.totalAUM -= usdcToPay;this.totalSharesOutstanding -= shareAmount;console.log(`✅ REDEMPTION SETTLED ATOMICALLY`);console.log(` USDC paid: $${usdcToPay.toFixed(2)}`);console.log(` New AUM: $${this.totalAUM.toLocaleString()}`);console.log(` 🔗 Explorer: ${this.getTxExplorerLink(signature)}`);return {signature,sharesRedeemed: shareAmount,usdcPaid: usdcToPay,executionNAV: this.currentNAV,timestamp: new Date()};}/*** Queue an order for the next NAV strike*/queueOrder(investor: Address,orderType: "subscribe" | "redeem",amount: number): StrikeOrder {const order: StrikeOrder = {orderId: `ORD-${++this.orderCounter}`,investor,orderType,amount,strikeTime: this.getNextStrikeTime(),status: "pending"};this.pendingOrders.push(order);const shortAddr = `${investor.slice(0, 4)}...${investor.slice(-4)}`;console.log(`\n📝 Order queued: ${order.orderId}`);console.log(` Investor: ${shortAddr}`);console.log(` Type: ${orderType}`);console.log(` Amount: ${orderType === "subscribe"? `$${amount.toLocaleString()} USDC`: `${amount.toLocaleString()} shares`}`);return order;}/*** Execute NAV strike - update NAV and process all pending orders*/async executeStrike(fundMint: Address,usdcMint: Address,newNAV: number): Promise<StrikeResult> {const strikeId = `STRIKE-${Date.now()}`;const strikeTime = new Date();console.log("\n╔══════════════════════════════════════════════════════════════╗");console.log("║ NAV STRIKE EXECUTION ║");console.log("╚══════════════════════════════════════════════════════════════╝");console.log(` Strike ID: ${strikeId}`);console.log(` Strike Time: ${strikeTime.toISOString()}`);console.log(` New NAV: $${newNAV.toFixed(6)}`);console.log(` Pending Orders: ${this.pendingOrders.length}`);console.log("────────────────────────────────────────────────────────────────");// 1. Update NAVawait this.updateNAV(fundMint, newNAV);// 2. Process pending ordersconst signatures: string[] = [];let totalUSDCSubscribed = 0;let totalSharesMinted = 0;let totalSharesRedeemed = 0;let totalUSDCPaid = 0;let subscriptionsProcessed = 0;let redemptionsProcessed = 0;const subscriptions = this.pendingOrders.filter((o) => o.orderType === "subscribe" && o.status === "pending");const redemptions = this.pendingOrders.filter((o) => o.orderType === "redeem" && o.status === "pending");// Process subscriptionsconsole.log(`\n📥 Processing ${subscriptions.length} subscriptions...`);for (const order of subscriptions) {try {const result = await this.processSubscription({fundMint,usdcMint,investor: order.investor,usdcAmount: order.amount});order.status = "executed";signatures.push(result.signature);totalUSDCSubscribed += result.usdcAmount;totalSharesMinted += result.sharesIssued;subscriptionsProcessed++;} catch (error) {console.error(` ❌ Order ${order.orderId} failed:`, error);order.status = "failed";}}// Process redemptionsconsole.log(`\n📤 Processing ${redemptions.length} redemptions...`);for (const order of redemptions) {try {const result = await this.processRedemption({fundMint,usdcMint,investor: order.investor,shareAmount: order.amount});order.status = "executed";signatures.push(result.signature);totalSharesRedeemed += result.sharesRedeemed;totalUSDCPaid += result.usdcPaid;redemptionsProcessed++;} catch (error) {console.error(` ❌ Order ${order.orderId} failed:`, error);order.status = "failed";}}// Clear executed ordersthis.pendingOrders = this.pendingOrders.filter((o) => o.status === "pending");// Print summaryconsole.log("\n════════════════════════════════════════════════════════════════");console.log(" STRIKE SUMMARY ");console.log("════════════════════════════════════════════════════════════════");console.log(` Subscriptions: ${subscriptionsProcessed} processed`);console.log(` Total USDC In: $${totalUSDCSubscribed.toLocaleString()}`);console.log(` Shares Minted: ${totalSharesMinted.toFixed(2)}`);console.log(` Redemptions: ${redemptionsProcessed} processed`);console.log(` Shares Burned: ${totalSharesRedeemed.toFixed(2)}`);console.log(` Total USDC Out: $${totalUSDCPaid.toLocaleString()}`);console.log(` New AUM: $${this.totalAUM.toLocaleString()}`);console.log(` Shares Outstanding: ${this.totalSharesOutstanding.toFixed(2)}`);console.log("════════════════════════════════════════════════════════════════\n");return {strikeId,strikeTime,nav: this.currentNAV,subscriptionsProcessed,totalUSDCSubscribed,totalSharesMinted,redemptionsProcessed,totalSharesRedeemed,totalUSDCPaid,signatures};}/*** Get current fund state*/getFundState(fundMint: Address): FundState {return {fundMint,currentNAV: this.currentNAV,lastStrikeTime: this.lastStrikeTime,totalAUM: this.totalAUM,totalSharesOutstanding: this.totalSharesOutstanding};}/*** Get current NAV*/getCurrentNAV(): number {return this.currentNAV;}/*** Get pending orders*/getPendingOrders(): StrikeOrder[] {return [...this.pendingOrders];}/*** Calculate next strike time based on schedule*/getNextStrikeTime(): Date {const now = new Date();const currentTime = `${now.getHours().toString().padStart(2, "0")}:${now.getMinutes().toString().padStart(2, "0")}`;for (const strikeTime of this.strikeSchedule) {if (strikeTime > currentTime) {const [hours, minutes] = strikeTime.split(":");const strikeDate = new Date(now);strikeDate.setHours(parseInt(hours), parseInt(minutes), 0, 0);return strikeDate;}}// Next strike is tomorrow's first strikeconst tomorrow = new Date(now);tomorrow.setDate(tomorrow.getDate() + 1);const [hours, minutes] = this.strikeSchedule[0].split(":");tomorrow.setHours(parseInt(hours), parseInt(minutes), 0, 0);return tomorrow;}/*** Airdrops SOL for testing*/async airdropSol(publicKey: Address, amount: number): Promise<void> {console.log(`\n💰 Airdropping ${amount} SOL to ${publicKey.slice(0, 20)}...`);const airdrop = airdropFactory({rpc: this.client.rpc,rpcSubscriptions: this.client.rpcSubscriptions});await airdrop({recipientAddress: publicKey,lamports: lamports(BigInt(amount * 1_000_000_000)),commitment: "confirmed"});console.log(`✅ Airdrop complete`);}}
Exemplo Completo de Uso
Aqui está um exemplo completo demonstrando um dia inteiro de operações de strike de NAV:
/*** NAV Strikes Demo - Solana Kit Version** Demonstrates multiple daily NAV strikes for a money market fund on Solana.* Built with @solana/kit (web3.js 2.0)** Run with: npm run demo:kit* Requires: solana-test-validator running locally** ⚠️ EDUCATIONAL REFERENCE ONLY - NOT FOR PRODUCTION USE*/import {airdropFactory,generateKeyPairSigner,lamports,KeyPairSigner,Address} from "@solana/kit";import {NAVStrikeEngine,createSolanaClient,getExplorerLink} from "../nav-strike-engine";import {createTestUSDC,mintTestUSDC,getUSDCBalance,getFundShareBalance} from "../test-usdc";import type { SolanaClient } from "../types";/*** Helper to print section headers*/function printHeader(title: string): void {console.log("\n");console.log("╔══════════════════════════════════════════════════════════════╗");console.log(`║ ${title.padEnd(60)}║`);console.log("╚══════════════════════════════════════════════════════════════╝");}/*** Print final balances for all participants*/async function printBalances(client: SolanaClient,fundMint: Address,usdcMint: Address,participants: { name: string; address: Address }[]): Promise<void> {console.log("\n┌─────────────────────────────────────────────────────────────────┐");console.log("│ FINAL BALANCES │");console.log("├─────────────────────────────────────────────────────────────────┤");for (const { name, address } of participants) {const usdcBalance = await getUSDCBalance(client, usdcMint, address);const shareBalance = await getFundShareBalance(client, fundMint, address);console.log(`│ ${name.padEnd(15)} USDC: $${usdcBalance.toFixed(2).padStart(10)} │ Shares: ${shareBalance.toFixed(2).padStart(10)} │`);}console.log("└─────────────────────────────────────────────────────────────────┘");// Cost comparisonconsole.log("\n┌─────────────────────────────────────────────────────────────────┐");console.log("│ COST COMPARISON │");console.log("├─────────────────────────────────────────────────────────────────┤");console.log("│ Traditional Solana NAV Strikes │");console.log("│ ───────────────────────────────────────────────────────────── │");console.log("│ Strikes/Day: 1 (4PM) 4+ (configurable)│");console.log("│ Settlement: T+1/T+2 Instant │");console.log("│ Pricing: Unknown til 4PM Exact NAV at strike │");console.log("│ Compliance: Manual KYC/AML Onchain whitelist │");console.log("│ Settlement Risk: High None │");console.log("│ Audit Trail: Manual Blockchain │");console.log("└─────────────────────────────────────────────────────────────────┘");}/*** Main demo function*/async function main(): Promise<void> {console.log(`╔══════════════════════════════════════════════════════════════════╗║ ║║ ███╗ ██╗ █████╗ ██╗ ██╗ ███████╗████████╗██████╗ ║║ ████╗ ██║██╔══██╗██║ ██║ ██╔════╝╚══██╔══╝██╔══██╗ ║║ ██╔██╗ ██║███████║██║ ██║ ███████╗ ██║ ██████╔╝ ║║ ██║╚██╗██║██╔══██║╚██╗ ██╔╝ ╚════██║ ██║ ██╔══██╗ ║║ ██║ ╚████║██║ ██║ ╚████╔╝ ███████║ ██║ ██║ ██║ ║║ ╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ║║ ║║ NAV STRIKES - Solana Kit Reference Implementation ║║ Built with @solana/kit (web3.js 2.0) ║║ ║╚══════════════════════════════════════════════════════════════════╝`);// ─────────────────────────────────────────────────────────────────// SETUP: Connect to local validator// ─────────────────────────────────────────────────────────────────printHeader("SETUP: Connecting to Local Validator");const client = await createSolanaClient();console.log("✅ Connected to local validator (http://127.0.0.1:8899)");// Create keypairsconst fundAdmin = await generateKeyPairSigner();const investorA = await generateKeyPairSigner();const investorB = await generateKeyPairSigner();console.log(`\n👤 Fund Administrator: ${fundAdmin.address}`);console.log(`👤 Investor A: ${investorA.address}`);console.log(`👤 Investor B: ${investorB.address}`);// Create airdrop functionconst airdrop = airdropFactory({rpc: client.rpc,rpcSubscriptions: client.rpcSubscriptions});// Fund accounts with SOLconsole.log("\n💰 Airdropping SOL to accounts...");await airdrop({recipientAddress: fundAdmin.address,lamports: lamports(10_000_000_000n),commitment: "confirmed"});console.log(" ✅ Fund Admin: 10 SOL");await airdrop({recipientAddress: investorA.address,lamports: lamports(2_000_000_000n),commitment: "confirmed"});console.log(" ✅ Investor A: 2 SOL");await airdrop({recipientAddress: investorB.address,lamports: lamports(2_000_000_000n),commitment: "confirmed"});console.log(" ✅ Investor B: 2 SOL");// ─────────────────────────────────────────────────────────────────// STEP 1: Create Test USDC// ─────────────────────────────────────────────────────────────────printHeader("STEP 1: Creating Test USDC");const usdcMint = await createTestUSDC(client, fundAdmin, fundAdmin);// Mint USDC to participantsawait mintTestUSDC(client,usdcMint,fundAdmin,investorA.address,500,"Investor A");await mintTestUSDC(client,usdcMint,fundAdmin,investorB.address,300,"Investor B");await mintTestUSDC(client,usdcMint,fundAdmin,fundAdmin.address,1000,"Fund Admin");// ─────────────────────────────────────────────────────────────────// STEP 2: Create NAV Strike Engine & Fund Token// ─────────────────────────────────────────────────────────────────printHeader("STEP 2: Creating Fund & NAV Strike Engine");const engine = new NAVStrikeEngine(client, fundAdmin, "localnet");const fundMint = await engine.createFundToken(fundAdmin, {name: "Example Money Market Fund",symbol: "EX-MMF",uri: "Link to of chain meta data",initialNAV: 1.0,strikeSchedule: ["09:30", "12:00", "14:30", "16:00"],decimals: 6});// ─────────────────────────────────────────────────────────────────// STEP 3: Whitelist Investors// ─────────────────────────────────────────────────────────────────printHeader("STEP 3: Whitelisting Investors (KYC/AML)");await engine.whitelistInvestor(fundMint, investorA.address, fundAdmin);await engine.whitelistInvestor(fundMint, investorB.address, fundAdmin);// ─────────────────────────────────────────────────────────────────// STRIKE 1: 9:30 AM - Initial Subscriptions// ─────────────────────────────────────────────────────────────────printHeader("STRIKE 1: 9:30 AM - Initial Subscriptions");// Investors delegate USDC and queue ordersawait engine.delegateUSDCForSubscription(investorA, usdcMint, 250);engine.queueOrder(investorA.address, "subscribe", 250);await engine.delegateUSDCForSubscription(investorB, usdcMint, 150);engine.queueOrder(investorB.address, "subscribe", 150);// Execute strike at $1.00 NAVawait engine.executeStrike(fundMint, usdcMint, 1.0);// ─────────────────────────────────────────────────────────────────// STRIKE 2: 12:00 PM - Additional Subscription// ─────────────────────────────────────────────────────────────────printHeader("STRIKE 2: 12:00 PM - Additional Subscription");// Investor A adds moreawait engine.delegateUSDCForSubscription(investorA, usdcMint, 100);engine.queueOrder(investorA.address, "subscribe", 100);// Execute strike at $1.01 NAV (slight gain from interest)await engine.executeStrike(fundMint, usdcMint, 1.01);// ─────────────────────────────────────────────────────────────────// STRIKE 3: 2:30 PM - Partial Redemption// ─────────────────────────────────────────────────────────────────printHeader("STRIKE 3: 2:30 PM - Partial Redemption");// Investor B redeems 50 sharesawait engine.delegateSharesForRedemption(investorB, fundMint, 50);engine.queueOrder(investorB.address, "redeem", 50);// Execute strike at $1.02 NAVawait engine.executeStrike(fundMint, usdcMint, 1.02);// ─────────────────────────────────────────────────────────────────// STRIKE 4: 4:00 PM - End of Day// ─────────────────────────────────────────────────────────────────printHeader("STRIKE 4: 4:00 PM - End of Day");// No new orders, just NAV updateawait engine.executeStrike(fundMint, usdcMint, 1.03);// ─────────────────────────────────────────────────────────────────// FINAL: Print Balances// ─────────────────────────────────────────────────────────────────printHeader("FINAL RESULTS");await printBalances(client, fundMint, usdcMint, [{ name: "Fund Admin", address: fundAdmin.address },{ name: "Investor A", address: investorA.address },{ name: "Investor B", address: investorB.address }]);// Print fund stateconst fundState = engine.getFundState(fundMint);console.log("\n📊 Fund State:");console.log(` Current NAV: $${fundState.currentNAV.toFixed(6)}`);console.log(` Total AUM: $${fundState.totalAUM.toLocaleString()}`);console.log(` Shares Outstanding: ${fundState.totalSharesOutstanding.toFixed(2)}`);console.log(` Last Strike: ${fundState.lastStrikeTime.toISOString()}`);console.log("\n✅ Demo complete! NAV Strikes with Solana Kit working correctly.");console.log(" View transactions on Solana Explorer (local validator - links shown above)\n");}// Run the demomain().catch((error) => {console.error("❌ Demo failed:", error);process.exit(1);});
Considerações para Produção
Antes de implantar em produção, certifique-se de abordar:
- Segurança: Gerenciamento profissional de chaves, controles multi-sig e auditorias de segurança abrangentes do código-fonte
- Conformidade Regulatória: Registro de valores mobiliários, sistemas de KYC/AML, restrições de transferência e relatórios exigidos
- Gerenciamento de Chaves: Soluções institucionais de custódia com procedimentos adequados de backup e recuperação
- Processamento de Transações: Taxas prioritárias, lógica de repetição, tratamento de confirmações e redundância de RPC
- Operações do Fundo: Cálculo de NAV a partir de fontes autorizadas, integração com custodiante e reconciliação
- Monitoramento: Rastreamento em tempo real, alertas e relatórios regulatórios automatizados
Próximos Passos
Após compreender os strikes de NAV na Solana:
-
Explorar Token Extensions: Saiba mais sobre outras extensões Token 2022, como por exemplo o delegado permanente para recursos adicionais de conformidade
-
Implementar Token ACL: Para maior conformidade, considere utilizar Token ACL para gerenciar a inclusão de investidores na whitelist com descongelamento sem permissão. Isso permite a verificação de KYC self-service, onde os investidores podem ativar automaticamente suas contas após passar pelas verificações de conformidade, reduzindo a sobrecarga operacional enquanto mantém o controle regulatório.
-
Estudar Liquidação DvP: Consulte nosso guia de Entrega contra Pagamento para padrões de liquidação de valores mobiliários
-
Arquitetura para Produção: Projete sistemas robustos de agendamento, monitoramento e recuperação de desastres
Conclusão
O modelo de transações atômicas da Solana e as Token Extensions fornecem uma base sólida para implementar múltiplos strikes de NAV diários para fundos do mercado monetário. A combinação de:
- Execução atômica nativa (sem risco de contrato inteligente)
- Finalidade em menos de um segundo
- Custos de transação quase nulos
- Transparência de NAV onchain
- Recursos de conformidade integrados (padrões congelados, metadados)
Torna a Solana uma plataforma ideal para modernizar a infraestrutura de liquidação de fundos e oferecer aos investidores oportunidades de negociação mais frequentes.
No entanto, a transição desta referência educacional para a produção requer trabalho adicional significativo em segurança, conformidade, custódia e operações. Trabalhe sempre com especialistas jurídicos, regulatórios e técnicos qualificados ao lidar com a tokenização de fundos.
Is this page helpful?