$MILAGRE
$MILAGRE
AvançadoTutorial📖 35 min de leitura

Desenvolvendo Smart Contracts em Solana com Rust e Anchor

Aprenda a criar smart contracts em Solana usando Rust e Anchor.

#solana#rust#smart contracts

Solana emergiu como uma das blockchains mais rápidas e eficientes do mercado, processando até 65.000 transações por segundo com custos mínimos. Desenvolver smart contracts (chamados de "programs" em Solana) usando Rust e o framework Anchor oferece uma experiência de desenvolvimento moderna e produtiva, ideal tanto para iniciantes quanto para desenvolvedores experientes.

Por Que Desenvolver em Solana

Performance Incomparável

Solana utiliza uma arquitetura única baseada em Proof of History (PoH) combinado com Proof of Stake (PoS), permitindo velocidades que deixam outras blockchains para trás. Isso significa que suas aplicações descentralizadas terão tempos de resposta quase instantâneos.

Custos Extremamente Baixos

As taxas de transação em Solana custam frações de centavo, tornando viável criar aplicações que requerem milhares de transações diárias sem preocupações com custos proibitivos.

Rust: Segurança e Performance

Rust é uma linguagem de programação de sistemas que oferece segurança de memória sem garbage collector, prevenindo classes inteiras de bugs comuns em outras linguagens. A combinação de performance e segurança torna Rust ideal para desenvolvimento blockchain.

Anchor: Simplificando o Desenvolvimento

Anchor é um framework desenvolvido pela Serum que simplifica drasticamente o desenvolvimento de programas Solana, abstraindo complexidades de serialização/deserialização de dados e fornecendo macros poderosas para validação de contas.

Pré-Requisitos e Ambiente de Desenvolvimento

Conhecimentos Necessários

Básico: Familiaridade com linha de comando, conceitos de programação e noções básicas de blockchain.

Recomendado: Experiência com alguma linguagem de programação (JavaScript, Python, C++), entendimento de conceitos de criptomoedas e carteiras digitais.

Rust: Não é obrigatório ser expert em Rust para começar, mas conhecimentos básicos ajudam significativamente. Você aprenderá conforme desenvolve.

Instalações Necessárias

1. Instalando Rust

Rust e Cargo (gerenciador de pacotes) são essenciais. Instale executando no terminal:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Após instalação, verifique:

rustc --version
cargo --version

2. Instalando Solana CLI

As ferramentas de linha de comando da Solana permitem interagir com a rede:

sh -c "$(curl -sSfL https://release.solana.com/stable/install)"

Verifique a instalação:

solana --version

Configure para usar a devnet (rede de desenvolvimento):

solana config set --url https://api.devnet.solana.com

3. Instalando Node.js e Yarn

Necessários para executar testes e scripts de cliente:

# Instale Node.js (versão 14 ou superior)
# Depois instale Yarn
npm install -g yarn

4. Instalando Anchor

O framework Anchor pode ser instalado via Cargo:

cargo install --git https://github.com/coral-xyz/anchor avm --locked
avm install latest
avm use latest

Verifique a instalação:

anchor --version

Arquitetura de Programas Solana

Separação de Código e Dados

Uma diferença fundamental entre Solana e outras blockchains é que programas Solana são stateless (sem estado). O código do programa é separado dos dados, permitindo que a mesma lógica seja aplicada a diferentes conjuntos de dados.

Em Ethereum, smart contracts armazenam dados internamente. Em Solana, os dados são armazenados em accounts (contas) separadas que o programa pode ler e modificar.

Componentes Principais

Um programa Anchor típico possui três componentes principais:

1. Accounts (Contas): Estruturas de dados que armazenam informações on-chain. Cada conta é de propriedade de um programa específico.

2. Instruction Contexts (Contextos de Instrução): Definem quais contas são necessárias para cada instrução e aplicam restrições de segurança através de macros.

3. Processor (Processador): Contém a lógica de negócio do programa, implementada como funções públicas no módulo principal.

Criando Seu Primeiro Programa: Hello Solana

Passo 1: Inicializando o Projeto

