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

@symmetry-hq/baskets-sdk

v0.0.45

Published

Software Development Kit for interacting with Symmetry Baskets Program

Downloads

433

Readme

Baskets Sdk

Software Development Kit for interacting with Symmetry Baskets Program

Init

import { BasketsSDK } from "@symmetry-hq/baskets-sdk";

let basketsSdk: BasketsSDK = await BasketsSDK.init(
    // rpc connection
    connection: Connection,
    // wallet (optional, can be provided later, using setWallet()
    wallet: Wallet,
);

// load list of supported tokens(returns TokenSettings array)
let tokens: TokenSettings[] = basketsSdk.getTokenListData();

// get tokenId(tokenId in symmetry supported tokens) from mint
let tokenId = basketsSdk.tokenIdFromMint(
  "So11111111111111111111111111111111111111112"
);

// set wallet
basketsSdk.setWallet(wallet: Wallet);

Create/Edit/Close Basket

Create Basket with specific settings and rules

Checkout Types and Full Example for more information on how settings should be provided.

let basket: Basket = await basketsSdk.createBasket(params: SimpleCreateParams);

Edit Basket (Only for Mutable & Permissioned baskets)

await basketsSdk
  .editBasket(basket: Basket, params: SimpleEditParams);

Close Basket (Basket TVL == 0 & no active BuyStates is required)

await basketsSdk.closeBasket(basket: Basket);

Create/Update Metadata for Baskets token mint:

let tx: TransactionSignature[] = await basketsSdk.setMetadata(
  basket: Basket,
  symbol: string,
  name: string,
  uri: string,
);

Edit Basket Manager:

let tx: TransactionSignature[] = await basketsSdk.editManager(
  basket: Basket,
  newManager: PublicKey,
);

Load Basket(s)

Load specific basket from pubkey

let basket: Basket = await basketsSdk.loadFromPubkey(pubkey: PublicKey);

Load baskets array based on filters (manager/host platform pubkey)

let baskets: Basket[] = await basketsSdk.findBaskets(filters: FilterOption[]);
let baskets: Basket[] = await basketsSdk.findBaskets([{
  filterType: "manager"|"host",
  filterPubkey: PublicKey
}]);

You can also load baskets' current compositions

let currentCompositions: CurrentComposition[] =
  await basketsSdk.getCurrentCompositions(baskets: Basket[]);

Buy Basket Tokens

There are three ways to buy a basket token.

Option 1 (Simple):

Users also have option to contribute USDC to the basket. Symmetry Engine will use that USDC to buy underlying tokens (rebalance usdc to baskets current composition based on their target weights) After rebalancing, basket tokens will be minted for user.

Step 1: Contribute USDC to Basket and create BuyState (Temporary on-chain account) Initially BuyState will have the information about users contribution.

let buyState: BuyState = await basketsSdk.buyBasket(
  basket: Basket,
  amountUsdc: number
);

Step 2: Rebalance BuyState: Buy underlying assets in the basket using contributed USDC. During rebalancing process, BuyState will store the information about already purchased assets, as well as remaining amount of USDC. Users can always force-rebalance their BuyState, but it is also done by Symmetry engine (BuyState will automatically rebalance within 3 minutes)

let txs: TransactionSignature[] = await basketsSdk
  .rebalanceBuyState(buyState: BuyState);

Step 3: Mint Basket Tokens & Close Buy State During this stage, all the assets acquired during rebalancing process will be added to the basket. Corresponding basket tokens will be minted for user, and circulating supply of the basket tokens will increase by same amount. After, it will safely close BuyState. Users can call force-mint function after 3 minutes, but Symmetry engine will also automatically handle this step. Rent for creating temporary account goes back to user.

let tx: TransactionSignature = await basketsSdk
  .mintBasket(buyState: BuyState);

Option 2 (Advanced):

Buy basket with a single token (token should be in the basket composition)

// Fetch expected amount of Basket tokens after contribution
let expectedAmount = await basketsSdk.computeMintAmountWithSingleToken(
  basket: Basket, 
  token: PublicKey, 
  amount: number
);

// Contribute single token and mint Basket tokens
let tx: TransactionSignature = await basketsSdk.buyWithSingleToken(
  basket: Basket, 
  token: PublicKey, 
  amount: number
);

Option 3 (Advanced): Contribute all tokens from baskets composition

Works for baskets with no more than 5 tokens Amounts for tokens should be provided in the same order tokens are present in baskets composition. Baskets composition can be found in Basket object of a basket, or using getCurrentCompositions()

// Fetch expected amount of Basket tokens after contribution
let expectedAmount: number = await basketsSdk.computeMintAmountWithMultipleTokens(
  basket: Basket,
  contribution: {token: PublicKey, amount: number}[],
);

// Contribute tokens and mint Basket tokens
let tx: TransactionSignature = await basketsSdk.buyBasketWithMultipleTokens(
  basket: Basket,
  contribution: {token: PublicKey, amount: number}[],
);

Helpers:

// Find active BuyStates for user that have been created after Step 1
let buyStates: BuyState[] = await basketsSdk
  .findActiveBuyStates(user: PublicKey);

// Fetch specific BuyState using its PublicKey
let buyState: BuyState[] = await basketsSdk
  .fetchBuyStateFromPubkey(pubkey: PublicKey);

// Claim Tokens from halted BuyState
// In a case where BuyState has been created more than 30 minutes ago,
// and mint was unsuccessful for some reason
// (no liquidity for token / oracle feed not live / chain congestion)
// contributed token(s) can be claimed back by user.
let tx = await basketsSdk.claimTokensFromBuyState(pubkey: PublicKey)

Sell Basket

There are two ways to sell basket tokens.

Option 1 (Simple):

Users can directly claim their portion of the tokens from Baskets composition. They also have an option to rebalance those tokens to USDC before claiming.

Step 1: Burn Basket Tokens and create SellState (Temporary on-chain account) SellState is an on-chain account which has exactly same structure as Basket. Initially SellState will store baskets composition with amounts corresponding to users holdings. Circulating supply will decrease by burn amount and users Basket tokens will be burned. From here, this SellState is already independent from the original Basket. User can specify if they want to rebalance their to-be-claimed tokens to USDC (rebalance = 1)

let sellState: Basket = await basketsSdk
  .sellBasket(basket: Basket, amount: number, rebalance: number);

Step 2: Rebalance SellState(Basket): Rebalance underlying assets to USDC This step is optional, user can skip this step and directly claim underlying tokens. SellState-s have exact structure as Baskets so rebalance function is same for them. (Optional, only when rebalance == true)

let txs: TransactionSignature[] = await basketsSdk
  .rebalanceBasket(sellState: Basket);

Step 3: Claim underlying tokens (or USDC) from SellState. After this step, SellState is closed and rent is collected by user.

let txs: TransactionSignature[] = await basketsSdk
  .claimTokens(sellState: Basket);

Helpers:

// Find active SellStates for user that have been created after Step 1
let sellStates: Basket[] = await basketsSdk
  .findActiveSellStates(user: PublicKey);

Option 2 (Advanced): Sell to a specific token (token should be in baskets composition)

// Fetch expected amount of received tokens.
let amount: number = await basketsSdk.computeOutputAmountWithSingleToken(
  basket: Basket,
  withdrawToken: PublicKey,
  amount: number,
);


// Burn/Sell Basket tokens.
let tx: TransactionSignature = await basketsSdk.sellBasketToSingleToken(
  basket: Basket,
  withdrawToken: PublicKey,
  amount: number,
);

Automation

Automation on all baskets, as well as monitoring active Buy/Sell states and rebalancing them is done by Symmetry Engine, based on RebalanceInterval settings provided in basket rules. Platforms and Developers can run their own automation for their baskets using examples bellow. Managers can Force-Rebalance anytime.

Rebalance Basket

Swap basket tokens using DEX aggregators to bring all tokens back to target weights.

let txs: TransactionSignature[] = await basketsSdk.rebalanceBasket(basket: Basket);

