UNPKG

@orca-so/whirlpool-sdk

Version:

Whirlpool SDK for the Orca protocol.

298 lines (297 loc) 13.2 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 tiny_invariant_1 = __importDefault(require("tiny-invariant")); const spl_token_1 = require("@solana/spl-token"); const parse_1 = require("./parse"); const address_1 = require("../utils/address"); /** * Data access layer for accounts used by OrcaWhirlpool and OrcaPosition. * The types of accounts that are being used are defined by CachedAccount. * Includes internal cache that can be refreshed by the client. */ class AccountFetcher { constructor(connection) { this._cache = {}; this._userTokens = []; this.connection = connection; } /*** Public Methods ***/ /** * 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) { return __awaiter(this, void 0, void 0, function* () { return this.get((0, address_1.toPubKey)(address), parse_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) { return __awaiter(this, void 0, void 0, function* () { return this.get((0, address_1.toPubKey)(address), parse_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) { return __awaiter(this, void 0, void 0, function* () { return this.get((0, address_1.toPubKey)(address), parse_1.ParsableTickArray, 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) { return __awaiter(this, void 0, void 0, function* () { return this.get((0, address_1.toPubKey)(address), parse_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) { return __awaiter(this, void 0, void 0, function* () { return this.get((0, address_1.toPubKey)(address), parse_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) { return __awaiter(this, void 0, void 0, function* () { return this.get((0, address_1.toPubKey)(address), parse_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((0, address_1.toPubKeys)(addresses), parse_1.ParsableWhirlpool, refresh); }); } /** * 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((0, address_1.toPubKeys)(addresses), parse_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((0, address_1.toPubKeys)(addresses), parse_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((0, address_1.toPubKeys)(addresses), parse_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((0, address_1.toPubKeys)(addresses), parse_1.ParsableMintInfo, refresh); }); } /** * Retrieve a list of tokens owned by the user. * * @param walletAddress user wallet address * @param refresh foree cache refresh * @returns user tokens */ listUserTokens(walletAddress, refresh) { return __awaiter(this, void 0, void 0, function* () { if (!this._userTokens || refresh) { const filter = { programId: spl_token_1.TOKEN_PROGRAM_ID }; const { value } = yield this.connection.getParsedTokenAccountsByOwner((0, address_1.toPubKey)(walletAddress), filter); const userTokens = value.map((accountInfo) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p; return ({ address: accountInfo.pubkey, amount: (_e = (_d = (_c = (_b = (_a = accountInfo.account) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.parsed) === null || _c === void 0 ? void 0 : _c.info) === null || _d === void 0 ? void 0 : _d.tokenAmount) === null || _e === void 0 ? void 0 : _e.amount, decimals: (_k = (_j = (_h = (_g = (_f = accountInfo.account) === null || _f === void 0 ? void 0 : _f.data) === null || _g === void 0 ? void 0 : _g.parsed) === null || _h === void 0 ? void 0 : _h.info) === null || _j === void 0 ? void 0 : _j.tokenAmount) === null || _k === void 0 ? void 0 : _k.decimals, mint: (_p = (_o = (_m = (_l = accountInfo.account) === null || _l === void 0 ? void 0 : _l.data) === null || _m === void 0 ? void 0 : _m.parsed) === null || _o === void 0 ? void 0 : _o.info) === null || _p === void 0 ? void 0 : _p.mint, }); }); this._userTokens = userTokens; return userTokens; } return this._userTokens; }); } /** * 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;