Skip to content

Instantly share code, notes, and snippets.

@AlmostEfficient
Last active January 18, 2024 08:38
Show Gist options
  • Save AlmostEfficient/d41bf4e0a2bf356556ef2bb85b59f2d7 to your computer and use it in GitHub Desktop.
Save AlmostEfficient/d41bf4e0a2bf356556ef2bb85b59f2d7 to your computer and use it in GitHub Desktop.
Solana quickstart guides
title description keywords
CLI & SDK quickstart
This quickstart guide will show you how to use the CLI and the SDK to send SOL.
solana cli send transaction
send SOL using SDK
transfer SOL using CLI
set up Solana Javascript SDK
set up Solana web3 API
tutorial
intro to solana development
blockchain developer
blockchain tutorial
web3 developer

This quickstart guide will show you how to use the Solana CLI and the Javascript SDK to send SOL, including environment setup.

What you will learn

  • How to use the Solana CLI to transfer SOL
  • How to set up the Javascript SDK
  • How to load a local filesystem wallet using the SDK
  • How to use the SDK to transfer SOL

Prerequisites

This guide assumes you've completed local development setup and have a filesystem wallet with SOL in it. Running solana config get should output the following:

$ solana config get
Config File: /Users/NAME/.config/solana/cli/config.yml
RPC URL: http://localhost:8899
WebSocket URL: ws://localhost:8900/ (computed)
Keypair Path: /Users/NAME/.config/solana/id.json
Commitment: confirmed

Make sure you have solana-test-validator running in one terminal window. Running solana balance in a second window should print out:

2 SOL

Use the CLI to transfer SOL

We'll start by transferring SOL from one account to another using the CLI.

Create a new wallet keypair

You need a recipient address to send SOL to. Create a new keypair and record its public key:

solana-keygen new --no-passphrase --no-outfile

The output will contain the address after the text pubkey:.

pubkey: GKvqsuNcnwWqPzzuhLmGi4rzzh55FhJtGizkhHaEJqiV

Send SOL to the new address

We'll use the transfer command to send 0.05 SOL to the new address.

# solana transfer <RECIPIENT_ADDRESS> <AMOUNT>
solana transfer GKvqsuNcnwWqPzzuhLmGi4rzzh55FhJtGizkhHaEJqiV 0.05

This will give you an error:

Error: The recipient address (GKvqsuNcnwWqPzzuhLmGi4rzzh55FhJtGizkhHaEJqiV) is not funded. Add `--allow-unfunded-recipient` to complete the transfer

The recipient address has not been "initialized" yet: it has never held any SOL and does not have enough for rent, so the Solana network does not know about it yet.

Add the --allow-unfunded-recipient flag to the command to send SOL to it:

solana transfer GKvqsuNcnwWqPzzuhLmGi4rzzh55FhJtGizkhHaEJqiV 0.05 --allow-unfunded-recipient

This will output a transaction signature. To view transaction details, you can run solana confirm -v <TRANSACTION_SIGNATURE>.

Finally, check the balance of the new address with:

solana balance GKvqsuNcnwWqPzzuhLmGi4rzzh55FhJtGizkhHaEJqiV

You now know how to transfer SOL from one account to another using the CLI!

For more information on the transfer command, you can run solana help transfer in your terminal.

Using the SDK to transfer SOL

Transferring SOL using the CLI is impractical for most applications. The Solana SDK is the primary tool used for such tasks. We'll use it to build a script that transfers SOL from one account to another.

Prerequisites

Make sure you have Node.js installed. You can check this by running node -v in your terminal. You should see a version number printed out.

Setup a local client

Start by creating a node project and installing the Solana SDK. Run this in your terminal:

mkdir send-sol
cd send-sol
npm init -y
npm install @solana/web3.js

Connect to file system wallet

Create a file called main.js and add the following code:

const web3 = require("@solana/web3.js");
const os = require("os");
const fs = require("fs");
const path = require("path");

const CLUSTER = "http://127.0.0.1:8899";
const connection = new web3.Connection(CLUSTER, "confirmed");
const recipient = new web3.PublicKey(
  "9B5XszUGdMaxCZ7uSQhPzdks5ZQSmWxrmzCSvtJ6Ns6g",
);

This code imports the Solana SDK and creates a connection to the local Solana cluster. It also defines the recipient address we'll be sending SOL to, which is the address of the faucet on the devnet. You can swap this out for any other address you want to send SOL to.

Load the filesystem wallet

Next, we need to load the keypair from the file system wallet. This is the keypair you created when you ran solana-keygen new in the local development quickstart. Add this code below the previous block:

// Import the existing keypair from the file system wallet
const keypairPath = path.join(os.homedir(), ".config", "solana", "id.json");
let keypair;

try {
  keypair = web3.Keypair.fromSecretKey(
    Buffer.from(JSON.parse(fs.readFileSync(keypairPath, "utf-8"))),
  );
} catch (err) {
  console.error("Error while loading the keypair:", err);
  process.exit(1);
}

console.log("Local keypair address:", keypair.publicKey.toBase58());

Create the transaction and send it

Finally, we'll use the built in transfer function to send 0.05 SOL to the recipient address.

async function transferSol(connection, payer, recipient) {
  console.log("Transferring 0.05 SOL...");

  // Create a transaction and add a system instruction to transfer SOL
  const transaction = new web3.Transaction().add(
    // This system instruction takes the sender's address, the recipient's address, and the amount to transfer
    web3.SystemProgram.transfer({
      fromPubkey: payer.publicKey,
      toPubkey: recipient,
      lamports: web3.LAMPORTS_PER_SOL * 0.005, // The amount of SOL must be in Lamports, the smallest unit of SOL
    }),
  );

  try {
    // Sign and send the transaction to the local cluster using the keypair
    const signature = await web3.sendAndConfirmTransaction(
      connection,
      transaction,
      [payer],
    );
    console.log(
      "View this transaction at:",
      `https://explorer.solana.com/tx/${signature}?cluster=custom`,
    );

    const newBalance = await connection.getBalance(payer.publicKey);
    console.log("New balance is", newBalance / web3.LAMPORTS_PER_SOL);
  } catch (err) {
    console.error("Error while transferring:", err);
  }
}

transferSol(connection, keypair, recipient).catch(console.error);

This code creates a transaction with one instruction that transfers 0.05 SOL from the keypair's address to the recipient address. It then signs and sends the transaction to the local cluster. Finally, it prints out the new balance of the keypair's address.

You can run this script with node main.js. You should see the following output:

Local keypair address: 8r97HfVyCprL98Cc1a4QgwAJ7eijqzQwrgDazxdSqLf8
Transferring 0.05 SOL...
New balance is 0.90

Check out the web3-examples repo for more examples of interacting with Solana programs using the SDK.

Going beyond localhost

Everything you've done so far has been on your local machine. The wallet balances will disappear when you exit the terminal running the solana-test-validator process.

You can send SOL on the devnet or the mainnet by changing your RPC URL, pointing the script to the a different cluster and running it again.

Update your CLI and get devnet SOL:

# This configures your CLI to use the devnet cluster
solana config set --url devnet
solana airdrop 2

In main.js, change the CLUSTER variable to devnet:

const CLUSTER = web3.clusterApiUrl("devnet");

Run the script again:

node main.js

You've now sent SOL on the devnet! Make sure you change the cluster on the explorer to devnet to see the transaction.

Next steps

See the links below to learn more about the CLI and the SDK:

title description keywords
Create a token
This quickstart guide will show you how to use the spl-token CLI to create a token.
create token on solana
make coin on solana
set up spl-token cli
spl-token tutorial
intro to solana development
blockchain developer
blockchain tutorial
web3 developer

This quickstart guide will show you how to use the spl-token CLI to create a token on the devnet, mint some tokens, and transfer them to another address.

Prerequisites

This guide assumes you've completed local development setup and have a filesystem wallet created.

You'll also need to have cargo installed. The recommended way to install it is with rustup. You can check if you have it installed by running cargo --version in a terminal window.

Configure the CLI to use devnet

We'll be using the devnet for this guide. Set the Solana CLI to use the devnet by running:

solana config set --url devnet

Next, run solana airdrop 2 to get some SOL for the devnet. You only need about 0.5 SOL to create a token, check your balance by running solana balance.

The Solana Program Library

Tokens on Solana are created using the Token Program, which is part of the Solana Program Library - a set of programs maintained by Solana Labs.

How tokens are made

To create a fungible token on Solana, you interact with the SPL token program to create a mint account. The mint account contains the token's metadata, including the token's supply and decimals. Think of it like a factory that can mint new tokens.

Users' wallets can't directly own tokens on Solana. For each token, a user has a token account. The token account stores the balance of a token and is linked to the user's wallet.

So to create a token and add them into your wallet, you need to:

  1. Create a token mint account
  2. Create a token account for your wallet
  3. Mint tokens into your token account

Here's a simplified visual representation of this relationship: Token mint and token account relationship

Install the spl-token-cli

The SPL has a CLI tool and a Javascript SDK that lets you interact with the on-chain programs. We'll use the CLI tool. Install it by running:

cargo install spl-token-cli

This may take a few minutes to run.

Create a fungible token

To create fungible token, you'll first need to create a mint account. Run this command to interact with the token program:

spl-token create-token

This will give you:

Creating token FpFppjxbnSwX7kBX9X1K5FZLG1N4qnJxAxj1D7VB7gk9 under program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA

Address:  FpFppjxbnSwX7kBX9X1K5FZLG1N4qnJxAxj1D7VB7gk9
Decimals:  9

Signature: 382YHasjtx9CCpu55hL9x8s3ZeY77Jmw9RKFd3y7SSf9HXqw7kUToMLQLuaUMf6mMZqs65rBPNaYhmQqwejntaKG

"Address" here is the token mint account - it contains the token's metadata and can be used to mint new tokens - copy this. You can change the metadata, such as the decimal count, later on.

Mint and transfer tokens

All tokens created by spl-token have no supply by default - zero of them exist. To mint new tokens, you have to first create a token account to hold them. Create it by running:

# spl-token create-account <token-mint-address>
spl-token create-account FpFppjxbnSwX7kBX9X1K5FZLG1N4qnJxAxj1D7VB7gk9

This creates a new token account for your wallet for the token you just created, printing out the address:

Creating account DkoGBArBHqNfbgdzrCAufe51uvn7SWSnSKEpwDYcU7F2

Signature: 4HEqkQY3PM1g9XKbpZ6PfKX3i55nGbg2YnfQcdhyB64JMtXPQSyEFCBSUHpSXF2stsRjkQ1caaYofSrVE73VvdWR

Next, mint some tokens for yourself by running:

# spl-token mint <token-mint-address> <amount>
spl-token mint FpFppjxbnSwX7kBX9X1K5FZLG1N4qnJxAxj1D7VB7gk9 1000

This will mint 1000 tokens into the token account you just created for your wallet.

You can now transfer tokens from your wallet to another wallet by running:

spl-token transfer --fund-recipient <token-mint-address> <amount> <destination-address>

You can check the balance of your wallet for any token using:

spl-token balance <token-mint-address>

To view all the token accounts you own and their balances:

spl-token accounts

Summary

Here are all the commands you need to run to create a token and mint it:

spl-token create-token
spl-token create-account <token-mint-address>
spl-token mint <token-mint-address> <amount>

And to transfer tokens:

spl-token transfer --fund-recipient <token-mint-address> <amount> <destination-address>

Next steps

You now know how to use the spl-token CLI to mint a token. It's possible to do this and much more with the spl-token Typescript library.

title description keywords
Create and send a transaction
This quickstart guide will show you how to build a transaction from scratch using the Solana SDK in Javascript.
Solana SDK
install solana SDK
create transaction solana
solana send transaction
create sdk transaction
tutorial
intro to solana development
blockchain developer
blockchain tutorial
web3 developer

This quickstart guide will show you how to use the Solana Javascript SDK to create, sign and send a transaction on the devnet, including environment setup.

What you will learn

  • How to load a local filesystem wallet using the SDK
  • How to interact with custom programs on the devnet
  • How to create instructions that include custom data
  • How to use the SDK to create, sign and send a transaction

