UNPKG

@symmetry-hq/baskets-v2-sdk

Version:

Symmetry Baskets V2 SDK

326 lines (325 loc) 16.7 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.getBasketsProgram = getBasketsProgram; exports.getBasketPda = getBasketPda; exports.getRebalanceStateAccount = getRebalanceStateAccount; exports.getStateCreatorAccount = getStateCreatorAccount; exports.getDepositFeesWallet = getDepositFeesWallet; exports.getWithdrawFeesWallet = getWithdrawFeesWallet; exports.getWithdrawStateAccount = getWithdrawStateAccount; exports.getBasketTokenMintAccount = getBasketTokenMintAccount; exports.getMetadataAccount = getMetadataAccount; exports.getLookupTableAccount = getLookupTableAccount; exports.getDeactivatedLookupTableAccount = getDeactivatedLookupTableAccount; exports.getAta = getAta; exports.getRandomSeed = getRandomSeed; exports.getAccountInfos = getAccountInfos; exports.getRaydiumV4Pools = getRaydiumV4Pools; exports.getRaydiumCpmmPools = getRaydiumCpmmPools; exports.getPythSponsoredFeeds = getPythSponsoredFeeds; exports.getAllBaskets = getAllBaskets; exports.getBasketsByCreator = getBasketsByCreator; exports.getWithdrawStatesByUser = getWithdrawStatesByUser; // Core dependencies const anchor_1 = require("@coral-xyz/anchor"); const nodewallet_1 = __importDefault(require("@coral-xyz/anchor/dist/cjs/nodewallet")); const spl_token_1 = require("@solana/spl-token"); const web3_js_1 = require("@solana/web3.js"); const js_1 = require("@metaplex-foundation/js"); // Local imports const constants_1 = require("./constants"); const idl_1 = require("../idl/idl"); const uuid_1 = require("uuid"); const oracle_1 = require("../state/oracle"); const basket_1 = require("../state/basket"); const withdrawState_1 = require("../state/withdrawState"); function getBasketsProgram(connection) { const program = new anchor_1.Program(idl_1.IDL, new anchor_1.AnchorProvider(connection, new nodewallet_1.default(web3_js_1.Keypair.generate()))); return program; } function getBasketPda(basket) { return web3_js_1.PublicKey.findProgramAddressSync([Uint8Array.from(basket.toBuffer())], constants_1.BASKETS_PROGRAM_ID)[0]; } function getRebalanceStateAccount() { return web3_js_1.PublicKey.findProgramAddressSync([constants_1.REBALANCE_STATE_SEED], constants_1.BASKETS_PROGRAM_ID)[0]; } function getStateCreatorAccount() { return web3_js_1.PublicKey.findProgramAddressSync([constants_1.STATE_CREATOR_SEED], constants_1.BASKETS_PROGRAM_ID)[0]; } function getDepositFeesWallet(basket) { return web3_js_1.PublicKey.findProgramAddressSync([constants_1.DEPOSIT_FEE_SEED, basket.toBuffer()], constants_1.BASKETS_PROGRAM_ID)[0]; } function getWithdrawFeesWallet(basket) { return web3_js_1.PublicKey.findProgramAddressSync([constants_1.WITHDRAW_FEE_SEED, basket.toBuffer()], constants_1.BASKETS_PROGRAM_ID)[0]; } function getWithdrawStateAccount(withdrawStateSeed) { return web3_js_1.PublicKey.findProgramAddressSync([Uint8Array.from(withdrawStateSeed)], constants_1.BASKETS_PROGRAM_ID)[0]; } function getBasketTokenMintAccount(basket) { return web3_js_1.PublicKey.findProgramAddressSync([ constants_1.MINT_SEED, basket.toBuffer() ], constants_1.BASKETS_PROGRAM_ID)[0]; } function getMetadataAccount(tokenMint) { const metaplex = js_1.Metaplex.make(new web3_js_1.Connection("https://api.devnet.solana.com")); return metaplex.nfts().pdas().metadata({ mint: tokenMint }); } function getLookupTableAccount(creator, slot) { const ixAndPubkey = web3_js_1.AddressLookupTableProgram.createLookupTable({ authority: creator, recentSlot: slot, payer: creator, }); return ixAndPubkey[1]; } function getDeactivatedLookupTableAccount(lookupTable) { return web3_js_1.PublicKey.findProgramAddressSync([lookupTable.toBuffer()], constants_1.BASKETS_PROGRAM_ID)[0]; } function getAta(wallet, tokenMint) { return (0, spl_token_1.getAssociatedTokenAddressSync)(tokenMint, wallet, true); } function getRandomSeed() { return Array.from((0, uuid_1.parse)((0, uuid_1.v4)())); } function getAccountInfos(connection, keys) { return __awaiter(this, void 0, void 0, function* () { const allAccounts = []; const batchSize = 100; for (let i = 0; i < keys.length; i += batchSize) { const batch = keys.slice(i, i + batchSize); const batchAccounts = yield connection.getMultipleAccountsInfo(batch); allAccounts.push(...batchAccounts); } return allAccounts; }); } function getRaydiumPools(connection, tokenMint, programId, dataSize, baseTokenOffset, quoteTokenOffset, baseMintOffset, quoteMintOffset) { return __awaiter(this, void 0, void 0, function* () { // Get all pools that pair tokenMint with either SOL or USDC const [solQuotePairs, solBasePairs, usdcQuotePairs, usdcBasePairs, memeQuotePairs, memeBasePairs] = yield Promise.all([ // Get pools where tokenMint is quote token and SOL is base connection.getProgramAccounts(programId, { filters: [ { dataSize }, { memcmp: { offset: baseMintOffset, bytes: constants_1.WSOL_MINT.toBase58() } }, { memcmp: { offset: quoteMintOffset, bytes: tokenMint.toBase58() } }, ] }), // Get pools where tokenMint is base token and SOL is quote connection.getProgramAccounts(programId, { filters: [ { dataSize }, { memcmp: { offset: quoteMintOffset, bytes: constants_1.WSOL_MINT.toBase58() } }, { memcmp: { offset: baseMintOffset, bytes: tokenMint.toBase58() } }, ] }), // Get pools where tokenMint is quote token and USDC is base connection.getProgramAccounts(programId, { filters: [ { dataSize }, { memcmp: { offset: baseMintOffset, bytes: constants_1.USDC_MINT.toBase58() } }, { memcmp: { offset: quoteMintOffset, bytes: tokenMint.toBase58() } }, ] }), // Get pools where tokenMint is base token and USDC is quote connection.getProgramAccounts(programId, { filters: [ { dataSize }, { memcmp: { offset: quoteMintOffset, bytes: constants_1.USDC_MINT.toBase58() } }, { memcmp: { offset: baseMintOffset, bytes: tokenMint.toBase58() } }, ] }), // Get pools where tokenMint is quote token and MEME SOL is base connection.getProgramAccounts(programId, { filters: [ { dataSize }, { memcmp: { offset: baseMintOffset, bytes: constants_1.MEME_SOL.toBase58() } }, { memcmp: { offset: quoteMintOffset, bytes: tokenMint.toBase58() } }, ] }), // Get pools where tokenMint is base token and MEME SOL is quote connection.getProgramAccounts(programId, { filters: [ { dataSize }, { memcmp: { offset: quoteMintOffset, bytes: constants_1.MEME_SOL.toBase58() } }, { memcmp: { offset: baseMintOffset, bytes: tokenMint.toBase58() } }, ] }), ]); // Combine all pool accounts const allPoolAccounts = [ ...solQuotePairs, ...solBasePairs, ...usdcQuotePairs, ...usdcBasePairs, ...memeQuotePairs, ...memeBasePairs, ]; // Process pool accounts const pools = allPoolAccounts .filter(account => { // For V4 pools, only include active pools (status === 6) if (programId === constants_1.RAYDIUM_LIQUIDITY_POOL_V4) { const status = parseInt(account.account.data.readBigUInt64LE(0).toString()); return status === 6; } return true; }) .map(account => { // Extract pool data const poolData = { liquidity: 0, poolType: 0, pool: account.pubkey.toBase58(), baseMint: new web3_js_1.PublicKey(account.account.data.slice(baseMintOffset, baseMintOffset + 32)).toBase58(), quoteMint: new web3_js_1.PublicKey(account.account.data.slice(quoteMintOffset, quoteMintOffset + 32)).toBase58(), baseTokenAccount: new web3_js_1.PublicKey(account.account.data.slice(baseTokenOffset, baseTokenOffset + 32)).toBase58(), quoteTokenAccount: new web3_js_1.PublicKey(account.account.data.slice(quoteTokenOffset, quoteTokenOffset + 32)).toBase58(), baseBalance: 0, quoteBalance: 0, baseDecimals: 0, quoteDecimals: 0, }; // Ensure tokenMint is always base token if (poolData.baseMint !== tokenMint.toBase58()) { [poolData.quoteMint, poolData.baseMint] = [poolData.baseMint, poolData.quoteMint]; [poolData.quoteTokenAccount, poolData.baseTokenAccount] = [poolData.baseTokenAccount, poolData.quoteTokenAccount]; } return poolData; }); // Collect token accounts for balance lookup const tokenAccountsToLookup = [ ...pools.map(pool => new web3_js_1.PublicKey(pool.quoteTokenAccount)), ...pools.map(pool => new web3_js_1.PublicKey(pool.baseTokenAccount)), ...pools.map(pool => new web3_js_1.PublicKey(pool.quoteMint)), ...pools.map(pool => new web3_js_1.PublicKey(pool.baseMint)) ]; // Get account info for all token and mint accounts const accountInfos = yield getAccountInfos(connection, tokenAccountsToLookup); // Update pool balances and decimals pools.forEach((pool, i) => { var _a, _b, _c, _d, _e, _f, _g, _h; pool.quoteBalance = parseInt((_b = (_a = accountInfos[i]) === null || _a === void 0 ? void 0 : _a.data.readBigUInt64LE(64).toString()) !== null && _b !== void 0 ? _b : "0"); pool.baseBalance = parseInt((_d = (_c = accountInfos[pools.length + i]) === null || _c === void 0 ? void 0 : _c.data.readBigUInt64LE(64).toString()) !== null && _d !== void 0 ? _d : "0"); pool.quoteDecimals = (_f = (_e = accountInfos[2 * pools.length + i]) === null || _e === void 0 ? void 0 : _e.data[44]) !== null && _f !== void 0 ? _f : 0; pool.baseDecimals = (_h = (_g = accountInfos[3 * pools.length + i]) === null || _g === void 0 ? void 0 : _g.data[44]) !== null && _h !== void 0 ? _h : 0; }); return pools; }); } function getRaydiumV4Pools(connection, tokenMint, usdcPrice, solPrice) { return __awaiter(this, void 0, void 0, function* () { const pools = yield getRaydiumPools(connection, tokenMint, constants_1.RAYDIUM_LIQUIDITY_POOL_V4, 752, 336, 368, 400, 432); pools.forEach(pool => { pool.poolType = 2; const price = (pool.quoteMint == constants_1.WSOL_MINT.toBase58() || pool.quoteMint == constants_1.MEME_SOL.toBase58()) ? solPrice : usdcPrice; const decimals = (pool.quoteMint == constants_1.WSOL_MINT.toBase58() || pool.quoteMint == constants_1.MEME_SOL.toBase58()) ? constants_1.WSOL_DECIMALS : constants_1.USDC_DECIMALS; pool.liquidity = price * pool.quoteBalance / 10 ** decimals * 2; }); pools.sort((a, b) => b.liquidity - a.liquidity); return pools.slice(0, 2); }); } function getRaydiumCpmmPools(connection, tokenMint, usdcPrice, solPrice) { return __awaiter(this, void 0, void 0, function* () { const pools = yield getRaydiumPools(connection, tokenMint, constants_1.RAYDIUM_CPMM, 637, 72, 104, 168, 200); pools.forEach(pool => { pool.poolType = 1; const price = (pool.quoteMint == constants_1.WSOL_MINT.toBase58() || pool.quoteMint == constants_1.MEME_SOL.toBase58()) ? solPrice : usdcPrice; const decimals = (pool.quoteMint == constants_1.WSOL_MINT.toBase58() || pool.quoteMint == constants_1.MEME_SOL.toBase58()) ? constants_1.WSOL_DECIMALS : constants_1.USDC_DECIMALS; pool.liquidity = price * pool.quoteBalance / 10 ** decimals * 2; }); pools.sort((a, b) => b.liquidity - a.liquidity); return pools.slice(0, 2); return pools; }); } function getPythSponsoredFeeds(program, tokenMint) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; const pythSponsoredFeeds = yield (0, oracle_1.fetchPythSponsoredFeeds)(program, constants_1.PYTH_SPONSORED_FEEDS); const pools = []; for (let i = 0; i < pythSponsoredFeeds.numTokens; i++) if (pythSponsoredFeeds.mints[i].toBase58() == tokenMint.toBase58()) { pools.push({ liquidity: 999999999, poolType: 0, pool: pythSponsoredFeeds.ownAddress.toBase58(), baseMint: pythSponsoredFeeds.mints[i].toBase58(), quoteMint: constants_1.USDC_MINT.toBase58(), baseTokenAccount: pythSponsoredFeeds.feeds[i].toBase58(), quoteTokenAccount: web3_js_1.PublicKey.default.toBase58(), baseBalance: 0, quoteBalance: 0, baseDecimals: (_b = (_a = (yield getAccountInfos(program.provider.connection, [tokenMint]))[0]) === null || _a === void 0 ? void 0 : _a.data[44]) !== null && _b !== void 0 ? _b : 0, quoteDecimals: 6, }); } return pools; }); } function getAllBaskets(program) { return __awaiter(this, void 0, void 0, function* () { const accounts = (yield program.account.basketV200.all()).map(account => account.account); return accounts.map(account => (0, basket_1.parseBasketState)(account)); }); } function getBasketsByCreator(program, creator) { return __awaiter(this, void 0, void 0, function* () { const accountFilters = [ { dataSize: basket_1.BASKETS_STATE_SIZE + 8, }, { memcmp: { offset: 8 + 1 + 32 + 1 + 32 + 32 + 8 + 8 + 8 + 8, bytes: creator.toBase58(), }, } ]; const accounts = yield program.provider.connection .getProgramAccounts(constants_1.BASKETS_PROGRAM_ID, { commitment: "confirmed", filters: accountFilters, encoding: 'base64' }); const baskets = accounts.map(account => program.coder.accounts.decode("basketV200", account.account.data)); return yield Promise.all(baskets.map(basket => (0, basket_1.parseBasketState)(basket))); }); } function getWithdrawStatesByUser(program, user) { return __awaiter(this, void 0, void 0, function* () { const accountFilters = [ { dataSize: withdrawState_1.WITHDRAW_STATE_SIZE + 8, }, { memcmp: { offset: 8 + 32 + 16 + 32, bytes: user.toBase58(), }, } ]; const accounts = yield program.provider.connection .getProgramAccounts(constants_1.BASKETS_PROGRAM_ID, { commitment: "confirmed", filters: accountFilters, encoding: 'base64' }); const withdrawStates = accounts.map(account => program.coder.accounts.decode("withdrawStateV200", account.account.data)); return withdrawStates.map(withdrawState => (0, withdrawState_1.parseWithdrawState)(withdrawState)); }); }