npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@anywalletkit/wallet-solana

v0.5.0

Published

Solana wallet for Any Wallet Kit: SLIP-10 m/44'/501'/i'/0' accounts, SOL and SPL transfers with ATA creation, noble ed25519 signing (no WebCrypto needed), built on @solana/kit.

Readme

@anywalletkit/wallet-solana

npm

Solana accounts at m/44'/501'/i'/0' (SLIP-10 ed25519 — the Phantom / Solflare default), pinned by golden vectors in @anywalletkit/conformance.

Install

npm install @anywalletkit/wallet-solana
# or: pnpm add @anywalletkit/wallet-solana  ·  yarn add @anywalletkit/wallet-solana

On npm: @anywalletkit/wallet-solana, published from the release workflow with provenance.

Signing uses noble ed25519 over the compiled message bytes, so there is no WebCrypto dependency: the same code runs in Node and in React Native Hermes without the Ed25519 subtle polyfill.

import WalletManagerSolana from '@anywalletkit/wallet-solana'

const wallet = new WalletManagerSolana(seedPhrase, { provider: ['https://rpc-a', 'https://rpc-b'] })
const account = await wallet.getAccount(0)
await account.transfer({ recipient: 'So1…', amount: 10_000_000n })                 // SOL (lamports)
await account.transfer({ token: USDC_MINT, recipient: 'So1…', amount: 1_000_000n }) // SPL, creates the ATA if missing
await account.sendTransaction(transactionMessage)                                  // any @solana/kit message
wallet.dispose()

What you sign, what it costs, and when the wallet refuses

Every transfer / sendTransaction / signTransaction goes through one plan, so the network is consulted once per fact and a failing step throws before the key is used:

  1. Describe — account.describeTransaction(tx) (or describeTransaction(tx, {account}), pure/offline) decodes System, Token, Token-2022, Associated-Token and Compute-Budget instructions and reports kind (sol-transfer, spl-transfer, token2022-transfer, ata-create, program-call, mixed) plus warnings: unknown-program, sets-authority, closes-account, approves-delegate, unlimited-approve, foreign-fee-payer, missing-signers, unresolved-lookup-tables, burn-address, durable-nonce, and lifetime: blockhash, or durable-nonce when the first instruction is AdvanceNonceAccount (the signature never expires).
  2. Hard checks — fee payer must be this wallet (allowForeignFeePayer to override), a provider transaction must not need other signatures (MissingSignersError), transfers to the System-program address are refused (UnsafeTransactionError, allowBurn to override). Every sendTransaction / signTransaction (message, kit Transaction or base64) also refuses the drainer shapes: System Assign / Allocate / CreateAccountAllowPrefund (and the WithSeed forms) of this wallet's account or a CreateAccount of it, AuthorizeNonceAccount by this wallet, a token SetAuthority (or the metadata / group update-authority handoff), a token Batch, lamports moved to anyone but this wallet by an instruction that is not a visible transfer (CloseAccount, WithdrawExcessLamports, UnwrapLamports, WithdrawNonceAccount, TransferSolWithSeed), a Stake-program instruction this wallet signs that hands a stake account over (Authorize* to another key, a Withdraw to anyone else, a SetLockup* with a foreign custodian or a lockup), a stake account the wallet funds that the same transaction does not initialize (or split into) with this wallet as staker and withdrawer, an undecodable System / Token / Stake instruction (UnsafeTransactionError with code core-instruction, sets-authority, closes-account or moves-lamports; allowAuthorityChanges: true to override, e.g. for a token launch that renounces its mint authority). A durable-nonce transaction is refused (code durable-nonce) unless allowDurableNonce is set in the config or the call's options. transfer / quoteTransfer refuse a recipient that is not a wallet: a token account, a mint or a program (recipient-not-wallet); an address with no account yet is a wallet. The same checks run in signCompiled and signTransactionMessage. What a program the wallet signs for does by CPI is proved by the simulation (step 4). Swap-aggregator transactions belong on sendSerialized, which adds a program allowlist and a simulated balance / ownership proof and ignores that flag.
  3. Compute budget — by default the price is the 75th percentile of getRecentPrioritizationFees over the writable accounts (clamped by minPriorityMicroLamports / maxPriorityMicroLamports) and the unit limit is the simulated usage × 1.1. Set priorityFee: {microLamports, computeUnitLimit} for explicit values or priorityFee: false to add nothing; a budget the caller already put in the message is kept.
  4. Simulation — simulateTransaction (sigVerify: false) runs before signing; a failure throws SimulationError with the program logs. It also returns the post-state of the wallet, of its token accounts the transaction writes and of every written account the transaction may create (one that does not exist yet, or a bare System account; one extra read for the pre-state), and refuses what CPI would do there: the wallet's account handed to a program or given data (wallet-owner), a token account's owner or close authority changed, or set on a token account of this wallet that the transaction creates, this wallet's associated token account created for another owner (sets-authority), a delegate allowance no top-level Approve shows (approves-delegate); allowAuthorityChanges skips this too. simulate: false disables the gate (and the CPI proof with it).
  5. Fee quote — quoteSendTransaction / quoteTransfer return {fee, baseFee, priorityFee, microLamports, computeUnitLimit, tokenFee?}; transferMaxFee / transactionMaxFee compare against fee.
const q = await account.quoteTransfer({ token: MINT, recipient, amount: 10_000n })
q.priorityFee  // lamports added by the compute budget
q.tokenFee     // Token-2022 transfer fee withheld by the mint (0 for plain SPL)

Token-2022

transfer reads the mint once: owner program, decimals and (for Token-2022) the TransferFee extension in force this epoch. Transfers use transferChecked (mint and decimals verified on chain); fee-bearing mints use transferCheckedWithFee so a changed fee makes the transaction fail instead of silently taking more. The recipient's ATA is derived for the right program.

Token info

getTokenInfo(mint) reads a mint from the chain, for a token the user adds by address: { standard: 'spl' | 'spl-token-2022', decimals, symbol, name, totalSupply, flags }. Name and symbol come from the Token-2022 metadata extension, else the Metaplex metadata account ("" when there is neither); anyone can name a token "USDC", so only the address identifies it. flags lists what a holder should know: mint-authority, freeze-authority, and the Token-2022 extensions permanent-delegate (someone can move or burn any balance), transfer-fee (non-zero), transfer-hook, non-transferable, default-frozen, pausable. An address that is not an initialized Token / Token-2022 mint throws NotATokenError.

const pyusd = await account.getTokenInfo('2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo')
// { standard: 'spl-token-2022', decimals: 6, symbol: 'PYUSD', name: 'PayPal USD',
//   flags: ['mint-authority', 'freeze-authority', 'permanent-delegate'], … }

Versioned transactions and provider wires

  • transfer({..., lookupTables: [table]}) / sendTransaction({to, value, lookupTables}) fetch the tables and compress the v0 message.
  • A swap or DeFi provider's base64 wire transaction is accepted directly: signTransaction(base64) / sendTransaction(base64) decode it, run the hard checks, simulate, add this wallet's signature (others are preserved) and return / broadcast the wire. The message bytes are never altered.
  • sendSerialized(base64, guard) / checkSerialized(base64, guard) prove a provider-built wire before signing: the program allowlist, the hard checks, and a simulated balance proof against guard.expect (maxLamportsOut, tokenIn: {mint, minAmount, maxAmount?}, maxTokenOut). Every bound must be a non-negative bigint (invalid-expectation otherwise, before any RPC call); tokenIn.maxAmount refuses a wire that pays the wallet more than asked (token-in-excess). A module that knows the exact instructions its operation takes passes guard.instructions: every top-level instruction must match one rule — program, data prefix (data, hex), optional exact dataLength, and the accounts at given positions, compared after lookup tables are resolved — or the wire is refused (unexpected-instruction). Net balances alone let a deposit that also withdraws collateral and transfers it away pass; the rules do not.
  • getAccountInfos(addresses) reads raw accounts (owner, lamports, data) in the order asked, null where none exists, for modules that check a provider's figures against chain state.

Broadcast outcome and timeouts

