Solana world for Rustaceans
Solana is a decentralized blockchain built to enable scalable, user-friendly apps for the world. I think it is the best choice for a promising career because this is a new blockchain, but despite this, it has already gained huge popularity. Solana is the fastest blockchain in the world and the fastest growing ecosystem in crypto.
Why Rust?
- quick
- safe
- popular
Safety, easy multi-platform development, speed, and quality is just what blockchain developers need - and where Rust excels.
Are you concerned about the question: do you need blockchain experience to understand how to build on Solana?
The very first thing that I think is important for any new developer coming into the Solana Ecosystem to understand is that it is not a requirement that you have experience with smart contracts or Rust to get started building on Solana. In fact, you don’t need blockchain experience at all. -Chase Barker
Smart Contract
In this article, you will try to develop a smart contract that will send SOL from the created account in the program to Phantom Wallet.
P.S I assume you have already installed Rust!
Phantom Wallet
I use the Phantom Wallet for Solana development. So, you need to create a new wallet:
Warning! You have to remember the secret recovery phrase (save it) for future work with your wallet!
Getting Started
Project creating
First of all, you need to create a rust project:
cargo new --bin transfer
Navigate to the folder containing the project and open your project in IDE.
For the correct project structure, let's create a new file and name it operations.rs. As a result, your project structure will look so:
Adding dependencies
Then you need to add some dependencies to Cargo.toml file:
[dependencies]
solana-sdk = "1.10.0"
solana-client = "1.10.0"
solana-program = "1.7.14"
Click to crate name to check the newest version and study more:
Let's write a smart contract logic!
At first, we will work with the operations.rs file.
new_keypair()
This is a function that creates a new keypair.
Import necessary for new_keypair function:
use solana_sdk::signature::Keypair;
Code:
pub fn new_keypair() -> Keypair {
Keypair::new()
}
check_balance()
This function allows us to check our account balance.
Import necessary for check_balance function:
use std::error::Error;
use solana_client::rpc_client::RpcClient;
use solana_program::pubkey::Pubkey;
Then create a constant LAMPORTS_PER_SOL:
const LAMPORTS_PER_SOL: f64 = 1000000000.0;
The input data of the method is provided in SOL, but we need in lamports. So, we create constant which is equal to the amount lamports in 1 SOL.
Code:
pub fn check_balance(rpc_client: &RpcClient, public_key: &Pubkey) -> Result<f64, Box<dyn Error>> {
Ok(rpc_client.get_balance(&public_key)? as f64 / LAMPORTS_PER_SOL)
}
airdrop()
This one request an airdrop.
Import necessary for airdrop function:
use solana_sdk::signature::Signature;
Code:
pub fn airdrop(rpc_client: &RpcClient, pub_key: &Pubkey, amount_sol: f64) -> Result<Signature, Box<dyn Error>> {
let sig = rpc_client.request_airdrop(&pub_key, (amount_sol * LAMPORTS_PER_SOL) as u64)?;
loop {
let confirmed = rpc_client.confirm_transaction(&sig)?;
if confirmed {
break;
}
}
Ok(sig)
}
transfer()
Makes a transfer between the created account and the wallet.
Code:
pub fn transfer(rpc_client: &RpcClient, sender_keypair: &Keypair, receiver_pub_key: &Pubkey, amount_sol: f64)
-> core::result::Result<Signature, Box<dyn Error>> {
let amount_lamports = (amount_sol * LAMPORTS_PER_SOL) as u64;
Ok(rpc_client.send_and_confirm_transaction(
&system_transaction::transfer(
&sender_keypair, &receiver_pub_key,
amount_lamports,
rpc_client.get_latest_blockhash()?))?)
}
Your operations.rs file needs to look like this:
use std::error::Error;
use solana_client::rpc_client::RpcClient;
use solana_program::pubkey::Pubkey;
use solana_sdk::{system_transaction, signature::{Keypair, Signature}};
const LAMPORTS_PER_SOL: f64 = 1000000000.0;
pub fn new_keypair() -> Keypair {
Keypair::new()
}
pub fn check_balance(rpc_client: &RpcClient, public_key: &Pubkey) -> Result<f64, Box<dyn Error>> {
Ok(rpc_client.get_balance(&public_key)? as f64 / LAMPORTS_PER_SOL)
}
pub fn airdrop(rpc_client: &RpcClient, pub_key: &Pubkey, amount_sol: f64) -> Result<Signature, Box<dyn Error>> {
let sig = rpc_client.request_airdrop(&pub_key, (amount_sol * LAMPORTS_PER_SOL) as u64)?;
loop {
let confirmed = rpc_client.confirm_transaction(&sig)?;
if confirmed {
break;
}
}
Ok(sig)
}
pub fn transfer(rpc_client: &RpcClient, sender_keypair: &Keypair, receiver_pub_key: &Pubkey, amount_sol: f64)
-> core::result::Result<Signature, Box<dyn Error>> {
let amount_lamports = (amount_sol * LAMPORTS_PER_SOL) as u64;
Ok(rpc_client.send_and_confirm_transaction(
&system_transaction::transfer(
&sender_keypair, &receiver_pub_key,
amount_lamports,
rpc_client.get_latest_blockhash()?))?)
}
Then we will work Phantom Wallet.
As you know, our smart contract will make transactions between created account and Phantom Wallet. So, you need to get your wallet keypair.
Run this command in the terminal:
solana-keygen recover 'prompt:?key=0/0' --outfile keypair.json
This command will create a new file called keypair.json. So, be sure you were in your project directory in your terminal when you have run this one.
You have to write the secret recovery phrase you have saved when creating a Phantom Wallet.
Then follow the steps terminal ask. Congratulations! keypair.json file has been created!
Changing network on Phantom Wallet
For work with our program, we will use Devnet network. So we need to set thin one on Phantom Wallet settings.
Then we will work with the main.rs file.
First of all, you need to import our functions from operations.rs to main.rs
mod operations;
You need to create an RPC client on main.rs, that will be injected in the functions: airdrop(), check_balance() and transfer().
const URL: &str = "https://api.devnet.solana.com";
Warning! We use Solana Devnet, so you need to run "solana config set --url https://api.devnet.solana.com" in your terminal
Let's create RPC client:
Import:
use solana_client::rpc_client::RpcClient;
Code:
let rpc_client = RpcClient::new(URL);
Then you need to create sender:
Import:
use solana_sdk::signature::Keypair;
let sender = lib::create_keypair();
Let's create receiver!
Open your keypair.json file and copy the content.
Create a receiver:
let receiver = Keypair::from_bytes(&[78,191,0,2,163,138,129,56,151,229,72,236,119,17,18,47,52,252,251,220,75,238,231,245,170,123,230,224,166,3,26,7,15,185,50,207,55,49,241,241,101,60,84,179,213,56,221,0,117,76,36,141,63,207,196,223,87,126,116,38,80,199,161,13]).unwrap();;
Import:
use solana_sdk::signer::Signer;
Then we request an airdrop for the sender with airdrop of 1 SOL. We can also check balance using check_balance. In the end, we need to call the transfer function.
Code:
if let Ok(_) = operations::airdrop(&rpc_client, &sender_key.pubkey(), 1.0) {
if let Ok(balance) = operations::check_balance(&rpc_client, &sender_key.pubkey()) {
println!("Sender balance now: {:?}", balance);
}
let transfer_amount = 0.5;
match operations::transfer(&rpc_client, &sender_key, &receiver_key.pubkey(), transfer_amount) {
Ok(_) => {
if let Ok(balance) = operations::check_balance(&rpc_client, &sender_key.pubkey()) {
println!("Sender balance after transaction: {:?}", balance);
}
if let Ok(balance) = operations::check_balance(&rpc_client, &receiver_key.pubkey()) {
println!("Receiver balance after transaction: {:?}", balance);
}
},
Err(err) => println!("Error: {:?}", err),
}
} else {
println!("Failed");
}
Your main.rs file needs to look like this:
mod operations;
use solana_sdk::signer::Signer;
use solana_client::rpc_client::RpcClient;
use solana_sdk::signature::Keypair;
const URL: &str = "https://api.devnet.solana.com";
fn main() {
let rpc_client = RpcClient::new(URL);
let sender_key = operations::new_keypair();
let receiver_key = Keypair::from_bytes(&[78,191,0,2,163,138,129,56,151,229,72,236,119,17,18,47,52,252,251,220,75,238,231,245,170,123,230,224,166,3,26,7,15,185,50,207,55,49,241,241,101,60,84,179,213,56,221,0,117,76,36,141,63,207,196,223,87,126,116,38,80,199,161,13]).unwrap();
if let Ok(_) = operations::airdrop(&rpc_client, &sender_key.pubkey(), 1.0) {
if let Ok(balance) = operations::check_balance(&rpc_client, &sender_key.pubkey()) {
println!("Sender balance now: {:?}", balance);
}
let transfer_amount = 0.5;
match operations::transfer(&rpc_client, &sender_key, &receiver_key.pubkey(), transfer_amount) {
Ok(_) => {
if let Ok(balance) = operations::check_balance(&rpc_client, &sender_key.pubkey()) {
println!("Sender balance after transaction: {:?}", balance);
}
if let Ok(balance) = operations::check_balance(&rpc_client, &receiver_key.pubkey()) {
println!("Receiver balance after transaction: {:?}", balance);
}
},
Err(err) => println!("Error: {:?}", err),
}
} else {
println!("Failed");
}
}
Congratulations! That is all! We have finished!
Now run in the terminal:
cargo run
GitHub repository with source code:
Other useful sources:
Got a problem that you can't handle?
The thing I like the most - Telegram chat and Solana Discord channels are always active! You can ask for help and other developers will help you. P.S On Discord, the channels are divided into subgroups, so it will be easier for you to find answers to certain topics.
Subscribe to my newsletter
Read articles from Yelyzaveta Dymchenko directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by