Prerequisites

This guide assumes you've completed local development setup and have a filesystem wallet set up.

Make sure you have Node.js installed. You can check this by running node -v in your terminal. You should see a version number printed out.

Set up your environment

Configure the CLI to use devnet

We'll be using the devnet for this guide. Set the Solana CLI to use the devnet by running:

solana config set --url devnet

Next, run solana airdrop 2 to get some SOL for the devnet. If this fails, reduce the amount by using solana airdrop 1. You need less than 0.01 SOL to send a transaction, check your balance by running solana balance.

Setup a local client

Start by creating a node project and installing the Solana SDK. Run this in your terminal:

mkdir create-tx
cd create-tx
npm init -y
npm install @solana/web3.js

Open the solana-tx directory in your favorite code editor.

Connect to the devnet and load a local wallet

Create a file called main.js and add the following code:

const web3 = require("@solana/web3.js");
const os = require("os");
const fs = require("fs");
const path = require("path");

const CLUSTER = web3.clusterApiUrl("devnet");
const connection = new web3.Connection(CLUSTER, "confirmed");
const PROGRAM_ID = new web3.PublicKey(
  "7UCpQWEgiX7nv4sibshx93JfEictrRcgrjhgTqGn77XK",
);

const keypairPath = path.join(os.homedir(), ".config", "solana", "id.json");
let keypair;

try {
  keypair = web3.Keypair.fromSecretKey(
    Buffer.from(JSON.parse(fs.readFileSync(keypairPath, "utf-8"))),
  );
} catch (err) {
  console.error("Error while loading the keypair:", err);
  process.exit(1);
}

console.log("Local keypair address:", keypair.publicKey.toBase58());

This code imports the Solana SDK, creates a connection to the Solana Devnet, and loads the keypair from the filesystem wallet. The PROGRAM_ID provided is for a program deployed on the devnet that echoes back whatever data we send to it.

Running the script as it is with node main.js will print out the address of your local keypair.

Create a transaction

We're now ready to create a transaction. Add this function below your existing code:

async function echoProgram(connection, payer, programId) {
  // Format instruction data as a buffer of UTF-8 bytes
  const instructionData = Buffer.from("Hello Solana Devnet");

  const instruction = new web3.TransactionInstruction({
    // An array of addresses that the instruction will read from, or write to
    keys: [
      {
        pubkey: payer.publicKey,
        isSigner: true,
        isWritable: false,
      },
    ],

    // The program we're interacting with
    programId,

    // Instruction data in bytes
    data: instructionData,
  });

  // You can create another instruction here if you want to echo a second time or interact with another program

  // Create a transaction and add the instruction we just defined
  // We can add multiple instructions here that are run sequentially
  const transaction = new web3.Transaction().add(instruction);

  const signature = await web3.sendAndConfirmTransaction(
    connection,
    transaction,
    [payer],
  );

  console.log(
    `You can view your transaction on the Solana Transaction https://explorer.solana.com/tx/${signature}?cluster=devnet`,
  );
}

echoProgram(connection, keypair, PROGRAM_ID).catch(console.error);

This function creates, signs and sends a transaction to the devnet. The transaction contains an instruction to interact with the echo program we defined earlier.

Build the instruction

The first thing we're doing is formatting our instruction data into a buffer of UTF-8 bytes. While UTF-8 itself is not "binary", it is a way of converting something (like text) into a binary format that can be more efficiently stored and transmitted by computer systems. This is called "serialization".

This is done for a few reasons:

  • Speed: It's more efficient to store and transmit data in bytes
  • Interoperability: Programs written in different languages can interact with each other because they all use the same format
  • Uniformity: It's easier to work with data in a consistent format
  • Security: It's more secure to use a consistent format to prevent ambiguity in interpretation of the data

The instruction data is then passed to the TransactionInstruction constructor along with the program ID and the keypair of the payer. The payer is the account that will pay for the transaction fees. In this case, we're using the keypair from our local wallet.

Create, sign and send the transaction

