Solana Cookbook | Local Development (2024)

# Starting a Local Validator

Testing your program code locally can be a lot more reliable than testing on devnet, and can help you test before trying it out on devnet.

You can setup your local-test-validator by installing the solana tool suite and running

solana-test-validator

1

Benefits of using local-test-validator include:

  • No RPC rate-limits
  • No airdrop limits
  • Direct on-chain program deployment (--bpf-program ...)
  • Clone accounts from a public cluster, including programs (--clone ...)
  • Configurable transaction history retention (--limit-ledger-size ...)
  • Configurable epoch length (--slots-per-epoch ...)
  • Jump to an arbitrary slot (--warp-slot ...)

# Connecting to Environments

When you are working on Solana development, you will need to connect to a specific RPC API endpoint. Solana has 3 public development environments:

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

1
2
3
4
5

const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");

1

from solana.rpc.api import Clientclient = Client("https://api.mainnet-beta.solana.com")

1
2
3

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

1

#include "solana.hpp"using namespace many::solana;int main() { Connection connection("https://api.mainnet-beta.solana.com"); return 0;}

1
2
3
4
5
6
7
8

Connection connection("https://api.mainnet-beta.solana.com");

1

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

1
2
3
4
5
6
7

let rpc_url = String::from("https://api.mainnet-beta.solana.com");let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());

1
2

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

1

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

1

Finally, you can also connect to a private cluster, either one local or running remotely with the following:

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

1
2
3
4
5
6

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

1

from solana.rpc.api import Clientclient = Client("http://127.0.0.1:8899")

1
2
3

client = Client("http://127.0.0.1:8899")

1

#include "solana.hpp"using namespace many::solana;int main() { Connection connection("http://127.0.0.1:8899"); return 0;}

1
2
3
4
5
6
7
8

Connection connection("http://127.0.0.1:8899");

1

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

1
2
3
4
5
6
7

let rpc_url = String::from("http://127.0.0.1:8899");let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());

1
2

solana config set --url http://privaterpc.com

1

solana config set --url http://privaterpc.com

1

# Subscribing to Events

Websockets provide a pub/sub interface where you can listen for certain events. Instead of pinging a typical HTTP endpoint at an interval to get frequent updates, you can instead receive those updates only when they happen.

Solana's web3 Connectionopen in new window under the hood generates a websocket endpoint and registers a websocket client when you create a new Connection instance (see source code hereopen in new window).

The Connection class exposes pub/sub methods - they all start with on, like event emitters. When you call these listener methods, it registers a new subscription to the websocket client of that Connection instance. The example pub/sub method we use below is onAccountChangeopen in new window. The callback will provide the updated state data through arguments (see AccountChangeCallbackopen in new window as an example).

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

// Establish new connect to devnet - websocket client connected to devnet will also be registered hereconst connection = new Connection(clusterApiUrl("devnet"), "confirmed");// Create a test wallet to listen toconst 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");

1
2
3
4
5
6
7
8
9
10
11
12
13

import asynciofrom solders.keypair import Keypairfrom solana.rpc.websocket_api import connectasync 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())

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

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) 

1
2
3
4
5
6
7
8
9
10

// 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;}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

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)

1
2
3
4
5
6
7
8
9
10
11
12

# Getting Test SOL

When you're working locally, you need some SOL in order to send transactions. In non-mainnet environments you can receive SOL by airdropping it to your address

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

const airdropSignature = await connection.requestAirdrop( keypair.publicKey, LAMPORTS_PER_SOL);await connection.confirmTransaction(airdropSignature);

1
2
3
4
5
6

from solders.keypair import Keypairfrom solana.rpc.api import Clientwallet = Keypair()client = Client("https://api.devnet.solana.com")#Input Airdrop amount in LAMPORTSclient.request_airdrop(wallet.pubkey(), 1000000000)#Airdrops 1 SOL

1
2
3
4
5
6
7
8
9
10
11

#Input Airdrop amount in LAMPORTSclient.request_airdrop(wallet.pubkey(), 1000000000)#Airdrops 1 SOL

1
2
3
4

// 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;}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

connection.request_airdrop(key_pair.public_key).unwrap();

1

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

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

1
2
3
4
5
6
7
8
9
10
11

