@raydium-io/raydium-sdk-v2
Version:
An SDK for building applications on top of Raydium.
336 lines (302 loc) • 10.8 kB
text/typescript
import { Connection, Keypair, PublicKey, EpochInfo, Commitment } from "@solana/web3.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 { Cluster } from "../solana";
import Account, { TokenAccountDataProp } from "./account/account";
import Farm from "./farm/farm";
import Liquidity from "./liquidity/liquidity";
import { Clmm } from "./clmm";
import Cpmm from "./cpmm/cpmm";
import TradeV2 from "./tradeV2/trade";
import Utils1216 from "./utils1216";
import MarketV2 from "./marketV2";
import Ido from "./ido";
import Launchpad from "./launchpad/launchpad";
import TokenModule from "./token/token";
import { SignAllTransactions } from "./type";
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;
disableFeatureCheck?: boolean;
disableLoadToken?: boolean;
blockhashCommitment?: Commitment;
loopMultiTxStatus?: 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?: DataBase<ApiV3Token[]>;
}
export class Raydium {
public cluster: Cluster;
public farm: Farm;
public account: Account;
public liquidity: Liquidity;
public clmm: Clmm;
public cpmm: Cpmm;
public tradeV2: TradeV2;
public utils1216: Utils1216;
public marketV2: MarketV2;
public ido: Ido;
public token: TokenModule;
public launchpad: Launchpad;
public rawBalances: Map<string, string> = new Map();
public apiData: ApiData;
public availability: Partial<AvailabilityCheckAPI3>;
public blockhashCommitment: Commitment;
public loopMultiTxStatus?: boolean;
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,
blockhashCommitment = "confirmed",
loopMultiTxStatus,
} = config;
this._connection = connection;
this.cluster = cluster || "mainnet";
this._owner = owner ? new Owner(owner) : undefined;
this._signAllTransactions = config.signAllTransactions;
this.blockhashCommitment = blockhashCommitment;
this.loopMultiTxStatus = loopMultiTxStatus;
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.cpmm = new Cpmm({ scope: this, moduleName: "Raydium_cpmm" });
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.launchpad = new Launchpad({ scope: this, moduleName: "Raydium_lauchpad" });
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 ?? true);
if (!config.disableLoadToken)
await raydium.token.load({
type: config.jupTokenType,
});
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;
this.account.resetTokenAccounts();
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) {
console.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;
try {
const raydiumList = await this.api.getTokenList();
const dataObject = {
fetched: Date.now(),
data: raydiumList,
};
this.apiData.tokenList = dataObject;
return dataObject.data;
} catch (e) {
console.error(e);
return {
mintList: [],
blacklist: [],
whiteList: [],
};
}
}
public async fetchJupTokenList(forceUpdate?: boolean): Promise<ApiV3Token[]> {
if (this.cluster === "devnet") return [];
const prevFetched = this.apiData.jupTokenList;
if (prevFetched && !this.isCacheInvalidate(prevFetched.fetched) && !forceUpdate) return prevFetched.data;
try {
const jupList = await this.api.getJupTokenList();
this.apiData.jupTokenList = {
fetched: Date.now(),
data: jupList.map((t) => ({
...t,
mintAuthority: t.mint_authority || undefined,
freezeAuthority: t.freeze_authority || undefined,
})),
};
return this.apiData.jupTokenList.data;
} catch (e) {
console.error(e);
return [];
}
}
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 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 {};
}
}
}