@kamino-finance/kliquidity-sdk
Version:
Typescript SDK for interacting with the Kamino Liquidity (kliquidity) protocol
192 lines (171 loc) • 6.54 kB
text/typescript
import Decimal from 'decimal.js';
import { PositionRange, RebalanceFieldInfo } 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 { upsertManyRebalanceFieldInfos } from './utils';
import { sqrtPriceToPrice as orcaSqrtPriceToPrice } from '@orca-so/whirlpools-core';
import { getPriceRangeFromPriceAndDiffBPS } from './math_utils';
import { getPriceFromQ64Price } from '../utils/meteora';
export const PricePercentageRebalanceTypeName = 'pricePercentage';
export function getPricePercentageRebalanceFieldInfos(
poolPrice: Decimal,
lowerPercentageBPS: Decimal,
upperPercentageBPS: Decimal,
enabled: boolean = true
): RebalanceFieldInfo[] {
const rebalanceType: RebalanceFieldInfo = {
label: RebalanceTypeLabelName,
type: 'string',
value: PricePercentageRebalanceTypeName,
enabled,
};
const lowerBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'lowerRangeBps',
type: 'number',
value: lowerPercentageBPS,
enabled,
};
const upperBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'upperRangeBps',
type: 'number',
value: upperPercentageBPS,
enabled,
};
const { lowerPrice, upperPrice } = getPositionRangeFromPercentageRebalanceParams(
poolPrice,
lowerPercentageBPS,
upperPercentageBPS
);
const lowerRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'rangePriceLower',
type: 'number',
value: lowerPrice!,
enabled: false,
};
const upperRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'rangePriceUpper',
type: 'number',
value: upperPrice!,
enabled: false,
};
return [
rebalanceType,
lowerBpsRebalanceFieldInfo,
upperBpsRebalanceFieldInfo,
lowerRangeRebalanceFieldInfo,
upperRangeRebalanceFieldInfo,
];
}
export function getPositionRangeFromPercentageRebalanceParams(
price: Decimal,
lowerPercentageBPS: Decimal,
upperPercentageBPS: Decimal
): PositionRange {
return getPriceRangeFromPriceAndDiffBPS(price, lowerPercentageBPS, upperPercentageBPS);
}
export function getDefaultPricePercentageRebalanceFieldInfos(price: Decimal): RebalanceFieldInfo[] {
const fieldInfos = getPricePercentageRebalanceFieldInfos(
price,
DefaultLowerPercentageBPSDecimal,
DefaultUpperPercentageBPSDecimal
);
return fieldInfos;
}
export function readPricePercentageRebalanceParamsFromStrategy(rebalanceRaw: RebalanceRaw): RebalanceFieldInfo[] {
const paramsBuffer = Buffer.from(rebalanceRaw.params);
const lowerBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'lowerRangeBps',
type: 'number',
value: new Decimal(paramsBuffer.readUint16LE(0)),
enabled: true,
};
const upperBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'upperRangeBps',
type: 'number',
value: new Decimal(paramsBuffer.readUint16LE(2)),
enabled: true,
};
return [lowerBpsRebalanceFieldInfo, upperBpsRebalanceFieldInfo];
}
export function readRawPricePercentageRebalanceStateFromStrategy(rebalanceRaw: RebalanceRaw): RebalanceFieldInfo[] {
const stateBuffer = Buffer.from(rebalanceRaw.state);
const lowerRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'rangePriceLower',
type: 'number',
value: new Decimal(readBigUint128LE(stateBuffer, 0).toString()),
enabled: false,
};
const upperRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'rangePriceUpper',
type: 'number',
value: new Decimal(readBigUint128LE(stateBuffer, 16).toString()),
enabled: false,
};
return [lowerRangeRebalanceFieldInfo, upperRangeRebalanceFieldInfo];
}
export function readPricePercentageRebalanceStateFromStrategy(
dex: Dex,
tokenADecimals: number,
tokenBDecimals: number,
rebalanceRaw: RebalanceRaw
): RebalanceFieldInfo[] {
const stateBuffer = Buffer.from(rebalanceRaw.state);
const lowerSqrtPriceX64 = readBigUint128LE(stateBuffer, 0).toString();
const upperSqrtPriceX64 = readBigUint128LE(stateBuffer, 16).toString();
let lowerPrice: Decimal, upperPrice: Decimal;
if (dex == 'ORCA') {
lowerPrice = new Decimal(
orcaSqrtPriceToPrice(BigInt(lowerSqrtPriceX64.toString()), tokenADecimals, tokenBDecimals)
);
upperPrice = new Decimal(
orcaSqrtPriceToPrice(BigInt(upperSqrtPriceX64.toString()), tokenADecimals, tokenBDecimals)
);
} else if (dex == 'RAYDIUM') {
lowerPrice = new Decimal(
orcaSqrtPriceToPrice(BigInt(lowerSqrtPriceX64.toString()), tokenADecimals, tokenBDecimals)
);
upperPrice = new Decimal(
orcaSqrtPriceToPrice(BigInt(upperSqrtPriceX64.toString()), tokenADecimals, tokenBDecimals)
);
} else if (dex == 'METEORA') {
lowerPrice = getPriceFromQ64Price(new Decimal(lowerSqrtPriceX64.toString()), tokenADecimals, tokenBDecimals);
upperPrice = getPriceFromQ64Price(new Decimal(upperSqrtPriceX64.toString()), tokenADecimals, tokenBDecimals);
} else {
throw new Error(`Unknown DEX ${dex}`);
}
const lowerRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'rangePriceLower',
type: 'number',
value: lowerPrice,
enabled: false,
};
const upperRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'rangePriceUpper',
type: 'number',
value: upperPrice,
enabled: false,
};
return [lowerRangeRebalanceFieldInfo, upperRangeRebalanceFieldInfo];
}
export function deserializePricePercentageRebalanceFromOnchainParams(
price: Decimal,
rebalanceRaw: RebalanceRaw
): RebalanceFieldInfo[] {
const params = readPricePercentageRebalanceParamsFromStrategy(rebalanceRaw);
const lowerRangeBPS = new Decimal(params.find((param) => param.label == 'lowerRangeBps')?.value!);
const upperRangeBPS = new Decimal(params.find((param) => param.label == 'upperRangeBps')?.value!);
return getPricePercentageRebalanceFieldInfos(price, lowerRangeBPS, upperRangeBPS);
}
export function deserializePricePercentageRebalanceWithStateOverride(
dex: Dex,
tokenADecimals: number,
tokenBDecimals: number,
price: Decimal,
rebalanceRaw: RebalanceRaw
) {
const stateFields = readPricePercentageRebalanceStateFromStrategy(dex, tokenADecimals, tokenBDecimals, rebalanceRaw);
const fields = deserializePricePercentageRebalanceFromOnchainParams(price, rebalanceRaw);
return upsertManyRebalanceFieldInfos(fields, stateFields);
}