@symmetry-hq/funds-sdk
TypeScript icon, indicating that this package has built-in type declarations

0.1.80 • Public • Published

Vaults Sdk

Software Development Kit for interacting with Symmetry Vaults Program

Init

import { FundsSDK } from "@symmetry-hq/funds-sdk";

let vaultsSdk: FundsSDK = await FundsSDK.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[] = vaultsSdk.getTokenListData();

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

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

Create/Edit/Close Vault

Create Vault with specific settings and rules

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

let vault: Fund = await vaultsSdk.createFund(params: CreateFundParams);

Edit Vault (Only for actively managed vaults) Vault name and symbol can not be changed.

// Option 1 - (Simple) Directly set desired composition
await vaultsSdk
  .simpleEditFund(vault: Fund, params: SimpleEditParams);

// Option 2 - (Advanced) Provide full settings with rules
await vaultsSdk
  .editFund(vault: Fund, params: CreateFundParams);

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

await vaultsSdk.closeFund(vault: Fund);

Create/Update Metadata for Vaults token mint:

let tx: TransactionSignature[] = await vaultsSdk.setMetadata(
  vault: Fund,
  symbol: string,
  name: string,
  uri: string,
);

Load Vault(s)

Load specific vault from pubkey

let vault: Fund = await vaultsSdk.loadFromPubkey(pubkey: PublicKey);

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

let vaults: Fund[] = await vaultsSdk.findFunds(filters: FilterOption[]);
let vaults: Fund[] = await vaultsSdk.findFunds([{
  filterType: "manager"|"host",
  filterPubkey: PublicKey
}]);

You can also load vaults' current compositions

let currentCompositions: CurrentComposition[] =
  await vaultsSdk.getCurrentCompositions(vaults: Fund[]);

Buy Vault Tokens

There are three ways to buy a vault token.

Option 1 (Simple):

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

// Fetch expected amount of Vault tokens after contribution
let expectedAmount = await vaultsSdk.computeMintAmountWithSingleToken(
  vault: Fund, 
  token: PublicKey, 
  amount: number
);

// Contribute single token and mint Vault tokens
let tx: TransactionSignature = await vaultsSdk.buyWithSingleToken(
  vault: Fund, 
  token: PublicKey, 
  amount: number
);

Option 2 (Simple): Contribute all tokens from vaults composition

Works for vaults with no more than 5 tokens Amounts for tokens should be provided in the same order tokens are present in vaults composition. Vaults composition can be found in Fund object of a vault, or using getCurrentCompositions()

// Fetch expected amount of Vault tokens after contribution
let expectedAmount: number = await vaultsSdk.computeMintAmount(
  vault: Fund,
  amounts: number[]
);

// Contribute tokens and mint Vault tokens
let tx: TransactionSignature = await vaultsSdk.buyFundInstant(
  vault: Fund,
  amounts: number[]
);

Option 3 (Advanced):

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

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

let buyState: BuyState = await vaultsSdk.buyFund(
  vault: Fund,
  amountUsdc: number
);

Step 2: Rebalance BuyState: Buy underlying assets in the vault 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 vaultsSdk
  .rebalanceBuyState(buyState: BuyState);

Step 3: Mint Vault Tokens & Close Buy State
During this stage, all the assets acquired during rebalancing process will be added
to the vault. Corresponding vault tokens will be minted for user, and circulating supply
of the vault 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 vaultsSdk
  .mintFund(buyState: BuyState);

Helpers:

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

// Fetch specific BuyState using its PublicKey
let buyState: BuyState[] = await vaultsSdk
  .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 vaultsSdk.claimTokensFromBuyState(pubkey: PublicKey)

Sell Vault

There are two ways to sell vault tokens.

Option 1 (Simple): Sell to a specific token (token should be in vaults composition)

// Fetch expected amount of received tokens.
let amount: number = await vaultsSdk.computeOutputAmountWithInstantBurn(
  vault: Fund,
  amount: number,
  withdrawToken: PublicKey
);


// Burn/Sell Vault tokens.
let tx: TransactionSignature = await vaultsSdk.instantBurn(
  vault: Fund,
  amount: number,
  withdrawToken: PublicKey
);

Option 2 (Advanced):

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

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

let sellState: Fund = await vaultsSdk
  .sellFund(vault: Fund, amount: number, rebalance: boolean);

Step 2: Rebalance SellState(Vault): 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 Vaults so rebalance function is same for them.
(Optional, only when rebalance == true)

let txs: TransactionSignature[] = await vaultsSdk
  .rebalanceFund(sellState: Fund);

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 vaultsSdk
  .claimTokens(sellState: Fund);

Helpers:

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

Automation

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

Refilter Vault

Automatically selects assets based on provided rules in vault settings.

let txs: TransactionSignature = await vaultsSdk.refilterFund(vault: Fund);

Reweight Vault

Automatically updates target weights for assets based on provided rules.

let txs: TransactionSignature = await vaultsSdk.reweightFund(vault: Fund);

Rebalance Vault

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

let txs: TransactionSignature[] = await vaultsSdk.rebalanceFund(vault: Fund);

Other Helper Functions

Set priority fee:

await vaultsSdk.setPriorityFee(lamports: number);

Fetch User's Holdings:

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

Get Oracle Prices

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

Retrieve TransactionInstruction Objects that can be called on vaults.

import { VaultInstructions } from "@symmetry-hq/funds-sdk";
let ix: TransactionInstruction = await VaultInstructions.closeVaultIx(...);
let ix: TransactionInstruction = await VaultInstructions.refilterVaultIx(...);
let ix: TransactionInstruction = await VaultInstructions.reweightVaultIx(...);
let ix: TransactionInstruction = await VaultInstructions.buyVaultWithSingleTokenIx(...);
let ix: TransactionInstruction = await VaultInstructions.instantBurnIx(...);
// and many more

Swap using Symmetry Vaults Liquidity

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

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

Full Example

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

Creating vault with following settings:

  • Manager fee : 0.1% - Users pay 0.1% to vault manager when buying
  • Is actively managed - Manager can edit vault settings and rules
  • Asset Pool consists of 4 tokens - [USDC, SOL, BTC, ETH]
  • Vault refilters underlying assets every week
  • Vault reweights underlying assets every day
  • Vault rebalances every 2 hours
  • Vault rebalances when target and current weights differ by at least 5%
  • Maximum allowed slippage on rebalance is 3% (compared to oracle price)
  • Vault provides swap liquidity for a token if current weight is within
    5% (rebalance) 50%(lpOffset) = 2.5% of target weight
  • Vault has 1 rule:
  • Select TOP 3 assets by 1 week average MarketCap,
  • Weight chosen assets by square root of 1 day performance
  • Automation is on (Symmetry Engine will Refilter/Reweight/Rebalance based on rules)
  • Liquidity Provision is enabled.
import {
  FundsSDK,
  Fund,
  BuyState,
  FilterType,
  FilterTime,
  SortBy,
  WeightType,
  WeightTime
} from "@symmetry-hq/funds-sdk";

/*  init vaults sdk */
let vaultsSdk: FundsSDK = await FundsSDK.init(
   connection,
   wallet
);

/*  Create a vault (CreateFund + SetRules + Refilter&Reweight) */
let vault: Fund = await vaultsSdk.createFund({
  name: "Solana Bitcoin Ethereum",
  symbol: "SOLBTCETH",
  uri: "",
  hostPlatform: wallet.publicKey,     // platform hosting UI 
  hostPlatformFee: 0,                 // fees on deposits for host 
  manager: wallet.publicKey,          // vault manager
  managerFee: 10,                     // deposit fee in bps: 10 = 0.1%
  activelyManaged: 1,                 // actively managed
  assetPool: [0, 1, 2, 3],            // USDC, SOL, BTC, ETH
  refilterInterval: 7 * 24 * 3600,    // 1 week
  reweightInterval: 24 * 3600,        // 1 day
  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
  rules: [{
    filterBy: FilterType.MarketCap,   // filter assets by marketcap
    filterDays: FilterTime.Week,      // by 1 week average of marketcap
    sortBy: SortBy.DescendingOrder,   // select top coins by marketcap
    totalWeight: 100,                 // total weight of this rule
    fixedAsset: 0,                    // (provide if filterBy is Fixed)
    numAssets: 3,                     // select 3 assets
    weightBy: WeightType.Performance, // weight by performance
    weightDays: WeightTime.Day,       // 1 day performance
    weightExpo: 0.5,                  // square root of performance
    excludeAssets: [0],               // Exclude USDC when filtering
   }],
});

