Repository: solana-foundation/gill Branch: master Commit: 45acd737726f Files: 416 Total size: 1.0 MB Directory structure: gitextract_un7yn6a6/ ├── .bundlemonrc.json ├── .changeset/ │ ├── README.md │ ├── config.json │ └── gold-chicken-rhyme.md ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── config.yml │ │ └── feature_request.md │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ ├── actions/ │ │ └── install-dependencies/ │ │ └── action.yml │ ├── bundlesize.yml │ ├── preview-docs.yml │ ├── publish-canary-releases.yml │ ├── publish-docs.yml │ ├── publish-packages.yml │ └── pull-requests.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode/ │ └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── MAINTAINERS.md ├── README.md ├── docs/ │ ├── .eslintrc.json │ ├── .gitignore │ ├── .npmrc │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── build-api-docs.sh │ ├── content/ │ │ ├── api/ │ │ │ ├── index.mdx │ │ │ └── meta.json │ │ └── docs/ │ │ ├── compare/ │ │ │ ├── kit.mdx │ │ │ └── meta.json │ │ ├── debug-mode.mdx │ │ ├── examples.mdx │ │ ├── getting-started/ │ │ │ ├── client.mdx │ │ │ ├── meta.json │ │ │ └── signers.mdx │ │ ├── guides/ │ │ │ ├── codama.mdx │ │ │ ├── index.mdx │ │ │ ├── loading-and-saving-keypairs.mdx │ │ │ ├── meta.json │ │ │ ├── reference-keys.mdx │ │ │ ├── solana-pay.mdx │ │ │ └── tokens/ │ │ │ ├── burn-tokens.mdx │ │ │ ├── create-token.mdx │ │ │ ├── get-token-metadata.mdx │ │ │ ├── index.mdx │ │ │ ├── meta.json │ │ │ ├── mint-tokens.mdx │ │ │ └── transfer-tokens.mdx │ │ ├── index.mdx │ │ ├── meta.json │ │ ├── react/ │ │ │ ├── examples.mdx │ │ │ ├── getting-started.mdx │ │ │ ├── hooks/ │ │ │ │ ├── client.mdx │ │ │ │ ├── data-fetching.mdx │ │ │ │ ├── index.mdx │ │ │ │ ├── meta.json │ │ │ │ └── transactions.mdx │ │ │ ├── index.mdx │ │ │ └── meta.json │ │ └── typescript.mdx │ ├── generic.d.ts │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.mjs │ ├── public/ │ │ └── site.webmanifest │ ├── source.config.ts │ ├── src/ │ │ ├── app/ │ │ │ ├── (home)/ │ │ │ │ ├── layout.tsx │ │ │ │ └── page.tsx │ │ │ ├── api/ │ │ │ │ ├── [[...slug]]/ │ │ │ │ │ └── page.tsx │ │ │ │ ├── layout.tsx │ │ │ │ └── search/ │ │ │ │ └── route.ts │ │ │ ├── docs/ │ │ │ │ ├── [[...slug]]/ │ │ │ │ │ └── page.tsx │ │ │ │ ├── layout.tsx │ │ │ │ └── og/ │ │ │ │ └── [...slug]/ │ │ │ │ ├── og.tsx │ │ │ │ └── route.tsx │ │ │ ├── global.css │ │ │ ├── layout.config.tsx │ │ │ ├── layout.tsx │ │ │ ├── not-found.tsx │ │ │ └── styles/ │ │ │ ├── brand.css │ │ │ ├── fumadocs-overrides.css │ │ │ └── typography.css │ │ ├── components/ │ │ │ ├── fathom-analytics.tsx │ │ │ ├── footer-links.tsx │ │ │ └── package-badges.tsx │ │ ├── const.ts │ │ └── lib/ │ │ ├── Spread.tsx │ │ └── source.ts │ ├── tsconfig.json │ ├── typedoc-data.mjs │ └── vercel.json ├── e2e/ │ └── imports/ │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── package.json │ ├── src/ │ │ ├── imports.js │ │ └── imports.ts │ └── tsconfig.json ├── examples/ │ ├── get-started/ │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── .prettierrc │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── airdrop.ts │ │ │ ├── basic-compare.ts │ │ │ ├── basic.ts │ │ │ ├── intro.ts │ │ │ ├── reference-keys.ts │ │ │ └── tokens.ts │ │ └── tsconfig.json │ └── tokens/ │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── package.json │ ├── src/ │ │ ├── 1.intro.ts │ │ ├── 2.create-token-mint.ts │ │ ├── 3.create-token-mint-builder.ts │ │ ├── 4.mint-tokens.ts │ │ ├── 5.mint-tokens-builder.ts │ │ ├── 6.transfer-tokens.ts │ │ └── 7.transfer-tokens-builder.ts │ └── tsconfig.json ├── idls/ │ ├── build-codama-clients.sh │ └── token_metadata/ │ ├── codama.json │ └── idl.json ├── package.json ├── packages/ │ ├── build-scripts/ │ │ ├── .npmrc │ │ ├── .prettierignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── dev-flag.ts │ │ ├── getBaseConfig.ts │ │ ├── package.json │ │ ├── register-node-globals.cjs │ │ ├── tsconfig.json │ │ ├── tsup.config.browser.ts │ │ ├── tsup.config.library.ts │ │ └── tsup.config.package.ts │ ├── config-eslint/ │ │ ├── base.mjs │ │ └── package.json │ ├── gill/ │ │ ├── .gitignore │ │ ├── .npmrc │ │ ├── .prettierignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── generate-reexports.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── __tests__/ │ │ │ │ ├── accounts.ts │ │ │ │ ├── addresses.ts │ │ │ │ ├── base64-bytes-to.ts │ │ │ │ ├── base64-transactions.ts │ │ │ │ ├── create-codama.config.ts │ │ │ │ ├── create-solana-client.ts │ │ │ │ ├── create-token-instructions.ts │ │ │ │ ├── create-transaction.ts │ │ │ │ ├── debug.ts │ │ │ │ ├── explorer.ts │ │ │ │ ├── get-oldest-signature.ts │ │ │ │ ├── keypairs-base58.ts │ │ │ │ ├── mint-tokens-instructions.ts │ │ │ │ ├── reference-keys.ts │ │ │ │ ├── rpc.ts │ │ │ │ ├── token/ │ │ │ │ │ ├── assert-is-mint.ts │ │ │ │ │ └── fetch-token-accounts.ts │ │ │ │ ├── transfer-tokens-instructions.ts │ │ │ │ ├── ui-amount.ts │ │ │ │ ├── utils.ts │ │ │ │ └── verify-signature.ts │ │ │ ├── __typetests__/ │ │ │ │ ├── create-solana-client.ts │ │ │ │ ├── create-token-transaction.ts │ │ │ │ ├── create-transaction.ts │ │ │ │ ├── mint-tokens-transaction.ts │ │ │ │ ├── prepare-transaction.ts │ │ │ │ ├── send-and-confirm-transaction-with-signers-typetests.ts │ │ │ │ ├── simulate-transaction.ts │ │ │ │ └── transfer-tokens-transaction.ts │ │ │ ├── core/ │ │ │ │ ├── base64-bytes-to.ts │ │ │ │ ├── base64-from-transaction.ts │ │ │ │ ├── base64-to-transaction.ts │ │ │ │ ├── const.ts │ │ │ │ ├── create-codama-config.ts │ │ │ │ ├── create-solana-client.ts │ │ │ │ ├── create-transaction.ts │ │ │ │ ├── debug.ts │ │ │ │ ├── explorer.ts │ │ │ │ ├── get-oldest-signature.ts │ │ │ │ ├── get-signature-from-bytes.ts │ │ │ │ ├── index.ts │ │ │ │ ├── insert-reference-key.ts │ │ │ │ ├── keypairs-base58.ts │ │ │ │ ├── keypairs-extractable.ts │ │ │ │ ├── prepare-transaction.ts │ │ │ │ ├── rent.ts │ │ │ │ ├── rpc.ts │ │ │ │ ├── send-and-confirm-transaction-with-signers.ts │ │ │ │ ├── simulate-transaction.ts │ │ │ │ ├── utils.ts │ │ │ │ └── verify-signature.ts │ │ │ ├── index.ts │ │ │ ├── node/ │ │ │ │ ├── const.ts │ │ │ │ ├── index.ts │ │ │ │ ├── load-keypair-base58.ts │ │ │ │ ├── load-keypair.ts │ │ │ │ └── save-keypair.ts │ │ │ ├── programs/ │ │ │ │ ├── address-lookup-table/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── reexports.ts │ │ │ │ ├── compute-budget/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── reexports.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── index.ts │ │ │ │ ├── memo/ │ │ │ │ │ ├── generated/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── instructions/ │ │ │ │ │ │ │ ├── addMemo.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── programs/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── memo.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── shared/ │ │ │ │ │ └── index.ts │ │ │ │ ├── system/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── reexports.ts │ │ │ │ ├── token/ │ │ │ │ │ ├── addresses.ts │ │ │ │ │ ├── assert-is-mint.ts │ │ │ │ │ ├── fetch-token-accounts.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── instructions/ │ │ │ │ │ │ ├── create-token.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── mint-tokens.ts │ │ │ │ │ │ ├── transfer-tokens.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── reexports.ts │ │ │ │ │ ├── transactions/ │ │ │ │ │ │ ├── create-token.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── mint-tokens.ts │ │ │ │ │ │ ├── transfer-tokens.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ └── ui-amount.ts │ │ │ │ └── token-metadata/ │ │ │ │ ├── addresses.ts │ │ │ │ ├── generated/ │ │ │ │ │ ├── accounts/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── metadata.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── instructions/ │ │ │ │ │ │ ├── createMetadataAccountV3.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── updateMetadataAccountV2.ts │ │ │ │ │ ├── programs/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── tokenMetadata.ts │ │ │ │ │ └── types/ │ │ │ │ │ ├── assetData.ts │ │ │ │ │ ├── authorizationData.ts │ │ │ │ │ ├── collection.ts │ │ │ │ │ ├── collectionDetails.ts │ │ │ │ │ ├── collectionDetailsToggle.ts │ │ │ │ │ ├── collectionToggle.ts │ │ │ │ │ ├── createArgs.ts │ │ │ │ │ ├── creator.ts │ │ │ │ │ ├── data.ts │ │ │ │ │ ├── dataV2.ts │ │ │ │ │ ├── delegateArgs.ts │ │ │ │ │ ├── escrowAuthority.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── key.ts │ │ │ │ │ ├── mintArgs.ts │ │ │ │ │ ├── payload.ts │ │ │ │ │ ├── payloadType.ts │ │ │ │ │ ├── printSupply.ts │ │ │ │ │ ├── programmableConfig.ts │ │ │ │ │ ├── proofInfo.ts │ │ │ │ │ ├── reservation.ts │ │ │ │ │ ├── ruleSetToggle.ts │ │ │ │ │ ├── seedsVec.ts │ │ │ │ │ ├── tokenStandard.ts │ │ │ │ │ ├── updateArgs.ts │ │ │ │ │ ├── useMethod.ts │ │ │ │ │ ├── uses.ts │ │ │ │ │ └── usesToggle.ts │ │ │ │ └── index.ts │ │ │ └── types/ │ │ │ ├── explorer.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── rpc.ts │ │ │ └── transactions.ts │ │ ├── tsconfig.declarations.json │ │ ├── tsconfig.json │ │ ├── tsup.config.package.ts │ │ ├── typedoc.core.json │ │ ├── typedoc.node.json │ │ └── typedoc.programs.json │ ├── react/ │ │ ├── .gitignore │ │ ├── .npmrc │ │ ├── .prettierignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── __tests__/ │ │ │ │ └── placeholder.ts │ │ │ ├── __typeset__/ │ │ │ │ ├── account.ts │ │ │ │ ├── balance.ts │ │ │ │ ├── get-slot.ts │ │ │ │ ├── latest-blockhash.ts │ │ │ │ ├── multiple-accounts.ts │ │ │ │ ├── program-accounts.ts │ │ │ │ ├── recent-prioritization-fees.ts │ │ │ │ ├── signature-statuses.ts │ │ │ │ ├── signatures-for-address.ts │ │ │ │ ├── simulate-transaction.ts │ │ │ │ ├── token-account-balance.ts │ │ │ │ ├── token-account.ts │ │ │ │ ├── token-mint.ts │ │ │ │ └── transaction.ts │ │ │ ├── const.ts │ │ │ ├── hooks/ │ │ │ │ ├── account.ts │ │ │ │ ├── balance.ts │ │ │ │ ├── client.ts │ │ │ │ ├── index.ts │ │ │ │ ├── latest-blockhash.ts │ │ │ │ ├── multiple-accounts.ts │ │ │ │ ├── program-accounts.ts │ │ │ │ ├── recent-prioritization-fees.ts │ │ │ │ ├── signature-statuses.ts │ │ │ │ ├── signatures-for-address.ts │ │ │ │ ├── simulate-transaction.ts │ │ │ │ ├── slot.ts │ │ │ │ ├── token-account-balance.ts │ │ │ │ ├── token-account.ts │ │ │ │ ├── token-mint.ts │ │ │ │ ├── transaction.ts │ │ │ │ └── types.ts │ │ │ ├── index.ts │ │ │ ├── providers.tsx │ │ │ └── types/ │ │ │ └── global.d.ts │ │ ├── tsconfig.declarations.json │ │ ├── tsconfig.json │ │ ├── tsup.config.package.ts │ │ └── typedoc.json │ ├── solana-pay/ │ │ ├── .gitignore │ │ ├── .npmrc │ │ ├── .prettierignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── __tests__/ │ │ │ │ ├── encode-url.ts │ │ │ │ ├── fetchers.ts │ │ │ │ ├── parse-url.ts │ │ │ │ └── response.ts │ │ │ ├── __typetests__/ │ │ │ │ └── response.ts │ │ │ ├── constants.ts │ │ │ ├── encode-url.ts │ │ │ ├── fetchers.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── parse-url.ts │ │ │ ├── request.ts │ │ │ └── response.ts │ │ ├── tsconfig.declarations.json │ │ ├── tsconfig.json │ │ ├── tsup.config.package.ts │ │ └── typedoc.json │ ├── svelte/ │ │ ├── .gitignore │ │ ├── .npmrc │ │ ├── .prettierignore │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── __tests__/ │ │ │ │ └── placeholder.ts │ │ │ ├── const.ts │ │ │ ├── index.ts │ │ │ └── types/ │ │ │ ├── global.d.ts │ │ │ └── index.ts │ │ ├── tsconfig.declarations.json │ │ ├── tsconfig.json │ │ └── tsup.config.package.ts │ ├── test-config/ │ │ ├── .npmrc │ │ ├── .prettierignore │ │ ├── browser-environment.ts │ │ ├── global.d.ts │ │ ├── jest-dev.config.ts │ │ ├── jest-lint.config.ts │ │ ├── jest-prettier.config.ts │ │ ├── jest-unit.config.browser.ts │ │ ├── jest-unit.config.common.ts │ │ ├── jest-unit.config.node.ts │ │ ├── package.json │ │ ├── setup-define-version-constant.ts │ │ ├── setup-dev-mode.ts │ │ ├── setup-secure-context.ts │ │ ├── setup-text-encoder.ts │ │ ├── setup-undici-fetch.ts │ │ ├── setup-web-buffer-global.ts │ │ ├── setup-webcrypto.ts │ │ ├── setup-whatwg-fetch.ts │ │ └── tsconfig.json │ ├── tsconfig/ │ │ ├── .npmrc │ │ ├── README.md │ │ ├── base.json │ │ └── package.json │ └── vue/ │ ├── .gitignore │ ├── .npmrc │ ├── .prettierignore │ ├── README.md │ ├── eslint.config.mjs │ ├── package.json │ ├── src/ │ │ ├── __tests__/ │ │ │ └── placeholder.ts │ │ ├── const.ts │ │ ├── index.ts │ │ └── types/ │ │ ├── global.d.ts │ │ └── index.ts │ ├── tsconfig.declarations.json │ ├── tsconfig.json │ └── tsup.config.package.ts ├── pnpm-workspace.yaml ├── turbo.json ├── typedoc.json └── typedoc.plugin.mjs ================================================ FILE CONTENTS ================================================ ================================================ FILE: .bundlemonrc.json ================================================ { "baseDir": "./packages", "files": [ { "friendlyName": "gill production bundle", "path": "gill/dist/index.production.min.js" }, { "path": "**/dist/**/index.*.mjs" } ], "includeCommitMessage": true, "reportOutput": ["github"] } ================================================ FILE: .changeset/README.md ================================================ # Changesets Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets) We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) ================================================ FILE: .changeset/config.json ================================================ { "$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json", "access": "public", "baseBranch": "master", "changelog": [ "@changesets/changelog-github", { "repo": "gillsdk/gill" } ], "commit": false, "ignore": [ "@gillsdk/vue", "@gillsdk/svelte", "@gillsdk/tsconfig", "@gillsdk/build-scripts", "@gillsdk/test-config" ], "fixed": [["@gillsdk/!({build-scripts,config-*,test-*,tsconfig})"]], "linked": [], "updateInternalDependencies": "patch" } ================================================ FILE: .changeset/gold-chicken-rhyme.md ================================================ --- "@gillsdk/solana-pay": minor --- made explicit types for parsed and non parsed solana pay transaction request response ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- 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: "" --- **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. ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature Request about: Suggest an idea for the gill sdk title: "[FEATURE] " labels: "enhancement" assignees: "" --- ## Summary ## 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 ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ### Problem ### Summary of Changes Fixes # ================================================ FILE: .github/workflows/actions/install-dependencies/action.yml ================================================ name: Install Dependencies description: Sets up Node and its package manager, then installs all dependencies inputs: version: default: 'lts/*' type: string runs: using: composite steps: - name: Install package manager uses: pnpm/action-setup@v3 with: version: 9.1.0 - name: Setup Node uses: actions/setup-node@v4 with: node-version: ${{ inputs.version }} cache: 'pnpm' - name: Install dependencies shell: bash run: pnpm install ================================================ FILE: .github/workflows/bundlesize.yml ================================================ name: Compare bundle size on: push: branches: [master] paths-ignore: - 'docs/**' pull_request: types: [synchronize, opened, reopened] paths-ignore: - 'docs/**' jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install Dependencies uses: ./.github/workflows/actions/install-dependencies with: version: current - name: Build run: pnpm build - name: BundleMon uses: lironer/bundlemon-action@v1 ================================================ FILE: .github/workflows/preview-docs.yml ================================================ name: Preview Documentation on: pull_request: paths: - "docs/**" permissions: contents: read env: # Among other things, opts out of Turborepo telemetry # See https://consoledonottrack.com/ DO_NOT_TRACK: "1" NEXT_TELEMETRY_DISABLED: "1" VERCEL_TELEMETRY_DISABLED: "1" # Some tasks slow down considerably on GitHub Actions runners when concurrency is high TURBO_CONCURRENCY: 1 # Enables Turborepo Remote Caching. TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ vars.TURBO_TEAM }} jobs: preview-docs: if: github.actor != 'dependabot[bot]' permissions: pull-requests: write runs-on: ubuntu-latest name: Build Documentation Preview steps: - name: Checkout uses: actions/checkout@v4 - name: Install Dependencies uses: ./.github/workflows/actions/install-dependencies - name: Install Isolated Docs Dependencies working-directory: ./docs/ shell: bash run: pnpm install --ignore-workspace - name: Install Vercel CLI run: pnpm install -g vercel - name: Deploy to Vercel shell: bash id: vercel_deploy env: BRANCH_NAME: ${{ github.head_ref }} PR_NUMBER: ${{ github.event.pull_request.number }} VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} run: | vercel pull --token="$VERCEL_TOKEN" --yes --environment=preview vercel build --token="$VERCEL_TOKEN" --target=preview DEPLOY_OUTPUT=$(vercel deploy --token="$VERCEL_TOKEN" --archive=tgz --env GITHUB_PR_NUMBER="$PR_NUMBER" --env GITHUB_PR_BRANCH="$BRANCH_NAME" --prebuilt --target=preview 2>&1) DEPLOY_EXIT_CODE=$? if [ $DEPLOY_EXIT_CODE -ne 0 ]; then echo "Vercel deploy failed:" echo "$DEPLOY_OUTPUT" exit $DEPLOY_EXIT_CODE fi DEPLOY_URL=$(echo "$DEPLOY_OUTPUT" | grep -o 'https://[a-zA-Z0-9.-]*\.vercel\.app' | tail -1) echo "preview_url=$DEPLOY_URL" >> $GITHUB_OUTPUT - name: Comment on PR with Preview URL env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} PREVIEW_URL: "${{ steps.vercel_deploy.outputs.preview_url }}" run: | gh pr comment $PR_NUMBER --body "Documentation Preview: $PREVIEW_URL" --create-if-none --edit-last ================================================ FILE: .github/workflows/publish-canary-releases.yml ================================================ name: Publish Canary Releases on: workflow_dispatch: branches: - master pull_request: types: [opened, synchronize, labeled] permissions: contents: read env: # Among other things, opts out of Turborepo telemetry # See https://consoledonottrack.com/ DO_NOT_TRACK: '1' # Some tasks slow down considerably on GitHub Actions runners when concurrency is high TURBO_CONCURRENCY: 1 # Enables Turborepo Remote Caching. TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ vars.TURBO_TEAM }} jobs: build-and-publish-snapshots-to-npm: permissions: pull-requests: write runs-on: ubuntu-latest # Only run if it's a push to master, manual dispatch, or PR with 'canary' label if: | github.event_name == 'push' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'canary')) steps: - name: Checkout uses: actions/checkout@v4 - name: Install Dependencies uses: ./.github/workflows/actions/install-dependencies - name: Run Build Step (force) run: pnpm turbo build --force=true - name: Configure NPM token run: | pnpm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}" env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Publish Canary Releases run: | find packages/* -maxdepth 0 -type d -print0 | \ xargs -t0 -n 1 -I {} \ sh -c 'cd {} && pnpm pkg delete devDependencies' pnpm changeset version --snapshot canary pnpm turbo publish-packages --concurrency=${TURBO_CONCURRENCY:-1} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} PUBLISH_TAG: canary - name: Remove canary label from PR if: github.event_name == 'pull_request' uses: actions/github-script@v7 with: script: | github.rest.issues.removeLabel({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, name: 'canary' }); ================================================ FILE: .github/workflows/publish-docs.yml ================================================ name: Publish Documentation on: workflow_dispatch: branches: - master push: branches: - master paths: - 'docs/**' permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: # Among other things, opts out of Turborepo telemetry # See https://consoledonottrack.com/ DO_NOT_TRACK: '1' NEXT_TELEMETRY_DISABLED: '1' VERCEL_TELEMETRY_DISABLED: '1' # Enables Turborepo Remote Caching. TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ vars.TURBO_TEAM }} jobs: deploy-docs: runs-on: ubuntu-latest name: Deploy Documentation steps: - name: Checkout uses: actions/checkout@v4 - name: Install Dependencies uses: ./.github/workflows/actions/install-dependencies - name: Install Isolated Docs Dependencies working-directory: ./docs/ shell: bash run: pnpm install --ignore-workspace - name: Install Vercel CLI run: pnpm install -g vercel - name: Deploy to Vercel shell: bash env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} run: | vercel pull --token="$VERCEL_TOKEN" --yes --environment=production vercel build --token="$VERCEL_TOKEN" --prod vercel deploy --token="$VERCEL_TOKEN" --archive=tgz --prebuilt --prod ================================================ FILE: .github/workflows/publish-packages.yml ================================================ name: Version & Publish Packages on: workflow_dispatch: branches: - master paths-ignore: - 'docs/**' push: branches: - master paths-ignore: - 'docs/**' env: # Among other things, opts out of Turborepo telemetry # See https://consoledonottrack.com/ DO_NOT_TRACK: "1" # Some tasks slow down considerably on GitHub Actions runners when concurrency is high TURBO_CONCURRENCY: 1 # Enables Turborepo Remote Caching. TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} jobs: build-and-publish-to-npm: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install Dependencies uses: ./.github/workflows/actions/install-dependencies - name: Configure NPM token run: | pnpm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}" env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Create Changesets Pull Request or Trigger an NPM Publish id: changesets uses: changesets/action@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Run Pre-Build Step run: pnpm prebuild --force=true - name: Choose Build Step id: build-step-decider run: echo "step-name=${{ steps.changesets.outputs.hasChangesets == 'false' && 'publish-packages --concurrency=${TURBO_CONCURRENCY:-1}' || 'build' }}" >> $GITHUB_OUTPUT - name: Run Build Step (force) run: pnpm turbo ${{ steps.build-step-decider.outputs.step-name }} --force=true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} PUBLISH_TAG: next # - name: Push Git Tag # if: steps.changesets.outputs.hasChangesets == 'false' # run: | # VERSION_TAG=v$(cd packages/library/ && pnpm pkg get version | sed -n '2p' | grep -o '"\([^"]\)\+"$' | tr -d \") # if ! git ls-remote --tags | grep -q "$VERSION_TAG"; then git tag $VERSION_TAG && git push --tags; fi # env: # GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/pull-requests.yml ================================================ name: Pull requests on: pull_request: paths-ignore: - "docs/**" env: # Among other things, opts out of Turborepo telemetry # See https://consoledonottrack.com/ DO_NOT_TRACK: "1" # Some tasks slow down considerably on GitHub Actions runners when concurrency is high TURBO_CONCURRENCY: 1 jobs: # Needed for grouping check-web3 strategies into one check for mergify all-pr-checks: runs-on: ubuntu-latest needs: build-and-test steps: - run: echo "Done" build-and-test: runs-on: ubuntu-latest strategy: matrix: node: # !stopping builds on current since node v25.2.0 breaks things with `localStorage` # - "current" - "lts/*" name: Build & Test on Node ${{ matrix.node }} steps: - name: Checkout uses: actions/checkout@v4 - name: Install Dependencies uses: ./.github/workflows/actions/install-dependencies with: version: ${{ matrix.node }} - name: Build (with prebuild) run: pnpm build # Don't add --concurrency here; it's already baked in - name: Test run: pnpm test # Don't add --concurrency here; it's already baked in ================================================ FILE: .gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies node_modules .pnp .pnp.js # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* .pnpm-debug.log* # turbo .turbo # typedocs output .docs # Sapling SCM .sl # `solana-test-validator` .agave/ .cache test-ledger # coverage reports coverage # GitHub Pages deploy directory .ghpages-deploy *.env* *.local* .vercel ================================================ FILE: .prettierignore ================================================ node_modules pnpm-lock.yaml LICENSE .changeset/ .github/PULL_REQUEST_TEMPLATE.md declarations/ dist/ doc/ lib/ kit/ .docs .turbo .next .vercel **/generated/ **/generated/** generated/ generated/** ================================================ FILE: .prettierrc ================================================ { "tabWidth": 2, "useTabs": false, "singleQuote": false, "bracketSpacing": true, "semi": true, "trailingComma": "all", "proseWrap": "always", "arrowParens": "always", "printWidth": 120 } ================================================ FILE: .vscode/settings.json ================================================ { "eslint.workingDirectories": [ { "mode": "auto" } ], "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll": "explicit", "source.organizeImports": "explicit", "source.sortMembers": "explicit" }, "editor.formatOnPaste": true, "editor.formatOnType": false, "typescript.preferences.organizeImports": true, "javascript.preferences.organizeImports": true, "typescript.preferences.importModuleSpecifierEnding": "js", // Set Prettier as the default formatter "editor.defaultFormatter": "esbenp.prettier-vscode", // Language-specific formatters (optional overrides) "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[javascriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[html]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[css]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[scss]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[markdown]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, // Ensure consistent line endings "files.eol": "\n" } ================================================ FILE: CONTRIBUTING.md ================================================ # 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 ================================================ FILE: LICENSE ================================================ Copyright (c) 2023 Solana Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MAINTAINERS.md ================================================ # Maintainers In this document, you can find various pieces of information about the gill sdk and its associated packages. From architecture and build steps to foot-guns and gotchas. This should be a living document to describe in detail anything the maintainers of this repository feel the need should be specifically elaborated on. # Publishing and versioning This monorepo contains many the many packages that constitute the "gill sdk". Including `gill` and those scoped within `@gillsdk` (e.g. `@gillsdk/react`). ## Versioning This repo uses [changesets](https://github.com/changesets/changesets) to aid in versioning and auto generating the various package changelog documents. On each PR that requires a version bump (major, minor, or patch), simply run the `pnpm changeset` command. This will automatically detect which packages had code files changed and requiring a version bump. The `changeset` command will create a markdown file in the [.changeset](./.changeset/) directory which will be auto detected by the Changeset bot when PRs are pushed to github. ## Publishing new versions All package versions are published by the [publish-packages](./.github/workflows/publish-packages.yml) action. When a PR is merged to the `master` branch, this github action will do one of two things: 1. Read all the available changeset files and create/update a "Version Packages" PR that details all the changes since the last version bump, generate changelog entries for them, and perform appropriate version bumps. ([See example here](https://github.com/gillsdk/gill/pull/218)). 2. If the last merge to the `master` branch was a "Version Packages" PR, the actions will publish all the changed packages. This repo has an NPM token stored in it for the [`gill-maintainers`](https://www.npmjs.com/~gill-maintainers) NPM user which is the CI bot account for publishing. This account should be the only authorized user to publish these packages, helping to mitigate various supply chain attacks that have become so common :/ # Multiple import paths The core `gill` library is constructed in such a way to have multiple "import paths" to access different functionality. Including: ```ts import { ... } from "gill"; import { ... } from "gill/programs"; import { ... } from "gill/node"; ``` To achieve this, the `gill` package includes the following configurations: - "exports" fields in the [`package.json`](./packages/gill/package.json) for each of these paths - TSUP entries for each desired paths ([see here](./packages/gill/tsup.config.package.ts)) - tsconfig settings for each path ([see here](./packages/gill/tsconfig.declarations.json)) Altering (or removing these) configuration settings will break these "import paths" for consumers of the `gill` package in different ways. For example, altering the tsconfig declarations from: ```json { ... "include": ["src/programs/token/index.ts", "src/programs/index.ts", "src/node/index.ts", "src/index.ts", "src/types"] } ``` to ```json { ... "include": ["src"] } ``` will break developer experience by removing all the types from the different paths. # Docs When building the docs for production, the `build-api-docs.sh` script is run which will run the `compile:docs` script for each of the configured submodule packages. This includes building the submodule itself, generating typedocs for it, and moving those typedocs into the `docs/content/api` directory in a sub-directory for each submodule. This process utilizes the `typedoc-data.mjs` to know where specifically to put each submodule's docs and what to insert into its respective `index.mdx` to ensure we have nice looking docs. ## Submodule API references To include any submodule in the gill docs' [API references section](https://www.gillsdk.com/api), each submodules must be properly configured. steps to update include a submodule package in the api docs: In the `docs` directory: 1. install the submodule into the docs themselves (required for use by `twoslash` to get prettier code blocks) ```shell pnpm add @gillsdk/react@latest --ignore-workspace ``` 2. update the docs' `update:gill` script to include the new package (allow maintainers to update all packages at once) 3. update the `typedoc-data.mjs` to include the key and frontmatter details for the submodules, following existing patterns In the respective submodule's source code directory: 1. Setup the submodule's typedoc configuration via a `typedoc.json` file in the submodules's directory (along side it's `package.json`) 1. Create a `compile:docs` script command that will compile the typedocs - See `@gillsdk/react` [here](/packages/react/package.json) for an example of a submodule with a single import path - See `gill` [here](/packages/gill/package.json) for an example of a submodule with a multiple import paths 1. Create a `move:docs` script command that will relocate the compiled typedocs from its submodule specific build location to the actual gill docs content directory 1. Create a `clean:docs` script command that can purge the existing submodule docs (ensures the docs build is fresh) > Note: The `docs/content/api` subdirectories are intentionally git ignored to ensure they are built fresh on docs > deployments. This also allows the docs site to load much faster when running locally (by removing the submodule api > references directory) since we rarely need to view these locally. But you can still build the api references and view > them if you need too. Flexibility :) # Token Metadata program client The included Token Metadata program client was generated using [Codama](https://github.com/codama-idl/codama). ## Minimal functionality Given the IDL, Codama will generate a LOT of code that we simply do not need to or want to ship within the gill sdk. So we (manually) removed it. Gill intentionally only ships a minimal amount of Token Metadata functionality to get most users by with core uses (namely attaching metadata to legacy SPL tokens) Care should be taken when regenerating this program client. Especially to prevent the `gill` package size from ballooning (and therefore developer applications from ballooning.) ## Naming collisions There is a naming collision between the Token Metadata program and the SPL Token/Token22 program clients for the `MintArgs` type. Since gill ships a generated client for Token Metadata, and reexports the Token22 client from its source package, the Token Metadata's `MintArgs` were renamed to `MetadataMintArgs`. See [token-metadata/generated/types/mintArgs.ts](./packages/gill/src/programs/token-metadata/generated/types/mintArgs.ts) ================================================ FILE: README.md ================================================

