@kamino-finance/kliquidity-sdk
Version:
Typescript SDK for interacting with the Kamino Liquidity (kliquidity) protocol
139 lines (124 loc) • 4.78 kB
text/typescript
import Decimal from 'decimal.js';
import { PositionRange, RebalanceFieldInfo, RebalanceFieldsDict } from '../utils/types';
import { FullBPSDecimal } from '../utils/CreationParameters';
import { Dex, readBigUint128LE } from '../utils';
import { sqrtPriceToPrice as orcaSqrtPriceToPrice } from '@orca-so/whirlpools-core';
import BN from 'bn.js';
import { RebalanceRaw } from '../@codegen/kliquidity/types';
import { getPriceFromQ64Price } from '../utils/meteora';
import { SqrtPriceMath } from '@raydium-io/raydium-sdk-v2/lib';
export const DEFAULT_LOWER_RANGE_PRICE_DIFF_BPS = new Decimal(500);
export const DEFAULT_UPPER_RANGE_PRICE_DIFF_BPS = new Decimal(500);
export const DEFAULT_DESTINATION_TOKEN = new Decimal(1);
export const TakeProfitRebalanceTypeName = 'takeProfit';
export function getTakeProfitRebalanceFieldsInfos(
lowerRangePrice: Decimal,
upperRangePrice: Decimal,
destinationToken: Decimal,
enabled: boolean = true
): RebalanceFieldInfo[] {
const rebalanceType: RebalanceFieldInfo = {
label: 'rebalanceType',
type: 'string',
value: TakeProfitRebalanceTypeName,
enabled,
};
const lowerRangePriceRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'rangePriceLower',
type: 'number',
value: lowerRangePrice,
enabled,
};
const upperRangePriceRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'rangePriceUpper',
type: 'number',
value: upperRangePrice,
enabled,
};
const destinationTokenRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'destinationToken',
type: 'number',
value: destinationToken,
enabled,
};
return [
rebalanceType,
lowerRangePriceRebalanceFieldInfo,
upperRangePriceRebalanceFieldInfo,
destinationTokenRebalanceFieldInfo,
];
}
export function getPositionRangeFromTakeProfitParams(
dex: Dex,
tokenADecimals: number,
tokenBDecimals: number,
lowerSqrtPriceX64: Decimal,
upperSqrtPriceX64: Decimal
): PositionRange {
if (dex == 'ORCA') {
const lowerPrice = new Decimal(
orcaSqrtPriceToPrice(BigInt(lowerSqrtPriceX64.toString()), tokenADecimals, tokenBDecimals)
);
const upperPrice = new Decimal(
orcaSqrtPriceToPrice(BigInt(upperSqrtPriceX64.toString()), tokenADecimals, tokenBDecimals)
);
return { lowerPrice, upperPrice };
} else if (dex == 'RAYDIUM') {
const lowerPrice = new Decimal(
orcaSqrtPriceToPrice(BigInt(lowerSqrtPriceX64.toString()), tokenADecimals, tokenBDecimals)
);
const upperPrice = new Decimal(
orcaSqrtPriceToPrice(BigInt(upperSqrtPriceX64.toString()), tokenADecimals, tokenBDecimals)
);
return { lowerPrice, upperPrice };
} else if (dex == 'METEORA') {
const lowerPrice = getPriceFromQ64Price(new Decimal(lowerSqrtPriceX64.toString()), tokenADecimals, tokenBDecimals);
const upperPrice = getPriceFromQ64Price(new Decimal(upperSqrtPriceX64.toString()), tokenADecimals, tokenBDecimals);
return { lowerPrice, upperPrice };
} else {
throw new Error(`Unknown DEX ${dex}`);
}
}
export function getDefaultTakeProfitRebalanceFieldsInfos(price: Decimal): RebalanceFieldInfo[] {
const lowerPrice = price.mul(FullBPSDecimal.sub(DEFAULT_LOWER_RANGE_PRICE_DIFF_BPS)).div(FullBPSDecimal);
const upperPrice = price.mul(FullBPSDecimal.add(DEFAULT_UPPER_RANGE_PRICE_DIFF_BPS)).div(FullBPSDecimal);
return getTakeProfitRebalanceFieldsInfos(lowerPrice, upperPrice, price);
}
export function readTakeProfitRebalanceParamsFromStrategy(
tokenADecimals: number,
tokenBDecimals: number,
rebalanceRaw: RebalanceRaw
) {
const paramsBuffer = Buffer.from(rebalanceRaw.params);
const params: RebalanceFieldsDict = {};
params['lowerRangePrice'] = SqrtPriceMath.sqrtPriceX64ToPrice(
new BN(readBigUint128LE(paramsBuffer, 0).toString()),
tokenADecimals,
tokenBDecimals
);
params['upperRangePrice'] = SqrtPriceMath.sqrtPriceX64ToPrice(
new BN(readBigUint128LE(paramsBuffer, 16).toString()),
tokenADecimals,
tokenBDecimals
);
params['destinationToken'] = new Decimal(paramsBuffer.readUint8(32));
return params;
}
export function readTakeProfitRebalanceStateFromStrategy(rebalanceRaw: RebalanceRaw) {
const stateBuffer = Buffer.from(rebalanceRaw.state);
const state: RebalanceFieldsDict = {};
state['step'] = new Decimal(stateBuffer.readUInt8(0));
return state;
}
export function deserializeTakeProfitRebalanceFromOnchainParams(
tokenADecimals: number,
tokenBDecimals: number,
rebalanceRaw: RebalanceRaw
): RebalanceFieldInfo[] {
const params = readTakeProfitRebalanceParamsFromStrategy(tokenADecimals, tokenBDecimals, rebalanceRaw);
return getTakeProfitRebalanceFieldsInfos(
params['lowerRangePrice'],
params['upperRangePrice'],
params['destinationToken']
);
}