Next, we create a transaction and add the instruction we just defined. We can add multiple instructions here that are run sequentially. Finally, we sign and send the transaction to the devnet.

Run this script with node main.js and you should see a link to the transaction on the Solana Explorer. Scroll down to the bottom and you'll see your message in the program instruction logs!

> Program logged: "Received data: [72, 101, 108, 108, 111, 32, 83, 111, 108, 97, 110, 97, 32, 68, 101, 118, 110, 101, 116]"
> Program logged: "Echo: Hello Solana Devnet"
> Program consumed: 6837 of 200000 compute units
> Program returned success

Congratulations!

You can now build transactions that take in custom instruction data! The majority of blockchain development is interacting with existing programs. You can build hundreds of apps that just interact with all the programs already out there.

Next steps

Check out these links to learn more about transactions and programs:

title description keywords
Create a dApp client
This quickstart guide will show you how to set up a client that lets anyone interact with a custom program on the Solana Devnet
Solana client
Nextjs app Solana
Create Solana dApp
Build Solana client
Deploy the Solana dApp scaffold
deploy a Solana dApp
Solana dApp tutorial
intro to Solana development

This quickstart guide will show you how to set up a client that lets anyone interact with an existing program on the Solana Devnet. We'll use the create-solana-dapp CLI to set up a Next.js app and configure it to send a message to the Echo program.

What you will learn

  • How to use create-solana-dapp to set up a client
  • Configuring the Solana dApp scaffold
  • Interacting with programs on the devnet
  • Sending instruction data from clients to programs

Prerequisites

Make sure you have Node.js installed. You can check that it's available by running node -v in your terminal. You should see a version number printed out.

Set up the client

The Solana dApp scaffold

The easiest and quickest way to get started building a web app client is using one of the available dApp scaffolds. These are available for Next.js, Vue and Svelte. We'll use the Next.js scaffold for this guide.

Start by setting up the Next.js scaffold using create-solana-dapp by running this in your terminal:

npx create-solana-dapp solana-dapp

The first time you run this, it will ask you if you want to install create-solana-dapp. Enter y and hit enter. This will install the CLI globally on your machine and run it. It may take a few minutes as it will also install all the dependencies for the scaffold.

Open up the solana-dapp folder in your code editor and navigate to the app folder inside it. You can ignore the program folder for now. Start the template by running npm run dev in your terminal. This will start the Next.js app on localhost:3000.

The wallet adapter

The Solana Wallet adapter is a library that makes it easy to connect to any wallets on Solana that support the official wallet standard. This means you don't need to implement separate APIs for different wallets - the wallet adapter will handle that for you.

Open up src/contexts/ContextProvider.tsx in your code editor. You'll find the ContextProvider component that wraps the entire app, making the wallet adapter available to all pages and children components in the app.

The wallet adapter is initialized with a list of wallets that the user can choose from. By default the scaffold uses the UnsafeWalletAdapter which is a wallet that is only meant for development purposes. Change it to a list of wallets that you want to support in your app like this:

// Line 3
import {
    SolflareWalletAdapter,
} from '@solana/wallet-adapter-wallets';

...

// Line 30
    const wallets = useMemo(
        () => [
            new SolflareWalletAdapter(),
        ],
        [network]
    );

This will enable the Solflare wallet in your app as well as any wallets that are registered as a standard wallet and installed in the user's browser (such as Phantom and Glow).

Interacting with programs

The scaffold has several components that you can use to interact with programs and send transactions. When exploring it, start with just one component and stick with it so you don't get overwhelmed.

Set up the SendEcho component

To interact with the echo program using the client, we'll need to send a transaction that is signed and funded by the user. Create a file called SendEcho.tsx in app/src/components and add the following code to it:

import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import {
  Transaction,
  TransactionSignature,
  TransactionInstruction,
  PublicKey,
} from "@solana/web3.js";
import { FC, useCallback } from "react";
import { notify } from "../utils/notifications";

