Skip to Content
Packages@tuwaio/orbit-solana@tuwaio/orbit-solana

@tuwaio/orbit-solana

@tuwaio/orbit-solana is the Solana Layer 2 (L2) package of Orbit Utils, the Stage 1 primitives layer of the TUWA ecosystem. It is built on @solana/kit and the Wallet Standard (@wallet-standard/*), and provides cached RPC clients, wallet discovery, cluster and explorer helpers, and SNS name and avatar lookups. It does not use the legacy @solana/web3.js or gill.


🏛️ Core Capabilities

  • RPC clients: createSolanaClientWithCache returns a cached { rpc, rpcSubscriptions } pair (the WebSocket URL is derived from the HTTP URL); createSolanaRPC returns a cached rpc only. Both accept a full URL or a cluster moniker (mainnet, devnet, testnet, localnet).
  • Wallet Standard discovery: getAvailableSolanaConnectors lists installed wallets that support every feature Orbit relies on (standard:connect, standard:disconnect, standard:events, solana:signAndSendTransaction, solana:signTransaction, solana:signMessage) and only Solana chains. getConnectedSolanaConnector finds the wallet that holds the last connected address stored by @tuwaio/orbit-core. watchAvailableSolanaConnectors calls back when a wallet registers or unregisters, so a shown list can be read again.
  • Transaction sending signer: createSolanaTransactionSendingSigner turns a Wallet Standard account (UiWalletAccount) into an @solana/kit TransactionSendingSigner that asks the wallet to sign and send with solana:signAndSendTransaction. It needs no UI framework and does what useWalletAccountTransactionSendingSigner of @solana/react does in React.
  • Message signer: createSolanaMessageSigner signs a text message with a Wallet Standard wallet, a wallet adapter or useWallet() of @solana/wallet-adapter (v1 and v3) and returns the base58 signature. It signs the UTF-8 bytes, or the version 1 off-chain message that hardware wallets can show (checked against the envelope built with @solana/kit). @tuwaio/siwx-solana and @tuwaio/satellite-solana sign with it. The @tuwaio/orbit-solana/signer entry loads only @solana/kit, so a server can import it without the Wallet Standard packages.
  • Clusters and explorer links: getCluster, getRpcUrlForCluster, getSolanaClusters, getAvailableSolanaClusters, isValidSolanaCluster, isSolanaChainList, and getSolanaExplorerLink for Solana Explorer  URLs.
  • Names and avatars: getSolanaAddressName resolves an address to its favorite SNS (.sol) domain; getSolanaAddressAvatar returns the SNS profile image or a generated identicon. Both cache results in memory (clear…Cache, get…CacheSize, preloadSolanaAvatar).

Transactions are built and sent with @solana/kit itself, so any kit-compatible instruction builder works with the rpc returned here, including program clients generated by Codama .


💾 Installation

pnpm add @tuwaio/orbit-solana @tuwaio/orbit-core @solana/kit @wallet-standard/app @wallet-standard/ui-core @wallet-standard/ui-registry

[!IMPORTANT] @tuwaio/orbit-core (>=0.4), @solana/kit (>=6) and the @wallet-standard packages (1.x) are peer dependencies and must be installed alongside @tuwaio/orbit-solana. The @wallet-standard packages are optional for code that imports only @tuwaio/orbit-solana/signer: pnpm add @tuwaio/orbit-solana @tuwaio/orbit-core @solana/kit is enough there.


🚀 Usage

Cached RPC client

import { address } from '@solana/kit'; import { createSolanaClientWithCache } from '@tuwaio/orbit-solana'; const { rpc } = createSolanaClientWithCache({ rpcUrlOrMoniker: 'devnet', rpcUrls: { devnet: 'https://devnet.my-rpc-provider.example' }, // optional; falls back to the public endpoint }); const { value: lamports } = await rpc.getBalance(address('So11111111111111111111111111111111111111112')).send();

Clients are cached by the rpcUrlOrMoniker string. localnet has no default URL, so pass it in rpcUrls. The public api.*.solana.com endpoints are rate-limited; use your own RPC provider in production.

Wallet discovery

import { getAvailableSolanaConnectors } from '@tuwaio/orbit-solana'; const wallets = getAvailableSolanaConnectors(); // UiWallet[] from @wallet-standard/ui-registry console.log(wallets.map((wallet) => wallet.name));

Signing and sending a transaction

import { appendTransactionMessageInstruction, createTransactionMessage, type Instruction, pipe, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, signAndSendTransactionMessageWithSigners, } from '@solana/kit'; import { createSolanaClientWithCache, createSolanaTransactionSendingSigner } from '@tuwaio/orbit-solana'; import type { UiWalletAccount } from '@wallet-standard/ui-core'; declare const account: UiWalletAccount; // for example `connectedAccount` of a Satellite Connect connection declare const instruction: Instruction; // for example from a Codama-generated program client const signer = createSolanaTransactionSendingSigner(account, 'devnet'); // or 'solana:devnet', or the CAIP-2 chain ID const { rpc } = createSolanaClientWithCache({ rpcUrlOrMoniker: 'devnet' }); const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const message = pipe( createTransactionMessage({ version: 0 }), (m) => setTransactionMessageFeePayerSigner(signer, m), (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m), (m) => appendTransactionMessageInstruction(instruction, m), ); const signature = await signAndSendTransactionMessageWithSigners(message); // opens the wallet prompt

The account must list the chain of the cluster and the solana:signAndSendTransaction feature, otherwise the function throws. Several transactions are sent in one wallet request; an abortSignal in the call config rejects the call, but the wallet prompt itself cannot be cancelled. signAndSendSolanaTx of @tuwaio/pulsar-solana takes the same signer.

Signing a message

import { createSolanaMessageSigner } from '@tuwaio/orbit-solana/signer'; import type { Wallet, WalletAccount } from '@wallet-standard/base'; declare const wallet: Wallet; // or a wallet adapter, or useWallet() of @solana/wallet-adapter as the whole target declare const account: WalletAccount; // one of wallet.accounts const signMessage = createSolanaMessageSigner({ wallet, account }); const signature = await signMessage('Hello from my app'); // opens the wallet prompt; base58 signature

The signer uses the first capability it finds: the solana:signMessage feature, a signMessages method, then a signMessage method of the wallet, its adapter, the account or the target itself. An account that lists solana:signOffchainMessage but not solana:signMessage signs the version 1 off-chain message instead; messageFormat: 'message' or 'offchainMessage' forces one format. The wallet’s own error is passed through when the user rejects.

import { getSolanaAddressAvatar, getSolanaAddressName, getSolanaExplorerLink } from '@tuwaio/orbit-solana'; const owner = 'So11111111111111111111111111111111111111112'; const name = await getSolanaAddressName(owner); // "name.sol", or the address itself if no SNS domain is set const avatarUrl = await getSolanaAddressAvatar(name); getSolanaExplorerLink(`/address/${owner}`, 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1'); // "https://explorer.solana.com/address/…?cluster=devnet"

The cluster helpers (getCluster, getSolanaClusters, getSolanaExplorerLink) accept a cluster in any form: the CAIP-2 chain ID with the genesis hash, as in SOLANA_CHAIN_IDS of @tuwaio/orbit-core, a Wallet Standard chain such as solana:devnet, or the moniker devnet.


🌐 External Services

Some helpers call public third-party HTTP services. The address or domain being looked up is sent to these hosts:

HelperHostPurpose
RPC clients (default URLs)api.mainnet-beta.solana.com, api.devnet.solana.com, api.testnet.solana.comPublic Solana RPC, used when no custom URL is configured
getSolanaAddressNamesns-api.bonfida.comFavorite SNS domain of an address
getSolanaAddressAvatarimage-api.bonfida.comSNS profile image of a .sol domain
getSolanaAddressAvatarapi.dicebear.comFallback identicon URL (loaded by the browser when rendered)

📚 API Reference

Every export, with signatures and types generated from the source, is documented at orbit.docs.tuwa.io/packages/orbit-solana .

📄 License

Licensed under the Apache-2.0 License. See the LICENSE  file for details.

Interfaces

Type Aliases

Functions

Last updated on