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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@marinade.finance/kamino-sdk

v2.2.2-1.1

Published

Kamino SDK

Downloads

951

Readme

Kamino SDK

Kamino SDK is a TypeScript client SDK for easy access to the Kamino on-chain data.

Install

npm

npm install @solana/web3.js decimal.js @hubbleprotocol/kamino-sdk

Usage

Read on-chain data

To read kamino-specific on-chain data, you may use the following methods:

import { clusterApiUrl, Connection, PublicKey } from '@solana/web3.js';
import { Kamino } from '@hubbleprotocol/kamino-sdk';

const connection = new Connection(clusterApiUrl('mainnet-beta'));
const kamino = new Kamino('mainnet-beta', connection); 

// get all strategies supported by Kamino 
const strategies = await kamino.getStrategies();

// get specific strategy 
const customStrategy = await kamino.getStrategyByAddress(new PublicKey('my strategy address'));

// get strategy share price
const sharePrice = await kamino.getStrategySharePrice(new PublicKey('my strategy address'));

// get token holders of a strategy 
const holders = await kamino.getStrategyHolders(usdhUsdtStrategy);

// get strategy share price
const strategyPrice = await kamino.getStrategySharePrice(usdhUsdtStrategy);

Create a Kamino strategy

Create a new Kamino strategy for an existing CLMM pool (Orca or Raydium).

Current limitations (planned to be fixed to allow anyone to use this in the near future):

  • After the strategy is created, only the global admin can update the treasury fee vault with token A/B, we need to allow non-admins to be able to do (and require) this as well.
  • Only the global admin can set the kToken (strategy shares) token metadata.
  • You can create a strategy only with the current supported tokens, please reach out if you want a new token to be supported.
import { clusterApiUrl, Connection, PublicKey, sendAndConfirmTransaction, Keypair, Transaction } from '@solana/web3.js';
import { 
  Kamino,
  getAssociatedTokenAddressAndData,
  createTransactionWithExtraBudget,
  assignBlockInfoToTransaction
} from '@hubbleprotocol/kamino-sdk';
import Decimal from 'decimal.js';

// generate a new strategy public key
const newStrategy = Keypair.generate();
// setup fee payer (wallet) that will sign the transaction and own the strategy
const signer = Keypair.fromSecretKey('your secret key here');
const owner = signer.publicKey; // or use different public key for owner (admin)

// setup Kamino SDK
const connection = new Connection(clusterApiUrl('mainnet-beta'));
const kamino = new Kamino('mainnet-beta', connection);

// depending on the DEX you want to use for your strategy (Orca or Raydium) fetch the pool
// Orca: get on-chain data for an existing Orca Whirlpool
const whirlpool = new PublicKey('5vHht2PCHKsApekfPZXyn7XthAYQbsCNiwA1VEcQmL12');
const whirlpoolState = await kamino.getWhirlpoolByAddress(whirlpool);
if (!whirlpool) {
  throw Error('Could not fetch Orca whirlpool from the chain');
}

// Raydium: get on-chain data for an existing Raydium CLMM pool
const raydiumPool = new PublicKey('3tD34VtprDSkYCnATtQLCiVgTkECU3d12KtjupeR6N2X');
const raydiumPoolState = await kamino.getWhirlpoolByAddress(raydiumPool);
if (!raydiumPool) {
  throw Error('Could not fetch Raydium CLMM pool from the chain');
}

// create a transaction that has an instruction for extra compute budget
let tx = createTransactionWithExtraBudget(owner);

// check if associated token addresses exist for token A or B
const [tokenAAta, tokenAData] = await getAssociatedTokenAddressAndData(connection, whirlpoolState.tokenAMint, owner);
const [tokenBAta, tokenBData] = await getAssociatedTokenAddressAndData(connection, whirlpoolState.tokenBMint, owner);
if (!tokenAData) {
  tx.add(createAssociatedTokenAccountInstruction(owner, tokenAAta, owner, whirlpoolState.tokenMintA));
}
if (!tokenBData) {
  tx.add(createAssociatedTokenAccountInstruction(owner, tokenBAta, owner, whirlpoolState.tokenMintB));
}

// create a rent exempt strategy account that will contain the Kamino strategy
const createStrategyAccountIx = await kamino.createStrategyAccount(owner, newStrategy.publicKey);
tx.add(createStrategyAccountIx);

// create the actual strategy with USDH as token A and USDC as token B (note: the tokens in the pool should match the tokens in the strategy)
const createStrategyIx = await kamino.createStrategy(newStrategy.publicKey, whirlpool, owner, 'USDH', 'USDC');
tx.add(createStrategyIx);

