@prism-hq/prism-ag
Version:
Prism Aggregator
199 lines (198 loc) • 10.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SolanaTickDataProvider = exports.getCykuraPools = void 0;
const web3_js_1 = require("@solana/web3.js");
const spl_token_1 = require("@solana/spl-token");
const common_1 = require("./common");
const types_1 = require("../../types/types");
const anchor_1 = require("@project-serum/anchor");
const nodewallet_1 = __importDefault(require("@project-serum/anchor/dist/cjs/nodewallet"));
const sdk_1 = require("@cykura/sdk");
const jsbi_1 = __importDefault(require("jsbi"));
const anchor = __importStar(require("@project-serum/anchor"));
const bn_js_1 = require("bn.js");
const pubkey_1 = require("@project-serum/anchor/dist/cjs/utils/pubkey");
function getCykuraPools(connection) {
return __awaiter(this, void 0, void 0, function* () {
let programAccounts = yield (0, common_1.getFilteredProgramAccounts)(connection, new web3_js_1.PublicKey(types_1.CYKURA_PROGRAM_ID), true, [{ dataSize: 138 }]);
let decodedAccounts = [];
let provider = new anchor.AnchorProvider(connection, new nodewallet_1.default(web3_js_1.Keypair.generate()), {
skipPreflight: true,
preflightCommitment: "recent",
commitment: "recent",
});
const cyclosCore = new anchor_1.Program(sdk_1.IDL, types_1.CYKURA_PROGRAM_ID, provider);
let waitLoad = [];
for (let i = 0; i < programAccounts.length; i++) {
let decoded = types_1.CYKURA_LAYOUT.decode(programAccounts[i].accountInfo.data);
let pool = Object.assign(Object.assign({}, decoded), { poolAccount: programAccounts[i].publicKey.toBase58(), provider: "cykura", cyclosCore: cyclosCore });
pool.vault0 = (0, pubkey_1.findProgramAddressSync)([new web3_js_1.PublicKey(pool.poolAccount).toBuffer(), new web3_js_1.PublicKey(types_1.TOKEN_PROGRAM_ID).toBuffer(), pool.mintA.toBuffer()], spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID)[0];
pool.vault1 = (0, pubkey_1.findProgramAddressSync)([new web3_js_1.PublicKey(pool.poolAccount).toBuffer(), new web3_js_1.PublicKey(types_1.TOKEN_PROGRAM_ID).toBuffer(), pool.mintB.toBuffer()], spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID)[0];
decodedAccounts.push(pool);
pool.tickProvider = new SolanaTickDataProvider(cyclosCore, {
token0: pool.mintA,
token1: pool.mintB,
fee: pool.fee,
});
waitLoad.push(pool.tickProvider.eagerLoadCache(pool.tick, sdk_1.TICK_SPACINGS[pool.fee]));
}
yield Promise.all(waitLoad);
let pools = {};
for (let i = 0; i < programAccounts.length; i++) {
let pool = decodedAccounts[i];
let coinMint = pool.mintA.toBase58();
let pcMint = pool.mintB.toBase58();
(pools[coinMint] || (pools[coinMint] = [])).push(Object.assign(Object.assign({}, pool), { other: pcMint }));
(pools[pcMint] || (pools[pcMint] = [])).push(Object.assign(Object.assign({}, pool), { other: coinMint }));
}
return pools;
});
}
exports.getCykuraPools = getCykuraPools;
class SolanaTickDataProvider {
// @ts-ignore
constructor(program, pool) {
this.program = program;
this.pool = pool;
this.bitmapCache = new Map();
this.tickCache = new Map();
}
/**
* Caches ticks and bitmap accounts near the current price
* @param tickCurrent The current pool tick
* @param tickSpacing The pool tick spacing
*/
eagerLoadCache(tickCurrent, tickSpacing) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// fetch 10 bitmaps on each side in a single fetch. Find active ticks and read them together
const compressed = jsbi_1.default.toNumber(jsbi_1.default.divide(jsbi_1.default.BigInt(tickCurrent), jsbi_1.default.BigInt(tickSpacing)));
const { wordPos } = (0, sdk_1.tickPosition)(compressed);
try {
const bitmapsToFetch = [];
const { wordPos: WORD_POS_MIN } = (0, sdk_1.tickPosition)(Math.floor(sdk_1.TickMath.MIN_TICK / tickSpacing));
const { wordPos: WORD_POS_MAX } = (0, sdk_1.tickPosition)(Math.floor(sdk_1.TickMath.MAX_TICK / tickSpacing));
const minWord = Math.max(wordPos - 10, WORD_POS_MIN);
const maxWord = Math.min(wordPos + 10, WORD_POS_MAX);
for (let i = minWord; i < maxWord; i++) {
bitmapsToFetch.push(this.getBitmapAddressSync(i));
}
const fetchedBitmaps = (yield this.program.account.tickBitmapState.fetchMultiple(bitmapsToFetch));
const tickAddresses = [];
for (let i = 0; i < maxWord - minWord; i++) {
const currentWordPos = i + minWord;
const wordArray = (_a = fetchedBitmaps[i]) === null || _a === void 0 ? void 0 : _a.word;
const word = wordArray ? (0, sdk_1.generateBitmapWord)(wordArray) : new bn_js_1.BN(0);
this.bitmapCache.set(currentWordPos, {
address: bitmapsToFetch[i],
word,
});
if (word && !word.eqn(0)) {
for (let j = 0; j < 256; j++) {
if (word.shrn(j).and(new bn_js_1.BN(1)).eqn(1)) {
const tick = ((currentWordPos << 8) + j) * tickSpacing;
const tickAddress = this.getTickAddressSync(tick);
tickAddresses.push(tickAddress);
}
}
}
}
const fetchedTicks = (yield this.program.account.tickState.fetchMultiple(tickAddresses));
for (const i in tickAddresses) {
const { tick, liquidityNet } = fetchedTicks[i];
this.tickCache.set(tick, {
address: tickAddresses[i],
liquidityNet: jsbi_1.default.BigInt(liquidityNet),
});
}
}
catch (error) {
}
});
}
getTickAddressSync(tick) {
return (0, pubkey_1.findProgramAddressSync)([sdk_1.TICK_SEED, this.pool.token0.toBuffer(), this.pool.token1.toBuffer(), (0, sdk_1.u32ToSeed)(this.pool.fee), (0, sdk_1.u32ToSeed)(tick)], this.program.programId)[0];
}
getBitmapAddressSync(wordPos) {
return (0, pubkey_1.findProgramAddressSync)([sdk_1.BITMAP_SEED, this.pool.token0.toBuffer(), this.pool.token1.toBuffer(), (0, sdk_1.u32ToSeed)(this.pool.fee), (0, sdk_1.u16ToSeed)(wordPos)], this.program.programId)[0];
}
getTick(tick) {
const savedTick = this.tickCache.get(tick);
if (!savedTick) {
throw new Error('Tick not cached');
}
return {
address: savedTick.address,
liquidityNet: savedTick.liquidityNet,
};
}
/**
* Fetches the cached bitmap for the word
* @param wordPos
*/
getBitmap(wordPos) {
const savedBitmap = this.bitmapCache.get(wordPos);
if (!savedBitmap) {
throw new Error('Bitmap not cached');
}
return savedBitmap;
}
/**
* Finds the next initialized tick in the given word. Fetched bitmaps are saved in a
* cache for quicker lookups in future.
* @param tick The current tick
* @param lte Whether to look for a tick less than or equal to the current one, or a tick greater than or equal to
* @param tickSpacing The tick spacing for the pool
* @returns
*/
nextInitializedTickWithinOneWord(tick, lte, tickSpacing) {
let compressed = jsbi_1.default.toNumber(jsbi_1.default.divide(jsbi_1.default.BigInt(tick), jsbi_1.default.BigInt(tickSpacing)));
if (tick < 0 && tick % tickSpacing !== 0) {
compressed -= 1;
}
if (!lte) {
compressed += 1;
}
const { wordPos, bitPos } = (0, sdk_1.tickPosition)(compressed);
const cachedBitmap = this.getBitmap(wordPos);
const { next: nextBit, initialized } = (0, sdk_1.nextInitializedBit)(cachedBitmap.word, bitPos, lte);
const nextTick = (0, sdk_1.buildTick)(wordPos, nextBit, tickSpacing);
return [nextTick, initialized, wordPos, bitPos, cachedBitmap.address];
}
}
exports.SolanaTickDataProvider = SolanaTickDataProvider;