Lokale Entwicklung
Starten eines lokalen Validators
Das lokale Testen Ihres Programmcodes kann viel zuverlässiger sein als Testen auf devnet und kann Ihnen beim Testen helfen, bevor Sie es auf devnet ausprobieren.
Sie können Ihren Local-Test-Validator einrichten, indem Sie die Solana Tool Suite installieren. und läuft
„Konsole solana-Test-Validator
Zu den Vorteilen der Verwendung von Local-Test-Validator gehören:
- Keine RPC-Ratenbegrenzungen
- Keine Airdrop-Grenzen
- Direkte On-Chain-Programmbereitstellung (`--bpf-program ...`)
- Konten aus einem öffentlichen Cluster klonen, einschließlich Programme (`--clone ...`)
- Konfigurierbare Aufbewahrung des Transaktionsverlaufs (`--limit-ledger-size ...`)
- Konfigurierbare Epochenlänge (`--slots-per-epoch ...`)
- Zu einem beliebigen Slot springen (`--warp-slot ...`)
Verbindung zu Umgebungen
Wenn Sie an der Solana-Entwicklung arbeiten, müssen Sie eine Verbindung herstellen zu einem bestimmten RPC-API-Endpunkt. Solana hat 3 öffentliche Entwicklung Umgebungen:
- mainnet-beta https://api.mainnet-beta.solana.com
- devnet https://api.devnet.solana.com
- testnet https://api.testnet.solana.com
import { clusterApiUrl, Connection } from "@solana/web3.js";
(async () => {
const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
})();
const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
from solana.rpc.api import Client
client = Client("https://api.mainnet-beta.solana.com")
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;
}
Connection connection("https://api.mainnet-beta.solana.com");
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());
}
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
solana config set --url https://api.mainnet-beta.solana.com
Schließlich können Sie sich auch mit einem privaten Cluster verbinden, entweder einem lokalen oder einem läuft remote mit folgendem:
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");
})();
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")
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;
}
Connection connection("http://127.0.0.1:8899");
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());
}
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
solana config set --url http://privaterpc.com
Abonnieren von Veranstaltungen
Websockets bieten eine Pub/Sub-Schnittstelle, auf der Sie auf bestimmte Ereignisse lauschen können. Anstatt einen typischen HTTP-Endpunkt in regelmäßigen Abständen zu pingen, um häufige Updates zu erhalten, können Sie diese Updates stattdessen nur dann erhalten, wenn sie auftreten.
Solanas web3 Connection
generiert unter der Haube einen Websocket-Endpunkt und registriert einen Websocket-Client, wenn Sie eine neue Connection
-Instanz (siehe Quellcode hier).
Die Klasse „Connection“ legt Pub/Sub-Methoden offen – sie beginnen alle mit „on“, wie Event-Emitter. Wenn Sie diese Listener-Methoden aufrufen, wird ein neues Abonnement für den Websocket-Client dieser „Connection“-Instanz registriert. Die Pub/Sub-Beispielmethode, die wir unten verwenden, ist onAccountChange
. Der Rückruf stellt die aktualisierten Zustandsdaten über Argumente bereit (siehe AccountChangeCallback
als Beispiel).
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"
);
})();
// 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())
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)
// 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;
}
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;
});
for (int i = 0; i < 10; i++) {
connection.poll();
sleep(1);
}
connection.remove_account_listener(subscriptionId);
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");
}
}
let ws_url = String::from("wss://api.devnet.solana.com/");
let (mut client, receiver) = PubsubClient::account_subscribe(
&ws_url,
&pubkey,
Some(RpcAccountInfoConfig {
encoding: None,
data_slice: None,
commitment: Some(CommitmentConfig::confirmed()),
}),
).unwrap();
let message = match receiver.recv().unwrap();
println!("{:?}", message)
Test SOL erhalten
Wenn Sie lokal arbeiten, benötigen Sie zum Senden etwas SOL Transaktionen. In Nicht-Mainnet-Umgebungen können Sie SOL per empfangen Airdrop an Ihre Adresse
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
});
})();
const airdropSignature = await connection.requestAirdrop(
keypair.publicKey,
LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature);
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
#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;
}
connection.request_airdrop(key_pair.public_key).unwrap();
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"),
};
}
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"
solana airdrop 1
Verwenden von Mainnet-Konten und -Programmen
Häufig verlassen sich lokale Tests auf Programme und Konten, die nur im Mainnet verfügbar sind. Die Solana CLI ermöglicht beides:
- Laden Sie Programme und Konten herunter
- Laden Sie Programme und Konten in einen lokalen Validator
So laden Sie Konten aus dem Mainnet
Es ist möglich, das SRM-Token-Mint-Konto in eine Datei herunterzuladen:
# 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
solana account -u m --output json-compact --output-file SRM_token.json SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt
Das Laden in Ihr lokales Netz erfolgt dann, indem die Datei und die Zieladresse des Kontos (auf dem lokalen Cluster) beim Starten des Validators übergeben werden:
# solana-test-validator --account <address to load the account to> <path to account file> --reset
solana-test-validator --account SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt SRM_token.json --reset
solana-test-validator --account SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt SRM_token.json --reset
So laden Sie Programme aus dem Mainnet
Ebenso ist es möglich, das Programm Serum Dex v3 herunterzuladen:
# 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
solana program dump -u m 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin serum_dex_v3.so
Das Laden in Ihr lokales Netz erfolgt dann, indem Sie beim Starten des Validators die Datei und die Zieladresse des Programms (auf dem lokalen Cluster) übergeben:
# 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
solana-test-validator --bpf-program 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin serum_dex_v3.so --reset