Lập trình ở Local
Khởi chạy Local Validator
Kiểm thử program của bạn trên máy cá nhân sẽ nhanh và đáng tin cậy hơn nhiều so với devnet. Quá trình này giúp bạn có thể kiểm tra chương trình trước khi triển khai và kiểm thứ chúng trên devnet.
Bạn có thể thiết lập local-test-validator
bằng cách cài đặt bộ công cụ solana và chạy lệnh sau:
solana-test-validator
Lợi ích của việc chạy local-test-validator
gồm:
- Không giới hạn truy vấn RPC
- Không giới hạn số lượng SOL nhận được để kiểm thử
- Triển khai trực tiếp program lên on-chain (
--bpf-program ...
) - Sao chép account từ các mạng công cộng, bao gồm cả các program (
--clone ...
) - Có thể điều chỉnh số lượng lịch sử transaction (
--limit-ledger-size ...
) - Có thể điều chỉnh độ dài epoch (
--slots-per-epoch ...
) - Nhảy đến bấy kỳ chỗ trống nào (
--warp-slot ...
)
Kết nối đến môi trường
Một khi bắt đầu làm việc với môi trường phát triển ứng dụng trên Solana, bạn sẽ cần phải kết nối ứng dụng của bạn đến một điểm RPC API cụ thể. Solana có 3 môi trường công cộng cho quá trình phát triển ứng dụng:
- 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
Cuối cùng bạn cũng có thể kết nối đến các mạng riêng, hoặc từ một điểm truy cập cá nhân, hoặc từ một dịch vụ từ xa ví dụ như:
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
Lắng nghe các sự kiện
Websockets cung cấp một giao diện pub/sub để bạn có thể lắng nghe các sự kiện cụ thể. Thay vì liên tục gọi vào các điểm thông tin thông qua HTTP để thường xuyên cập nhật dữ liệu, bạn có thể nhận được dữ liệu cập nhật mỗi khi có thay đổi xảy ra.
Connection
trong web3 của Solana sẽ chủ động tạo ra một kết nối websocket mỗi khi bạn tạo mới Connection
(chi tiết mã nguồn tại đây).
Lớp Connection
sẽ cung cấp các phương thức pub/sub - tất cả chúng đều bắt đầu với tiền tố on
, giống như các emitter
. Khi bạn gọi vào một phương thức nghe, nó sẽ đăng ký một sự kiện mới vào websocket người dùng của Connection
vừa tạo. Ví dụ cho phương thức pub/sub bên dưới sẽ sử dụng onAccountChange
. Hàm gọi lại (callback) sẽ nhận các tham số và cập nhật lại trạng thái mới (tham khảo ví dụ AccountChangeCallback
).
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)
Nhận SOL để kiểm thử
Khi bạn làm việc trên môi trường địa phương, bạn sẽ phải cần một ít SOL để gửi transaction. Trong các môi trường không phải mainnet, bạn có thể nhận được SOL miễn phí.
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
Sử dụng Accounts và Programs trên Mainnet
Thường xuyên, các bài kiểm thử trên môi trường địa phương sẽ dùng đến các account và program chỉ sẵn có trên mainnet. Solana CLI cho phép:
- Tải xuống Programs và Accounts
- Cài đặt Programs và Accounts vào local validator
Làm thế nào để cài đặt accounts từ mainnet
Bạn có thể tải xuống mint account của SRM vào một tập tin:
# 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
Sau đó, cài đặt nó vào môi trường localnet của bạn bằng cách truyền tập tin ở trên và địa chỉ đích (trên môi trường địa phương) khi bắt đầu khởi chạy validator:
# 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
Làm thế nào để cải đặt programs từ mainnet
Tương tự, bạn phải tải xuống program của OpenBook Dex v3:
# solana program dump -u <source cluster> <address of account to fetch> <destination file name/path>
solana program dump -u m 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin OpenBook_dex_v3.so
solana program dump -u m 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin OpenBook_dex_v3.so
Cài đặt nó vào localnet được thực hiện bằng cách truyền tập tin program và địa chỉ đích (trên môi trường đại phương) khi bắt đầu khởi chạy validator:
# solana-test-validator --bpf-program <address to load the program to> <path to program file> --reset
solana-test-validator --bpf-program 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin OpenBook_dex_v3.so --reset
solana-test-validator --bpf-program 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin OpenBook_dex_v3.so --reset