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

@sweefi/solana

v0.1.0

Published

Solana-native s402 protocol implementation for SweeFi

Readme

@sweefi/solana

Solana-native s402 payment adapter — exact SPL token and native SOL transfers with browser-safe signing.

@sweefi/solana is the Solana-specific implementation of the SweeFi payment protocol. It implements the PaymentAdapter interface from @sweefi/ui-core, exposes two-sided signer abstractions (ClientSolanaSigner for payers, FacilitatorSolanaSigner for verifiers), and provides the full s402 scheme trio (client, server, facilitator) for exact payments.

Apache 2.0 open source. Part of the SweeFi ecosystem.

Installation

npm install @sweefi/solana

@solana/web3.js and @solana/spl-token are peer dependencies and must be installed alongside this package:

npm install @sweefi/solana @solana/web3.js @solana/spl-token

Peer Dependencies

| Package | Version | |---------|---------| | @solana/web3.js | ^1.95.0 | | @solana/spl-token | ^0.4.0 |

Quick Start

Node.js agent (keypair)

import { Connection, Keypair, clusterApiUrl } from '@solana/web3.js';
import {
  SolanaPaymentAdapter,
  SolanaKeypairSigner,
  SOLANA_DEVNET_CAIP2,
  USDC_DEVNET_MINT,
} from '@sweefi/solana';

// 1. Set up the connection and signer
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
const keypair = Keypair.fromSecretKey(secretKeyBytes);
const wallet = new SolanaKeypairSigner(keypair);

// 2. Create the adapter
const adapter = new SolanaPaymentAdapter({
  wallet,
  connection,
  network: SOLANA_DEVNET_CAIP2,
});

// 3. Simulate before committing
const sim = await adapter.simulate({
  s402Version: '1',
  accepts: ['exact'],
  network: SOLANA_DEVNET_CAIP2,
  asset: USDC_DEVNET_MINT,
  amount: '1000000',          // 1 USDC (6 decimals)
  payTo: '0xRecipient...',
});

if (sim.success) {
  console.log('Estimated fee:', sim.estimatedFee); // { amount: 5000n, currency: 'SOL' }
}

// 4. Sign and broadcast
const { txId } = await adapter.signAndBroadcast({
  s402Version: '1',
  accepts: ['exact'],
  network: SOLANA_DEVNET_CAIP2,
  asset: USDC_DEVNET_MINT,
  amount: '1000000',
  payTo: '0xRecipient...',
});

console.log('Confirmed:', txId); // base58 transaction signature

Browser wallet (Phantom, Backpack, etc.)

import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { SolanaPaymentAdapter, SolanaWalletSigner } from '@sweefi/solana';

function usePaymentAdapter() {
  const { connection } = useConnection();
  const wallet = useWallet();

  // SolanaWalletSigner wraps any @solana/wallet-adapter-base compatible wallet
  const signer = new SolanaWalletSigner(wallet);

  return new SolanaPaymentAdapter({
    wallet: signer,
    connection,
    network: 'solana:mainnet-beta',
  });
}

Native SOL transfer

Pass NATIVE_SOL_MINT (or the string 'native') as the asset field:

import { NATIVE_SOL_MINT, SOLANA_DEVNET_CAIP2 } from '@sweefi/solana';

const { txId } = await adapter.signAndBroadcast({
  s402Version: '1',
  accepts: ['exact'],
  network: SOLANA_DEVNET_CAIP2,
  asset: NATIVE_SOL_MINT,
  amount: '1000000000', // 1 SOL in lamports
  payTo: 'Recipient...',
});

PaymentAdapter Interface

SolanaPaymentAdapter implements the PaymentAdapter interface from @sweefi/ui-core:

| Method | Description | |--------|-------------| | network | CAIP-2 identifier ('solana:mainnet-beta', 'solana:devnet', or 'solana:testnet') | | getAddress() | Returns the payer's base58 public key, or null if the wallet is disconnected | | simulate(reqs) | Dry-run the transaction. Returns { success, estimatedFee? } or { success: false, error } | | signAndBroadcast(reqs) | Sign, broadcast, and confirm the transaction. Returns { txId } |

simulate() builds the exact same instruction set as signAndBroadcast() — including protocol fee splits — using a legacy Transaction so simulation results match on-chain behavior.

Signer Utilities

SolanaKeypairSigner — Node.js agents and CLI tools

import { Keypair } from '@solana/web3.js';
import { SolanaKeypairSigner } from '@sweefi/solana';

const keypair = Keypair.fromSecretKey(secretKeyBytes);
const signer = new SolanaKeypairSigner(keypair);

// signer.address — base58 public key
// signer.signTransaction(tx, connection) — signs, returns { serialized, signature, blockhash, lastValidBlockHeight }

Use this when: running an autonomous agent, a CLI tool, or any server-side process with direct access to a keypair.

SolanaWalletSigner — Browser wallets

import { SolanaWalletSigner } from '@sweefi/solana';
import type { SolanaWalletAdapter } from '@sweefi/solana';

// Any wallet implementing { publicKey, signTransaction } works — no hard dep on wallet-adapter-base
const signer = new SolanaWalletSigner(wallet);

Use this when: building a frontend where the private key lives in a browser extension (Phantom, Backpack, Solflare, etc.). SolanaWalletSigner delegates signing to the wallet's signTransaction method and never touches the private key.

Blockhash threading — why signTransaction returns blockhash

Every Solana transaction embeds a recent blockhash at sign time. That same blockhash's lastValidBlockHeight is the deadline for confirmation.

A naive implementation fetches a fresh blockhash twice: once when building the transaction and again when calling confirmTransaction. If the second fetch returns a different lastValidBlockHeight (which happens whenever a new blockhash is produced between the two calls), confirmTransaction may report expiry even though the transaction succeeds on-chain.

@sweefi/solana avoids this by threading the blockhash through the signing stack:

// ClientSolanaSigner.signTransaction returns all four values:
const { serialized, signature, blockhash, lastValidBlockHeight } =
  await signer.signTransaction(tx, connection);

// ExactSolanaClientScheme.createPaymentWithMeta surfaces them:
const { s402Payload, blockhash, lastValidBlockHeight } =
  await scheme.createPaymentWithMeta(requirements);

// SolanaPaymentAdapter.signAndBroadcast uses the sign-time blockhash — no second fetch:
await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight }, 'confirmed');

This guarantees that the confirmation window matches the transaction exactly.

toFacilitatorSolanaSigner(config?) — server-side verification

import { toFacilitatorSolanaSigner } from '@sweefi/solana';

// Uses public cluster endpoints by default
const facilitator = toFacilitatorSolanaSigner();

// With a custom RPC URL
const facilitator = toFacilitatorSolanaSigner({
  rpcUrl: 'https://my-rpc.example.com',
});

// With per-network overrides
const facilitator = toFacilitatorSolanaSigner({
  rpcUrls: {
    'solana:mainnet-beta': 'https://mainnet-rpc.example.com',
    'solana:devnet': 'https://devnet-rpc.example.com',
  },
});

FacilitatorSolanaSigner methods:

| Method | Description | |--------|-------------| | verifyAndGetPayer(serializedTx, network) | Ed25519-verify the payer's signature via Web Crypto API. Returns the payer's base58 address or throws | | simulateTransaction(serializedTx, network) | Dry-run; returns pre/post SOL and SPL token balances keyed by account index | | executeTransaction(serializedTx, network) | Broadcast raw signed bytes; returns the transaction signature (txid) | | confirmTransaction(signature, network) | Wait for 'confirmed' finality; throws on failure |

verifyAndGetPayer uses crypto.subtle.verify('Ed25519', ...) — zero extra dependencies, works in Node.js 18+ and all modern browsers.

s402 Scheme Implementations

| Scheme | Client | Facilitator | Server | |--------|--------|-------------|--------| | Exact | ExactSolanaClientScheme | ExactSolanaFacilitatorScheme | ExactSolanaServerScheme | | Prepaid | — | — | — | | Stream | — | — | — | | Escrow | — | — | — |

Only the Exact scheme is implemented. Prepaid, Stream, and Escrow require Anchor programs that have not yet been deployed on Solana. Those will be added once the on-chain programs are ready.

  • Exact — One-shot transfer. For SPL tokens, the client signs a createTransferInstruction from their ATA to the recipient's ATA. For native SOL, it uses SystemProgram.transfer. Protocol fee splits are handled atomically by adding a second instruction to the same transaction.

Server scheme — building payment requirements

import { ExactSolanaServerScheme, SOLANA_DEVNET_CAIP2, USDC_DEVNET_MINT } from '@sweefi/solana';

const server = new ExactSolanaServerScheme();

const requirements = server.buildRequirements({
  network: SOLANA_DEVNET_CAIP2,
  payTo: 'YourWallet...',
  amount: '1000000', // 1 USDC
  asset: USDC_DEVNET_MINT, // optional — defaults to USDC for the network
  protocolFeeBps: 50,      // optional — 0.5%
  protocolFeeAddress: 'FeeWallet...',
});
// Returns a fully-formed s402PaymentRequirements object

Client scheme — building a payment payload

import { ExactSolanaClientScheme, SolanaKeypairSigner } from '@sweefi/solana';
import { Connection } from '@solana/web3.js';

const scheme = new ExactSolanaClientScheme(
  new SolanaKeypairSigner(keypair),
  new Connection(clusterApiUrl('devnet'), 'confirmed'),
);

// Standard interface method (returns payload only)
const payload = await scheme.createPayment(requirements);

// Extended method (also returns blockhash metadata for confirmTransaction)
const { s402Payload, blockhash, lastValidBlockHeight } =
  await scheme.createPaymentWithMeta(requirements);

Facilitator scheme — verifying and settling

import { ExactSolanaFacilitatorScheme, toFacilitatorSolanaSigner } from '@sweefi/solana';

const scheme = new ExactSolanaFacilitatorScheme(toFacilitatorSolanaSigner());

// Cryptographically verify + simulate (does not submit on-chain)
const verification = await scheme.verify(payload, requirements);
// { valid: true, payerAddress: '...' } or { valid: false, invalidReason: '...' }

// Verify + submit on-chain
const settlement = await scheme.settle(payload, requirements);
// { success: true, txDigest: '...', finalityMs: 450 }

verify() runs a 4-step check:

  1. Scheme validation (is the payload type 'exact'?)
  2. Network validation (is this actually a Solana network?)
  3. Signature recovery — cryptographic proof the payer signed the transaction (Web Crypto Ed25519)
  4. Simulation — would this transaction succeed if submitted?
  5. Balance verification — did the recipient's account increase by the required amount?

settle() re-runs verification before submitting, mirroring the defense-in-depth pattern from @sweefi/sui.

Constants

import {
  SOLANA_MAINNET_CAIP2,  // 'solana:mainnet-beta'
  SOLANA_DEVNET_CAIP2,   // 'solana:devnet'
  SOLANA_TESTNET_CAIP2,  // 'solana:testnet'
  NATIVE_SOL_MINT,       // 'So11111111111111111111111111111111111111112'
  USDC_MAINNET_MINT,     // 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
  USDC_DEVNET_MINT,      // '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU'
  USDC_DECIMALS,         // 6
  SOL_DECIMALS,          // 9
  LAMPORTS_PER_SOL,      // 1_000_000_000
  BASE_FEE_LAMPORTS,     // 5_000n
} from '@sweefi/solana';

Utilities

import {
  createSolanaConnection,   // Create a Connection from a CAIP-2 network identifier
  networkToCluster,         // 'solana:devnet' → 'devnet'
  getDefaultUsdcMint,       // Returns the correct USDC mint for mainnet-beta or devnet
  uint8ArrayToBase64,       // Browser-safe Uint8Array → base64 (btoa, not Buffer)
  base64ToUint8Array,       // Browser-safe base64 → Uint8Array (atob, not Buffer)
} from '@sweefi/solana';

// createSolanaConnection accepts an optional RPC URL override
const connection = createSolanaConnection('solana:devnet');
const connection = createSolanaConnection('solana:mainnet-beta', 'https://my-rpc.example.com');

Browser note: All binary/base64 conversions in @sweefi/solana use btoa/atob (Web Platform globals). Buffer.from() is intentionally avoided — it is a Node.js global absent in browsers without a polyfill (e.g., Vite does not include one by default).

License

Apache 2.0 — https://github.com/sweeinc/sweefi