UNPKG

@raydium-io/raydium-sdk-v2

Version:

An SDK for building applications on top of Raydium.

224 lines (188 loc) 8.77 kB
import BN from "bn.js"; import { MAX_TICK, MIN_TICK } from "./constants"; import { TICK_ARRAY_BITMAP_SIZE, TICK_ARRAY_SIZE, TickUtils } from "./tick"; import { TickQuery } from "./tickQuery"; import { isZero, leadingZeros, leastSignificantBit, mostSignificantBit, trailingZeros } from "./util"; import { TickArrayBitmapExtensionLayout } from "../layout"; export const EXTENSION_TICKARRAY_BITMAP_SIZE = 14; export class TickArrayBitmap { public static maxTickInTickarrayBitmap(tickSpacing: number): number { return tickSpacing * TICK_ARRAY_SIZE * TICK_ARRAY_BITMAP_SIZE; } public static getBitmapTickBoundary( tickarrayStartIndex: number, tickSpacing: number, ): { minValue: number; maxValue: number; } { const ticksInOneBitmap = this.maxTickInTickarrayBitmap(tickSpacing); let m = Math.floor(Math.abs(tickarrayStartIndex) / ticksInOneBitmap); if (tickarrayStartIndex < 0 && Math.abs(tickarrayStartIndex) % ticksInOneBitmap != 0) m += 1; const minValue = ticksInOneBitmap * m; return tickarrayStartIndex < 0 ? { minValue: -minValue, maxValue: -minValue + ticksInOneBitmap } : { minValue, maxValue: minValue + ticksInOneBitmap }; } public static nextInitializedTickArrayStartIndex( bitMap: BN, lastTickArrayStartIndex: number, tickSpacing: number, zeroForOne: boolean, ): { isInit: boolean; tickIndex: number } { if (!TickQuery.checkIsValidStartIndex(lastTickArrayStartIndex, tickSpacing)) throw Error("nextInitializedTickArrayStartIndex check error"); const tickBoundary = this.maxTickInTickarrayBitmap(tickSpacing); const nextTickArrayStartIndex = zeroForOne ? lastTickArrayStartIndex - TickQuery.tickCount(tickSpacing) : lastTickArrayStartIndex + TickQuery.tickCount(tickSpacing); if (nextTickArrayStartIndex < -tickBoundary || nextTickArrayStartIndex >= tickBoundary) { return { isInit: false, tickIndex: lastTickArrayStartIndex }; } const multiplier = tickSpacing * TICK_ARRAY_SIZE; let compressed = nextTickArrayStartIndex / multiplier + 512; if (nextTickArrayStartIndex < 0 && nextTickArrayStartIndex % multiplier != 0) { compressed--; } const bitPos = Math.abs(compressed); if (zeroForOne) { const offsetBitMap = bitMap.shln(1024 - bitPos - 1); const nextBit = mostSignificantBit(1024, offsetBitMap); if (nextBit !== null) { const nextArrayStartIndex = (bitPos - nextBit - 512) * multiplier; return { isInit: true, tickIndex: nextArrayStartIndex }; } else { return { isInit: false, tickIndex: -tickBoundary }; } } else { const offsetBitMap = bitMap.shrn(bitPos); const nextBit = leastSignificantBit(1024, offsetBitMap); if (nextBit !== null) { const nextArrayStartIndex = (bitPos + nextBit - 512) * multiplier; return { isInit: true, tickIndex: nextArrayStartIndex }; } else { return { isInit: false, tickIndex: tickBoundary - TickQuery.tickCount(tickSpacing) }; } } } } export class TickArrayBitmapExtensionUtils { public static getBitmapOffset(tickIndex: number, tickSpacing: number): number { if (!TickQuery.checkIsValidStartIndex(tickIndex, tickSpacing)) { throw new Error("No enough initialized tickArray"); } this.checkExtensionBoundary(tickIndex, tickSpacing); const ticksInOneBitmap = TickArrayBitmap.maxTickInTickarrayBitmap(tickSpacing); let offset = Math.floor(Math.abs(tickIndex) / ticksInOneBitmap) - 1; if (tickIndex < 0 && Math.abs(tickIndex) % ticksInOneBitmap === 0) offset--; return offset; } public static getBitmap( tickIndex: number, tickSpacing: number, tickArrayBitmapExtension: ReturnType<typeof TickArrayBitmapExtensionLayout.decode>, ): { offset: number; tickarrayBitmap: BN[] } { const offset = this.getBitmapOffset(tickIndex, tickSpacing); if (tickIndex < 0) { return { offset, tickarrayBitmap: tickArrayBitmapExtension.negativeTickArrayBitmap[offset] }; } else { return { offset, tickarrayBitmap: tickArrayBitmapExtension.positiveTickArrayBitmap[offset] }; } } public static checkExtensionBoundary(tickIndex: number, tickSpacing: number) { const { positiveTickBoundary, negativeTickBoundary } = this.extensionTickBoundary(tickSpacing); if (tickIndex >= negativeTickBoundary && tickIndex < positiveTickBoundary) { throw Error("checkExtensionBoundary -> InvalidTickArrayBoundary"); } } public static extensionTickBoundary(tickSpacing: number): { positiveTickBoundary: number; negativeTickBoundary: number; } { const positiveTickBoundary = TickArrayBitmap.maxTickInTickarrayBitmap(tickSpacing); const negativeTickBoundary = -positiveTickBoundary; if (MAX_TICK <= positiveTickBoundary) throw Error(`extensionTickBoundary check error: ${MAX_TICK}, ${positiveTickBoundary}`); if (negativeTickBoundary <= MIN_TICK) throw Error(`extensionTickBoundary check error: ${negativeTickBoundary}, ${MIN_TICK}`); return { positiveTickBoundary, negativeTickBoundary }; } public static checkTickArrayIsInit( tickArrayStartIndex: number, tickSpacing: number, tickArrayBitmapExtension: ReturnType<typeof TickArrayBitmapExtensionLayout.decode>, ): { isInitialized: boolean; startIndex: number } { const { tickarrayBitmap } = this.getBitmap(tickArrayStartIndex, tickSpacing, tickArrayBitmapExtension); const tickArrayOffsetInBitmap = this.tickArrayOffsetInBitmap(tickArrayStartIndex, tickSpacing); return { isInitialized: TickUtils.mergeTickArrayBitmap(tickarrayBitmap).testn(tickArrayOffsetInBitmap), startIndex: tickArrayStartIndex, }; } public static nextInitializedTickArrayFromOneBitmap( lastTickArrayStartIndex: number, tickSpacing: number, zeroForOne: boolean, tickArrayBitmapExtension: ReturnType<typeof TickArrayBitmapExtensionLayout.decode>, ): { isInit: boolean; tickIndex: number; } { const multiplier = TickQuery.tickCount(tickSpacing); const nextTickArrayStartIndex = zeroForOne ? lastTickArrayStartIndex - multiplier : lastTickArrayStartIndex + multiplier; const { tickarrayBitmap } = this.getBitmap(nextTickArrayStartIndex, tickSpacing, tickArrayBitmapExtension); return this.nextInitializedTickArrayInBitmap(tickarrayBitmap, nextTickArrayStartIndex, tickSpacing, zeroForOne); } public static nextInitializedTickArrayInBitmap( tickarrayBitmap: BN[], nextTickArrayStartIndex: number, tickSpacing: number, zeroForOne: boolean, ): { isInit: boolean; tickIndex: number; } { const { minValue: bitmapMinTickBoundary, maxValue: bitmapMaxTickBoundary } = TickArrayBitmap.getBitmapTickBoundary( nextTickArrayStartIndex, tickSpacing, ); const tickArrayOffsetInBitmap = this.tickArrayOffsetInBitmap(nextTickArrayStartIndex, tickSpacing); if (zeroForOne) { // tick from upper to lower // find from highter bits to lower bits const offsetBitMap = TickUtils.mergeTickArrayBitmap(tickarrayBitmap).shln( TICK_ARRAY_BITMAP_SIZE - 1 - tickArrayOffsetInBitmap, ); const nextBit = isZero(512, offsetBitMap) ? null : leadingZeros(512, offsetBitMap); if (nextBit !== null) { const nextArrayStartIndex = nextTickArrayStartIndex - nextBit * TickQuery.tickCount(tickSpacing); return { isInit: true, tickIndex: nextArrayStartIndex }; } else { // not found til to the end return { isInit: false, tickIndex: bitmapMinTickBoundary }; } } else { // tick from lower to upper // find from lower bits to highter bits const offsetBitMap = TickUtils.mergeTickArrayBitmap(tickarrayBitmap).shrn(tickArrayOffsetInBitmap); const nextBit = isZero(512, offsetBitMap) ? null : trailingZeros(512, offsetBitMap); if (nextBit !== null) { const nextArrayStartIndex = nextTickArrayStartIndex + nextBit * TickQuery.tickCount(tickSpacing); return { isInit: true, tickIndex: nextArrayStartIndex }; } else { // not found til to the end return { isInit: false, tickIndex: bitmapMaxTickBoundary - TickQuery.tickCount(tickSpacing) }; } } } public static tickArrayOffsetInBitmap(tickArrayStartIndex: number, tickSpacing: number): number { const m = Math.abs(tickArrayStartIndex) % TickArrayBitmap.maxTickInTickarrayBitmap(tickSpacing); let tickArrayOffsetInBitmap = Math.floor(m / TickQuery.tickCount(tickSpacing)); if (tickArrayStartIndex < 0 && m != 0) { tickArrayOffsetInBitmap = TICK_ARRAY_BITMAP_SIZE - tickArrayOffsetInBitmap; } return tickArrayOffsetInBitmap; } }