tx = await assignBlockInfoToTransaction(connection, tx, owner);

const txHash = await sendAndConfirmTransaction(connection, tx, [signer, newStrategy], {
  commitment: 'finalized',
});

console.log('transaction hash', txHash);
console.log('new strategy has been created', newStrategy.publicKey.toString());

// this will work with 'finalized' transaction commitment level, 
// it might fail if you use anything other than that as the on-chain data won't be updated as quickly
// and you have to wait a bit
const strategy = await kamino.getStrategyByAddress(newStrategy.publicKey);
console.log(strategy?.toJSON());

Withdraw shares

Withdraw x amount of strategy shares from a specific shareholder (wallet), example code:

import { clusterApiUrl, Connection, PublicKey, sendAndConfirmTransaction, Keypair, Transaction } from '@solana/web3.js';
import { 
  Kamino,
  getAssociatedTokenAddressAndData,
  createTransactionWithExtraBudget,
  getCreateAssociatedTokenAccountInstructionsIfNotExist,
  assignBlockInfoToTransaction
} from '@hubbleprotocol/kamino-sdk';
import Decimal from 'decimal.js';

// setup Kamino SDK
const strategyPubkey = new PublicKey('2H4xebnp2M9JYgPPfUw58uUQahWF8f1YTNxwwtmdqVYV'); // you may also fetch strategies from hubble config
const owner = new PublicKey('HrwbdQYwSnAyVpVHuGQ661HiNbWmGjDp5DdDR9YMw7Bu'); // wallet with shares
const connection = new Connection(clusterApiUrl('mainnet-beta'));
const kamino = new Kamino('mainnet-beta', connection);
// setup fee payer (wallet) that will sign the transaction 
const signer = Keypair.generate();

// get on-chain data for a Kamino strategy 
const strategy = await kamino.getStrategyByAddress(strategyPubkey);
if (!strategy) {
  throw Error('Could not fetch strategy from the chain');
}
const strategyWithAddress = { strategy, address: strategyPubkey };

// check if associated token addresses exist for token A, B and shares
const [sharesAta, sharesMintData] = await getAssociatedTokenAddressAndData(connection, strategy.sharesMint, owner);
const [tokenAAta, tokenAData] = await getAssociatedTokenAddressAndData(connection, strategy.tokenAMint, owner);
const [tokenBAta, tokenBData] = await getAssociatedTokenAddressAndData(connection, strategy.tokenBMint, owner);

// create a transaction that has an instruction for extra compute budget (withdraw operation needs this),
let tx = createTransactionWithExtraBudget(owner);

// add creation of associated token addresses to the transaction instructions if they don't exist
const ataInstructions = await kamino.getCreateAssociatedTokenAccountInstructionsIfNotExist(
  owner,
  strategyWithAddress,
  tokenAData,
  tokenAAta,
  tokenBData,
  tokenBAta,
  sharesMintData,
  sharesAta
);
if (ataInstructions.length > 0) {
  tx.add(...ataInstructions);
}

// specify amount of shares to withdraw, e.g. to withdraw 5 shares:
const withdrawIx = await kamino.withdrawShares(strategyWithAddress, new Decimal(5), owner);
tx.add(withdrawIx);
// assign block hash, block height and fee payer to the transaction
tx = await assignBlockInfoToTransaction(connection, tx, signer.publicKey);

const txHash = await sendAndConfirmTransaction(connection, tx, [signer], {
  commitment: 'confirmed',
});

Withdraw all strategy shares from a specific shareholder (wallet), example code:

import { clusterApiUrl, Connection, PublicKey, sendAndConfirmTransaction, Keypair, Transaction } from '@solana/web3.js';
import { 
  Kamino,
  getAssociatedTokenAddressAndData,
  createTransactionWithExtraBudget,
  getCreateAssociatedTokenAccountInstructionsIfNotExist,
  assignBlockInfoToTransaction
} from '@hubbleprotocol/kamino-sdk';
import Decimal from 'decimal.js';

// setup Kamino SDK
const strategyPubkey = new PublicKey('2H4xebnp2M9JYgPPfUw58uUQahWF8f1YTNxwwtmdqVYV'); // you may also fetch strategies from hubble config
const owner = new PublicKey('HrwbdQYwSnAyVpVHuGQ661HiNbWmGjDp5DdDR9YMw7Bu'); // wallet to deposit shares into
const connection = new Connection(clusterApiUrl('mainnet-beta'));
const kamino = new Kamino('mainnet-beta', connection);
// setup fee payer (wallet) that will sign the transaction 
const signer = Keypair.generate();