Other Helper Functions

Set priority fee:

await basketsSdk.setPriorityFee(lamports: number);

Fetch User's Holdings:

let allHoldings = await basketsSdk.fetchAllHoldings(user: PublicKey);

Get Oracle Prices

let txs: number[] = await basketsSdk.getOraclePrices();

Retrieve TransactionInstruction Objects that can be called on baskets.

import { BasketInstructions } from "@symmetry-hq/baskets-sdk";
let ix: TransactionInstruction = await BasketInstructions.createBasketIx(...);
let ix: TransactionInstruction = await BasketInstructions.editBasketIx(...);
let ix: TransactionInstruction = await BasketInstructions.buyBasketIx(...);
let ix: TransactionInstruction = await BasketInstructions.sellBasketIx(...);
// and many more

Swap using Symmetry Baskets Liquidity

Users can swap and check available swap liquidity for a specific basket

Find out more at @symmetry-hq/liquidity-sdk

Full Example

Example shows how to create/buy/sell/rebalance/refilter/reweight a basket

Creating basket with following settings:

  • Manager fee : 0.1% - Users pay 0.1% to basket manager when buying
  • Is Mutable - Manager can edit basket settings and rules
  • Basket rebalances every 2 hours
  • Basket rebalances when target and current weights differ by at least 5%
  • Maximum allowed slippage on rebalance is 3% (compared to oracle price)
  • Basket provides swap liquidity for a token if current weight is within 5% (rebalance) 50%(lpOffset) = 2.5% of target weight
  • Basket has 2 Assets - USDC and BONK,
  • Automation is on (Symmetry Engine will Rebalance based on settings)
  • Liquidity Provision is enabled.
import {
  BasketsSDK,
  Basket,
  BuyState,
  FilterType,
  FilterTime,
  SortBy,
  WeightType,
  WeightTime
} from "@symmetry-hq/baskets-sdk";

/*  init baskets sdk */
let basketsSdk: BasketsSDK = await BasketsSDK.init(
   connection,
   wallet
);

/*  Create a basket */
let basket: Basket = await basketsSdk.createBasket({
  name: "BONK - USDC POOL",
  symbol: "BOOONK",
  uri: "",
  hostPlatform: wallet.publicKey,     // platform hosting UI 
  hostPlatformFee: 0,                 // fees on deposits for host 
  manager: wallet.publicKey,          // basket manager
  managerFee: 10,                     // deposit fee in bps: 10 = 0.1%
  activelyManaged: 1,                 // actively managed
  rebalanceInterval: 2 * 3600,        // 2 hours
  rebalanceThreshold: 500,            // 5%
  rebalanceSlippage: 300,             // 3%
  lpOffsetThreshold: 5000,            // 50% of rebalance threshold
  disableRebalance: 0,                // Auto Rebalance is on
  disableLp: 0,                       // Liquidity provision is on
  composition: [
    {
      token: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
      weight: 40
    }, // USDC
    {
      token: new PublicKey("DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"),
      weight: 60
    }, // BONK
  ],
  feeDelagate: wallet.publicKey,
});

/*  1. buy a basket with 50 USDC
    2. rebalance buyState (buy underlying assets)
    3. mint basket tokens */
let buyState1: BuyState = await basketsSdk.buyBasket(basket, 50);
let txsRebalanceBuyState1 = await basketsSdk.rebalanceBuyState(buyState1);
let txMintBasket1 = await basketsSdk.mintBasket(buyState1);


/*  1. buy a basket with 50 USDC without rebalancing
      unspent amount will have *penalty* and counted as if rebalance
      happend with maximum slippage (rebalanceSlippage = 3%)
    2. mint basket tokens */
let buyState2: BuyState = await basketsSdk.buyBasket(basket, 50);
let txMintBasket2 = await basketsSdk.mintBasket(buyState2);


/*  1. sell a basket
    2. rebalance basket (to USDC)
    3. claim tokens (USDC + tokens for which rebalance failed) */
