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

@kestrelfi/lyc-sdk

v1.0.16

Published

Client for the long_yield_carry Anchor program: PDA derivation, account decoding, instruction builders, and high-level transaction plans.

Readme

Yield Carry TypeScript SDK

Client for the long_yield_carry Anchor program: PDA derivation, account decoding, instruction builders, and high-level transaction plans. The main entry point is LongYieldCarryClient (the “LYC client”).

Installation and import

In this monorepo, depend on the workspace package and use the long-yield-carry path alias (see root tsconfig.json):

import { LongYieldCarryClient } from "@kestrelfi/lyc-sdk";
import BN from "bn.js";
import { address, type Address } from "@solana/kit";

External consumers install @kestrelfi/lyc-sdk from npm. Two entries:

  • @kestrelfi/lyc-sdk — the full SDK. Requires the optional Kamino peer deps (@kamino-finance/klend-sdk, farms-sdk, scope-sdk) to be installed.
  • @kestrelfi/lyc-sdk/core — browser-safe read + mint/burn surface (account fetchers, PDA derivation, MintTokenBuilder/BurnTokenBuilder, constants, IDL, and the send pipeline). No Kamino/Jupiter graph; this is what frontends should import. See src/core.ts for a usage sketch.

Building and publishing

The manifest is split between two consumers:

  • Inside the monorepo (backend Lambdas, tests), main/types point at src/index.ts — esbuild/tsx bundle the TypeScript source through the pnpm workspace links, no build step required.
  • On npm, publishConfig rewrites main/module/types/exports to dist/ at pack time, and prepack runs tsup to produce it. tsup bundles the unpublished workspace packages (common, lending-platforms, oracle, perena-helpers, swap-aggregator, jupiter-helpers) into dist/ — a consumer installing from npm cannot resolve their file: links, so they must never appear in the published dependencies (they live in devDependencies). Everything declared in dependencies / peerDependencies stays external.

Always release with pnpm publish (or inspect with pnpm pack) from this directory — never npm publish. npm ignores the publishConfig entry-point overrides and would ship a package whose main points at monorepo-only TypeScript source. Sanity-check a release by unpacking the pnpm pack tarball and confirming package.json has dist/ entry points and no file: entries outside devDependencies.

Emergency circuit-breaker CLI

From the monorepo root, disable a yielding bank's circuit breaker with its PDA:

pnpm run lyc:disable-yb-cb -- prod <YIELDING_BANK_PDA>

Pass --keypair <PATH> to select an explicit admin keypair on test. On prod, the bank admin must be a configured Squads vault; the command fails closed otherwise. It creates a proposal that must be approved and executed before the circuit breaker is disabled.

Constructing the client

You need an Anchor AnchorProvider (connection + wallet). The second argument selects the deployment environment and thus the program ID and RPC URL used by the SDK:

import { AnchorProvider } from "@anchor-lang/core";
import { LongYieldCarryClient } from "@kestrelfi/lyc-sdk";

const client = new LongYieldCarryClient(provider, "local");
// or "test" | "prod"

Optional third argument: LongYieldCarryClientOptions — e.g. swapClient or a lendingPlatformClients override for tests or custom routing.

What hangs off the client

| Property | Role | | ------------------------------------------------ | -------------------------------------------------------------------- | | client.pda | Derive Token and YieldingBank PDAs | | client.account | Fetch and cache on-chain accounts (LYCToken, LYCYieldingBank) | | client.ix | Build single Kit Instructions | | client.tx | Build composed transaction plans (ATAs, wraps, refreshes, CPI pools) | | client.rpc | Shared RPC used by send helpers | | client.swap | Aggregate swap client (used by manager flows and some planners) | | client.getLendingPlatformClient(...) | Resolve the lending client for an on-chain lending position | | client.getLendingPlatformClientByPlatform(...) | Resolve a lending client when only the platform enum is known | | client.sendTransaction(...) | Sign, send, confirm, and apply plan cache invalidations |

