|
@@ -0,0 +1,164 @@
|
|
|
+// sellWorker.ts
|
|
|
+import { parentPort, workerData } from 'worker_threads';
|
|
|
+import { PublicKey, TokenAmount, Connection, Commitment } from '@solana/web3.js';
|
|
|
+import { LIQUIDITY_STATE_LAYOUT_V4, Liquidity, SPL_ACCOUNT_LAYOUT, TOKEN_PROGRAM_ID, TokenAccount } from '@raydium-io/raydium-sdk';
|
|
|
+import { retrieveEnvVariable } from '../utils';
|
|
|
+import BN from 'bn.js';
|
|
|
+import pino from 'pino';
|
|
|
+
|
|
|
+const transport = pino.transport({
|
|
|
+ targets: [
|
|
|
+ {
|
|
|
+ level: 'trace',
|
|
|
+ target: 'pino-pretty',
|
|
|
+ options: {},
|
|
|
+ },
|
|
|
+ ],
|
|
|
+});
|
|
|
+
|
|
|
+export const logger = pino(
|
|
|
+ {
|
|
|
+ redact: ['poolKeys'],
|
|
|
+ serializers: {
|
|
|
+ error: pino.stdSerializers.err,
|
|
|
+ },
|
|
|
+ base: undefined,
|
|
|
+ },
|
|
|
+ transport,
|
|
|
+);
|
|
|
+
|
|
|
+async function getTokenAccounts(connection: Connection, owner: PublicKey) {
|
|
|
+ const tokenResp = await connection.getTokenAccountsByOwner(owner, {
|
|
|
+ programId: TOKEN_PROGRAM_ID
|
|
|
+ });
|
|
|
+
|
|
|
+ const accounts: TokenAccount[] = [];
|
|
|
+ for (const { pubkey, account } of tokenResp.value) {
|
|
|
+ accounts.push({
|
|
|
+ pubkey,
|
|
|
+ accountInfo: SPL_ACCOUNT_LAYOUT.decode(account.data),
|
|
|
+ programId: new PublicKey(account.owner.toBase58())
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return accounts;
|
|
|
+}
|
|
|
+
|
|
|
+const SOL_SDC_POOL_ID = "58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2";
|
|
|
+const OPENBOOK_PROGRAM_ID = new PublicKey(
|
|
|
+ "srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX"
|
|
|
+);
|
|
|
+
|
|
|
+async function parsePoolInfo() {
|
|
|
+ const network = 'mainnet-beta';
|
|
|
+ const RPC_ENDPOINT = retrieveEnvVariable('RPC_ENDPOINT', logger);
|
|
|
+ const RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable(
|
|
|
+ 'RPC_WEBSOCKET_ENDPOINT',
|
|
|
+ logger,
|
|
|
+ );
|
|
|
+
|
|
|
+ const connection = new Connection(RPC_ENDPOINT, {
|
|
|
+ wsEndpoint: RPC_WEBSOCKET_ENDPOINT,
|
|
|
+ });
|
|
|
+ const owner = new PublicKey("VnxDzsZ7chE88e9rB6UKztCt2HUwrkgCTx8WieWf5mM");
|
|
|
+
|
|
|
+ const tokenAccounts = await getTokenAccounts(connection, owner);
|
|
|
+
|
|
|
+ // example to get pool info
|
|
|
+ const info = await connection.getAccountInfo(new PublicKey(SOL_SDC_POOL_ID));
|
|
|
+ if (!info) {
|
|
|
+ throw new Error("Pool not found");
|
|
|
+ }
|
|
|
+
|
|
|
+ const poolState = LIQUIDITY_STATE_LAYOUT_V4.decode(info.data);
|
|
|
+
|
|
|
+ const baseDecimal = 10 ** poolState.baseDecimal.toNumber();
|
|
|
+ const quoteDecimal = 10 ** poolState.quoteDecimal.toNumber();
|
|
|
+
|
|
|
+ const baseTokenAmount = await connection.getTokenAccountBalance(
|
|
|
+ poolState.baseVault
|
|
|
+ )
|
|
|
+
|
|
|
+ const quoteTokenAmount = await connection.getTokenAccountBalance(
|
|
|
+ poolState.quoteVault
|
|
|
+ )
|
|
|
+
|
|
|
+ const basePnl = poolState.baseNeedTakePnl.toNumber() / baseDecimal;
|
|
|
+ const quotePnl = poolState.quoteNeedTakePnl.toNumber() / quoteDecimal;
|
|
|
+
|
|
|
+ const base = (baseTokenAmount.value?.uiAmount || 0) - basePnl;
|
|
|
+ const quote = (quoteTokenAmount.value?.uiAmount || 0) - quotePnl;
|
|
|
+
|
|
|
+ const denominator = new BN(10).pow(poolState.baseDecimal);
|
|
|
+
|
|
|
+ const addedLpAccount = tokenAccounts.find((a) => a.accountInfo.mint.equals(poolState.lpMint));
|
|
|
+
|
|
|
+ const message = `
|
|
|
+ SOL - USDC Pool Info:
|
|
|
+
|
|
|
+ Pool total base: ${base},
|
|
|
+ Pool total quote: ${quote},
|
|
|
+
|
|
|
+ Base vault balance: ${baseTokenAmount.value.uiAmount},
|
|
|
+ Quote vault balance: ${quoteTokenAmount.value.uiAmount},
|
|
|
+
|
|
|
+ Base token decimals: ${poolState.baseDecimal.toNumber()},
|
|
|
+ Quote token decimals: ${poolState.quoteDecimal.toNumber()},
|
|
|
+ Total LP: ${poolState.lpReserve.div(denominator).toString()},
|
|
|
+
|
|
|
+ Added LP amount: ${(addedLpAccount?.accountInfo.amount.toNumber() || 0) / baseDecimal},
|
|
|
+ `;
|
|
|
+
|
|
|
+ logger.info(message);
|
|
|
+
|
|
|
+ // send message to discord (embed)
|
|
|
+ // post to discord webhook
|
|
|
+ let embed = {
|
|
|
+ embeds: [
|
|
|
+ {
|
|
|
+ title: "SOL - USDC Pool Info",
|
|
|
+ description: `
|
|
|
+ Pool total base: **${base}**,
|
|
|
+ Pool total quote: **${quote}**,
|
|
|
+
|
|
|
+ Base vault balance: **${baseTokenAmount.value.uiAmount}**,
|
|
|
+ Quote vault balance: **${quoteTokenAmount.value.uiAmount}**,
|
|
|
+
|
|
|
+ Base token decimals:** ${poolState.baseDecimal.toNumber()}**,
|
|
|
+ Quote token decimals:** ${poolState.quoteDecimal.toNumber()}**,
|
|
|
+
|
|
|
+ Total LP: **${poolState.lpReserve.div(denominator).toString()}**,
|
|
|
+ Added LP amount: **${(addedLpAccount?.accountInfo.amount.toNumber() || 0) / baseDecimal}**
|
|
|
+
|
|
|
+ Happy trading! 🚀
|
|
|
+ `
|
|
|
+ }
|
|
|
+
|
|
|
+ ]
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ const DISCORD_WEBHOOK = retrieveEnvVariable('DISCORD_WEBHOOK', logger);
|
|
|
+ // use native fetch to post to discord
|
|
|
+ fetch(DISCORD_WEBHOOK, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ },
|
|
|
+ body: JSON.stringify(embed),
|
|
|
+ });
|
|
|
+
|
|
|
+ logger.info("Message sent to Discord");
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// Function to periodically check the pool
|
|
|
+async function checkPoolPeriodically(interval: number) {
|
|
|
+ while (true) {
|
|
|
+ await parsePoolInfo();
|
|
|
+ await new Promise(resolve => setTimeout(resolve, interval));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Check pool periodically with a specified interval
|
|
|
+checkPoolPeriodically(60000); // 1 minute
|