let sellState1: Basket = await basketsSdk.sellBasket(basket, 0.4, 1);
let txsRebalanceSellState = await basketsSdk.rebalanceBasket(sellState1);
let txsClaimTokens1 = await basketsSdk.claimTokens(sellState);

/*  1. sell a basket without rebalancing
    2. claim tokens (all tokens in a SellState) */
let sellState2: Basket = await basketsSdk.sellBasket(basket, 0.4, 1);
let txsClaimTokens2 = await basketsSdk.claimTokens(sellState2);


/*  1. Rebalance basket (RebalanceInterval seconds since last rebalanace) */
let txsRebalance = await basketsSdk.rebalanceBasket(basket);


/* In some cases, when basket decides to remove token after refiltering,
   and rebalance function sells it to USDC, basket might end up with 
   small amount of that token, for which it is unable to rebalance and
   sell the full amount. Although it's possible to remove the "dust"
   if user swaps equivalent amount of usdc (based on oracle price) for 
   the token, which is stuck in a basket. Meaning that basket will receive
   USDC and user will receive token. RemoveDust will be triggered for
   tokens with equivalent USD value < 0.005.
   Amount of usdc to be swapped is computed in a following way:
   max(tokenAmount * tokenPrice * 1.1 (for slippage purposes), 0.000001) */
let txsRemoveDust = await basket.removeDust();

Types

// Information about Token and its settings
type TokenSettings = {
    id: number,
    symbol: string,
    name: string,
    tokenMint: string,
    decimals: number,
    coingeckoId: string,
    pdaTokenAccount: string,
    oracleType: string,
    oracleAccount: string,
    oracleIndex: number,
    oracleConfidencePct: number,
    fixedConfidenceBps: number,
    tokenSwapFeeBeforeTwBps: number,
    tokenSwapFeeAfterTwBps: number,
    isLive: boolean,
    lpOn: boolean,
    useCurveData: boolean,
    additionalData: number[],
}
// Create/Edit Basket settings
type SimpleEditParams = {
    managerFee: number,         // manager fee in bps 100 = 1%
    rebalanceInterval: number,  // rebalance interval in seconds
    rebalanceThreshold: number, // rebalance threshold in bps 100 = 1%
    rebalanceSlippage: number,  // rebalance slippage in bps 100 = 1%
    lpOffsetThreshold: number,  // liquidity provsion ... in bps
    disableRebalance: boolean,  // true for turning of automation
    disableLp: boolean,         // true for turining of lp
    composition: {token: PublicKey, weight: number}[],
    feeDelegate?: PublicKey     // account which recives fees (manager by default)
}

type SimpleCreateParams = {
    name: string,
    symbol: string,
    uri: string,                // metadata URI which stores name, symbol, logo.
    hostPlatform: PublicKey,    // platofrm hosting symmetry UI
    hostPlatformFee: number,    // deposit fee for host platform in bps
    manager: PublicKey,         // basket manager
    managerFee: number,         // deposit fee for manager in bps 
    activelyManaged: number,    // 0 - Immutable, 1 - Mutable, 2 - Permissioned
    rebalanceInterval: number,  // rebalance interval in seconds
    rebalanceThreshold: number, // rebalance threshold in bps 
    rebalanceSlippage: number,  // rebalance slippage in bps
    lpOffsetThreshold: number,  // liquidity provision .. in bps
    disableRebalance: boolean,  // true for disabling automaion
    disableLp: boolean,         // true for disabling liquidity provision
    composition: {token: PublicKey, weight: number}[], // desired composition
    feeDelegate?: PublicKey     // account which recives fees (manager by default)
}

type Rule = {
  totalWeight: number,     // (integer) - total weight of this rule (relative to other rules)
  filterBy: FilterType,    // FilterType.Fixed if you want this rule to have 1 specific asset
  filterDays: FilterTime,
  sortBy: SortBy,
  fixedAsset: number,      // id of token if you want this rule to have 1 specific asset
  numAssets: number,       // number of tokens for this rule (1 for specific asset)
  weightBy: WeightType,
  weightDays: WeightTime,
  weightExpo: number,      // (integer) expo multiplied by 100x. for ^0.5 use 500
  excludeAssets: number[], // token ids to be excluded when filtering
}

