UNPKG

@renec-foundation/redex-sdk

Version:

Typescript SDK to interact with Orca's Whirlpool program.

329 lines (328 loc) 14 kB
"use strict"; 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.AccountFetcher = void 0; const common_sdk_1 = require("@orca-so/common-sdk"); const spl_token_1 = require("@solana/spl-token"); const tiny_invariant_1 = __importDefault(require("tiny-invariant")); const __1 = require("../.."); const parsing_1 = require("./parsing"); /** * Data access layer to access Whirlpool related accounts * Includes internal cache that can be refreshed by the client. * * @category Fetcher */ class AccountFetcher { constructor(connection, cache) { this._cache = {}; this.connection = connection; this._cache = cache !== null && cache !== void 0 ? cache : {}; } /*** Public Methods ***/ /** * Retrieve minimum balance for rent exemption of a Token Account; * * @param refresh force refresh of account rent exemption * @returns minimum balance for rent exemption */ getAccountRentExempt(refresh = false) { return __awaiter(this, void 0, void 0, function* () { // This value should be relatively static or at least not break according to spec // https://docs.solana.com/developing/programming-model/accounts#rent-exemption if (!this._accountRentExempt || refresh) { this._accountRentExempt = yield this.connection.getMinimumBalanceForRentExemption(spl_token_1.AccountLayout.span); } return this._accountRentExempt; }); } /** * Retrieve a cached whirlpool account. Fetch from rpc on cache miss. * * @param address whirlpool address * @param refresh force cache refresh * @returns whirlpool account */ getPool(address, refresh = false) { return __awaiter(this, void 0, void 0, function* () { return this.get(common_sdk_1.AddressUtil.toPubKey(address), parsing_1.ParsableWhirlpool, refresh); }); } /** * Retrieve a cached position account. Fetch from rpc on cache miss. * * @param address position address * @param refresh force cache refresh * @returns position account */ getPosition(address, refresh = false) { return __awaiter(this, void 0, void 0, function* () { return this.get(common_sdk_1.AddressUtil.toPubKey(address), parsing_1.ParsablePosition, refresh); }); } /** * Retrieve a cached tick array account. Fetch from rpc on cache miss. * * @param address tick array address * @param refresh force cache refresh * @returns tick array account */ getTickArray(address, refresh = false) { return __awaiter(this, void 0, void 0, function* () { return this.get(common_sdk_1.AddressUtil.toPubKey(address), parsing_1.ParsableTickArray, refresh); }); } /** * Retrieve a cached fee tier account. Fetch from rpc on cache miss. * * @param address fee tier address * @param refresh force cache refresh * @returns fee tier account */ getFeeTier(address, refresh = false) { return __awaiter(this, void 0, void 0, function* () { return this.get(common_sdk_1.AddressUtil.toPubKey(address), parsing_1.ParsableFeeTier, refresh); }); } /** * Retrieve a cached token info account. Fetch from rpc on cache miss. * * @param address token info address * @param refresh force cache refresh * @returns token info account */ getTokenInfo(address, refresh = false) { return __awaiter(this, void 0, void 0, function* () { return this.get(common_sdk_1.AddressUtil.toPubKey(address), parsing_1.ParsableTokenInfo, refresh); }); } /** * Retrieve a cached mint info account. Fetch from rpc on cache miss. * * @param address mint info address * @param refresh force cache refresh * @returns mint info account */ getMintInfo(address, refresh = false) { return __awaiter(this, void 0, void 0, function* () { return this.get(common_sdk_1.AddressUtil.toPubKey(address), parsing_1.ParsableMintInfo, refresh); }); } /** * Retrieve a cached whirlpool config account. Fetch from rpc on cache miss. * * @param address whirlpool config address * @param refresh force cache refresh * @returns whirlpool config account */ getConfig(address, refresh = false) { return __awaiter(this, void 0, void 0, function* () { return this.get(common_sdk_1.AddressUtil.toPubKey(address), parsing_1.ParsableWhirlpoolsConfig, refresh); }); } /** * Retrieve a list of cached whirlpool accounts. Fetch from rpc for cache misses. * * @param addresses whirlpool addresses * @param refresh force cache refresh * @returns whirlpool accounts */ listPools(addresses, refresh) { return __awaiter(this, void 0, void 0, function* () { return this.list(common_sdk_1.AddressUtil.toPubKeys(addresses), parsing_1.ParsableWhirlpool, refresh); }); } /** * Retrieve a list of cached whirlpool addresses and accounts filtered by the given params using * getProgramAccounts. * * @param params whirlpool filter params * @returns tuple of whirlpool addresses and accounts */ listPoolsWithParams({ programId, configId, }) { return __awaiter(this, void 0, void 0, function* () { const filters = [ { dataSize: __1.WHIRLPOOL_ACCOUNT_SIZE }, { memcmp: __1.WHIRLPOOL_CODER.memcmp(__1.AccountName.Whirlpool, common_sdk_1.AddressUtil.toPubKey(configId).toBuffer()), }, ]; const accounts = yield this.connection.getProgramAccounts(common_sdk_1.AddressUtil.toPubKey(programId), { filters, }); const parsedAccounts = []; accounts.forEach(({ pubkey, account }) => { const parsedAccount = parsing_1.ParsableWhirlpool.parse(account.data); (0, tiny_invariant_1.default)(!!parsedAccount, `could not parse whirlpool: ${pubkey.toBase58()}`); parsedAccounts.push([pubkey, parsedAccount]); this._cache[pubkey.toBase58()] = { entity: parsing_1.ParsableWhirlpool, value: parsedAccount }; }); return parsedAccounts; }); } /** * Retrieve a list of cached position accounts. Fetch from rpc for cache misses. * * @param addresses position addresses * @param refresh force cache refresh * @returns position accounts */ listPositions(addresses, refresh) { return __awaiter(this, void 0, void 0, function* () { return this.list(common_sdk_1.AddressUtil.toPubKeys(addresses), parsing_1.ParsablePosition, refresh); }); } /** * Retrieve a list of cached tick array accounts. Fetch from rpc for cache misses. * * @param addresses tick array addresses * @param refresh force cache refresh * @returns tick array accounts */ listTickArrays(addresses, refresh) { return __awaiter(this, void 0, void 0, function* () { return this.list(common_sdk_1.AddressUtil.toPubKeys(addresses), parsing_1.ParsableTickArray, refresh); }); } /** * Retrieve a list of cached token info accounts. Fetch from rpc for cache misses. * * @param addresses token info addresses * @param refresh force cache refresh * @returns token info accounts */ listTokenInfos(addresses, refresh) { return __awaiter(this, void 0, void 0, function* () { return this.list(common_sdk_1.AddressUtil.toPubKeys(addresses), parsing_1.ParsableTokenInfo, refresh); }); } /** * Retrieve a list of cached mint info accounts. Fetch from rpc for cache misses. * * @param addresses mint info addresses * @param refresh force cache refresh * @returns mint info accounts */ listMintInfos(addresses, refresh) { return __awaiter(this, void 0, void 0, function* () { return this.list(common_sdk_1.AddressUtil.toPubKeys(addresses), parsing_1.ParsableMintInfo, refresh); }); } /** * Update the cached value of all entities currently in the cache. * Uses batched rpc request for network efficient fetch. */ refreshAll() { return __awaiter(this, void 0, void 0, function* () { const addresses = Object.keys(this._cache); const data = yield this.bulkRequest(addresses); for (const [idx, [key, cachedContent]] of Object.entries(this._cache).entries()) { const entity = cachedContent.entity; const value = entity.parse(data[idx]); this._cache[key] = { entity, value }; } }); } /*** Private Methods ***/ /** * Retrieve from cache or fetch from rpc, an account */ get(address, entity, refresh) { var _a; return __awaiter(this, void 0, void 0, function* () { const key = address.toBase58(); const cachedValue = (_a = this._cache[key]) === null || _a === void 0 ? void 0 : _a.value; if (cachedValue !== undefined && !refresh) { return cachedValue; } const accountInfo = yield this.connection.getAccountInfo(address); const accountData = accountInfo === null || accountInfo === void 0 ? void 0 : accountInfo.data; const value = entity.parse(accountData); this._cache[key] = { entity, value }; return value; }); } /** * Retrieve from cache or fetch from rpc, a list of accounts */ list(addresses, entity, refresh) { return __awaiter(this, void 0, void 0, function* () { const keys = addresses.map((address) => address.toBase58()); const cachedValues = keys.map((key) => { var _a; return [ key, refresh ? undefined : (_a = this._cache[key]) === null || _a === void 0 ? void 0 : _a.value, ]; }); /* Look for accounts not found in cache */ const undefinedAccounts = []; cachedValues.forEach(([key, value], cacheIndex) => { if (value === undefined) { undefinedAccounts.push({ cacheIndex, key }); } }); /* Fetch accounts not found in cache */ if (undefinedAccounts.length > 0) { const data = yield this.bulkRequest(undefinedAccounts.map((account) => account.key)); undefinedAccounts.forEach(({ cacheIndex, key }, dataIndex) => { var _a; const value = entity.parse(data[dataIndex]); (0, tiny_invariant_1.default)(((_a = cachedValues[cacheIndex]) === null || _a === void 0 ? void 0 : _a[1]) === undefined, "unexpected non-undefined value"); cachedValues[cacheIndex] = [key, value]; this._cache[key] = { entity, value }; }); } const result = cachedValues .map(([_, value]) => value) .filter((value) => value !== undefined); (0, tiny_invariant_1.default)(result.length === addresses.length, "not enough results fetched"); return result; }); } /** * Make batch rpc request */ bulkRequest(addresses) { return __awaiter(this, void 0, void 0, function* () { const responses = []; const chunk = 100; // getMultipleAccounts has limitation of 100 accounts per request for (let i = 0; i < addresses.length; i += chunk) { const addressesSubset = addresses.slice(i, i + chunk); const res = this.connection._rpcRequest("getMultipleAccounts", [ addressesSubset, { commitment: this.connection.commitment }, ]); responses.push(res); } const combinedResult = []; (yield Promise.all(responses)).forEach((res) => { var _a; (0, tiny_invariant_1.default)(!res.error, `bulkRequest result error: ${res.error}`); (0, tiny_invariant_1.default)(!!((_a = res.result) === null || _a === void 0 ? void 0 : _a.value), "bulkRequest no value"); res.result.value.forEach((account) => { if (!account || account.data[1] !== "base64") { combinedResult.push(null); } else { combinedResult.push(Buffer.from(account.data[0], account.data[1])); } }); }); (0, tiny_invariant_1.default)(combinedResult.length === addresses.length, "bulkRequest not enough results"); return combinedResult; }); } } exports.AccountFetcher = AccountFetcher;