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
Want Stuff? Why We Are Driven to Buy More
What is my External Wallet address and where do I find it?
Mchoul Funeral Home Of Fishkill Inc. Services
Mountain Dew Bennington Pontoon
Triumph Speed Twin 2025 e Speed Twin RS, nelle concessionarie da gennaio 2025 - News - Moto.it
Byrn Funeral Home Mayfield Kentucky Obituaries
Northern Whooping Crane Festival highlights conservation and collaboration in Fort Smith, N.W.T. | CBC News
San Diego Terminal 2 Parking Promo Code
Computer Repair Tryon North Carolina
Grand Park Baseball Tournaments
Caroline Cps.powerschool.com
Dumb Money
Job Shop Hearthside Schedule
Gwdonate Org
Cvs Appointment For Booster Shot
Becu Turbotax Discount Code
Buy PoE 2 Chaos Orbs - Cheap Orbs For Sale | Epiccarry
Cpt 90677 Reimbursem*nt 2023
Aucklanders brace for gales, hail, cold temperatures, possible blackouts; snow falls in Chch
Cambridge Assessor Database
Acts 16 Nkjv
Ups Print Store Near Me
Laveen Modern Dentistry And Orthodontics Laveen Village Az
Ice Dodo Unblocked 76
Ac-15 Gungeon
Like Some Annoyed Drivers Wsj Crossword
Boxer Puppies For Sale In Amish Country Ohio
2011 Hyundai Sonata 2 4 Serpentine Belt Diagram
Bra Size Calculator & Conversion Chart: Measure Bust & Convert Sizes
JVID Rina sauce set1
FAQ's - KidCheck
How rich were the McCallisters in 'Home Alone'? Family's income unveiled
Vadoc Gtlvisitme App
Imagetrend Elite Delaware
Trust/Family Bank Contingency Plan
Aladtec Login Denver Health
Montrose Colorado Sheriff's Department
Labyrinth enchantment | PoE Wiki
M Life Insider
Brandon Spikes Career Earnings
Bekah Birdsall Measurements
Doublelist Paducah Ky
Denise Monello Obituary
Babykeilani
Dicks Mear Me
Wrentham Outlets Hours Sunday
Santa Ana Immigration Court Webex
Craigslist Com Brooklyn
Southwind Village, Southend Village, Southwood Village, Supervision Of Alcohol Sales In Church And Village Halls
The Significance Of The Haitian Revolution Was That It Weegy
Craigslist Charlestown Indiana
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.