Modelo de Conta Solana
Na Solana, todos os dados são armazenados no que chamamos de "contas". Você pode pensar nos dados na Solana como um banco de dados público com uma única tabela "Contas", onde cada entrada nesta tabela é uma "conta". Cada conta da Solana compartilha o mesmo tipo de Conta base.
Contas
Pontos-chave
- As contas podem armazenar até 10MiB de dados, que contêm código de programa executável ou estado do programa.
- As contas exigem um depósito de rent em lamports (SOL) que é proporcional à quantidade de dados armazenados, e você pode recuperá-lo totalmente ao fechar a conta.
- Cada conta tem um proprietário de programa. Apenas o programa que possui uma conta pode alterar seus dados ou deduzir seu saldo de lamports. Mas qualquer pessoa pode aumentar o saldo.
- Contas sysvar são contas especiais que armazenam o estado do cluster da rede.
- Contas de programa armazenam o código executável de contratos inteligentes.
- Contas de dados são criadas por programas para armazenar e gerenciar o estado do programa.
Conta
Cada conta na Solana tem um endereço único de 32 bytes, frequentemente mostrado
como uma string codificada em base58 (ex:
vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo6NmXEyxm
).
A relação entre a conta e seu endereço funciona como um par chave-valor, onde o endereço é a chave para localizar os dados on-chain correspondentes da conta. O endereço da conta atua como o "ID único" para cada entrada na tabela "Contas".
Endereço da Conta
A maioria das contas Solana usa uma chave pública Ed25519 como seu endereço.
import { generateKeyPairSigner } from "@solana/kit";// Kit does not enable extractable private keysconst keypairSigner = await generateKeyPairSigner();console.log(keypairSigner);
Embora as chaves públicas sejam comumente usadas como endereços de conta, a Solana também suporta um recurso chamado Endereços Derivados de Programa (PDAs). PDAs são endereços especiais que você pode derivar deterministicamente de um ID de programa e entradas opcionais (seeds).
import { Address, getProgramDerivedAddress } from "@solana/kit";const programAddress = "11111111111111111111111111111111" as Address;const seeds = ["helloWorld"];const [pda, bump] = await getProgramDerivedAddress({programAddress,seeds});console.log(`PDA: ${pda}`);console.log(`Bump: ${bump}`);
Tipo de Conta
As contas têm um tamanho máximo de 10MiB e cada conta na Solana compartilha o mesmo tipo base Account.
Tipo de Conta
Cada Conta na Solana tem os seguintes campos.
pub struct Account {/// lamports in the accountpub lamports: u64,/// data held in this account#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]pub data: Vec<u8>,/// the program that owns this account. If executable, the program that loads this account.pub owner: Pubkey,/// this account's data contains a loaded program (and is now read-only)pub executable: bool,/// the epoch at which this account will next owe rentpub rent_epoch: Epoch,}
Campo Lamports
O saldo da conta em lamports, a menor unidade de SOL (1 SOL = 1 bilhão de
lamports). O saldo em SOL de uma conta é a quantidade no campo lamports
convertida para SOL.
As contas Solana devem ter um saldo mínimo de lamports que é proporcional à quantidade de dados armazenados na conta (em bytes). Este saldo mínimo é chamado de "rent".
O saldo de lamports armazenado na conta pode ser totalmente recuperado quando a conta é fechada.
Campo de Dados
Um array de bytes que armazena dados arbitrários para uma conta. O campo de dados é comumente chamado de "dados da conta."
- Para contas de programa (contratos inteligentes), este campo contém o código do programa executável ou o endereço de outra conta que armazena o código do programa executável.
- Para contas não executáveis, geralmente armazena estados que devem ser lidos.
A leitura de dados de uma conta Solana envolve duas etapas:
- Buscar a conta usando seu endereço (chave pública)
- Desserializar o campo de dados da conta de bytes brutos para a estrutura de dados apropriada, que é definida pelo programa que possui a conta
Campo Owner
O ID do programa (chave pública) do programa que possui esta conta.
Cada conta Solana tem um programa designado como seu proprietário. Apenas o programa que possui uma conta pode alterar os dados da conta ou deduzir seu saldo de lamports.
As instruções definidas em um programa determinam como os dados da conta e o saldo de lamports podem ser alterados.
Campo Executable
Este campo indica se uma conta é um programa executável.
- Se
true
, a conta é um programa Solana executável. - Se
false
, a conta é uma conta de dados que armazena estado.
Para contas executáveis, o campo owner
contém o ID do programa de um programa
carregador. Programas carregadores são programas integrados responsáveis por
carregar e gerenciar contas de programas executáveis.
Campo Rent Epoch
O campo rent_epoch
é um campo legado que não é mais utilizado.
Originalmente, este campo rastreava quando uma conta precisaria pagar rent (em lamports) para manter seus dados na rede. No entanto, esse mecanismo de cobrança de rent foi descontinuado.
Rent
Para armazenar dados na blockchain, as contas também devem manter um saldo em lamports (SOL) que é proporcional à quantidade de dados armazenados na conta (em bytes). Este saldo é chamado de "rent", mas funciona mais como um depósito porque você pode recuperar o valor total ao fechar uma conta. Você pode encontrar o cálculo aqui usando estas constantes.
O termo "rent" vem de um mecanismo obsoleto que deduzia regularmente lamports das contas que ficavam abaixo do limite de rent. Este mecanismo não está mais ativo.
Proprietário do programa
Na Solana, os "contratos inteligentes" são chamados de programas. A propriedade do programa é uma parte fundamental do Modelo de Conta da Solana. Cada conta tem um programa designado como seu proprietário. Apenas o programa proprietário pode:
- Alterar o campo
data
da conta - Deduzir lamports do saldo da conta
Cada programa define a estrutura dos dados armazenados no campo data
de uma
conta. As instruções do programa determinam como esses dados e o saldo
lamports
da conta podem ser alterados.
System Program
Por padrão, todas as novas contas pertencem ao System Program. O System Program executa as seguintes funções principais:
Função | Descrição |
---|---|
Criação de novas contas | Apenas o System Program pode criar novas contas. |
Alocação de espaço | Define a capacidade em bytes para o campo de dados de cada conta. |
Atribuição de propriedade | Uma vez que o System Program cria uma conta, ele pode reatribuir o programa proprietário designado para uma conta de programa diferente. É assim que programas personalizados assumem a propriedade de novas contas criadas pelo System Program. |
Transferência de SOL | Transfere lamports (SOL) de contas do System Program para outras contas. |
Observe que todas as contas "wallet" na Solana são "System Accounts" pertencentes ao System Program. O saldo em lamports nessas contas mostra a quantidade de SOL possuída pela carteira. Apenas System Accounts podem pagar taxas de transação.
System Account
Quando SOL é enviado para um novo endereço pela primeira vez, uma conta é automaticamente criada nesse endereço, pertencente ao System Program.
No exemplo abaixo, um novo keypair é gerado e financiado com SOL. Execute o
código para ver o resultado. Observe que o campo owner
da conta é o System
Program com o endereço 11111111111111111111111111111111
.
import {airdropFactory,createSolanaRpc,createSolanaRpcSubscriptions,generateKeyPairSigner,lamports} from "@solana/kit";// Create a connection to Solana clusterconst rpc = createSolanaRpc("http://localhost:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");// Generate a new keypairconst keypair = await generateKeyPairSigner();console.log(`Public Key: ${keypair.address}`);// Funding an address with SOL automatically creates an accountconst signature = await airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: keypair.address,lamports: lamports(1_000_000_000n),commitment: "confirmed"});const accountInfo = await rpc.getAccountInfo(keypair.address).send();console.log(accountInfo);
Contas Sysvar
Contas Sysvar são contas especiais em endereços predefinidos que fornecem acesso aos dados de estado do cluster. Essas contas são atualizadas dinamicamente com dados sobre o cluster da rede. Você pode encontrar a lista completa de contas Sysvar aqui.
O exemplo a seguir mostra como buscar e desserializar os dados da conta Sysvar Clock.
import { createSolanaRpc } from "@solana/kit";import { fetchSysvarClock, SYSVAR_CLOCK_ADDRESS } from "@solana/sysvars";const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");const accountInfo = await rpc.getAccountInfo(SYSVAR_CLOCK_ADDRESS, { encoding: "base64" }).send();console.log(accountInfo);// Automatically fetch and deserialize the account dataconst clock = await fetchSysvarClock(rpc);console.log(clock);
Program Account
Implantar um programa Solana cria um program account executável. O program account armazena o código executável do programa. Program accounts são de propriedade de um Loader Program.
Program Account
Para simplificar, você pode tratar o program account como o próprio programa. Quando você invoca as instruções de um programa, você especifica o endereço do program account (comumente chamado de "Program ID").
O exemplo a seguir busca o Token Program account para mostrar que program
accounts têm o mesmo tipo base Account
, exceto que o campo executable
é
definido como true
. Como program accounts contêm código executável em seu
campo de dados, não desserializamos os dados.
import { Address, createSolanaRpc } from "@solana/kit";const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");const programId = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address;const accountInfo = await rpc.getAccountInfo(programId, { encoding: "base64" }).send();console.log(accountInfo);
Quando você implanta um programa Solana, ele é armazenado em um program account. Os program accounts são de propriedade de um Programa Carregador. Existem várias versões do carregador, mas todas, exceto o loader-v3, armazenam o código executável diretamente no program account. O loader-v3 armazenena o código executável em uma "program data account" separada, e o program account apenas aponta para ela. Quando você implanta um novo programa, a CLI da Solana usa a versão mais recente do carregador por padrão.
Buffer Account
O loader-v3 possui um tipo especial de conta para preparar temporariamente o upload de um programa durante a implantação ou atualizações. No loader-v4, ainda existem buffers, mas são apenas program accounts normais.
Program Data Account
O loader-v3 funciona de maneira diferente de todos os outros programas BPF Loader. O program account contém apenas o endereço de uma program data account, que armazena o código executável real:
Program Data Account
Não confunda essas program data accounts com as data accounts dos programas (veja abaixo).
Data Account
Na Solana, o código executável de um programa é armazenado em uma conta diferente da que guarda o estado do programa. Isso é semelhante a como os sistemas operacionais normalmente têm arquivos separados para programas e seus dados.
Para manter o estado, os programas definem instruções para criar contas separadas que eles possuem. Cada uma dessas contas tem seu próprio endereço único e pode armazenar qualquer dado arbitrário definido pelo programa.
Data Account
Observe que apenas o System Program pode criar novas contas. Uma vez que o System Program cria uma conta, ele pode então atribuir a propriedade da nova conta a outro programa.
Em outras palavras, criar uma conta de dados para um programa personalizado envolve duas etapas:
- Invocar o System Program para criar uma conta e transferir a propriedade para o programa personalizado
- Invocar o programa personalizado, que agora é proprietário da conta, para inicializar os dados da conta conforme definido pela instrução do programa
Este processo de criação de conta é frequentemente abstraído como uma única etapa, mas é útil entender o processo subjacente.
O exemplo a seguir mostra como criar e buscar uma conta Token Mint pertencente ao programa Token 2022.
import {airdropFactory,appendTransactionMessageInstructions,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,generateKeyPairSigner,getSignatureFromTransaction,lamports,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners} from "@solana/kit";import { getCreateAccountInstruction } from "@solana-program/system";import {getInitializeMintInstruction,getMintSize,TOKEN_2022_PROGRAM_ADDRESS,fetchMint} from "@solana-program/token-2022";// Create Connection, local validator in this exampleconst rpc = createSolanaRpc("http://localhost:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");// Generate keypairs for fee payerconst feePayer = await generateKeyPairSigner();// Fund fee payerawait airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: feePayer.address,lamports: lamports(1_000_000_000n),commitment: "confirmed"});// Generate keypair to use as address of mintconst mint = await generateKeyPairSigner();// Get default mint account size (in bytes), no extensions enabledconst space = BigInt(getMintSize());// Get minimum balance for rent exemptionconst rent = await rpc.getMinimumBalanceForRentExemption(space).send();// Instruction to create new account for mint (token 2022 program)// Invokes the system programconst createAccountInstruction = getCreateAccountInstruction({payer: feePayer,newAccount: mint,lamports: rent,space,programAddress: TOKEN_2022_PROGRAM_ADDRESS});// Instruction to initialize mint account data// Invokes the token 2022 programconst initializeMintInstruction = getInitializeMintInstruction({mint: mint.address,decimals: 9,mintAuthority: feePayer.address});const instructions = [createAccountInstruction, initializeMintInstruction];// Get latest blockhash to include in transactionconst { value: latestBlockhash } = await rpc.getLatestBlockhash().send();// Create transaction messageconst transactionMessage = pipe(createTransactionMessage({ version: 0 }), // Create transaction message(tx) => setTransactionMessageFeePayerSigner(feePayer, tx), // Set fee payer(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), // Set transaction blockhash(tx) => appendTransactionMessageInstructions(instructions, tx) // Append instructions);// Sign transaction message with required signers (fee payer and mint keypair)const signedTransaction =await signTransactionMessageWithSigners(transactionMessage);// Send and confirm transactionawait sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction,{ commitment: "confirmed" });// Get transaction signatureconst transactionSignature = getSignatureFromTransaction(signedTransaction);console.log("Mint Address:", mint.address);console.log("Transaction Signature:", transactionSignature);const accountInfo = await rpc.getAccountInfo(mint.address).send();console.log(accountInfo);const mintAccount = await fetchMint(rpc, mint.address);console.log(mintAccount);
Is this page helpful?