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

@upstake/sdk

v0.13.1

Published

TypeScript SDK for upstake.fun Solana program - marketplace for selling future staking rewards

Readme

@upstake/sdk

TypeScript SDK for interacting with the upstake.fun Solana program - a marketplace for selling future staking rewards.

Version

Current: v0.4.0 Program ID: H4c5Qr5rYQU7JJfPgiQFNKfMCod8Fzb3nHb8nBHQRmaw Network: Solana Testnet

Installation

npm install @upstake/sdk @coral-xyz/anchor @solana/web3.js

Quick Start

With Phantom/Wallet Adapter (Recommended for dApps)

Perfect for React apps using Phantom, Solflare, Backpack, etc.

import { UpstakeClient } from '@upstake/sdk';
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { useMemo } from 'react';
import { BN, LAMPORTS_PER_SOL, Keypair } from '@solana/web3.js';

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

  // Create client - it handles all Program setup internally!
  const client = useMemo(() => {
    if (!wallet.publicKey) return null;
    return UpstakeClient.fromWallet(connection, wallet);
  }, [connection, wallet]);

  const handleAcceptOffer = async () => {
    if (!client) return;

    // 1. Browse available offers
    const offers = await client.getActiveOffers();

    // 2. Generate new stake account
    const stakeAccount = Keypair.generate();

    // 3. Accept offer and receive instant payout
    const signature = await client.acceptOffer(
      offers[0].account.validator,
      offers[0].account.validatorVoteAccount,
      stakeAccount,
      new BN(1 * LAMPORTS_PER_SOL),      // 1 SOL principal
      new BN(60 * 60 * 24 * 30 * 6)      // 6 months lock
    );

    console.log('Success!', signature);
  };

  return <button onClick={handleAcceptOffer}>Stake & Get Instant Payout</button>;
}

See FRONTEND_INTEGRATION.md for complete wallet-adapter setup.

With Keypair (Node.js Scripts/Bots)

For backend services, automation, or testing.