Crie um novo projeto Anchor:

anchor init hello_solana
cd hello_solana

Este comando gera uma estrutura de projeto completa com os seguintes diretórios:

  • programs/: Contém o código Rust do seu programa
  • tests/: Scripts de teste em TypeScript/JavaScript
  • app/: Frontend (opcional)
  • target/: Arquivos compilados
  • Anchor.toml: Configurações do projeto

Passo 2: Entendendo a Estrutura

Abra o arquivo programs/hello_solana/src/lib.rs. Você verá um template básico:

use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWxTW6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod hello_solana {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize {}

Explicando cada parte:

use anchor_lang::prelude::*; - Importa todas as funcionalidades principais do Anchor.

declare_id!() - Macro que declara o ID único do seu programa. Anchor gera automaticamente um ID placeholder.

#[program] - Macro que marca o módulo contendo a lógica do programa.

pub fn initialize() - Uma função pública que representa uma instrução do programa.

Context<Initialize> - Contexto que contém as contas necessárias para executar a instrução.

#[derive(Accounts)] - Macro que define quais contas são necessárias para a instrução.

Passo 3: Criando uma Função Simples

Vamos criar um programa que armazena e retorna uma mensagem. Substitua o conteúdo de lib.rs por:

use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWxTW6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod hello_solana {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>, message: String) -> Result<()> {
        let greeting = &mut ctx.accounts.greeting;
        greeting.message = message;
        greeting.bump = ctx.bumps.greeting;
        msg!("Mensagem armazenada: {}", greeting.message);
        Ok(())
    }

    pub fn update(ctx: Context<Update>, message: String) -> Result<()> {
        let greeting = &mut ctx.accounts.greeting;
        greeting.message = message;
        msg!("Mensagem atualizada: {}", greeting.message);
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(
        init,
        payer = user,
        space = 8 + 32 + 256 + 1,
        seeds = [b"greeting"],
        bump
    )]
    pub greeting: Account<'info, Greeting>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Update<'info> {
    #[account(
        mut,
        seeds = [b"greeting"],
        bump = greeting.bump
    )]
    pub greeting: Account<'info, Greeting>,
}

#[account]
pub struct Greeting {
    pub message: String,
    pub bump: u8,
}

Explicando o código:

Funções do programa:

  • initialize: Cria e inicializa a conta que armazena a mensagem
  • update: Atualiza a mensagem existente

Context Initialize:

  • #[account(init, ...)]: Cria uma nova conta
  • payer = user: Especifica quem paga pela criação da conta
  • space: Define o espaço necessário (8 bytes discriminador + dados)
  • seeds e bump: Criam um Program Derived Address (PDA)

Struct Greeting: Define a estrutura de dados armazenada on-chain.

Passo 4: Configurando a Wallet

Crie uma keypair para deploy:

solana-keygen new --outfile ~/.config/solana/id.json

Solicite SOL de teste na devnet:

solana airdrop 2

Verifique seu saldo:

solana balance

Passo 5: Building o Programa

Compile seu programa:

anchor build

Este comando:

  1. Compila o código Rust para bytecode Solana (BPF)
  2. Gera os tipos TypeScript para interação com o programa
  3. Atualiza o program ID se necessário

Após o build bem-sucedido, você encontrará o binário compilado em target/deploy/.

Passo 6: Deploy na Devnet

Faça deploy do programa:

anchor deploy

O Anchor irá:

  1. Fazer upload do programa compilado para a blockchain
  2. Retornar o Program ID único
  3. Atualizar Anchor.toml e lib.rs com o ID correto

Importante: Anote o Program ID retornado. Você precisará dele para interagir com o programa.

Passo 7: Testando o Programa

Anchor gera automaticamente um arquivo de teste básico em tests/hello_solana.ts. Atualize-o:

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { HelloSolana } from "../target/types/hello_solana";
import { expect } from "chai";

