Created
August 1, 2024 23:16
-
-
Save rubpy/e408574bccd8c0ce641599e5f19ec9d5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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