solana airdrop 1# Return# "1 SOL"

1
2
3
4

solana airdrop 1

1

# Using Mainnet Accounts and Programs

Oftentimes, local tests rely on programs and accounts available only on mainnet. The Solana CLI allows to both:

  • Download Programs and Accounts
  • Load Programs and Accounts to a local validator

# How to load accounts from mainnet

It is possible to download the SRM token mint account to file:

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

1
2

solana account -u m --output json-compact --output-file SRM_token.json SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt

1

Loading it to your localnet is then done by passing the account's file and destination address (on the local cluster) when starting the validator:

Press </> button to view full source

# solana-test-validator --account <address to load the account to> <path to account file> --resetsolana-test-validator --account SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt SRM_token.json --reset

1
2

solana-test-validator --account SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt SRM_token.json --reset

1

# How to load programs from mainnet

Similarly, it is possible to download the Serum Dex v3 program:

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

1
2

solana program dump -u m 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin serum_dex_v3.so

1

Loading it to your localnet is then done by passing the program's file and destination address (on the local cluster) when starting the validator:

Press </> button to view full source

# solana-test-validator --bpf-program <address to load the program to> <path to program file> --resetsolana-test-validator --bpf-program 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin serum_dex_v3.so --reset

1
2

solana-test-validator --bpf-program 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin serum_dex_v3.so --reset

1

Solana Cookbook | Local Development (2024)
Top Articles
Send Payments Online - PayPal Philippines
Setting the Record Straight | BlackRock
Jordanbush Only Fans
Bleak Faith: Forsaken – im Test (PS5)
Sound Of Freedom Showtimes Near Governor's Crossing Stadium 14
Team 1 Elite Club Invite
Miss Carramello
Roblox Developers’ Journal
Green Bay Press Gazette Obituary
Umn Pay Calendar
Lesson 3 Homework Practice Measures Of Variation Answer Key
Mawal Gameroom Download
Https //Advanceautoparts.4Myrebate.com
Breakroom Bw
Fredericksburg Free Lance Star Obituaries
iOS 18 Hadir, Tapi Mana Fitur AI Apple?
Shannon Dacombe
Yakimacraigslist
Lola Bunny R34 Gif
Understanding Genetics
Orange Pill 44 291
Euro Style Scrub Caps
Walmart Near South Lake Tahoe Ca
Rubber Ducks Akron Score
Hesburgh Library Catalog
Is Poke Healthy? Benefits, Risks, and Tips
Summoners War Update Notes
Our 10 Best Selfcleaningcatlitterbox in the US - September 2024
Shia Prayer Times Houston
How To Improve Your Pilates C-Curve
Ff14 Laws Order
Craigslist Central Il
Morlan Chevrolet Sikeston
Staar English 1 April 2022 Answer Key
Aliciabibs
Greater Keene Men's Softball
Gets Less Antsy Crossword Clue
Mohave County Jobs Craigslist
Blasphemous Painting Puzzle
Dogs Craiglist
Aita For Announcing My Pregnancy At My Sil Wedding
Mugshots Journal Star
Wal-Mart 140 Supercenter Products
Sun Tracker Pontoon Wiring Diagram
Weekly Math Review Q2 7 Answer Key
Top 1,000 Girl Names for Your Baby Girl in 2024 | Pampers
Tlc Africa Deaths 2021
Lyons Hr Prism Login
Union Supply Direct Wisconsin
Fine Taladorian Cheese Platter
Gear Bicycle Sales Butler Pa
Cars & Trucks near Old Forge, PA - craigslist
Latest Posts
Article information

Author: Aracelis Kilback

Last Updated:

Views: 5903

Rating: 4.3 / 5 (44 voted)

Reviews: 91% of readers found this page helpful

Author information

Name: Aracelis Kilback

Birthday: 1994-11-22

Address: Apt. 895 30151 Green Plain, Lake Mariela, RI 98141

Phone: +5992291857476

Job: Legal Officer

Hobby: LARPing, role-playing games, Slacklining, Reading, Inline skating, Brazilian jiu-jitsu, Dance

Introduction: My name is Aracelis Kilback, I am a nice, gentle, agreeable, joyous, attractive, combative, gifted person who loves writing and wants to share my knowledge and understanding with you.