diff --git a/checking.go b/checking.go index a301165..04b83fd 100644 --- a/checking.go +++ b/checking.go @@ -173,6 +173,8 @@ func checkBonkGmgnBuy(rawTx *RawTx) bool { var ( axiomTxLoopupTable = solana.MustPublicKeyFromBase58("7RKtfATWCe98ChuwecNq8XCzAzfoK3DtZTprFsPMGtio") + axiomProgramID = solana.MustPublicKeyFromBase58("AxiomfHaWDemCFBLBayqnEnNwE6b7B2Qz3UmzMpgbMG6") + gmgnProgramID = solana.MustPublicKeyFromBase58("GMgnVFR8Jb39LoXsEVzb3DvBy3ywCmdmJquHUy1Lrkqb") ) func checkBonkAxiomBuy(rawTx *RawTx) bool { @@ -366,3 +368,209 @@ func checkBonkAxiomBuy(rawTx *RawTx) bool { return true } + +func checkPumpFunAxiomBuy(rawTx *RawTx) bool { + + // 检查交易版本 + if rawTx.Version == "legacy" || len(rawTx.Transaction.Message.AddressTableLookups) != 1 { + return false + } + + // 检查 addressLookupTable 是否是 Axiom 的 + if rawTx.Transaction.Message.AddressTableLookups[0].AccountKey != axiomTxLoopupTable { + return false + } + + // 检查交易指令数量 + if len(rawTx.Transaction.Message.Instructions) != 6 { + return false + } + + accountList := rawTx.getAccountList() + // 检查 cu limit + { + instruction := rawTx.Transaction.Message.Instructions[0] + programId := accountList[instruction.ProgramIDIndex] + if programId != solana.ComputeBudget { + return false + } + + if len(instruction.Accounts) != 1 { + return false + } + + accountId := accountList[instruction.Accounts[0]].String() + if !strings.HasPrefix(accountId, "jitodontfront") || !strings.HasSuffix(accountId, "TradeWithAxiomDotTrade") { + return false + } + } + + // 检查 cu price + { + instruction := rawTx.Transaction.Message.Instructions[1] + programId := accountList[instruction.ProgramIDIndex] + if programId != solana.ComputeBudget { + return false + } + } + + // 检查 ata.createIdempotent + { + instruction := rawTx.Transaction.Message.Instructions[2] + programId := accountList[instruction.ProgramIDIndex] + if programId != solana.SPLAssociatedTokenAccountProgramID { + return false + } + + if instruction.Data.String() != "2" { + return false + } + + if len(instruction.Accounts) < 4 { + return false + } + + // axiom 会先创建 token 账户, 而不是 wsol 账户 + accountId := accountList[instruction.Accounts[3]] + if accountId == solana.WrappedSol { + return false + } + } + + // 检查调用axiom合约 + { + instruction := rawTx.Transaction.Message.Instructions[3] + programId := accountList[instruction.ProgramIDIndex] + if programId != axiomProgramID { + return false + } + } + + // 检查 transfer + { + instruction := rawTx.Transaction.Message.Instructions[4] + programId := accountList[instruction.ProgramIDIndex] + if programId != solana.SystemProgramID { + return false + } + + if len(instruction.Data) == 0 || instruction.Data[0] != 2 { + return false + } + } + + // 检查 transfer + { + instruction := rawTx.Transaction.Message.Instructions[5] + programId := accountList[instruction.ProgramIDIndex] + if programId != solana.SystemProgramID { + return false + } + + if len(instruction.Data) == 0 || instruction.Data[0] != 2 { + return false + } + } + + return true +} + +func checkPumpFunGmgnBuy(rawTx *RawTx) bool { + + // 检查交易版本 + if rawTx.Version != "legacy" { + return false + } + + // 检查交易指令数量 + if len(rawTx.Transaction.Message.Instructions) != 6 { + return false + } + + accountList := rawTx.getAccountList() + // 检查 cu limit + { + instruction := rawTx.Transaction.Message.Instructions[0] + programId := accountList[instruction.ProgramIDIndex] + if programId != solana.ComputeBudget { + return false + } + + if len(instruction.Accounts) != 1 { + return false + } + + accountId := accountList[instruction.Accounts[0]].String() + if !strings.HasPrefix(accountId, "jitodontfront1111111111151111111111111655") { + return false + } + } + + // 检查 cu price + { + instruction := rawTx.Transaction.Message.Instructions[1] + programId := accountList[instruction.ProgramIDIndex] + if programId != solana.ComputeBudget { + return false + } + } + + // 检查 ata.createIdempotent + { + instruction := rawTx.Transaction.Message.Instructions[2] + programId := accountList[instruction.ProgramIDIndex] + if programId != solana.SPLAssociatedTokenAccountProgramID { + return false + } + + if instruction.Data.String() != "2" { + return false + } + + if len(instruction.Accounts) < 4 { + return false + } + + accountId := accountList[instruction.Accounts[3]] + if accountId == solana.WrappedSol { + return false + } + } + + // 检查调用 gmgn 合约 + { + instruction := rawTx.Transaction.Message.Instructions[3] + programId := accountList[instruction.ProgramIDIndex] + if programId != gmgnProgramID { + return false + } + } + + // 检查 transfer + { + instruction := rawTx.Transaction.Message.Instructions[4] + programId := accountList[instruction.ProgramIDIndex] + if programId != solana.SystemProgramID { + return false + } + + if len(instruction.Data) == 0 || instruction.Data[0] != 2 { + return false + } + } + + // 检查 transfer + { + instruction := rawTx.Transaction.Message.Instructions[5] + programId := accountList[instruction.ProgramIDIndex] + if programId != solana.SystemProgramID { + return false + } + + if len(instruction.Data) == 0 || instruction.Data[0] != 2 { + return false + } + } + + return true +} diff --git a/parser.go b/parser.go index e60b91d..d0db04d 100644 --- a/parser.go +++ b/parser.go @@ -61,6 +61,13 @@ func (tx *Tx) Parser() error { return errors.New("rawTx is nil") } accountList := tx.rawTx.getAccountList() + if tx.rawTx.Meta.Err == nil { + for _, acc := range tx.rawTx.Transaction.Message.Instructions { + if accountList[acc.ProgramIDIndex] == solana.VoteProgramID { + tx.Vote = true + } + } + } tx.TxHash = (*[64]byte)((tx.rawTx.Transaction.Signatures[0][:])) tx.Signer = tx.rawTx.GetSigner() diff --git a/tx.go b/tx.go index e180ae4..2fd134e 100644 --- a/tx.go +++ b/tx.go @@ -65,6 +65,7 @@ type SolTransfer struct { type Tx struct { rawTx *RawTx + Vote bool Signer solana.PublicKey Err interface{} `json:"err,omitempty"` Swaps []Swap `json:"swaps,omitempty"` @@ -91,6 +92,10 @@ type Tx struct { // todo pool info ?? } +func (tx *Tx) GetRawTx() *RawTx { + return tx.rawTx +} + func (tx *Tx) SetRawTx(t *RawTx) { tx.rawTx = t } @@ -136,16 +141,33 @@ func (tx *Tx) CheckPlatform(swap Swap) (string, decimal.Decimal) { platformFee = info.PlatformFee break } - if swap.Event == "buy" && swap.Program == SolProgramRaydiumLaunchLabBonk { - for _, p := range tx.Platform { - switch p.Platform { - case PlatformAxiom: - if !checkBonkAxiomBuy(rawTx) { - platform = PlatformFake + if swap.Event == "buy" { + switch swap.Program { + case SolProgramRaydiumLaunchLabBonk: + for _, p := range tx.Platform { + switch p.Platform { + case PlatformAxiom: + if !checkBonkAxiomBuy(rawTx) { + platform = PlatformFake + } + case PlatformGMGN: + if !checkBonkGmgnBuy(rawTx) { + platform = PlatformFake + } } - case PlatformGMGN: - if !checkBonkGmgnBuy(rawTx) { - platform = PlatformFake + } + } + if swap.Program == SolProgramRaydiumLaunchLabBonk { + for _, p := range tx.Platform { + switch p.Platform { + case PlatformAxiom: + if !checkPumpFunAxiomBuy(rawTx) { + platform = PlatformFake + } + case PlatformGMGN: + if !checkPumpFunGmgnBuy(rawTx) { + platform = PlatformFake + } } } } @@ -196,6 +218,29 @@ func (s Swap) CheckEntryContract() string { return EntryContractUnknown } +func (tx *Tx) LoadAfterSOLBalance(swap Swap) decimal.Decimal { + if swap.User.Equals(tx.Signer) { + return tx.AfterSOLBalance + } + + found := false + makerIndex := 0 + for i, account := range tx.rawTx.getAccountList() { + if account == swap.User { + found = true + makerIndex = i + break + } + } + + if found && makerIndex < len(tx.rawTx.Meta.PostBalances) { + return decimal.NewFromInt( + int64(tx.rawTx.Meta.PostBalances[makerIndex]), + ).Div(decimal.NewFromInt(1000000000)) // sol decimals + } + return decimal.Zero +} + func (s Swap) CheckEntryContractV2() string { name, ok := entryContractAddresses[s.EntryContract] if ok {