A url provider gives up after timeoutMs (default 15 000) per request (AbortSignal.timeout, or a timer where the runtime lacks it); a read that times out is a NetworkError. A provider list fails over with the same signed bytes, never a new signature. A send the node refused (preflight failure, bad signature, an HTTP 4xx such as a rate limit) throws that error: nothing was forwarded, plan and sign again. Anything else — a timeout, a dropped connection, a 408 / 5xx, or a refusal after another provider went unanswered — throws BroadcastUnknownError (maybeSent: true, hash = the signature): the transaction may land, so do not sign it again; watch getTransactionReceipt(hash) until its blockhash expires.

Activity history

createSolanaHistory is the chain's HistorySource (@anywalletkit/core): newest-first ChainTx pages for an address over plain JSON-RPC, no indexer.

import { createSolanaHistory } from '@anywalletkit/wallet-solana/history'

const history = createSolanaHistory({ rpcUrls: ['https://api.mainnet-beta.solana.com'] })
const page = await history.listActivity('2tKRnQTAXmryVYxDNSajdZg1DfX2FbHKNboWr5EBiKhW', { limit: 25 })
const older = await history.listActivity(address, { cursor: page.cursor })
const one = await history.getTransaction(page.items[0].hash, address)
history.explorerTxUrl(one.hash) // https://solscan.io/tx/…

| option | default | notes | |---|---|---| | rpcUrl / rpcUrls | — | One or several JSON-RPC endpoints, tried in order per request (HTTP 429 / 5xx / network → next). | | network | mainnet | devnet / testnet switch the CAIP-2 id and add ?cluster= to Solscan links. | | rateLimit | 5 rps | Requests per second (a JSON-RPC batch counts as one). | | batchSize | 10 | getTransaction calls per JSON-RPC batch (1–10). | | maxTokenAccounts | 50 | Token accounts merged into the address's history. | | fetch, timeoutMs | global, 15 000 | AbortSignal.timeout when the runtime has it. |

Endpoints. getTokenAccountsByOwner (Token + Token-2022, jsonParsed, cached per address) → getSignaturesForAddress for the wallet and each token account → getTransaction (jsonParsed, maxSupportedTransactionVersion: 0, confirmed) in batches of ≤ 10. A transaction the node pruned still yields a row (status from the signature list, no transfers, raw.tx === null).

Pages and cursor. Signatures of every list are merged by slot (newest first), deduplicated, and the page takes the top limit plus ties at the boundary slot, so each list's consumed rows form a prefix of what the node returned. The cursor is base64 JSON { v: 1, l: { <list address>: <before signature> | '' | null } }: one before signature per list, '' = not started, null = exhausted. The list set is frozen in the cursor; a token account created mid-pagination joins on the next fresh listing. Pagination is exact even when one transaction touches several lists.

Mapping. Native transfers come from preBalances/postBalances with the fee added back to the payer, token transfers from pre/postTokenBalances per mint attributed to the token accounts' owners (standard = spl / token2022 from programId, decimals from the balance), senders paired with receivers largest-first; a receiver without a sender is a mint (from = mint), a sender without a receiver a burn (to = mint). fee = meta.fee, status from meta.err, block = slot. call.programs lists every top-level instruction program id, then inner ones; call.to is the first program that is not plumbing (System, ComputeBudget, Memo, Token, Token-2022, Associated Token) and call.data that instruction's base58 data — what @anywalletkit/protocol-staking-spl-stake-pool-solana reads. A transaction made only of plumbing (a SOL or SPL transfer) has no call; to is then the recipient owner. nativeValue is the SOL the payer sent in a plain transfer, 0 for program calls. Symbols are not resolved here (no catalog dependency); the kit fills them.

Fixtures in test/fixtures/history/ were captured on 2026-10-14 from api.mainnet-beta.solana.com for the test wallet: signature pages for the wallet, its JitoSOL and USDC accounts, both token-account lists, the Jito DepositSol (3SmwE6qf…, the same transaction as the stake-pool module's activity fixture), a SOL send and receive, a USDC send and a Token-2022 → USDC aggregator swap. test/history.test.ts checks that the row this source builds for the Jito deposit is described as stake by the stake-pool module.