export const SendEcho: FC = () => {
  const { connection } = useConnection();
  const { publicKey, sendTransaction } = useWallet();
  const programId = new PublicKey(
    "7UCpQWEgiX7nv4sibshx93JfEictrRcgrjhgTqGn77XK",
  );

  const onClick = useCallback(async () => {
    // Transaction code will go here
  }, []);

  return (
    <div className="flex flex-row justify-center">
      <div className="relative group items-center">
        <div
          className="m-1 absolute -inset-0.5 bg-gradient-to-r from-indigo-500 to-fuchsia-500 
                rounded-lg blur opacity-20 group-hover:opacity-100 transition duration-1000 group-hover:duration-200 animate-tilt"
        ></div>
        <button
          className="group w-60 m-2 btn animate-pulse bg-gradient-to-br from-indigo-500 to-fuchsia-500 hover:from-white hover:to-purple-300 text-black"
          onClick={onClick}
          disabled={!publicKey}
        >
          <div className="hidden group-disabled:block ">
            Wallet not connected
          </div>
          <span className="block group-disabled:hidden">Send Echo</span>
        </button>
      </div>
    </div>
  );
};

This has the same styling and structure as the other transaction components in the scaffold. The imports have been updated for this transaction and the programId of the Echo program has been added.

To test it, we'll put it in app/src/views/basics/index.tsx alongside the other basics:

import { FC } from "react";
import { SignMessage } from "../../components/SignMessage";
import { SendTransaction } from "../../components/SendTransaction";
import { SendVersionedTransaction } from "../../components/SendVersionedTransaction";
import { SendEcho } from "../../components/SendEcho";

export const BasicsView: FC = ({}) => {
  return (
    <div className="md:hero mx-auto p-4">
      <div className="md:hero-content flex flex-col">
        <h1 className="text-center text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-br from-indigo-500 to-fuchsia-500 mt-10 mb-8">
          Basics
        </h1>
        <div className="text-center">
          <SignMessage />
          <SendTransaction />
          <SendVersionedTransaction />
          {/* Added here */}
          <SendEcho />
        </div>
      </div>
    </div>
  );
};

Head over to localhost:3000/basics and you'll see the Send Echo button. It does nothing right now as we haven't added the transaction code.

Create a transaction on the client

The code for this will be largely the same as the transaction quickstart, however, instead of loading a keypair from a file, we'll use the wallet adapter to get the user to sign and pay for the transaction.

Add the following code to the onClick function on line 18:

const onClick = useCallback(async () => {
  if (!publicKey) {
    notify({ type: "error", message: `Wallet not connected!` });
    console.log("error", `Send Transaction: Wallet not connected!`);
    return;
  }

  let signature: TransactionSignature = "";
  const message = prompt("Enter message for the blockchain:");

  if (!message) {
    notify({ type: "error", message: `No message entered!` });
    console.log("error", `No message entered!`);
    return;
  }

  try {
    // Format the message as bytes
    const messageBytes = Buffer.from(message);

    console.log("Message bytes:", messageBytes);

    const instructions = new TransactionInstruction({
      keys: [{ pubkey: publicKey, isSigner: true, isWritable: false }],
      programId,
      data: messageBytes,
    });

    let latestBlockhash = await connection.getLatestBlockhash();

    const transaction = new Transaction().add(instructions);

    signature = await sendTransaction(transaction, connection);

    await connection.confirmTransaction(
      { signature, ...latestBlockhash },
      "confirmed",
    );

    notify({
      type: "success",
      message: "Transaction successful!",
      txid: signature,
    });
  } catch (error: any) {
    notify({
      type: "error",
      message: `Transaction failed!`,
      description: error?.message,
      txid: signature,
    });
    console.log("error", `Transaction failed! ${error?.message}`, signature);
    return;
  }
}, [publicKey, notify, connection, sendTransaction]);

This asks the user for a message they want to send to the echo program, formats it, creates a transaction and prompts the user to approve it via their wallet.

Head over to the basics page and try it out. You should see a success toast message on the bottom left corner on your screen which contains a link to the transaction.

You now know how to create a transaction with custom instruction data via a client!

Next steps

The scaffold is loaded with useful components and utilities that you will find useful when building dapps. Check out the SignMessage.tsx component to see how to sign messages with the wallet adapter, or see the links below to learn more about Solana clients:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment