@symmetry-hq/baskets-v2-sdk
Version:
Symmetry Baskets V2 SDK
326 lines (325 loc) • 16.7 kB
JavaScript
"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));
});
}