Introduction
Introducing Gill: The new javascript client library to interact with the solana blockchain. It is built on top of modern JavaScript libraries for Solana, which Anza built. it leverages the speed and elegance of web3 js version two and delivers a lightweight and highly abstract solution. it is lightweight, fast, and has tree-shakable architecture. you can use it to build solana d-apps in node, web, React Native or just about any other javascript environment.
Why is Gill useful?
Since Solana Web3.js v2 is great in performance with its tree-shakable architecture but feels verbose in everything you do with its longer function names and hyper-modular sub-packages, Gill is fully compatible with v2 and offers an easier adoption curve with improved developer experience(DevEx).
Gill provides a single entry point for Web3.js v2, popular SPL programs like System, Compute Budget, Memo, Token, Token22, and ecosystem programs like Metaplex’s Token Metadata.
Gill offers Transaction Builders to make it easy to assemble ready-to-sign transactions for common Solana tasks such as creating tokens with metadata, minting tokens, and transferring tokens. These tasks often involve interacting with multiple programs at once.
Gill can be useful for building Solana d-apps in Node, web, React Native, or any other JavaScript environment.
Gill provides a debug mode that you can enable to automatically log additional information helpful for troubleshooting your transactions. You can enable it from the most common places where your code runs, including within your code, Node.js backends, serverless functions, and even directly in the web browser console.
What can I do with Gill?
Gill offers the same features as Solana Web3.js v2 (Solana Kit) but with improved naming and a simplified approach for common Solana tasks.
By using Gill, you can accomplish the following -:
Keypairs/wallets -:
Generate random keypairs - Use
generateKeyPairSigner
to generate a non-extractable keypair for signing transactions. Non-extractable means that the private key cannot be retrieved from the instance, providing enhanced security for keypairs.Generate random extractable keypairs - Use
generateExtractableKeyPairSigner
to generate extractable keypairs. These are less secure because they allow the secret key material to be extracted.Load a wallet from a file - Use
loadKeypairSignerFromFile
to load a keypair signer from a filesystem wallet JSON file. By default, this function loads the wallet keypair stored in the file used by the Solana CLI:~/.config/solana/id.json
. It also resolves relative paths that use the~
character for the user's home directory.Load a keypair from an environment variable - Load a keypairSigner from environment-stored bytes using
loadKeypairSignerFromEnvironment
, consistent with standard Solana tools.
Client Connection and Rpc Calls-:
Create a solana RPC connection - Use
createSolanaClient
to create a Solana client with your preferred RPC endpoint URL or a standard Solana network moniker (e.g.,devnet
,localnet
,mainnet
, etc.).To make an RPC call, the RPC client requires you to call
.send()
on the RPC method to send the request to your RPC provider and receive a response.
Transactions
Create a transaction - Use the
createTransaction
method to easily create a transaction with all standard parameters.Sign and Send transaction - After you have a signable transaction, you can sign it using
signTransactionMessageWithSigners
, retrieve the signature usinggetSignatureFromTransaction
(even before sending it to the network), and send the transaction usingsendAndConfirmTransaction
.Get a Solana Explorer link - Use the
getExplorerLink
method to generate a Solana Explorer link for transactions, accounts, or blocks.Transaction builders - To simplify common Solana tasks like creating tokens, minting tokens, and transferring SPL tokens, Gill introduces Transaction Builders to help easily assemble ready-to-sign transactions for these tasks.
Debug mode
- Dubug solana transaction - Gill introduced debug mode that will make it easier to troubleshoot the solana transaction. 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.
Example Comparision
At this point, we've learned what Gill is, why it's helpful for developers, and the common tasks it can accomplish. Now, let’s look at an example of creating a token using Gill and Web3.js v2:
Create ‘OPOS’ token with Gill-:
import {
createSolanaClient,
signTransactionMessageWithSigners,
getSignatureFromTransaction,
getExplorerLink,
createKeyPairSignerFromBytes,
getBase58Codec,
generateKeyPairSigner
} from "gill";
import { buildCreateTokenTransaction, TOKEN_2022_PROGRAM_ADDRES } from "gill/programs/token";
const { rpc, sendAndConfirmTransaction } = createSolanaClient({
urlOrMoniker: "devnet",
});
// get the latest blockhash
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const keypairBase58feePayer = "your_secret_key";
const keypairBytesfeePayer = getBase58Codec().encode(keypairBase58feePayer);
const feepayersigner = await createKeyPairSignerFromBytes(keypairBytesfeePayer);
async function createTokenFunc() {
const mint = await generateKeyPairSigner();
// create tokens
const createTokenTx = await buildCreateTokenTransaction({
feePayer: feepayersigner,
latestBlockhash,
mint: mint, // Use mintKey here
metadata: {
isMutable: true, // if the `updateAuthority` can change this metadata in the future
name: "Only Possible On Solana",
symbol: "OPOS",
uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/Climate/metadata.json",
},
decimals: 2,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const signedTransaction = await signTransactionMessageWithSigners(createTokenTx);
const signature: string = getSignatureFromTransaction(signedTransaction);
console.log(getExplorerLink({ transaction: signature }));
// default commitment level of `confirmed`
await sendAndConfirmTransaction(signedTransaction);
}
createTokenFunc().catch(console.error);
Create ‘OPOS’ token without Gill:
import {
airdropFactory,
appendTransactionMessageInstructions,
createSolanaRpc,
createSolanaRpcSubscriptions,
createTransactionMessage,
generateKeyPairSigner,
getSignatureFromTransaction,
lamports,
pipe,
sendAndConfirmTransactionFactory,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
signTransactionMessageWithSigners,
some,
} from "@solana/web3.js";
import { getCreateAccountInstruction } from "@solana-program/system";
import {
getInitializeMintInstruction,
getMintSize,
TOKEN_2022_PROGRAM_ADDRESS,
extension,
getInitializeMetadataPointerInstruction,
getInitializeTokenMetadataInstruction,
tokenMetadataField,
getUpdateTokenMetadataFieldInstruction,
} from "@solana-program/token-2022";
const rpc = createSolanaRpc("http://127.0.0.1:8899");
const rpcSubscriptions = createSolanaRpcSubscriptions("ws://127.0.0.1:8900");
const feePayer = await generateKeyPairSigner();
console.log(feePayer.address);
const mint = await generateKeyPairSigner();
await airdropFactory({ rpc, rpcSubscriptions })({
recipientAddress: feePayer.address,
lamports: lamports(1_000_000_000n),
commitment: "confirmed",
});
const balance = await rpc.getBalance(feePayer.address).send();
console.log("balance:", balance.value);
const metadataPointerExtension = extension("MetadataPointer", {
authority: some(feePayer.address),
metadataAddress: some(mint.address),
});
const tokenMetadataExtension = extension("TokenMetadata", {
updateAuthority: some(feePayer.address),
mint: mint.address,
name: "Only Possible On Solana",
symbol: "OPOS",
uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/Climate/metadata.json",
additionalMetadata: new Map<string, string>([["description", "Only Possible On Solana"]]),
});
const spaceWithoutMetadata = BigInt(getMintSize([metadataPointerExtension]));
const spaceWithMetadata = BigInt(getMintSize([metadataPointerExtension, tokenMetadataExtension]));
const rent = await rpc.getMinimumBalanceForRentExemption(spaceWithMetadata).send();
const createAccountInstruction = getCreateAccountInstruction({
payer: feePayer,
newAccount: mint,
lamports: rent,
space: spaceWithoutMetadata,
programAddress: TOKEN_2022_PROGRAM_ADDRESS,
});
const initializeMetadataPointerInstruction = getInitializeMetadataPointerInstruction({
mint: mint.address,
authority: feePayer.address,
metadataAddress: mint.address,
});
const initializeMintInstruction = getInitializeMintInstruction({
mint: mint.address,
decimals: 2,
mintAuthority: feePayer.address,
});
const initializeTokenMetadataInstruction = getInitializeTokenMetadataInstruction({
metadata: mint.address,
updateAuthority: feePayer.address,
mint: mint.address,
mintAuthority: feePayer,
name: tokenMetadataExtension.name,
symbol: tokenMetadataExtension.symbol,
uri: tokenMetadataExtension.uri,
});
const updateTokenMetadataInstruction = getUpdateTokenMetadataFieldInstruction({
metadata: mint.address,
updateAuthority: feePayer,
field: tokenMetadataField("Key", ["description"]),
value: "Only Possible On Solana",
});
const instructions = [
createAccountInstruction,
initializeMetadataPointerInstruction,
initializeMintInstruction,
initializeTokenMetadataInstruction,
updateTokenMetadataInstruction,
];
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(message) => setTransactionMessageFeePayerSigner(feePayer, message),
(message) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, message),
(message) => appendTransactionMessageInstructions(instructions, message),
);
const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);
const transactionSignature = getSignatureFromTransaction(signedTransaction);
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction, {
commitment: "confirmed",
skipPreflight: true,
});
console.log("Transaction Signature:", `https://explorer.solana.com/tx/${transactionSignature}?cluster=custom`);
As you can see, creating tokens with Gill is easy thanks to its transaction builders and a higher level of abstraction over Web3.js v2. Using one transaction builder and some line of code, we created an SPL token.
Now that we understand what Gill is, why it matters, and how it enhances the Web3.js v2 developer experience, it's time to code a simple SPL token mint and transfer example. Let's go!
Quick spl token mint and transfer With Gill
As we know, there are various ways to mint or create an SPL token. We can use JavaScript/TypeScript with Solana Web3.js and Solana's developer helpers for SPL tokens, or create a Solana program in Rust/Anchor to mint an SPL token. In this guide, we will use the Gill library to mint an SPL token. We'll also utilize the quickest method, leveraging Gill's pre-built transaction builder methods for various transactions.
What you will need
- Experience with Solana Web3.js v2
- Latest Node.js version installed
- TypeScript and
ts-node
installed
Dependencies used in this guide
For this project, the dependencies in the package.json
file will look like this:
"dependencies": {
"esrun": "^3.2.26",
"gill": "^0.6.0"
},
"devDependencies": {
"@types/node": "^22.13.10",
"ts-node": "^10.9.2",
"typescript": "^5.8.2"
}
Let’s set up a new project:
mkdir spl-token && cd spl-token
Initialize your project as a Node.js project:
npm init -y
Install the required dependencies:
pnpm install gill esrun && pnpm install --save-dev @types/node typescript ts-node
Create a new file named spl-token.ts in your project directory:
echo > spl-token.ts
Great! Now, let’s start coding.
Import dependencies
In your spl-token.ts
file, let's start by importing the necessary dependencies:
import {
address,
KeyPairSigner,
getBase58Codec,
getExplorerLink,
createSolanaClient,
generateKeyPairSigner,
getSignatureFromTransaction,
createKeyPairSignerFromBytes,
signTransactionMessageWithSigners,
setTransactionMessageLifetimeUsingBlockhash,
} from "gill";
import {
buildCreateTokenTransaction,
buildMintTokensTransaction,
buildTransferTokensTransaction
} from "gill/programs/token";
We're importing key functions from the Gill library to create, mint, and transfer SPL tokens.
Create an RPC connection to interact with the blockchain
// create Rpc connection
const { rpc, sendAndConfirmTransaction } = createSolanaClient({
urlOrMoniker: "devnet",
});
// get slot
const slot = await rpc.getSlot().send();
// get the latest blockhash
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
We are using Gill's createSolanaClient method to establish an RPC connection to Devnet. To call any RPC method, we need to use .send() on the RPC method to send the request to the RPC provider and receive a response.
Generate Keypairs
const keypairBase58alice = "your_wallet_secret_key";
const keypairBytesalice = getBase58Codec().encode(keypairBase58alice);
const aliceKeyPair = await createKeyPairSignerFromBytes(keypairBytesalice);
// KeyPairs and addresses
const alice = aliceKeyPair.address;
const bob = address("4d4zsfq4gtJixDGvisSdFjsY78uH7BypkwmkXL1D8RfT");
const mint = await generateKeyPairSigner();
Here, we generate the Alice, Bob, and Mint addresses. For Alice, we use a secret key pair to generate her address. In this guide, Alice will create and mint tokens in her wallet, and then send the minted tokens to Bob’s wallet.
Create the Main Function
Next, let's create our main function that will house the logic of our script:
async function main(){
// Create token transaction
// Mint token transaction
// Transfer tokens transaction
}
main().catch(console.error);
Create or mint spl token
Let's add the following code to the main function as the first step:
const createTokenTx = await buildCreateTokenTransaction({
feePayer: aliceKeyPair,
latestBlockhash,
mint: mint, // Use mintKey here
metadata: {
isMutable: true, // if the `updateAuthority` can change this metadata in the future
name: "Only Possible On Solana",
symbol: "OPOS",
uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/Climate/metadata.json",
},
decimals: 2,
});
const signedTransaction = await signTransactionMessageWithSigners(createTokenTx);
const signature: string = getSignatureFromTransaction(signedTransaction);
console.log(getExplorerLink({ transaction: signatureformint }));
// default commitment level of `confirmed`
await sendAndConfirmTransaction(signedTransaction);
Here, we use the buildCreateTokenTransaction
tx builder from Gill to create an SPL token, specifying aliceKeypair
as the feePayer
and providing metadata for the token.
Next, we use signTransactionMessageWithSigners
to sign the transaction before sending it to the network, similar to Web3.js. We then obtain the signature using getSignatureFromTransaction
and finally send the transaction to the network using sendAndConfirmTransaction
.
Congrats! Our OPOS token has been created. Check the Solana Explorer link for details.
Minting Created token to Alice's wallet
Let's add the following code to the main function as the second step:
const mintTokensTx = await buildMintTokensTransaction({
feePayer: aliceKeyPair,
latestBlockhash,
mint,
mintAuthority: aliceKeyPair,
amount: 1000, // note: be sure to consider the mint's `decimals` value
// if decimals=2 => this will mint 10.00 tokens
// if decimals=4 => this will mint 0.100 tokens
destination: alice,
// use the correct token program for the `mint`
// tokenProgram, // default=TOKEN_PROGRAM_ADDRESS
// default cu limit set to be optimized, but can be overriden here
// computeUnitLimit?: number,
// obtain from your favorite priority fee api
// computeUnitPrice?: number, // no default set
});
const signedTransactionformint = await signTransactionMessageWithSigners(mintTokensTx);
const signatureformint: string = getSignatureFromTransaction(signedTransactionformint);
console.log(getExplorerLink({ transaction: signatureformint }));
// default commitment level of `confirmed`
await sendAndConfirmTransaction(signedTransactionformint);
Here, we use the buildMintTokensTransaction
tx builder from Gill to mint tokens to Alice's address, sign the transaction, obtain the signature, and send the transaction to the network.
You don't need to worry about associated token accounts because, in transaction builders, if the destination owner does not have an associated token account (ATA) for the mint, one will be automatically created for them.
Congrats! You have minted 10 OPOS SPL tokens into Alice's token account.
Transfer the spl token to Bob’s wallet
Let's add the following code to the main function as the third step:
const transferTokensTx = await buildTransferTokensTransaction({
feePayer: aliceKeyPair,
latestBlockhash,
mint,
authority: aliceKeyPair,
// sourceAta, // default=derived from the `authority`.
/*
* if the `sourceAta` is not derived from the `authority` (like for multi-sig wallets),
* manually derive with `getAssociatedTokenAccountAddress()`
*/
amount: 900, // note: be sure to consider the mint's `decimals` value
// if decimals=2 => this will transfer 9.00 tokens
// if decimals=4 => this will transfer 0.090 tokens
destination: bob,
// use the correct token program for the `mint`
// tokenProgram, // default=TOKEN_PROGRAM_ADDRESS
// default cu limit set to be optimized, but can be overriden here
// computeUnitLimit?: number,
// obtain from your favorite priority fee api
// computeUnitPrice?: number, // no default set
});
const signedTransactionfortransfer = await signTransactionMessageWithSigners(transferTokensTx);
const signaturefortransfer: string = getSignatureFromTransaction(signedTransactionfortransfer);
console.log(getExplorerLink({ transaction: signaturefortransfer }));
// default commitment level of `confirmed`
await sendAndConfirmTransaction(signedTransactionfortransfer);
We use the buildTransferTokensTransaction
method to transfer SPL tokens from Alice to Bob, Then sign, obtain the signature, and send it to the network. The associated token account (ATA) is also auto-created for Bob's wallet for this mint.
Congrats! You transferred 9 OPOS tokens from Alice's token account to Bob's token account.
Run Your Code
In your Terminal, type:
npx esrun ./index.ts
Upon successful execution, you should see an output of three Solana Explorer links for each operation: create, mint, and transfer of the 'OPOS' token.
https://explorer.solana.com/tx/58KC1GPc1f8aCUox6Pst7YheYRCqrQY9Np2gP6LfqDP5ogQjP5Hy76opzmJ8EKW2PyMdoGh71MYGWHL6oYHLAvdD
https://explorer.solana.com/tx/4DXAFBfsAVCgZ3X3rwEZ72EN81JVWj2zpzsYERCu5xitf1aztuytn9og3cyNbNeR3t5KnaJgneNckFy6MGAoWLGr
https://explorer.solana.com/tx/4PkHW9dSbQicKcempBoD3VCe2xQhJidJ6gptXPNvq4LVRBzJYAQrN5AGcaVfu88NabrczkgV8FrF4x1sVxKB7xSH
Let’s wrap
Gill is a new JavaScript client library for interacting with the Solana blockchain. In this post, we discussed what Gill is, why it matters for Solana developers, and how it helps improve the developer experience. We also explored its key features, provided examples comparing it to Web3.js v2, and demonstrated how to create and mint an SPL token using Gill.
Overall, you'll find that Gill offers a great developer experience when working with Web3.js v2.
Top comments (0)