Conservative lending-position flow band

For tokens with both a regular carry position and a conservative (lowest configured target-utilization) position, ordinary collateral flows use a 10%–20% band around the 15% strategic allocation:

  • New lent collateral fills the conservative position toward 20% of total post-deposit lent collateral, then routes the remainder to regular positions.
  • Outflows use the conservative position first down to 10% of total post-withdrawal lent collateral, then use regular positions.
  • Async redemptions preserve the 10% floor while other positions and existing unlent reserves are available, but may cross it as a final liveness fallback.

Minting yTokens (mint_token)

Recommended: client.tx.mintToken.getTx(...)

Use this for end-user flows. It prepends, when needed:

  • SOL collateral: create the user’s wSOL ATA (if missing), transfer lamports from the signer, sync_native.
  • yToken: idempotent ATA creation for the user’s receipt account.

Then it appends mint_token. The signer must be the wallet that owns the collateral and will receive minted yTokens.

import BN from "bn.js";
import { address } from "@solana/kit";

const signer = address(walletAddress);
const tokenPda = address("…"); // Token PDA

const mintPlan = await client.tx.mintToken.getTx({
  signer,
  token: tokenPda,
  params: {
    depositAmount: new BN(1_000_000_000), // base units, e.g. lamports
  },
});
await client.sendTransaction(walletSigner, mintPlan);

Minting from any Jupiter-routable token

The input does not have to be the yToken's configured collateral. Set inputMint to any mint for which Jupiter can find a route and the builder will create one atomic transaction plan that:

  1. creates any required associated token accounts and wraps native SOL when needed;
  2. swaps the exact depositAmount from inputMint into the configured collateral; and
  3. deposits the received collateral and mints yTokens.

The Jupiter swap is inserted before mint_token, so the user does not need to swap or hold the collateral in a separate transaction.

For example, to spend exactly 10 USDC to mint a SOL-collateralized yToken:

import BN from "bn.js";
import { address } from "@solana/kit";

const signer = address(walletAddress);
const tokenPda = address(lycTokenPda);
const usdcMint = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");

const mintPlan = await client.tx.mintToken.getTx({
  signer,
  token: tokenPda,
  params: {
    inputMint: usdcMint,
    depositAmount: new BN(10_000_000), // 10 USDC in 6-decimal base units
    slippageBps: 50, // optional; defaults to 50 (0.5%)
  },
});

// Send the complete plan, not only mintPlan.instructions: Jupiter routes may
// require the address lookup tables included in mintPlan.lookupTables.
await client.sendTransaction(walletSigner, mintPlan);

depositAmount is always denominated in base units of inputMint. You may pass "all" to spend the wallet's entire input-token balance. When the input is native SOL, "all" keeps 0.01 SOL in the wallet for transaction fees and rent. For other input tokens, the SDK must know the mint's token program to resolve "all"; use an explicit BN amount if the mint is not registered by the SDK.

The swap is exact-in. slippageBps controls the minimum collateral output accepted by Jupiter, and the builder deposits that guaranteed output without consuming any collateral the user already held. Any execution surplus remains in the user's collateral account. Building the plan fails without sending a transaction if Jupiter cannot find a route or the quote is otherwise invalid.

Burning yTokens (burn_token)

Recommended: client.tx.burnToken.getTx(...)

burn_token redeems from unlent collateral when balances suffice at execution time. Without asyncFallback, the SDK sends BurnTokenParams.sync; insufficient unlent balance surfaces as InsufficientUnlentCollateral. Pass asyncFallback (epoch/request PDAs + BurnTokenParams.async) when the same transaction should create or extend an async redemption queue instead.

const burnPlan = await client.tx.burnToken.getTx({
  signer,
  token: tokenPda,
  params: {
    burnAmount: new BN(500_000_000),
  },
});
await client.sendTransaction(walletSigner, burnPlan);

