Desarrollo en un entorno local

Iniciando un validador

Probar el código de tu programa localmente puede ser mucho más confiable que probarlo en devnet, y puede ayudarte con las pruebas previas a devnet.

Puedes configurar un validador de prueba local instalando solana tool suite y ejecutando

solana-test-validator

Beneficios de usar local-test-validator incluyen:

  • No tener limites en el número de llamadas RPC
  • No tener límites de airdrop
  • Despliegue directo de un programa on-chain (--bpf-program ...)
  • Conar cuentas de un cluster público, incluyendo programas (--clone ...)
  • La posibilidad de configurar el historial de retención de transacciones (--limit-ledger-size ...)
  • Poder configurar los slots por epoch (--slots-per-epoch ...)
  • Saltar a un slot específico (--warp-slot ...)

Conectándote con los diferentes entornos

Cuando desarrollas en Solana, debes conectarte a un API de RPC específico. Solana cuenta con 3 entornos distintos para desarrollo:

  • mainnet-beta https://api.mainnet-beta.solana.com
  • devnet https://api.devnet.solana.com
  • testnet https://api.testnet.solana.com
Press </> button to view full source
import { clusterApiUrl, Connection } from "@solana/web3.js";

(async () => {
  const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
})();
from solana.rpc.api import Client

client = Client("https://api.mainnet-beta.solana.com")
#include "solana.hpp"

using namespace many::solana;

int main() {
    Connection connection("https://api.mainnet-beta.solana.com");
    return 0;
}
use solana_client::rpc_client::RpcClient;
use solana_sdk::commitment_config::CommitmentConfig;

fn main() {
    let rpc_url = String::from("https://api.mainnet-beta.solana.com");
    let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());
}
solana config set --url https://api.mainnet-beta.solana.com

Finalmente, también te puedes conectar con un cluster privado, sea local o remoto con lo siguiente:

Press </> button to view full source
import { Connection } from "@solana/web3.js";

(async () => {
  // This will connect you to your local validator
  const connection = new Connection("http://127.0.0.1:8899", "confirmed");
})();
from solana.rpc.api import Client

client = Client("http://127.0.0.1:8899")
#include "solana.hpp"

using namespace many::solana;

int main() {
    Connection connection("http://127.0.0.1:8899");
    return 0;
}
use solana_client::rpc_client::RpcClient;
use solana_sdk::commitment_config::CommitmentConfig;

fn main() {
    let rpc_url = String::from("http://127.0.0.1:8899");
    let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());
}
solana config set --url http://privaterpc.com

Suscripción a eventos

Websockets proporciona una interfaz pub/sub donde puedes escuchar ciertos eventos. En lugar de hacer ping a un endpoint HTTP de forma recurrente para obtener actualizaciones frecuentes, puede recibir esas actualizaciones solo cuando sucedan.

Solana web3 Connectionopen in new window por debajo genera un endpoint de websocket y registra a un cliente websocket cuando creas una nueva instancia de conexión (Connection) (puedes ver el código fuente aquíopen in new window).

La clase Connection expone methods pub/sub - todos empiezan con on, como emisores de eventos. Cuando llamas a estos métodos de escucha, se registra una nueva suscripción al cliente de websocket de esa instancia de conexión (Connection). El ejemplo de método pub/sub que utilizamos abajo es onAccountChangeopen in new window. El callback brindará información actualizada a través de los argumentos (ver AccountChangeCallbackopen in new window como un ejemplo).

Press </> button to view full source
import { clusterApiUrl, Connection, Keypair } from "@solana/web3.js";

(async () => {
  // Establish new connect to devnet - websocket client connected to devnet will also be registered here
  const connection = new Connection(clusterApiUrl("devnet"), "confirmed");

  // Create a test wallet to listen to
  const wallet = Keypair.generate();

  // Register a callback to listen to the wallet (ws subscription)
  connection.onAccountChange(
    wallet.publicKey(),
    (updatedAccountInfo, context) =>
      console.log("Updated account info: ", updatedAccountInfo),
    "confirmed"
  );
})();
import asyncio
from solders.keypair import Keypair
from solana.rpc.websocket_api import connect

async def main():
    async with connect("wss://api.devnet.solana.com") as websocket:
        # Create a Test Wallet
        wallet = Keypair()
        # Subscribe to the Test wallet to listen for events
        await websocket.account_subscribe(wallet.pubkey())
        # Capture response from account subscription 
        first_resp = await websocket.recv()
        print("Subscription successful with id {}, listening for events \n".format(first_resp.result))
        updated_account_info = await websocket.recv()
        print(updated_account_info)
        
asyncio.run(main())
// clang++ on_account_change.cpp -o on_account_change -std=c++17 -lssl -lcrypto -lsodium

#include "solana.hpp"

using namespace many::solana;

