@kamino-finance/kliquidity-sdk
Version:
Typescript SDK for interacting with the Kamino Liquidity (kliquidity) protocol
115 lines (100 loc) • 3.8 kB
text/typescript
import { Address, getAddressEncoder, getProgramDerivedAddress } from '@solana/kit';
import { encodeUtf8 } from './bytes';
import Decimal from 'decimal.js';
import { U64_MAX } from '../constants/numericalValues';
import { BinArray } from '../@codegen/meteora/accounts';
import { Bin } from '../@codegen/meteora/types';
import { ProgramDerivedAddress } from '@solana/addresses/dist/types/program-derived-address';
const BASIS_POINT_MAX = 10000;
const MAX_BIN_ARRAY_SIZE = 70;
const addressEncoder = getAddressEncoder();
export function getPriceOfBinByBinId(binId: number, tickSpacing: number): Decimal {
const binStepNum = new Decimal(tickSpacing).div(new Decimal(BASIS_POINT_MAX));
return new Decimal(1).add(new Decimal(binStepNum)).pow(new Decimal(binId));
}
export function getBinIdFromPrice(price: Decimal, tickSpacing: number, min: boolean): number {
const binStepNum = new Decimal(tickSpacing).div(new Decimal(BASIS_POINT_MAX));
const binId = price.log().dividedBy(new Decimal(1).add(binStepNum).log());
return (min ? binId.floor() : binId.ceil()).toNumber();
}
export function getPriceOfBinByBinIdWithDecimals(
binId: number,
tickSpacing: number,
decimalsA: number,
decimalsB: number
): Decimal {
return getPriceOfBinByBinId(binId, tickSpacing)
.mul(new Decimal(10).pow(decimalsA))
.div(new Decimal(10).pow(decimalsB));
}
export function getBinIdFromPriceWithDecimals(
price: Decimal,
tickSpacing: number,
min: boolean,
decimalsA: number,
decimalsB: number
): number {
const scaledPrice = price.mul(new Decimal(10).pow(decimalsB)).div(new Decimal(10).pow(decimalsA));
return getBinIdFromPrice(scaledPrice, tickSpacing, min);
}
export function getPriceFromQ64Price(price: Decimal, decimalsA: number, decimalsB: number): Decimal {
const scaledPrice = price.mul(new Decimal(10).pow(decimalsA)).div(new Decimal(10).pow(decimalsB));
return scaledPrice.div(new Decimal(U64_MAX));
}
export function getBinArrayLowerUpperBinId(binArrayIndex: number): [number, number] {
const lowerBinId = binArrayIndex * MAX_BIN_ARRAY_SIZE;
const upperBinId = lowerBinId + MAX_BIN_ARRAY_SIZE - 1;
return [lowerBinId, upperBinId];
}
export function getBinFromBinArray(binIndex: number, binArray: BinArray): Bin | null {
const [lowerBinId] = getBinArrayLowerUpperBinId(Number(binArray.index));
const offset = binIndex - lowerBinId;
if (offset >= 0 && offset < binArray.bins.length) {
return binArray.bins[offset];
}
return null;
}
export function getBinFromBinArrays(binIndex: number, binArrays: BinArray[]): Bin | null {
for (let i = 0; i < binArrays.length; i++) {
const bin = getBinFromBinArray(binIndex, binArrays[i]);
if (bin) {
return bin;
}
}
return null;
}
export function binIdToBinArrayIndex(binId: bigint): bigint {
const idx = binId / BigInt(MAX_BIN_ARRAY_SIZE);
const mod = binId % BigInt(MAX_BIN_ARRAY_SIZE);
return binId < 0n && mod !== 0n ? idx - 1n : idx;
}
export async function deriveBinArray(
lbPair: Address,
index: bigint,
programId: Address
): Promise<ProgramDerivedAddress> {
let binArrayBytes: Uint8Array;
if (index < 0n) {
const mask = (1n << 64n) - 1n;
const encoded = (~-index + 1n) & mask;
const buf = new Uint8Array(8);
const dv = new DataView(buf.buffer);
dv.setBigUint64(0, encoded, true);
binArrayBytes = buf;
} else {
const buf = new Uint8Array(8);
const dv = new DataView(buf.buffer);
dv.setBigUint64(0, index, true);
binArrayBytes = buf;
}
const pda = await getProgramDerivedAddress({
seeds: [encodeUtf8('bin_array'), addressEncoder.encode(lbPair), binArrayBytes],
programAddress: programId,
});
return pda;
}
export type MeteoraPosition = {
Address: Address;
amountX: Decimal;
amountY: Decimal;
};