When collateral is native SOL (WSOL), the plan appends a close-account after burn_token so proceeds settle as SOL.

const plainBurn = await client.tx.burnToken.getTx({
  signer,
  token: tokenPda,
  params: {
    burnAmount: new BN(500_000_000),
    // Optional: include a fallback so the same burn_token instruction creates
    // an async request if reserves are short by the time the tx lands.
    asyncFallback: {
      epochId: new BN(7),
      // Omit requestSequence to let the builder read the next epoch sequence.
      requestSequence: new BN(0),
    },
  },
});

Async Burns

Async burns group one or more FIFO requests into a redemption epoch. Users still call burnToken; there is no separate user-facing create/request instruction:

await client.tx.burnToken.getTx({
  signer,
  token: tokenPda,
  params: {
    burnAmount: new BN(500_000_000),
    asyncFallback: {
      epochId: new BN(7),
      requestSequence: new BN(0),
    },
  },
});

Managers fund closed epochs by running decreaseCarryPosition with redemptionEpoch when DCP loss should be charged to that epoch, then topUpUnlentReserves to withdraw freed collateral. processAsyncBurn processes requests in exact sequence order and rejects out-of-order requests.

Reading on-chain state

Token accounts → LYCToken

const token = await client.account.fetchToken(tokenPda);
// token.address — Token PDA
// token.data — raw IDL shape
// token.mint, token.collateralMint, token.decimals
// token.price, token.totalSupply, token.tvlUsd — UI helpers

Batch and discovery:

  • client.account.fetchTokens([pda1, pda2]) — multiple PDAs.
  • client.account.fetchAllTokens() — all Token accounts owned by the program.

Use { fresh: true } to bypass the short-lived in-memory cache when you must see writes immediately:

await client.account.fetchToken(tokenPda, { fresh: true });

Yielding bank accounts → LYCYieldingBank

const bank = await client.account.fetchYieldingBank(yieldingBankPda);
// bank.address, bank.data
// bank.baseMint, bank.defaultRedemptionMint, bank.id, bank.decimals // share decimals, canonical 8
// bank.sharePrice, bank.minSharePrice, bank.totalShares — UI helpers

Also: fetchYieldingBanks([...]), fetchAllYieldingBanks().

User holdings → UserHolding[]

Fetch every LYC yToken a wallet holds, with balances and optional USD notionals when you pass a collateral USD priceMap.

const holdings = await client.account.fetchUserHoldings(walletAddress, {
  priceMap: { [collateralMint]: usdPrice },
});
// holdings[i].token        — LYCToken model (price, label, decimals, …)
// holdings[i].balance      — bigint, base units of the yToken
// holdings[i].uiBalance    — decimal-adjusted number
// holdings[i].valueUsd     — uiBalance × token.price × priceMap[collateralMint], if priced
// holdings[i].tokenAccount — user ATA for the yToken mint

Without a usable entry in priceMap for the collateral mint, valueUsd is omitted (no inference from stale on-chain TVL). Zero-balance holdings are filtered out by default; pass { includeEmpty: true } to include them.

PDAs (when you know mint + id)

const [tokenPda] = await client.pda.deriveTokenPda(yTokenMint, tokenId);
const [bankPda] = await client.pda.deriveYieldingBankPda(baseMint, yieldingBankId);

Seeds match the program: TOKEN + mint + id; YIELDING_BANK + base mint + id.


Sending transactions

client.sendTransaction(payer, planOrInstructions, options?) accepts either a LongYieldCarryTransactionPlan (instructions, lookupTables, optional postSuccessCacheInvalidations) or a bare Instruction[]. When you pass a plan, post-success hooks clear SDK caches for affected Tokens / YieldingBanks plus any Kamino-related invalidations. Use the payer’s Kit TransactionSigner expected by common’s signSendAndConfirmTransaction.