int main() {
  Connection connection("https://api.devnet.solana.com");

  auto key_pair = Keypair::generate();

  int subscriptionId = connection.on_account_change(key_pair.public_key, [&](Result<Account> result) {
    Account account = result.unwrap();
    std::cout << "owner = " << account.owner.to_base58() << std::endl;
    std::cout << "lamports = " << account.lamports << std::endl;
    std::cout << "data = " << account.data << std::endl;
    std::cout << "executable = " << (account.executable ? "true" : "false") << std::endl;
  });

  sleep(1);

  std::string tx_hash = connection.request_airdrop(key_pair.public_key).unwrap();
  std::cout << "tx hash = " << tx_hash << std::endl;

  for (int i = 0; i < 10; i++) {
    connection.poll();
    sleep(1);
  }

  connection.remove_account_listener(subscriptionId);

  return 0;
}
use solana_client::pubsub_client::PubsubClient;
use solana_client::rpc_config::RpcAccountInfoConfig;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::signature::{Keypair, Signer};

fn main() {
    let wallet = Keypair::new();
    let pubkey = Signer::pubkey(&wallet);
    let ws_url = String::from("wss://api.devnet.solana.com/");
    println!("{}", ws_url);
    if let Ok(subscription) = PubsubClient::account_subscribe(
        &ws_url,
        &pubkey,
        Some(RpcAccountInfoConfig {
            encoding: None,
            data_slice: None,
            commitment: Some(CommitmentConfig::confirmed()),
        }),
    ) {
        let (mut ws_client, receiver) = subscription;
        println!("Subscription successful, listening for events");
        let handle = std::thread::spawn(move || loop {
            println!("Waiting for a message");
            match receiver.recv() {
                Ok(message) => println!("{:?}", message),
                Err(err) => {
                    println!("Connection broke with {:}", err);
                    break;
                }
            }
        });
        handle.join().unwrap();
        ws_client.shutdown().unwrap()
    } else {
        println!("Errooooor");
    }
}

Obteniendo SOL para pruebas

Cuando trabajas localmente, necesitas SOL para enviar transacciones. En entornos que no son de red principal (mainnet), puedes recibir SOL haciendo airdrop a tu dirección

Press </> button to view full source
import { Connection, Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js";

(async () => {
  const keypair = Keypair.generate();

  const connection = new Connection("http://127.0.0.1:8899", "confirmed");

  const signature = await connection.requestAirdrop(
    keypair.publicKey,
    LAMPORTS_PER_SOL
  );
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
  await connection.confirmTransaction({
      blockhash,
      lastValidBlockHeight,
      signature
    });
})();
from solders.keypair import Keypair
from solana.rpc.api import Client

wallet = Keypair()

client = Client("https://api.devnet.solana.com")

#Input Airdrop amount in LAMPORTS
client.request_airdrop(wallet.pubkey(), 1000000000)

#Airdrops 1 SOL
// clang++ request_airdrop.cpp -o request_airdrop -std=c++17 -lssl -lcrypto -lsodium

#include "solana.hpp"

using namespace many::solana;

int main() {
  Connection connection("https://api.devnet.solana.com");

  auto key_pair = Keypair::generate();

  std::string tx_hash = connection.request_airdrop(key_pair.public_key).unwrap();

  std::cout << "tx hash = " << tx_hash << std::endl;

  return 0;
}
use solana_client::rpc_client::RpcClient;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::native_token::LAMPORTS_PER_SOL;
use solana_sdk::signature::{Keypair, Signer};

fn main() {
    let wallet = Keypair::new();
    let pubkey = Signer::pubkey(&wallet);
    let rpc_url = String::from("https://api.devnet.solana.com");
    let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());
    match client.request_airdrop(&pubkey, LAMPORTS_PER_SOL) {
        Ok(sig) => loop {
            if let Ok(confirmed) = client.confirm_transaction(&sig) {
                if confirmed {
                    println!("Transaction: {} Status: {}", sig, confirmed);
                    break;
                }
            }
        },
        Err(_) => println!("Error requesting airdrop"),
    };
}
solana airdrop 1

# Return
# "1 SOL"

Usando cuentas de mainnet y programas

A menudo, las pruebas locales se basan en programas y cuentas disponibles solo en la red principal. La CLI de Solana permite tanto:

  • Descargar programas y cuentas
  • Cargar programas y cuentas a un validador local

Cómo cargar cuentas de mainnet

Es posible descargar la cuenta mint del token SRM:

Press </> button to view full source
# solana account -u <source cluster> --output <output format> --output-file <destination file name/path> <address of account to fetch>
solana account -u m --output json-compact --output-file SRM_token.json SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt

Luego, para cargarlo a la red local se debe pasar el archivo de la cuenta y la dirección de destino (en el clúster local) al iniciar el validador:

Press </> button to view full source
# solana-test-validator --account <address to load the account to> <path to account file> --reset
solana-test-validator --account SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt SRM_token.json --reset

Cómo cargar programas de mainnet

De igual forma, es posible descargar el programa Serum Dex v3:

Press </> button to view full source
# solana program dump -u <source cluster> <address of account to fetch> <destination file name/path>
solana program dump -u m 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin serum_dex_v3.so

Luego, para cargarlo a la red local se debe pasar el archivo de la cuenta y la dirección de destino (en el clúster local) al iniciar el validador:

Press </> button to view full source
# solana-test-validator --bpf-program <address to load the program to> <path to program file> --reset
solana-test-validator --bpf-program 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin serum_dex_v3.so --reset
Last Updated:
Contributors: Marco Ordonez, Partially Sorted