Compare commits

...

8 Commits

Author SHA1 Message Date
8e49f01054 must to next inner at least 2026-02-21 09:29:28 +08:00
thloyi
62cc64a90a fix from rpc ComputeUnitsConsumed 2026-02-12 10:43:30 +08:00
thloyi
629ffe2ea7 fix pump complete parse error 2026-02-12 10:38:56 +08:00
cachalots
56dac04a2a fix culimit 2026-02-12 10:11:29 +08:00
cachalots
852ad4b382 cu limit 2026-02-11 17:49:43 +08:00
thloyi
3fdd4c4490 fix parse error 2026-02-11 14:58:12 +08:00
thloyi
40012b531c fix meteora pool entrycontract 2026-02-10 10:32:46 +08:00
bijianing97
0e30d6b35f Update address 2026-02-09 17:36:35 +08:00
16 changed files with 1000 additions and 35 deletions

View File

@@ -27,10 +27,11 @@ func budgetParser(tx *Tx, instr Instruction, _ InnerInstructions, offset [2]uint
}
}
func computeUnitLimitParser(offset [2]uint, _ *Tx, decodedData []byte) ([2]uint, error) {
if len(decodedData) < 8 {
func computeUnitLimitParser(offset [2]uint, tx *Tx, decodedData []byte) ([2]uint, error) {
if len(decodedData) < 4 {
return increaseOffset(offset), nil
}
tx.CuLimit = binary.LittleEndian.Uint32(decodedData[:4])
return increaseOffset(offset), nil
}

120
consts.go
View File

@@ -199,6 +199,124 @@ var mevAgentFeeAddresses = map[solana.PublicKey]string{
solana.MustPublicKeyFromBase58("B1ooMauwuJPhHsXqt3uj7B92CAFG8kaD1Q2iGEmGYnx"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("B1ooMdjcY7zemxDWiH8jVZPxEMdHnE5AraWPHdHQoPj"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("B1ooMKzu6siJzQutP6a6oLiY3fpzgQnBZsAjxuAm9qo"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("Gu2UGEfze3Gg5cHuEC4jGbyCufgpev75RkVvBdKKtf12"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("E8wD3SMD1trozPrvSN9F6SyuUXD7rrFDuR3WexGziKG5"): MevAgent0slot,
solana.MustPublicKeyFromBase58("18hCV7f9CPmZRAH3QCNZaGHhHeNSfisQKeKuFkQsPLY"): MevAgent0slot,
solana.MustPublicKeyFromBase58("2sYKRWBNVY6UomMBi4juoMrrL98bqizDMn98cJ3cBmye"): MevAgent0slot,
solana.MustPublicKeyFromBase58("CZubxabMM7CPFSDAfMUhxNuvXRDLjDf6yVVq1RoJ66rk"): MevAgent0slot,
solana.MustPublicKeyFromBase58("Dz8rMcdokTLfbnNz2ZdYocZixgaA1TMqbA31xtwPgcxb"): MevAgent0slot,
solana.MustPublicKeyFromBase58("ForLDu55GfA2U1aTUaitmjzjs92vvVn1MSqzY3D9HtAK"): MevAgent0slot,
solana.MustPublicKeyFromBase58("6MgjyQU7G988jgL6EGAgfHYoeesCnwYMyPeh1fpJ71FP"): MevAgent0slot,
solana.MustPublicKeyFromBase58("12pHu2j2DDShyCVFU7vtSLXga74et9y83VD38mw6XYhB"): MevAgent0slot,
solana.MustPublicKeyFromBase58("5QuV4TS5TJFWPu7Yd56VaPvf4nKUicPvTfC3mwnb7dNW"): MevAgent0slot,
solana.MustPublicKeyFromBase58("4gh9m7RV7G4WwRftA6qV7RhDfytdepb3XbxFRfTtneYJ"): MevAgent0slot,
solana.MustPublicKeyFromBase58("AumQWSLrWwDXRq1yDEYPiw8vT5NUBYzrbdWCprJ4ZUa8"): MevAgent0slot,
solana.MustPublicKeyFromBase58("3vGEsQA5jzvN8TBgytuYEdZxW6P2pK1c6pq56JiFuygS"): MevAgent0slot,
solana.MustPublicKeyFromBase58("AsEF2SWSEZ1xpGZ5fdzDKaoka1XEtFSjGo39YUXkpvAh"): MevAgent0slot,
solana.MustPublicKeyFromBase58("2WoQNgmc4SEXrR3rKQypmeWmsxGqHHE6rApnVrP6Pt77"): MevAgent0slot,
solana.MustPublicKeyFromBase58("9vTpfGYN2jtjZgXQ7gihyHmN3FseLP7uW1CWMdsgcny"): MevAgentBlocxRoute,
solana.MustPublicKeyFromBase58("CyL8mfycXYbWHVoTTsfvnAfF2MvfcqeQAmmsqNQLxF7g"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("Eg85QSYLwtZfBBPF4CsNmijJDXUAeCMjoh36L1cwboqg"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("9gBzvLKedrs9HxaLPhBdkPaeFTxEDNDGfqJmqvHjfiZp"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("7BxoFqM3swL46Lt9EWzL9z2LeXYfmJL7MVzpFrDpLPei"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("91Ht2gq1CMPcLySuq8NjHaA1rXysm8zzoiiyfT4uSE7u"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("2zCYpNSWcHX9AzFndF1mcT1bMkG1EXMzzjFcBjSnJq9f"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("4Kfqkx3c8TxLX74J1nzfzfHCGdoDCuZ8k84sGpnVh1a4"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("GeiVfSfUBVxjJA6F2SNSASoK8JaSCiSmsC2hBrPLfpiv"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("DggsS83MWeUHZdrV2jyMUh8GDfLrU5P9Es36h7Uf3wRp"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("2d5viHZBHKt5DgEpMckXEfndR1CoZ1tHvcbL9fU4xqT7"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("73VnqgMJq29j4HMzF6GRdBeVpZgz7ibouyKQvyAKbVZy"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("HvgA9hTyrTQCU5869fhZ7My9WkkHK2yBo4Wu6ojHmMio"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("4xJEQnuMpoUNxhNew4AechRBo1DnpVfLyUe68BXTTF73"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("7ZKL8BAPfKKa6FNmds48QKFnckrcj4mkppRnsBAR2xVH"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("BYpBPSRkVSvutxHngtxnqeoTBrENZ8iM56Ywnsmy829w"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("4LEkLhb2u5qCUXS1Hc3eL2zTxk2kjSzQeFK4ZgWsV3EM"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("96Zc2GT7ZmMvF7rXgcwHAyJ7KmK8RaS4Z3VZw2b7GjJx"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("9Kaz3Q9KJ3x8SXvui37FK5m1AwcwqkYLvS9Xg1Why9Q1"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("muV321VhQ4XgJkVtsZP13zbCqg9HokT222bWS3DBxp3"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("Hth7qf5dv683k3ZJffjJvJ8gSU21dfPWy3mBEyRRhCiN"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("pD7KfmGkxHqQFNLqYv3zshSzkGaAB99vjNDKz6e7nGC"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("AQChXZ1ZWvPH8EjdPxXXsC8VqCaBmPVruJbswhE3xNZ8"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("2L2DgQ5ZXRYnv8K97NFDJvsNrA1MsrCGr3CvokPtDy8D"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("4BjQeBGZmGNWeHfQC4scHK5d4RtDr79h1hZNPcrLDS8C"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("E99TTcqBPAY1F4ZppMRkDX3pTqaSnRC24tUErfd2opNL"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("8zi6AG7oSKoswSEMaxNmXrwBYmDwuQ4GLiY4Q1j9Rayu"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("8GgU7tKJSA97G97kD9AbxYgsC9Hcjfg7RpAofWuA6oHt"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("JDxQoXGFRwEojWzkirDNeHz88SDEPzdDakjsobJ4YHrj"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("GQgHdPuDNcss3BoKrMfS6bgGekjitmKQRJxnuhUBu921"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("8FoxPbnucCZ3wuzhMofKE5VdYKcHfWmYNrnC2whVBAhS"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("3L9UZWLAprLtB2xddEHsCmgXbPc2PidgSjtHGZd2MzB3"): MevAgentBlockRazor,
solana.MustPublicKeyFromBase58("FAST3dMFZvESiEipBvLSiXq3QCV51o3xuoHScqRU6cB6"): MevAgentFast,
solana.MustPublicKeyFromBase58("FASTCKnwwY6iL3CknRgg3Zqir7jeagDDhxSnBQQy5a1C"): MevAgentFast,
solana.MustPublicKeyFromBase58("FASTHPW6akdGh9PFSdhMTbCuGkCSX7LsUjjnaB2RTQ4v"): MevAgentFast,
solana.MustPublicKeyFromBase58("FASTKL1AamNKrwnvbKwo4PU8434BBdqVrTtugM6oDU71"): MevAgentFast,
solana.MustPublicKeyFromBase58("FASTPB76TxKPMZ7Q29m8v4zJn8gUjbWyvTEQaaxhwN7M"): MevAgentFast,
solana.MustPublicKeyFromBase58("FASTYKWXRfAoty7SQCM1mGVrmPUyyNcF4tc3DUkLDAu9"): MevAgentFast,
solana.MustPublicKeyFromBase58("FASTYmSidNfLwdwiQEhCTtzghxEtaipeNSDSwh9xDPs3"): MevAgentFast,
solana.MustPublicKeyFromBase58("FASTs6ctgbsuZegMzUs4DPUYhRSZUPCjgCVnttHbpQAp"): MevAgentFast,
solana.MustPublicKeyFromBase58("node1FdMPnJBN7QTuhzNw3VS823nxFuDTizrrbcEqzp"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1zrVjcY2XB3Au8qYj5MxjbNfGu3baHaqZMkPM7Z"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1E3hguapYA18HCpEEkRHQmLNiyv9pdfE9s2zo5X"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1CVxtFas2Pw5Vcf86Pq89Hqx4jveo1ntY7ARFMK"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1EoLojAvoUmyDytcvgdXs6GPtY3zpQXPCRVncEA"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1VwH169UqyJHr5MYCH3EBuwrdvn5KHXAkhEEfav"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1JkDqyiEg7CDNj3ATPiRmWaAG2gnrAEiMJ4Rzcc"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1GS8pZnP6MzGSXwhA2MXH6EBfCpFaAE64G2ubpB"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1AVfbcSi98LAgGyAHUGS4eYkYTbS5vUPZYQnViF"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1L7Xat2tSkRNNi6TSuUScMYfj64ovhr2aceJm9g"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1kMY97W3LPXaKKV43yRa2Q3BLg4WZiT27VifUDc"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1Zi3r7hmGYwF9cJAkfCHh9EKWbkSrYdvcvLukF4"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1G3fmoCuEJzcPNF4hLbSZ2ypcUuh9CB3k9E7Q8k"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node18nQgpjoKe1fM72GiV6tHXg5dMKbVPFGwRBD9MU"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1spgxXR8HCbm4LyZNoisFLmBXxy2qnZrv63WxMp"): MevAgentNode1,
solana.MustPublicKeyFromBase58("node1rmmFXeLh94mBGtDHbSwCrBJqDnc16xrURHRYD9"): MevAgentNode1,
solana.MustPublicKeyFromBase58("EnchantKMZ93cDKwsnyvnD5WCpZLFTLVRWozFjAUzTko"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("CJwbwPfVFZDPGKJKCtLkzDJPFrGyyroEPFjXigmJB6mr"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("3Thhhj3omvVFfbhEHdFe8djwDZT5oS6BQ4k5KrZkYt1r"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("6CQzBpGJn6XYcCkm77xNd944MpbjLHLsP6sCEWSZVUHS"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("DeMZbwKtu9kteFdxL1yh6aTWqDwYfH79DKzYrgfTwAc2"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("3Pn9ZFCsNTf9MvWbpemQccWuyHNMbBjxg1eW53ikHcpH"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("DjfRXWegRn9bWnBvZFxAnpu1jNikcoy8iiu6ZX9AxAd5"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("8mbjzuz8ka3zVGnry6xMEwm96tzk4yKnWgvwAT1LwEGx"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("5KpS5Q3nUtp1cUynUxzH2bA93SWzmx2y3GwU45AeEEP5"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgK8f5H4ocVdNrkUrspUFmAaEosGQtbc1JCMqLwvvRe"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bg7BgfutLpjFdxDNcbwQFGFkLGQT9Kww9wv6EWUHQr3"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bg9zUQnVkYLgAWJvL9MjP4tFDecCxbvmQRqrAuZpQUA"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgu2xgHEJocs4tggHDEwNnmgduftnXfJuWoLiUYfiLW"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgd4MvpBH3LaVz6sHvqFphoUex4taUe2E5mKuk4sVXn"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgSfpx2Pr3bHYev6ikwTqdBo2aaPGgjEseAWhjxp6F5"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgfaB4sngcm7cARjjiEvKfWE87owf2HuDfYDy8EyP45"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgzmGhu9qcyLW6qR1HKQuLTY6PWktNSAuzLNmo7aiQY"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgMTi1qFtbiFiHsURKW4Bfg4wjXtT8iJL7HC1z3gXsm"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgDRbhSLK62ApA2PbZs1W7SecodGhTFf6udU3MWDadu"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bg67LJN9Ngvfq4hJbSmm7tZ2wqmn2f1pxXbXW5QfxRz"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgmnrKWgN5jE8pF3PbFxRWYaho1bjCtmcTZ9VfRbhxf"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgTZqxCX4ej98P1UyYJjgGmGDmst7nteSyUWDwzMxNj"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgauKkwFcT8w7SHau9NufDfvmq1cy79X52bRbL6yzEB"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bg8WP8cEtWhdCjDd7rrwzsnz7K9f3oiEm2Qqu7TYmDn"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("DzrVK357ynzkPtdC7jzUbXgsUY8ULUeR2ihoPcX1JB3n"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfn2d1g6xkwhykkyjtoccFbC7r19ADf5dGB2YnT1Hgw"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfnXi2FdpFUUn6VyoxUohNyWk2Nup3ruguTgK8jaZaF"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bg1rCzhyASbzib75ohpRfNY3mGJaX1k6v56WCrUkh3a"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgsouue9XeHUzNwwuAKqBj1Fk1RbJkcBjvs4zkmUhLc"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("mwGELGMgGGrNL1UibNCQeJHDE7qdPptWRYB6noUHmTj"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfn9b35be4L7xh7G8P2jUzWsJAigrKDSoBeRMiyg75p"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfnSbG36fCGpT8WsB1NEbQ2BH11iog6qjFqMEVCZZgV"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfnaxrmMoemfvbhXek6offTNXas11GtepGQMN9UF3gk"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfnKWwarhjuKKg3WV3nw3wAE8zuymigT3vuJHwZeL4s"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfnaZXdkjJq26auzzFKeQm7YKphuNCdDGcJVqqb6awr"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfnP85qobXv2wETniKjXBhxKvgivpfT8EGAcS8sb3bq"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfnSAoQWtJCDKnjmR8oduqbZYXr69Q4cFQ6VhgFkvgT"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfnSbziLrSSVNqPBD9tpx3Ud4VtbxwsXjdfYv9SmBDx"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("Vn7tfMvrvrymGYMnxhj1DV16Sz2R9YXmaXF3hiSAHuC"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfnTcJ1i4mRYzbqGduF71RsooUCFkPSpk8UE7drCkjh"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfnUxCuZcfP6yidkG3EsqyR5DTbyie3R74fGoA5oB3J"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfnEJvqLGddJxQTA9DcYLTbVwiFdT3KmLXo6UcnmcgC"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfn6dyanKiTTinHs887D7qe2S4727wzK7xi7ERGaizC"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgDETv6tnt9mwYqAKebLXY5B5o6akiKJmAdU7Gd9G7H"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("bgH7YhymSykyvMa3nAZpzvrn73owJHU5iB75S1aiLT9"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfngGVVQLiVRFbLWw3Ektiv17ef9NiRZbcgdAhh4ZEW"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("nEFs3jph8HJt7honu3k7XtGUufMnwAvSXmXcKSPxryP"): MevAgentNozomi,
}
var entryContractAddresses = map[solana.PublicKey]string{
@@ -241,6 +359,8 @@ var entryContractAddresses = map[solana.PublicKey]string{
solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9"): EntryContractAxiom,
solana.MustPublicKeyFromBase58("F5tfvbLog9VdGUPqBDTT8rgXvTTcq7e5UiGnupL1zvBq"): EntryContractAxiom,
solana.MustPublicKeyFromBase58("B3jytJa6Tzpn4Ly7GNnDm3dMGqUin5aMRm5aPsJGU5G7"): EntryContractTradewiz,
solana.MustPublicKeyFromBase58("DBotWvSso9oD1ZB3aHx2LiD2ZoFpF8PbKjaT4uHKLLVs"): EntryContractDbot,
solana.MustPublicKeyFromBase58("term9YPb9mzAsABaqN71A4xdbxHmpBNZavpBiQKZzN3"): EntryContractPadre,
}
var okxDexRoutersV2 = solana.MustPublicKeyFromBase58("proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u")

View File

@@ -11,6 +11,7 @@ const (
MevAgentFlashBlock = "flashBlock"
MevAgentUnknown = "unknown"
MevAgentBlockRazor = "blockrazor"
MevAgentFast = "fast"
MevAgentSoyas = "soyas"
MevAgentStellium = "stellium"
MevAgentAstralane = "astralane"
@@ -45,14 +46,16 @@ const (
EntryContractFluxbeamDEX = "fluxbeamDEX"
EntryContractNovaBotsProgram = "novaBotsProgram"
EntryContractTaggedSearcher = "taggedSearcher"
EntryContractPadre = "padre"
EntryContractDFlow = "dflow"
EntryContractMaestroBot = "maestroBot"
EntryContractBonkBot = "bonkBot"
EntryContractBinanceWallet = "binanceWallet"
EntryContractMayhem = "pumpMayhem"
EntryContractTerm = "term"
EntryContractUnknown = "unknown"
EntryContractTradewiz = "tradewiz"
EntryContractDbot = "dbot"
EntryContractUnknown = "unknown"
)
const (

View File

@@ -31,6 +31,7 @@ func main() {
continue
}
ptx := msg.Tx
fmt.Println("consume", ptx.ComputeUnitsConsumed, "limit", ptx.CuLimit, "hash", ptx.GetTxHash())
//data, _ := json.Marshal(tx)
//fmt.Println(string(data))
//continue

View File

@@ -23,7 +23,7 @@ var ()
func main() {
var slot uint64 = 399060766
var slot uint64 = 399477968
var data = NewBlockData(decimal.NewFromFloat(100.0))
client := rpc.New("https://staked.helius-rpc.com?api-key=5adcf1f9-5719-43d1-bf3f-c2d4e1e5f94d")
var rewards = false

817
internal/test3/test.go Normal file
View File

@@ -0,0 +1,817 @@
package main
import (
"context"
"errors"
"fmt"
"log"
"log/slog"
"strings"
"time"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"
"github.com/jackc/pgtype"
"github.com/shopspring/decimal"
solana_parser "github.com/thloyi/pump-parser"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var ()
func main() {
var data = NewBlockData(decimal.NewFromFloat(100.0))
client := rpc.New("https://staked.helius-rpc.com?api-key=5adcf1f9-5719-43d1-bf3f-c2d4e1e5f94d")
var version uint64 = 0
txSig, _ := solana.SignatureFromBase58("2LCw5yZy6sGTWKpJNxpFxR11M66cXPsrGmJXnQmWW9QVv6SDWRmu1aevc6yE9NeUz78mFb4T8TEx9w5781NHnz2T")
tx, err := client.GetTransaction(context.Background(), txSig, &rpc.GetTransactionOpts{
Commitment: rpc.CommitmentFinalized,
Encoding: solana.EncodingBase64,
MaxSupportedTransactionVersion: &version,
})
if err != nil {
fmt.Println("get block error:", err)
return
}
solana_parser.EnableAllParsers()
var blockTime uint64
rawTx, err := solana_parser.FromRpcTransactionWithMeta(rpc.TransactionWithMeta{
Slot: 0,
BlockTime: nil,
Transaction: rpc.DataBytesOrJSONFromBytes(tx.Transaction.GetBinary()),
Meta: tx.Meta,
Version: tx.Version,
}, &blockTime, 0, int64(0))
if err != nil {
fmt.Println("from rpc tx error:", err)
return
}
result, err := solana_parser.ParseRawTx(rawTx)
if err != nil {
fmt.Println("parse tx error:", rawTx.TxHash(), err)
return
}
swapsLen := len(result.Swaps)
for i := 0; i < swapsLen; i++ {
action := result.Swaps[i]
var actions []solana_parser.Swap = make([]solana_parser.Swap, 0, 2)
actions = append(actions, action)
if i+1 < swapsLen {
nextAction := result.Swaps[i+1]
if action.Event == "buy" && nextAction.Event == "complete" &&
action.Program == solana_parser.SolProgramPump &&
nextAction.Program == solana_parser.SolProgramPump &&
action.BaseMint == nextAction.BaseMint {
actions = append(actions, nextAction)
i++
}
if action.Event == "migrate" && nextAction.Event == "create" &&
action.Program == solana_parser.SolProgramPump &&
nextAction.Program == solana_parser.SolProgramPumpAMM &&
action.BaseMint == nextAction.BaseMint {
actions = append(actions, nextAction)
i++
}
}
if err = HandleAction(context.Background(), result, actions, data); err != nil {
//h.logger.Errorf("handle action error: %s - %v", result.RawTx.Transaction.Signatures[0].String(), err)
fmt.Println("parse action error:", "tx", result.GetTxHash(), "i", i, "err", err)
}
}
fmt.Println("tx count: ", len(data.Txs))
}
var (
meteoraDammV2Program = solana.MustPublicKeyFromBase58("cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG")
raydiumCPmmProgramID = solana.MustPublicKeyFromBase58("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C")
)
func HandleAction(ctx context.Context, tx *solana_parser.Tx, swaps []solana_parser.Swap, data *BlockData) error {
swapLen := len(swaps)
if len(swaps) == 0 {
return nil
}
if swaps[0].BaseMint != solana_parser.WSOL && swaps[0].QuoteMint != solana_parser.WSOL && !swaps[0].QuoteMint.IsZero() {
return nil
}
if len(swaps) == 0 {
return nil
}
event := swaps[0].Event
swap := swaps[0]
action := SwapGetter{swap}
switch event {
case "buy", "sell":
data.AppendTx(action.GetTx(tx, uint64(swap.TxIndex), data.Price))
if swap.Program == solana_parser.SolProgramPump {
if swapLen == 2 && swaps[1].Event == "complete" {
t := pgtype.Timestamptz{}
t.Set(time.Unix(tx.BlockAt, 0))
data.AppendAction(Action{
Maker: swaps[1].User.String(),
Token: swaps[1].BaseMint.String(),
Pair: swaps[1].Pool.String(),
Action: "pump-migrate",
Block: tx.Block,
BlockAt: t,
TxHash: tx.GetTxHash(),
})
}
}
return data.SetPair(action, tx.Block, "")
case "create":
pair, err := action.GetPair(tx.Block, "")
if err != nil {
return err
}
data.AppendTx(action.GetTx(tx, uint64(swap.TxIndex), data.Price))
data.Pairs[pair.Address] = *pair
case "add_liquidity", "remove_liquidity", "deposit", "withdraw", "add", "remove":
liquidityTx, err := action.GetLiquidityTx(tx, uint64(swap.TxIndex))
if liquidityTx == nil {
return err
}
data.AppendTx(*liquidityTx)
return data.SetPair(action, tx.Block, "")
}
if event != "migrate" {
return nil
}
if swap.Program == solana_parser.SolProgramPump {
t := pgtype.Timestamptz{}
t.Set(time.Unix(tx.BlockAt, 0))
if swapLen == 2 && swaps[1].Event == "create" && swaps[1].Program == solana_parser.SolProgramPumpAMM && swaps[1].BaseMint == swap.BaseMint {
tokenMint := swap.BaseMint.String()
data.AppendAction(Action{
Maker: swap.User.String(),
Token: tokenMint,
Pair: swaps[1].Pool.String(),
Action: "on-pumpswap",
Block: tx.Block,
BlockAt: t,
TxHash: tx.GetTxHash(),
})
data.NewRaydium = append(data.NewRaydium, tokenMint)
}
} else if swap.Program == solana_parser.SolProgramRaydiumLaunchLab || swap.Program == solana_parser.SolProgramRaydiumLaunchLabBonk {
t := pgtype.Timestamptz{}
t.Set(time.Unix(tx.BlockAt, 0))
var actionType string
if action.MigrateTopProgram == raydiumCPmmProgramID {
actionType = "on-raydium-cpmm"
} else {
actionType = "on-raydium-amm"
}
data.AppendAction(Action{
Maker: action.User.String(),
Token: action.BaseMint.String(),
Pair: action.MigrateToPool.String(),
Action: actionType,
Block: tx.Block,
BlockAt: t,
TxHash: tx.GetTxHash(),
})
} else if swap.Program == solana_parser.SolProgramMeteoraBondingCurve {
t := pgtype.Timestamptz{}
t.Set(time.Unix(tx.BlockAt, 0))
var actionType string
if swap.MigrateTopProgram == meteoraDammV2Program {
actionType = "on-meteora-amm-v2"
} else {
actionType = "on-meteora-amm-v1"
}
data.AppendAction(Action{
Maker: action.User.String(),
Token: action.BaseMint.String(),
Pair: action.MigrateToPool.String(),
Action: actionType,
Block: uint64(tx.Block),
BlockAt: t,
TxHash: tx.GetTxHash(),
})
}
return nil
}
type Pair struct {
Id string `gorm:"column:id;primaryKey;default:uuid_generate_v4()"`
Address string
Name string
Token0 string
Token1 string
LpToken string
ChainId int64
Reserve0 decimal.Decimal
Reserve1 decimal.Decimal
Block uint64
BlockAt *pgtype.Timestamptz `gorm:"column:block_at;default:NULL" json:"block_at,omitempty"`
CreatedAt *pgtype.Timestamptz `gorm:"autoCreateTime" json:"created_at,omitempty"`
SortId uint64
Program string
IsCreate bool `gorm:"-"`
//TokenObj *Token `gorm:"-" json:"token_obj,omitempty"`
UpdateSlot uint64 `gorm:"-"`
InDB bool `gorm:"-"`
}
type Tx struct {
Id pgtype.UUID `gorm:"column:id;primaryKey;default:uuid_generate_v4()" json:"-"`
PairAddress string `json:"pair_address"`
Maker string `json:"maker"`
Token0Address string `json:"token0_address"`
Token1Address string `json:"token1_address"`
Token0Amount decimal.Decimal `json:"token0Amount" gorm:"column:token0_amount;type:numeric"`
Token1Amount decimal.Decimal `json:"token1Amount" gorm:"column:token1_amount;type:numeric"`
PriceUsd decimal.Decimal `json:"price_usd" gorm:"column:price_usd;type:numeric"`
AmountUsd decimal.Decimal `json:"amount_usd" gorm:"column:amount_usd;type:numeric"`
Block uint64 `json:"block"`
BlockIndex uint64 `json:"index"`
Event string `json:"event"`
TxHash string `json:"tx_hash"`
TxIndex uint64 `json:"topic_index"`
Program string `json:"program"`
BlockAt pgtype.Timestamptz `gorm:"column:block_at;default:NULL" json:"block_at"`
CreatedAt *pgtype.Timestamptz `gorm:"autoCreateTime" json:"-"`
TotalSupply string `gorm:"total_supply"`
AfterReserve0 string `gorm:"after_reserve0"`
AfterReserve1 string `gorm:"after_reserve1"`
PositionChange int64 `gorm:"position_change"`
Platform string `gorm:"column:tx_platform;type:platform;default:'none'" json:"tx_platform"`
PlatformFee decimal.Decimal `gorm:"-" json:"-"` // TODO: save to db
CUPrice decimal.Decimal `gorm:"column:tx_cu_price;type:numeric" json:"tx_cu_price"`
MevAgent string `gorm:"column:tx_mev_agent;type:mev_agent;default:'none'" json:"tx_mev_agent"`
MevAgentFee decimal.Decimal `gorm:"column:tx_mev_agent_fee;type:numeric" json:"tx_mev_agent_fee"`
AfterSOLBalance decimal.Decimal `gorm:"column:after_sol_balance;type:numeric" json:"after_sol_balance"`
EntryContract string `gorm:"column:tx_entry_contract;type:entry_contract;default:'none'" json:"tx_entry_contract"`
}
type Action struct {
Id pgtype.UUID `gorm:"column:id;primaryKey;default:uuid_generate_v4()" json:"-"`
Maker string `json:"maker"`
Token string `json:"token"`
Pair string `json:"pair"`
Action string `json:"action"`
Block uint64 `json:"block"`
BlockAt pgtype.Timestamptz `json:"block_at"`
TxHash string `json:"tx_hash"`
CreatedAt *pgtype.Timestamptz `gorm:"autoCreateTime" json:"-"`
}
type BlockData struct {
Pairs map[string]Pair
Txs []Tx
Actions []Action
Price decimal.Decimal
NewRaydium []string
}
func NewBlockData(price decimal.Decimal) *BlockData {
return &BlockData{
Pairs: make(map[string]Pair),
Txs: make([]Tx, 0),
Actions: make([]Action, 0),
Price: price,
NewRaydium: make([]string, 0),
}
}
func (bd *BlockData) AppendTx(tx Tx) {
bd.Txs = append(bd.Txs, tx)
}
func (bd *BlockData) AppendAction(action Action) {
bd.Actions = append(bd.Actions, action)
}
func (bd *BlockData) SetPair(action SwapGetter, block uint64, _ string) error {
pair, err := action.GetPair(block, "")
if err != nil {
return err
}
bd.Pairs[pair.Address] = *pair
return nil
}
type SwapGetter struct {
solana_parser.Swap
}
const (
PositionChangeNone = int64(iota)
PositionChangeNewBuy
PositionChangeBuyMore
PositionChangeSellPart
PositionChangeSellAll
)
func (spg SwapGetter) GetLiquidityTx(tx *solana_parser.Tx, index uint64) (*Tx, error) {
if spg.BaseMint != solana.WrappedSol && spg.QuoteMint != solana.WrappedSol {
return nil, nil
}
var (
token0 string
amount0 decimal.Decimal
amount1 decimal.Decimal
pool0 decimal.Decimal
pool1 decimal.Decimal
event string
)
if spg.BaseMint == solana.WrappedSol {
amount0 = spg.QuoteAmount.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
amount1 = spg.BaseAmount.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
token0 = spg.QuoteMint.String()
pool0 = spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
pool1 = spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
} else {
amount0 = spg.BaseAmount.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
amount1 = spg.QuoteAmount.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
token0 = spg.BaseMint.String()
pool0 = spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
pool1 = spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
}
if spg.Event == "deposit" || spg.Event == "add" || spg.Event == "add_liquidity" || spg.Event == "add_liquidity_one_side" {
event = "add"
} else if spg.Event == "withdraw" || spg.Event == "remove" || spg.Event == "remove_liquidity" || spg.Event == "remove_liquidity_one_side" {
event = "remove"
}
if event == "" {
return nil, nil
}
mevName, mevFee := tx.CheckMevAgent()
platformName, platformFee := tx.CheckPlatform(spg.Swap)
pairString := ""
if spg.Program == solana_parser.SolProgramPump {
pairString = spg.BaseMint.String()
} else {
pairString = spg.Pool.String()
}
t := pgtype.Timestamptz{}
_ = t.Set(time.Unix(tx.BlockAt, 0))
return &Tx{
PairAddress: pairString,
Maker: spg.User.String(),
Token0Address: token0,
Token1Address: "So11111111111111111111111111111111111111112",
Token0Amount: amount0,
Token1Amount: amount1,
Block: tx.Block,
BlockIndex: tx.BlockIndex,
Event: event,
TxHash: tx.GetTxHash(),
TxIndex: index,
BlockAt: t,
Program: spg.Program,
AfterReserve0: pool0.String(),
AfterReserve1: pool1.String(),
Platform: platformName,
PlatformFee: platformFee,
CUPrice: tx.CUPrice,
MevAgent: mevName,
MevAgentFee: mevFee,
AfterSOLBalance: spg.AfterSOLBalance,
EntryContract: spg.CheckEntryContract(),
}, nil
}
func (spg SwapGetter) GetTx(tx *solana_parser.Tx, index uint64, price decimal.Decimal) Tx {
var (
token0 string
amount0 decimal.Decimal
amount1 decimal.Decimal
pool0 decimal.Decimal
pool1 decimal.Decimal
event string
)
if spg.BaseMint == solana.WrappedSol {
amount0 = spg.QuoteAmount.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
amount1 = spg.BaseAmount.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
token0 = spg.QuoteMint.String()
pool0 = spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
pool1 = spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
if spg.Event == "buy" {
event = "sell"
} else if spg.Event == "sell" {
event = "buy"
}
} else {
amount0 = spg.BaseAmount.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
amount1 = spg.QuoteAmount.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
token0 = spg.BaseMint.String()
pool0 = spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
pool1 = spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
event = spg.Event
}
priceUsd := decimal.Zero
if amount0.GreaterThan(priceUsd) {
priceUsd = amount1.Div(amount0).Mul(price)
}
pc := PositionChangeNone
if event == "buy" {
pc = PositionChangeNewBuy
if spg.BaseMint == solana.WrappedSol {
if spg.UserQuoteBalance.GreaterThan(spg.QuoteAmount) {
pc = PositionChangeBuyMore
}
} else {
if spg.UserBaseBalance.GreaterThan(spg.BaseAmount) {
pc = PositionChangeBuyMore
}
}
} else if event == "sell" {
pc = PositionChangeSellPart
if spg.BaseMint == solana.WrappedSol {
if spg.UserQuoteBalance.Div(decimal.New(1, int32(spg.QuoteMintDecimals))).LessThan(decimal.NewFromFloat(0.01)) {
pc = PositionChangeSellAll
}
} else {
if spg.UserBaseBalance.Div(decimal.New(1, int32(spg.BaseMintDecimals))).LessThan(decimal.NewFromFloat(0.01)) {
pc = PositionChangeSellAll
}
}
}
mevName, mevFee := tx.CheckMevAgent()
platformName, platformFee := tx.CheckPlatform(spg.Swap)
if mevName == "" {
mevName = "none"
}
if mevName == "unknown" {
mevName = "none"
mevFee = decimal.Zero
}
pairString := ""
if spg.Program == solana_parser.SolProgramPump {
pairString = spg.BaseMint.String()
} else {
pairString = spg.Pool.String()
}
t := pgtype.Timestamptz{}
_ = t.Set(time.Unix(tx.BlockAt, 0))
return Tx{
PairAddress: pairString,
Maker: spg.User.String(),
Token0Address: token0,
Token1Address: "So11111111111111111111111111111111111111112",
Token0Amount: amount0,
Token1Amount: amount1,
PriceUsd: priceUsd,
AmountUsd: amount1.Mul(price),
Block: tx.Block,
BlockIndex: tx.BlockIndex,
Event: event,
TxHash: tx.GetTxHash(),
TxIndex: index,
BlockAt: t,
Program: spg.Program,
AfterReserve0: pool0.String(),
AfterReserve1: pool1.String(),
PositionChange: pc,
Platform: platformName,
PlatformFee: platformFee,
CUPrice: tx.CUPrice,
MevAgent: mevName,
MevAgentFee: mevFee,
AfterSOLBalance: spg.AfterSOLBalance,
EntryContract: spg.CheckEntryContract(),
}
}
func (spg SwapGetter) GetPair(slot uint64, _ string) (*Pair, error) {
//pump amm
if spg.Program == solana_parser.SolProgramPump {
tokenMint := spg.BaseMint.String()
return &Pair{
Address: tokenMint,
Token0: tokenMint,
Token1: "So11111111111111111111111111111111111111112",
ChainId: 900,
Reserve0: spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals))),
Reserve1: spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals))),
IsCreate: spg.Event == "create",
Program: spg.Program,
UpdateSlot: slot,
}, nil
} else {
var (
token0 string
amount0 decimal.Decimal
amount1 decimal.Decimal
)
if spg.BaseMint.IsZero() || spg.QuoteMint.IsZero() {
return nil, errors.New("base mint or quote mint is empty")
}
if spg.BaseMint == solana.WrappedSol {
amount0 = spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
amount1 = spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
//decimal0 = spg.QuoteMintDecimals
token0 = spg.QuoteMint.String()
} else {
amount0 = spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
amount1 = spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
//decimal0 = a.BaseDecimals
token0 = spg.BaseMint.String()
}
return &Pair{
Address: spg.Pool.String(),
LpToken: spg.LpMint.String(),
Token0: token0,
Token1: "So11111111111111111111111111111111111111112",
ChainId: 900,
Reserve0: amount0,
Reserve1: amount1,
IsCreate: false,
Program: spg.Program,
UpdateSlot: slot,
}, nil
}
}
func getBlockTxsFromDb(db *gorm.DB, block uint64) ([]Tx, error) {
var txs []Tx
result := db.Table("tx").Where("block = ?", block).Find(&txs)
return txs, result.Error
}
func getBlockActionsFromDb(db *gorm.DB, block uint64) ([]Action, error) {
var txs []Action
result := db.Table("action").Where("block = ?", block).Find(&txs)
return txs, result.Error
}
type dbLog struct {
logger *slog.Logger
}
func (l *dbLog) Printf(format string, args ...interface{}) {
l.logger.Info(fmt.Sprintf(format, args...))
}
func newDbLog() *dbLog {
return &dbLog{logger: slog.Default()}
}
func NewGorm(dsn string) *gorm.DB {
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: logger.New(newDbLog(), logger.Config{
Colorful: false,
LogLevel: logger.Warn,
SlowThreshold: time.Second * 10,
IgnoreRecordNotFoundError: true,
}),
})
if err != nil {
panic(err)
}
return db
}
func compareTxs(dbTxs []Tx, dataTxs []Tx) (diff int, missing int) {
dataByHash := make(map[string][]Tx, len(dataTxs))
for _, tx := range dataTxs {
dataByHash[fmt.Sprintf("%s-%d", tx.TxHash, tx.TxIndex)] = append(dataByHash[fmt.Sprintf("%s-%d", tx.TxHash, tx.TxIndex)], tx)
}
for _, dbTx := range dbTxs {
candidates := dataByHash[fmt.Sprintf("%s-%d", dbTx.TxHash, dbTx.TxIndex)]
if len(candidates) == 0 {
missing++
log.Printf("missing tx: %s", txCompareString(dbTx))
continue
}
matched := false
for _, dataTx := range candidates {
if txEqualWithoutHash(dbTx, dataTx) {
matched = true
break
}
}
if !matched {
diff++
log.Printf("tx diff hash=%s, program=%s, event:%s: %s, ", dbTx.TxHash, dbTx.Program, dbTx.Event, txCompareDiffString(dbTx, candidates[0]))
}
}
log.Printf("compare txs done: db=%d parsed=%d missing=%d diff=%d", len(dbTxs), len(dataTxs), missing, diff)
return diff, missing
}
func withinOnePercentDecimal(a decimal.Decimal, b decimal.Decimal) bool {
if a.IsZero() {
return b.IsZero()
}
diff := a.Sub(b).Abs()
threshold := a.Abs().Mul(decimal.NewFromFloat(0.03))
return diff.LessThanOrEqual(threshold)
}
func withinOnePercentStringDecimal(a string, b string) bool {
ad, errA := decimal.NewFromString(a)
bd, errB := decimal.NewFromString(b)
if errA != nil || errB != nil {
return a == b
}
return withinOnePercentDecimal(ad, bd)
}
func txEqualWithoutHash(a Tx, b Tx) bool {
//mevMatch := a.MevAgent == b.MevAgent || (a.MevAgent == "none" && b.MevAgent == "unknown") || (a.MevAgent == "unknown" && b.MevAgent == "none")
//mevNone := a.MevAgent == "none" || a.MevAgent == "unknown"
return a.PairAddress == b.PairAddress &&
a.Token1Address == b.Token1Address &&
(a.Token0Address == "" || a.Token0Address == b.Token0Address) &&
//a.Maker == b.Maker &&
(a.Token0Address == "" || withinOnePercentDecimal(a.Token0Amount, b.Token0Amount)) &&
withinOnePercentDecimal(a.Token1Amount, b.Token1Amount) &&
a.Block == b.Block &&
a.BlockIndex == b.BlockIndex &&
a.Event == b.Event &&
a.TxIndex == b.TxIndex &&
a.Program == b.Program &&
(a.Program == solana_parser.SolProgramPumpAMM || a.Program == solana_parser.SolProgramPump || a.Program == solana_parser.SolProgramMeteoraPools || (a.Event == "create") || withinOnePercentStringDecimal(a.AfterReserve0, b.AfterReserve0)) &&
(a.Program == solana_parser.SolProgramPumpAMM || a.Program == solana_parser.SolProgramPump || a.Program == solana_parser.SolProgramMeteoraPools || (a.Event == "create") || withinOnePercentStringDecimal(a.AfterReserve1, b.AfterReserve1)) &&
// a.PositionChange == b.PositionChange &&
(a.Platform == b.Platform || (a.Platform == "photon" && b.Platform == "fake") || (a.Platform == "trojan" && b.Platform == "fake")) &&
a.CUPrice.String() == b.CUPrice.String() // &&
//mevMatch &&
//(mevNone || a.MevAgentFee.String() == b.MevAgentFee.String()) &&
//(a.Program == solana_parser.SolProgramRaydiumV4 || a.AfterSOLBalance.String() == b.AfterSOLBalance.String())
//&&
// a.EntryContract == b.EntryContract
}
func txCompareDiffString(a Tx, b Tx) string {
var diffs []string
if a.PairAddress != b.PairAddress {
diffs = append(diffs, fmt.Sprintf("PairAddress db=%s data=%s", a.PairAddress, b.PairAddress))
}
//if a.Maker != b.Maker {
// diffs = append(diffs, fmt.Sprintf("Maker db=%s, data=%s", a.Maker, b.Maker))
//}
if a.Token1Address != b.Token1Address {
diffs = append(diffs, fmt.Sprintf("Token1Address db=%s data=%s", a.Token1Address, b.Token1Address))
}
if a.Token0Address != b.Token0Address {
diffs = append(diffs, fmt.Sprintf("Token0Address db=%s data=%s", a.Token0Address, b.Token0Address))
}
if !withinOnePercentDecimal(a.Token0Amount, b.Token0Amount) {
diffs = append(diffs, fmt.Sprintf("Token0Amount db=%s data=%s", a.Token0Amount.String(), b.Token0Amount.String()))
}
if !withinOnePercentDecimal(a.Token1Amount, b.Token1Amount) {
diffs = append(diffs, fmt.Sprintf("Token1Amount db=%s data=%s", a.Token1Amount.String(), b.Token1Amount.String()))
}
if a.Block != b.Block {
diffs = append(diffs, fmt.Sprintf("Block db=%d data=%d", a.Block, b.Block))
}
if a.BlockIndex != b.BlockIndex {
diffs = append(diffs, fmt.Sprintf("BlockIndex db=%d data=%d", a.BlockIndex, b.BlockIndex))
}
if a.Event != b.Event {
diffs = append(diffs, fmt.Sprintf("Event db=%s data=%s", a.Event, b.Event))
}
if a.TxIndex != b.TxIndex {
diffs = append(diffs, fmt.Sprintf("TxIndex db=%d data=%d", a.TxIndex, b.TxIndex))
}
if a.Program != b.Program {
diffs = append(diffs, fmt.Sprintf("Program db=%s data=%s", a.Program, b.Program))
}
if !withinOnePercentStringDecimal(a.AfterReserve0, b.AfterReserve0) {
diffs = append(diffs, fmt.Sprintf("AfterReserve0 db=%s data=%s", a.AfterReserve0, b.AfterReserve0))
}
if !withinOnePercentStringDecimal(a.AfterReserve1, b.AfterReserve1) {
diffs = append(diffs, fmt.Sprintf("AfterReserve1 db=%s data=%s", a.AfterReserve1, b.AfterReserve1))
}
//if a.PositionChange != b.PositionChange {
// diffs = append(diffs, fmt.Sprintf("PositionChange db=%d data=%d", a.PositionChange, b.PositionChange))
//}
if a.Platform != b.Platform {
diffs = append(diffs, fmt.Sprintf("Platform db=%s data=%s", a.Platform, b.Platform))
}
if a.CUPrice.String() != b.CUPrice.String() {
diffs = append(diffs, fmt.Sprintf("CUPrice db=%s data=%s", a.CUPrice.String(), b.CUPrice.String()))
}
//if a.MevAgent != b.MevAgent {
// diffs = append(diffs, fmt.Sprintf("MevAgent db=%s data=%s", a.MevAgent, b.MevAgent))
//}
//if a.MevAgentFee.String() != b.MevAgentFee.String() {
// diffs = append(diffs, fmt.Sprintf("MevAgentFee db=%s data=%s", a.MevAgentFee.String(), b.MevAgentFee.String()))
//}
//if a.AfterSOLBalance.String() != b.AfterSOLBalance.String() {
// diffs = append(diffs, fmt.Sprintf("AfterSOLBalance db=%s data=%s", a.AfterSOLBalance.String(), b.AfterSOLBalance.String()))
//}
//if a.EntryContract != b.EntryContract {
// diffs = append(diffs, fmt.Sprintf("EntryContract db=%s data=%s", a.EntryContract, b.EntryContract))
//}
return strings.Join(diffs, "; ")
}
func compareActions(dbActions []Action, dataActions []Action) (diff, missing int) {
dataByHash := make(map[string][]Action, len(dataActions))
for _, action := range dataActions {
dataByHash[action.TxHash] = append(dataByHash[action.TxHash], action)
}
for _, dbAction := range dbActions {
candidates := dataByHash[dbAction.TxHash]
if len(candidates) == 0 {
missing++
log.Printf("missing action: %s", actionCompareString(dbAction))
continue
}
matched := false
for _, dataAction := range candidates {
if actionEqualWithoutHash(dbAction, dataAction) {
matched = true
break
}
}
if !matched {
diff++
log.Printf("action diff hash=%s: %s", dbAction.TxHash, actionCompareDiffString(dbAction, candidates[0]))
}
}
log.Printf("compare actions done: db=%d parsed=%d missing=%d diff=%d", len(dbActions), len(dataActions), missing, diff)
return diff, missing
}
func actionEqualWithoutHash(a Action, b Action) bool {
return a.Maker == b.Maker &&
a.Token == b.Token &&
a.Pair == b.Pair &&
a.Action == b.Action &&
a.Block == b.Block
}
func actionCompareDiffString(a Action, b Action) string {
var diffs []string
if a.Maker != b.Maker {
diffs = append(diffs, fmt.Sprintf("Maker db=%s data=%s", a.Maker, b.Maker))
}
if a.Token != b.Token {
diffs = append(diffs, fmt.Sprintf("Token db=%s data=%s", a.Token, b.Token))
}
if a.Pair != b.Pair {
diffs = append(diffs, fmt.Sprintf("Pair db=%s data=%s", a.Pair, b.Pair))
}
if a.Action != b.Action {
diffs = append(diffs, fmt.Sprintf("Action db=%s data=%s", a.Action, b.Action))
}
if a.Block != b.Block {
diffs = append(diffs, fmt.Sprintf("Block db=%d data=%d", a.Block, b.Block))
}
return strings.Join(diffs, "; ")
}
func actionCompareString(action Action) string {
return fmt.Sprintf("Maker=%s Token=%s Pair=%s Action=%s Block=%d TxHash=%s", action.Maker, action.Token, action.Pair, action.Action, action.Block, action.TxHash)
}
func txCompareString(tx Tx) string {
return fmt.Sprintf(
"tx.Program=%s TxHash=%s PairAddress=%s Token1Address=%s Token0Amount=%s Token1Amount=%s Block=%d BlockIndex=%d Event=%s TxIndex=%d AfterReserve0=%s AfterReserve1=%s PositionChange=%d Platform=%s CUPrice=%s MevAgent=%s MevAgentFee=%s AfterSOLBalance=%s EntryContract=%s",
tx.Program,
tx.TxHash,
tx.PairAddress,
tx.Token1Address,
tx.Token0Amount.String(),
tx.Token1Amount.String(),
tx.Block,
tx.BlockIndex,
tx.Event,
tx.TxIndex,
tx.AfterReserve0,
tx.AfterReserve1,
tx.PositionChange,
tx.Platform,
tx.CUPrice.String(),
tx.MevAgent,
tx.MevAgentFee.String(),
tx.AfterSOLBalance.String(),
tx.EntryContract,
)
}

View File

@@ -234,5 +234,6 @@ var transferDiscriminator = uint32(2)
var createAccountWithSeedDiscriminator = uint32(3)
var systemProgram = solana.MustPublicKeyFromBase58("11111111111111111111111111111111")
var momoProgram = solana.MustPublicKeyFromBase58("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr")
var eventDiscriminator = [8]byte{228, 69, 165, 46, 81, 203, 154, 29}

View File

@@ -371,6 +371,7 @@ func metaoraPoolAddLiquidity(tx *Tx, instruction Instruction, innerInstructions
if len(instruction.Accounts) < 14 {
return nil, increaseOffset(offset), fmt.Errorf("invalid instruction accounts length")
}
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
pool := tx.rawTx.accountList[instruction.Accounts[0]]
lpMint := tx.rawTx.accountList[instruction.Accounts[1]]
@@ -494,8 +495,6 @@ func metaoraPoolAddLiquidity(tx *Tx, instruction Instruction, innerInstructions
return nil, increaseOffset(offset), fmt.Errorf("failed to find deposit instructions")
}
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
var event = "add_liquidity_one_side"
if baseFound && quoteFound {
// both sides
@@ -530,6 +529,7 @@ func metaoraPoolRemoveLiquidity(tx *Tx, instruction Instruction, innerInstructio
if len(instruction.Accounts) < 14 {
return nil, increaseOffset(offset), fmt.Errorf("invalid instruction accounts length")
}
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
pool := tx.rawTx.accountList[instruction.Accounts[0]]
lpMint := tx.rawTx.accountList[instruction.Accounts[1]]
@@ -700,8 +700,6 @@ func metaoraPoolRemoveLiquidity(tx *Tx, instruction Instruction, innerInstructio
return nil, increaseOffset(offset), fmt.Errorf("failed to find withdraw instructions, baseFound: %v, quoteFound: %v", baseFound, quoteFound)
}
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
var event = "remove_liquidity_one_side"
if baseFound && quoteFound {
event = "remove_liquidity"

View File

@@ -90,7 +90,7 @@ func meteoraDammV2InitializePoolParser(tx *Tx, instruction Instruction, innerIns
var prefixLen = offset[1]
inners, err := getInnerInstructions(innerInstructions, prefixLen)
if err != nil {
return nil, increaseOffset(offset), fmt.Errorf("meta Bonding Curve initial get inner instructions error: %v, offset, %d, %d", err, offset[0], offset[1])
return nil, increaseOffset(offset), fmt.Errorf("meta damm initial get inner instructions error: %v, offset, %d, %d", err, offset[0], offset[1])
}
var loadedEvent bool
var initializePoolEvent MetaoraDammInitializePoolEvent
@@ -119,6 +119,13 @@ func meteoraDammV2InitializePoolParser(tx *Tx, instruction Instruction, innerIns
}
baseVaultAccountIndex := instruction.Accounts[10]
quoteVaultAccountIndex := instruction.Accounts[11]
if bytes.Equal(instruction.Data[:8], meteoraDammV2InitializePoolWithDynamicConfig[:]) {
baseVaultAccountIndex = instruction.Accounts[11]
quoteVaultAccountIndex = instruction.Accounts[12]
} else if bytes.Equal(instruction.Data[:8], meteoraDammV2InitializeCustomizablePoolDiscriminator[:]) {
baseVaultAccountIndex = instruction.Accounts[9]
quoteVaultAccountIndex = instruction.Accounts[10]
}
baseVaultTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, baseVaultAccountIndex)
if err != nil {

View File

@@ -848,11 +848,11 @@ func orcaWhirPoolSwapV2Parser(tx *Tx, instruction Instruction, innerInstructions
var baseFound, quoteFound bool
var skipOffset = 0
for i, inner := range inners {
from, to, amount, err := parseTokenTransfer(tx.rawTx, inner)
if err != nil {
if i <= 1 { //maybe momo??
if !tx.rawTx.accountList[inner.ProgramIDIndex].Equals(solana.Token2022ProgramID) && !tx.rawTx.accountList[inner.ProgramIDIndex].Equals(solana.TokenProgramID) {
continue
}
from, to, amount, err := parseTokenTransfer(tx.rawTx, inner)
if err != nil {
return nil, increaseOffset(offset), fmt.Errorf("orca whirpool swapv2 parse token transfer error: %v, offset, %d, %d", err, offset[0], offset[1])
}
if !baseFound && (from.Equals(vault0Account) || to.Equals(vault0Account)) {
@@ -1010,11 +1010,11 @@ func orcaWhirPoolTwoHopSwapParser(tx *Tx, instruction Instruction, innerInstruct
{
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, pool2VaultBase)
if err != nil {
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool1 token0 vault balance after tx: %v", err)
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool2 token0 vault balance after tx: %v", err)
}
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, pool2VaultQuote)
if err != nil {
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool1 token1 vault balance after tx: %v", err)
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool2 token1 vault balance after tx: %v", err)
}
userBase := getAccountBalanceAfterTx(tx.rawTx, pool2UserBase)
@@ -1105,8 +1105,8 @@ func orcaWhirPoolTwoHopSwapV2Parser(tx *Tx, instruction Instruction, innerInstru
//pool2UserBase := instruction.Accounts[8]
pool2VaultBase := instruction.Accounts[11]
pool2UserQuote := instruction.Accounts[12]
pool2VaultQuote := instruction.Accounts[13]
pool2VaultQuote := instruction.Accounts[12]
pool2UserQuote := instruction.Accounts[13]
swaps := make([]Swap, 2)
{
@@ -1186,11 +1186,11 @@ func orcaWhirPoolTwoHopSwapV2Parser(tx *Tx, instruction Instruction, innerInstru
{
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, pool2VaultBase)
if err != nil {
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool1 token0 vault balance after tx: %v", err)
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool2 token0 vault balance after tx: %v", err)
}
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, pool2VaultQuote)
if err != nil {
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool1 token1 vault balance after tx: %v", err)
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool2 token1 vault balance after tx: %v", err)
}
userBase := decimal.Zero

View File

@@ -91,6 +91,7 @@ func (tx *Tx) Parser() error {
tx.BlockAt = tx.rawTx.BlockTime
tx.CuFee = decimal.NewFromUint64(tx.rawTx.Meta.Fee)
tx.ComputeUnitsConsumed = tx.rawTx.Meta.ComputeUnitsConsumed
tx.BeforeSolBalance = decimal.NewFromUint64(tx.rawTx.Meta.PreBalances[0]).Div(decimal.NewFromInt(1e9))
tx.AfterSOLBalance = decimal.NewFromUint64(tx.rawTx.Meta.PostBalances[0]).Div(decimal.NewFromInt(1e9))
@@ -176,8 +177,13 @@ func (tx *Tx) Parser() error {
tx.Swaps = append(tx.Swaps, swap)
}
// tx.Swaps = append(tx.Swaps, swaps...)
if ii == int(offset[0]) && j == int(offset[1]) {
j = j + 1
} else {
j = int(offset[1])
ii = int(offset[0])
}
} else if p, exists := actionPrograms[innerProgramAccount]; exists {
offset, err := p(tx, innerInstr, innersMap[i], [2]uint{uint(i), uint(j)})
if err != nil {

View File

@@ -348,6 +348,8 @@ func BuyOrSellParser(tx *Tx, instruction Instruction, innerInstructions InnerIns
BaseMintDecimals: 6,
QuoteMintDecimals: 9,
User: user,
BaseReserve: decimal.NewFromUint64(tradeEvent.RealTokenReserves),
QuoteReserve: decimal.NewFromUint64(tradeEvent.RealSolReserves),
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[1]]),
UserBaseBalance: userBase,
UserQuoteBalance: decimal.NewFromUint64(userQuote),

View File

@@ -156,6 +156,7 @@ type Meta struct {
PreBalances []uint64 `json:"preBalances"`
PreTokenBalances []TokenBalance `json:"preTokenBalances"`
Rewards []interface{} `json:"rewards"`
ComputeUnitsConsumed uint64 `json:"computeUnitsConsumed"`
}
type Header struct {
NumReadonlySignedAccounts int `json:"numReadonlySignedAccounts"`
@@ -325,7 +326,9 @@ func FromRpcTransactionWithMeta(tx rpc.TransactionWithMeta, blockTime *uint64, s
}
sTx.Meta.Fee = meta.Fee
//sTx.Meta.InnerInstructions = meta.InnerInstructions
if meta.ComputeUnitsConsumed != nil {
sTx.Meta.ComputeUnitsConsumed = *meta.ComputeUnitsConsumed
}
for _, innerInstr := range meta.InnerInstructions {
var instrs []Instruction
for _, instr := range innerInstr.Instructions {
@@ -513,6 +516,7 @@ func intSliceFromUint16Slice(in []uint16) []int {
}
return out
}
func getAtaIdxByOwner(result *RawTx, owner solana.PublicKey, mint solana.PublicKey) (int, error) {
var preBalance *TokenBalance
for _, pre := range result.Meta.PreTokenBalances {
@@ -805,7 +809,7 @@ func ConvertYellowstoneGrpcTransactionToSolanaTransaction(y *pb.SubscribeUpdateT
}
sTx.Meta.Fee = meta.Fee
//sTx.Meta.InnerInstructions = meta.InnerInstructions
sTx.Meta.ComputeUnitsConsumed = *meta.ComputeUnitsConsumed
for _, innerInstr := range meta.InnerInstructions {
var instrs []Instruction
for _, instr := range innerInstr.Instructions {

View File

@@ -54,12 +54,12 @@ func raydiumCPmmCreatePoolParser(tx *Tx, instruction Instruction, innerInstructi
vault1 = instruction.Accounts[12]
}
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, instruction.Accounts[vault0])
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, vault0)
if err != nil {
return nil, increaseOffset(offset), fmt.Errorf("failed to get token0 vault balance after tx: %v", err)
}
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, instruction.Accounts[vault1])
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, vault1)
if err != nil {
return nil, increaseOffset(offset), fmt.Errorf("failed to get token1 vault balance after tx: %v", err)
}

View File

@@ -143,7 +143,7 @@ type RaydiumLaunchLabCreateEvent struct {
}
func raydiumLaunchLabInitializeParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
if len(instruction.Accounts) < 16 {
if len(instruction.Accounts) < 15 {
return nil, increaseOffset(offset), fmt.Errorf("not enough accounts for initialize instruction")
}
user := tx.rawTx.accountList[instruction.Accounts[0]]

5
tx.go
View File

@@ -47,6 +47,8 @@ type Swap struct {
StartBinId int32
EndBinId int32
BinChanges []DlmmBinLiquidityChange
ConsumeUnit uint64
}
type DlmmBinLiquidityChange struct {
@@ -98,6 +100,9 @@ type Tx struct {
// update tokenInfo
Token map[solana.PublicKey]TokenMeta `gorm:"-"`
ComputeUnitsConsumed uint64 `json:"compute_units_consumed"`
CuLimit uint32 `json:"cu_limit"`
// todo pool info ??
}