chore: add gmgn and remove entry contract

This commit is contained in:
2026-01-08 12:54:21 +08:00
parent 35c57c3c7a
commit 26e07ec52e
2 changed files with 91 additions and 40 deletions

View File

@@ -45,7 +45,6 @@ type TxSignal struct {
IsToken2022 bool `json:"is_token2022"`
IsMayhemMode bool `json:"is_mayhem_mode"`
TxFee decimal.Decimal `json:"tx_fee"`
EntryContract string `json:"entry_contract"`
ExactSOL bool `json:"exact_in"`

View File

@@ -23,42 +23,30 @@ const (
// program ids
var (
pumpProgramID = solana.MustPublicKeyFromBase58("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P")
pumpProgramIDString = pumpProgramID.String()
// has no sell function with pump and pump.amm program
azczProgramID = solana.MustPublicKeyFromBase58("AzcZqCRUQgKEg5FTAgY7JacATABEYCEfMbjXEzspLYFB")
azczProgramIDString = azczProgramID.String()
// only buy function with pump program
f5tfProgramID = solana.MustPublicKeyFromBase58("F5tfvbLog9VdGUPqBDTT8rgXvTTcq7e5UiGnupL1zvBq")
f5tfProgramIDString = f5tfProgramID.String()
// only pump.fun function
photonProgramID = solana.MustPublicKeyFromBase58("BSfD6SHZigAfDWSjzD5Q41jw8LmKwtmjskPH9XW1mrRW")
photonProgramIDString = photonProgramID.String()
pumpAmmProgramID = solana.MustPublicKeyFromBase58("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA")
pumpAmmProgramIDString = pumpAmmProgramID.String()
boboProgramID = solana.MustPublicKeyFromBase58("BobogA5N2KN2GG4XN3E3rNNRw3L8H1QPXp7QLxGrNHGM")
boboProgramIDString = boboProgramID.String()
qtkvProgramID = solana.MustPublicKeyFromBase58("qtkvapJEvRWWrB7i5K6RaA1kvq5x3qmMKZ98ad71XQ7")
qtkvProgramIDString = qtkvProgramID.String()
// only buy function with pump program
fjszProgramID = solana.MustPublicKeyFromBase58("FJsZbftBqRLfF7uqUKpm4s2goDr6xsQ5Q3mN7AFJB6hK")
fjszProgramIDString = fjszProgramID.String()
flasProgramID = solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9")
flasProgramIDString = flasProgramID.String()
terminalProgramID = solana.MustPublicKeyFromBase58("term9YPb9mzAsABaqN71A4xdbxHmpBNZavpBiQKZzN3")
terminalProgramIDString = terminalProgramID.String()
jupiterV6ProgramID = solana.MustPublicKeyFromBase58("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4")
jupiterV6ProgramIDString = jupiterV6ProgramID.String()
gmgnProgramID = solana.MustPublicKeyFromBase58("GMgnVFR8Jb39LoXsEVzb3DvBy3ywCmdmJquHUy1Lrkqb")
gmgnProgramIDString = gmgnProgramID.String()
)
type AccountNotFoundError struct {
@@ -108,6 +96,8 @@ var (
terminalBuyTokensIX = []byte{0xa6, 0x54, 0x14, 0x96, 0x9f, 0x77, 0x59, 0xca}
terminalSellTokensIX = []byte{0xbe, 0x84, 0xa2, 0x96, 0x93, 0x7c, 0xf8, 0x6b}
terminalAmmSellTokensIX = []byte{0x40, 0x64, 0x97, 0xb9, 0x16, 0xfa, 0xec, 0xb1}
gmgnBuyTokensIX = []byte{0x66, 0x06, 0x3d, 0x12, 0x01, 0xda, 0xeb, 0xea}
)
type compiledInstruction struct {
@@ -312,50 +302,53 @@ func ParseTransaction(update *SubscribeUpdateTransaction, loader *AddressTables,
switch programID {
case pumpProgramID:
txRes, err := parsePumpInstruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "pump", pumpProgramIDString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "pump")
case azczProgramID:
txRes, err := parseAzczInstruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "azcz", azczProgramIDString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "azcz")
case f5tfProgramID:
txRes, err := parseF5tfInstruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "f5tf", f5tfProgramIDString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "f5tf")
case flasProgramID:
txRes, err := parseFlasInstruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "flas", flasProgramIDString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "flas")
case photonProgramID:
txRes, err := parsePhotonInstruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "photon", photonProgramIDString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "photon")
case pumpAmmProgramID:
txRes, err := parsePumpAmmInstruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "pumpamm", pumpAmmProgramIDString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "pumpamm")
case boboProgramID:
txRes, err := parseBoboInstruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "bobo", boboProgramIDString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "bobo")
case qtkvProgramID:
txRes, err := parseQtkvInstruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "qtkv", qtkvProgramIDString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "qtkv")
case fjszProgramID:
txRes, err := parseFjszInstruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "fjsz", fjszProgramIDString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "fjsz")
case terminalProgramID:
txRes, err := parseTermInstruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "terminal", terminalProgramIDString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "terminal")
case jupiterV6ProgramID:
txRes, err := parseJupiterV6Instruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "jupiterv6", jupiterV6ProgramIDString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "jupiterv6")
case okxDexRouteV2ProgramID:
txRes, err := parseOkxDexRouteV2Instruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "okxdexroutev2", okxDexRouteV2ProgramIDString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "okxdexroutev2")
case dflowProgramID:
txRes, err := parseDFlowInstruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "dflow", dflowProgramString)
parsed = appendParsed(now, parsed, txRes, err, txHash, "dflow")
case gmgnProgramID:
txRes, err := parseGMGNInstruction(versioned, i)
parsed = appendParsed(now, parsed, txRes, err, txHash, "gmgn")
}
}
return parsed
}
func appendParsed(start time.Time, list []*TxSignal, parsed *TxSignal, err error, txHash [64]byte, label string, entryContract string) []*TxSignal {
func appendParsed(start time.Time, list []*TxSignal, parsed *TxSignal, err error, txHash [64]byte, label string) []*TxSignal {
if err != nil {
if !strings.HasPrefix(err.Error(), "account index") {
logger.Debug("txparser: failed to parse", "label", label, "instruction", err, "tx_hash", base58.Encode(txHash[:]))
@@ -363,7 +356,6 @@ func appendParsed(start time.Time, list []*TxSignal, parsed *TxSignal, err error
return list
}
if parsed != nil {
parsed.EntryContract = entryContract
parsed.Label = label
if !start.IsZero() {
parsed.ParseEnd = time.Now()
@@ -1007,6 +999,66 @@ func parseFlasBuy(tx *versionedTransaction, instructionIndex int) (*TxSignal, er
}, nil
}
func parseGMGNInstruction(tx *versionedTransaction, instructionIndex int) (*TxSignal, error) {
msg := tx.Message
if instructionIndex >= len(msg.Instructions) {
return nil, fmt.Errorf("instruction index out of bounds")
}
instruction := msg.Instructions[instructionIndex]
if len(instruction.Data) == 0 {
return nil, fmt.Errorf("data is empty")
}
if len(instruction.Data) < 8 {
return nil, nil
}
if matchMethod(instruction.Data, gmgnBuyTokensIX) {
return parseGMGNBuy(tx, &instruction)
}
return nil, nil
}
func parseGMGNBuy(tx *versionedTransaction, instruction *compiledInstruction) (*TxSignal, error) {
if len(instruction.Accounts) < 8 {
return nil, fmt.Errorf("accounts too short")
}
if len(instruction.Data) < 24 {
return nil, fmt.Errorf("data too short for gmgn buy args, len=%d", len(instruction.Data))
}
staticKeys := tx.Message.StaticAccountKeys
mint, err := getStaticKey(staticKeys, int(instruction.Accounts[2]))
if err != nil {
return nil, err
}
user, err := getStaticKey(staticKeys, int(instruction.Accounts[6]))
if err != nil {
return nil, err
}
solAmount := binary.LittleEndian.Uint64(instruction.Data[8:16])
tokenAmount := binary.LittleEndian.Uint64(instruction.Data[16:24])
return &TxSignal{
TxHash: tx.Signatures[0].String(),
Label: "gmgn",
Maker: user.String(),
Token0Address: mint.String(),
Token1Address: wsolMint,
Token0Amount: formatTokenAmount(tokenAmount),
Token1Amount: formatSolAmount(solAmount),
Program: "Pump",
Event: "buy",
IsToken2022: false,
IsMayhemMode: false,
ExactSOL: true,
Block: tx.Block,
Token0AmountUint64: tokenAmount,
Token1AmountUint64: solAmount,
}, nil
}
func parsePhotonInstruction(tx *versionedTransaction, instructionIndex int) (*TxSignal, error) {
msg := tx.Message
if instructionIndex >= len(msg.Instructions) {