Pārlūkot izejas kodu

improved readme and contributing (#269)

* feat: improved readme

* chore: issue templates

* feat: add contrib
Nick Frostbutter 1 mēnesi atpakaļ
vecāks
revīzija
70a14b5582

+ 51 - 0
.github/ISSUE_TEMPLATE/bug_report.md

@@ -0,0 +1,51 @@
+---
+name: Bug report
+about: If you've already confirmed something is broken within the gill sdk itself, create a bug report.
+title: "[BUG] "
+labels: ""
+assignees: ""
+---
+
+<!-- Please provide all of the information requested below. Our maintainers are limited on time without all of this information it's not possible for us to help and your bug report will be closed. -->
+
+**Which gill sdk packages are you having issues with?**
+
+For example: gill, @gillsdk/react
+
+**What versions of these packages are you using?**
+
+For example: v0.11.0
+
+**What build tool (or framework if it abstracts the build tool) are you using?**
+
+For example: Next.js 15.1.7, Vite 6.1.0
+
+**What version of NodeJS (or other server runtime) are you using?**
+
+For example: NodeJS v20.0.0, Bun v1.2.1
+
+**What package manager (and version) are you using?**
+
+For example: pnpm v9.1.0, npm v11.4.2
+
+**What browser are you using?**
+
+For example: Chrome, Brave, Safari, or N/A
+
+**What operating system are you using?**
+
+For example: Ubuntu, PopOS, Fedora, MacOS, Windows, etc
+
+**Reproduction URL**
+
+A public GitHub repo that includes a minimal reproduction of the bug. **Please do not link to your actual project**,
+what we need instead is a _minimal_ reproduction in a fresh project without any unnecessary code. This means it doesn't
+matter if your real project is private/confidential, since we want a link to a separate, isolated reproduction anyways.
+
+A reproduction is **required** when filing a bug report — any bug report opened without a reproduction will be closed
+and you'll be asked to create a new issue that includes a reproduction. We're a small team and we can't keep up with the
+volume of issues we receive if we need to reproduce each issue from scratch ourselves.
+
+**Describe your issue**
+
+Describe the problem you're seeing, any important steps to reproduce and what behavior you expect instead.

+ 1 - 0
.github/ISSUE_TEMPLATE/config.yml

@@ -0,0 +1 @@
+blank_issues_enabled: false

+ 70 - 0
.github/ISSUE_TEMPLATE/feature_request.md

@@ -0,0 +1,70 @@
+---
+name: Feature Request
+about: Suggest an idea for the gill sdk
+title: "[FEATURE] "
+labels: "enhancement"
+assignees: ""
+---
+
+## Summary
+
+<!-- A clear and concise description of what you want to happen. -->
+
+## Problem Statement
+
+**Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem
+is.
+
+## Proposed Solution
+
+**Describe the solution you'd like** A clear and concise description of what you want to happen.
+
+## Alternatives Considered
+
+**Describe alternatives you've considered** A clear and concise description of any alternative solutions or features
+you've considered.
+
+## Use Cases
+
+**Who would benefit from this feature?**
+
+- [ ] End users
+- [ ] Developers
+- [ ] System administrators
+- [ ] Other: \***\*\_\_\_\*\***
+
+**How would this feature be used?** Provide specific examples or user stories.
+
+## Implementation Details
+
+**Do you have any ideas on how this could be implemented?**
+
+- Technical approach suggestions
+- Dependencies or requirements
+- Breaking changes (if any)
+
+## Additional Context
+
+**Add any other context or screenshots about the feature request here.**
+
+- Screenshots
+- Mockups
+- References to similar features in other projects
+- Links to relevant documentation
+
+## Acceptance Criteria
+
+**What would need to be true for this feature to be considered complete?**
+
+- [ ] Criterion 1
+- [ ] Criterion 2
+- [ ] Criterion 3
+
+## Priority
+
+**How important is this feature to you?** Select one.
+
+- [ ] Critical - Blocking my work
+- [ ] High - Would significantly improve my workflow
+- [ ] Medium - Nice to have
+- [ ] Low - Minor improvement

+ 79 - 0
CONTRIBUTING.md

@@ -0,0 +1,79 @@
+# Contributing
+
+## Bug fixes
+
+If you've found a bug in gill that you'd like to fix, please [open an issue](https://github.com/gillsdk/gill/issues/new)
+before working on specific code changes to ensure it is within scope and desire for this library. Once approved,
+[submit a pull request](https://github.com/gillsdk/gill/pulls) with your changes. Include a helpful description of the
+problem and how your changes address it, and provide tests so we can verify the fix works as expected.
+
+## New features
+
+If there's a new feature you'd like to see added to gill, please
+[open an issue](https://github.com/gillsdk/gill/issues/new) before working on specific code changes to ensure it is
+within scope and desire for this library.
+
+Contributions are welcome and loved, but it's best to discuss major changes before investing time in implementation.
+
+## System requirements
+
+Before getting started, ensure your system has access to the following tools:
+
+- [Node.js](https://nodejs.org/)
+- [pnpm](https://pnpm.io/)
+
+## Getting started
+
+Clone and prepare the repo locally:
+
+```sh
+git clone https://github.com/gillsdk/gill.git
+cd gill
+pnpm install
+```
+
+Build all the packages in parallel (via Turborepo):
+
+```sh
+pnpm build
+```
+
+> Note: You must run the build command the first time manually before running the test commands detailed below.
+
+To build a specific package, use the `--filter` flag:
+
+```sh
+pnpm build --filter=gill
+pnpm build --filter=@gillsdk/react
+# or multiple specific packages
+pnpm build --filter=gill --filter=@gillsdk/react
+```
+
+## Running tests
+
+All unit tests can be run at the same time (including rebuilding):
+
+```sh
+pnpm test
+```
+
+> Note: You must run the build command the first time manually before running the `test` command.
+
+Please ensure that all tests are passing when submitting a pull request. If you're adding new features to the gill sdk,
+always include tests.
+
+## Pull request process
+
+When submitting a pull request:
+
+- Ensure the pull request title and description explain the changes you made and why you made them.
+- Include a test plan section that outlines how you tested your contributions. We do not accept contributions without
+  tests.
+- Ensure all tests pass.
+
+When a pull request is created, gill maintainers will be notified automatically.
+
+## Communication
+
+- **GitHub issues**: For bug reports and feature requests
+- **GitHub pull requests**: For code contributions

+ 40 - 739
README.md

@@ -1,5 +1,5 @@
 <h1 align="center">
-  gill
+  gill sdk
 </h1>
 
 <p align="center">
@@ -18,7 +18,7 @@
 
 ## Overview
 
-Welcome to `gill`, a JavaScript/TypeScript client library for interacting with the [Solana](http://solana.com/)
+Welcome to the gill sdk, a JavaScript/TypeScript client library for interacting with the [Solana](http://solana.com/)
 blockchain. You can use it to build Solana apps in Node, web, React Native, or just about any other JavaScript
 environment.
 
@@ -38,767 +38,68 @@ You can find the gill library docs here:
 - [gill setup guide](https://gillsdk.com/docs#quick-start)
 - [gill API references](https://gillsdk.com/api)
 
-## Installation
+## Packages
 
-Install `gill` with your package manager of choice:
+The following packages are published from within this repo, collectively known as the "gill sdk":
 
-```shell
-npm install gill
-```
-
-```shell
-pnpm add gill
-```
-
-```shell
-yarn add gill
-```
-
-### Replace Kit with gill
-
-All imports from the `@solana/kit` library can be directly replaces with `gill` to achieve the exact same functionality.
-Plus unlock the additional functionality only included in Gill, like `createSolanaTransaction`.
-
-Simply [install gill](#installation) and replace your imports
-
-## Quick start
-
-> Find a collection of example code snippets using `gill` inside the
-> [`/examples` directory](https://github.com/gillsdk/gill/tree/master/examples), including
-> [basic operations](https://github.com/gillsdk/gill/tree/master/examples/get-started) and common
-> [token operations](https://github.com/gillsdk/gill/tree/master/examples/tokens).
-
-- [Create a Solana RPC connection](#create-a-solana-rpc-connection)
-- [Making Solana RPC calls](#making-solana-rpc-calls)
-- [Create a transaction](#create-a-transaction)
-- [Signing transactions](#signing-transactions)
-- [Simulating transactions](#simulating-transactions)
-- [Sending and confirming transaction](#sending-and-confirming-transactions)
-- [Get a transaction signature](#get-the-signature-from-a-signed-transaction)
-- [Get a Solana Explorer link](#get-a-solana-explorer-link-for-transactions-accounts-or-blocks)
-- [Calculate minimum rent balance for an account](#calculate-minimum-rent-for-an-account)
-- [Generating keypairs and signers](#generating-keypairs-and-signers)
-- [Generating extractable keypairs and signers](#generating-extractable-keypairs-and-signers)
-
-You can also find some [NodeJS specific helpers](#node-specific-imports) like:
-
-- [Loading a keypair from a file](#loading-a-keypair-from-a-file)
-- [Saving a keypair to a file](#saving-a-keypair-to-a-file)
-- [Loading a keypair from an environment variable](#loading-a-keypair-from-an-environment-variable)
-- [Saving a keypair to an environment variable file](#saving-a-keypair-to-an-environment-file)
-- [Loading a base58 keypair from an environment variable](#loading-a-base58-keypair-from-an-environment-variable)
-
-You can find [transaction builders](#transaction-builders) for common tasks, including:
-
-- [Creating a token with metadata](#create-a-token-with-metadata)
-- [Minting tokens to a destination wallet](#mint-tokens-to-a-destination-wallet)
-- [Transfer tokens to a destination wallet](#transfer-tokens-to-a-destination-wallet)
-
-For troubleshooting and debugging your Solana transactions, see [Debug mode](#debug-mode) below and the gill docs for
-[Debug Mode](https://gillsdk.com/docs/debug-mode).
-
-> You can also consult the documentation for Anza's [JavaScript client](https://github.com/anza-xyz/solana-web3.js)
-> library for more information and helpful resources.
-
-### Generating keypairs and signers
-
-See also: the docs on
-[Generating a keypair signer](https://gillsdk.com/docs/getting-started/signers#generating-a-keypair-signer).
-
-For most "signing" operations, you will need a `KeyPairSigner` instance, which can be used to sign transactions and
-messages.
-
-To generate a random `KeyPairSigner`:
-
-```typescript
-import { generateKeyPairSigner } from "gill";
-
-const signer: KeyPairSigner = await generateKeyPairSigner();
-```
-
-> Note: These Signers are non-extractable, meaning there is no way to get the secret key material out of the instance.
-> This is a more secure practice and highly recommended to be used over extractable keypairs, unless you REALLY need to
-> be able to save the keypair for some reason.
-
-### Generating extractable keypairs and signers
-
-See also: the docs on
-[Generating extractable keypairs and signers](https://gillsdk.com/docs/getting-started/signers#generating-extractable-keypairs-and-signers).
-
-Extractable keypairs are less secure and should not be used unless you REALLY need to save the key for some reason.
-Since there are a few useful cases for saving these keypairs, gill contains a separate explicit function to generate
-these extractable keypairs.
-
-To generate a random, **extractable** `KeyPairSigner`:
-
-```typescript
-import { generateExtractableKeyPairSigner } from "gill";
-
-const signer: KeyPairSigner = await generateExtractableKeyPairSigner();
-```
-
-> WARNING: Using **extractable** keypairs are inherently less-secure, since they allow the secret key material to be
-> extracted. Obviously. As such, they should only be used sparingly and ONLY when you have an explicit reason you need
-> extract the key material (like if you are going to save the key to a file).
-
-### Create a Solana RPC connection
-
-See also: the docs on [how to create a Solana client](https://gillsdk.com/docs/getting-started/client)
-
-Create a Solana `rpc` and `rpcSubscriptions` client for any RPC URL or standard Solana network moniker (i.e. `devnet`,
-`localnet`, `mainnet` etc).
-
-```typescript
-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:
-
-```typescript
-import { createSolanaClient } from "gill";
-
-const { rpc, rpcSubscriptions, sendAndConfirmTransaction } = createSolanaClient({
-  urlOrMoniker: "localnet",
-});
-```
-
-To create an RPC client for an custom RPC provider or service:
-
-```typescript
-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](https://solana.com/docs/rpc) calls
-directly off of it.
-
-```typescript
-import { createSolanaClient } from "gill";
-
-const { rpc } = createSolanaClient({ urlOrMoniker: "devnet" });
-
-// get slot
-const slot = await rpc.getSlot().send();
-
-// get the latest blockhash
-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](https://developer.mozilla.org/en-US/docs/Web/API/AbortController), by passing it into `send()`:
-
-```typescript
-import { createSolanaClient } from "gill";
-
-const { rpc } = createSolanaClient({ urlOrMoniker: "devnet" });
-
-// Create a new AbortController.
-const abortController = new AbortController();
-
-// Abort the request when the user navigates away from the current page.
-function onUserNavigateAway() {
-  abortController.abort();
-}
-
-// The request will be aborted if and only if the user navigates away from the page.
-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`.
-
-```typescript
-import { createTransaction } from "gill";
-
-const transaction = createTransaction({
-  version,
-  feePayer,
-  instructions,
-  // the compute budget values are HIGHLY recommend to be set in order to maximize your transaction landing rate
-  // computeUnitLimit: number,
-  // computeUnitPrice: number,
-});
-```
-
-To create a transaction while setting the latest blockhash:
-
-```typescript
-import { createTransaction } from "gill";
-
-const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
-
-const transaction = createTransaction({
-  version,
-  feePayer,
-  instructions,
-  latestBlockhash,
-  // the compute budget values are HIGHLY recommend to be set in order to maximize your transaction landing rate
-  // computeUnitLimit: number,
-  // computeUnitPrice: number,
-});
-```
-
-### Signing transactions
-
-If your transaction already has the latest blockhash lifetime set via `createTransaction`:
-
-```typescript
-import { createTransaction, signTransactionMessageWithSigners } from "gill";
-
-const transaction = createTransaction(...);
-
-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:
-
-```typescript
-import {
-  createTransaction,
-  createSolanaClient,
-  signTransactionMessageWithSigners,
-  setTransactionMessageLifetimeUsingBlockhash,
-} from "gill";
-
-const { rpc } = createSolanaClient(...);
-const transaction = createTransaction(...);
-
-const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
-
-const signedTransaction = await signTransactionMessageWithSigners(
-  setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, transaction),
-);
-```
-
-### Simulating transactions
-
-To simulate a transaction on the blockchain, you can use the `simulateTransaction()` function initialized from
-`createSolanaClient()`.
-
-```typescript
-import { ... } from "gill";
-
-const { simulateTransaction } = createSolanaClient({
-  urlOrMoniker: "mainnet",
-});
-
-const transaction = createTransaction(...);
-
-const simulation = await simulateTransaction(transaction)
-```
-
-The transaction provided to `simulateTransaction()` can either be signed or not.
-
-### Sending and confirming transactions
-
-To send and confirm a transaction to the blockchain, you can use the `sendAndConfirmTransaction` function initialized
-from `createSolanaClient()`.
-
-```typescript
-import { ... } from "gill";
-
-const { sendAndConfirmTransaction } = createSolanaClient({
-  urlOrMoniker: "mainnet",
-});
-
-const transaction = createTransaction(...);
-
-const signedTransaction = await signTransactionMessageWithSigners(transaction);
-const signature: string = getSignatureFromTransaction(signedTransaction);
-
-console.log(getExplorerLink({ transaction: signature }));
-
-// default commitment level of `confirmed`
-await sendAndConfirmTransaction(signedTransaction)
-```
+| Package          | Description                                   | Version                                                                                                                       | Source                                                               |
+| :--------------- | :-------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
+| `gill`           | SDK for building on the Solana blockchain     | [![npm](https://img.shields.io/npm/v/gill.svg?logo=npm&color=377CC0)](https://www.npmjs.com/package/gill)                     | [Source](https://github.com/gillsdk/gill/tree/master/packages/gill)  |
+| `@gillsdk/react` | React hooks library for the Solana blockchain | [![npm](https://img.shields.io/npm/v/@gillsdk/react.svg?logo=npm&color=377CC0)](https://www.npmjs.com/package/@gillsdk/react) | [Source](https://github.com/gillsdk/gill/tree/master/packages/react) |
 
-If you would like more fine grain control over the configuration of the `sendAndConfirmTransaction` functionality, you
-can include configuration settings:
+## Development
 
-```typescript
-await sendAndConfirmTransaction(signedTransaction, {
-  commitment: "confirmed",
-  skipPreflight: true,
-  maxRetries: 10n,
-  ...
-});
-```
-
-### Get the signature from a signed transaction
-
-After you have a transaction signed by the `feePayer` (either a partially or fully signed transaction), you can get the
-transaction signature as follows:
-
-```typescript
-import { getSignatureFromTransaction } from "gill";
-
-const signature: string = getSignatureFromTransaction(signedTransaction);
-console.log(signature);
-// Example output: 4nzNU7YxPtPsVzeg16oaZvLz4jMPtbAzavDfEFmemHNv93iYXKKYAaqBJzFCwEVxiULqTYYrbjPwQnA1d9ZCTELg
-```
-
-> Note: After a transaction has been signed by the fee payer, 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 potentially 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):
-
-```typescript
-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:
-
-```typescript
-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:
-
-```typescript
-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:
-
-```typescript
-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:
-
-```typescript
-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):
-
-```typescript
-import { getMinimumBalanceForRentExemption } from "gill";
-
-// when not `space` argument is provided: defaults to `0`
-const rent: bigint = getMinimumBalanceForRentExemption();
-// Expected value: 890_880n
-
-// same as
-// getMinimumBalanceForRentExemption(0);
-
-// same as, but this requires a network call
-// const rent = await rpc.getMinimumBalanceForRentExemption(0n).send();
-```
-
-```typescript
-import { getMinimumBalanceForRentExemption } from "gill";
-
-const rent: bigint = getMinimumBalanceForRentExemption(50 /* 50 bytes */);
-// Expected value: 1_238_880n
-
-// same as, but this requires a network call
-// const rent = await rpc.getMinimumBalanceForRentExemption(50n).send();
-```
-
-> 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](#create-a-solana-rpc-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`).
-
-```typescript
-import { ... } from "gill/node"
-```
-
-### Loading a keypair from a file
-
-```typescript
-import { loadKeypairSignerFromFile } from "gill/node";
-
-// default file path: ~/.config/solana/id.json
-const signer = await loadKeypairSignerFromFile();
-console.log("address:", signer.address);
-```
+### Environment setup
 
-Load a `KeyPairSigner` from a filesystem wallet json file, like those output from the
-[Solana CLI](https://solana.com/docs/intro/installation#install-the-solana-cli) (i.e. a JSON array of numbers).
+1. Install [NodeJS](https://nodejs.org/en)
+2. Install [pnpm](https://pnpm.io/installation)
 
-By default, the keypair file loaded is the Solana CLI's default keypair: `~/.config/solana/id.json`
+Clone and prepare this repo locally:
 
-To load a Signer from a specific filepath:
-
-```typescript
-import { loadKeypairSignerFromFile } from "gill/node";
-
-const signer = await loadKeypairSignerFromFile("/path/to/your/keypair.json");
-console.log("address:", signer.address);
-```
-
-### Saving a keypair to a file
-
-> See [`saveKeypairSignerToEnvFile`](#saving-a-keypair-to-an-environment-file) for saving to an env file.
-
-Save an **extractable** `KeyPairSigner` to a local json file (e.g. `keypair.json`).
-
-```typescript
-import { ... } from "gill/node";
-const extractableSigner = generateExtractableKeyPairSigner();
-await saveKeypairSignerToFile(extractableSigner, filePath);
-```
-
-See [`loadKeypairSignerFromFile`](#loading-a-keypair-from-a-file) for how to load keypairs from the local filesystem.
-
-### Loading a keypair from an environment variable
-
-Load a `KeyPairSigner` from the bytes stored in the environment process (e.g. `process.env[variableName]`)
-
-```typescript
-import { loadKeypairSignerFromEnvironment } from "gill/node";
-
-// loads signer from bytes stored at `process.env[variableName]`
-const signer = await loadKeypairSignerFromEnvironment(variableName);
-console.log("address:", signer.address);
-```
-
-### Saving a keypair to an environment file
-
-Save an **extractable** `KeyPairSigner` to a local environment variable file (e.g. `.env`).
-
-```typescript
-import { ... } from "gill/node";
-const extractableSigner = generateExtractableKeyPairSigner();
-// default: envPath = `.env` (in your current working directory)
-await saveKeypairSignerToEnvFile(extractableSigner, variableName, envPath);
-```
-
-See [`loadKeypairSignerFromEnvironment`](#loading-a-keypair-from-an-environment-variable) for how to load keypairs from
-environment variables.
-
-### Loading a base58 keypair from an environment variable
-
-Load a `KeyPairSigner` from the bytes stored in the environment process (e.g. `process.env[variableName]`)
-
-```typescript
-import { loadKeypairSignerFromEnvironmentBase58 } from "gill/node";
-
-// loads signer from base58 keypair stored at `process.env[variableName]`
-const signer = await loadKeypairSignerFromEnvironmentBase58(variableName);
-console.log("address:", signer.address);
+```shell
+git clone https://github.com/gillsdk/gill.git
+cd gill
+pnpm install
 ```
 
-## Transaction builders
-
-To simplify the creation of common transactions, gill includes various "transaction builders" to help easily assemble
-ready-to-sign transactions for these tasks, which often interact with multiple programs at once.
-
-Since each transaction builder is scoped to a single task, they can easily abstract away various pieces of boilerplate
-while also helping to create an optimized transaction, including:
-
-- sets/recommends a default compute unit limit (easily overridable of course) to optimize the transaction and improve
-  landing rates
-- auto derive required address where needed
-- generally recommend safe defaults and fallback settings
-
-All of the auto-filled information can also be manually overriden to ensure you always have escape hatches to achieve
-your desired functionality.
-
-As these transaction builders may not be for everyone, gill exposes a related "instruction builder" function for each
-which is used under the hood to craft the respective transactions. Developers can also completely forgo these builder
-abstractions and manually craft the same functionality.
-
-### Create a token with metadata
-
-Build a transaction that can create a token with metadata, either using the
-[original token](https://github.com/solana-program/token) or
-[token extensions (token22)](https://github.com/solana-program/token-2022) program.
-
-- Tokens created with the original token program (`TOKEN_PROGRAM_ADDRESS`, default) will use Metaplex's Token Metadata
-  program for onchain metadata
-- Tokens created with the token extensions program (`TOKEN_2022_PROGRAM_ADDRESS`) will use the metadata pointer
-  extensions
-
-Related instruction builder: `getCreateTokenInstructions`
-
-```typescript
-import { buildCreateTokenTransaction } from "gill/programs";
-
-const createTokenTx = await buildCreateTokenTransaction({
-  feePayer: signer,
-  latestBlockhash,
-  mint,
-  // mintAuthority, // default=same as the `feePayer`
-  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",
-  },
-  // updateAuthority, // default=same as the `feePayer`
-  decimals: 2, // default=9,
-  tokenProgram, // default=TOKEN_PROGRAM_ADDRESS, token22 also supported
-  // 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
-});
-```
+### Build
 
-### Mint tokens to a destination wallet
-
-Build a transaction that mints new tokens to the `destination` wallet address (raising the token's overall supply).
-
-- ensure you set the correct `tokenProgram` used by the `mint` itself
-- if the `destination` owner does not have an associated token account (ata) created for the `mint`, one will be
-  auto-created for them
-- ensure you take into account the `decimals` for the `mint` when setting the `amount` in this transaction
-
-Related instruction builder: `getMintTokensInstructions`
-
-```typescript
-import { buildMintTokensTransaction } from "gill/programs";
-
-const mintTokensTx = await buildMintTokensTransaction({
-  feePayer: signer,
-  latestBlockhash,
-  mint,
-  mintAuthority: signer,
-  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,
-  // 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
-});
-```
+To build all the packages in parallel (via Turborepo):
 
-### Transfer tokens to a destination wallet
-
-Build a transaction that transfers tokens to the `destination` wallet address from the `source` (aka from `sourceAta` to
-`destinationAta`).
-
-- ensure you set the correct `tokenProgram` used by the `mint` itself
-- if the `destination` owner does not have an associated token account (ata) created for the `mint`, one will be
-  auto-created for them
-- ensure you take into account the `decimals` for the `mint` when setting the `amount` in this transaction
-
-Related instruction builder: `getTransferTokensInstructions`
-
-```typescript
-import { buildTransferTokensTransaction } from "gill/programs";
-
-const transferTokensTx = await buildTransferTokensTransaction({
-  feePayer: signer,
-  latestBlockhash,
-  mint,
-  authority: signer,
-  // 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: address(...),
-  // 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
-});
+```shell
+pnpm build
 ```
 
-## Debug mode
+> Note: You must run the build command the first time manually before running the test commands detailed below.
 
-See also: the docs for [Debug Mode](https://gillsdk.com/docs/debug-mode)
+To build a specific package, use the `--filter` flag:
 
-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`](https://github.com/solana-developers/mucho?tab=readme-ov-file#inspect) or Solana Explorer's
-  [Transaction Inspector](https://explorer.solana.com/tx/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 not
-- `debug()` - print debug message if the set log level is reached
-
-```typescript
-import { debug, isDebugEnabled } from "gill";
-
-if (isDebugEnabled()) {
-  // your custom logic
-}
-
-// log this message if the "info" or above log level is enabled
-debug("custom message");
-
-// log this message if the "debug" or above log level is enabled
-debug("custom message", "debug");
-
-// log this message if the "warn" or above log level is enabled
-debug("custom message", "warn");
-
-// log this message if the "warn" or above log level is enabled
-debug("custom message", "warn");
+```shell
+pnpm build --filter=gill
+pnpm build --filter=@gillsdk/react
+# or multiple specific packages
+pnpm build --filter=gill --filter=@gillsdk/react
 ```
 
-## Program clients
+### Testing
 
-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).
+All unit tests can be run at the same time (including rebuilding):
 
-To import any of these program clients:
-
-```typescript
-import { ... } from "gill/programs";
-import { ... } from "gill/programs";
+```shell
+pnpm test
 ```
 
-> 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`.
-
-The program clients included inside `gill` are:
-
-- System program - re-exported from [`@solana-program/system`](https://github.com/solana-program/system)
-- Compute Budget program- re-exported from
-  [`@solana-program/compute-budget`](https://github.com/solana-program/compute-budget)
-- Memo program - re-exported from [`@solana-program/memo`](https://github.com/solana-program/memo)
-- Token Program and Token Extensions program (aka Token22) - re-exported from
-  [`@solana-program/token-2022`](https://github.com/solana-program/token-2022), which is a fully backwards compatible
-  client with the original Token Program
-- Address Lookup Table program - re-exported from
-  [`@solana-program/address-lookup-table`](https://github.com/solana-program/address-lookup-table)
-- Token Metadata program from Metaplex (only the v3 functionality) - generated via Codama their IDL
-  ([source](https://github.com/metaplex-foundation/mpl-token-metadata))
-
-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.
-
-> Note: Since the Token Extensions program client is fully compatible with the original Token Program client, `gill`
-> only ships the `@solana-program/token-2022` client and the `TOKEN_PROGRAM_ADDRESS` in order to remove all that
-> redundant code from the library.
->
-> To use the original Token Program, simply pass the `TOKEN_PROGRAM_ADDRESS` as the the program address for any
-> instructions
+> Note: You must run the build command the first time manually before running the `test` command.
 
-### Other compatible program clients
+## Contributing
 
-From the [solana-program](https://github.com/solana-program/token) GitHub organization, formerly known as the Solana
-Program Library (SPL), you can find various other client libraries for specific programs. Install their respective
-package to use in conjunction with gill:
+Contributions are welcome and loved! Please [open an issue](https://github.com/gillsdk/gill/issues/new) before working
+on specific code changes to ensure it is within scope and desire for this library.
 
-- [Stake program](https://github.com/solana-program/stake) - `@solana-program/stake`
-- [Vote program](https://github.com/solana-program/vote) - `@solana-program/vote`
+See the [CONTRIBUTING.md](./CONTRIBUTING.md) document for full details.
 
-### Generate a program client from an IDL
+Seriously. Read (and follow) this document if you want to contribute.
 
-See also: this official gill docs and guide on
-[how to generate a program client with codama](https://gillsdk.com/docs/guides/codama)
+## Maintainers
 
-If you want to easily interact with any custom program with this library, you can use
-[Codama](https://github.com/codama-idl/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.
+See the [MAINTAINERS.md](./MAINTAINERS.md) document for full details.