describe("hello_solana", () => {
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);

  const program = anchor.workspace.HelloSolana as Program<HelloSolana>;

  it("Inicializa e armazena mensagem", async () => {
    const [greetingPDA] = anchor.web3.PublicKey.findProgramAddressSync(
      [Buffer.from("greeting")],
      program.programId
    );

    const tx = await program.methods
      .initialize("Olá, Solana!")
      .accounts({
        greeting: greetingPDA,
        user: provider.wallet.publicKey,
        systemProgram: anchor.web3.SystemProgram.programId,
      })
      .rpc();

    console.log("Transaction signature:", tx);

    const greetingAccount = await program.account.greeting.fetch(greetingPDA);
    expect(greetingAccount.message).to.equal("Olá, Solana!");
  });

  it("Atualiza a mensagem", async () => {
    const [greetingPDA] = anchor.web3.PublicKey.findProgramAddressSync(
      [Buffer.from("greeting")],
      program.programId
    );

    await program.methods
      .update("Mensagem atualizada!")
      .accounts({
        greeting: greetingPDA,
      })
      .rpc();

    const greetingAccount = await program.account.greeting.fetch(greetingPDA);
    expect(greetingAccount.message).to.equal("Mensagem atualizada!");
  });
});

Execute os testes:

anchor test

Projeto Prático: Sistema de Tweets Simples

Vamos criar um programa mais complexo: uma plataforma de tweets on-chain.

Estrutura do Programa

use anchor_lang::prelude::*;

declare_id!("YourProgramIDHere");

#[program]
pub mod twitter_solana {
    use super::*;

    pub fn create_tweet(
        ctx: Context<CreateTweet>,
        message: String,
    ) -> Result<()> {
        let tweet = &mut ctx.accounts.tweet;

        require!(message.len() <= 280, TwitterError::MessageTooLong);
        require!(!message.trim().is_empty(), TwitterError::EmptyMessage);

        tweet.author = ctx.accounts.author.key();
        tweet.message = message;
        tweet.timestamp = Clock::get()?.unix_timestamp;
        tweet.likes = 0;

        msg!("Tweet criado por: {}", tweet.author);
        Ok(())
    }

    pub fn like_tweet(ctx: Context<LikeTweet>) -> Result<()> {
        let tweet = &mut ctx.accounts.tweet;

        require!(tweet.likes < 999, TwitterError::MaxLikesReached);

        tweet.likes += 1;
        msg!("Tweet agora tem {} likes", tweet.likes);
        Ok(())
    }
}

#[derive(Accounts)]
#[instruction(message: String)]
pub struct CreateTweet<'info> {
    #[account(
        init,
        payer = author,
        space = 8 + 32 + 4 + 280 + 8 + 8,
        seeds = [b"tweet", author.key().as_ref(), &Clock::get()?.unix_timestamp.to_le_bytes()],
        bump
    )]
    pub tweet: Account<'info, Tweet>,
    #[account(mut)]
    pub author: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct LikeTweet<'info> {
    #[account(mut)]
    pub tweet: Account<'info, Tweet>,
}

#[account]
pub struct Tweet {
    pub author: Pubkey,        // 32 bytes
    pub message: String,       // 4 + 280 bytes
    pub timestamp: i64,        // 8 bytes
    pub likes: u64,            // 8 bytes
}

#[error_code]
pub enum TwitterError {
    #[msg("A mensagem não pode ter mais de 280 caracteres")]
    MessageTooLong,
    #[msg("A mensagem não pode estar vazia")]
    EmptyMessage,
    #[msg("O tweet alcançou o máximo de likes")]
    MaxLikesReached,
}

Conceitos Importantes

1. Program Derived Addresses (PDAs)

PDAs são endereços determinísticos derivados de seeds e do program ID. Não possuem chave privada e só podem ser controlados pelo programa que os criou.

seeds = [b"tweet", author.key().as_ref(), &timestamp.to_le_bytes()],
bump

2. Space Calculation

Calcular corretamente o espaço necessário é crucial:

  • 8 bytes: discriminador de conta
  • 32 bytes: Pubkey
  • String: 4 bytes (length) + conteúdo máximo
  • Tipos numéricos: tamanho fixo (u64 = 8 bytes, i64 = 8 bytes)

3. Constraints e Validações

Anchor fornece macros para validar contas:

  • #[account(init)]: Inicializa nova conta
  • #[account(mut)]: Conta mutável
  • payer: Quem paga pela criação
  • seeds e bump: Para PDAs

4. Error Handling

Defina erros customizados usando #[error_code]:

#[error_code]
pub enum TwitterError {
    #[msg("Mensagem de erro amigável")]
    ErrorName,
}

Use com require!():

require!(condition, TwitterError::ErrorName);

Conceitos Avançados

Cross-Program Invocations (CPI)

CPIs permitem que seu programa chame instruções de outros programas:

use anchor_spl::token::{self, Transfer};

pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {
    let cpi_accounts = Transfer {
        from: ctx.accounts.from.to_account_info(),
        to: ctx.accounts.to.to_account_info(),
        authority: ctx.accounts.authority.to_account_info(),
    };

    let cpi_program = ctx.accounts.token_program.to_account_info();
    let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);

    token::transfer(cpi_ctx, amount)?;
    Ok(())
}

Account Constraints

Anchor oferece diversas constraints para segurança:

#[derive(Accounts)]
pub struct SecureContext<'info> {
    #[account(
        mut,
        has_one = owner,              // Valida que owner corresponde
        constraint = account.amount > 0 @ ErrorCode::InsufficientFunds
    )]
    pub account: Account<'info, MyAccount>,
    pub owner: Signer<'info>,
}

Account Types

Anchor fornece vários tipos de conta:

  • Account<'info, T>: Conta deserializada do tipo T
  • Signer<'info>: Conta que assinou a transação
  • Program<'info, T>: Referência a outro programa
  • SystemAccount<'info>: Conta do system program
  • UncheckedAccount<'info>: Conta sem validações (use com cautela)

Eventos (Events)

Emita eventos para que clientes possam observar atividades:

#[event]
pub struct TweetCreated {
    pub author: Pubkey,
    pub message: String,
    pub timestamp: i64,
}

// No código do programa
emit!(TweetCreated {
    author: tweet.author,
    message: tweet.message.clone(),
    timestamp: tweet.timestamp,
});

Melhores Práticas

Segurança

1. Sempre valide propriedade de contas:

#[account(
    mut,
    has_one = owner @ ErrorCode::Unauthorized
)]
pub my_account: Account<'info, MyAccount>,
pub owner: Signer<'info>,

2. Use constraints do Anchor em vez de validações manuais:

Preferir:

#[account(mut, constraint = account.amount >= withdraw_amount)]

Em vez de:

if account.amount < withdraw_amount {
    return Err(error!(...));
}

3. Proteja contra reentrancy:

Atualize estado antes de fazer CPIs para outros programas.

4. Limite tamanho de dados:

Use require!() para validar tamanhos de strings e arrays.

5. Feche contas não utilizadas:

#[account(mut, close = receiver)]
pub account_to_close: Account<'info, MyAccount>,
pub receiver: SystemAccount<'info>,

Performance

1. Minimize espaço de conta:

Contas maiores custam mais SOL para criar e manter.

2. Use PDAs eficientemente:

Seeds curtas e descritivas reduzem custos computacionais.

3. Batch operações quando possível:

Processe múltiplas ações em uma única transação.

Organização de Código

1. Separe em módulos:

src/
├── lib.rs              // Entry point
├── state.rs            // Account structs
├── instructions/       // Lógica de instruções
│   ├── mod.rs
│   ├── create.rs
│   └── update.rs
├── errors.rs           // Error definitions
└── constants.rs        // Constantes

2. Use módulos Anchor:

pub mod instructions;
pub use instructions::*;

3. Documente seu código:

/// Cria um novo tweet na plataforma
///
/// # Arguments
/// * `message` - Conteúdo do tweet (máximo 280 caracteres)
pub fn create_tweet(ctx: Context<CreateTweet>, message: String) -> Result<()> {
    // implementação
}

Deploy em Mainnet

Preparação

1. Teste extensivamente na devnet:

Execute todos os testes e simulações possíveis antes de considerar mainnet.

