1+ import {
2+ CommitmentLevel ,
3+ SubscribeRequest ,
4+ } from "@triton-one/yellowstone-grpc" ;
5+ import pino from "pino" ;
6+ import Client from "@triton-one/yellowstone-grpc" ;
7+ import { PublicKey , VersionedTransactionResponse } from "@solana/web3.js" ;
8+ // import { buy, solanaConnection, sell } from "../transaction/transaction";
9+ import * as borsh from "@coral-xyz/borsh" ;
10+ import bs58 from "bs58" ;
11+ import base58 from "bs58" ;
12+ import { Buffer } from "buffer" ;
13+ import { struct , bool , u64 , Layout } from "@coral-xyz/borsh" ;
14+ import { Idl } from "@project-serum/anchor" ;
15+ import { SolanaParser } from "@shyft-to/solana-transaction-parser" ;
16+ import { TOKEN_PROGRAM_ID , AccountLayout } from "@solana/spl-token" ;
17+ import { TransactionFormatter , SolanaEventParser , bnLayoutFormatter , transactionOutput , sell , buy } from "../../src/pumpfunsdk/pumpdotfun-sdk/src" ;
18+ import { createSubscribeNewPumpfunBondsTxRequest } from "./grpc-requests" ;
19+ import { token_db } from "../constants" ;
20+ import { handleSubscribe } from "./utils" ;
21+ let tokenSoldCount = { } ;
22+ let tokenRugCheckCount = { } ;
23+ let tokensHas40percentFreshWallet :any = { } ;
24+ const PROGRAM_ID = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" ;
25+ export const GLOBAL_ACCOUNT_SEED = "global" ;
26+ export const MINT_AUTHORITY_SEED = "mint-authority" ;
27+ export const BONDING_CURVE_SEED = "bonding-curve" ;
28+ const global_opeator = "jito" ;
29+ const client :any = new Client (
30+ GRPC_URL ,
31+ GRPC_XTOKEN ,
32+ {
33+ "grpc.max_receive_message_length" : 64 * 1024 * 1024 , // 64MiB
34+ }
35+ ) ; //grpc endpoint
36+ const TXN_FORMATTER = new TransactionFormatter ( ) ;
37+ const PUMP_FUN_PROGRAM_ID = new PublicKey (
38+ PROGRAM_ID
39+ ) ;
40+ const PUMP_FUN_IX_PARSER = new SolanaParser ( [ ] ) ;
41+ PUMP_FUN_IX_PARSER . addParserFromIdl (
42+ PUMP_FUN_PROGRAM_ID . toBase58 ( ) ,
43+ pumpFunIdl as Idl ,
44+ ) ;
45+ const PUMP_FUN_EVENT_PARSER = new SolanaEventParser ( [ ] , console ) ;
46+ PUMP_FUN_EVENT_PARSER . addParserFromIdl (
47+ PUMP_FUN_PROGRAM_ID . toBase58 ( ) ,
48+ pumpFunIdl as Idl ,
49+ ) ;
50+
51+ async function PingWhenTimeout ( ) {
52+ //const currentSlot = await connection.getSlot("finalized");
53+ const pingNumber = await client . ping ( 1 ) ;
54+ //logger.info(`Ping number in price checker bot: ${pingNumber}`);
55+ if ( pingNumber !== 1 ) throw new Error ( "the grpc server has not response, restart the stream" ) ;
56+
57+ }
58+
59+
60+ async function getBondingCurveAccount (
61+ mint : PublicKey
62+ ) {
63+ const tokenAccount = await connection . getAccountInfo (
64+ getBondingCurvePDA ( mint ) ,
65+ {
66+ commitment : "processed" ,
67+ }
68+ ) ;
69+ if ( ! tokenAccount ) {
70+ return null ;
71+ }
72+ return BondingCurveAccount . fromBuffer ( tokenAccount ! . data ) ;
73+ }
74+ function getBondingCurvePDA ( mint : PublicKey ) {
75+ return PublicKey . findProgramAddressSync (
76+ [ Buffer . from ( BONDING_CURVE_SEED ) , mint . toBuffer ( ) ] ,
77+ new PublicKey ( PROGRAM_ID )
78+ ) [ 0 ] ;
79+ }
80+ function decodePumpFunTxn ( tx : any ) {
81+
82+ if ( tx . meta === undefined ) return ;
83+ if ( tx . meta ?. err ) return ;
84+
85+ const paredIxs = PUMP_FUN_IX_PARSER . parseTransactionData (
86+ tx . transaction . message ,
87+ tx . meta . loadedAddresses ,
88+ ) ;
89+
90+ const pumpFunIxs = paredIxs . filter ( ( ix ) =>
91+ ix . programId . equals ( PUMP_FUN_PROGRAM_ID ) ,
92+ ) ;
93+
94+ if ( pumpFunIxs . length === 0 ) return ;
95+ const events = PUMP_FUN_EVENT_PARSER . parseEvent ( tx ) ;
96+ const result = { instructions : pumpFunIxs , events } ;
97+ bnLayoutFormatter ( result ) ;
98+ return result ;
99+ }
100+ export const structure = struct ( [
101+ u64 ( "discriminator" ) ,
102+ u64 ( "virtualTokenReserves" ) ,
103+ u64 ( "virtualSolReserves" ) ,
104+ u64 ( "realTokenReserves" ) ,
105+ u64 ( "realSolReserves" ) ,
106+ u64 ( "tokenTotalSupply" ) ,
107+ bool ( "complete" ) ,
108+ ] ) ;
109+
110+
111+ export function decodeTransact ( data ) {
112+ const output = base58 . encode ( Buffer . from ( data , 'base64' ) )
113+ return output ;
114+ }
115+
116+ export function bondingCurveData ( buffer : Buffer ) {
117+
118+ let value = structure . decode ( buffer ) ;
119+ const discriminator = BigInt ( value . discriminator ) ;
120+ const virtualTokenReserves = BigInt ( value . virtualTokenReserves ) ;
121+ const virtualSolReserves = BigInt ( value . virtualSolReserves ) ;
122+ const realTokenReserves = BigInt ( value . realTokenReserves ) ;
123+ const realSolReserves = BigInt ( value . realSolReserves ) ;
124+ const tokenTotalSupply = BigInt ( value . tokenTotalSupply ) ;
125+ const complete = value . complete ;
126+ return {
127+ discriminator,
128+ virtualTokenReserves,
129+ virtualSolReserves,
130+ realTokenReserves,
131+ realSolReserves,
132+ tokenTotalSupply,
133+ complete
134+ } ;
135+ }
136+
137+ async function retriveCurveState ( bonding_curve : string ) {
138+ try {
139+ const filters = [
140+ {
141+ dataSize : 165 , //size of account (bytes)
142+ } ,
143+ {
144+ memcmp : {
145+ offset : 32 , //location of our query in the account (bytes)
146+ bytes : bonding_curve , //our search criteria, a base58 encoded string
147+ } ,
148+ } ,
149+ ] ;
150+ const accounts = await connection . getParsedProgramAccounts (
151+ TOKEN_PROGRAM_ID , //new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
152+ { filters : filters }
153+ ) ;
154+ let results : any = { } ;
155+ const solBalance = await connection . getBalance (
156+ new PublicKey ( bonding_curve )
157+ ) ;
158+ accounts . forEach ( ( account , i ) => {
159+ //Parse the account data
160+ const parsedAccountInfo : any = account . account . data ;
161+ const mintAddress = parsedAccountInfo [ "parsed" ] [ "info" ] [ "mint" ] ;
162+ const tokenBalance =
163+ parsedAccountInfo [ "parsed" ] [ "info" ] [ "tokenAmount" ] [ "uiAmount" ] ;
164+
165+ results [ mintAddress ] = tokenBalance ;
166+ results [ "SOL" ] = solBalance / 10 ** 9 ;
167+ } ) ;
168+ return results ;
169+ } catch ( e ) {
170+ console . log ( e ) ;
171+ }
172+ }
173+ export async function tOutPut ( data :any ) {
174+ // Ensure data is defined and contains the necessary properties
175+ if ( ! data || ! data . account || ! data . account . account ) {
176+ // throw new Error("Invalid data format");
177+ return ;
178+ }
179+
180+
181+ const dataTx = data . account . account ;
182+
183+ // Safely decode each piece of transaction data
184+ const signature = dataTx . txnSignature ? decodeTransact ( dataTx . txnSignature ) : null ;
185+ const pubKey :any = dataTx . pubkey ? decodeTransact ( dataTx . pubkey ) : null ;
186+ const owner = dataTx . owner ? decodeTransact ( dataTx . owner ) : null ;
187+ let poolstate :any = null ;
188+ if ( signature !== null ) {
189+ console . log ( "signature: " , signature ) ;
190+ console . log ( "pubKey: " , pubKey ) ;
191+ console . log ( "owner: " , owner )
192+
193+ let targetToken = '' ;
194+
195+ const curve_state = await retriveCurveState ( pubKey ) ;
196+ const accList = Object . keys ( curve_state ) ;
197+ console . log ( accList ) ;
198+ for ( const token of accList ) {
199+ if ( token !== "SOL" ) targetToken = token ;
200+ }
201+ console . log ( targetToken ) ;
202+ try {
203+ poolstate = bondingCurveData ( dataTx . data ) ;
204+ console . log ( poolstate ) ;
205+ if ( poolstate . complete === true ) {
206+ //token_db.insertNewGrad(targetToken, 0);
207+ }
208+ } catch ( error ) {
209+ console . error ( "Failed to decode pool state:" , error ) ;
210+ }
211+ }
212+
213+ return {
214+ signature,
215+ pubKey,
216+ owner,
217+ poolstate
218+ } ;
219+ }
220+
221+ setInterval ( PingWhenTimeout , 1000 ) ;
222+
223+
224+ /**
225+ * Subscribes to a new pumpfun bonds and insert it to db
226+ */
227+ export async function streamAnyNewBonds ( ) {
228+
229+ const stream = await client . subscribe ( ) ;
230+ const r1 : SubscribeRequest = await createSubscribeNewPumpfunBondsTxRequest ( ) ;
231+ handleSubscribe ( stream , r1 ) ;
232+ stream . on ( "data" , ( data :any ) => {
233+ try {
234+ tOutPut ( data ) ;
235+ } catch ( err ) {
236+ console . log ( err )
237+ }
238+
239+ } ) ;
240+
241+
242+
243+ }
244+
245+ streamAnyNewBonds ( ) ;
246+
0 commit comments