gill sdk

javascript/typescript client library for interacting with the Solana blockchain

gill

## Overview 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. Gill is built on top of the modern javascript libraries for Solana built by Anza called [@solana/kit](https://github.com/anza-xyz/kit) (formerly known as "web3.js v2"). By utilizing the same types and functions under the hood, `gill` is compatible with `kit`. See [Replacing Kit with gill](#replace-kit-with-gill). > For a comparison of using gill vs `@solana/kit`, take a look at the > [gill vs @solana/kit comparison docs](https://gillsdk.com/docs/compare/kit) and the > [comparison examples](https://github.com/gillsdk/gill/tree/master/examples/get-started#comparison-of-gill-vs-solanakit-aka-web3js-v2). ## Documentation You can find the gill library docs here: - [gill docs site](https://gillsdk.com) - [gill setup guide](https://gillsdk.com/docs#quick-start) - [gill API references](https://gillsdk.com/api) ## Packages The following packages are published from within this repo, collectively known as the "gill sdk": | 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) | | `@gillsdk/solana-pay` | Modern Solana Pay protocol client library | [![npm](https://img.shields.io/npm/v/@gillsdk/solana-pay.svg?logo=npm&color=377CC0)](https://www.npmjs.com/package/@gillsdk/solana-pay) | [Source](https://github.com/gillsdk/gill/tree/master/packages/solana-pay) | ## Development ### Environment setup 1. Install [NodeJS](https://nodejs.org/en) 2. Install [pnpm](https://pnpm.io/installation) Clone and prepare this repo locally: ```shell git clone https://github.com/gillsdk/gill.git cd gill pnpm install ``` ### Build To build all the packages in parallel (via Turborepo): ```shell 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: ```shell pnpm build --filter=gill pnpm build --filter=@gillsdk/react # or multiple specific packages pnpm build --filter=gill --filter=@gillsdk/react ``` ### Testing All unit tests can be run at the same time (including rebuilding): ```shell pnpm test ``` > Note: You must run the build command the first time manually before running the `test` command. ## Contributing 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. See the [CONTRIBUTING.md](./CONTRIBUTING.md) document for full details. Seriously. Read (and follow) this document if you want to contribute. ## Maintainers See the [MAINTAINERS.md](./MAINTAINERS.md) document for full details. ================================================ FILE: docs/.eslintrc.json ================================================ { "extends": ["next/core-web-vitals", "next/typescript"], "rules": { "@next/next/no-img-element": "off" } } ================================================ FILE: docs/.gitignore ================================================ # deps /node_modules # generated content .contentlayer .content-collections .source # api content .docs content/api/ !./content/api/index.mdx !./content/api/meta.json # test & build /coverage /.next/ /out/ /build *.tsbuildinfo # misc .DS_Store *.pem /.pnp .pnp.js npm-debug.log* yarn-debug.log* yarn-error.log* # others .env*.local .vercel next-env.d.ts # Turborepo .turbo ================================================ FILE: docs/.npmrc ================================================ enable-pre-post-scripts = true ================================================ FILE: docs/.prettierignore ================================================ node_modules pnpm-lock.yaml LICENSE .changeset/ .github/PULL_REQUEST_TEMPLATE.md declarations/ dist/ doc/ lib/ kit/ .docs .turbo .next .vercel **/generated/ **/generated/** generated/ generated/** ================================================ FILE: docs/.prettierrc ================================================ { "tabWidth": 2, "useTabs": false, "singleQuote": false, "bracketSpacing": true, "semi": true, "trailingComma": "all", "proseWrap": "always", "arrowParens": "always", "printWidth": 100 } ================================================ FILE: docs/README.md ================================================ # gill documentation Documentation website for gill, built with [Fumadocs](https://github.com/fuma-nama/fumadocs). ## Install dependencies Install using the `--ignore-workspace` flag to ensure the dependencies of the documentation website are separate from the library dependencies. ```bash pnpm install --ignore-workspace ``` ## Run development server ```bash pnpm dev ``` Open http://localhost:3000 with your browser to see the result. ## Generating the API references documentation The docs have a `prebuild` command that will generate API docs (via TypeDoc) for the library's packages. > When working locally, it is not required to build these in order to test the local docs site. You can manually trigger building the API references using the following command: ```shell pnpm prebuild ``` ## Learn more To learn more about Next.js and Fumadocs, take a look at the following resources: - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - [Fumadocs](https://fumadocs.vercel.app) - learn about Fumadocs ================================================ FILE: docs/build-api-docs.sh ================================================ #!/usr/bin/env bash set -euo pipefail cd .. pnpm compile:docs --output-logs=hash-only ================================================ FILE: docs/content/api/index.mdx ================================================ --- title: API Reference description: "Explore the functions, variables, types, and more that are included within the gill library, the new Solana developer JavaScript SDK." --- ## Install gill Install gill using the core `gill` library: ```package-install gill ``` ## Importing from gill The `gill` library has a few different import paths to help segment the code within library and improve compatibility in different environments (i.e. browser vs server). To utilize the core functionality in either browser or server environments, you can import directly from `gill` as expected: ```ts import { ... } from "gill" ``` ### Import program clients The `gill` package includes several Solana program clients including Token/Token22, System, Compute Budget, and several others. The available program clients can be imported from the `gill/programs` path as follows: ```ts import { ... } from "gill/programs" ``` ### Import server only utilities The `gill` package includes some "server only" functionality that is only usable in JavaScript server runtimes like NodeJs and Bun. To utilize this specific functionality, you can import from `gill/node` as follows: ```ts import { ... } from "gill/node" ``` ================================================ FILE: docs/content/api/meta.json ================================================ { "title": "API Reference", "root": true, "pages": ["!index", "..."] } ================================================ FILE: docs/content/docs/compare/kit.mdx ================================================ --- title: gill vs @solana/kit description: "Explore a side-by-side comparison of gill and @solana/kit (formerly known as web3js v2)" --- The gill library is built directly on top of [`@solana/kit`](https://www.npmjs.com/package/@solana/kit), the new JavaScript primitives developed by [Anza](https://anza.xyz) as a more performant replacement for the old [`@solana/web3.js`](https://www.npmjs.com/package/@solana/web3.js). Since Kit only ships these new low level primitives, developers are forced into a single experience of manually crafting everything and ballooning their applications with verbose boilerplate. Gill is able to ship **both** the same low level primitives as Kit **and** lightly opinionated abstractions to simplify common tasks, all from a single compatible interface. By simplifying across the board with gill, developers can focus more time on their application's business logic and not verbose boilerplate. `@solana/kit` was formerly know as "web3.js v2" as it was originally published as the version `2.x` release of `@solana/web3.js`. Anza engineers noted this to complicate and confuse the upgrade path for the new technologies, so they decided to publish under the new package name of `@solana/kit`. You can find the complete scripts that the example code snippets were created from in the gill repo [here](https://github.com/gillsdk/gill/tree/master/examples/get-started#comparison-of-gill-vs-solanakit-aka-web3js-v2). Both are written with honest intentions, best practices, and attempt to be as concise as possible in accomplishing the same task. ## Connecting to the blockchain Every application will need to create a connection to the blockchain. A complete and usable connection consists of the following three parts (at a minimum): 1. `rpc` - used to send [Solana RPC](https://solana.com/docs/rpc) requests over HTTP (the most common way) 2. `rpcSubscriptions` - used to make certain RPC requests over websockets 3. `sendAndConfirmTransaction` - function used to actually send a complete transaction to the blockchain for confirmation The following code snippets demonstrate how to instantiate all three of these connection pieces for each of the libraries: ```ts tab="gill" import { createSolanaClient } from "gill"; const { rpc, rpcSubscriptions, sendAndConfirmTransaction } = createSolanaClient({ urlOrMoniker: "devnet", }); ``` ```ts tab="@solana/kit" import { devnet, createSolanaRpc, createSolanaRpcSubscriptions, sendAndConfirmTransactionFactory, } from "@solana/kit"; const rpc = createSolanaRpc(devnet("https://api.devnet.solana.com")); const rpcSubscriptions = createSolanaRpcSubscriptions(devnet("wss://api.devnet.solana.com")); const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions, }); ``` ## Making RPC requests For both `@solana/kit` and `gill`, making standard Solana RPC requests is the same: use the previously created `rpc` object. ```ts // get slot const slot = await rpc.getSlot().send(); // get the latest blockhash const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); ``` ## Loading a signer There are many different ways to obtain a keypair that is capable of performing the Solana specific signing operations (aka a "signer"). This will look different for frontend and backend applications. For local scripts, JavaScript server runtimes (like NodeJS and Bun), and backend applications it's common to load a keypair from the file system or an ENV variable. The following is how to accomplish this for both libraries: ```ts tab="gill" import { loadKeypairSignerFromFile } from "gill/node"; const signer = await loadKeypairSignerFromFile(); ``` ```ts tab="@solana/kit" import { readFileSync } from "node:fs"; import { homedir } from "node:os"; import { resolve } from "node:path"; import { createKeyPairFromBytes } from "@solana/kit"; const keypairFilePath = "~/.config/solana/id.json"; const resolvedKeypairPath = resolve(keypairFilePath.replace("~", homedir())); const keypair = await createKeyPairFromBytes( Uint8Array.from(JSON.parse(readFileSync(resolvedKeypairPath, "utf8"))), ); ``` Note that `@solana/kit` has no specific functionality for loading keypairs from files or ENV variables, so developers must implement the file/ENV reading operations themselves. Whereas gill includes multiple functions to handle these very scenarios: - [`loadKeypairSignerFromFile()`](/api/gill-node/functions/loadKeypairSignerFromFile) - [`loadKeypairSignerFromEnvironment()`](/api/gill-node/functions/loadKeypairSignerFromEnvironment) - [`loadKeypairSignerFromEnvironmentBase58()`](/api/gill-node/functions/loadKeypairSignerFromEnvironmentBase58) The `loadKeypairSignerFromFile()` function defaults to the Solana CLI's keypair path (`~/.config/solana/id.json`). If you wish to load a different keypair file, provide the path in as an argument. ## Creating a transaction After connecting to the blockchain and making RPC requests, the next most common tasks is actually creating transactions. The following are examples of how to create a simple Memo transaction that includes basic optimizations (via compute budget instructions): ```ts tab="gill" import { createTransaction } from "gill"; import { getAddMemoInstruction } from "gill/programs"; const transaction = createTransaction({ version: "legacy", feePayer: signer, instructions: [ getAddMemoInstruction({ memo: "gm world!", }), ], latestBlockhash, computeUnitLimit: 5000, computeUnitPrice: 1000, }); ``` ```ts tab="@solana/kit" import { pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, appendTransactionMessageInstructions, setTransactionMessageLifetimeUsingBlockhash, } from "@solana/kit"; import { getAddMemoInstruction } from "@solana-program/memo"; import { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction, } from "@solana-program/compute-budget"; const transaction = pipe( createTransactionMessage({ version: "legacy" }), (tx) => setTransactionMessageFeePayerSigner(signer, tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions( [ getAddMemoInstruction({ memo: "gm world!", }), getSetComputeUnitLimitInstruction({ units: 5000 }), getSetComputeUnitPriceInstruction({ microLamports: 1000 }), ], tx, ), ); ``` Notice that the `@solana/kit` based example above requires manually installing two additional packages: `@solana-program/compute-budget` and `@solana-program/memo`. Whereas gill takes a different approach: - `createTransaction` natively supports setting the same compute budget instructions - the included Memo program client is directly accessible via the `gill/programs` import path without having to manually install the package ================================================ FILE: docs/content/docs/compare/meta.json ================================================ { "title": "Comparisons", "pages": ["kit", "web3js", "..."] } ================================================ FILE: docs/content/docs/debug-mode.mdx ================================================ --- title: Debug Mode description: "Understand how to use gill's debug mode to more easily debug your Solana applications." --- Within `gill`, you can enable "debug mode" to automatically log additional information that will be helpful in troubleshooting your Solana applications (especially failing transactions). [Debug mode](./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 via `sendAndConfirmTransaction` - log the base64 transaction string for further troubleshooting using [`mucho inspect`](https://github.com/solana-developers/mucho?tab=readme-ov-file#inspect) or the 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) ```ts import { ... } from "gill" /** Turn on debug mode */ global.__GILL_DEBUG__ = true; ``` Set the `GILL_DEBUG` environment variable in your applications' preview/staging environments to improve your logging and troubleshooting workflows. ## How to set a debug level 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) ```ts import { ... } from "gill" /** Set the debug mode log level (default: `info`) */ global.__GILL_DEBUG_LEVEL__ = "debug"; ``` The log levels supported (in order of priority): 1. `debug` (lowest) 2. `info` (default) 3. `warn` 4. `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 application and use the same controller for it as `gill` does. These functions include: - `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"); ``` ================================================ FILE: docs/content/docs/examples.mdx ================================================ --- title: Examples description: Collection of example scripts and code snippets on how to use gill, the Solana JavaScript SDK. --- Listed here and in gill's open source repository, you can find a collection of example scripts and code snippets that demonstrate how to accomplish various common tasks for Solana developers. [github.com/gillsdk/gill/tree/master/examples](https://github.com/gillsdk/gill/tree/master/examples) ## Get started with the basics Inside of the gill repository, you can find the `get-started` directory that contains gill examples scripts on the following: ### intro.ts https://github.com/gillsdk/gill/blob/master/examples/get-started/src/intro.ts A brief introduction to the `gill` library. Demonstrating and explaining the commonly used tasks involved to interact with the Solana blockchain, including: - load a keypair signer from the local filesystem - create an rpc connection to the blockchain - creating basic instructions (like the memo instruction) - getting the latest blockhash - building a complete transaction - signing the transaction with the loaded local keypair signer - getting the signature of a transaction (even before it is sent) - logging Solana Explorer links - sending and confirming a transaction These are all the most basic tasks required for any application sending transaction to the Solana blockchain. ### airdrop.ts https://github.com/gillsdk/gill/blob/master/examples/get-started/src/airdrop.ts Demonstrates how to create a client connection to the Solana blockchain on a test cluster (e.g. `devnet`, `testnet`, or `localnet`) and request airdrops of testing SOL tokens to a wallet address. ### tokens.ts https://github.com/gillsdk/gill/blob/master/examples/get-started/src/tokens.ts Demonstrates how to use gill's "transaction builders" to create a brand new Solana token (with onchain metadata) and then mint tokens to another user's wallet: - load a keypair signer from the local filesystem - create an rpc connection to the blockchain - getting the latest blockhash - build an optimized transaction to create a token - sign, send, and confirm that "create token" transaction - build an optimized transaction to mint - sign, send, and confirm that "mint tokens" transaction ### reference-keys.ts https://github.com/gillsdk/gill/blob/master/examples/get-started/src/reference-keys.ts This script demonstrates the process to add a reference key into a transaction. Adding reference keys to transactions allows developers to be able track the completion of transactions given to users, without knowing the signature ahead of time. Then, perform any desired logic after detection of the reference keyed transaction landing onchain. Most notably utilized within SolanaPay and Blinks. See also: the gill docs for [Reference Keys](https://gillsdk.com/docs/guides/reference-keys) for more information. ### Comparison between @solana/kit and gill You can find comparison scripts that demonstrates some of the differences between [gill](https://github.com/gillsdk/gill) and [@solana/kit](https://github.com/anza-xyz/kit) (formerly known as "web3.js v2"). Find a more comprehensive comparison in [gill vs @solana/kit comparison docs](https://gillsdk.com/docs/compare/kit) Both scripts accomplish the same task: send an optimized transaction to the Solana blockchain. - Using gill - [`basic.ts`](https://github.com/gillsdk/gill/blob/master/examples/get-started/src/basic.ts) - Using web3js v2 - [`basic-compare.ts`](https://github.com/gillsdk/gill/blob/master/examples/get-started/src/basic-compare.ts) ================================================ FILE: docs/content/docs/getting-started/client.mdx ================================================ --- title: Create a Solana client description: Create a client connection to the Solana blockchain to perform Solana RPC requests. --- Setting up a client connection to the Solana blockchain is a very important part of your application. This client connection is how your application will be sending and receiving data from the Solana JSON RPC layer, including [fetching accounts](#fetch-an-account) and sending transactions. Gill considers the "Solana client" to be (at a minimum) the following pieces: - `rpc` - used to interact with the your [Solana JSON RPC](https://solana.com/docs/rpc/http) provider (typically via HTTP) - `rpcSubscriptions` - used to interact with the Solana RPC over websockets - `sendAndConfirmTransaction` - used to send and confirm a Solana transaction over the RPC connections If you are familiar with the `Connection` class from the older `@solana/web3.js` library, gill's `createSolanaClient` is similar. But more bare-bones and lightweight. Most client applications will need to initialize the above functionality in their codebase. Within gill, there are two primary ways to create a Solana client: 1. using the [`createSolanaClient()`](#create-a-solana-rpc-connection) function (recommended in order to reduce application boilerplate) 2. manually initialize them all individually ## Create a Solana RPC connection Create the Solana client connections (i.e. `rpc` and `rpcSubscriptions`) from any RPC URL or standard Solana network moniker (i.e. `devnet`, `localnet`, `mainnet` etc) using the `createSolanaClient()` function. ```typescript twoslash 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 use the URL provided by them. ### Solana client for localnet Developers can also connect to a local test validator (like [`solana-test-validator`](https://solana.com/docs/intro/installation#run-local-validator)) running on your computer. To create a Solana client for your local test validator: ```typescript twoslash import { createSolanaClient } from "gill"; const { rpc, rpcSubscriptions, sendAndConfirmTransaction } = createSolanaClient({ urlOrMoniker: "localnet", }); ``` The `urlOrMoniker` value of `localnet` will utilize the default test validator address and port of `http://127.0.0.1:8899`. If you need to connect to a different address/port, then simply pass in its entire URL. See [custom RPC URL](#solana-client-for-a-custom-rpc-url) below. ### Solana client for a custom RPC URL To create an RPC client for an custom RPC provider or service: ```typescript twoslash 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. Most commonly to get the [latest blockhash](#get-the-latest-blockhash) or [fetching a specific account](#fetch-an-account). ```typescript twoslash 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. #### Destructure and renaming response values Many of the Solana RPC responses will return a generic `value` attribute containing the typed response payload. It is a common practice to [destructure](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring) this generic `value` into a more aptly named variable, such as `latestBlockhash` (as demonstrated in the example below). ### Get the latest blockhash On Solana, the latest blockhash is uses as a sort of "recent timestamp" check within the transaction. To get the latest blockhash from your RPC: ```typescript twoslash import { createSolanaClient } from "gill"; const { rpc } = createSolanaClient({ urlOrMoniker: "devnet" }); // get the latest blockhash const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); ``` Only request this value *just before* you are going to use it your code. Since latest blockhashes are only valid for approximately 1-2 minutes, requesting it at the latest possible time in your codebase can help improve transaction landing rates. ### Fetch an account All the data stored on the Solana blockchain is stored in [accounts](https://solana.com/docs/core/accounts), including native SOL balance, tokens, and programs. The structure of an account's data and associated metadata information is called an `AccountInfo`. To get an account's `AccountInfo` from your RPC: ```typescript twoslash import { createSolanaClient, address } from "gill"; const { rpc } = createSolanaClient({ urlOrMoniker: "devnet" }); const accountToFetch = address("nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5"); // get the `AccountInfo` with (default) `base58` encoding for the data const { value: accountInfo } = await rpc.getAccountInfo(accountToFetch).send(); ``` By default, the `getAccountInfo` RPC method will utilize the `base58` encoding for the `data` within the account itself. This is fine for accounts with small amounts of data stored in them, but fetching accounts with larger amounts of `data` will result in an error with the `base58` encoding. It is **strongly** recommended to utilize `base64` encoding when fetching accounts from the blockchain to avoid the common errors when fetching with the default `base58` encoding. ```typescript twoslash import { createSolanaClient, address } from "gill"; const { rpc } = createSolanaClient({ urlOrMoniker: "devnet" }); const accountToFetch = address("nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5"); // get the `AccountInfo` with `base64` encoding for the data const { value: accountInfo } = await rpc .getAccountInfo(accountToFetch, { encoding: "base64" }) .send(); ``` An even better solution is to utilize the `fetchEncodedAccount()` function to fetch accounts, which always utilizes the `base64` encoding. ```typescript twoslash import { createSolanaClient, address, fetchEncodedAccount } from "gill"; const { rpc } = createSolanaClient({ urlOrMoniker: "devnet" }); const accountToFetch = address("nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5"); const account = await fetchEncodedAccount(rpc, accountToFetch); ``` These encoded accounts can then easily be decoded by the correct `Decoder` for the structure of the account's `data`. ### Using AbortControllers 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 them into `send()`: ```typescript twoslash 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 }); ``` ================================================ FILE: docs/content/docs/getting-started/meta.json ================================================ { "title": "Getting Started", "defaultOpen": true, "pages": ["client", "signers", "..."] } ================================================ FILE: docs/content/docs/getting-started/signers.mdx ================================================ --- title: Generate a signer description: Create a new keypair signer that can perform Solana signing operations. --- For most typical Solana transaction signing operations, you will be utilizing a `TransactionSigner`. This object type is capable of being "attached" to instructions and transaction to perform signing operations. The most common of which is a `KeyPairSigner`, which is able to be passed around to the various functions within gill to satisfies any `TransactionSigner` type requirements, like when building instructions or creating transactions. Unless otherwise specifically noted in the gill documentation, the term "signer" refers to `TransactionSigner` and usually a `KeyPairSigner`. ## Generating a keypair signer For various Solana development tasks, you may need to generate a new signer. Including when creating a new account, generating reference keys for transactions, or [creating tokens](/docs/guides/tokens/create-token). The `generateKeyPairSigner()` function allows you to generate a new random `KeyPairSigner` (which satisfies the `TransactionSigner` type) to perform signing operations. ```typescript twoslash import { generateKeyPairSigner, type KeyPairSigner } from "gill"; const signer = await generateKeyPairSigner(); ``` ### Non-extractable by default Under the hood, a `KeyPairSigner` utilize the [Web Crypto APIs](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) to improve security. These signers are non-extractable by default; 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 Extractable keypairs are less secure and should not be used unless you REALLY need to [save a keypair](/docs/guides/loading-and-saving-keypairs) 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 twoslash import { generateExtractableKeyPairSigner } from "gill"; const signer = await generateExtractableKeyPairSigner(); ``` 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 or environment variable). ## Create a signer without the secret key If your Solana application allows users to sign transaction's by way of connecting their wallet (e.g. Backpack, Phantom, Solflare, etc) to your app, you will not have access to their secret key material. You will need to create a "noop signer" in order to satisfy the `TransactionSigner` type, such as the `createTransaction()` functions's `feePayer`: ```typescript twoslash import { createNoopSigner, type Address, createTransaction } from "gill"; const wallet = "nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5" as Address; const signer = createNoopSigner(wallet); const transaction = createTransaction({ version: "legacy", feePayer: signer, instructions: [], }); ``` ================================================ FILE: docs/content/docs/guides/codama.mdx ================================================ --- title: Generate Solana program clients with Codama description: "Step-by-step guide on how to use Codama to generate a Solana program client from an IDL." --- [Codama](https://github.com/codama-idl/codama) is a tool that allows developers to take a Solana program's IDL and generate client libraries (e.g. JavaScript, Rust, etc) for use by other applications. Codama handles all the complexities of crafting Solana instructions into an IDL, a config file, and importing a function. Codama is quickly becoming part of the defacto standard tooling for Solana developers. Even the new Solana Program Library (SPL) program clients are now generated using Codama. See the [solana-program](https://github.com/solana-program) organization on GitHub. This guide will walk you through the step-by-step of taking a Solana program IDL and generating a JavaScript/TypeScript client library that utilizes the `gill` library. Afterwards, you can import the various functions, types, and more into your application and easily interact with the specific Solana program your IDL is from. This guide was written using `v1.1.1` of the Codama CLI. If you experience any issues or discrepancies on newer versions, please [open an issue](https://github.com/gillsdk/gill/issues/new?title=%5BCodama%5D%20) on the gill repo here so we can investigate and update this guide. ## Install the Codama CLI To get started, you first need to install the [Codama CLI](https://github.com/codama-idl/codama/tree/main/packages/cli) inside your repo: ```package-install @codama/cli ``` The Codama CLI is **not** meant to be installed globally, but rather version controlled with your repo. Ensuring your generated program client will continue to be compatible with the rest of your code base (should breaking changes happen in the future). This will allow you to execute the Codama CLI via your package manager's "run" command: ```shell npx codama --version # output: 1.1.1 ``` ## Create a Codama config The Codama CLI utilizes a config file to declare which "renderers" should be used and other assorted logic. Gill provides the `createCodamaConfig()` helper function to simplify this setup. Utilizing a JavaScript Codama config file (e.g. `codama.js`) is recommended for use with gill. If you need more control, you can always fallback to [manually initializing your Codama config](#manually-initialize-a-codama-config) using a JSON file. Create a `codama.js` file to utilize the `createCodamaConfig()` function, providing your program specific configuration: ```ts import { createCodamaConfig } from "gill"; export default createCodamaConfig({ idl: "program/idl.json", clientJs: "clients/js/src/generated", }); ``` - `idl` - should be the relative path to your program's IDL JSON file - `clientJs` - should be the relative path to where your generated program client should be stored within your repo It's recommended to store your generated program client in a `generated` directory. This will allow you to easily co-locate other manually crafted functions. See [this example](https://github.com/solana-program/token-2022/tree/main/clients/js/src) of the SPL Token Extension program client doing exactly this. ## Generate your program client with Codama With your Codama config file setup, you can use the `run` command to generate your JavaScript/TypeScript client, storing the generated output in your configured destination: ```shell npx codama run js ``` You can now import your newly generated client library from anywhere in your code base or publish as an installable package for any application to easily consume and interact with your program. ## Multiple program clients in a single repo At the time of writing this guide, the latest Codama CLI (`v1.1.1`) does not support multiple program clients in a single Codama config file. If you have multiple Solana program IDLs in a single repo, you will need to create a separate config file for each. We recommend creating a shell script to generate all the program clients with a single command. For example, the gill library itself utilizes a shell script similar to this one: ```shell #!/usr/bin/env bash set -euo pipefail # generate all the codama clients (per their respective config files) npx codama run js --config ./idls/token_metadata/codama.js npx codama run js --config ./idls/another_program/codama.js # run the repo's prettier settings on the generated files npx prettier --write './packages/gill/src/programs/**/generated/{*,**/*}.{ts,js}' ``` This script will execute the `codama run js` command for each program by explicitly passing in the config file to each. Then you can easily execute any additional commands you desire, like performing your standard prettier formatting operations. You can find the [exact shell script](https://github.com/gillsdk/gill/blob/master/idls/build-codama-clients.sh) that gill uses in our repo to ship some of the program clients gill includes. ## Manually initialize a Codama config For more complex scenarios, you may desire to have more control over the Codama config file. In this case, use the `codama.json` file and manually configure it to your needs. ### Create a codama.json file Run the Codama `init` command and follow the prompts to scaffold your `codama.json` config file: ```shell npx codama init ``` You will be asked a few questions in order to generate the config file for your IDL: - location of the IDL within your repo (e.g. `program/idl.json`) - the type of clients to generate (make sure to select "JavaScript client") - where your generated JavaScript client should be output to (e.g. `clients/js/src/generated`) It's recommended to store your generated program client in a `generated` directory. This will allow you to easily co-locate other manually crafted functions. See [this example](https://github.com/solana-program/token-2022/tree/main/clients/js/src) of the SPL Token Extension program client doing exactly this. Your newly created `codama.json` config file should look something like this: ```json { "idl": "program/idl.json", "before": [], "scripts": { "js": { "from": "@codama/renderers-js", "args": ["clients/js/src/generated"] } } } ``` At the time of writing this guide, the latest Codama CLI version of `v1.1.1` does not support multiple program clients in a single `codama.json` config file. You will have to create a separate config file for each Solana program. See [Multiple program clients in a single repo](#multiple-program-clients-in-a-single-repo) for more info. ### Replace @solana/kit imports with gill By default, Codama will utilize the `@solana/kit` package for the imported symbols within the generated client. Since the gill library includes Kit (so you do not have to manually install it), we need to instruct Codama to utilize `gill` instead. Update your `codama.json` config file to include the following `dependencyMap` settings by passing the following object as the **second value** of the JavaScript renderer's `args` array: ```json { "idl": "program/idl.json", "before": [], "scripts": { "js": { "from": "@codama/renderers-js", "args": [ "clients/js/src/generated", { "dependencyMap": { "solanaAccounts": "gill", "solanaAddresses": "gill", "solanaCodecsCore": "gill", "solanaCodecsDataStructures": "gill", "solanaCodecsNumbers": "gill", "solanaCodecsStrings": "gill", "solanaErrors": "gill", "solanaInstructions": "gill", "solanaOptions": "gill", "solanaPrograms": "gill", "solanaRpcTypes": "gill", "solanaSigners": "gill" } } ] } } } ``` ================================================ FILE: docs/content/docs/guides/index.mdx ================================================ --- title: Guides description: "Collection of helpful task-based references and code snippets for Solana developers." --- ## Wallet & Keys Learn how to manage Solana keypairs and signers in your applications, from loading existing wallets to creating and saving new ones. Load and save Solana keypairs from files and environment variables in Node.js, perfect for backend applications, serverless functions, and CI/CD pipelines. ## Tokens on Solana The following [Solana token guides](/docs/guides/tokens) demonstrate the most common tasks when working with SPL tokens on Solana. Learn how to create a new Solana token, with metadata, using either the legacy Token Program or Token Extensions program. Learn how to mint new token supply to a wallet using the mint authority, including automatically creating the destination's Associated Token Account. Learn how to transfer tokens between wallets using the token authority, including automatically creating the destination's Associated Token Account. Learn how to permanently remove tokens from circulation by burning them from a wallet's Associated Token Account. Learn how to read and parse token metadata from the Solana blockchain, including both legacy Token Metadata Program accounts and Token Extensions (Token22) inline metadata. ## Transactions Master advanced transaction techniques including reference keys for priority fees and implementing Solana Pay for payment flows. Learn how to insert reference keys into Solana transactions to enable priority fees and improve transaction processing. Implement the Solana Pay protocol in your applications to create payment requests, transfer requests, and transaction requests. ## Developer Tools Streamline your Solana development workflow with code generation and automation tools. Use Codama to automatically generate type-safe Solana program clients from IDL files, saving time and reducing errors. ================================================ FILE: docs/content/docs/guides/loading-and-saving-keypairs.mdx ================================================ --- title: Loading and Saving Keypairs description: Guide on loading and saving keypairs from files and environment variables in server-side environments. --- When building server-side applications, backend services, or CLI tools, you'll often need to manage keypairs for signing transactions. The `gill/node` package provides utilities for loading keypairs from the filesystem or environment variables, and even simplifies saving them for later use. This guide covers the essential functions for keypair management in JavaScript server environments including Node.js, Bun, and others. ## Installation To get started, install the `gill` package: ```package-install gill ``` The server-side keypair utilities are available under the `gill/node` export path: ```ts import { loadKeypairSignerFromFile, saveKeypairSignerToFile } from "gill/node"; ``` ## Available Functions The `gill/node` package provides the following keypair management functions: **Loading functions:** - `loadKeypairSignerFromFile()` - Load a keypair signer from a filesystem JSON file (defaults to Solana CLI path) - `loadKeypairSignerFromEnvironment()` - Load a keypair signer from an environment variable (JSON array format) - `loadKeypairSignerFromEnvironmentBase58()` - Load a keypair signer from an environment variable (base58 format) **Saving functions:** - `saveKeypairSignerToFile()` - Save an extractable keypair signer to a filesystem JSON file - `saveKeypairSignerToEnvFile()` - Save an extractable keypair signer to a `.env` file All functions also have non-`Signer` variants (e.g., `loadKeypairFromFile`) that work with raw `CryptoKeyPair` objects instead of `KeyPairSigner` objects. See the [Related Functions](#related-functions) section for more details. ## Loading Keypairs from Environment Variables Environment variables are the recommended approach for loading Solana keypairs in production environments, serverless functions, and CI/CD pipelines. This method provides better security and portability compared to storing private keys in files on disk. When you load a keypair from an environment variable, the keypair data is stored as a string value in your system's environment (like `process.env` in Node.js). This approach is particularly useful in cloud deployments, containerized applications, GitHub Actions, and other automated workflows where file system access may be limited or where secrets are managed through environment configuration. ### JSON Array Format The most common format for storing keypairs in environment variables is the JSON array format (a stringified array of 64 numbers representing the keypair bytes). This is the same format as the keypairs generated and used by the Solana CLI. ```ts twoslash import { loadKeypairSignerFromEnvironment } from "gill/node"; // Expects process.env.MY_KEYPAIR to contain: [123,45,67,89,...] const signer = await loadKeypairSignerFromEnvironment("MY_KEYPAIR"); console.log("Loaded signer:", signer.address); ``` Your `.env` file would look like this: ```bash MY_KEYPAIR=[123,45,67,89,...] # or MY_KEYPAIR="[123,45,67,89,...]" ``` ### Base58 Format Some Solana wallets, browser extensions, and third-party tools export keypairs as base58-encoded strings instead of JSON arrays. The base58 format is a compact string representation that's easier to copy and paste, and is commonly used in certain wallet applications. Use `loadKeypairSignerFromEnvironmentBase58()` when your keypair is stored in this format: ```ts twoslash import { loadKeypairSignerFromEnvironmentBase58 } from "gill/node"; // Expects process.env.MY_KEYPAIR to contain a base58 string const signer = await loadKeypairSignerFromEnvironmentBase58("MY_KEYPAIR"); console.log("Loaded signer:", signer.address); ``` Your `.env` file with base58 format would look like this: ```bash MY_KEYPAIR=5J7WTMRm9FG6TvmzJkKp3qQ... # or MY_KEYPAIR="5J7WTMRm9FG6TvmzJkKp3qQ..." ``` Both environment variable loading functions will throw an error if the specified environment variable is not set. Make sure your environment variables are properly configured before running your application. ## Saving Keypairs to Environment Files When setting up a server-side application or preparing configuration for deployment, you may need to generate new keypairs and save them to environment files for later use. The `saveKeypairSignerToEnvFile()` function generates a `.env` file entry that you can use in development or copy to your production environment's secret management system. This is particularly useful for: - Initial project setup when you need to create a new Solana wallet for your backend - Local development configurations - Generating keypairs that will be deployed to cloud platforms (AWS, Google Cloud, Vercel, etc.) - CI/CD pipeline configuration **Security Warning**: Only [**extractable** keypairs](/docs/getting-started/signers#generating-extractable-keypairs-and-signers) can be saved to environment files. You must generate keypairs using `generateExtractableKeyPairSigner()` instead of the regular `generateKeyPairSigner()`. This is a deliberate security feature - regular keypairs keep the private key material locked in memory and cannot be exported. Here's how to generate and save a keypair to a `.env` file: ```ts twoslash import { generateExtractableKeyPairSigner } from "gill"; import { saveKeypairSignerToEnvFile } from "gill/node"; // Generate an extractable keypair const signer = await generateExtractableKeyPairSigner(); // Save to .env file (defaults to ./.env) await saveKeypairSignerToEnvFile(signer, "MY_KEYPAIR"); console.log("Keypair saved to .env file"); ``` This will append to your `.env` file with a helpful comment showing the public address: ```bash # Solana Address: 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU MY_KEYPAIR=[123,45,67,89,...] ``` ### Custom .env File Path You can specify a custom path for the environment file: ```ts twoslash import { generateExtractableKeyPairSigner } from "gill"; import { saveKeypairSignerToEnvFile } from "gill/node"; const signer = await generateExtractableKeyPairSigner(); // Save to a custom .env file await saveKeypairSignerToEnvFile(signer, "MY_KEYPAIR", "./config/.env.production"); ``` The function will throw an error if the environment variable name already exists in `process.env`. This prevents accidentally overwriting existing configuration. ## Loading Keypairs from Files For local development, testing, and CLI tools, loading Solana keypairs from JSON files on your filesystem is often the most convenient approach. The Solana CLI (Command Line Interface) stores keypairs as JSON files by default, making this method seamlessly compatible with existing Solana tooling. File-based keypair loading is ideal for: - **Local development** - Working on your laptop or development machine - **Solana CLI integration** - Using keypairs generated with `solana-keygen` - **CLI tools and scripts** - Node.js or Bun scripts that interact with Solana - **Testing and debugging** - Quick iteration without managing environment variables The keypair files contain a JSON array of 64 bytes (numbers from 0-255) representing the full keypair. ### Using the Default Solana CLI Path By default, `loadKeypairSignerFromFile()` loads the keypair from the default Solana CLI keypair path (`~/.config/solana/id.json`): ```ts twoslash import { loadKeypairSignerFromFile } from "gill/node"; const signer = await loadKeypairSignerFromFile(); console.log("Loaded signer:", signer.address); ``` ### Using a Custom File Path You can also specify a custom path to load keypairs from other locations: ```ts twoslash import { loadKeypairSignerFromFile } from "gill/node"; const signer = await loadKeypairSignerFromFile("./my-keypair.json"); console.log("Loaded signer:", signer.address); ``` The file should contain a JSON array of 64 numbers (the standard Solana CLI format): ```json [123, 45, 67, 89, ...] ``` The `loadKeypairSignerFromFile()` function supports tilde (`~`) expansion in file paths, so you can use paths like `~/wallets/my-keypair.json`. ## Saving Keypairs to Files When building CLI tools, development scripts, or testing utilities, you may need to generate new Solana keypairs and save them as JSON files on your local filesystem. The `saveKeypairSignerToFile()` function makes this easy by creating Solana CLI-compatible keypair files that can be used across your development workflow. Common use cases for saving keypairs to files include: - **Generating test wallets** - Creating throwaway keypairs for development and testing on devnet/testnet - **CLI tool development** - Building tools that need to create and manage multiple wallets - **Development automation** - Scripts that set up local development environments - **Backup and portability** - Creating keypair files that can be easily copied or backed up **Important Security Consideration**: Only [**extractable** keypairs](/docs/getting-started/signers#generating-extractable-keypairs-and-signers) can be saved to files or environment variables. Regular keypairs generated with `generateKeyPairSigner()` are **non-extractable** by design for security reasons - the private key material is locked in memory and cannot be exported. To save a keypair, you must use `generateExtractableKeyPairSigner()` instead. ### Generating and Saving an Extractable Keypair ```ts twoslash import { generateExtractableKeyPairSigner } from "gill"; import { saveKeypairSignerToFile } from "gill/node"; // Generate an extractable keypair const signer = await generateExtractableKeyPairSigner(); console.log("Generated signer:", signer.address); // Save to a JSON file const saved = await saveKeypairSignerToFile(signer, "./my-new-keypair.json"); if (saved) { console.log("Keypair saved successfully!"); } ``` The function validates the save by attempting to reload the file, returning `true` if successful. The file path must end with `.json` extension. The function will create the file if it doesn't exist, or overwrite it if it does. ## Best Practices ### Development vs Production - **Development**: Use `loadKeypairSignerFromFile()` to load from the Solana CLI default path or project-specific files - **Production**: Use `loadKeypairSignerFromEnvironment()` or `loadKeypairSignerFromEnvironmentBase58()` to load from environment variables ### Security Considerations Never commit keypair files or `.env` files containing private keys to version control. Add them to your `.gitignore` file immediately. Here are some security best practices: 1. **Use extractable keypairs only when necessary**: Regular (non-extractable) keypairs are more secure because the private key material cannot be extracted from memory. Only use extractable keypairs when you actually need to save or export them. 2. **Protect your files**: If you must store keypairs in files, ensure they have appropriate file permissions (e.g., `chmod 600` on Unix systems). 3. **Use environment variables in production**: Environment variables are generally more secure than files for production deployments, as they don't persist on disk and can be managed by secret management systems. 4. **Rotate keys regularly**: Implement a key rotation strategy, especially for production systems. 5. **Use secret management services**: For production environments, consider using dedicated secret management services like AWS Secrets Manager, Google Secret Manager, or HashiCorp Vault instead of plain environment variables. ### Common Patterns Here's a typical pattern for a backend service that works in both development and production: ```ts twoslash import { loadKeypairSignerFromFile, loadKeypairSignerFromEnvironment } from "gill/node"; // Load keypair based on environment const signer = process.env.NODE_ENV === "production" ? await loadKeypairSignerFromEnvironment("KEYPAIR") : await loadKeypairSignerFromFile(); // Uses Solana CLI default console.log("Loaded signer for", process.env.NODE_ENV, ":", signer.address); ``` ## Related Functions If you need to work with raw `CryptoKeyPair` objects instead of `KeyPairSigner` objects, the following functions are also available: - `loadKeypairFromFile()` - Returns `CryptoKeyPair` - `loadKeypairFromEnvironment()` - Returns `CryptoKeyPair` - `loadKeypairFromEnvironmentBase58()` - Returns `CryptoKeyPair` - `saveKeypairToFile()` - Accepts `CryptoKeyPair` - `saveKeypairToEnvFile()` - Accepts `CryptoKeyPair` The `*Signer` variants are recommended for most use cases as they return ready-to-use signers for transactions. ================================================ FILE: docs/content/docs/guides/meta.json ================================================ { "title": "Guides", "pages": ["tokens", "..."] } ================================================ FILE: docs/content/docs/guides/reference-keys.mdx ================================================ --- title: Reference Keys description: "Guide on how to insert reference keys into Solana transactions." --- On Solana, a "reference key" is a unique, single-use, non-signer address that is put inside of a transaction in order to track its completion on the network. This is a common technique used within the [Solana Pay](https://docs.solanapay.com/spec#reference) and [Blockchain Link (Blink)](https://solana.com/solutions/actions) specifications. Until a Solana transaction is signed by the "fee payer", it does not have a signature; making it difficult to determine if and when the transaction has landed on-chain. Inserting a reference key into the transaction allows developers to programmatically call the [`getSignaturesForAddress`](https://solana.com/docs/rpc/http/getsignaturesforaddress) RPC method to determine if the transaction has landed. Triggering any desired business logic in their application. ## Add a reference key to a transaction Inserting a reference key inside of a transaction can be accomplished by adding the desired address as a "non-signer" account key in any supporting instruction. Within gill, there are two functions for inserting reference keys: 1. `insertReferenceKeyToTransactionMessage()` - insert just one reference key 2. `insertReferenceKeysToTransactionMessage()` - insert multiple reference keys Under the hood, these "insert reference key" functions search the instructions inside the transaction and manually inserts the reference key address as a non-signer on the [first supported instruction](#program-errors-due-to-reference-keys). The following is an example of constructing a transaction using the `createTransaction()` then inserting the reference key using the "pipe method". This is the most recommended way to perform this: ```ts twoslash // @noErrors import { pipe, createTransaction, generateKeyPairSigner, insertReferenceKeysToTransactionMessage, } from "gill"; // generate a reference key address const { address: reference } = await generateKeyPairSigner(); const transaction = pipe( createTransaction({ version: "legacy", feePayer: signer, instructions: [ getAddMemoInstruction({ memo: "gm world!", }), ], latestBlockhash, // setting a CU limit ensures there is at least one non-memo instruction computeUnitLimit: 5000, }), (tx) => insertReferenceKeysToTransactionMessage([reference], tx), ); ``` The above transaction will have two instructions in it: 1. SPL memo instruction (via the `getAddMemoInstruction` function) 2. compute unit limit instruction (via the `computeUnitLimit` value) To insert a reference key into a transaction, the transaction **must** have at least one non-memo instruction. See [Program errors due to reference keys](#program-errors-due-to-reference-keys) below for details. This transaction can be signed and sent to the network. Then anyone can [monitor for the reference key](#monitoring-for-a-reference-key) and trigger their desired business logic. ### Add to an existing transaction If your application is not directly creating the transaction, like when consuming external APIs, you can still add a reference key to the transaction. First fetch the transaction from your desired external source, then insert the reference key as follows: ```ts twoslash // @noErrors import { createTransaction, generateKeyPairSigner, insertReferenceKeysToTransactionMessage } from "gill"; // note: `transaction` is mutable here so we can modify it later to insert the reference key let { transaction } = await fetchTransactionFromExternalSource(...); // ... [your other business logic here] // generate a reference key address const { address: reference } = await generateKeyPairSigner(); transaction = insertReferenceKeysToTransactionMessage([reference], transaction); ``` If the transaction obtained from an external source or API is already signed, you cannot modify the transaction to insert a reference key. Your only option is to wipe the existing signatures, insert the reference key, and resign. Or encourage your external API provider to accept a reference key input to their API endpoints that return signed transactions. This transaction can be signed and sent to the network. Then anyone can [monitor for the reference key](#monitoring-for-a-reference-key) and trigger their desired business logic. ## Program errors due to reference keys Some programs on the Solana network do not allow adding additional non-signer accounts due to the way they are coded. Often times, these programs expect all provided account keys to be writable or a signer. If they receive additional or unexpected accounts, they will error. As such, gill's "insert reference key" functions attempt to prevent these errors by skipping instructions from these programs. The following programs are known to fall into this condition and are skipped when inserting reference keys: - `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` - SPL memo program If you locate another commonly used program that results in errors due to inserting additional account keys, please [open an issue](https://github.com/gillsdk/gill/issues?title=%5BReference%20Key%20Program%5D%20) and share with the community. ## Monitoring for a reference key After a reference key has been inserted into a transaction, and that transaction is expected to have been signed then sent to the network for confirmation, you use the [`getSignaturesForAddress`](https://solana.com/docs/rpc/http/getsignaturesforaddress) RPC method to determine if the transaction has been confirmed. Using gill's `getOldestSignatureForAddress()` function, we can easily fetch the oldest signature that includes the reference key. If the reference key is not found, it will throw an error. ```ts twoslash // @noErrors import { createSolanaClient, address, getOldestSignatureForAddress } from "gill"; const { rpc, sendAndConfirmTransaction } = createSolanaClient({ urlOrMoniker: "devnet", }); const reference = address("..."); try { const { signature } = await getOldestSignatureForAddress(rpc, reference); const transaction = await rpc.getTransaction(signature).send(); // ... [validate the `transaction` performed the expected actions on-chain] // perform business logic for a successful transaction } catch (err) { // handle errors } ``` While you can utilize the raw response from the `getSignaturesForAddress` RPC method, your application will need to manually handle recursively fetching signatures until you locate the oldest one (i.e. the only one we care about). Your application can then fetch and process the transaction with the signature returned by `getOldestSignatureForAddress()`, validating the transaction is as expected. After your application validates the oldest transaction accomplishes the expected on-chain actions (e.g. correct token balance changes, etc), you can proceed with any of your "success case" business logic and handle errors accordingly. ### Security concerns Due to the architecture of Solana transactions, reference keys can be easily spoofed inside of transactions (i.e. anyone can insert them into transactions). Just because your reference key was located in a confirmed transaction, does **NOT** mean the transaction is what you expect it to be. It is crucially important that your application validates the transaction associated with the oldest signature obtained. Otherwise, your application can be vulnerable to various attacks. ================================================ FILE: docs/content/docs/guides/solana-pay.mdx ================================================ --- title: Solana Pay description: "Guide on how to utilize the Solana Pay protocol using the gill sdk." --- Solana Pay is a standardized protocol for encoding transaction requests within URLs, enabling payments and other blockchain interactions across the Solana ecosystem. The protocol supports both simple transfer requests for direct payments and interactive "transaction requests" for more complex use cases like merchant checkouts or scenarios where you need a non-user controlled keypair to pre-sign a transaction. The gill SDK provides a complete, type-safe implementation of the [Solana Pay specification](https://github.com/solana-foundation/solana-pay/blob/master/SPEC.md) with comprehensive validation and error handling. This guide covers how to create, parse, and validate Solana Pay URLs using [`@gillsdk/solana-pay`](https://www.npmjs.com/package/@gillsdk/solana-pay). ## Request Types Solana Pay supports two distinct request types, each suited for different use cases: **Transfer Requests:** - Non-interactive request for SOL or SPL token transfer - All payment details encoded directly in the URL - Wallet constructs and submits transaction immediately - Best for simple payments, invoices, and QR code payments - No server infrastructure required ```text solana:nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5?amount=1.5&label=Coffee+Shop ``` **Transaction Requests:** - Interactive, multi-step checkout flow - URL points to an HTTPS endpoint that provides transaction details - Requires GET request for merchant info, then POST request for transaction - Best for complex transactions, server signers, dynamic pricing, and merchant integrations - Requires server-side implementation and HTTPS ``` solana:https://checkout.usedecal.com/api/solana-pay-transaction ``` ## Install the Solana Pay SDK This guide requires both the `gill` SDK and the [`@gillsdk/solana-pay`](https://www.npmjs.com/package/@gillsdk/solana-pay) package. ```package-install gill @gillsdk/solana-pay ``` ## Transfer Requests Transfer requests are non-interactive URLs that encode all SOL or SPL token transfer details directly in the URL parameters. When a wallet app scans or receives a transfer request URL, it can construct and submit the transaction without any additional network requests. The Solana Pay Transfer Request's `amount` parameter is used to denote how many tokens are requested in the transfer transaction. Crucially, this `amount` is the UI amount (aka human-readable amount), not the raw blockchain amount. - For SOL, this means SOL (not lamports) - For SPL tokens, this means the token's display amount (not the raw token amount) ### Basic SOL Transfer The simplest transfer request specifies only a recipient address: ```ts twoslash import { encodeSolanaPayURL } from "@gillsdk/solana-pay"; import { address } from "gill"; const recipient = address("nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5"); const url = encodeSolanaPayURL({ recipient }); // → "solana:nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5" ``` To specify a specific amount of tokens to be transferred by the user, add the `amount` parameter. The `amount` field is the UI amount (human-readable amount), not the raw blockchain amount. For SOL, this means SOL (not lamports). For SPL tokens, this means the token's display amount (not the raw token amount). ```ts twoslash import { encodeSolanaPayURL } from "@gillsdk/solana-pay"; import { address } from "gill"; const recipient = address("nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5"); // Request 1.5 SOL const url = encodeSolanaPayURL({ recipient, amount: 1.5, }); // → "solana:nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5?amount=1.5" ``` ### SPL Token Transfers To request a token transfer instead of SOL, specify the `splToken` parameter with the token mint address. The `amount` is still the UI amount based on the token's decimals (not the raw token amount). ```ts twoslash import { encodeSolanaPayURL } from "@gillsdk/solana-pay"; import { address } from "gill"; const recipient = address("nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5"); const usdcMint = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); // Request 100 USDC const url = encodeSolanaPayURL({ recipient, amount: 100, splToken: usdcMint, }); ``` When `splToken` is specified, the wallet will create a token transfer instruction using the Associated Token Account convention. ### Reference Keys Reference keys enable transaction tracking by including one or more public keys as read-only keys in the transaction. This allows you to query for specific transactions using the `getSignaturesForAddress` RPC method. ```ts twoslash import { encodeSolanaPayURL } from "@gillsdk/solana-pay"; import { address } from "gill"; const recipient = address("nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5"); const referenceKey = address("Href9m18T7a9TKgS21e9Y9Aa1yce1Sw3TjXbRJ9Exm5P"); const url = encodeSolanaPayURL({ recipient, amount: 1, reference: referenceKey, }); ``` You can include multiple reference keys by passing an array: ```ts twoslash import { encodeSolanaPayURL } from "@gillsdk/solana-pay"; import { address } from "gill"; const recipient = address("nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5"); const ref1 = address("Href9m18T7a9TKgS21e9Y9Aa1yce1Sw3TjXbRJ9Exm5P"); const ref2 = address("2nT8kNX7YvTBMekVWKqpRdDKQ7z9r8FVq4VNSS3bH4Qo"); const url = encodeSolanaPayURL({ recipient, amount: 1, reference: [ref1, ref2], }); ``` For more details on generating and using reference keys, see the [Reference Keys guide](/docs/guides/reference-keys). ### Labels, Messages, and Memos Solana Pay Transfer requests support additional metadata to provide context: - **label**: Describes the source of the request (e.g., merchant name) - **message**: Describes the nature of the transfer (e.g., what's being purchased) - **memo**: Text included in an SPL Memo instruction in the transaction ```ts twoslash import { encodeSolanaPayURL } from "@gillsdk/solana-pay"; import { address } from "gill"; const recipient = address("nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5"); const usdcMint = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); const url = encodeSolanaPayURL({ recipient, amount: 9.67, label: "Coffee Shop", message: "Payment for espresso and croissant", memo: "Order #12345", splToken: usdcMint, }); ``` Memo data is recorded publicly onchain and should not contain sensitive information. ## Transaction Requests Transaction requests are interactive URLs that point to an HTTPS endpoint. Unlike transfer requests, transaction requests require the wallet app to make HTTP requests to fetch transaction details from a server. ### Creating Transaction Request URLs Transaction requests require an HTTPS URL and optionally include `label` and `message` metadata: ```ts twoslash import { encodeSolanaPayURL } from "@gillsdk/solana-pay"; const url = encodeSolanaPayURL({ link: new URL("https://merchant.example.com/checkout"), label: "Example Merchant", message: "Purchase item #42", }); // → "solana:https%3A%2F%2Fmerchant.example.com%2Fcheckout?label=Example+Merchant&message=Purchase+item+%2342" ``` Transaction request links MUST use HTTPS. The `@gillsdk/solana-pay` SDK validates this requirement and will throw an error for non-HTTPS URLs. If your link includes query parameters, they will be properly URL-encoded: ```ts twoslash import { encodeSolanaPayURL } from "@gillsdk/solana-pay"; const url = encodeSolanaPayURL({ link: new URL("https://merchant.example.com/api?item=123&quantity=2"), }); ``` ### Fetching Merchant Information (GET Request) When a wallet receives a transaction request URL, it first makes a GET request to fetch the merchant's label and icon: ```ts twoslash import { solanaPayTransactionRequest } from "@gillsdk/solana-pay"; const response = await solanaPayTransactionRequest.get(new URL("https://merchant.example.com/api")); console.log(response.label); // "Example Merchant" console.log(response.icon); // "https://merchant.example.com/icon.svg" ``` The icon URL must point to an SVG, PNG, WebP, JPG, or JPEG file. ### Requesting a Transaction (POST Request) After displaying the merchant information to the user, the wallet makes a POST request with the user's account to receive the transaction to sign: ```ts import { solanaPayTransactionRequest } from "@gillsdk/solana-pay"; import { address } from "gill"; const userAccount = address("nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5"); const response = await solanaPayTransactionRequest.post( new URL("https://merchant.example.com/api"), { account: userAccount }, ); const transaction = response.transaction; const message = response.message; // Optional message to display ``` The underlying response contains a base64-encoded transaction that the wallet will present to the user for signing. The `@gillsdk/solana-pay` sdk will automatically decode and deserialize this transaction from a `string` to a ready to sign `Transaction`. ## Parsing Solana Pay URLs The `parseSolanaPayURL` function parses any Solana Pay URL and returns the appropriate typed structure: ```ts twoslash import { parseSolanaPayURL } from "@gillsdk/solana-pay"; const url = "solana:nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5?amount=1"; const parsed = parseSolanaPayURL(url); // Use a type guard to determine the request type if ("link" in parsed) { // Transaction request const { link, label, message } = parsed; console.log("Transaction request to:", link.href); } else { // Transfer request const { recipient, amount, splToken, reference, label, message, memo } = parsed; console.log("Transfer request for:", amount, "to", recipient); } ``` The parser automatically validates the URL format and throws a `SolanaPayParseURLError` if the URL is invalid. ## Response Validation For advanced use cases where you need to manually validate API responses, the SDK provides validation functions: ### Validating GET Responses ```ts twoslash import { parseSolanaPayGetResponse } from "@gillsdk/solana-pay"; const data = await fetch("https://merchant.example.com/api").then((r) => r.json()); const validated = parseSolanaPayGetResponse(data); // Ensures data has required 'label' and 'icon' fields with correct formats ``` ### Validating POST Responses ```ts twoslash import { parseSolanaPayPostResponse } from "@gillsdk/solana-pay"; const data = await fetch("https://merchant.example.com/api", { method: "POST", body: JSON.stringify({ account: "nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5" }), }).then((r) => r.json()); const validated = parseSolanaPayPostResponse(data); // Ensures data has required 'transaction' field and optional 'message' ``` Both functions throw a `SolanaPayResponseError` if validation fails. The gill SDK enforces several security measures: - **HTTPS Requirement**: Transaction request links must use HTTPS to prevent man-in-the-middle attacks - **URL Length Validation**: URLs are limited to 2048 characters - **Comprehensive Input Validation**: All addresses, amounts, and other parameters are validated before encoding - **Transaction Validation**: Wallets should validate all transaction details before prompting the user to sign Always validate transactions received from untrusted sources and never sign transactions without user review. ## Summary The `@gillsdk/solana-pay` package provides everything needed to implement Solana Pay in your application: - **Transfer Requests**: Create payment URLs for SOL and SPL tokens with optional reference tracking - **Transaction Requests**: Build interactive checkout flows with HTTPS endpoints - **URL Parsing**: Parse and validate any Solana Pay URL with full type safety - **Response Handling**: Fetch and validate merchant information and transactions For more information on the Solana Pay protocol, refer to the [official specification](https://github.com/solana-foundation/solana-pay/blob/master/SPEC.md). ================================================ FILE: docs/content/docs/guides/tokens/burn-tokens.mdx ================================================ --- title: Burn Tokens description: Learn how to burn tokens from a wallet using the gill JavaScript library. --- Burning tokens permanently removes them from circulation by destroying them from a wallet's Associated Token Account (ATA). The token account `authority` (owner) must sign the transaction to authorize the burn. This guide demonstrates how to burn tokens using the [`gill` package](https://www.npmjs.com/package/gill) with the `getBurnCheckedInstruction` from `gill/programs`. ## Install gill Install gill using the core `gill` library: ```package-install gill ``` import { PackageBadges } from "@/components/package-badges"; ## Create an RPC connection In order to send transactions and/or fetch data from the Solana blockchain, you will need a client connection. You can easily create a Solana client connection using the `createSolanaClient()` function. The `urlOrMoniker` can be either a Solana network moniker (e.g. `devnet`, `mainnet`, `localnet`) or a full URL of your RPC provider. ```ts twoslash import { createSolanaClient } from "gill"; const { rpc, sendAndConfirmTransaction } = createSolanaClient({ urlOrMoniker: "devnet", // `mainnet`, `localnet`, etc }); ``` Using a 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. ## Prepare a Signer Every Solana transaction requires at least one "signer" to be the fee payer for the transaction. When burning tokens, the `authority` (the token account owner) must also be a signer to authorize the burn. ### Load a signer from a local keypair file For backend scripts and some server environments, you can load a signer from your local filesystem: ```ts twoslash import { type KeyPairSigner } from "gill"; import { loadKeypairSignerFromFile } from "gill/node"; // This defaults to the file path used by the Solana CLI: `~/.config/solana/id.json` const signer: KeyPairSigner = await loadKeypairSignerFromFile(); console.log("signer:", signer.address); ``` ## Understanding token amounts When burning tokens, the `amount` you provide is in raw base units, not human-readable units. The conversion depends on the `decimals` value of your token mint. For example, if your token has `decimals = 9` (the most common for fungible tokens): - `1_000_000_000` (1e9) = **1 token** - `5_000_000_000` (5e9) = **5 tokens** - `1_000_000` (1e6) = **0.001 tokens** With `decimals = 9`, to burn **100 tokens** you would set `amount` to `100_000_000_000` (100e9). With `decimals = 6`, to burn **100 tokens** you would set `amount` to `100_000_000` (100e6). Always check your token's decimals to calculate the correct raw amount. ## Build the burn transaction To burn tokens, use the `getBurnCheckedInstruction()` from `gill/programs`. This is the recommended burn instruction because it validates the token's decimals for safety. First, derive the ATA for the wallet that holds the tokens to burn: ```ts import { getAssociatedTokenAccountAddress } from "gill/programs"; const ata = await getAssociatedTokenAccountAddress(mint, signer.address); ``` Then create the burn instruction and build the transaction: ```ts import { createTransaction } from "gill"; import { getBurnCheckedInstruction } from "gill/programs"; const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const burnIx = getBurnCheckedInstruction({ account: ata, mint, authority: signer, amount: 1_000_000_000, // 1 token (with decimals=9) decimals: 9, }); const transaction = createTransaction({ feePayer: signer, version: "legacy", instructions: [burnIx], latestBlockhash, }); ``` Where `mint` is the address of the token mint and `signer` is the owner of the token account. ## Sign and send the transaction With your transaction fully created, you can now sign and send it: ```ts import { signTransactionMessageWithSigners, getSignatureFromTransaction, getExplorerLink, } from "gill"; const signedTransaction = await signTransactionMessageWithSigners(transaction); console.log( "Explorer:", getExplorerLink({ cluster: "devnet", transaction: getSignatureFromTransaction(signedTransaction), }), ); ``` If your transaction is already fully signed or has all signers available, you can send and confirm it on the blockchain: ```ts await sendAndConfirmTransaction(signedTransaction); ``` If you do not need to know the transaction signature prior to sending the transaction AND all signers are attached to the transaction, you can pass a fully signable transaction to the `sendAndConfirmTransaction()` function initialized from `createSolanaClient()`. It will then perform the signing operations prior to sending and confirming. ## Using Token Extensions (Token22) If your token was created with the Token Extensions program (Token22), pass the `programAddress` in the config to `getBurnCheckedInstruction()` and the `tokenProgram` to `getAssociatedTokenAccountAddress()`: ```ts import { TOKEN_2022_PROGRAM_ADDRESS, getAssociatedTokenAccountAddress, getBurnCheckedInstruction, } from "gill/programs"; const ata = await getAssociatedTokenAccountAddress( mint, signer.address, TOKEN_2022_PROGRAM_ADDRESS, ); const burnIx = getBurnCheckedInstruction( { account: ata, mint, authority: signer, amount: 1_000_000_000, decimals: 9, }, { programAddress: TOKEN_2022_PROGRAM_ADDRESS }, ); ``` ================================================ FILE: docs/content/docs/guides/tokens/create-token.mdx ================================================ --- title: Create a Token with Metadata description: Learn how to create a new Solana token, with metadata, using the gill JavaScript library. --- Tokens are digital assets that represent ownership over diverse categories of assets. Tokenization enables the digitalization of property rights. Tokens on Solana are referred to as SPL ([Solana Program Library](https://github.com/solana-program)) Tokens. This article will demonstrate [how to create a new SPL token](./create-token) using the [`gill` package](https://www.npmjs.com/package/gill), including attaching metadata to the token for users to see and applications to display. ## Install gill Install gill using the core `gill` library: ```package-install gill ``` import { PackageBadges } from "@/components/package-badges"; ## Create an RPC connection In order to send transactions and/or fetch data from the Solana blockchain, you will need an client connection. You can easily create a Solana client connection using the `createSolanaClient()` function. The `urlOrMoniker` can be either a Solana network moniker (e.g. `devnet`, `mainnet`, `localnet`) or a full URL of your RPC provider. ```ts twoslash import { createSolanaClient } from "gill"; const { rpc, sendAndConfirmTransaction } = createSolanaClient({ urlOrMoniker: "devnet", // `mainnet`, `localnet`, etc }); ``` Using a 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. ## Prepare a Signer Every Solana transaction requires at least one "signer" to be the fee payer for the transaction. The following are common practices: - load a signer from a local keypair file (like `~/.config/solana/id.json`, the one used by the Solana CLI) - loading the signer from an ENV variable (e.g. `process.env.SERVER_SIGNER`) - having a user's wallet be the signer via a front end application ### Load a signer from a local keypair file For backend scripts and some server environments, you can load a signer from your local filesystem: ```ts twoslash import { type KeyPairSigner } from "gill"; import { loadKeypairSignerFromFile } from "gill/node"; // This defaults to the file path used by the Solana CLI: `~/.config/solana/id.json` const signer: KeyPairSigner = await loadKeypairSignerFromFile(); console.log("signer:", signer.address); ``` ## Decide which Token Program to use To use the legacy Token Program: ```ts twoslash import { TOKEN_PROGRAM_ADDRESS } from "gill/programs"; const tokenProgram = TOKEN_PROGRAM_ADDRESS; ``` To use the Token Extensions Program (aka Token22): ```ts twoslash import { TOKEN_2022_PROGRAM_ADDRESS } from "gill/programs"; const tokenProgram = TOKEN_2022_PROGRAM_ADDRESS; ``` ## Generate a Mint and metadata address ```ts twoslash import { generateKeyPairSigner } from "gill"; const mint = await generateKeyPairSigner(); ``` If you are using the legacy Token Program, you will need to derive the "metadata address" from Metaplex's Token Metadata program. ```ts twoslash import { generateKeyPairSigner } from "gill"; import { getTokenMetadataAddress } from "gill/programs"; const mint = await generateKeyPairSigner(); const metadataAddress = await getTokenMetadataAddress(mint); ``` ## Get the latest blockhash ```ts const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); ``` ## Create a transaction that creates a token Instead of manually crafting all of these instructions, you can also use gill's instruction builder function: `getCreateTokenInstructions()` ```ts import { createTransaction } from "gill"; import { getMintSize } from "gill/programs"; const space = getMintSize(); const transaction = createTransaction({ feePayer: signer, version: "legacy", instructions: [ getCreateAccountInstruction({ space, lamports: getMinimumBalanceForRentExemption(space), newAccount: mint, payer: signer, programAddress: tokenProgram, }), getInitializeMintInstruction( { mint: mint.address, mintAuthority: signer.address, freezeAuthority: signer.address, decimals: 9, }, { programAddress: tokenProgram, }, ), getCreateMetadataAccountV3Instruction({ collectionDetails: null, isMutable: true, updateAuthority: signer, mint: mint.address, metadata: metadataAddress, mintAuthority: signer, payer: signer, data: { sellerFeeBasisPoints: 0, collection: null, creators: null, uses: null, name: "super sweet token", symbol: "SST", uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/Climate/metadata.json", }, }), ], latestBlockhash, }); ``` ## Sign and send the transaction With your transaction fully created, you can now sign the transaction to ```ts import { signTransactionMessageWithSigners } from "gill"; const signedTransaction = await signTransactionMessageWithSigners(transaction); console.log( "Explorer:", getExplorerLink({ cluster: "devnet", transaction: getSignatureFromTransaction(signedTransaction), }), ); ``` If your transaction is already fully signed or has all signer's available, you can send and confirm it on the blockchain. ```ts await sendAndConfirmTransaction(signedTransaction); ``` If you do not need to know the transaction signature prior to sending the transaction AND you all signers are attached to the transaction, you can pass a fully signable transaction to the `sendAndConfirmTransaction()` function initialized from `createSolanaClient()`. It will then perform the signing operations prior to sending and confirming. ================================================ FILE: docs/content/docs/guides/tokens/get-token-metadata.mdx ================================================ --- title: Get Token Metadata description: Learn how to read and parse token metadata from the Solana blockchain using the gill JavaScript library. --- Every Solana token can have metadata associated with it, including a name, symbol, URI pointing to off-chain data, and more. The approach to reading this metadata depends on which token program was used to create the token: - **Token Metadata Program (legacy):** Metadata lives in a separate PDA account managed by the Metaplex Token Metadata program. - **Token Extensions (Token22):** Metadata is stored inline on the mint account as an extension. This guide demonstrates how to fetch token metadata using the [`gill` package](https://www.npmjs.com/package/gill), covering both approaches. ## Install gill Install gill using the core `gill` library: ```package-install gill ``` import { PackageBadges } from "@/components/package-badges"; ## Create an RPC connection In order to fetch data from the Solana blockchain, you will need a client connection. You can easily create a Solana client connection using the `createSolanaClient()` function. The `urlOrMoniker` can be either a Solana network moniker (e.g. `devnet`, `mainnet`, `localnet`) or a full URL of your RPC provider. ```ts twoslash import { createSolanaClient } from "gill"; const { rpc } = createSolanaClient({ urlOrMoniker: "mainnet", // `devnet`, `localnet`, etc }); ``` Using a 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. ## Token Metadata Program (legacy tokens) For tokens created with the original Token Program and the Metaplex Token Metadata program, metadata is stored in a separate PDA account derived from the token's mint address. ### Derive the metadata address Use `getTokenMetadataAddress()` from `gill/programs` to derive the metadata PDA from the mint address: ```ts import { address } from "gill"; import { getTokenMetadataAddress } from "gill/programs"; const mint = address("So11111111111111111111111111111111111111112"); const metadataAddress = await getTokenMetadataAddress(mint); ``` ### Fetch the metadata account Use `fetchMetadata()` from `gill/programs` to fetch and decode the metadata account: ```ts import { fetchMetadata } from "gill/programs"; const metadata = await fetchMetadata(rpc, metadataAddress); console.log("Name:", metadata.data.name); console.log("Symbol:", metadata.data.symbol); console.log("URI:", metadata.data.uri); console.log("Seller fee:", metadata.data.sellerFeeBasisPoints); console.log("Creators:", metadata.data.creators); ``` The metadata account also contains additional fields: ```ts console.log("Update authority:", metadata.updateAuthority); console.log("Is mutable:", metadata.isMutable); console.log("Collection:", metadata.collection); console.log("Token standard:", metadata.tokenStandard); ``` If you are unsure whether a metadata account exists for a given mint, use `fetchMaybeMetadata()` instead. It returns `null` if the account does not exist, rather than throwing an error. ```ts import { fetchMaybeMetadata } from "gill/programs"; const maybeMeta = await fetchMaybeMetadata(rpc, metadataAddress); if (maybeMeta.exists) { console.log("Name:", maybeMeta.data.name); } else { console.log("No metadata account found"); } ``` ## Token Extensions (Token22) For tokens created with the Token Extensions program (Token22), metadata can be stored directly on the mint account as an extension. No separate PDA is needed. ### Fetch the mint account Use `fetchMint()` from `gill/programs` to fetch the mint account, which includes any extensions: ```ts import { address } from "gill"; import { fetchMint } from "gill/programs"; const mint = address("your-token22-mint-address"); const mintAccount = await fetchMint(rpc, mint); ``` ### Find the TokenMetadata extension The mint account's `extensions` array contains all enabled extensions. Use the `isExtension()` type guard from `gill/programs` to find the `TokenMetadata` extension: ```ts import { fetchMint, isExtension } from "gill/programs"; const mintAccount = await fetchMint(rpc, mint); const tokenMetadata = mintAccount.data.extensions?.find((ext) => isExtension("TokenMetadata", ext)); if (tokenMetadata && tokenMetadata.__kind === "TokenMetadata") { console.log("Name:", tokenMetadata.name); console.log("Symbol:", tokenMetadata.symbol); console.log("URI:", tokenMetadata.uri); console.log("Update authority:", tokenMetadata.updateAuthority); console.log("Additional metadata:", tokenMetadata.additionalMetadata); } ``` Token Extensions metadata includes an `additionalMetadata` field, which is a `Map` of arbitrary key-value string pairs. This is different from the legacy Token Metadata Program, which uses structured fields like `creators` and `collection`. ## Fetching the off-chain JSON metadata Both the legacy Token Metadata Program and Token Extensions store a `uri` field that points to an off-chain JSON file. This JSON typically contains the token's image, description, attributes, and other rich metadata. Fetching this data is a standard HTTP request, not a Solana RPC call: ```ts const response = await fetch(uri); const offChainMetadata = await response.json(); console.log("Image:", offChainMetadata.image); console.log("Description:", offChainMetadata.description); ``` The off-chain JSON format generally follows the [Metaplex Token Metadata Standard](https://developers.metaplex.com/token-metadata/token-standard), which includes fields like `image`, `description`, `attributes`, `external_url`, and more. ================================================ FILE: docs/content/docs/guides/tokens/index.mdx ================================================ --- title: Tokens on Solana description: "Learn the basics of creating and working with Solana tokens." --- Tokens are digital assets that represent ownership over diverse categories of assets. Tokenization enables the digitalization of property rights. Tokens on Solana are referred to as SPL ([Solana Program Library](https://github.com/solana-program)) Tokens. The following guides demonstrate the most common tasks a Solana developer should be familiar with if building an application that utilizes tokens. ### Create a token with metadata Learn how to create a new Solana token, with metadata, using either the legacy Token Program or Token Extensions program. Read the full guide here on [how to create an SPL token on Solana](/docs/guides/tokens/create-token) ### Mint tokens Learn how to mint new token supply to a wallet using the mint authority, including automatically creating the destination's Associated Token Account. Read the full guide here on [how to mint tokens on Solana](/docs/guides/tokens/mint-tokens) ### Get token metadata Learn how to read and parse token metadata from the Solana blockchain, including both legacy Token Metadata Program accounts and Token Extensions (Token22) inline metadata. Read the full guide here on [how to get token metadata on Solana](/docs/guides/tokens/get-token-metadata) ### Transfer tokens Learn how to transfer tokens between wallets using the token authority, including automatically creating the destination's Associated Token Account. Read the full guide here on [how to transfer tokens on Solana](/docs/guides/tokens/transfer-tokens) ### Burn tokens Learn how to permanently remove tokens from circulation by burning them from a wallet's Associated Token Account. Read the full guide here on [how to burn tokens on Solana](/docs/guides/tokens/burn-tokens) ================================================ FILE: docs/content/docs/guides/tokens/meta.json ================================================ { "root": false, "pages": ["create-token", "mint-tokens", "get-token-metadata", "transfer-tokens", "burn-tokens"] } ================================================ FILE: docs/content/docs/guides/tokens/mint-tokens.mdx ================================================ --- title: Mint Tokens description: Learn how to mint new token supply to a wallet using the gill JavaScript library. --- Minting tokens creates new supply of an existing token and sends it to a wallet via its Associated Token Account (ATA). Only the mint authority signer can authorize minting new tokens. This guide demonstrates how to mint tokens using the [`gill` package](https://www.npmjs.com/package/gill), including automatically creating the destination wallet's ATA if it does not already exist. ## Install gill Install gill using the core `gill` library: ```package-install gill ``` import { PackageBadges } from "@/components/package-badges"; ## Create an RPC connection In order to send transactions and/or fetch data from the Solana blockchain, you will need a client connection. You can easily create a Solana client connection using the `createSolanaClient()` function. The `urlOrMoniker` can be either a Solana network moniker (e.g. `devnet`, `mainnet`, `localnet`) or a full URL of your RPC provider. ```ts twoslash import { createSolanaClient } from "gill"; const { rpc, sendAndConfirmTransaction } = createSolanaClient({ urlOrMoniker: "devnet", // `mainnet`, `localnet`, etc }); ``` Using a 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. ## Prepare a Signer Every Solana transaction requires at least one "signer" to be the fee payer for the transaction. When minting tokens, the `mintAuthority` must also be a signer to authorize the mint. ### Load a signer from a local keypair file For backend scripts and some server environments, you can load a signer from your local filesystem: ```ts twoslash import { type KeyPairSigner } from "gill"; import { loadKeypairSignerFromFile } from "gill/node"; // This defaults to the file path used by the Solana CLI: `~/.config/solana/id.json` const signer: KeyPairSigner = await loadKeypairSignerFromFile(); console.log("signer:", signer.address); ``` ## Understanding token amounts When minting tokens, the `amount` you provide is in raw base units, not human-readable units. The conversion depends on the `decimals` value of your token mint. For example, if your token has `decimals = 9` (the most common for fungible tokens): - `1_000_000_000` (1e9) = **1 token** - `5_000_000_000` (5e9) = **5 tokens** - `1_000_000` (1e6) = **0.001 tokens** With `decimals = 9`, to mint **100 tokens** you would set `amount` to `100_000_000_000` (100e9). With `decimals = 6`, to mint **100 tokens** you would set `amount` to `100_000_000` (100e6). Always check your token's decimals to calculate the correct raw amount. ## Build the mint transaction The simplest way to mint tokens is using the `buildMintTokensTransaction()` helper from `gill/programs`. It automatically derives the destination wallet's ATA and sets compute unit limits. ```ts import { buildMintTokensTransaction } from "gill/programs"; const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const transaction = await buildMintTokensTransaction({ feePayer: signer, version: "legacy", latestBlockhash, mint: mint.address, mintAuthority: signer, destination: destinationWallet, amount: 1_000_000_000, // 1 token (with decimals=9) }); ``` Where `mint.address` is the address of the token mint you previously created and `destinationWallet` is the wallet address that should receive the tokens. ## Manually create the mint instructions If you need more control over the transaction, you can use `getMintTokensInstructions()` to get the individual instructions and compose the transaction yourself. First, derive the ATA for the destination wallet: ```ts import { getAssociatedTokenAccountAddress } from "gill/programs"; const ata = await getAssociatedTokenAccountAddress(mint.address, destinationWallet); ``` Then create the instructions and build the transaction: ```ts import { createTransaction } from "gill"; import { getMintTokensInstructions } from "gill/programs"; const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const instructions = getMintTokensInstructions({ feePayer: signer, mint: mint.address, mintAuthority: signer, destination: destinationWallet, ata, amount: 1_000_000_000, // 1 token (with decimals=9) }); const transaction = createTransaction({ feePayer: signer, version: "legacy", instructions, latestBlockhash, }); ``` This gives you the same two instructions that `buildMintTokensTransaction()` creates under the hood: 1. **Create ATA** (idempotent) — creates the destination's Associated Token Account if it does not already exist 2. **Mint to** — mints the specified amount of tokens to the ATA ## Sign and send the transaction With your transaction fully created, you can now sign and send it: ```ts import { signTransactionMessageWithSigners, getSignatureFromTransaction, getExplorerLink, } from "gill"; const signedTransaction = await signTransactionMessageWithSigners(transaction); console.log( "Explorer:", getExplorerLink({ cluster: "devnet", transaction: getSignatureFromTransaction(signedTransaction), }), ); ``` If your transaction is already fully signed or has all signers available, you can send and confirm it on the blockchain: ```ts await sendAndConfirmTransaction(signedTransaction); ``` If you do not need to know the transaction signature prior to sending the transaction AND all signers are attached to the transaction, you can pass a fully signable transaction to the `sendAndConfirmTransaction()` function initialized from `createSolanaClient()`. It will then perform the signing operations prior to sending and confirming. ## Using Token Extensions (Token22) If your token was created with the Token Extensions program (Token22), pass the `tokenProgram` parameter to ensure the correct program is used: ```ts import { TOKEN_2022_PROGRAM_ADDRESS, buildMintTokensTransaction } from "gill/programs"; const transaction = await buildMintTokensTransaction({ feePayer: signer, version: "legacy", latestBlockhash, mint: mint.address, mintAuthority: signer, destination: destinationWallet, amount: 1_000_000_000, tokenProgram: TOKEN_2022_PROGRAM_ADDRESS, }); ``` The same `tokenProgram` parameter is available on `getMintTokensInstructions()` and `getAssociatedTokenAccountAddress()` as well. ================================================ FILE: docs/content/docs/guides/tokens/transfer-tokens.mdx ================================================ --- title: Transfer Tokens description: Learn how to transfer tokens between wallets using the gill JavaScript library. --- Transferring tokens sends existing token supply from one wallet to another via their Associated Token Accounts (ATAs). The source wallet's `authority` must sign the transaction to authorize the transfer. This guide demonstrates how to transfer tokens using the [`gill` package](https://www.npmjs.com/package/gill), including automatically creating the destination wallet's ATA if it does not already exist. ## Install gill Install gill using the core `gill` library: ```package-install gill ``` import { PackageBadges } from "@/components/package-badges"; ## Create an RPC connection In order to send transactions and/or fetch data from the Solana blockchain, you will need a client connection. You can easily create a Solana client connection using the `createSolanaClient()` function. The `urlOrMoniker` can be either a Solana network moniker (e.g. `devnet`, `mainnet`, `localnet`) or a full URL of your RPC provider. ```ts twoslash import { createSolanaClient } from "gill"; const { rpc, sendAndConfirmTransaction } = createSolanaClient({ urlOrMoniker: "devnet", // `mainnet`, `localnet`, etc }); ``` Using a 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. ## Prepare a Signer Every Solana transaction requires at least one "signer" to be the fee payer for the transaction. When transferring tokens, the `authority` (the source wallet's owner) must also be a signer to authorize the transfer. ### Load a signer from a local keypair file For backend scripts and some server environments, you can load a signer from your local filesystem: ```ts twoslash import { type KeyPairSigner } from "gill"; import { loadKeypairSignerFromFile } from "gill/node"; // This defaults to the file path used by the Solana CLI: `~/.config/solana/id.json` const signer: KeyPairSigner = await loadKeypairSignerFromFile(); console.log("signer:", signer.address); ``` ## Understanding token amounts When transferring tokens, the `amount` you provide is in raw base units, not human-readable units. The conversion depends on the `decimals` value of your token mint. For example, if your token has `decimals = 9` (the most common for fungible tokens): - `1_000_000_000` (1e9) = **1 token** - `5_000_000_000` (5e9) = **5 tokens** - `1_000_000` (1e6) = **0.001 tokens** With `decimals = 9`, to transfer **100 tokens** you would set `amount` to `100_000_000_000` (100e9). With `decimals = 6`, to transfer **100 tokens** you would set `amount` to `100_000_000` (100e6). Always check your token's decimals to calculate the correct raw amount. ## Build the transfer transaction The simplest way to transfer tokens is using the `buildTransferTokensTransaction()` helper from `gill/programs`. It automatically derives the source and destination wallet's ATAs and sets compute unit limits (default 31,000 CU). ```ts import { buildTransferTokensTransaction } from "gill/programs"; const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const transaction = await buildTransferTokensTransaction({ feePayer: signer, version: "legacy", latestBlockhash, mint: mint.address, authority: signer, destination: destinationWallet, amount: 1_000_000_000, // 1 token (with decimals=9) }); ``` Where `mint.address` is the address of the token mint and `destinationWallet` is the wallet address that should receive the tokens. ## Manually create the transfer instructions If you need more control over the transaction, you can use `getTransferTokensInstructions()` to get the individual instructions and compose the transaction yourself. First, derive the ATAs for both the source and destination wallets: ```ts import { getAssociatedTokenAccountAddress } from "gill/programs"; const sourceAta = await getAssociatedTokenAccountAddress(mint.address, signer.address); const destinationAta = await getAssociatedTokenAccountAddress(mint.address, destinationWallet); ``` Then create the instructions and build the transaction: ```ts import { createTransaction } from "gill"; import { getTransferTokensInstructions } from "gill/programs"; const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const instructions = getTransferTokensInstructions({ feePayer: signer, mint: mint.address, authority: signer, sourceAta, destination: destinationWallet, destinationAta, amount: 1_000_000_000, // 1 token (with decimals=9) }); const transaction = createTransaction({ feePayer: signer, version: "legacy", instructions, latestBlockhash, }); ``` ## Sign and send the transaction With your transaction fully created, you can now sign and send it: ```ts import { signTransactionMessageWithSigners, getSignatureFromTransaction, getExplorerLink, } from "gill"; const signedTransaction = await signTransactionMessageWithSigners(transaction); console.log( "Explorer:", getExplorerLink({ cluster: "devnet", transaction: getSignatureFromTransaction(signedTransaction), }), ); ``` If your transaction is already fully signed or has all signers available, you can send and confirm it on the blockchain: ```ts await sendAndConfirmTransaction(signedTransaction); ``` If you do not need to know the transaction signature prior to sending the transaction AND all signers are attached to the transaction, you can pass a fully signable transaction to the `sendAndConfirmTransaction()` function initialized from `createSolanaClient()`. It will then perform the signing operations prior to sending and confirming. ## Using Token Extensions (Token22) If your token was created with the Token Extensions program (Token22), pass the `tokenProgram` parameter to ensure the correct program is used: ```ts import { TOKEN_2022_PROGRAM_ADDRESS, buildTransferTokensTransaction } from "gill/programs"; const transaction = await buildTransferTokensTransaction({ feePayer: signer, version: "legacy", latestBlockhash, mint: mint.address, authority: signer, destination: destinationWallet, amount: 1_000_000_000, tokenProgram: TOKEN_2022_PROGRAM_ADDRESS, }); ``` The same `tokenProgram` parameter is available on `getTransferTokensInstructions()` and `getAssociatedTokenAccountAddress()` as well. ================================================ FILE: docs/content/docs/index.mdx ================================================ --- title: Installation description: Get started with gill, the new JavaScript client for Solana developers. --- Gill is a modern javascript/typescript client library for interacting with the [Solana](http://solana.com/) blockchain. You can use it to build Solana apps in NodeJS, web browsers, React Native, and just about any other JavaScript environment. Gill is built on top of the Solana JavaScript libraries built by Anza: [@solana/kit](https://github.com/anza-xyz/kit) (formerly known as "web3.js v2"). By utilizing the same types and functions under the hood, `gill` is compatible with `@solana/kit`. All `@solana/kit` imports can even be directly replaced with `gill` for one-to-one compatibility, plus unlocking the various quality-of-life improvements that gill provides. ## Install gill Install the core `gill` library in your project: ```package-install 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 `createTransaction`. ## Quick start After [installing gill](#install-gill), follow these simple steps to install and get started with the `gill` library: ### Create a Solana RPC connection Create a Solana `rpc` and `rpcSubscriptions` client connection from 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", }); ``` The above snippet demonstrates how to use the public Solana RPC endpoints. These are great for quick local testing, but they are (rightfully) subject to heavy rate limits. When you are ready to ship your application to production, you will need to utilize a production ready RPC provider. ```typescript import { createSolanaClient } from "gill"; const { rpc, rpcSubscriptions, sendAndConfirmTransaction } = createSolanaClient({ urlOrMoniker: "https://private-solana-rpc-provider.com", }); ``` ### Make Solana RPC requests 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. ### Create a transaction You can easily create transactions using the `createTransaction()` function. It accepts a single object argument that is fully typed for all the required (and optional) pieces of a Solana transaction. When creating a Solana transaction, you will need several pieces of information: - version - `legacy` works well for every task, unless you need Address Lookup Tables (then use `0`) - latest blockhash - This is like a recent timestamp check that the blockchain uses. Simply request it from your `rpc`. - instructions - Instructs the Solana runtime which programs and logic to execute onchain. - fee payer - The signer that will cover the small fee collected by the network to execute the transaction. For simplicity, the following examples utilize `loadKeypairSignerFromFile()` to load a Solana keypair file from the local file system. Specifically the Solana CLI's default file path: `~/.config/solana/id.json`. This can work well for running local scripts, but not for frontend applications where users will need to sign using their wallets. See [Creating a signer without a secret key](/docs/getting-started/signers#create-a-signer-without-the-secret-key) for details. ```ts twoslash import { createTransaction, createSolanaClient } from "gill"; import { loadKeypairSignerFromFile } from "gill/node"; import { getAddMemoInstruction } from "gill/programs"; /** * load the Solana CLI's default keypair file (`~/.config/solana/id.json`) * as a signer into your script */ const signer = await loadKeypairSignerFromFile(); const { rpc } = createSolanaClient({ urlOrMoniker: "devnet" }); const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const transaction = createTransaction({ version: "legacy", // or `0` if using address lookup tables feePayer: signer, instructions: [ getAddMemoInstruction({ memo: "gm world!", }), ], latestBlockhash, // computeUnitLimit, // optional, but highly recommend to set // computeUnitPrice, // optional, but highly recommend to set }); ``` In the example above, we are loading a signer from the local file system to act as our fee payer and only signer. The transaction itself has a single "memo instruction" in it to log a simple message onchain. You can now [sign the transaction](#signing-transactions) and send it to the blockchain for confirmation. You can (and should) optimize your Solana transactions by including a compute unit limit and compute unit price instructions within your transaction. The `createTransaction()` function supports easily adding these instructions via the `computeUnitLimit` and `computeUnitPrice` arguments. ### Signing transactions Once you have a transaction that is ready to be signed, you can call `signTransactionMessageWithSigners()` to perform the signing operations with all the available signers. This function will also assert that the transaction is fully signed. If your transaction cannot be fully signed at this time (because you only have some of the signers available and attached), you can call `partiallySignTransactionMessageWithSigners()` to partially sign the transaction. Then the remaining signatures can be added later. ```ts twoslash // @noErrors import { createTransaction, createSolanaClient, signTransactionMessageWithSigners, } from "gill"; const { rpc, sendAndConfirmTransaction } = createSolanaClient({ urlOrMoniker: "devnet", }); // ... [other business logic here] const transaction = createTransaction({...}); const signedTransaction = await signTransactionMessageWithSigners(transaction); const signature = getSignatureFromTransaction(signedTransaction); ``` After a transaction has been signed by the `feePayer`, you can obtain its transaction signature using the `getSignatureFromTransaction()` function (yes, even before sending it to the network). Once you have a fully signed transaction, `signedTransaction` in the example above, you can now send it to the blockchain network for confirmation. ### Send and confirm transactions When your transaction is ready and signed, you can send it to the network via your RPC provider using the `sendAndConfirmTransaction()` function initialized via gill's `createSolanaClient()`: ```ts twoslash // @noErrors import { createTransaction, createSolanaClient, signTransactionMessageWithSigners, } from "gill"; const { rpc, sendAndConfirmTransaction } = createSolanaClient({ urlOrMoniker: "devnet", }); // ... [other business logic here] const transaction = createTransaction({...}); const signedTransaction = await signTransactionMessageWithSigners(transaction); const signature = getSignatureFromTransaction(signedTransaction); try { console.log("Sending transaction:", signature); await sendAndConfirmTransaction(signedTransaction); console.log("Transaction confirmed!"); } catch (err) { console.error("Unable to send and confirm the transaction"); console.error(err); } ``` The `sendAndConfirmTransaction()` function performs a check to ensure all required signatures are present (aka the transactions is "fully signed") before attempting to send it to the network. If any signatures are missing, it will throw an error. Congratulations! You now understand the basics of using the `gill` library to perform create and send Solana transactions. ## Example source code You can find the well-commented source code file for the above code snippets in gill's open source repo here: https://github.com/gillsdk/gill/blob/master/examples/get-started/src/intro.ts ================================================ FILE: docs/content/docs/meta.json ================================================ { "title": "Documentation", "defaultOpen": true, "root": true, "pages": [ "---Introduction---", "index", "typescript", "examples", "---Getting Started---", "...getting-started", "---React---", "...react", "---Comparisons---", "...compare", "---Guides---", "...guides", "---Miscellaneous---", "..." ] } ================================================ FILE: docs/content/docs/react/examples.mdx ================================================ --- title: Examples description: Real-world examples of using @gillsdk/react in your applications --- Learn how to use `@gillsdk/react` through practical examples that demonstrate common patterns and use cases. ## Basic Examples ### Display Wallet Balance A simple component to display a wallet's SOL balance: ```tsx "use client"; import { lamportsToSol } from "gill"; import { useBalance } from "@gillsdk/react"; export function WalletBalance({ address }: { address: string }) { const { balance, isLoading, isError, error } = useBalance({ address, refetchInterval: 10000, // Refetch every 10 seconds }); if (isLoading) return
Loading balance...
; if (isError) return
Error: {error?.message}
; return (

Wallet Balance

{lamportsToSol(balance)} SOL

); } ``` ### Token Balance Display Display a specific SPL token balance: ```tsx "use client"; import { useTokenAccount, useTokenMint } from "@gillsdk/react"; import { tokenAmountToUiAmount } from "@gillsdk/programs"; export function TokenBalance({ mint, owner }: { mint: string; owner: string }) { const { account: tokenAccount, isLoading: loadingAccount } = useTokenAccount({ mint, owner, }); const { account: mintAccount, isLoading: loadingMint } = useTokenMint({ mint, }); if (loadingAccount || loadingMint) return
Loading...
; const decimals = mintAccount?.data?.decimals || 0; const amount = tokenAccount?.data?.amount || 0n; const displayAmount = tokenAmountToUiAmount(amount, decimals); return (

Token Balance

{displayAmount.toLocaleString()}

); } ``` ## Advanced Examples ### Transaction History Display recent transactions for an address: ```tsx "use client"; import { useSignaturesForAddress } from "@gillsdk/react"; export function TransactionHistory({ address }: { address: string }) { const { signatures, isLoading, refetch } = useSignaturesForAddress({ address, config: { limit: 10, }, }); if (isLoading) return
Loading transactions...
; return (

Recent Transactions

{signatures?.map((sig) => (

{sig.signature}

Slot: {sig.slot} {sig.blockTime && new Date(sig.blockTime * 1000).toLocaleString()}
{sig.err &&

Error: {JSON.stringify(sig.err)}

}
))}
); } ``` ## Integration Examples ### With Next.js App Router Create a providers file: ```tsx filename="app/providers.tsx" "use client"; import { createSolanaClient } from "gill"; import { SolanaProvider } from "@gillsdk/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 60 * 1000, // 1 minute cacheTime: 10 * 60 * 1000, // 10 minutes }, }, }); const solanaClient = createSolanaClient({ urlOrMoniker: process.env.NEXT_PUBLIC_RPC_URL || "mainnet", }); export function Providers({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` Use in layout: ```tsx filename="app/layout.tsx" import { Providers } from "./providers"; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` ### With Wallet Adapter Combine `@gillsdk/react` with Solana wallet adapter: ```tsx "use client"; import { useWallet } from "@solana/wallet-adapter-react"; import { useBalance } from "@gillsdk/react"; import { lamportsToSol } from "gill"; export function ConnectedWalletInfo() { const { publicKey, connected } = useWallet(); const { balance, isLoading } = useBalance({ address: publicKey?.toString() || "", enabled: !!publicKey, // Only fetch when wallet is connected }); if (!connected) { return
Please connect your wallet
; } return (

Connected Wallet

{publicKey?.toString()}

{isLoading ? (

Loading balance...

) : (

{lamportsToSol(balance)} SOL

)}
); } ``` ## Performance Patterns ### Dependent Queries Fetch data conditionally based on other data: ```tsx "use client"; import { useAccount, useTokenAccount } from "@gillsdk/react"; export function ConditionalTokenBalance({ address }: { address: string }) { // First, check if the address exists const { account, isLoading: loadingAccount } = useAccount({ address, }); // Only fetch token balance if account exists const { account: tokenAccount, isLoading: loadingToken } = useTokenAccount({ mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC owner: address, enabled: !!account, // Only fetch if account exists }); if (loadingAccount) return
Checking account...
; if (!account) return
Account not found
; if (loadingToken) return
Loading token balance...
; return (

USDC Balance: {tokenAccount?.data?.amount.toString()}

); } ``` ### Optimistic Updates Update UI immediately while transaction confirms: ```tsx "use client"; import { useBalance } from "@gillsdk/react"; import { useQueryClient } from "@tanstack/react-query"; export function OptimisticBalance({ address }: { address: string }) { const queryClient = useQueryClient(); const { balance } = useBalance({ address }); const handleTransfer = async (amount: bigint) => { // Optimistically update the balance queryClient.setQueryData(["gill", "balance", address], (old: bigint) => old - amount); try { // Send transaction... // await sendTransaction(...) // Refetch to get real balance queryClient.invalidateQueries({ queryKey: ["gill", "balance", address], }); } catch (error) { // Revert optimistic update on error queryClient.invalidateQueries({ queryKey: ["gill", "balance", address], }); } }; return (

Balance: {balance?.toString()}

{/* Transfer UI */}
); } ``` ## Error Handling ### Global Error Boundary ```tsx "use client"; import { ErrorBoundary } from "react-error-boundary"; import { useQueryErrorResetBoundary } from "@tanstack/react-query"; function ErrorFallback({ error, resetErrorBoundary }) { return (

Something went wrong

{error.message}
); } export function AppWithErrorBoundary({ children }) { const { reset } = useQueryErrorResetBoundary(); return ( {children} ); } ``` ## Resources - [gill sdk Documentation](/docs) - Core `gill` library documentation - [TanStack Query Docs](https://tanstack.com/query) - Learn more about React Query ================================================ FILE: docs/content/docs/react/getting-started.mdx ================================================ --- title: Getting Started with @gillsdk/react description: Learn how to set up and configure @gillsdk/react in your React application --- This guide will walk you through setting up `@gillsdk/react` in your React application, including configuration for Next.js App Router, Remix, and other frameworks that support React Server Components. ## Prerequisites Before you begin, make sure you have: 1. A React application (Create React App, Next.js, Vite, etc.) 2. Node.js LTS (v22 or later) installed 3. Basic knowledge of React hooks ## Setup Steps ### Install Dependencies First, install `@gillsdk/react` along with its peer dependencies: ```package-install gill @gillsdk/react @tanstack/react-query ``` ### Create a Solana Client Create a Solana client using gill's `createSolanaClient` function: ```typescript import { createSolanaClient } from "gill"; const client = createSolanaClient({ urlOrMoniker: "devnet", // or "mainnet", "testnet", or a custom RPC URL }); ``` The public Solana RPC endpoints are subject to rate limits and should not be used in production. For production applications, use a dedicated RPC provider like Helius, QuickNode, or Alchemy. ### Wrap Your App with SolanaProvider The `SolanaProvider` is a React context provider that makes the Solana client available to all `@gillsdk/react` hooks in your component tree. #### For Standard React Apps ```tsx import { createSolanaClient } from "gill"; import { SolanaProvider } from "@gillsdk/react"; const client = createSolanaClient({ urlOrMoniker: "devnet", }); function App() { return {/* Your app components */}; } export default App; ``` #### For Next.js App Router and React Server Components For applications that use React Server Components (like Next.js App Router, Remix, or other RSC-enabled frameworks), you need to create a client-side wrapper component. The `"use client"` directive tells the bundler that this component and its children should run on the client side (in the browser) rather than on the server. This is necessary because: - `@gillsdk/react` uses React hooks (`useState`, `useEffect`, etc.) which only work on the client - The provider creates and hooks consume React context, which is a client-side feature - Without this directive, you'll get errors about using hooks in Server Components. Here's how to create a client-side provider wrapper: ```tsx filename="app/providers/solana-provider.tsx" "use client"; // Required - marks this as a Client Component import { createSolanaClient } from "gill"; import { SolanaProvider } from "@gillsdk/react"; const client = createSolanaClient({ urlOrMoniker: "devnet", }); export function SolanaProviderClient({ children }: { children: React.ReactNode }) { return {children}; } ``` Then wrap your app in the root layout with this `SolanaProviderClient` component: ```tsx filename="app/layout.tsx" import { SolanaProviderClient } from "@/providers/solana-provider"; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` ### Use @gillsdk/react Hooks Now you can use any `@gillsdk/react` hook in your components. When using frameworks with React Server Components (like Next.js App Router), remember to add the `"use client"` directive to any component that uses `@gillsdk/react` hooks: ```tsx filename="app/components/balance.tsx" "use client"; // Required when using React Server Components import { lamportsToSol } from "gill"; import { useBalance } from "@gillsdk/react"; export function WalletBalance() { const { balance, isLoading, isError, error } = useBalance({ address: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c", }); if (isLoading) return
Loading balance...
; if (isError) return
Error: {error?.message}
; return (

Balance: {lamportsToSol(balance)} SOL

); } ```
## Configuration Options ### Custom RPC Endpoints For production applications, configure a dedicated RPC endpoint: ```typescript const client = createSolanaClient({ urlOrMoniker: "https://your-rpc-provider.com/your-api-key", }); ``` ### Popular RPC Providers - [Helius](https://helius.dev) - [QuickNode](https://quicknode.com) - [Alchemy](https://alchemy.com) - [Triton](https://triton.one) - [Syndica](https://syndica.io) ### Multiple Clients You can manage multiple Solana clients using the `useUpdateSolanaClient` hook: ```tsx "use client"; import { createSolanaClient } from "gill"; import { useUpdateSolanaClient } from "@gillsdk/react"; function NetworkSwitcher() { const { mutate: updateClient } = useUpdateSolanaClient(); const switchToMainnet = () => { const mainnetClient = createSolanaClient({ urlOrMoniker: "mainnet", }); updateClient(mainnetClient); }; return ; } ``` ## TypeScript Configuration `@gillsdk/react` is fully typed out of the box. No additional TypeScript configuration is required. However, ensure your `tsconfig.json` has: ```json { "compilerOptions": { "strict": true, "esModuleInterop": true, "skipLibCheck": true, "jsx": "react-jsx" // or "preserve" for Next.js } } ``` ## Troubleshooting ### "use client" Errors If you see errors like "You're importing a component that needs useEffect. It only works in a Client Component" or "useState only works in Client Components", this means you're trying to use `@gillsdk/react` in a Server Component. (which NextJS App router does by default unless the `"use client"` directive is explicitly used). **Why this happens:** - React Server Components run on the server during build/request time - `@gillsdk/react` uses browser-specific features like React hooks and Web APIs - The Solana client needs to make RPC calls from the browser **To fix:** 1. Add `"use client"` at the top of any file using `@gillsdk/react` hooks 2. Ensure your `SolanaProvider` wrapper has `"use client"` at the top 3. Make sure the provider is properly wrapped around your component tree **Common scenarios:** - Forgot to add `"use client"` to a component using [`useBalance`](/docs/react/hooks/data-fetching#usebalance), [`useAccount`](/docs/react/hooks/data-fetching#useaccount), etc. - Trying to use `@gillsdk/react` hooks directly in a `page.tsx` without the directive - Importing a Client Component into a Server Component without proper boundaries ### RPC Rate Limiting If you're hitting rate limits: 1. Switch from public endpoints to a dedicated RPC provider 2. Implement request batching and caching strategies 3. Consider using the `staleTime` and `cacheTime` options in hooks ### Bundle Size To minimize bundle size: 1. Import only the hooks you need 2. Ensure tree-shaking is enabled in your bundler 3. Use dynamic imports for heavy components --- Ready to start using `@gillsdk/react`? Check out the [available hooks](/docs/react/hooks) to begin fetching blockchain data in your React application. ================================================ FILE: docs/content/docs/react/hooks/client.mdx ================================================ --- title: Client Management Hooks description: Hooks for managing and accessing the Solana client connection --- These hooks allow you to access and manage the Solana client configured in your `SolanaProvider`. ## useSolanaClient Get the current Solana client configured in the `SolanaProvider`, including the `rpc` and `rpcSubscriptions` connections. ### Usage ```tsx "use client"; import { useSolanaClient } from "@gillsdk/react"; export function MyComponent() { const { rpc, rpcSubscriptions } = useSolanaClient(); // You can now use rpc to access any Solana JSON RPC methods const getSlot = async () => { const slot = await rpc.getSlot().send(); console.log("Current slot:", slot); }; return ; } ``` ### Return Value ```typescript interface SolanaClient { rpc: SolanaRpc; rpcSubscriptions: SolanaRpcSubscriptions; sendAndConfirmTransaction: SendAndConfirmTransaction; } ``` ### Advanced Example Using the client to make custom RPC calls: ```tsx "use client"; import { useSolanaClient } from "@gillsdk/react"; import type { Address } from "gill"; export function AccountExplorer({ accountAddress }: { accountAddress: Address }) { const { rpc } = useSolanaClient(); const [accountInfo, setAccountInfo] = useState(null); const fetchAccountInfo = async () => { try { const info = await rpc .getAccountInfo(accountAddress, { encoding: "base64", }) .send(); setAccountInfo(info); } catch (error) { console.error("Failed to fetch account info:", error); } }; return (
{accountInfo &&
{JSON.stringify(accountInfo, null, 2)}
}
); } ``` ## useUpdateSolanaClient Update the current Solana client in the `SolanaProvider`. This is useful for switching between different networks or RPC endpoints. ### Usage ```tsx "use client"; import { createSolanaClient } from "gill"; import type { SolanaClusterMoniker } from "gill"; import { useUpdateSolanaClient } from "@gillsdk/react"; export function NetworkSwitcher() { const updateClient = useUpdateSolanaClient(); const switchNetwork = (network: SolanaClusterMoniker) => { const newClient = createSolanaClient({ urlOrMoniker: network, }); updateClient(newClient); }; return (
); } ``` ### Parameters ```typescript updateClient(client: SolanaClient): void ``` - `client` - The new Solana client to set ### Advanced Example Switching to a custom RPC endpoint with error handling: ```tsx "use client"; import { createSolanaClient } from "gill"; import type { SolanaClientUrlOrMoniker } from "gill"; import { useUpdateSolanaClient, useSolanaClient } from "@gillsdk/react"; export function CustomRPCManager() { const currentClient = useSolanaClient(); const updateClient = useUpdateSolanaClient(); const [rpcUrl, setRpcUrl] = useState(""); const [isConnecting, setIsConnecting] = useState(false); const connectToCustomRPC = async () => { if (!rpcUrl) return; setIsConnecting(true); try { const newClient = createSolanaClient({ urlOrMoniker: rpcUrl, }); // Test the connection await newClient.rpc.getSlot().send(); // If successful, update the client updateClient(newClient); alert("Successfully connected to custom RPC!"); } catch (error) { alert(`Failed to connect: ${error.message}`); } finally { setIsConnecting(false); } }; return (
setRpcUrl(e.target.value)} placeholder="Enter custom RPC URL" />
); } ``` ## Best Practices ### Client Initialization Initialize your client with appropriate configuration for your use case: ```typescript // Development const devClient = createSolanaClient({ urlOrMoniker: "devnet", }); // Production with custom RPC const prodClient = createSolanaClient({ urlOrMoniker: process.env.NEXT_PUBLIC_RPC_URL || "mainnet", }); // With custom configuration const customClient = createSolanaClient({ urlOrMoniker: "https://your-rpc.com", config: { commitment: "confirmed", wsEndpoint: "wss://your-rpc.com/ws", }, }); ``` ### Network Switching When implementing network switching, consider: 1. **Clear caches** when switching networks to avoid stale data 2. **Update wallet connections** if using wallet adapters 3. **Notify users** of network changes 4. **Persist preferences** in local storage ```tsx function NetworkManager() { const updateClient = useUpdateSolanaClient(); const queryClient = useQueryClient(); const switchNetwork = async (network: SolanaClientUrlOrMoniker) => { // Create new client const newClient = createSolanaClient({ urlOrMoniker: network, }); // Update client updateClient(newClient); // Clear all cached queries await queryClient.invalidateQueries(); // Save preference localStorage.setItem("preferred-network", network); }; // ... rest of component } ``` ## Troubleshooting ### Client Not Available If `useSolanaClient` returns undefined, ensure: 1. Your component is wrapped in `SolanaProvider` 2. The provider is properly initialized with a client 3. You're not calling the hook during SSR (use `"use client"` directive) ### RPC Connection Issues When updating clients, handle connection failures gracefully: ```tsx const updateClientSafely = async (url: SolanaClientUrlOrMoniker) => { try { const newClient = createSolanaClient({ urlOrMoniker: url }); // Test connection await newClient.rpc.getHealth().send(); updateClient(newClient); } catch (error) { console.error("Failed to connect to RPC:", error); // Fallback to previous client or default } }; ``` ================================================ FILE: docs/content/docs/react/hooks/data-fetching.mdx ================================================ --- title: Data Fetching Hooks description: Hooks for fetching data from the Solana blockchain --- These hooks provide easy access to common Solana blockchain data with automatic caching, refetching, and error handling. ## useAccount Get the account info for an address using the Solana RPC method [`getAccountInfo`](https://solana.com/docs/rpc/http/getaccountinfo). ### Usage ```tsx "use client"; import { useAccount } from "@gillsdk/react"; export function AccountInfo() { const { account, isLoading, isError, error } = useAccount({ address: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c", }); if (isLoading) return
Loading...
; if (isError) return
Error: {error?.message}
; return (
{JSON.stringify(account, null, 2)}
); } ``` ### With Decoder Fetch and decode an account's data into a typed object using an appropriate `decoder` function: ```tsx "use client"; import { useAccount } from "@gillsdk/react"; import { getMintDecoder } from "gill/programs"; export function MintInfo() { const { account, isLoading } = useAccount({ // USDC mint account on mainnet address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", decoder: getMintDecoder(), }); if (isLoading) return
Loading...
; return (

Supply: {account?.data?.supply.toString()}

Decimals: {account?.data?.decimals}

); } ``` ### Parameters ```typescript interface UseAccountOptions { address: Address | string; decoder?: Decoder; commitment?: Commitment; minContextSlot?: number; enabled?: boolean; // ... other TanStack Query options } ``` ## useBalance Get an account's balance in lamports using [`getBalance`](https://solana.com/docs/rpc/http/getbalance). ### Usage ```tsx "use client"; import { lamportsToSol } from "gill"; import { useBalance } from "@gillsdk/react"; export function WalletBalance() { const { balance, isLoading, isError, error } = useBalance({ address: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c", }); if (isLoading) return
Loading...
; if (isError) return
Error: {error?.message}
; return (

Balance: {lamportsToSol(balance)} SOL

); } ``` ### With Automatic Refetch ```tsx const { balance } = useBalance({ address: walletAddress, refetchInterval: 5000, // Refetch every 5 seconds refetchOnWindowFocus: true, }); ``` ### Parameters ```typescript interface UseBalanceOptions { address: Address | string; commitment?: Commitment; minContextSlot?: number; enabled?: boolean; // ... other TanStack Query options } ``` ## useLatestBlockhash Get the latest blockhash using [`getLatestBlockhash`](https://solana.com/docs/rpc/http/getlatestblockhash). ### Usage ```tsx "use client"; import { useLatestBlockhash } from "@gillsdk/react"; export function BlockhashDisplay() { const { latestBlockhash, isLoading } = useLatestBlockhash(); if (isLoading) return
Loading...
; return (

Blockhash: {latestBlockhash?.blockhash}

Last Valid Height: {latestBlockhash?.lastValidBlockHeight}

); } ``` ### For Transactions ```tsx const { latestBlockhash } = useLatestBlockhash(); // Use in transaction const transaction = createTransaction({ blockhash: latestBlockhash.blockhash, // ... other transaction params }); ``` ### Parameters ```typescript interface UseLatestBlockhashOptions { commitment?: Commitment; minContextSlot?: number; enabled?: boolean; // ... other TanStack Query options } ``` ## useSignatureStatuses Get the statuses of signatures using [`getSignatureStatuses`](https://solana.com/docs/rpc/http/getSignatureStatuses). ### Usage ```tsx "use client"; import { useSignatureStatuses } from "@gillsdk/react"; export function TransactionStatus({ signature }) { const { statuses, isLoading } = useSignatureStatuses({ signatures: [signature], }); if (isLoading) return
Checking status...
; const status = statuses?.[0]; return (

Confirmed: {status?.confirmationStatus}

Confirmations: {status?.confirmations}

{status?.err &&

Error: {JSON.stringify(status.err)}

}
); } ``` ### Multiple Signatures ```tsx const { statuses } = useSignatureStatuses({ signatures: [sig1, sig2, sig3], searchTransactionHistory: true, }); statuses?.forEach((status, index) => { console.log(`Signature ${index}:`, status); }); ``` ### Parameters ```typescript interface UseSignatureStatusesOptions { signatures: string[]; searchTransactionHistory?: boolean; enabled?: boolean; // ... other TanStack Query options } ``` ## useSignaturesForAddress Get signatures for confirmed transactions using [`getSignaturesForAddress`](https://solana.com/docs/rpc/http/getsignaturesforaddress). ### Usage ```tsx "use client"; import { useSignaturesForAddress } from "@gillsdk/react"; export function TransactionHistory({ address }) { const { signatures, isLoading } = useSignaturesForAddress({ address, config: { limit: 10, }, }); if (isLoading) return
Loading history...
; return (
    {signatures?.map((sig) => (
  • Signature: {sig.signature}

    Slot: {sig.slot}

    Time: {new Date(sig.blockTime * 1000).toLocaleString()}

  • ))}
); } ``` ### With Pagination ```tsx const { signatures, refetch } = useSignaturesForAddress({ address, config: { limit: 20, before: lastSignature, // For pagination until: firstSignature, }, }); ``` ### Parameters ```typescript interface UseSignaturesForAddressOptions { address: Address | string; config?: { limit?: number; before?: string; until?: string; commitment?: Commitment; }; enabled?: boolean; // ... other TanStack Query options } ``` ## useProgramAccounts Get all accounts owned by a program using the [`getProgramAccounts`](https://solana.com/docs/rpc/http/getProgramAccounts) RPC method. - `getProgramAccounts` can return large amounts of data and is resource-intensive - Many RPC providers return errors or have rate limits due to the system load - Public RPC endpoints often have `getProgramAccounts` disabled entirely - Consider using filters to reduce the data returned and always use a paid RPC provider for production - For large programs, consider alternative approaches like indexers or specialized APIs ### Usage ```tsx "use client"; import { useProgramAccounts } from "@gillsdk/react"; export function ProgramAccounts() { const { accounts, isLoading } = useProgramAccounts({ program: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", config: { encoding: "base64", filters: [ { dataSize: 165n }, // Token account size ], }, }); if (isLoading) return
Loading accounts...
; return (

Found {accounts?.length} accounts

); } ``` ### With Memcmp Filter ```tsx const { accounts } = useProgramAccounts({ program: programId, config: { filters: [ { memcmp: { offset: 0n, bytes: base58.encode(ownerPubkey), encoding: "base58", }, }, ], }, }); ``` ### Parameters ```typescript interface UseProgramAccountsOptions { program: Address | string; config?: { encoding?: "base64" | "jsonParsed"; filters?: Filter[]; commitment?: Commitment; minContextSlot?: number; withContext?: boolean; }; decoder?: Decoder; enabled?: boolean; // ... other TanStack Query options } ``` ## useTokenMint Get a decoded [Mint account](https://solana.com/docs/tokens#mint-account) for a token. ### Usage ```tsx "use client"; import { useTokenMint } from "@gillsdk/react"; export function TokenInfo() { const { account, isLoading } = useTokenMint({ // USDC mint on mainnet mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", }); if (isLoading) return
Loading...
; const mintData = account?.data; return (

Supply: {mintData?.supply.toString()}

Decimals: {mintData?.decimals}

Mint Authority: {mintData?.mintAuthority?.toString()}

); } ``` ### Parameters ```typescript interface UseTokenMintOptions { mint: Address | string; commitment?: Commitment; enabled?: boolean; // ... other TanStack Query options } ``` ## useTokenAccount Get the decoded [Token Account](https://solana.com/docs/tokens#token-account) for a given mint and owner, automatically deriving the Associated Token Account (ATA) address: ### Usage ```tsx "use client"; import { useTokenAccount } from "@gillsdk/react"; export function TokenBalance() { const { account, isLoading } = useTokenAccount({ mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", owner: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c", }); if (isLoading) return
Loading...
; const tokenData = account?.data; return (

Balance: {tokenData?.amount.toString()}

Delegated: {tokenData?.delegatedAmount.toString()}

); } ``` ### With ATA Address If you need to fetch and decode a specific Token Account's address (like for ancillary token accounts), you can manually derive the address and provide it to the hook: ```tsx const { account } = useTokenAccount({ ata: "CCMCWh4FudPEmY6Q1AVi5o8mQMXkHYkJUmZfzRGdcJ9P", }); ``` ### Parameters ```typescript interface UseTokenAccountOptions { // Option 1: Provide mint and owner mint?: Address | string; owner?: Address | string; // Option 2: Provide ATA address directly ata?: Address | string; commitment?: Commitment; enabled?: boolean; // ... other TanStack Query options } ``` ## Performance Optimization ### Conditional Fetching ```tsx const { balance } = useBalance({ address: walletAddress, enabled: !!walletAddress, // Only fetch if address exists }); ``` ### Stale Time Configuration ```tsx const { account } = useAccount({ address, options: { staleTime: 30000, // Consider data fresh for 30 seconds cacheTime: 300000, // Keep in cache for 5 minutes }, }); ``` ### Query Invalidation ```tsx import { useQueryClient } from "@tanstack/react-query"; function RefreshButton() { const queryClient = useQueryClient(); const refreshAll = () => { // Invalidate all `@gillsdk/react` queries queryClient.invalidateQueries({ queryKey: ["gill"] }); }; return ; } ``` ================================================ FILE: docs/content/docs/react/hooks/index.mdx ================================================ --- title: React Hooks Reference description: Complete reference for all available hooks in @gillsdk/react --- `@gillsdk/react` provides a comprehensive set of React hooks for interacting with the Solana blockchain. All hooks are built on top of TanStack Query, providing automatic caching, background refetching, and error handling. ## Hook Categories ### Client Management Hooks for managing and accessing the Solana client connection: - [`useSolanaClient`](/docs/react/hooks/client#usesolanaclient) - Get the current Solana client - [`useUpdateSolanaClient`](/docs/react/hooks/client#useupdatesolanaclient) - Update the Solana client ### Data Fetching Hooks for fetching data from the Solana blockchain: - [`useAccount`](/docs/react/hooks/data-fetching#useaccount) - Get account info and data - [`useBalance`](/docs/react/hooks/data-fetching#usebalance) - Get an account's balance - [`useLatestBlockhash`](/docs/react/hooks/data-fetching#uselatestblockhash) - Get the latest blockhash - [`useSignatureStatuses`](/docs/react/hooks/data-fetching#usesignaturestatuses) - Get the statuses of signatures - [`useSignaturesForAddress`](/docs/react/hooks/data-fetching#usesignaturesforaddress) - Get signatures for an address - [`useProgramAccounts`](/docs/react/hooks/data-fetching#useprogramaccounts) - Get all accounts owned by a program - [`useTokenMint`](/docs/react/hooks/data-fetching#usetokenmint) - Get a token mint account - [`useTokenAccount`](/docs/react/hooks/data-fetching#usetokenaccount) - Get a token account ### Transaction Handling Hooks for sending and managing transactions (coming soon): - `useSendTransaction` - Send transactions with automatic confirmation - `useSimulateTransaction` - Simulate transactions before sending ## Common Hook Options All data fetching hooks in `@gillsdk/react` accept standard TanStack Query options: ```typescript interface HookOptions { // Query key for caching queryKey?: QueryKey; // How often to refetch in milliseconds refetchInterval?: number; // Whether to refetch on window focus refetchOnWindowFocus?: boolean; // Whether to refetch on reconnect refetchOnReconnect?: boolean; // Time in milliseconds before data is considered stale staleTime?: number; // Time in milliseconds to keep data in cache cacheTime?: number; // Whether to enable the query enabled?: boolean; // Retry configuration retry?: boolean | number | RetryConfig; } ``` ## Common Return Values All data fetching hooks return a consistent shape based on TanStack Query: ```typescript interface HookResult { // The fetched data - hooks return specific field names like `account`, `balance`, etc. instead of generic `data` account?: T; // or balance, signatures, etc. depending on the hook // Loading state isLoading: boolean; isFetching: boolean; // Error state isError: boolean; error: Error | null; // Success state isSuccess: boolean; // Refetch function refetch: () => void; // Query status status: "loading" | "error" | "success" | "idle"; } ``` ## Error Handling All hooks provide built-in error handling. You can handle errors at the component level: ```tsx function MyComponent() { const { balance, isError, error } = useBalance({ address: "...", }); if (isError) { return
Error: {error?.message}
; } // ... rest of component } ``` Or configure global error handling through TanStack Query: ```tsx import { QueryClient } from "@tanstack/react-query"; const queryClient = new QueryClient({ defaultOptions: { queries: { onError: (error) => { console.error("Query error:", error); // Handle error globally }, }, }, }); ``` ## Performance Tips 1. **Use `staleTime` wisely** - Set appropriate stale times to reduce unnecessary refetches 2. **Enable queries conditionally** - Use the `enabled` option to prevent unnecessary queries 3. **Batch requests** - Multiple hooks can share the same query key to deduplicate requests 4. **Implement pagination** - For large datasets, use pagination parameters ## TypeScript Support All hooks are fully typed with TypeScript. Import types from `gill` for complete type safety: ```typescript import type { Address, Lamports } from "gill"; import { useBalance } from "@gillsdk/react"; function Balance({ address }: { address: Address }) { const { balance } = useBalance({ address }); // balance is typed as `Lamports | undefined` } ``` ================================================ FILE: docs/content/docs/react/hooks/meta.json ================================================ { "title": "Hooks", "defaultOpen": false, "pages": ["index", "client", "data-fetching", "transactions"] } ================================================ FILE: docs/content/docs/react/hooks/transactions.mdx ================================================ --- title: Transaction Hooks description: Hooks for sending and managing Solana transactions --- Transaction hooks are coming soon to `@gillsdk/react`. This page documents the planned API. These hooks will provide an easy way to send, confirm, and manage Solana transactions with automatic error handling and retry logic. ## Planned Hooks ### useSendTransaction Send and confirm transactions with automatic retry and confirmation handling. ```tsx // Coming soon const { sendTransaction, isLoading, error } = useSendTransaction(); const handleTransfer = async () => { const signature = await sendTransaction({ transaction, signers: [keypair], options: { skipPreflight: false, commitment: "confirmed", }, }); }; ``` ### useSimulateTransaction Simulate transactions before sending to check for errors. ```tsx // Coming soon const { simulate, result } = useSimulateTransaction(); const checkTransaction = async () => { const simulation = await simulate({ transaction, signers: [keypair], }); if (simulation.err) { console.error("Transaction would fail:", simulation.err); } }; ``` ### useTransactionStatus Monitor transaction confirmation status in real-time. ```tsx // Coming soon const { status, confirmations } = useTransactionStatus({ signature: "...", targetConfirmations: 10, }); ``` ## Current Approach Until transaction hooks are available, you can use the Solana client directly: ```tsx "use client"; import { useSolanaClient } from "@gillsdk/react"; import { createTransaction } from "gill"; export function SendTransaction() { const { rpc, sendAndConfirmTransaction } = useSolanaClient(); const [isLoading, setIsLoading] = useState(false); const handleSend = async () => { setIsLoading(true); try { // Create your transaction const transaction = createTransaction({ // ... transaction params }); // Send and confirm const signature = await sendAndConfirmTransaction(transaction, { commitment: "confirmed" }); console.log("Transaction confirmed:", signature); } catch (error) { console.error("Transaction failed:", error); } finally { setIsLoading(false); } }; return ( ); } ``` ## With Wallet Adapter If you're using a wallet adapter, combine it with `@gillsdk/react`: ```tsx "use client"; import { useWallet } from "@solana/wallet-adapter-react"; import { useSolanaClient } from "@gillsdk/react"; import { createTransaction } from "gill"; export function WalletTransaction() { const { publicKey, signTransaction } = useWallet(); const { rpc } = useSolanaClient(); const sendTransaction = async () => { if (!publicKey || !signTransaction) return; // Create transaction const transaction = createTransaction({ // ... params }); // Sign with wallet const signedTx = await signTransaction(transaction); // Send via RPC const signature = await rpc.sendTransaction(signedTx, { skipPreflight: false }).send(); console.log("Sent:", signature); }; // ... rest of component } ``` ## Contributing Transaction hooks are actively being developed. If you have suggestions or want to contribute, please check out the [`@gillsdk/react` repository](https://github.com/gillsdk/gill/tree/master/packages/react) on GitHub. ================================================ FILE: docs/content/docs/react/index.mdx ================================================ --- title: React Hooks for Solana description: A React hooks library for easily interacting with the Solana blockchain, built on top of gill --- Welcome to `@gillsdk/react`, a React hooks library for easily interacting with the [Solana](http://solana.com/) blockchain. `@gillsdk/react` is in active development. All APIs are subject to change until reaching the first major version (v1.0.0). This React hooks library is built on top of two core libraries: 1. [`gill`](https://www.npmjs.com/package/gill) - modern JavaScript/TypeScript library for interacting with the Solana blockchain. 2. [`@tanstack/react-query`](https://www.npmjs.com/package/@tanstack/react-query) - popular and powerful asynchronous state management for React. ## Installation Install `@gillsdk/react` with your package manager of choice: ```package-install gill @gillsdk/react @tanstack/react-query ``` `gill` and `@tanstack/react-query` are peer dependencies of `@gillsdk/react` so you need to explicitly install them. This allows you have more control over managing dependencies yourself. ## Key Features - **Type-safe hooks** - Full TypeScript support with proper typing for all Solana data - **Built on TanStack Query** - Leverages the power of React Query for caching, background refetching, and optimistic updates - **Easy to use** - Simple, intuitive API that follows React best practices - **Server component ready** - Works with Next.js and other React Server Component frameworks - **Lightweight** - Minimal bundle size with tree-shaking support ## Available Hooks ### Client Management - [`useSolanaClient`](/docs/react/hooks/client#usesolanaclient) - Get the current Solana client - [`useUpdateSolanaClient`](/docs/react/hooks/client#useupdatesolanaclient) - Update the Solana client ### Data Fetching - [`useAccount`](/docs/react/hooks/data-fetching#useaccount) - Get account info and data - [`useBalance`](/docs/react/hooks/data-fetching#usebalance) - Get an account's balance in lamports - [`useLatestBlockhash`](/docs/react/hooks/data-fetching#uselatestblockhash) - Get the latest blockhash - [`useSignatureStatuses`](/docs/react/hooks/data-fetching#usesignaturestatuses) - Get the statuses of signatures - [`useSignaturesForAddress`](/docs/react/hooks/data-fetching#usesignaturesforaddress) - Get signatures for an address - [`useProgramAccounts`](/docs/react/hooks/data-fetching#useprogramaccounts) - Get all accounts owned by a program (GPA) - [`useTokenMint`](/docs/react/hooks/data-fetching#usetokenmint) - Get a decoded token's Mint account - [`useTokenAccount`](/docs/react/hooks/data-fetching#usetokenaccount) - Get a token account for a mint and owner ## Quick Example ```tsx import { lamportsToSol } from "gill"; import { useBalance } from "@gillsdk/react"; export function WalletBalance({ address }: { address: string }) { const { balance, isLoading, isError, error } = useBalance({ address }); if (isLoading) return
Loading...
; if (isError) return
Error: {error?.message}
; return (

Balance: {lamportsToSol(balance)} SOL

); } ``` ================================================ FILE: docs/content/docs/react/meta.json ================================================ { "title": "React", "defaultOpen": false, "pages": ["index", "getting-started", "hooks", "examples"] } ================================================ FILE: docs/content/docs/typescript.mdx ================================================ --- title: TypeScript Support description: The gill library is TypeScript first and heavily typed to improve your Solana developer experience. --- Gill is a heavily typed library to improve developer experience. After completing the [simple installation guide](/docs#install-gill) to get your Solana application setup, everything _should_ work out of the box for most common application configurations. However, there may be some configuration your codebase may require in order to utilize the gill library and avoid TypeScript issues, either in your editor, runtime, or build time. If your codebase is experiencing TypeScript related issues not covered by this document, please [open an issue here](https://github.com/gillsdk/gill/issues/new?title=%5BTypeScript%5D%20) to describe your scenario. Please also include a simple reproduction to aid in troubleshooting by this library's maintainers. ## Running \*.ts files with NodeJS More recent versions of NodeJS support TypeScript natively, either out of the box or requiring using the `--experimental-strip-types` flag. Allowing you to run `.ts` files using the `node` command. ```shell node --version # output: v24.1.0 node ./file.ts ``` However, due to the differences in how NodeJS handles these types you may experience runtime errors when attempting to run `.ts` files. The following is an example error message you may experience: ``` file:///home/gill/code/file.ts:22 SolanaClusterMoniker, ^^^^^^^^^^^^^^^^^^^^ SyntaxError: The requested module 'gill' does not provide an export named 'SolanaClusterMoniker' ``` ### Solution Update your project's `tsconfig.json` file to utilize the [verbatimModuleSyntax](https://www.typescriptlang.org/tsconfig/#verbatimModuleSyntax) compiler option. This will ensure your editor and build system will correctly show errors when codes does not explicitly annotate imported types/interfaces with the `type` keyword. ```ts { "compilerOptions": { "verbatimModuleSyntax": true, } } ``` You will then need to update all your `import` statements to annotate which imported symbols are types by explicitly using the `type` keyword. This is required for all TypeScript types **AND** interfaces. For example, `SolanaClusterMoniker` is a type export from `gill`: ```ts import { createSolanaClient, SolanaClusterMoniker } from "gill"; ``` It should be updated to be explicitly imported as a `type` (for both TypeScript's types and interfaces): ```ts tab="Option 1 (recommended)" import { createSolanaClient } from "gill"; import type { SolanaClusterMoniker } from "gill"; ``` ```ts tab="Option 2" import { createSolanaClient, type SolanaClusterMoniker } from "gill"; ``` If you declare two different imports from the same library, one with the `type` keyword and one without. Most code editors will correct import new types into the "type import" statement when performing tab completion. Making it so you do not have to manually add the `type` annotation to each individual imported type. ================================================ FILE: docs/generic.d.ts ================================================ type Option = Some | None; type Entries = { [K in keyof T]: [K, T[K]]; }[keyof T][]; type SearchParams = Promise< Record >; type SiteConfig = { name: string; domain: string; description: string; url: string; ogImage: string; links: { twitter?: string; github?: string; docs?: string; changelog?: string; }; twitterHandle?: string; twitterId?: string; }; type NotFoundResponse = { notFound: true }; type SimpleComponentProps = { children?: React.ReactNode; className?: string; }; type ImageSize = { width: number; height: number; }; type LinkDetails = { title: string; href: string; description?: React.ReactNode; icon?: string; subLinks?: SubLinkDetails[]; }; type SubLinkDetails = Omit; ================================================ FILE: docs/next.config.mjs ================================================ import { createMDX } from 'fumadocs-mdx/next'; const withMDX = createMDX(); /** @type {import('next').NextConfig} */ const config = { experimental: { webpackMemoryOptimizations: true, }, reactStrictMode: true, serverExternalPackages: ['twoslash', 'typescript'], }; export default withMDX(config); ================================================ FILE: docs/package.json ================================================ { "name": "@gillsdk/docs", "version": "0.0.0", "private": true, "scripts": { "update:gill": "pnpm add --ignore-workspace gill@latest @gillsdk/react@latest @gillsdk/solana-pay@latest", "prebuild": "./build-api-docs.sh", "build": "next build", "dev": "next dev --turbo", "start": "next start", "postinstall": "fumadocs-mdx", "clean": "rimraf coverage dist build node_modules .turbo .next .docs ./content/api/gill*", "style:check": "prettier --check '{*,**/*}.{ts,tsx,js,jsx,css,json,md,mdx}'", "style:fix": "pnpm style:check --write", "lint": "eslint src", "lint:fix": "eslint --fix src" }, "engines": { "node": ">=20.18.0", "npm": "please-use-pnpm", "pnpm": "^9", "yarn": "please-use-pnpm" }, "dependencies": { "@gillsdk/react": "^0.6.2", "@gillsdk/solana-pay": "^0.6.2", "fathom-client": "^3.7.2", "fumadocs-core": "15.8.5", "fumadocs-docgen": "^2.1.0", "fumadocs-mdx": "13.0.8", "fumadocs-twoslash": "^3.1.14", "fumadocs-ui": "15.8.5", "gill": "0.14.0", "lucide-react": "^0.513.0", "next": "15.3.9", "react": "^19.0.0", "react-dom": "^19.0.0", "twoslash": "^0.3.1" }, "devDependencies": { "@tailwindcss/postcss": "^4.1.4", "@types/mdx": "^2.0.13", "@types/node": "22.13.8", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "eslint": "^8", "eslint-config-next": "15.3.9", "postcss": "^8.5.3", "prettier": "^3.3", "tailwindcss": "^4.1.4", "typescript": "^5.8.3" }, "packageManager": "pnpm@9.1.0" } ================================================ FILE: docs/postcss.config.mjs ================================================ export default { plugins: { '@tailwindcss/postcss': {}, }, }; ================================================ FILE: docs/public/site.webmanifest ================================================ {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} ================================================ FILE: docs/source.config.ts ================================================ import { rehypeCodeDefaultOptions } from "fumadocs-core/mdx-plugins"; import { remarkInstall } from "fumadocs-docgen"; import { defineConfig, defineDocs } from "fumadocs-mdx/config"; import { transformerTwoslash } from "fumadocs-twoslash"; export default defineConfig({ mdxOptions: { rehypeCodeOptions: { langs: [ // FIXME(#403): If a popup itself contains a code fence in any language other than // `ts`, the Shiki highlighter will throw an error that it hasn't loaded that // language. Until we figure this out, preemptively load the `js` language. "js", ], themes: { dark: "github-dark", light: "github-light", }, transformers: [...(rehypeCodeDefaultOptions.transformers ?? []), transformerTwoslash()], }, remarkPlugins: [() => remarkInstall({ persist: { id: "package-install" } })], }, }); export const docs = defineDocs({ dir: "content/docs", }); export const api = defineDocs({ dir: "content/api", }); ================================================ FILE: docs/src/app/(home)/layout.tsx ================================================ import type { ReactNode } from "react"; import { HomeLayout } from "fumadocs-ui/layouts/home"; import { baseOptions } from "@/app/layout.config"; export default function Layout({ children }: { children: ReactNode }) { return {children}; } ================================================ FILE: docs/src/app/(home)/page.tsx ================================================ import { FooterLinks } from "@/components/footer-links"; import { PackageBadges } from "@/components/package-badges"; import { siteConfig } from "@/const"; import icon from "@@/public/icon-black.svg"; import { CodeBlock, Pre } from "fumadocs-ui/components/codeblock"; import { Tab, Tabs } from "fumadocs-ui/components/tabs"; import Image from "next/image"; import Link from "next/link"; export default function Page() { return (

{""} {siteConfig.name}

gill is a modern javascript/typescript client library for interacting with the Solana blockchain

npm install gill
pnpm add gill
yarn add gill
bun install gill
Read the quickstart guide
); } ================================================ FILE: docs/src/app/api/[[...slug]]/page.tsx ================================================ import { getPageTreePeers } from "fumadocs-core/server"; import { Popup, PopupContent, PopupTrigger } from "fumadocs-twoslash/ui"; import { Card, Cards } from "fumadocs-ui/components/card"; import { ImageZoom } from "fumadocs-ui/components/image-zoom"; import { Step, Steps } from "fumadocs-ui/components/steps"; import { Tab, Tabs } from "fumadocs-ui/components/tabs"; import defaultMdxComponents from "fumadocs-ui/mdx"; import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/page"; import type { Metadata } from "next"; import { notFound } from "next/navigation"; import { apiSource } from "@/lib/source"; import { Spread } from "@/lib/Spread"; export async function generateStaticParams() { return apiSource.generateParams(); } export async function generateMetadata(props: { params: Promise<{ slug?: string[] }>; }): Promise { const { slug = [""] } = await props.params; const page = apiSource.getPage(slug); if (!page) notFound(); const image = { url: `/images/og/api_references.png`, width: 1200, height: 630, }; return { title: page.data.title, description: page.data.description, openGraph: { url: `/api/${page.slugs.join("/")}`, images: [image], }, twitter: { images: [image], }, }; } export default async function Page(props: { params: Promise<{ slug?: string[] }> }) { const params = await props.params; const page = apiSource.getPage(params.slug); if (!page) notFound(); const MDX = page.data.body; const hasCategory = page.file.name === "index" && page.slugs.length > 0; return ( {page.data.title} {page.data.description} , Popup, PopupContent, PopupTrigger, Spread, Step, Steps, Tab, Tabs, }} /> {hasCategory && ( {getPageTreePeers(apiSource.pageTree, page.url).map((peer) => ( {peer.description} ))} )} ); } ================================================ FILE: docs/src/app/api/layout.tsx ================================================ import { DocsLayout } from "fumadocs-ui/layouts/docs"; import type { ReactNode } from "react"; import { baseOptions } from "@/app/layout.config"; import { apiSource } from "@/lib/source"; import { Metadata } from "next"; import { siteConfig } from "@/const"; export const metadata: Metadata = { title: { default: `${siteConfig.name} API References`, template: `%s | ${siteConfig.name} API`, }, description: siteConfig.description, }; export default function Layout({ children }: { children: ReactNode }) { return ( {children} ); } ================================================ FILE: docs/src/app/api/search/route.ts ================================================ import { docsSource } from "@/lib/source"; import { createFromSource } from "fumadocs-core/search/server"; export const { GET } = createFromSource(docsSource); ================================================ FILE: docs/src/app/docs/[[...slug]]/page.tsx ================================================ import { docsSource } from "@/lib/source"; import { Spread } from "@/lib/Spread"; import { getPageTreePeers } from "fumadocs-core/server"; import { Popup, PopupContent, PopupTrigger } from "fumadocs-twoslash/ui"; import { Card, Cards } from "fumadocs-ui/components/card"; import { ImageZoom } from "fumadocs-ui/components/image-zoom"; import { Step, Steps } from "fumadocs-ui/components/steps"; import { Tab, Tabs } from "fumadocs-ui/components/tabs"; import defaultMdxComponents from "fumadocs-ui/mdx"; import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/page"; import type { Metadata } from "next"; import { notFound } from "next/navigation"; export async function generateStaticParams() { return docsSource.generateParams(); } export async function generateMetadata(props: { params: Promise<{ slug?: string[] }>; }): Promise { const { slug = [""] } = await props.params; const page = docsSource.getPage(slug); if (!page) notFound(); const image = { url: ["/docs/og", ...slug, "image.png"].join("/") + `?ref=${Date.now()}`, width: 1200, height: 630, }; return { title: page.data.title, description: page.data.description, openGraph: { url: `/docs/${page.slugs.join("/")}`, images: [image], }, twitter: { images: [image], }, }; } export default async function Page(props: { params: Promise<{ slug?: string[] }> }) { const params = await props.params; const page = docsSource.getPage(params.slug); if (!page) notFound(); const MDX = page.data.body; const hasCategory = page.file.name === "index" && page.slugs.length > 0; // Don't show cards for the React section index page const isReactIndex = page.slugs.join("/") === "react"; const showCards = !isReactIndex; return ( {page.data.title} {page.data.description} , Popup, PopupContent, PopupTrigger, Spread, Step, Steps, Tab, Tabs, }} /> {hasCategory && showCards && ( {getPageTreePeers(docsSource.pageTree, page.url).map((peer) => ( {peer.description} ))} )} ); } ================================================ FILE: docs/src/app/docs/layout.tsx ================================================ import { DocsLayout } from "fumadocs-ui/layouts/docs"; import type { ReactNode } from "react"; import { baseOptions } from "@/app/layout.config"; import { docsSource } from "@/lib/source"; export default function Layout({ children }: { children: ReactNode }) { return ( {children} ); } ================================================ FILE: docs/src/app/docs/og/[...slug]/og.tsx ================================================ import { ImageResponse } from "next/og"; import type { ReactElement, ReactNode } from "react"; import type { ImageResponseOptions } from "next/dist/compiled/@vercel/og/types"; interface GenerateProps { title: ReactNode; description?: ReactNode; primaryTextColor?: string; } export function generateOGImage(options: GenerateProps & ImageResponseOptions): ImageResponse { const { title, description, primaryTextColor, ...rest } = options; return new ImageResponse( generate({ title, description, primaryTextColor, }), { width: 1200, height: 630, ...rest, }, ); } export function generate({ primaryTextColor = "rgb(255,150,255)", ...props }: GenerateProps): ReactElement { return (

{props.title}

{props.description}

gill

https://gillsdk.com

); } ================================================ FILE: docs/src/app/docs/og/[...slug]/route.tsx ================================================ import { readFileSync } from "node:fs"; import { generateOGImage } from "./og"; import { docsSource } from "@/lib/source"; import { notFound } from "next/navigation"; const font = readFileSync("./src/app/docs/og/[...slug]/Inter_24pt-Regular.ttf"); const fontBold = readFileSync("./src/app/docs/og/[...slug]/Inter_24pt-SemiBold.ttf"); export async function GET(_req: Request, { params }: { params: Promise<{ slug: string[] }> }) { const { slug } = await params; const page = docsSource.getPage(slug.slice(0, -1)); if (!page) notFound(); return generateOGImage({ primaryTextColor: "rgb(240,240,240)", title: page.data.title, description: page.data.description, fonts: [ { name: "Regular", data: font, weight: 400, }, { name: "Regular", data: fontBold, weight: 600, }, ], }); } export function generateStaticParams(): { slug: string[]; }[] { return docsSource.generateParams().map((page) => ({ ...page, slug: [...page.slug, "image.png"], })); } ================================================ FILE: docs/src/app/global.css ================================================ @import "tailwindcss"; /* Fumadocs imports. */ @import "fumadocs-ui/css/neutral.css"; @import "fumadocs-ui/css/preset.css"; @import "fumadocs-twoslash/twoslash.css"; @source '../../node_modules/fumadocs-ui/dist/**/*.js'; /* App imports. */ @import "../../src/app/styles/brand.css"; @import "../../src/app/styles/typography.css"; @import "../../src/app/styles/fumadocs-overrides.css"; @layer base { button:not(:disabled), [role="button"]:not(:disabled) { cursor: pointer; } .underline { @apply underline-offset-4; } .link { @apply underline underline-offset-4; /* @apply text-primary hover:text-accent underline; */ } } ================================================ FILE: docs/src/app/layout.config.tsx ================================================ import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"; import { BookTextIcon, CodeXmlIcon, ShapesIcon } from "lucide-react"; import Image from "next/image"; import icon from "@@/public/icon-black.svg"; import { siteConfig } from "@/const"; /** * Shared layout configurations * * you can customize layouts individually from: * Home Layout: app/(home)/layout.tsx * Docs Layout: app/docs/layout.tsx */ export const baseOptions: BaseLayoutProps = { githubUrl: siteConfig.links.github, nav: { title: ( <> {" "} {siteConfig.name} ), }, links: [ { text: "Documentation", url: "/docs", active: "nested-url", icon: , }, { text: "Guides", url: "/docs/guides", active: "nested-url", icon: , }, { text: "API Reference", url: "/api", active: "nested-url", icon: , }, ], }; ================================================ FILE: docs/src/app/layout.tsx ================================================ import { siteConfig } from "@/const"; import { RootProvider } from "fumadocs-ui/provider"; import type { Metadata } from "next"; import { Inter } from "next/font/google"; import type { ReactNode } from "react"; import "./global.css"; import FathomAnalytics from "@/components/fathom-analytics"; const inter = Inter({ subsets: ["latin"], }); export const metadata: Metadata = { title: { default: `${siteConfig.name} - Solana JavaScript SDK`, template: `%s | ${siteConfig.name} Solana SDK`, }, metadataBase: new URL(siteConfig.url), description: siteConfig.description, openGraph: { images: { url: "/cover.png", }, }, twitter: { card: "summary_large_image", }, }; export default function Layout({ children }: { children: ReactNode }) { return ( {children} ); } ================================================ FILE: docs/src/app/not-found.tsx ================================================ import { FooterLinks } from "@/components/footer-links"; import { Metadata } from "next"; import Link from "next/link"; export const metadata: Metadata = { title: "Page not found", }; export default function Page() { return (