import { UpstakeClient, IDL, PROGRAM_ID } from '@upstake/sdk';
import { AnchorProvider, Program, BN } from "@coral-xyz/anchor";
import { Connection, Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js";

// Setup
const connection = new Connection("https://api.testnet.solana.com");
const wallet = Keypair.generate(); // or Keypair.fromSecretKey(...)

// Create provider & program
const provider = new AnchorProvider(connection, { publicKey: wallet.publicKey } as any, {});
const program = new Program(IDL as any, PROGRAM_ID, provider);

// Create client
const client = new UpstakeClient(connection, program, wallet);

// Use client
const offers = await client.getActiveOffers();
const stakeAccount = Keypair.generate();

const signature = await client.acceptOffer(
  offers[0].account.validator,
  offers[0].account.validatorVoteAccount,
  stakeAccount,
  new BN(1 * LAMPORTS_PER_SOL),
  new BN(60 * 60 * 24 * 30 * 3)  // 3 months
);

Features

  • Type-safe - Full TypeScript support with auto-generated types from IDL
  • Simple API - Clean, intuitive function signatures
  • PDA helpers - Automatic PDA derivation for all accounts
  • Account fetchers - Easy querying of all account types
  • Comprehensive - All 14 instructions + account fetching utilities
  • Standing Offer Model - Instant liquidity from validator pools

API Reference

Platform Admin Instructions

initializePlatform(program, admin, treasury)

Initialize the platform configuration (one-time setup by admin).

import { initializePlatform } from "@upstake/sdk";

const signature = await initializePlatform(
  program,
  adminKeypair
);

pausePlatform(program, admin)

Emergency pause all new acceptances (admin only).

import { pausePlatform } from "@upstake/sdk";

await pausePlatform(program, adminKeypair);

unpausePlatform(program, admin)

Resume platform operations (admin only).

import { unpausePlatform } from "@upstake/sdk";

await unpausePlatform(program, adminKeypair);

updatePlatformConfig(program, admin, options)

Update platform fees and limits with trust-minimized caps (admin only).

import { updatePlatformConfig } from "@upstake/sdk";

await updatePlatformConfig(
  program,
  adminKeypair,
  {
    payoutFeeBps: 30,                       // 0.3% upfront payout fee (max 5%)
    rewardFeeBps: 80,                       // 0.8% (max 10%)
    keeperRewardBps: 20,                    // 0.2% (max 1%)
    minPrincipal: new BN(0.05 * LAMPORTS_PER_SOL),  // 0.05 SOL minimum
    minLockDuration: new BN(7 * 24 * 60 * 60),      // 1 week minimum
    // maxPrincipal: keep current
    // maxLockDuration: keep current
  }
);

Validator Pool Instructions

createStandingOffer(program, validator, validatorVoteAccount, params)

Create a standing offer with liquidity pool.

import { createStandingOffer } from "@upstake/sdk";

const signature = await createStandingOffer(
  program,
  validatorKeypair,
  validatorVoteAccount,
  500,                              // 5% APR
  new BN(0.1 * LAMPORTS_PER_SOL),  // min 0.1 SOL
  new BN(10 * LAMPORTS_PER_SOL),   // max 10 SOL
  new BN(60 * 60 * 24 * 30 * 6),   // min 6 months
  new BN(60 * 60 * 24 * 365),      // max 1 year
  new BN(5 * LAMPORTS_PER_SOL)     // initial deposit (escrow PDA created automatically)
);

depositToPool(program, validator, amount)

Add liquidity to validator's pool.

import { depositToPool } from "@upstake/sdk";

await depositToPool(
  program,
  validatorKeypair,
  new BN(2 * LAMPORTS_PER_SOL)
);

withdrawFromPool(program, validator, amount)

Withdraw available (non-staked) funds from pool.

import { withdrawFromPool } from "@upstake/sdk";

await withdrawFromPool(
  program,
  validatorKeypair,
  new BN(1 * LAMPORTS_PER_SOL)
);

updateStandingOffer(program, validator, params)

Update standing offer parameters.

import { updateStandingOffer } from "@upstake/sdk";

await updateStandingOffer(
  program,
  validatorKeypair,
  600,  // new APR: 6%
  null, // keep min principal
  new BN(15 * LAMPORTS_PER_SOL), // new max principal
  null, // keep min lock duration
  null  // keep max lock duration
);

pauseStandingOffer(program, validator)

Pause standing offer (prevent new acceptances).

import { pauseStandingOffer } from "@upstake/sdk";

await pauseStandingOffer(program, validatorKeypair);

unpauseStandingOffer(program, validator)

Resume standing offer (allow new acceptances).

import { unpauseStandingOffer } from "@upstake/sdk";

await unpauseStandingOffer(program, validatorKeypair);

closeStandingOffer(program, validator)

Close a standing offer after all stakes are settled and liquidity is withdrawn.

import { closeStandingOffer } from "@upstake/sdk";

await closeStandingOffer(program, validatorKeypair);

User & Settlement Instructions

acceptStandingOffer(program, user, validator, voteAccount, stakeAccount, principal, lockDuration)

User accepts standing offer and receives instant upfront payment.

import { acceptStandingOffer } from "@upstake/sdk";

const stakeAccount = Keypair.generate();
await acceptStandingOffer(
  program,
  userKeypair,
  validatorPublicKey,
  validatorVoteAccount,
  stakeAccount,
  new BN(0.5 * LAMPORTS_PER_SOL),   // 0.5 SOL principal
  new BN(60 * 60 * 24 * 30 * 11)    // 11 months
);

deactivateStake(program, userStake, stakeAccount)

Deactivate stake after lock period (permissionless).

import { deactivateStake } from "@upstake/sdk";
import { getUserStakePDA } from "@upstake/sdk";

const [userStakePDA] = getUserStakePDA(userPublicKey, stakeAccountPublicKey);

await deactivateStake(
  program,
  userStakePDA,
  stakeAccountPublicKey
);

settle(program, userStake, user, validator, settler, stakeAccount)

Settle stake and distribute rewards (permissionless - settler receives 0.25% reward).

import { settle } from "@upstake/sdk";

await settle(
  program,
  userStakePDA,
  userPublicKey,
  validatorPublicKey,
  settlerKeypair,
  stakeAccountPublicKey
);

Account Fetchers

fetchPlatformConfig(program)

Fetch the platform configuration.

import { fetchPlatformConfig } from "@upstake/sdk";

const config = await fetchPlatformConfig(program);
console.log('Payout fee:', config.payoutFeeBps, 'bps');
console.log('Reward fee:', config.rewardFeeBps, 'bps');
console.log('Keeper reward:', config.keeperRewardBps, 'bps');
console.log('Paused:', config.paused);
console.log('Min principal:', config.minPrincipal.toString(), 'lamports');
console.log('Min lock duration:', config.minLockDuration.toString(), 'seconds');

fetchStandingOffer(program, validator)

Fetch a specific standing offer.

import { fetchStandingOffer } from "@upstake/sdk";

const offer = await fetchStandingOffer(program, validatorPublicKey);
if (offer) {
  console.log('APR:', offer.aprBps, 'bps');
  console.log('Available liquidity:', offer.available.toString());
  console.log('Active:', offer.isActive);
}

fetchAllStandingOffers(program)

Fetch all standing offers (marketplace view).

import { fetchAllStandingOffers } from "@upstake/sdk";

const offers = await fetchAllStandingOffers(program);
console.log('Found', offers.length, 'standing offers');

// Filter active offers
const activeOffers = offers.filter(o => o.account.isActive);

fetchActiveStandingOffers(program)

Fetch only active standing offers.

import { fetchActiveStandingOffers } from "@upstake/sdk";

const offers = await fetchActiveStandingOffers(program);
offers.forEach(({ publicKey, account }) => {
  console.log('Validator:', account.validator.toBase58());
  console.log('APR:', account.aprBps, 'bps (', account.aprBps / 100, '%)');
  console.log('Available:', account.available.toString(), 'lamports');
});

fetchUserStake(program, user, stakeAccount)

Fetch a specific user stake.

import { fetchUserStake } from "@upstake/sdk";

const userStake = await fetchUserStake(program, userPublicKey, stakeAccountPublicKey);
if (userStake) {
  console.log('Principal:', userStake.principal.toString());
  console.log('Lock duration:', userStake.lockDuration.toString());
  console.log('Instant payout:', userStake.instantPayout.toString());
  console.log('Status:', userStake.status);
}

fetchUserStakesByUser(program, user)

Fetch all stakes for a user.

import { fetchUserStakesByUser } from "@upstake/sdk";

const stakes = await fetchUserStakesByUser(program, userPublicKey);
stakes.forEach(({ publicKey, account }) => {
  console.log('Stake:', publicKey.toBase58());
  console.log('Principal:', account.principal.toString());
  console.log('Status:', account.status);
});

fetchActiveUserStakes(program, user)

Fetch only active stakes for a user.

import { fetchActiveUserStakes } from "@upstake/sdk";

const activeStakes = await fetchActiveUserStakes(program, userPublicKey);
console.log('User has', activeStakes.length, 'active stakes');

PDA Helpers

All PDA derivation functions:

import {
  getPlatformConfigPDA,
  getPlatformPDA,
  getTreasuryPDA,
  getStandingOfferPDA,
  getPoolEscrowPDA,
  getUserStakePDA,
} from "@upstake/sdk";

// Platform PDAs
const [platformConfig] = getPlatformConfigPDA();
const [platform] = getPlatformPDA();
const [treasury] = getTreasuryPDA();

// Standing Offer & Pool PDAs
const [standingOffer] = getStandingOfferPDA(validatorPublicKey);
const [poolEscrow] = getPoolEscrowPDA(standingOffer);

// User Stake PDA
const [userStake] = getUserStakePDA(userPublicKey, stakeAccountPublicKey);

Constants

import { PROGRAM_ID, ENDPOINTS } from "@upstake/sdk";

console.log('Program ID:', PROGRAM_ID.toBase58());
console.log('Testnet:', ENDPOINTS.TESTNET);
console.log('Mainnet:', ENDPOINTS.MAINNET_BETA);

Complete Example: User Flow

import { AnchorProvider, Program, BN } from "@coral-xyz/anchor";
import { Connection, Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js";
import {
  IDL,
  PROGRAM_ID,
  fetchActiveStandingOffers,
  acceptStandingOffer,
} from "@upstake/sdk";

// Setup
const connection = new Connection("https://api.testnet.solana.com");
const wallet = // ... your wallet
const provider = new AnchorProvider(connection, wallet, {});
const program = new Program(IDL, PROGRAM_ID, provider);

// 1. Browse active standing offers
const offers = await fetchActiveStandingOffers(program);
console.log('Found', offers.length, 'active offers');

// 2. Find best offer (highest rate with sufficient liquidity)
const desiredPrincipal = new BN(1 * LAMPORTS_PER_SOL);
const desiredDuration = new BN(60 * 60 * 24 * 30 * 11); // 11 months

const suitableOffers = offers.filter(({ account }) => {
  const meetsMinimums =
    account.minPrincipal.lte(desiredPrincipal) &&
    account.maxPrincipal.gte(desiredPrincipal) &&
    account.minLockDuration.lte(desiredDuration) &&
    account.maxLockDuration.gte(desiredDuration);

  const SECONDS_PER_YEAR = 365.25 * 24 * 60 * 60;
  const instantPayout = desiredPrincipal
    .mul(desiredDuration)
    .mul(new BN(account.aprBps))
    .div(new BN(SECONDS_PER_YEAR * 10000));
  const hasLiquidity = account.available.gte(instantPayout);

  return meetsMinimums && hasLiquidity;
});

const bestOffer = suitableOffers.sort((a, b) =>
  b.account.aprBps - a.account.aprBps
)[0];

console.log('Best offer:', bestOffer.account.aprBps / 100, '% APR from',
            bestOffer.account.validator.toBase58());

// 3. Accept best offer
const stakeAccount = Keypair.generate();
const acceptSig = await acceptStandingOffer(
  program,
  wallet, // user keypair
  bestOffer.account.validator,
  bestOffer.account.validatorVoteAccount,
  stakeAccount,
  desiredPrincipal,
  desiredDuration
);
console.log('Accepted! Transaction:', acceptSig);

Complete Example: Validator Flow

import {
  createStandingOffer,
  depositToPool,
  fetchStandingOffer,
} from "@upstake/sdk";

// 1. Create standing offer with initial liquidity
const sig = await createStandingOffer(
  program,
  validatorKeypair,
  validatorVoteAccount,
  500,                                  // 5% APR
  new BN(0.1 * LAMPORTS_PER_SOL),      // min 0.1 SOL
  new BN(10 * LAMPORTS_PER_SOL),       // max 10 SOL
  new BN(60 * 60 * 24 * 30 * 6),       // min 6 months
  new BN(60 * 60 * 24 * 365),          // max 1 year
  new BN(5 * LAMPORTS_PER_SOL)         // initial 5 SOL deposit
);
console.log('Standing offer created:', sig);

// 2. Monitor and add more liquidity as needed
const offer = await fetchStandingOffer(program, validatorKeypair.publicKey);
console.log('Current liquidity:', offer.available.toString(), 'lamports');
console.log('Active stakes:', offer.activeStakes.toString(), 'lamports');

// 3. Add more liquidity if pool is running low
if (offer.available.lt(new BN(1 * LAMPORTS_PER_SOL))) {
  console.log('Pool low, adding 3 SOL...');
  await depositToPool(
    program,
    validatorKeypair,
    new BN(3 * LAMPORTS_PER_SOL)
  );
}

Complete Example: Settler Bot

import {
  fetchUserStakesByValidator,
  deactivateStake,
  settle,
} from "@upstake/sdk";

// Settler bot that earns 0.25% keeper rewards

// 1. Find all stakes for a validator
const stakes = await fetchUserStakesByValidator(program, validatorPublicKey);

// 2. Find stakes ready to deactivate
const now = Date.now() / 1000;
const readyToDeactivate = stakes.filter(({ account }) =>
  account.status.active && account.unlockTimestamp.toNumber() <= now
);

// 3. Deactivate unlocked stakes
for (const { account } of readyToDeactivate) {
  try {
    const [userStakePDA] = getUserStakePDA(account.user, account.stakeAccount);
    await deactivateStake(program, userStakePDA, account.stakeAccount);
    console.log('Deactivated:', account.stakeAccount.toBase58());
  } catch (err) {
    console.error('Deactivation failed:', err);
  }
}

// 4. Find deactivating stakes ready to settle (after cooldown)
const readyToSettle = stakes.filter(({ account }) =>
  account.status.deactivating // Check if cooldown complete (use stake account data)
);

// 5. Settle and earn keeper rewards (0.25% of rewards)
for (const { account } of readyToSettle) {
  try {
    const [userStakePDA] = getUserStakePDA(account.user, account.stakeAccount);
    await settle(
      program,
      userStakePDA,
      account.user,
      account.validator,
      settlerKeypair,
      account.stakeAccount
    );
    console.log('Settled and earned keeper reward!');
  } catch (err) {
    console.error('Settlement failed:', err);
  }
}

Validator Verification

The SDK provides utilities to verify if an address is a validator. Validators in Solana have both an identity account (keypair) and one or more vote accounts (where votes are recorded). The SDK handles both cases automatically.

isValidator(connection, address) - Recommended

Smart function that checks if an address is a validator (handles both identity and vote accounts).

import { isValidator } from "@upstake/sdk";
import { Connection, PublicKey } from "@solana/web3.js";

const connection = new Connection("https://api.testnet.solana.com");
const address = new PublicKey("..."); // Could be identity OR vote account

const result = await isValidator(connection, address);

if (result.isValidator) {
  console.log("✅ Valid validator!");
  console.log("Type:", result.type); // 'vote_account' or 'validator_identity'
  console.log("Vote accounts:", result.voteAccounts);

  // Use the first vote account for creating standing offer
  const voteAccount = result.voteAccounts[0];
  await createStandingOffer(program, validator, voteAccount, ...);
} else {
  console.error("❌", result.error);
}

isValidatorVoteAccount(connection, address)

Check if a specific address is a valid vote account (owned by Vote program).

import { isValidatorVoteAccount } from "@upstake/sdk";

const voteAccount = new PublicKey("BwcLcLFz8nfuT8Q7p4WicS3Y5TMB9MJRWR54BydKRQmb");

const result = await isValidatorVoteAccount(connection, voteAccount);

if (result.isValidator) {
  console.log("✅ Valid vote account");
} else {
  console.error("❌", result.error);
}

findVoteAccountsForValidator(connection, validatorIdentity)

Find all vote accounts associated with a validator identity.

import { findVoteAccountsForValidator } from "@upstake/sdk";

const validatorIdentity = new PublicKey("...");

const voteAccounts = await findVoteAccountsForValidator(connection, validatorIdentity);

if (voteAccounts.length > 0) {
  console.log(`Found ${voteAccounts.length} vote account(s):`);
  voteAccounts.forEach(va => console.log("  ", va.toBase58()));
} else {
  console.log("No vote accounts found for this identity");
}

checkMultipleValidators(connection, addresses)

Batch check multiple vote accounts in parallel for better performance.

import { checkMultipleValidators } from "@upstake/sdk";

const addresses = [
  new PublicKey("vote1..."),
  new PublicKey("vote2..."),
  new PublicKey("vote3..."),
];

const results = await checkMultipleValidators(connection, addresses);

results.forEach((result, addressStr) => {
  console.log(addressStr, result.isValidator ? "✅" : "❌", result.error || "");
});

Validation Checks

Vote Account Validation:

  • Exists on-chain
  • Owned by Vote program (Vote111111111111111111111111111111111111111)
  • Has valid vote account data (minimum size check)

Validator Identity → Vote Account:

  • Queries cluster for all vote accounts
  • Matches by node_pubkey (validator identity)
  • Returns all associated vote accounts

Example Use Cases:

  • Frontend form validation (accept both identity and vote account inputs)
  • Pre-flight checks in validator onboarding flows
  • Bulk validator verification for admin dashboards
  • Validator discovery and listing

TypeScript Types

All types are automatically generated from the program IDL:

import type {
  PlatformConfig,
  StandingOffer,
  UserStake,
  StakeStatus,
} from "@upstake/sdk";

// Stake status enum
type Status = StakeStatus; // Active | Deactivating | Settled

// Platform Configuration
const config: PlatformConfig = {
  admin: PublicKey,
  paused: boolean,
  payoutFeeBps: number,      // e.g., 50 = 0.5%
  rewardFeeBps: number,      // e.g., 100 = 1%
  keeperRewardBps: number,   // e.g., 25 = 0.25%
  minPrincipal: BN,          // lamports
  minLockDuration: BN,       // seconds
  maxPrincipal: BN,          // lamports
  maxLockDuration: BN,       // seconds
  bump: number,
};

// Standing Offer (Validator Pool)
const offer: StandingOffer = {
  validator: PublicKey,
  validatorVoteAccount: PublicKey,
  aprBps: number,            // e.g., 500 = 5% APR
  minPrincipal: BN,
  maxPrincipal: BN,
  minLockDuration: BN,
  maxLockDuration: BN,
  totalDeposited: BN,
  activeStakes: BN,
  available: BN,
  isActive: boolean,
  createdAt: BN,
  bump: number,
};

// User Stake
const userStake: UserStake = {
  user: PublicKey,
  stakeAccount: PublicKey,
  standingOffer: PublicKey,
  principal: BN,
  lockDuration: BN,
  instantPayout: BN,
  createdAt: BN,
  unlockTimestamp: BN,
  status: StakeStatus,
  validator: PublicKey,
  validatorVoteAccount: PublicKey,
  bump: number,
};

Network Configuration

import { ENDPOINTS } from "@upstake/sdk";

// Testnet (current deployment)
const connection = new Connection(ENDPOINTS.TESTNET);

// Mainnet (when deployed)
const connection = new Connection(ENDPOINTS.MAINNET_BETA);

Program Information

  • Program ID (Testnet): H4c5Qr5rYQU7JJfPgiQFNKfMCod8Fzb3nHb8nBHQRmaw
  • Anchor Version: 0.31.1
  • Solana Version: Agave 3.0.5
  • Network: Solana Testnet
  • Model: Standing Offer Pool (v0.2.0+)

Development

# Install dependencies
cd sdk
npm install

# Build
npm run build

# Clean
npm run clean

Publishing

cd sdk
npm version patch/minor/major
npm publish

Changelog

v0.11.0 (Current)

BREAKING: Wallet-Adapter Support 🎉

This release fixes the systematic design issue with wallet-adapter integration and makes the SDK properly work with Phantom, Solflare, and all browser wallets!

What's New:

  • UpstakeClient.fromWallet(connection, wallet) - New factory method for wallet-adapter
    • Automatically creates AnchorProvider and Program
    • Works with any wallet-adapter compatible wallet
    • No manual Program setup required!
    • See examples in Quick Start section
  • Fixed documentation - FRONTEND_INTEGRATION.md now shows correct usage
  • Updated examples - All examples now show both wallet-adapter and Keypair patterns

Migration Guide:

Old (incorrect):

const client = new UpstakeClient(connection, wallet); // ❌ WRONG

New (correct):

// Option 1: Use factory method (recommended for dApps)
const client = UpstakeClient.fromWallet(connection, wallet); // ✅

// Option 2: Manual setup (for advanced use cases)
const provider = new AnchorProvider(connection, wallet, {});
const program = new Program(IDL, PROGRAM_ID, provider);
const client = new UpstakeClient(connection, program, wallet); // ✅

Why This Matters:

  • Frontend developers no longer need to understand Anchor internals
  • One-line setup for wallet-adapter integration
  • Proper transaction signing with browser wallets
  • The SDK now follows Solana dApp best practices

v0.10.0

New Feature: Comprehensive validator verification utilities

What's New:

  • isValidator(connection, address) - Smart function that handles both validator identity accounts and vote accounts
    • Automatically detects if address is a vote account or validator identity
    • Returns all associated vote accounts
    • Returns type: 'vote_account' or 'validator_identity'
  • isValidatorVoteAccount(connection, address) - Check if address is a valid vote account
    • Verifies ownership by Vote program
    • Validates account data size
    • Returns detailed error messages
  • findVoteAccountsForValidator(connection, validatorIdentity) - Find vote accounts for validator identity
    • Queries cluster for all vote accounts
    • Filters by node_pubkey (validator identity)
    • Returns array of vote account PublicKeys
  • checkMultipleValidators(connection, addresses[]) - Batch verification
    • Parallel checking for multiple addresses
    • Efficient using getMultipleAccountsInfo
    • Returns Map of results

Use Cases:

  • Frontend form validation (accept both identity and vote account inputs)
  • Validator onboarding pre-flight checks
  • Admin dashboards with bulk validator verification
  • Validator discovery and listing

Documentation:

  • Full API documentation in README
  • Usage examples for all functions
  • Type definitions with detailed comments

v0.9.0

Contract Update: Synced with Standing Offer Pool model changes

Changes:

  • Updated field names to match contract v0.1.0
  • Refreshed IDL with latest contract changes
  • Documentation updates for terminology consistency

v0.6.0 (Frontend Ready)

Major SDK Enhancement: Complete frontend and indexing support

New Modules (7 new files, ~2000 lines of utility code):

  1. Event Parsing (events.ts) - Webhook payload parsing

    • Parse all 13 Upstake events (12 existing + 4 new admin events)
    • Automatic event type detection
    • Support for both camelCase and snake_case formats
    • Helper functions: parseEventFromWebhook(), getEventType()
    • Event categorization: isValidatorPoolEvent(), isUserStakeEvent(), isAdminEvent()
  2. Formatting (formatting.ts) - Display utilities

    • SOL formatting: formatSOL(), lamportsToSOL(), solToLamports()
    • Rate formatting: formatAPR(), formatBPS()
    • Time formatting: formatDuration(), formatTimestamp(), formatTimeRemaining(), formatRelativeTime()
    • Progress: calculateProgress(), formatProgress()
    • Numbers: formatNumber(), formatTVL()
  3. Calculations (calculations.ts) - Math utilities

    • Instant payout: calculateInstantPayout(), calculateNetInstantPayout()
    • Rewards: calculateExpectedRewards(), calculateEffectiveAPR(), calculateAPY()
    • ROI analysis: calculateEffectiveReturn(), calculateBreakeven()
    • Pool metrics: calculatePoolUtilization(), calculateAvailableLiquidity()
    • Fees: calculatePlatformFee(), calculateRewardFee(), calculateKeeperReward()
    • Validation: canOfferFulfill(), calculateMaxPrincipal()
  4. Simulation (simulation.ts) - Transaction preview

    • simulateAcceptOffer() - Preview exact amounts before signing
    • previewFees() - Detailed fee breakdown
    • checkEligibility() - Pre-flight validation
    • Returns: gross/net payouts, unlock dates, estimated rewards, validation errors
  5. Aggregators (aggregators.ts) - Batch fetchers

    • fetchOfferWithBalance() - Offer + pool balance in one call
    • fetchUserPortfolio() - Complete user dashboard data
    • fetchMarketOverview() - Platform-wide stats (TVL, offers, APR, etc.)
    • fetchValidatorDashboard() - Validator complete state
  6. Error Handling (errors.ts) - User-friendly errors

    • parseTransactionError() - Convert Anchor errors to readable messages
    • 30+ error codes mapped to user-friendly messages
    • retryWithBackoff() - Resilient RPC calls
    • isUpstakeError(), getUserErrorMessage()
  7. Display Helpers (display.ts) - UI utilities

    • Status badges: getStakeStatusBadge(), getOfferStatusBadge()
    • Risk assessment: getValidatorRiskLevel()
    • Sorting: sortOffersByBestValue(), sortStakesByUnlockTime()
    • Filtering: filterOffersForUser()
    • Color helpers: getAPRColor(), getUtilizationColor()

Contract Updates:

  • ✅ Added 4 new admin events: PlatformConfigUpdated, PlatformPaused, PlatformUnpaused, AdminTransferred
  • ✅ Updated all admin instructions to emit events

Benefits:

  • ✅ Complete frontend integration support
  • ✅ Webhook indexing ready
  • ✅ Transaction preview before signing
  • ✅ User-friendly error messages
  • ✅ Comprehensive display utilities
  • ✅ Performance optimizations (batch fetching)

v0.5.0

New Features:

  • UpstakeClient Class - New unified client wrapper for all operations
  • Query Utilities - Added queries.ts with helper functions for fetching data
  • Type Guards - Added typeGuards.ts for runtime type checking
  • Validators - Added validators.ts for input validation

Bug Fixes:

  • Fixed Parameter Names - Corrected client.ts method parameters to match instruction wrappers
    • upfrontFeeBpspayoutFeeBps
    • rateBpsaprBps
    • newRateBpsnewAprBps

Documentation Updates:

  • Terminology Sync - All field names now match contract v0.1.0
  • Updated Examples - Fixed instant payout calculations with time factor
  • Type Documentation - Updated PlatformConfig, StandingOffer, UserStake types

Breaking Changes:

  • ⚠️ Client Method Signatures - Parameter names changed in UpstakeClient methods (see bug fixes above)

v0.4.0 (Documentation Update)

Documentation Fixes:

  • Fixed Program ID - Updated to correct testnet ID: H4c5Qr5rYQU7JJfPgiQFNKfMCod8Fzb3nHb8nBHQRmaw
  • Removed Outdated Examples - Removed all Intent/Offer model examples
  • Added Standing Offer Examples - Complete user, validator, and settler flows
  • Updated API Reference - All 14 instructions documented with correct signatures
  • Improved Type Documentation - Added comprehensive type examples

No Breaking Changes - This is a documentation-only update. The SDK code remains unchanged from v0.3.1.


v0.3.1

Build Fix:

  • Fixed npm package to exclude old ValidatorStats compiled files from dist/

v0.3.0 (Breaking Changes)

Architecture Optimization:

  • Removed ValidatorStats Account - All validator statistics now tracked off-chain via event indexing
    • Lower gas costs (~5,000 CU savings per transaction)
    • No rent burden for validator stats accounts
    • More flexible analytics capabilities
    • Same data available through event logs

Breaking Changes:

  • ⚠️ Removed on-chain accounts:

    • ValidatorStats account removed
    • getValidatorStatsPDA() function removed
    • fetchValidatorStats() function removed
    • fetchAllValidatorStats() function removed
  • ⚠️ Instruction changes:

    • accept_standing_offer: no longer requires validator_stats account
    • settle: no longer requires validator_stats account

Migration Guide:

Validator statistics should now be computed from indexed events using Helius/QuickNode + Supabase. See docs/WEBHOOK_SUPABASE.md for event indexing setup.


v0.2.0 (Breaking Changes - Standing Offer Model)

Major Architectural Change:

  • Standing Offer Pool Model - Replaced Intent/Offer bidding with validator liquidity pools
    • Instant upfront payments (no waiting for offers)
    • Validators create standing offers with deposited liquidity
    • Users accept offers immediately if pool has funds
  • Staking Power Model - Flexible minimum based on principal × lock_duration
    • Minimum: 1 SOL-month (2,592,000,000,000,000 lamport-seconds)
    • Examples: 1 SOL × 30 days, 2 SOL × 15 days, 0.5 SOL × 60 days
  • Security Enhancements:
    • Vote account validation
    • Rent-exempt protection
    • Fee caps (5%/10%/1% max)
  • Keeper Rewards - 0.25% of rewards for settlement

Breaking Changes:

  • ⚠️ Program ID changed - New program at H4c5Qr5rYQU7JJfPgiQFNKfMCod8Fzb3nHb8nBHQRmaw
  • ⚠️ Complete model change:
    • Removed: Intent, Offer accounts
    • Added: StandingOffer, UserStake accounts
    • Removed: createIntent, makeOffer, acceptOffer, cancelIntent, cancelOffer, updateOffer
    • Added: createStandingOffer, acceptStandingOffer, depositToPool, withdrawFromPool, etc.

v0.1.0

  • Initial release with Intent/Offer model

License

MIT

Links