gill
javascript/typescript client library for interacting with the Solana blockchain
Overview
Welcome to gill
, a JavaScript/TypeScript client library for interacting with the
Solana blockchain. You can use it to build Solana apps in Node, web, React
Native, or just about any other JavaScript environment.
Gill is built on top of the modern javascript libraries for Solana built by Anza and used in
(@solana/web3.js v2). By utilizing the same types and
functions under the hood, gill
is compatible with web3.js.
Installation
Install gill
with your package manager of choice:
npm install gill
pnpm add gill
yarn add gill
Quick start
You can also find some NodeJS specific helpers like:
For troubleshooting and debugging your Solana transactions, see Debug mode below.
You can also consult the documentation for Anza's
JavaScript client library for more information and
helpful resources.
Create a Solana RPC connection
Create a Solana rpc
and rpcSubscriptions
client for any RPC URL or standard Solana network
moniker (i.e. devnet
, localnet
, mainnet
etc).
import { createSolanaClient } from "gill";
const { rpc, rpcSubscriptions, sendAndConfirmTransaction } = createSolanaClient({
urlOrMoniker: "mainnet",
});
Using the Solana moniker will connect to the public RPC endpoints. These are subject to rate
limits and should not be used in production applications. Applications should find their own RPC
provider and the URL provided from them.
To create an RPC client for your local test validator:
import { createSolanaClient } from "gill";
const { rpc, rpcSubscriptions, sendAndConfirmTransaction } = createSolanaClient({
urlOrMoniker: "localnet",
});
To create an RPC client for an custom RPC provider or service:
import { createSolanaClient } from "gill";
const { rpc, rpcSubscriptions, sendAndConfirmTransaction } = createSolanaClient({
urlOrMoniker: "https://private-solana-rpc-provider.com",
});
Making Solana RPC calls
After you have a Solana rpc
connection, you can make all the
JSON RPC method calls directly off of it.
import { createSolanaClient } from "gill";
const { rpc } = createSolanaClient({ urlOrMoniker: "devnet" });
const slot = await rpc.getSlot().send();
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
The rpc
client requires you to call .send()
on the RPC method in order to actually send the
request to your RPC provider and get a response.
You can also include custom configuration settings on your RPC calls, like using a JavaScript
AbortController, by passing it
into send()
:
import { createSolanaClient } from "gill";
const { rpc } = createSolanaClient({ urlOrMoniker: "devnet" });
const abortController = new AbortController();
function onUserNavigateAway() {
abortController.abort();
}
const slot = await rpc.getSlot().send({ abortSignal: abortController.signal });
Create a transaction
Quickly create a Solana transaction:
Note: The feePayer
can be either an Address
or TransactionSigner
.
import { createTransaction } from "gill";
const transaction = createTransaction({
version,
feePayer,
instructions,
});
To create a transaction while setting the latest blockhash:
import { createTransaction } from "gill";
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transaction = createTransaction({
version,
feePayer,
instructions,
latestBlockhash,
});
To create a transaction while setting the latest blockhash:
import { createTransaction } from "gill";
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transaction = createTransaction({
version,
feePayer,
instructions,
latestBlockhash,
});
Signing transactions
If your transaction already has the latest blockhash lifetime set via createTransaction
:
import {
signTransactionMessageWithSigners,
setTransactionMessageLifetimeUsingBlockhash,
} from "gill";
const signedTransaction = await signTransactionMessageWithSigners(transaction);
If your transaction does NOT have the latest blockhash lifetime set via createTransaction
, you
must set the latest blockhash lifetime before (or during) the signing operation:
import {
signTransactionMessageWithSigners,
setTransactionMessageLifetimeUsingBlockhash,
} from "gill";
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const signedTransaction = await signTransactionMessageWithSigners(
setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
);
Sending and confirming transactions
To send and confirm a transaction to the blockchain, you can use the sendAndConfirmTransaction
function initialized from createSolanaClient
.
import { createSolanaClient, createTransaction, signTransactionMessageWithSigners } from "gill";
const { rpc, rpcSubscriptions, sendAndConfirmTransaction } = createSolanaClient({
urlOrMoniker: "mainnet",
});
const transaction = createTransaction(...);
const signedTransaction = await signTransactionMessageWithSigners(transaction);
const signature: string = getSignatureFromTransaction(signedTransaction);
await sendAndConfirmTransaction(signedTransaction)
If you would like more fine grain control over the configuration of the sendAndConfirmTransaction
functionality, you can include configuration settings:
await sendAndConfirmTransaction(signedTransaction, {
commitment: "confirmed",
skipPreflight: true,
maxRetries: 10n,
...
});
Get the signature from a signed transaction
After you already have a partially or fully signed transaction, you can get the transaction
signature as follows:
import { getSignatureFromTransaction } from "gill";
const signature: string = getSignatureFromTransaction(signedTransaction);
console.log(signature);
Note: After a transaction has been signed by at least one Signer, it will have a transaction
signature (aka transaction id). This is due to Solana transaction ids are the first item in the
transaction's signatures
array. Therefore, client applications can know the signature before it
is even sent to the network for confirmation.
Get a Solana Explorer link for transactions, accounts, or blocks
Craft a Solana Explorer link for transactions, accounts, or blocks on any cluster.
When no cluster
is provided in the getExplorerLink
function, it defaults to mainnet
.
Get a Solana Explorer link for a transaction
To get an explorer link for a transaction's signature (aka transaction id):
import { getExplorerLink } from "gill";
const link: string = getExplorerLink({
transaction:
"4nzNU7YxPtPsVzeg16oaZvLz4jMPtbAzavDfEFmemHNv93iYXKKYAaqBJzFCwEVxiULqTYYrbjPwQnA1d9ZCTELg",
});
If you have a partially or fully signed transaction, you can get the Explorer link before even
sending the transaction to the network:
import {
getExplorerLink,
getSignatureFromTransaction
signTransactionMessageWithSigners,
} from "gill";
const signedTransaction = await signTransactionMessageWithSigners(...);
const link: string = getExplorerLink({
transaction: getSignatureFromTransaction(signedTransaction),
});
Get a Solana Explorer link for an account
To get an explorer link for an account on Solana's devnet:
import { getExplorerLink } from "gill";
const link: string = getExplorerLink({
cluster: "devnet",
account: "nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5",
});
To get an explorer link for an account on your local test validator:
import { getExplorerLink } from "gill";
const link: string = getExplorerLink({
cluster: "localnet",
account: "11111111111111111111111111111111",
});
Get a Solana Explorer link for a block
To get an explorer link for a block:
import { getExplorerLink } from "gill";
const link: string = getExplorerLink({
cluster: "mainnet",
block: "242233124",
});
Calculate minimum rent for an account
To calculate the minimum rent balance for an account (aka data storage deposit fee):
import { getMinimumBalanceForRentExemption } from "gill";
const rent: bigint = getMinimumBalanceForRentExemption();
import { getMinimumBalanceForRentExemption } from "gill";
const rent: bigint = getMinimumBalanceForRentExemption(50 );
Note: At this time, the minimum rent amount for an account is calculated based on static values in
the Solana runtime. While you can use the getMinimumBalanceForRentExemption
RPC call on your
connection to fetch this value, it will result in a network
call and subject to latency.
Node specific imports
The gill
package has specific imports for use in NodeJS server backends and/or serverless
environments which have access to Node specific APIs (like the file system via node:fs
).
import { ... } from "gill/node"
Loading a keypair from a file
import { loadKeypairSignerFromFile } from "gill/node";
const signer = await loadKeypairSignerFromFile();
console.log("address:", signer.address);
Load a KeyPairSigner
from a filesystem wallet json file, like those output from the
Solana CLI (i.e. a JSON array
of numbers).
By default, the keypair file loaded is the Solana CLI's default keypair: ~/.config/solana/id.json
To load a Signer from a specific filepath:
import { loadKeypairSignerFromFile } from "gill/node";
const signer = await loadKeypairSignerFromFile("/path/to/your/keypair.json");
console.log("address:", signer.address);
Debug mode
Within gill
, you can enable "debug mode" to automatically log additional information that will be
helpful in troubleshooting your transactions.
Debug mode is disabled by default to minimize additional logs for your application. But with its
flexible debug controller, you can enable it from the most common places your code will be run.
Including your code itself, NodeJS backends, serverless functions, and even the in web browser
console itself.
Some examples of the existing debug logs that gill
has sprinkled in:
- log the Solana Explorer link for transactions as you are sending them
- log the base64 transaction string to troubleshoot via
mucho inspect
or Solana
Explorer's Transaction Inspector
How to enable debug mode
To enable debug mode, set any of the following to true
or 1
:
process.env.GILL_DEBUG
global.__GILL_DEBUG__
window.__GILL_DEBUG__
(i.e. in your web browser's console)- or manually set any debug log level (see below)
To set a desired level of logs to be output in your application, set the value of one of the
following (default: info
):
process.env.GILL_DEBUG_LEVEL
global.__GILL_DEBUG_LEVEL__
window.__GILL_DEBUG_LEVEL__
(i.e. in your web browser's console)
The log levels supported (in order of priority):
debug
(lowest)info
(default)warn
error
Custom debug logs
Gill also exports the same debug functions it uses internally, allowing you to implement your own
debug logic related to your Solana transactions and use the same controller for it as gill
does.
isDebugEnabled()
- check if debug mode is enabled or notdebug()
- print debug message if the set log level is reached
import { debug, isDebugEnabled } from "gill";
if (isDebugEnabled()) {
}
debug("custom message");
debug("custom message", "debug");
debug("custom message", "warn");
debug("custom message", "warn");
Program clients
With gill
you can also import some of the most commonly used clients for popular programs. These
are also fully tree-shakable, so if you do not import them inside your project they will be removed
by your JavaScript bundler at build time (i.e. Webpack).
To import any of these program clients:
import { ... } from "gill/programs";
import { ... } from "gill/programs/token";
import { ... } from "gill/programs/token22";
Note: Some client re-exported client program clients have a naming collision. As a result, they
may be re-exported under a subpath of gill/programs
. For example, gill/programs/token22
and
gill/programs/token
.
The program clients included inside gill
are:
If one of the existing clients are not being exported from gill/programs
or a subpath therein, you
can of course manually add their compatible client to your repo.
Other compatible program clients
From the solana-program GitHub organization - formerly
known as the Solana Program Library (SPL)
Generate a program client from an IDL
If you want to easily interact with any custom program with this library, you can use
Codama to generate a compatible JavaScript/TypeScript client
using its IDL. You can either store the generated client inside your repo or publish it as a NPM
package for others to easily consume.