export enum FilterType {
    Fixed,       // 0
    MarketCap,   // 1
    Volume,      // 2
    Performance, // 3
}
export enum WeightType {
    Fixed,       // 0
    MarketCap,   // 1
    Volume,      // 2
    Performance, // 3
}
export enum FilterTime {
    Day,         // 0
    Week,        // 1
    Month,       // 2
    Quarter,     // 3
    HalfYear,    // 4
    Year,        // 5
}
export enum WeightTime {
    Day,         // 0
    Week,        // 1
    Month,       // 2
    Quarter,     // 3
    HalfYear,    // 4
    Year,        // 5
}
export enum SortBy {
    DescendingOrder,  // 0
    AscendingOrder,   // 1
}
// Current Composition 
type CurrentComposition = {
  currentSupply: number,
  basketTokenMint: PublicKey,
  basketWorth: number,
  rawPrice: number,
  manager: PublicKey,
  managerFee: number,
  activelyManaged: boolean,
  host: PublicKey,
  hstFee: number,
  currentComposition: CurrComp[],
  rules: Rule[]
}
type CurrComp = {
  coingeckoId: string,
  currentWeight: number,
  lockedAmount: number,
  mintAddress: string,
  oraclePrice: number,
  symbol: string,
  symmetryTokenId: number,
  targetWeight: number
  usdValue: number,
  tokenData: TokenSettings
}
// Filter options for finding baskets:
type BasketFilter = "manager" | "host";

type FilterOption = {
    filterType: BasketFilter,
    filterPubkey: PublicKey,
}
// On-chain state account structure for Baskets and SellStates
type BasketStateChainData = {
  version: BN,
  manager: PublicKey,
  fundToken: PublicKey,
  managerFee: BN,
  supplyOutstanding: BN,
  activelyManaged: BN,
  activeBuyStates: BN,

  sellState: BN,
  rebalanceSellState: BN,

  hostPubkey: PublicKey,
  hostFee: BN,

  numOfTokens: BN,     
  currentCompToken: BN[],
  currentCompAmount: BN[],
  lastRebalanceTime: BN[],
  targetWeight: BN[],
  weightSum: BN,

  currentWeight: BN[],
  fundWorth: BN,
  lastUpdateTime: BN,

  refilterInterval: BN,
  reweightInterval: BN,
  rebalanceInterval: BN,
  rebalanceThreshold: BN,
  rebalanceSlippage: BN,
  lpOffsetThreshold: BN,
  lastRefilterTime: BN,
  lastReweightTime: BN,

  rulesReady: BN,
  assetPool: BN[],
  numOfRules: BN,      
  rules: {
    filterBy: BN,
    filterDays: BN,
    sortBy: BN,
    totalWeight: BN,
    fixedAsset: BN,
    numAssets: BN,
    weightBy: BN,  
    weightDays: BN,
    weightExpo: BN,
    excludeNum: BN,
    excludeAssets: BN[],
    ruleAssets: BN[],
  }[],

  numRuleTokens: BN,   
  ruleTokens: BN[],
  ruleTokenWeights: BN[],

  messageDigestFive: number[],

  disableRebalance: BN,
  disableLp: BN,

  allowMultiAssetContribution: number,
  symbolLength: number,
  symbol: number[],
  nameLength: number,
  name: number[],
  uriLength: number,
  uri: number[],

  feeDelegate: PublicKey,
  extraBytes: number[],
}

// On-chain state account structure for BuyStates
type BuyStateChainData = {
  fund: PublicKey,
  buyer: PublicKey,
  fundManager: PublicKey,
  hostPlatform: PublicKey,
  buyerFundTokenAccount: PublicKey,
  usdcContributed: BN,
  usdcLeft: BN,
  token: BN[],
  amountToSpend: BN[],
  amountBought: BN[],
  creationTimestamp: BN,
  contributedValue: BN
}