// get on-chain data for a Kamino strategy 
const strategy = await kamino.getStrategyByAddress(strategyPubkey);
if (!strategy) {
  throw Error('Could not fetch strategy from the chain');
}
const strategyWithAddress = { strategy, address: strategyPubkey };

// check if associated token addresses exist for token A, B and shares
const [sharesAta, sharesMintData] = await getAssociatedTokenAddressAndData(connection, strategy.sharesMint, owner);
const [tokenAAta, tokenAData] = await getAssociatedTokenAddressAndData(connection, strategy.tokenAMint, owner);
const [tokenBAta, tokenBData] = await getAssociatedTokenAddressAndData(connection, strategy.tokenBMint, owner);

// create a transaction that has an instruction for extra compute budget (withdraw operation needs this),
let tx = createTransactionWithExtraBudget(owner);

// add creation of associated token addresses to the transaction instructions if they don't exist
const ataInstructions = await kamino.getCreateAssociatedTokenAccountInstructionsIfNotExist(
  owner,
  strategyWithAddress,
  tokenAData,
  tokenAAta,
  tokenBData,
  tokenBAta,
  sharesMintData,
  sharesAta
);
if (ataInstructions.length > 0) {
  tx.add(...ataInstructions);
}

// withdraw all strategy shares from the wallet:
const withdrawIxns = await kamino.withdrawAllShares(strategyWithAddress, owner);
if (withdrawIxns) {
  tx.add(...withdrawIxns);
} else {
  console.log('Wallet balance is 0 shares, cant withdraw any');
  return;
}

// assign block hash, block height and fee payer to the transaction
tx = await assignBlockInfoToTransaction(connection, tx, signer.publicKey);

const txHash = await sendAndConfirmTransaction(connection, tx, [signer], {
  commitment: 'confirmed',
});

Deposit shares

Deposit custom amount of token A and B for a specific strategy, example code:

import { clusterApiUrl, Connection, PublicKey, sendAndConfirmTransaction, Keypair, Transaction } from '@solana/web3.js';
import { 
  Kamino,
  getAssociatedTokenAddressAndData,
  createTransactionWithExtraBudget,
  getCreateAssociatedTokenAccountInstructionsIfNotExist,
  assignBlockInfoToTransaction
} from '@hubbleprotocol/kamino-sdk';
import Decimal from 'decimal.js';

// setup Kamino SDK
const strategyPubkey = new PublicKey('2H4xebnp2M9JYgPPfUw58uUQahWF8f1YTNxwwtmdqVYV'); // you may also fetch strategies from hubble config
const owner = new PublicKey('HrwbdQYwSnAyVpVHuGQ661HiNbWmGjDp5DdDR9YMw7Bu'); // wallet with shares
const connection = new Connection(clusterApiUrl('mainnet-beta'));
const kamino = new Kamino('mainnet-beta', connection);
// setup fee payer (wallet) that will sign the transaction 
const signer = Keypair.generate();

// get on-chain data for a Kamino strategy 
const strategy = await kamino.getStrategyByAddress(strategyPubkey);
if (!strategy) {
  throw Error('Could not fetch strategy from the chain');
}
const strategyWithAddress = { strategy, address: strategyPubkey };

// check if associated token addresses exist for token A, B and shares
const [sharesAta, sharesMintData] = await getAssociatedTokenAddressAndData(connection, strategy.sharesMint, owner);
const [tokenAAta, tokenAData] = await getAssociatedTokenAddressAndData(connection, strategy.tokenAMint, owner);
const [tokenBAta, tokenBData] = await getAssociatedTokenAddressAndData(connection, strategy.tokenBMint, owner);

// create a transaction that has an instruction for extra compute budget (deposit operation needs this),
let tx = createTransactionWithExtraBudget(owner);

// add creation of associated token addresses to the transaction instructions if they don't exist
const ataInstructions = await kamino.getCreateAssociatedTokenAccountInstructionsIfNotExist(
  owner,
  strategyWithAddress,
  tokenAData,
  tokenAAta,
  tokenBData,
  tokenBAta,
  sharesMintData,
  sharesAta
);
if (ataInstructions.length > 0) {
  tx.add(...ataInstructions);
}

// specify amount of token A and B to deposit, e.g. to deposit 5 USDH and 5 USDC:
const depositIx = await kamino.deposit(strategyWithAddress, new Decimal(5), new Decimal(5), owner);
tx.add(depositIx);

// assign block hash, block height and fee payer to the transaction
tx = await assignBlockInfoToTransaction(connection, tx, signer.publicKey);

const txHash = await sendAndConfirmTransaction(connection, tx, [signer], {
  commitment: 'confirmed',
});

Collect strategy fees and rewards

