Solana from First Principles: Part 1

Het Dagli
7 min readApr 18, 2024

--

Solana can seem intimidating at first glance, especially for developers new to the world of blockchain.

With its unique architecture, parallel transaction processing, and unfamiliar terminology, it’s easy to feel overwhelmed. But what if I told you that understanding Solana is simpler than you think? That by breaking it down to its core components and reasoning from first principles, the elegance and power of Solana becomes clear?

In this series, my aim is to guide you on a journey to develop strong mental models and intuition about Solana development. We’ll peel back the layers of complexity and discover that, at its heart, Solana shares many similarities with concepts you may already be familiar with, like databases.

You don’t need any prior knowledge of Solana, Rust, or other blockchains to follow along. All that’s required is an open mind, curiosity, and some programming experience in any domain.

Let’s get started…

From Databases to Blockchains

At its core, Solana can be thought of as a massive key-value database. If you’re familiar with traditional databases like Redis or MongoDB, you’re already halfway there!

Key-Value on Solana(source)

We as devs write code to manipulate this database, similar to how you would write queries to interact with a conventional database.

In Solana, we refer to the logic to manipulate this database as Programs. In other blockchains, this logic is referred to as Smart Contracts.

The main distinction between blockchains like Solana and normal databases lies in who controls the database.

In the Web2 world, the owner of the database server has complete control over it. Take Facebook, for example, they have databases that store all user data, and they have full control over this data. Similarly, YouTube controls all the videos uploaded on its platform.

But in the world of blockchains, the database is community-owned. Instead of a single entity having control, it’s distributed among many individuals and organizations.

This is the main distinction between Web2 and Web3, in Web2 data is owned by the platform but in Web3 data is owned by the user.

Web1 v/s Web2 v/s Web3

The obvious next question is…

How does community ownership work practically?

The control each participant has is determined by the private keys they hold.

This is where the concept of public and private key pairs comes into play.

Your public key is like your account number, and your private key is like your password. If you have the private key associated with certain data (like token balances), you control that data. You can think of it as having the password to your account in Solana.

The actual derivation method for creating a public-private keypair is a little bit complex but it would mostly be abstracted away through wallets for end users and through JS libraries for devs.

So in summary, as a developer, you’re still interacting with a database, but the ownership model is decentralized. You write Programs to define the rules for how the database can be updated, and users interact with these Programs using their public and private keys.

This decentralized control is one of the key innovations of blockchains. It enables trustless, permissionless interaction and eliminates the need for intermediaries. As a developer, understanding this model is crucial for designing effective Programs and building decentralized applications.

Now that we understand the role of public and private keys, let’s…

Get our Hands Dirty

We’ll put this knowledge into practice by creating a simple JavaScript project that interacts with Solana.

We’ll use the `@solana/web3.js` library to create a wallet, airdrop some SOL tokens, and then transfer those tokens to a different wallet.

Here is the Replit link for the code: https://replit.com/@HetDagli/UnwillingBarrenQuadrilateral#index.js

Hit fork and run, you may get an ‘internal error’, this is due to Devnet RPC issues, if so, follow the next steps to run locally.

First, make sure you have Node.js installed. Then create a new directory for your project and initialize a new Node.js project:


mkdir solana-wallet-example
cd solana-wallet-example
npm init -y

Next, install the `@solana/web3.js` library:

npm install @solana/web3.js

Now, create a new file named `index.js` and add the following code:


const {
Connection,
PublicKey,
Keypair,
LAMPORTS_PER_SOL,
Transaction,
sendAndConfirmTransaction,
SystemProgram,
} = require("@solana/web3.js");


// Connect to the Solana devnet
const connection = new Connection("https://api.devnet.solana.com");


// Create a new wallet keypair
const wallet = Keypair.generate();


// Get the public key (address) of the wallet
const publicKey = wallet.publicKey;
console.log("Public Key:", publicKey.toBase58());


// Request an airdrop of 1 SOL
(async () => {
const airdropSignature = await connection.requestAirdrop(
publicKey,
LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature);
console.log("Airdrop completed!");


// Check the balance of the wallet
const balance = await connection.getBalance(publicKey);
console.log(`Wallet balance: ${balance / LAMPORTS_PER_SOL} SOL`);


// Create a new wallet to transfer tokens to
const recipientWallet = Keypair.generate();
const recipientPublicKey = recipientWallet.publicKey;


// Transfer 0.5 SOL to the recipient wallet
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: recipientPublicKey,
lamports: 0.5 * LAMPORTS_PER_SOL,
})
);


