Skip to content

Instantly share code, notes, and snippets.

@rubpy
Created August 1, 2024 23:16
Show Gist options
  • Save rubpy/e408574bccd8c0ce641599e5f19ec9d5 to your computer and use it in GitHub Desktop.
Save rubpy/e408574bccd8c0ce641599e5f19ec9d5 to your computer and use it in GitHub Desktop.
import web3 from "@solana/web3.js";
import mmw from "@raydium-io/raydium-sdk";
import QuickLRU from "quick-lru";
//////////////////////////////////////////////////
export const OPAQUE_SIGNATURE = "1111111111111111111111111111111111111111111111111111111111111111";
export const SIGNATURE_PROGRAM = "Program ";
export const SIGNATURE_PROGRAM_FAILED = " failed: ";
export const SIGNATURE_PROGRAM_LOG = "log: ";
export function findLogEntry(needle: string | ((log: string) => boolean), logEntries: string[]): string | null {
if (typeof needle !== "string" && typeof needle !== "function") return null;
if (!logEntries || !logEntries.length) return null;
const len = logEntries.length;
for (let i = 0; i < len; ++i) {
const log = logEntries[i];
if (!log) continue;
if (typeof needle === "string") {
if (!log.includes(needle as string)) continue;
} else if (!(needle as ((log: string) => boolean))(log)) {
continue;
}
return log;
}
return null;
};
//////////////////////////////////////////////////
export const RAYDIUM_AMM_V4_PROGRAM_ID = new web3.PublicKey("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8");
export const RAYDIUM_AMM_V4_LOG_PREFIX = "ray_log: ";
export const RAYDIUM_AMM_V4_LOG_PREFIX_OFFSET = SIGNATURE_PROGRAM.length + SIGNATURE_PROGRAM_LOG.length;
export const RAYDIUM_AMM_V4_LOG_OFFSET = RAYDIUM_AMM_V4_LOG_PREFIX_OFFSET + RAYDIUM_AMM_V4_LOG_PREFIX.length;
export const RAYDIUM_AMM_V4_LOG_MIN_LENGTH = RAYDIUM_AMM_V4_LOG_OFFSET + 8;
export const RAYDIUM_AMM_V4_LOG_RAW_MIN_LENGTH = 57;
export const RAYDIUM_AMM_V4_LOG_SWAP_BASE_IN = 3;
export const RaydiumAmmV4LogSwapBaseInLayout = mmw.struct([
mmw.u8("log_type"),
mmw.u64("amount_in"),
mmw.u64("minimum_out"),
mmw.u64("direction"),
mmw.u64("user_source"),
mmw.u64("pool_coin"),
mmw.u64("pool_pc"),
mmw.u64("out_amount"),
]);
export type RaydiumAmmV4LogSwapBaseIn = ReturnType<typeof RaydiumAmmV4LogSwapBaseInLayout.decode>;
export const RAYDIUM_AMM_V4_LOG_SWAP_BASE_OUT = 4;
export const RaydiumAmmV4LogSwapBaseOutLayout = mmw.struct([
mmw.u8("log_type"),
mmw.u64("max_in"),
mmw.u64("amount_out"),
mmw.u64("direction"),
mmw.u64("user_source"),
mmw.u64("pool_coin"),
mmw.u64("pool_pc"),
mmw.u64("deduct_in"),
]);
export type RaydiumAmmV4LogSwapBaseOut = ReturnType<typeof RaydiumAmmV4LogSwapBaseOutLayout.decode>;
export type RaydiumAmmV4Log = RaydiumAmmV4LogSwapBaseIn | RaydiumAmmV4LogSwapBaseOut;
export function deserializeRayLog(buf: Buffer): RaydiumAmmV4Log | null {
if (!buf || buf.byteLength < RAYDIUM_AMM_V4_LOG_RAW_MIN_LENGTH) {
return null;
}
const rayLogType = buf.readUint8(0);
switch (rayLogType) {
case RAYDIUM_AMM_V4_LOG_SWAP_BASE_IN:
return RaydiumAmmV4LogSwapBaseInLayout.decode(buf);
case RAYDIUM_AMM_V4_LOG_SWAP_BASE_OUT:
return RaydiumAmmV4LogSwapBaseOutLayout.decode(buf);
}
return null;
}
//////////////////////////////////////////////////
(async (rpcUrl: string) => {
const conn = new web3.Connection(rpcUrl, "processed");
const seenSignatures = new QuickLRU({ maxSize: 256 });
conn.onLogs(RAYDIUM_AMM_V4_PROGRAM_ID, (txLogs: web3.Logs, ctx: web3.Context) => {
if (!ctx || !txLogs || txLogs.err !== null || !txLogs.logs || !txLogs.logs.length) return;
if (!txLogs.signature || txLogs.signature === OPAQUE_SIGNATURE) return;
if (seenSignatures.has(txLogs.signature)) return;
seenSignatures.set(txLogs.signature, true);
const rayLogEncoded = findLogEntry(
s => s.length >= RAYDIUM_AMM_V4_LOG_MIN_LENGTH
&& s.startsWith(RAYDIUM_AMM_V4_LOG_PREFIX, RAYDIUM_AMM_V4_LOG_PREFIX_OFFSET),
txLogs.logs,
);
if (!rayLogEncoded) return;
let rayLog: RaydiumAmmV4Log | null = null;
try {
rayLog = deserializeRayLog(Buffer.from(rayLogEncoded.slice(RAYDIUM_AMM_V4_LOG_OFFSET), "base64"));
} catch (e) {}
if (!rayLog) return;
console.log(ctx.slot, txLogs.signature.padStart(88));
console.log(JSON.stringify(
Object.fromEntries(Object.entries(rayLog).map(([k, v]) =>
[k, v !== null && typeof v === "object" && v.toString ? String(v) : v])),
null, 2,
));
console.log();
});
})(process.env.SOL_RPC_URL || "https://mainnet.helius-rpc.com/?api-key=00000000-0000-0000-0000-000000000000");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment