diff --git a/pkg/shreder/program_binancewallet.go b/pkg/shreder/program_binancewallet.go new file mode 100644 index 0000000..e2f0600 --- /dev/null +++ b/pkg/shreder/program_binancewallet.go @@ -0,0 +1,118 @@ +package shreder + +import ( + "bytes" + "encoding/binary" + "fmt" + + "github.com/gagliardetto/solana-go" +) + +var binanceWalletProgramID = solana.MustPublicKeyFromBase58("B3111yJCeHBcA1bizdJjUFPALfhAfSRnAbJzGUtnt56A") + +const ( + binanceWalletMinDataLen = 72 + binanceWalletSolOffset = 23 + binanceWalletTokenOff = 39 + binanceWalletSolRepeat = 51 + binanceWalletSideOff = 71 + + binanceWalletPumpBuy = 0x05 + binanceWalletPumpSell = 0x06 +) + +var binanceWalletMarker = []byte{0x13, 0x2c, 0x82, 0x94, 0x48, 0x38, 0x2c, 0xee} + +func parseBinanceWalletInstruction(tx VersionedTransaction, instructionIndex int) (TxSignalBatch, error) { + if instructionIndex >= len(tx.Instructions) { + return nil, fmt.Errorf("instruction index out of bounds") + } + + instruction := tx.Instructions[instructionIndex] + if len(instruction.Data) < len(binanceWalletMarker) || !bytes.Contains(instruction.Data, binanceWalletMarker) { + return nil, nil + } + if len(instruction.Data) < binanceWalletMinDataLen { + return nil, fmt.Errorf("data too short for binance wallet, len=%d", len(instruction.Data)) + } + + side := instruction.Data[binanceWalletSideOff] + if side != binanceWalletPumpBuy && side != binanceWalletPumpSell { + return nil, nil + } + + if len(instruction.Accounts) <= 8 { + return nil, fmt.Errorf("accounts too short") + } + + wsolIdx := 7 + tokenIdx := 8 + if side == binanceWalletPumpSell { + wsolIdx = 8 + tokenIdx = 7 + } + + wsolKey, err := tx.GetAccount(int(instruction.Accounts[wsolIdx])) + if err != nil { + return nil, err + } + if !wsolKey.Equals(solana.WrappedSol) { + return nil, nil + } + mint, err := tx.GetAccount(int(instruction.Accounts[tokenIdx])) + if err != nil { + return nil, err + } + + amountA := binary.LittleEndian.Uint64(instruction.Data[binanceWalletSolOffset : binanceWalletSolOffset+8]) + if amountA == 0 && len(instruction.Data) >= binanceWalletSolRepeat+8 { + repeat := binary.LittleEndian.Uint64(instruction.Data[binanceWalletSolRepeat : binanceWalletSolRepeat+8]) + if repeat > 0 { + amountA = repeat + } + } + amountB := binary.LittleEndian.Uint64(instruction.Data[binanceWalletTokenOff : binanceWalletTokenOff+8]) + + solAmount := amountA + tokenAmount := amountB + if side == binanceWalletPumpSell { + solAmount = amountB + tokenAmount = amountA + } + + maker := "" + if len(tx.StaticAccountKeys) > 0 { + maker = tx.StaticAccountKeys[0].String() + } else if len(instruction.Accounts) > 0 { + key, err := tx.GetAccount(int(instruction.Accounts[0])) + if err != nil { + return nil, err + } + maker = key.String() + } + + event := "buy" + exactIn := true + if side == binanceWalletPumpSell { + event = "sell" + exactIn = false + } + + return TxSignalBatch{&TxSignal{ + TxHash: tx.Signatures[0].String(), + Label: "binancewallet", + Maker: maker, + Token0Address: mint.String(), + Token1Address: wsolMint, + Token0Amount: formatTokenAmount(tokenAmount), + Token1Amount: formatSolAmount(solAmount), + Program: "Pump", + Event: event, + IsToken2022: false, + IsMayhemMode: false, + ExactSOL: exactIn, + Block: tx.Block, + Token0AmountUint64: tokenAmount, + Token1AmountUint64: solAmount, + }}, nil +} diff --git a/pkg/shreder/txparser.go b/pkg/shreder/txparser.go index a025676..1dd00b3 100644 --- a/pkg/shreder/txparser.go +++ b/pkg/shreder/txparser.go @@ -54,6 +54,7 @@ var ( flasProgramID: {parseFlasInstruction, "flas"}, photonProgramID: {parsePhotonInstruction, "photon"}, pumpAmmProgramID: {parsePumpAmmInstruction, "pumpamm"}, + binanceWalletProgramID: {parseBinanceWalletInstruction, "binancewallet"}, boboProgramID: {parseBoboInstruction, "bobo"}, qtkvProgramID: {parseQtkvInstruction, "qtkv"}, fjszProgramID: {parseFjszInstruction, "fjsz"},