UNPKG

test-raydium-sdk-v2

Version:

An SDK for building applications on top of Raydium.

1 lines 16.3 kB
{"version":3,"sources":["../../../src/common/txTool/txUtils.ts","../../../src/common/logger.ts","../../../src/raydium/farm/pda.ts"],"sourcesContent":["import {\n Connection,\n PublicKey,\n ComputeBudgetProgram,\n SimulatedTransactionResponse,\n Transaction,\n TransactionInstruction,\n TransactionMessage,\n Keypair,\n EpochInfo,\n VersionedTransaction,\n} from \"@solana/web3.js\";\nimport { TOKEN_PROGRAM_ID } from \"@solana/spl-token\";\n\nimport { createLogger } from \"../logger\";\nimport { InstructionType } from \"./txType\";\nimport { CacheLTA } from \"./lookupTable\";\n\nimport { ComputeBudgetConfig } from \"@/raydium/type\";\n\nconst logger = createLogger(\"Raydium_txUtil\");\n\nexport const MAX_BASE64_SIZE = 1644;\n\nexport function addComputeBudget(config: ComputeBudgetConfig): {\n instructions: TransactionInstruction[];\n instructionTypes: string[];\n} {\n const ins: TransactionInstruction[] = [];\n const insTypes: string[] = [];\n if (config.microLamports) {\n ins.push(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: config.microLamports }));\n insTypes.push(InstructionType.SetComputeUnitPrice);\n }\n if (config.units) {\n ins.push(ComputeBudgetProgram.setComputeUnitLimit({ units: config.units }));\n insTypes.push(InstructionType.SetComputeUnitLimit);\n }\n\n return {\n instructions: ins,\n instructionTypes: insTypes,\n };\n}\n\nexport async function getRecentBlockHash(connection: Connection): Promise<string> {\n try {\n return (await connection.getLatestBlockhash?.())?.blockhash || (await connection.getRecentBlockhash()).blockhash;\n } catch {\n return (await connection.getRecentBlockhash()).blockhash;\n }\n}\n\n/**\n * Forecast transaction size\n */\nexport function forecastTransactionSize(instructions: TransactionInstruction[], signers: PublicKey[]): boolean {\n if (instructions.length < 1) logger.logWithError(`no instructions provided: ${instructions.toString()}`);\n if (signers.length < 1) logger.logWithError(`no signers provided:, ${signers.toString()}`);\n\n const transaction = new Transaction();\n transaction.recentBlockhash = \"11111111111111111111111111111111\";\n transaction.feePayer = signers[0];\n transaction.add(...instructions);\n\n try {\n return Buffer.from(transaction.serialize({ verifySignatures: false })).toString(\"base64\").length < MAX_BASE64_SIZE;\n } catch (error) {\n return false;\n }\n}\n\n/**\n * Simulates multiple instruction\n */\n/**\n * Simulates multiple instruction\n */\nexport async function simulateMultipleInstruction(\n connection: Connection,\n instructions: TransactionInstruction[],\n keyword: string,\n batchRequest = true,\n): Promise<string[]> {\n const feePayer = new PublicKey(\"RaydiumSimuLateTransaction11111111111111111\");\n\n const transactions: Transaction[] = [];\n\n let transaction = new Transaction();\n transaction.feePayer = feePayer;\n\n for (const instruction of instructions) {\n if (!forecastTransactionSize([...transaction.instructions, instruction], [feePayer])) {\n transactions.push(transaction);\n transaction = new Transaction();\n transaction.feePayer = feePayer;\n }\n transaction.add(instruction);\n }\n if (transaction.instructions.length > 0) {\n transactions.push(transaction);\n }\n\n let results: SimulatedTransactionResponse[] = [];\n\n try {\n results = await simulateTransaction(connection, transactions, batchRequest);\n if (results.find((i) => i.err !== null)) throw Error(\"rpc simulateTransaction error\");\n } catch (error) {\n if (error instanceof Error) {\n logger.logWithError(\"failed to simulate for instructions\", \"RPC_ERROR\", {\n message: error.message,\n });\n }\n }\n\n const logs: string[] = [];\n for (const result of results) {\n logger.debug(\"simulate result:\", result);\n\n if (result.logs) {\n const filteredLog = result.logs.filter((log) => log && log.includes(keyword));\n logger.debug(\"filteredLog:\", logs);\n if (!filteredLog.length) logger.logWithError(\"simulate log not match keyword\", \"keyword\", keyword);\n logs.push(...filteredLog);\n }\n }\n\n return logs;\n}\n\nexport function parseSimulateLogToJson(log: string, keyword: string): any {\n const results = log.match(/{[\"\\w:,]+}/g);\n if (!results || results.length !== 1) {\n return logger.logWithError(`simulate log fail to match json, keyword: ${keyword}`);\n }\n\n return results[0];\n}\n\nexport function parseSimulateValue(log: string, key: string): any {\n const reg = new RegExp(`\"${key}\":(\\\\d+)`, \"g\");\n\n const results = reg.exec(log);\n if (!results || results.length !== 2) {\n return logger.logWithError(`simulate log fail to match key\", key: ${key}`);\n }\n\n return results[1];\n}\n\nexport interface ProgramAddress {\n publicKey: PublicKey;\n nonce: number;\n}\nexport function findProgramAddress(\n seeds: Array<Buffer | Uint8Array>,\n programId: PublicKey,\n): {\n publicKey: PublicKey;\n nonce: number;\n} {\n const [publicKey, nonce] = PublicKey.findProgramAddressSync(seeds, programId);\n return { publicKey, nonce };\n}\n\nexport async function simulateTransaction(\n connection: Connection,\n transactions: Transaction[],\n batchRequest?: boolean,\n): Promise<any[]> {\n let results: any[] = [];\n if (batchRequest) {\n const getLatestBlockhash = await connection.getLatestBlockhash();\n\n const encodedTransactions: string[] = [];\n for (const transaction of transactions) {\n transaction.recentBlockhash = getLatestBlockhash.blockhash;\n transaction.lastValidBlockHeight = getLatestBlockhash.lastValidBlockHeight;\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n const message = transaction._compile();\n const signData = message.serialize();\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n const wireTransaction = transaction._serialize(signData);\n const encodedTransaction = wireTransaction.toString(\"base64\");\n\n encodedTransactions.push(encodedTransaction);\n }\n\n const batch = encodedTransactions.map((keys) => {\n const args = connection._buildArgs([keys], undefined, \"base64\");\n return {\n methodName: \"simulateTransaction\",\n args,\n };\n });\n\n const reqData: { methodName: string; args: any[] }[][] = [];\n const itemReqIndex = 20;\n for (let i = 0; i < Math.ceil(batch.length / itemReqIndex); i++) {\n reqData.push(batch.slice(i * itemReqIndex, (i + 1) * itemReqIndex));\n }\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n results = await (\n await Promise.all(\n reqData.map(async (i) => (await (connection as any)._rpcBatchRequest(i)).map((ii) => ii.result.value)),\n )\n ).flat();\n } else {\n try {\n results = await Promise.all(\n transactions.map(async (transaction) => await (await connection.simulateTransaction(transaction)).value),\n );\n } catch (error) {\n if (error instanceof Error) {\n logger.logWithError(\"failed to get info for multiple accounts\", \"RPC_ERROR\", {\n message: error.message,\n });\n }\n }\n }\n\n return results;\n}\n\nexport function checkLegacyTxSize({\n instructions,\n payer,\n signers,\n}: {\n instructions: TransactionInstruction[];\n payer: PublicKey;\n signers: PublicKey[];\n}): boolean {\n return forecastTransactionSize(instructions, [payer, ...signers]);\n}\n\nexport function checkV0TxSize({\n instructions,\n payer,\n lookupTableAddressAccount,\n recentBlockhash = Keypair.generate().publicKey.toString(),\n}: {\n instructions: TransactionInstruction[];\n payer: PublicKey;\n lookupTableAddressAccount?: CacheLTA;\n recentBlockhash?: string;\n}): boolean {\n const transactionMessage = new TransactionMessage({\n payerKey: payer,\n recentBlockhash,\n instructions,\n });\n\n const messageV0 = transactionMessage.compileToV0Message(Object.values(lookupTableAddressAccount ?? {}));\n try {\n const buildLength = Buffer.from(new VersionedTransaction(messageV0).serialize()).toString(\"base64\").length;\n return buildLength < MAX_BASE64_SIZE;\n } catch (error) {\n return false;\n }\n}\n\nlet epochInfoCache: { time: number; data?: EpochInfo } = {\n time: 0,\n data: undefined,\n};\n\nexport async function getEpochInfo(connection: Connection): Promise<EpochInfo> {\n if (!epochInfoCache.data || (Date.now() - epochInfoCache.time) / 1000 > 30) {\n const data = await connection.getEpochInfo();\n epochInfoCache = {\n time: Date.now(),\n data,\n };\n return data;\n } else {\n return epochInfoCache.data;\n }\n}\n\nexport const toBuffer = (arr: Buffer | Uint8Array | Array<number>): Buffer => {\n if (Buffer.isBuffer(arr)) {\n return arr;\n } else if (arr instanceof Uint8Array) {\n return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength);\n } else {\n return Buffer.from(arr);\n }\n};\n\nexport function printSimulate(transactions: Transaction[] | VersionedTransaction[]): string[] {\n const allBase64: string[] = [];\n transactions.forEach((transaction) => {\n if (transaction instanceof Transaction) {\n if (!transaction.recentBlockhash) transaction.recentBlockhash = TOKEN_PROGRAM_ID.toBase58();\n if (!transaction.feePayer) transaction.feePayer = Keypair.generate().publicKey;\n }\n let serialized = transaction.serialize({ requireAllSignatures: false, verifySignatures: false });\n if (transaction instanceof VersionedTransaction) serialized = toBuffer(serialized);\n const base64 = serialized.toString(\"base64\");\n allBase64.push(base64);\n });\n console.log(\"simulate tx string:\", allBase64);\n\n return allBase64;\n}\n","import { get, set } from \"lodash\";\nimport dayjs from \"dayjs\";\nimport utc from \"dayjs/plugin/utc\";\ndayjs.extend(utc);\n\nexport type ModuleName = \"Common.Api\";\n\nexport enum LogLevel {\n Error,\n Warning,\n Info,\n Debug,\n}\nexport class Logger {\n private logLevel: LogLevel;\n private name: string;\n constructor(params: { name: string; logLevel?: LogLevel }) {\n this.logLevel = params.logLevel !== undefined ? params.logLevel : LogLevel.Error;\n this.name = params.name;\n }\n\n set level(logLevel: LogLevel) {\n this.logLevel = logLevel;\n }\n get time(): string {\n return dayjs().utc().format(\"YYYY/MM/DD HH:mm:ss UTC\");\n }\n get moduleName(): string {\n return this.name;\n }\n\n private isLogLevel(level: LogLevel): boolean {\n return level <= this.logLevel;\n }\n\n public error(...props): Logger {\n if (!this.isLogLevel(LogLevel.Error)) return this;\n console.error(this.time, this.name, \"sdk logger error\", ...props);\n return this;\n }\n\n public logWithError(...props): Logger {\n // this.error(...props)\n const msg = props.map((arg) => (typeof arg === \"object\" ? JSON.stringify(arg) : arg)).join(\", \");\n throw new Error(msg);\n }\n\n public warning(...props): Logger {\n if (!this.isLogLevel(LogLevel.Warning)) return this;\n console.warn(this.time, this.name, \"sdk logger warning\", ...props);\n return this;\n }\n\n public info(...props): Logger {\n if (!this.isLogLevel(LogLevel.Info)) return this;\n console.info(this.time, this.name, \"sdk logger info\", ...props);\n return this;\n }\n\n public debug(...props): Logger {\n if (!this.isLogLevel(LogLevel.Debug)) return this;\n console.debug(this.time, this.name, \"sdk logger debug\", ...props);\n return this;\n }\n}\n\nconst moduleLoggers: { [key in ModuleName]?: Logger } = {};\nconst moduleLevels: { [key in ModuleName]?: LogLevel } = {};\n\nexport function createLogger(moduleName: string): Logger {\n let logger = get(moduleLoggers, moduleName);\n if (!logger) {\n // default level is error\n const logLevel = get(moduleLevels, moduleName);\n\n logger = new Logger({ name: moduleName, logLevel });\n set(moduleLoggers, moduleName, logger);\n }\n\n return logger;\n}\n\nexport function setLoggerLevel(moduleName: string, level: LogLevel): void {\n set(moduleLevels, moduleName, level);\n\n const logger = get(moduleLoggers, moduleName);\n if (logger) logger.level = level;\n}\n","import { PublicKey } from \"@solana/web3.js\";\n\nimport { findProgramAddress } from \"@/common/txTool/txUtils\";\n\nexport function getRegistrarAddress(\n programId: PublicKey,\n realm: PublicKey,\n communityTokenMint: PublicKey,\n): {\n publicKey: PublicKey;\n nonce: number;\n} {\n return findProgramAddress(\n [realm.toBuffer(), Buffer.from(\"registrar\", \"utf8\"), communityTokenMint.toBuffer()],\n programId,\n );\n}\n\nexport function getVotingTokenMint(\n programId: PublicKey,\n poolId: PublicKey,\n): {\n publicKey: PublicKey;\n nonce: number;\n} {\n return findProgramAddress([poolId.toBuffer(), Buffer.from(\"voting_mint_seed\", \"utf8\")], programId);\n}\n\nexport function getVotingMintAuthority(\n programId: PublicKey,\n poolId: PublicKey,\n): {\n publicKey: PublicKey;\n nonce: number;\n} {\n return findProgramAddress([poolId.toBuffer()], programId);\n}\n\nexport function getVoterAddress(\n programId: PublicKey,\n registrar: PublicKey,\n authority: PublicKey,\n): {\n publicKey: PublicKey;\n nonce: number;\n} {\n return findProgramAddress([registrar.toBuffer(), Buffer.from(\"voter\", \"utf8\"), authority.toBuffer()], programId);\n}\n\nexport function getVoterWeightRecordAddress(\n programId: PublicKey,\n registrar: PublicKey,\n authority: PublicKey,\n): {\n publicKey: PublicKey;\n nonce: number;\n} {\n return findProgramAddress(\n [registrar.toBuffer(), Buffer.from(\"voter-weight-record\", \"utf8\"), authority.toBuffer()],\n programId,\n );\n}\n\nexport function getTokenOwnerRecordAddress(\n programId: PublicKey,\n realm: PublicKey,\n governingTokenMint: PublicKey,\n governingTokenOwner: PublicKey,\n): {\n publicKey: PublicKey;\n nonce: number;\n} {\n return findProgramAddress(\n [\n Buffer.from(\"governance\", \"utf8\"),\n realm.toBuffer(),\n governingTokenMint.toBuffer(),\n governingTokenOwner.toBuffer(),\n ],\n programId,\n );\n}\n"],"mappings":"AAAA,sJAYA,qDCZA,sCACA,qBACA,gCACA,EAAM,OAAO,CAAG,EAUT,WAAa,CAGlB,YAAY,EAA+C,CACzD,KAAK,SAAW,EAAO,WAAa,OAAY,EAAO,SAAW,EAClE,KAAK,KAAO,EAAO,IACrB,IAEI,OAAM,EAAoB,CAC5B,KAAK,SAAW,CAClB,IACI,OAAe,CACjB,MAAO,GAAM,EAAE,IAAI,EAAE,OAAO,yBAAyB,CACvD,IACI,aAAqB,CACvB,MAAO,MAAK,IACd,CAEQ,WAAW,EAA0B,CAC3C,MAAO,IAAS,KAAK,QACvB,CAEO,SAAS,EAAe,CAC7B,MAAK,MAAK,WAAW,CAAc,EACnC,SAAQ,MAAM,KAAK,KAAM,KAAK,KAAM,mBAAoB,GAAG,CAAK,EACzD,MAFsC,IAG/C,CAEO,gBAAgB,EAAe,CAEpC,GAAM,GAAM,EAAM,IAAI,AAAC,GAAS,MAAO,IAAQ,SAAW,KAAK,UAAU,CAAG,EAAI,CAAI,EAAE,KAAK,IAAI,EAC/F,KAAM,IAAI,OAAM,CAAG,CACrB,CAEO,WAAW,EAAe,CAC/B,MAAK,MAAK,WAAW,CAAgB,EACrC,SAAQ,KAAK,KAAK,KAAM,KAAK,KAAM,qBAAsB,GAAG,CAAK,EAC1D,MAFwC,IAGjD,CAEO,QAAQ,EAAe,CAC5B,MAAK,MAAK,WAAW,CAAa,EAClC,SAAQ,KAAK,KAAK,KAAM,KAAK,KAAM,kBAAmB,GAAG,CAAK,EACvD,MAFqC,IAG9C,CAEO,SAAS,EAAe,CAC7B,MAAK,MAAK,WAAW,CAAc,EACnC,SAAQ,MAAM,KAAK,KAAM,KAAK,KAAM,mBAAoB,GAAG,CAAK,EACzD,MAFsC,IAG/C,CACF,EAEM,EAAkD,CAAC,EACnD,EAAmD,CAAC,EAEnD,WAAsB,EAA4B,CACvD,GAAI,GAAS,EAAI,EAAe,CAAU,EAC1C,GAAI,CAAC,EAAQ,CAEX,GAAM,GAAW,EAAI,EAAc,CAAU,EAE7C,EAAS,GAAI,GAAO,CAAE,KAAM,EAAY,UAAS,CAAC,EAClD,EAAI,EAAe,EAAY,CAAM,CACvC,CAEA,MAAO,EACT,CD5DA,GAAM,GAAS,EAAa,gBAAgB,EAuIrC,WACL,EACA,EAIA,CACA,GAAM,CAAC,EAAW,GAAS,EAAU,uBAAuB,EAAO,CAAS,EAC5E,MAAO,CAAE,YAAW,OAAM,CAC5B,CEhKO,WACL,EACA,EACA,EAIA,CACA,MAAO,GACL,CAAC,EAAM,SAAS,EAAG,OAAO,KAAK,YAAa,MAAM,EAAG,EAAmB,SAAS,CAAC,EAClF,CACF,CACF,CAEO,WACL,EACA,EAIA,CACA,MAAO,GAAmB,CAAC,EAAO,SAAS,EAAG,OAAO,KAAK,mBAAoB,MAAM,CAAC,EAAG,CAAS,CACnG,CAEO,WACL,EACA,EAIA,CACA,MAAO,GAAmB,CAAC,EAAO,SAAS,CAAC,EAAG,CAAS,CAC1D,CAEO,WACL,EACA,EACA,EAIA,CACA,MAAO,GAAmB,CAAC,EAAU,SAAS,EAAG,OAAO,KAAK,QAAS,MAAM,EAAG,EAAU,SAAS,CAAC,EAAG,CAAS,CACjH,CAEO,WACL,EACA,EACA,EAIA,CACA,MAAO,GACL,CAAC,EAAU,SAAS,EAAG,OAAO,KAAK,sBAAuB,MAAM,EAAG,EAAU,SAAS,CAAC,EACvF,CACF,CACF,CAEO,WACL,EACA,EACA,EACA,EAIA,CACA,MAAO,GACL,CACE,OAAO,KAAK,aAAc,MAAM,EAChC,EAAM,SAAS,EACf,EAAmB,SAAS,EAC5B,EAAoB,SAAS,CAC/B,EACA,CACF,CACF","names":[]}