Développement Local

Démarrer un Validateur Local

Tester le code de votre programme localement peut être beaucoup plus fiable que de le tester sur le devnet, et peut vous aider à faire des tests avant de l'essayer sur le devnet.

Vous pouvez configurer votre validateur de test local (local-test-validator) en installant la suite d'outils de solana (solana tool suite) et en exécutant

solana-test-validator

Les avantages de l'utilisation de local-test-validator sont les suivants :

  • Pas de limites de débit RPC
  • Pas de limites d'airdrop
  • Déploiement direct de programme sur la blockchain (--bpf-program ...)
  • Clonage de comptes à partir d'un cluster public, y compris les programmes (--clone ...)
  • Possibilité de configurer l'historique de conservation des transactions (--limit-ledger-size ...)
  • Possibilité de configurer la durée des époques (--slots-per-epoch ...)
  • Sauter à un slot précis (--warp-slot ...)

Connexion aux Environnements

Lorsque vous développez sur Solana, vous devez vous connecter à un point de terminaison API de RPC en particulier. Solana dispose de 3 environnements de développement publics :

  • 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

Enfin, vous pouvez également vous connecter à un cluster privé, qu'il soit local ou fonctionnant à distance avec l'élément suivant :

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

Inscription à des Evénements

Les Websockets fournissent une interface pub/sub où vous pouvez écouter certains événements. Au lieu d'envoyer une requête à un point de terminaison HTTP classique à intervalles réguliers pour obtenir des mises à jour fréquentes, vous pouvez recevoir ces mises à jour uniquement lorsqu'elles se produisent.

La classe web3 Connectionopen in new window de Solana génère un point de terminaison websocket et enregistre un client websocket lorsque vous créez une nouvelle instance Connection (voir le code source iciopen in new window).

La classe Connection présente des méthodes pub/sub - elles commencent toutes par on, comme les émetteurs d'événements. Lorsque vous appelez ces méthodes d'écoute, un nouvel enregistrement est effectué auprès du client websocket de l'instance Connection. L'exemple de méthode pub/sub que nous utilisons ci-dessous est onAccountChangeopen in new window. Le callback fournira les données d'état mises à jour par le biais d'arguments (voir AccountChangeCallbackopen in new window en guise d'exemple).

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");
    }
}

Obtention de SOL pour les Tests

Lorsque vous travaillez localement, vous avez besoin d'un certain nombre de SOL pour envoyer des transactions. Dans les environnements non-mainnet, vous pouvez recevoir des SOL en les airdroppant à votre adresse.

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"

Utilisation des Comptes et des Programmes du Mainnet

Parfois, les tests locaux reposent sur des programmes et des comptes disponibles uniquement sur le mainnet. Le CLI de Solana permet à la fois de :

  • Télécharger des Programmes et des Comptes
  • Charger des Programmes et des Comptes dans un validateur local

Comment charger des comptes depuis le mainnet

Il est possible de télécharger les comptes de mint du jeton SRM dans un fichier comme suit :

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

Le chargement sur votre réseau local se fait ensuite en passant le fichier contenant les comptes et l'adresse de destination (sur le cluster local) lors du démarrage du validateur :

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

Comment charger des programmes depuis le mainnet

De même, il est possible de télécharger le programme 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

Le chargement sur votre réseau local se fait alors en passant le fichier du programme et l'adresse de destination (sur le cluster local) lors du démarrage du validateur :

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: Partially Sorted, cryptoloutre