2. Audite seu código:

Considere auditoria profissional para programas que manipulam valores significativos.

3. Configure para mainnet:

solana config set --url https://api.mainnet-beta.solana.com

4. Adquira SOL suficiente:

Deploy em mainnet requer SOL real. Compre em uma exchange.

Processo de Deploy

1. Build final:

anchor build --verifiable

2. Deploy:

anchor deploy --provider.cluster mainnet

3. Verifique o deploy:

solana program show <PROGRAM_ID>

4. Atualize configurações:

Atualize Anchor.toml e frontend com o Program ID de mainnet.

Upgrades

Programas Solana podem ser upgradable. Para tornar imutável:

solana program set-upgrade-authority <PROGRAM_ID> --final

Atenção: Isso é irreversível. Certifique-se de que o programa está perfeito antes.

Ferramentas e Recursos

IDEs e Extensões

Visual Studio Code:

  • Rust Analyzer: Análise de código Rust
  • Solana Tools: Sintaxe highlighting para Solana

IntelliJ IDEA:

  • Rust plugin: Suporte completo a Rust

Exploradores de Blockchain

Solana Explorer: https://explorer.solana.com Visualize transações, contas e programas.

Solscan: https://solscan.io Explorador alternativo com interface amigável.

Documentação Oficial

Solana Docs: https://docs.solana.com Documentação completa da plataforma.

Anchor Docs: https://www.anchor-lang.com Guias e referências do framework.

Rust Book: https://doc.rust-lang.org/book/ Aprenda Rust do zero.

Comunidades

Solana Discord: Suporte técnico e networking.

Anchor GitHub: https://github.com/coral-xyz/anchor Reporte bugs e contribua com o framework.

Stack Overflow: Tag solana para perguntas técnicas.

Ferramentas de Desenvolvimento

Solana Playground: https://beta.solpg.io IDE online para prototipar programas rapidamente.

Anchor Test Suite: Framework de testes integrado para validar programas.

Metaplex CLI: Ferramentas para trabalhar com NFTs.

Próximos Passos

Projetos Para Praticar

1. Sistema de Votação: Crie um programa onde usuários podem criar propostas e votar.

2. Marketplace Simples: Implemente listagens de produtos e compras on-chain.

3. Sistema de Reputação: Desenvolva um programa que rastreia pontuações de usuários.

4. Token Personalizado: Use Anchor com SPL Token para criar e gerenciar tokens.

5. Staking Program: Permita que usuários façam stake de tokens por recompensas.

Recursos Avançados

Aprenda sobre:

  • Otimização de compute units
  • Account compression
  • Zero-copy deserialization
  • Parallel transaction execution
  • Estado efêmero vs permanente

Continue Aprendendo

Estude projetos open-source:

  • Serum DEX
  • Metaplex NFT Standard
  • Jupiter Aggregator
  • Mango Markets

Analise como projetos estabelecidos estruturam código e implementam funcionalidades complexas.

Participe de hackathons: Solana realiza regularmente hackathons com prêmios, ótima oportunidade para aprender e construir.

Contribua para o ecossistema: Melhore documentação, reporte bugs ou crie ferramentas úteis para outros desenvolvedores.

Conclusão

Desenvolver smart contracts em Solana usando Rust e Anchor oferece uma experiência moderna e eficiente. A combinação de performance excepcional, custos baixíssimos e ferramentas robustas torna Solana uma escolha excelente para aplicações descentralizadas de próxima geração.

Este tutorial cobriu desde configuração básica até conceitos avançados, mas a jornada de aprendizado continua. Pratique consistentemente, participe da comunidade e mantenha-se atualizado sobre novos desenvolvimentos no ecossistema.

O futuro das finanças descentralizadas e aplicações blockchain está sendo construído agora em Solana. Com os conhecimentos adquiridos neste guia, você está preparado para fazer parte dessa revolução tecnológica. Comece pequeno, teste extensivamente e construa projetos que agreguem valor real ao ecossistema.

Boa sorte em sua jornada de desenvolvimento em Solana!

Compartilhe este artigo