UNPKG

@kamino-finance/kliquidity-sdk

Version:

Typescript SDK for interacting with the Kamino Liquidity (kliquidity) protocol

375 lines (339 loc) 12.2 kB
import Decimal from 'decimal.js'; import { PositionRange, RebalanceFieldInfo, RebalanceFieldsDict } from '../utils/types'; import { DefaultLowerPercentageBPSDecimal, DefaultUpperPercentageBPSDecimal } from '../utils/CreationParameters'; import { RebalanceRaw } from '../@codegen/kliquidity/types'; import { RebalanceTypeLabelName } from './consts'; import { Dex, readBigUint128LE } from '../utils'; import { sqrtPriceToPrice as orcaSqrtPriceToPrice } from '@orca-so/whirlpools-core'; import { upsertManyRebalanceFieldInfos } from './utils'; import { getPriceRangeFromPriceAndDiffBPS, getResetRangeFromPriceAndDiffBPS } from './math_utils'; import { getPriceFromQ64Price } from '../utils/meteora'; export const DefaultMaxNumberOfExpansions = new Decimal(10); export const DefaultExpansionSizeBPS = new Decimal(100); export const DefaultSwapUnevenAllowed = new Decimal(1); export const ExpanderRebalanceTypeName = 'expander'; export function getExpanderRebalanceFieldInfos( price: Decimal, lowerPercentageBPS: Decimal, upperPercentageBPS: Decimal, resetLowerPercentageBPS: Decimal, resetUpperPercentageBPS: Decimal, expansionBPS: Decimal, maxNumberOfExpansions: Decimal, swapUnevenAllowed: Decimal, enabled: boolean = true ): RebalanceFieldInfo[] { const rebalanceType: RebalanceFieldInfo = { label: RebalanceTypeLabelName, type: 'string', value: ExpanderRebalanceTypeName, enabled, }; const lowerBpsRebalanceFieldInfo: RebalanceFieldInfo = { label: 'lowerRangeBps', type: 'number', value: lowerPercentageBPS, enabled, }; const upperBpsRebalanceFieldInfo: RebalanceFieldInfo = { label: 'upperRangeBps', type: 'number', value: upperPercentageBPS, enabled, }; const resetLowerBpsRebalanceFieldInfo: RebalanceFieldInfo = { label: 'resetLowerRangeBps', type: 'number', value: resetLowerPercentageBPS, enabled, }; const resetUpperBpsRebalanceFieldInfo: RebalanceFieldInfo = { label: 'resetUpperRangeBps', type: 'number', value: resetUpperPercentageBPS, enabled, }; const expansionBpsRebalanceFieldInfo: RebalanceFieldInfo = { label: 'expansionBps', type: 'number', value: expansionBPS, enabled, }; const maxNumberOfExpansionsRebalanceFieldInfo: RebalanceFieldInfo = { label: 'maxNumberOfExpansions', type: 'number', value: maxNumberOfExpansions, enabled, }; const swapUnevenAllowedFieldInfo: RebalanceFieldInfo = { label: 'swapUnevenAllowed', type: 'number', value: swapUnevenAllowed, enabled, }; const { lowerPrice, upperPrice } = getPositionRangeFromExpanderParams(price, lowerPercentageBPS, upperPercentageBPS); const lowerRangeRebalanceFieldInfo: RebalanceFieldInfo = { label: 'rangePriceLower', type: 'number', value: lowerPrice, enabled: false, }; const upperRangeRebalanceFieldInfo: RebalanceFieldInfo = { label: 'rangePriceUpper', type: 'number', value: upperPrice, enabled: false, }; const { lowerPrice: resetLowerPrice, upperPrice: resetUpperPrice } = getPositionResetRangeFromExpanderParams( price, lowerPercentageBPS, upperPercentageBPS, resetLowerPercentageBPS, resetUpperPercentageBPS ); const resetLowerRangeRebalanceFieldInfo: RebalanceFieldInfo = { label: 'resetPriceLower', type: 'number', value: resetLowerPrice, enabled: false, }; const resetUpperRangeRebalanceFieldInfo: RebalanceFieldInfo = { label: 'resetPriceUpper', type: 'number', value: resetUpperPrice, enabled: false, }; return [ rebalanceType, lowerBpsRebalanceFieldInfo, upperBpsRebalanceFieldInfo, resetLowerBpsRebalanceFieldInfo, resetUpperBpsRebalanceFieldInfo, expansionBpsRebalanceFieldInfo, maxNumberOfExpansionsRebalanceFieldInfo, swapUnevenAllowedFieldInfo, lowerRangeRebalanceFieldInfo, upperRangeRebalanceFieldInfo, resetLowerRangeRebalanceFieldInfo, resetUpperRangeRebalanceFieldInfo, ]; } export function getPositionRangeFromExpanderParams( price: Decimal, lowerPriceDifferenceBPS: Decimal, upperPriceDifferenceBPS: Decimal ): PositionRange { return getPriceRangeFromPriceAndDiffBPS(price, lowerPriceDifferenceBPS, upperPriceDifferenceBPS); } export function getPositionResetRangeFromExpanderParams( price: Decimal, lowerPriceDifferenceBPS: Decimal, upperPriceDifferenceBPS: Decimal, resetLowerPriceDifferenceBPS: Decimal, resetUpperPriceDifferenceBPS: Decimal ): PositionRange { return getResetRangeFromPriceAndDiffBPS( price, lowerPriceDifferenceBPS, upperPriceDifferenceBPS, resetLowerPriceDifferenceBPS, resetUpperPriceDifferenceBPS ); } export function getDefaultExpanderRebalanceFieldInfos(price: Decimal): RebalanceFieldInfo[] { return getExpanderRebalanceFieldInfos( price, DefaultLowerPercentageBPSDecimal, DefaultUpperPercentageBPSDecimal, DefaultLowerPercentageBPSDecimal, DefaultUpperPercentageBPSDecimal, DefaultLowerPercentageBPSDecimal, DefaultMaxNumberOfExpansions, DefaultSwapUnevenAllowed ); } export function readRawExpanderRebalanceParamsFromStrategy(rebalanceRaw: RebalanceRaw) { const paramsBuffer = Buffer.from(rebalanceRaw.params); const params: RebalanceFieldsDict = {}; params['lowerRangeBps'] = new Decimal(paramsBuffer.readUInt16LE(0)); params['upperRangeBps'] = new Decimal(paramsBuffer.readUInt16LE(2)); params['lowerResetRatioBps'] = new Decimal(paramsBuffer.readUInt16LE(4)); params['upperResetRatioBps'] = new Decimal(paramsBuffer.readUInt16LE(6)); params['expansionBps'] = new Decimal(paramsBuffer.readUInt16LE(8)); params['maxNumberOfExpansions'] = new Decimal(paramsBuffer.readUInt16LE(10)); params['swapUnevenAllowed'] = new Decimal(paramsBuffer.readUInt8(12)); return params; } export function readExpanderRebalanceParamsFromStrategy(rebalanceRaw: RebalanceRaw): RebalanceFieldInfo[] { const paramsBuffer = Buffer.from(rebalanceRaw.params); const lowerRangeBps = new Decimal(paramsBuffer.readUInt16LE(0)); const upperRangeBps = new Decimal(paramsBuffer.readUInt16LE(2)); const lowerResetRatioBps = new Decimal(paramsBuffer.readUInt16LE(4)); const upperResetRatioBps = new Decimal(paramsBuffer.readUInt16LE(6)); const expansionBps = new Decimal(paramsBuffer.readUInt16LE(8)); const maxNumberOfExpansions = new Decimal(paramsBuffer.readUInt16LE(10)); const swapUnevenAllowed = new Decimal(paramsBuffer.readUInt8(12)); const lowerBpsRebalanceFieldInfo: RebalanceFieldInfo = { label: 'lowerRangeBps', type: 'number', value: lowerRangeBps, enabled: true, }; const upperBpsRebalanceFieldInfo: RebalanceFieldInfo = { label: 'upperRangeBps', type: 'number', value: upperRangeBps, enabled: true, }; const resetLowerBpsRebalanceFieldInfo: RebalanceFieldInfo = { label: 'resetLowerRangeBps', type: 'number', value: lowerResetRatioBps, enabled: true, }; const resetUpperBpsRebalanceFieldInfo: RebalanceFieldInfo = { label: 'resetUpperRangeBps', type: 'number', value: upperResetRatioBps, enabled: true, }; const expansionBpsRebalanceFieldInfo: RebalanceFieldInfo = { label: 'expansionBps', type: 'number', value: expansionBps, enabled: true, }; const maxNumberOfExpansionsRebalanceFieldInfo: RebalanceFieldInfo = { label: 'maxNumberOfExpansions', type: 'number', value: maxNumberOfExpansions, enabled: true, }; const swapUnevenAllowedFieldInfo: RebalanceFieldInfo = { label: 'swapUnevenAllowed', type: 'number', value: swapUnevenAllowed, enabled: true, }; return [ lowerBpsRebalanceFieldInfo, upperBpsRebalanceFieldInfo, resetLowerBpsRebalanceFieldInfo, resetUpperBpsRebalanceFieldInfo, expansionBpsRebalanceFieldInfo, maxNumberOfExpansionsRebalanceFieldInfo, swapUnevenAllowedFieldInfo, ]; } export function readRawExpanderRebalanceStateFromStrategy(rebalanceRaw: RebalanceRaw) { const stateBuffer = Buffer.from(rebalanceRaw.state); const state: RebalanceFieldsDict = {}; state['initialPoolPrice'] = new Decimal(readBigUint128LE(stateBuffer, 0).toString()); state['expansionCount'] = new Decimal(stateBuffer.readUInt16LE(16)); return state; } export function readExpanderRebalanceStateFromStrategy( dex: Dex, tokenADecimals: number, tokenBDecimals: number, rebalanceRaw: RebalanceRaw ): RebalanceFieldInfo[] { const params = readRawExpanderRebalanceParamsFromStrategy(rebalanceRaw); const lowerRangeBps = params['lowerRangeBps']; const upperRangeBps = params['upperRangeBps']; const lowerResetRatioBps = params['lowerResetRatioBps']; const upperResetRatioBps = params['upperResetRatioBps']; const expansionBps = params['expansionBps']; const state = readRawExpanderRebalanceStateFromStrategy(rebalanceRaw); const initialPriceX64 = state['initialPoolPrice']; const expansionCount = state['expansionCount']; let initialPrice: Decimal; if (dex == 'ORCA') { initialPrice = new Decimal( orcaSqrtPriceToPrice(BigInt(initialPriceX64.toString()), tokenADecimals, tokenBDecimals) ); } else if (dex == 'RAYDIUM') { initialPrice = new Decimal( orcaSqrtPriceToPrice(BigInt(initialPriceX64.toString()), tokenADecimals, tokenBDecimals) ); } else if (dex == 'METEORA') { initialPrice = getPriceFromQ64Price(new Decimal(initialPriceX64.toString()), tokenADecimals, tokenBDecimals); } else { throw new Error(`Unknown DEX ${dex}`); } const lowerRangeFactorBPS = lowerRangeBps.add(expansionBps.mul(expansionCount)); const upperRangeFactorBPS = upperRangeBps.add(expansionBps.mul(expansionCount)); const { lowerPrice, upperPrice } = getPriceRangeFromPriceAndDiffBPS( initialPrice, lowerRangeFactorBPS, upperRangeFactorBPS ); const { lowerPrice: lowerResetPrice, upperPrice: upperResetPrice } = getResetRangeFromPriceAndDiffBPS( initialPrice, lowerRangeFactorBPS, upperRangeFactorBPS, lowerResetRatioBps, upperResetRatioBps ); const lowerRangeRebalanceFieldInfo: RebalanceFieldInfo = { label: 'rangePriceLower', type: 'number', value: lowerPrice, enabled: false, }; const upperRangeRebalanceFieldInfo: RebalanceFieldInfo = { label: 'rangePriceUpper', type: 'number', value: upperPrice, enabled: false, }; const resetLowerRangeRebalanceFieldInfo: RebalanceFieldInfo = { label: 'resetPriceLower', type: 'number', value: lowerResetPrice, enabled: false, }; const resetUpperRangeRebalanceFieldInfo: RebalanceFieldInfo = { label: 'resetPriceUpper', type: 'number', value: upperResetPrice, enabled: false, }; return [ lowerRangeRebalanceFieldInfo, upperRangeRebalanceFieldInfo, resetLowerRangeRebalanceFieldInfo, resetUpperRangeRebalanceFieldInfo, ]; } export function readExpanderRebalanceFieldInfosFromStrategy(price: Decimal, rebalanceRaw: RebalanceRaw) { const paramsBuffer = Buffer.from(rebalanceRaw.params); const lowerRangeBps = new Decimal(paramsBuffer.readUInt16LE(0)); const upperRangeBps = new Decimal(paramsBuffer.readUInt16LE(2)); const lowerResetRatioBps = new Decimal(paramsBuffer.readUInt16LE(4)); const upperResetRatioBps = new Decimal(paramsBuffer.readUInt16LE(6)); const expansionBps = new Decimal(paramsBuffer.readUInt16LE(8)); const maxNumberOfExpansions = new Decimal(paramsBuffer.readUInt16LE(10)); const swapUnevenAllowed = new Decimal(paramsBuffer.readUInt8(12)); return getExpanderRebalanceFieldInfos( price, lowerRangeBps, upperRangeBps, lowerResetRatioBps, upperResetRatioBps, expansionBps, maxNumberOfExpansions, swapUnevenAllowed ); } export function deserializeExpanderRebalanceWithStateOverride( dex: Dex, tokenADecimals: number, tokenBDecimals: number, price: Decimal, rebalanceRaw: RebalanceRaw ): RebalanceFieldInfo[] { const stateFields = readExpanderRebalanceStateFromStrategy(dex, tokenADecimals, tokenBDecimals, rebalanceRaw); const fields = readExpanderRebalanceFieldInfosFromStrategy(price, rebalanceRaw); return upsertManyRebalanceFieldInfos(fields, stateFields); }