Collect strategy fees from the treasury fee vaults and rewards from the reward vaults:

import { clusterApiUrl, Connection, PublicKey, sendAndConfirmTransaction, Keypair, Transaction } from '@solana/web3.js';
import { 
  Kamino,
  createTransactionWithExtraBudget,
  assignBlockInfoToTransaction
} from '@hubbleprotocol/kamino-sdk';
import Decimal from 'decimal.js';

// setup Kamino SDK
const strategyPubkey = new PublicKey('2H4xebnp2M9JYgPPfUw58uUQahWF8f1YTNxwwtmdqVYV'); // you may also fetch strategies from hubble config
const owner = new PublicKey('HrwbdQYwSnAyVpVHuGQ661HiNbWmGjDp5DdDR9YMw7Bu'); // strategy owner
const connection = new Connection(clusterApiUrl('mainnet-beta'));
const kamino = new Kamino('mainnet-beta', connection);
// setup fee payer (wallet) that will sign the transaction 
const signer = Keypair.generate();

// create a transaction that has an instruction for extra compute budget
let tx = createTransactionWithExtraBudget(owner);

// get on-chain data for a Kamino strategy 
const strategy = await kamino.getStrategyByAddress(strategyPubkey);
if (!strategy) {
  throw Error('Could not fetch strategy from the chain');
}
const strategyWithAddress = { strategy, address: strategyPubkey };

// create collect fee/rewards instructions
const collectIx = await kamino.collectFeesAndRewards(strategyWithAddress);

tx.add(collectIx);

// assign block hash, block height and fee payer to the transaction
tx = await assignBlockInfoToTransaction(connection, tx, signer.publicKey);

const txHash = await sendAndConfirmTransaction(connection, tx, [signer], {
  commitment: 'confirmed',
});

Rebalance strategy

import { clusterApiUrl, Connection, PublicKey, sendAndConfirmTransaction, Keypair, Transaction } from '@solana/web3.js';
import {
  Kamino,
  createTransactionWithExtraBudget,
  assignBlockInfoToTransaction
} from '@hubbleprotocol/kamino-sdk';
import Decimal from 'decimal.js';

// setup Kamino SDK
const strategyPubkey = new PublicKey('2H4xebnp2M9JYgPPfUw58uUQahWF8f1YTNxwwtmdqVYV'); // you may also fetch strategies from hubble config
const owner = new PublicKey('HrwbdQYwSnAyVpVHuGQ661HiNbWmGjDp5DdDR9YMw7Bu'); // strategy owner
const connection = new Connection(clusterApiUrl('mainnet-beta'));
const kamino = new Kamino('mainnet-beta', connection);
// setup fee payer (wallet) that will sign the transaction 
const signer = Keypair.generate();
// setup new position mint
const newPosition = Keypair.generate();

// create a transaction that has an instruction for extra compute budget
let tx = createTransactionWithExtraBudget(owner);

// rebalance a USDH-USDC strategy to range 0.9 - 1.1 
const rebalanceInstructions = await kamino.rebalance(
  strategy,
  newPosition.publicKey,
  new Decimal(0.9),
  new Decimal(1.1),
  signer.publicKey
);

for (const rebalanceInstruction of rebalanceInstructions) {
  let tx = new Transaction();
  // assign block hash, block height and fee payer to the transaction
  tx = await assignBlockInfoToTransaction(connection, tx, owner);
  const txHash = await sendAndConfirmTransaction(connection, tx, [signer, newPosition], {
    commitment: 'finalized',
  });
  console.log('transaction hash', txHash);
}

Get all strategies with filters

  • The current filters that are supported are:
    • strategyType which can be:

      • NON_PEGGED: e.g. SOL-BONK
      • PEGGED: e.g. BSOL-JitoSOL
      • STABLE: e.g. USDH-USDC
    • strategyCreationStatus which can be:

      • IGNORED
      • SHADOW
      • LIVE
      • DEPRECATED
      • STAGING
import { clusterApiUrl, Connection, PublicKey, sendAndConfirmTransaction, Keypair, Transaction } from '@solana/web3.js';
import {
  Kamino,
  createTransactionWithExtraBudget,
  assignBlockInfoToTransaction,
  StrategiesFilters
} from '@hubbleprotocol/kamino-sdk';
import Decimal from 'decimal.js';

// create a Kamino instance
const kamino = new Kamino('mainnet-beta', connection);

// get all Kamino strategies that are non_pegged
let filters: StrategiesFilters = {
      strategyType: 'NON_PEGGED',
      strategyCreationStatus: undefined,
    };

let nonPeggedStrats = await kamino.getAllStrategiesWithFilters(filters);