/*  1. buy a vault with 50 USDC
    2. rebalance buyState (buy underlying assets)
    3. mint vault tokens */
let buyState1: BuyState = await vaultsSdk.buyFund(vault, 50);
let txsRebalanceBuyState1 = await vaultsSdk.rebalanceBuyState(buyState1);
let txMintFund1 = await vaultsSdk.mintFund(buyState1);


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


/*  1. sell a vault
    2. rebalance vault (to USDC)
    3. claim tokens (USDC + tokens for which rebalance failed) */
let sellState1: Fund = await vaultsSdk.sellFund(vault, 0.4, true);
let txsRebalanceSellState = await vaultsSdk.rebalanceFund(sellState1);
let txsClaimTokens1 = await vaultsSdk.claimTokens(sellState);

/*  1. sell a vault without rebalancing
    2. claim tokens (all tokens in a SellState) */
let sellState2: Fund = await vaultsSdk.sellFund(vault, 0.4, false);
let txsClaimTokens2 = await vaultsSdk.claimTokens(sellState2);


/*  1. Refilter vault (RefilterInterval seconds since last refilter)
    2. Reweight vault (ReweightInterval seconds since last reweight)
    3. Rebalance vault (RebalanceInterval seconds since last rebalanace) */
let txRefilter: TransactionSignature = await vaultsSdk.refilterFund(vault);
let txReweight: TransactionSignature = await vaultsSdk.reweightFund(vault);
let txsRebalance = await vaultsSdk.rebalanceFund(vault);


/* In some cases, when vault decides to remove token after refiltering,
   and rebalance function sells it to USDC, vault 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 vault. Meaning that vault 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 vault.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 Vault 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}[],
}
type CreateFundParams = {
    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,         // vault manager
    managerFee: number,         // deposit fee for manager in bps 
    activelyManaged: number,    // 0 - Index, 1 - Actively Manged, 2 - Private
    assetPool: number[],        // Asset pool with tokenIds (check getTokenListData())
    refilterInterval: number,   // refilter interval in seconds
    reweightInterval: number,   // reweight interval in seconds
    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
    rules: Rule[],
}

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,
  fundTokenMint: PublicKey,
  fundWorth: number,
  rawPrice: number,
  manager: PublicKey,
  managerFee: number,
  activelyManagded: boolean,
  host: PublicKey,
  hstFee: number,
  currentComposition: CurrComp[],
  rules: Rule[]
}
type CurrComp = {
  mintAddress: PublicKey,
  coingeckoId: string,
  symbol: string,
  symmetryTokenId: number,
  lockedAmount: number,
  oraclePrice: number,
  usdValue: number,
  currentWeight: number,
  targetWeight: number
}
// Filter options for finding vaults:
type FundFilter = "manager" | "host";

type FilterOption = {
    filterType: FundFilter,
    filterPubkey: PublicKey,
}
// On-chain state account structure for Vaults and SellStates
type FundStateChainData = {
  version: BN,
  manager: PublicKey,
  fundToken: PublicKey,
  managerFee: BN,
  supplyOutsanding: 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[],

  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
}

Dependents (1)

Package Sidebar

Install

npm i @symmetry-hq/funds-sdk

Weekly Downloads

16

Version

0.1.80

License

ISC

Unpacked Size

785 kB

Total Files

28

Last publish

Collaborators

  • effarigesrever
  • prism-ag
  • ubermenschsym
  • symmetry_fi