// Sign the transaction with the sender's private key
const signature = await sendAndConfirmTransaction(connection, transaction, [
wallet,
]);
console.log("Transfer completed. Transaction signature:", signature);


// Check the balance of both wallets
const senderBalance = await connection.getBalance(publicKey);
const recipientBalance = await connection.getBalance(recipientPublicKey);
console.log(`Sender balance: ${senderBalance / LAMPORTS_PER_SOL} SOL`);
console.log(`Recipient balance: ${recipientBalance / LAMPORTS_PER_SOL} SOL`);
})();

Run the logic using:

node index.js

Let’s break down the code:

Creating a Wallet

We created a new wallet keypair using the `Keypair.generate()` function provided by the `@solana/web3.js` library.

// Create a new wallet keypair
const wallet = Keypair.generate();
// Get the public key (address) of the wallet
const publicKey = wallet.publicKey;
console.log("Public Key:", publicKey.toBase58());

As we learned earlier, a keypair consists of a public key and a private key. The public key serves as the wallet’s address, which can be shared with others to receive SOL tokens. The private key would be later used to sign transactions and should be kept secret.

Interacting with Solana Devnet RPC

To interact with the Solana blockchain, we need to connect to a cluster. In this example, we connect to the Solana devnet, which is a test network that allows developers to experiment with Solana’s features without risking real funds.

// Connect to the Solana devnet
const connection = new Connection("https://api.devnet.solana.com");

The `Connection` object establishes a connection to the Solana devnet RPC (Remote Procedure Call) endpoint. RPC is a protocol that allows a program to request services from another program located on a different computer.

Requesting an Airdrop

To get some SOL tokens for testing purposes, we can request an airdrop from the Solana devnet faucet.

// Request an airdrop of 1 SOL
const airdropSignature = await connection.requestAirdrop(
publicKey,
LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature);
console.log("Airdrop completed!");

The `requestAirdrop` function sends a request to the Solana devnet to transfer 1 SOL (1 billion lamports) to our wallet’s public key. We then wait for the transaction to be confirmed using `confirmTransaction`.

SOL is the native token of the Solana blockchain, and it is used to pay for transaction fees and interact with Solana programs.

You can think of these steps as if we have added a new row in the Solana key-value database, the key is the account number and the value is the data which in our case is the 1 SOL that we airdropped.

Transferring SOL Tokens

To demonstrate transferring SOL tokens, we create another wallet keypair to serve as the recipient.

// Create a new wallet to transfer tokens to
const recipientWallet = Keypair.generate();
const recipientPublicKey = recipientWallet.publicKey;

Using the System Program

Solana provides a set of native programs like System Program out of the box, which allows us to perform basic operations like creating accounts and transferring tokens.

// Transfer 0.5 SOL to the recipient wallet
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: recipientPublicKey,
lamports: 0.5 * LAMPORTS_PER_SOL,
})
);

Here, we create a new transaction and add an instruction to transfer 0.5 SOL from our wallet to the recipient wallet using the `SystemProgram.transfer` function.

An instruction is the most basic unit of operation in Solana. It specifies a single action to be performed, such as transferring tokens, interacting with a program, or modifying account data.

A transaction is a collection of one or more instructions that are executed atomically. If any of the instructions within a transaction fail, the entire transaction is rolled back as if no changes were made.

In our case, we are constructing a transaction with 1 transfer instruction.

The `SystemProgram.transfer` function is defined as follows:

transfer(
from: PublicKey,
to: PublicKey,
amount: number | bigint,
): TransactionInstruction;

It takes the source and destination public keys, along with the amount of lamports to transfer, and returns a `TransactionInstruction` object that can be added to a transaction.

Signing and Sending the Transaction

After creating the transaction, we need to sign it with our wallet’s private key to prove that we have the authority to transfer the tokens.

// Sign the transaction with the sender's private key
const signature = await sendAndConfirmTransaction(connection, transaction, [
wallet,
]);
console.log("Transfer completed. Transaction signature:", signature);

The `sendAndConfirmTransaction` function sends the signed transaction to the Solana cluster and waits for confirmation. It returns the transaction signature, which can be used to track the transaction on the blockchain.

The transaction signature can be searched on a block explorer like solscan.io, select devnet from the top right corner; the final transaction would look something like this:

We have now covered the basic concepts of creating wallets, requesting airdrops, and transferring SOL tokens on Solana using JS. The `@solana/web3.js` library provides a convenient way to interact with the Solana blockchain, abstracting away many of the low-level details.

In Part 2, we will deep dive into Solana Accounts, and PDAs and write our first-ever program on Solana.

You can also look at additional resources to learn about Solana here.

--

--