Page not found

The page you are looking for was not found. Instead, try exploring one of these links below.

Read the quickstart guide
); } ================================================ FILE: docs/src/app/styles/brand.css ================================================ @theme { /* Colors */ --color-primary-50: #fff7f1; /* Containers */ --container-5xs: 12rem; --container-4xs: 14rem; } ================================================ FILE: docs/src/app/styles/fumadocs-overrides.css ================================================ /* Colours. */ /* @theme { --color-fd-background: #fff7f1; --color-fd-border: #6d5d6e; --color-fd-background: hsl(0, 0%, 96%); --color-fd-foreground: hsl(0, 0%, 3.9%); --color-fd-muted: hsl(0, 0%, 96.1%); --color-fd-muted-foreground: hsl(0, 0%, 45.1%); --color-fd-popover: hsl(0, 0%, 98%); --color-fd-popover-foreground: hsl(0, 0%, 15.1%); --color-fd-card: hsl(0, 0%, 94.7%); --color-fd-card-foreground: hsl(0, 0%, 3.9%); --color-fd-border: hsl(0, 0%, 89.8%); --color-fd-primary: hsl(0, 0%, 9%); --color-fd-primary-foreground: hsl(0, 0%, 98%); --color-fd-secondary: hsl(0, 0%, 93.1%); --color-fd-secondary-foreground: hsl(0, 0%, 9%); --color-fd-accent: hsl(0, 0%, 90.1%); --color-fd-accent-foreground: hsl(0, 0%, 9%); --color-fd-ring: hsl(0, 0%, 63.9%); --spacing-fd-container: 1400px; --fd-sidebar-width: 0px; --fd-toc-width: 0px; --fd-layout-width: 100vw; --fd-banner-height: 0px; --fd-nav-height: 0px; --fd-tocnav-height: 0px; --fd-diff-remove-color: rgba(200, 10, 100, 0.12); --fd-diff-remove-symbol-color: rgb(230, 10, 100); --fd-diff-add-color: rgba(14, 180, 100, 0.12); --fd-diff-add-symbol-color: rgb(10, 200, 100); } */ /* @variant dark { --color-fd-background: #2a2834; --color-fd-border: #a190a2; } */ /* Sidebar. */ /* #nd-sidebar { background-color: #fff7f1; @variant dark { background-color: #2a2834; } } #nd-sidebar > div { border-color: transparent; } */ ================================================ FILE: docs/src/app/styles/typography.css ================================================ #nd-page .prose { font-size: 1.05rem; } /* .prose :where(h2):not(:where([class~="not-prose"], [class~="not-prose"] *, :first-child)) { margin-top: 4em; } */ /* .prose :where(h3):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 3em; } */ figure.shiki pre > *, .prose :where(code):not(:where([class~="not-prose"], [class~="not-prose"] *)) { font-size: 0.9rem !important; } ================================================ FILE: docs/src/components/fathom-analytics.tsx ================================================ "use client"; import { useEffect, Suspense } from "react"; import { load, trackPageview } from "fathom-client"; import { usePathname, useSearchParams } from "next/navigation"; function TrackPageView() { const pathname = usePathname(); const searchParams = useSearchParams(); useEffect(() => { if (process?.env?.NODE_ENV !== "production" || !process.env.NEXT_PUBLIC_FATHOM_ID) { return; } load(process.env.NEXT_PUBLIC_FATHOM_ID, { auto: false, // includedDomains: [SITE.domain, `www.${SITE.domain}`], }); }, []); // Record a page view when route changes (including params) useEffect(() => { if (!pathname) return; trackPageview({ url: pathname + searchParams.toString(), referrer: document.referrer, }); }, [pathname, searchParams]); return null; } export default function FathomAnalytics() { return ( ); } ================================================ FILE: docs/src/components/footer-links.tsx ================================================ import { BookTextIcon, CodeXmlIcon, MoveRightIcon, ShapesIcon } from "lucide-react"; import Link from "next/link"; export function FooterLinks() { return (
API Reference Guides Docs
); } ================================================ FILE: docs/src/components/package-badges.tsx ================================================ import { cn } from "fumadocs-ui/utils/cn"; interface ComponentProps extends React.HTMLAttributes { packageName: string; } export function PackageBadges({ packageName, className, ...props }: ComponentProps) { return ( ); } ================================================ FILE: docs/src/const.ts ================================================ export const siteConfig: SiteConfig = { name: "gill", domain: "gillsdk.com", description: "gill is a modern javascript/typescript client library for interacting with the Solana blockchain.", url: "https://gillsdk.com", ogImage: "https://gillsdk.com/og.png", links: { github: "https://github.com/gillsdk/gill", twitter: "https://x.com/gillsdk", // docs: "", }, twitterHandle: "gillsdk", // twitterId: "0", }; ================================================ FILE: docs/src/lib/Spread.tsx ================================================ import type { ReactNode } from "react"; export function Spread({ children }: { children: ReactNode }) { return (
{children}
); } ================================================ FILE: docs/src/lib/source.ts ================================================ import { api, docs } from "@/.source"; import { loader } from "fumadocs-core/source"; export const docsSource = loader({ baseUrl: "/docs", source: docs.toFumadocsSource(), }); export const apiSource = loader({ baseUrl: "/api", source: api.toFumadocsSource(), }); ================================================ FILE: docs/tsconfig.json ================================================ { "compilerOptions": { "baseUrl": ".", "target": "ESNext", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "paths": { "@/.source": ["./.source/index.ts"], "@/*": ["./src/*"], "@@/*": ["./*"] }, "plugins": [ { "name": "next" } ] }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] } ================================================ FILE: docs/typedoc-data.mjs ================================================ // @ts-check /** * Declare data and information to be injected into the * TypeDoc generated API reference's index files */ export const typedocPageIndexData = { gill: { frontmatter: { title: "gill", description: "A modern JavaScript/TypeScript client library for interacting with the Solana blockchain.", }, }, "gill-node": { frontmatter: { title: "gill/node", description: "Functions and utilities designed for interacting with the Solana blockchain using JavaScript server backends and runtimes.", }, }, "gill-programs": { frontmatter: { title: "gill/programs", description: "Utilize the compatible Solana program clients that ship directly within gill. Including common programs from the official Solana Program Library (SPL), Metaplex, and others.", }, }, react: { frontmatter: { title: "@gillsdk/react", description: "A React hooks library for easily interacting with the Solana blockchain. Built on top of gill and TanStack Query.", }, }, "solana-pay": { frontmatter: { title: "@gillsdk/solana-pay", description: "A complete, type-safe implementation of the Solana Pay specification for creating, parsing, and validating transfer and transaction request URLs.", }, }, }; ================================================ FILE: docs/vercel.json ================================================ { "github": { "silent": true }, "git": { "deploymentEnabled": false } } ================================================ FILE: e2e/imports/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.cache /.pnp .pnp.js # testing /coverage # next.js /.next/ /out/ # production /build /dist /bin # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env !.env.example # vercel .vercel # contentlayer .contentlayer # typescript *.tsbuildinfo next-env.d.ts # sitemaps / robots public/sitemap* public/robot* test-ledger temp .cache ================================================ FILE: e2e/imports/.prettierignore ================================================ node_modules pnpm-lock.yaml LICENSE .changeset/ .github/PULL_REQUEST_TEMPLATE.md declarations/ dist/ doc/ lib/ kit/ .docs .turbo .next .vercel **/generated/ **/generated/** generated/ generated/** ================================================ FILE: e2e/imports/.prettierrc ================================================ { "tabWidth": 2, "useTabs": false, "singleQuote": false, "bracketSpacing": true, "semi": true, "trailingComma": "all", "proseWrap": "always", "arrowParens": "always", "printWidth": 80 } ================================================ FILE: e2e/imports/package.json ================================================ { "name": "@gillsdk/tests-e2e", "license": "MIT", "private": true, "type": "module", "version": "0.1.0", "scripts": { "test:unit:node": "TERM_OVERRIDE=\"${TURBO_HASH:+dumb}\" TERM=${TERM_OVERRIDE:-$TERM} pnpm start:tsx && pnpm start:node", "start:tsx": "pnpm tsx ./src/imports.ts", "start:node": "node ./src/imports.js", "start:tsc": "tsc ./src/imports.ts --outFile ./src/imports-tsc.js && node ./src/imports-tsc.js", "style:check": "prettier --check '{*,**/*}.{ts,tsx,js,jsx,css,json,md,mdx}'", "style:fix": "pnpm style:check --write" }, "dependencies": { "gill": "workspace:*" }, "devDependencies": { "tsx": "^4.19.4", "typescript": "^5.8.3" } } ================================================ FILE: e2e/imports/src/imports.js ================================================ /** * Import and log one of each type of symbol from the reexported or generated program clients */ /** * SPL System program client */ import { SYSTEM_PROGRAM_ADDRESS } from "gill/programs"; SYSTEM_PROGRAM_ADDRESS; import { getTransferSolInstruction } from "gill/programs"; getTransferSolInstruction; /** * SPL Address Lookup Table program client */ import { ADDRESS_LOOKUP_TABLE_PROGRAM_ADDRESS } from "gill/programs"; ADDRESS_LOOKUP_TABLE_PROGRAM_ADDRESS; import { getAddressLookupTableDecoder } from "gill/programs"; getAddressLookupTableDecoder; /** * SPL Compute Budget program client */ import { COMPUTE_BUDGET_PROGRAM_ADDRESS } from "gill/programs"; COMPUTE_BUDGET_PROGRAM_ADDRESS; import { getSetComputeUnitLimitInstruction } from "gill/programs"; getSetComputeUnitLimitInstruction; // !this is a custom symbol that gill provides import { isSetComputeLimitInstruction } from "gill/programs"; isSetComputeLimitInstruction; /** * !SPL Memo program is generated and vendored in */ import { MEMO_PROGRAM_ADDRESS } from "gill/programs"; MEMO_PROGRAM_ADDRESS; import { getAddMemoInstruction } from "gill/programs"; getAddMemoInstruction; /** * ! Metaplex's Token Metadata client is generated and vendored in */ import { TOKEN_METADATA_PROGRAM_ADDRESS } from "gill/programs"; TOKEN_METADATA_PROGRAM_ADDRESS; import { getMetadataCodec } from "gill/programs"; getMetadataCodec; /** * SPL Token 2022 program client */ import { TOKEN_2022_PROGRAM_ADDRESS } from "gill/programs"; TOKEN_2022_PROGRAM_ADDRESS; import { getMintToInstruction } from "gill/programs"; getMintToInstruction; // !this is a custom symbol that gill provides import { getAssociatedTokenAccountAddress } from "gill/programs"; getAssociatedTokenAccountAddress; // !this is a custom symbol that gill provides import { TOKEN_PROGRAM_ADDRESS } from "gill/programs"; TOKEN_PROGRAM_ADDRESS; ================================================ FILE: e2e/imports/src/imports.ts ================================================ /** * Import and log one of each type of symbol from the reexported or generated program clients */ /** * SPL System program client */ import { SYSTEM_PROGRAM_ADDRESS } from "gill/programs"; SYSTEM_PROGRAM_ADDRESS; import type { ParsedSystemInstruction } from "gill/programs"; null as unknown as ParsedSystemInstruction; import { getTransferSolInstruction } from "gill/programs"; getTransferSolInstruction; /** * SPL Address Lookup Table program client */ import { ADDRESS_LOOKUP_TABLE_PROGRAM_ADDRESS } from "gill/programs"; ADDRESS_LOOKUP_TABLE_PROGRAM_ADDRESS; import type { ParsedAddressLookupTableInstruction } from "gill/programs"; null as unknown as ParsedAddressLookupTableInstruction; import { getAddressLookupTableDecoder } from "gill/programs"; getAddressLookupTableDecoder; /** * SPL Compute Budget program client */ import { COMPUTE_BUDGET_PROGRAM_ADDRESS } from "gill/programs"; COMPUTE_BUDGET_PROGRAM_ADDRESS; import type { ParsedComputeBudgetInstruction } from "gill/programs"; null as unknown as ParsedComputeBudgetInstruction; import { getSetComputeUnitLimitInstruction } from "gill/programs"; getSetComputeUnitLimitInstruction; // !this is a custom symbol that gill provides import { isSetComputeLimitInstruction } from "gill/programs"; isSetComputeLimitInstruction; /** * !SPL Memo program is generated and vendored in */ import { MEMO_PROGRAM_ADDRESS } from "gill/programs"; MEMO_PROGRAM_ADDRESS; // see Token Metadata's `MAINTAINERS.md` file about this type being renamed from `MintArgs` to `MetadataMintArgs` import type { MetadataMintArgs } from "gill/programs"; null as unknown as MetadataMintArgs; import { getAddMemoInstruction } from "gill/programs"; getAddMemoInstruction; /** * ! Metaplex's Token Metadata client is generated and vendored in */ import { TOKEN_METADATA_PROGRAM_ADDRESS } from "gill/programs"; TOKEN_METADATA_PROGRAM_ADDRESS; import type { ParsedMemoInstruction } from "gill/programs"; null as unknown as ParsedMemoInstruction; import { getMetadataCodec } from "gill/programs"; getMetadataCodec; /** * SPL Token 2022 program client */ import { TOKEN_2022_PROGRAM_ADDRESS as TOKEN_2022_PROGRAM_ADDRESS_token } from "gill/programs"; TOKEN_2022_PROGRAM_ADDRESS_token; import { getMintToInstruction as getMintToInstruction_token } from "gill/programs"; getMintToInstruction_token; import type { ParsedToken2022Instruction as ParsedToken2022Instruction_token } from "gill/programs"; null as unknown as ParsedToken2022Instruction_token; // !this is a custom symbol that gill provides import { getAssociatedTokenAccountAddress as getAssociatedTokenAccountAddress_token } from "gill/programs"; getAssociatedTokenAccountAddress_token; // !this is a custom symbol that gill provides import { TOKEN_PROGRAM_ADDRESS as TOKEN_PROGRAM_ADDRESS_token } from "gill/programs"; TOKEN_PROGRAM_ADDRESS_token; /** * SPL Token 2022 program client */ import { TOKEN_2022_PROGRAM_ADDRESS } from "gill/programs"; TOKEN_2022_PROGRAM_ADDRESS; import { getMintToInstruction } from "gill/programs"; getMintToInstruction; import type { ParsedToken2022Instruction } from "gill/programs"; null as unknown as ParsedToken2022Instruction; // !this is a custom symbol that gill provides import { getAssociatedTokenAccountAddress } from "gill/programs"; getAssociatedTokenAccountAddress; // !this is a custom symbol that gill provides import { TOKEN_PROGRAM_ADDRESS } from "gill/programs"; TOKEN_PROGRAM_ADDRESS; ================================================ FILE: e2e/imports/tsconfig.json ================================================ { "compilerOptions": { "target": "es2022", "module": "NodeNext", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true } } ================================================ FILE: examples/get-started/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.cache /.pnp .pnp.js # testing /coverage # next.js /.next/ /out/ # production /build /dist /bin # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env !.env.example # vercel .vercel # contentlayer .contentlayer # typescript *.tsbuildinfo next-env.d.ts # sitemaps / robots public/sitemap* public/robot* test-ledger temp .cache ================================================ FILE: examples/get-started/.prettierignore ================================================ node_modules pnpm-lock.yaml LICENSE .changeset/ .github/PULL_REQUEST_TEMPLATE.md declarations/ dist/ doc/ lib/ kit/ .docs .turbo .next .vercel **/generated/ **/generated/** generated/ generated/** ================================================ FILE: examples/get-started/.prettierrc ================================================ { "tabWidth": 2, "useTabs": false, "singleQuote": false, "bracketSpacing": true, "semi": true, "trailingComma": "all", "proseWrap": "always", "arrowParens": "always", "printWidth": 80 } ================================================ FILE: examples/get-started/README.md ================================================ # gill examples Gill is aimed at abstracting away many of the complexities and boilerplate required to perform common interactions with the Solana blockchain, while still offering the low level "escape hatches" when developers need (or want) fine-grain control. Take a look through these examples to see how gill works and even [how it compares](#comparison-of-gill-vs-solanakit-aka-web3js-v2) to using the vanilla web3js v2 library. ## Tech stack used - TypeScript and NodeJS - Package manger: `pnpm` - Running the scripts: `esrun` ## Setup locally 1. Clone this repo to your local system 2. Install the packages via `pnpm install` 3. Change into this directory: `cd examples/get-started` ### Running the included scripts with esrun Once setup locally, you will be able to run the scripts included within this repo using `esrun`: ```shell npx esrun ./src/