UNPKG

test-raydium-sdk-v2

Version:

An SDK for building applications on top of Raydium.

345 lines (310 loc) 11.7 kB
import { Connection, Keypair, PublicKey, EpochInfo } from "@solana/web3.js"; import BN from "bn.js"; import { merge } from "lodash"; import { Api, API_URL_CONFIG, ApiV3TokenRes, ApiV3Token, JupTokenType, AvailabilityCheckAPI3 } from "../api"; import { EMPTY_CONNECTION, EMPTY_OWNER } from "../common/error"; import { createLogger, Logger } from "../common/logger"; import { Owner } from "../common/owner"; import { PublicKeyish, WSOLMint, SOLMint } from "../common/pubKey"; import { TokenAmount } from "../module/amount"; import { Token } from "../module/token"; import { Cluster } from "../solana"; import Account, { TokenAccountDataProp } from "./account/account"; import Farm from "./farm/farm"; import Liquidity from "./liquidity/liquidity"; import { Clmm } from "./clmm"; import TradeV2 from "./tradeV2/trade"; import Utils1216 from "./utils1216"; import MarketV2 from "./marketV2"; import Ido from "./ido"; import TokenModule, { MintToTokenAmount } from "./token/token"; import { SignAllTransactions, TransferAmountFee } from "./type"; import { TokenInfo } from "./token"; export interface RaydiumLoadParams extends TokenAccountDataProp, Omit<RaydiumApiBatchRequestParams, "api"> { /* ================= solana ================= */ // solana web3 connection connection: Connection; // solana cluster/network/env cluster?: Cluster; // user public key owner?: PublicKey | Keypair; /* ================= api ================= */ // api request interval in ms, -1 means never request again, 0 means always use fresh data, default is 5 mins (5 * 60 * 1000) apiRequestInterval?: number; // api request timeout in ms, default is 10 secs (10 * 1000) apiRequestTimeout?: number; apiCacheTime?: number; signAllTransactions?: SignAllTransactions; urlConfigs?: API_URL_CONFIG; logRequests?: boolean; logCount?: number; jupTokenType?: JupTokenType; preloadTokenPrice?: boolean; disableFeatureCheck?: boolean; } export interface RaydiumApiBatchRequestParams { api: Api; defaultChainTimeOffset?: number; defaultChainTime?: number; } export type RaydiumConstructorParams = Required<RaydiumLoadParams> & RaydiumApiBatchRequestParams; interface DataBase<T> { fetched: number; data: T; extInfo?: Record<string, any>; } interface ApiData { tokens?: DataBase<ApiV3Token[]>; // v3 data tokenList?: DataBase<ApiV3TokenRes>; jupTokenList?: { [JupTokenType.ALL]?: DataBase<ApiV3Token[]>; [JupTokenType.Strict]?: DataBase<ApiV3Token[]>; }; } export class Raydium { public cluster: Cluster; public farm: Farm; public account: Account; public liquidity: Liquidity; public clmm: Clmm; public tradeV2: TradeV2; public utils1216: Utils1216; public marketV2: MarketV2; public ido: Ido; public token: TokenModule; public rawBalances: Map<string, string> = new Map(); public apiData: ApiData; public availability: Partial<AvailabilityCheckAPI3>; private _connection: Connection; private _owner: Owner | undefined; public api: Api; private _apiCacheTime: number; private _signAllTransactions?: SignAllTransactions; private logger: Logger; private _chainTime?: { fetched: number; value: { chainTime: number; offset: number; }; }; private _epochInfo?: { fetched: number; value: EpochInfo; }; constructor(config: RaydiumConstructorParams) { const { connection, cluster, owner, api, defaultChainTime, defaultChainTimeOffset, apiCacheTime } = config; this._connection = connection; this.cluster = cluster; this._owner = owner ? new Owner(owner) : undefined; this._signAllTransactions = config.signAllTransactions; this.api = api; this._apiCacheTime = apiCacheTime || 5 * 60 * 1000; this.logger = createLogger("Raydium"); this.farm = new Farm({ scope: this, moduleName: "Raydium_Farm" }); this.account = new Account({ scope: this, moduleName: "Raydium_Account", tokenAccounts: config.tokenAccounts, tokenAccountRawInfos: config.tokenAccountRawInfos, }); this.liquidity = new Liquidity({ scope: this, moduleName: "Raydium_LiquidityV2" }); this.token = new TokenModule({ scope: this, moduleName: "Raydium_tokenV2" }); this.tradeV2 = new TradeV2({ scope: this, moduleName: "Raydium_tradeV2" }); this.clmm = new Clmm({ scope: this, moduleName: "Raydium_clmm" }); this.utils1216 = new Utils1216({ scope: this, moduleName: "Raydium_utils1216" }); this.marketV2 = new MarketV2({ scope: this, moduleName: "Raydium_marketV2" }); this.ido = new Ido({ scope: this, moduleName: "Raydium_ido" }); this.availability = {}; const now = new Date().getTime(); this.apiData = {}; if (defaultChainTimeOffset) this._chainTime = { fetched: now, value: { chainTime: defaultChainTime || Date.now() - defaultChainTimeOffset, offset: defaultChainTimeOffset, }, }; } static async load(config: RaydiumLoadParams): Promise<Raydium> { const custom: Required<RaydiumLoadParams> = merge( // default { cluster: "mainnet", owner: null, apiRequestInterval: 5 * 60 * 1000, apiRequestTimeout: 10 * 1000, }, config, ); const { cluster, apiRequestTimeout, logCount, logRequests, urlConfigs } = custom; const api = new Api({ cluster, timeout: apiRequestTimeout, urlConfigs, logCount, logRequests }); const raydium = new Raydium({ ...custom, api, }); await raydium.fetchAvailabilityStatus(config.disableFeatureCheck); await raydium.token.load({ type: config.jupTokenType, fetchTokenPrice: config.preloadTokenPrice, }); return raydium; } get owner(): Owner | undefined { return this._owner; } get ownerPubKey(): PublicKey { if (!this._owner) throw new Error(EMPTY_OWNER); return this._owner.publicKey; } public setOwner(owner?: PublicKey | Keypair): Raydium { this._owner = owner ? new Owner(owner) : undefined; return this; } get connection(): Connection { if (!this._connection) throw new Error(EMPTY_CONNECTION); return this._connection; } public setConnection(connection: Connection): Raydium { this._connection = connection; return this; } get signAllTransactions(): SignAllTransactions | undefined { return this._signAllTransactions; } public setSignAllTransactions(signAllTransactions?: SignAllTransactions): Raydium { this._signAllTransactions = signAllTransactions; return this; } public checkOwner(): void { if (!this.owner) { this.logger.error(EMPTY_OWNER); throw new Error(EMPTY_OWNER); } } private isCacheInvalidate(time: number): boolean { return new Date().getTime() - time > this._apiCacheTime; } public async fetchChainTime(): Promise<void> { try { const data = await this.api.getChainTimeOffset(); this._chainTime = { fetched: Date.now(), value: { chainTime: Date.now() - data.offset * 1000, offset: data.offset * 1000, }, }; } catch { this._chainTime = undefined; } } public async fetchV3TokenList(forceUpdate?: boolean): Promise<ApiV3TokenRes> { if (this.apiData.tokenList && !this.isCacheInvalidate(this.apiData.tokenList.fetched) && !forceUpdate) return this.apiData.tokenList.data; const raydiumList = await this.api.getTokenList(); const dataObject = { fetched: Date.now(), data: raydiumList, }; this.apiData.tokenList = dataObject; return dataObject.data; } public async fetchJupTokenList(type: JupTokenType, forceUpdate?: boolean): Promise<ApiV3Token[]> { const prevFetched = this.apiData.jupTokenList?.[type]; if (prevFetched && !this.isCacheInvalidate(prevFetched.fetched) && !forceUpdate) return prevFetched.data; const jupList = await this.api.getJupTokenList(type); this.apiData.jupTokenList = { ...this.apiData.jupTokenList, [type]: { fetched: Date.now(), data: jupList, }, }; return this.apiData.jupTokenList[type]!.data; } get chainTimeData(): { offset: number; chainTime: number } | undefined { return this._chainTime?.value; } public async chainTimeOffset(): Promise<number> { if (this._chainTime && Date.now() - this._chainTime.fetched <= 1000 * 60 * 5) return this._chainTime.value.offset; await this.fetchChainTime(); return this._chainTime?.value.offset || 0; } public async currentBlockChainTime(): Promise<number> { if (this._chainTime && Date.now() - this._chainTime.fetched <= 1000 * 60 * 5) return this._chainTime.value.chainTime; await this.fetchChainTime(); return this._chainTime?.value.chainTime || Date.now(); } public async fetchEpochInfo(): Promise<EpochInfo> { if (this._epochInfo && Date.now() - this._epochInfo.fetched <= 1000 * 30) return this._epochInfo.value; this._epochInfo = { fetched: Date.now(), value: await this.connection.getEpochInfo(), }; return this._epochInfo.value; } public async getChainTokenInfo(mint: PublicKeyish): Promise<{ token: Token; tokenInfo: TokenInfo }> { return this.token.getChainTokenInfo(mint); } public async fetchAvailabilityStatus(skipCheck?: boolean): Promise<Partial<AvailabilityCheckAPI3>> { if (skipCheck) return {}; try { const data = await this.api.fetchAvailabilityStatus(); const isAllDisabled = data.all === false; this.availability = { all: data.all, swap: isAllDisabled ? false : data.swap, createConcentratedPosition: isAllDisabled ? false : data.createConcentratedPosition, addConcentratedPosition: isAllDisabled ? false : data.addConcentratedPosition, addStandardPosition: isAllDisabled ? false : data.addStandardPosition, removeConcentratedPosition: isAllDisabled ? false : data.removeConcentratedPosition, removeStandardPosition: isAllDisabled ? false : data.removeStandardPosition, addFarm: isAllDisabled ? false : data.addFarm, removeFarm: isAllDisabled ? false : data.removeFarm, }; return data; } catch { return {}; } } public mintToToken(mint: PublicKeyish): Token { return this.token.mintToToken(mint); } public mintToTokenAmount(params: MintToTokenAmount): TokenAmount { return this.token.mintToTokenAmount(params); } // export interface TransferAmountFee {amount: TokenAmount | CurrencyAmount, fee: TokenAmount | CurrencyAmount | undefined, expirationTime: number | undefined} public solToWsolTokenAmount(tokenAmount: TokenAmount): TokenAmount { if (!tokenAmount.token.mint.equals(SOLMint)) return tokenAmount; return this.token.mintToTokenAmount({ mint: WSOLMint, amount: tokenAmount.toExact(), }); } public solToWsolTransferAmountFee(tokenAmountFee: TransferAmountFee): TransferAmountFee { if (!tokenAmountFee.amount.token.mint.equals(SOLMint)) return tokenAmountFee; return { amount: this.token.mintToTokenAmount({ mint: WSOLMint, amount: tokenAmountFee.amount.toExact(), }), fee: tokenAmountFee.fee ? this.token.mintToTokenAmount({ mint: WSOLMint, amount: tokenAmountFee.fee.toExact(), }) : tokenAmountFee.fee, expirationTime: tokenAmountFee.expirationTime, }; } public decimalAmount(params: MintToTokenAmount): BN { return this.token.decimalAmount(params); } public uiAmount(params: MintToTokenAmount): string { return this.token.uiAmount(params); } }