pump and pump swap errTx parser

This commit is contained in:
thloyi
2026-02-26 16:11:34 +08:00
parent 972ddc7960
commit b0d4342fa2
12 changed files with 704 additions and 722 deletions

View File

@@ -152,14 +152,29 @@ func pumpAmmParser(tx *Tx, instruction Instruction, innerInstructions InnerInstr
discriminator := *(*[8]byte)(decode[:8])
switch discriminator {
case pumpAmmCreateDiscriminator:
if tx.Err != nil {
return nil, increaseOffset(offset), InstructionIgnoredError
}
return ammCreatePoolParser(tx, instruction, innerInstructions, offset)
case pumpAmmBuyDiscriminator, pumpAmmBuyV2Discriminator:
if tx.Err != nil {
return failedTxAmmBuyParser(tx, instruction, innerInstructions, offset)
}
return ammBuyParser(tx, instruction, innerInstructions, offset)
case pumpAmmSellDiscriminator:
if tx.Err != nil {
return failedTxAmmSellParser(tx, instruction, innerInstructions, offset)
}
return ammSellParser(tx, instruction, innerInstructions, offset)
case pumpAmmDepositDiscriminator:
if tx.Err != nil {
return nil, increaseOffset(offset), InstructionIgnoredError
}
return depositParse(tx, instruction, innerInstructions, offset)
case pumpAmmWithdrawDiscriminator:
if tx.Err != nil {
return nil, increaseOffset(offset), InstructionIgnoredError
}
return withdrawParse(tx, instruction, innerInstructions, offset)
default:
return nil, increaseOffset(offset), InstructionIgnoredError
@@ -240,6 +255,254 @@ func ammCreatePoolParser(tx *Tx, instruction Instruction, innerInstructions Inne
}
type PumpSwapArgs struct {
Discriminator [8]byte
Amount1 uint64
Amount2 uint64
}
func failedTxAmmBuyParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
if tx.Err == nil || tx.Err.UnKnown != "" {
return nil, increaseOffset(offset), fmt.Errorf("tx pump amm sell failed but error is nil, offset, %d, %d", offset[0], offset[1])
}
if tx.Err.Variant != InstructionError {
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but error variant is not instruction error, offset, %d, %d", offset[0], offset[1])
}
if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded {
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but error is not custom or computational budget exceeded, offset, %d, %d", offset[0], offset[1])
}
if tx.Err.Enum == Custom {
if !(tx.Err.CustomCode == 1 || tx.Err.CustomCode == 6004 ||
tx.Err.CustomCode == 6040 ||
tx.Err.CustomCode == 6039 ||
tx.Err.CustomCode == 6016 ||
tx.Err.CustomCode == 6014) {
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but custom error code is unexpected, offset, %d, %d, code: %d", offset[0], offset[1], tx.Err.CustomCode)
}
}
result := tx.rawTx
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
var args PumpSwapArgs
err := agbinary.NewBorshDecoder(instruction.Data[:]).Decode(&args)
if err != nil {
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm buy failed decode error: %v, offset, %d, %d", err, offset[0], offset[1])
}
var event string
var (
quoteAmount, tokenAmount uint64
)
if bytes.Equal(args.Discriminator[:], pumpAmmBuyV2Discriminator[:]) {
event = "buy_failed"
quoteAmount = args.Amount1
tokenAmount = args.Amount2
} else if bytes.Equal(args.Discriminator[:], pumpAmmBuyDiscriminator[:]) {
event = "buy_failed"
quoteAmount = args.Amount2
tokenAmount = args.Amount1
} else {
return nil, increaseOffset(offset), fmt.Errorf("unknown pump amm trade instruction discriminator, offset, %d, %d", offset[0], offset[1])
}
baseMint := result.accountList[instruction.Accounts[3]]
quoteMint := result.accountList[instruction.Accounts[4]]
baseTokenProgram := result.accountList[instruction.Accounts[11]]
quoteTokenProgram := result.accountList[instruction.Accounts[12]]
poolBaseAccountIdx := instruction.Accounts[7]
poolQuoteAccountIdx := instruction.Accounts[8]
var (
baseMintDecimals uint8
quoteMintDecimals uint8
)
for _, meta := range result.Meta.PostTokenBalances {
if meta.AccountIndex == poolBaseAccountIdx {
baseMintDecimals = uint8(meta.UITokenAmount.Decimals)
} else if meta.AccountIndex == poolQuoteAccountIdx {
quoteMintDecimals = uint8(meta.UITokenAmount.Decimals)
}
}
if _, exists := tx.Token[baseMint]; !exists && !baseMint.Equals(wSolMint) {
tx.Token[baseMint] = TokenMeta{
Mint: baseMint,
Decimals: baseMintDecimals,
TokenProgram: baseTokenProgram,
}
}
if _, exists := tx.Token[quoteMint]; !exists && !quoteMint.Equals(wSolMint) {
tx.Token[quoteMint] = TokenMeta{
Mint: quoteMint,
Decimals: quoteMintDecimals,
TokenProgram: quoteTokenProgram,
}
}
var eventUser = tx.rawTx.accountList[instruction.Accounts[1]]
baseMintAtaUserIdx := instruction.Accounts[5]
userIndex := instruction.Accounts[1]
if !eventUser.IsOnCurve() && (entryContract.Equals(okxDexRoutersV2) || entryContract.Equals(okxAggregatorV2)) {
userBaseAmount, ataIndex := tokenBalanceChange(result, 0, baseTokenProgram, baseMint)
// && userBaseAmount.BigInt().Uint64() == event.BaseAmountOut
if !userBaseAmount.IsZero() {
eventUser = result.accountList[0]
userIndex = 0
baseMintAtaUserIdx = ataIndex
}
}
userBase := getAccountBalanceAfterTx(result, baseMintAtaUserIdx)
userQuote := GetTokenBalanceAfterTx(result, userIndex, quoteTokenProgram, quoteMint)
if quoteMint.Equals(wSolMint) {
userBalance, _ := GetSolAfterTx(result, userIndex)
userQuote = userQuote.Add(decimal.NewFromUint64(userBalance))
}
baseReserve := getAccountBalanceAfterTx(result, instruction.Accounts[7])
quoteReserve := getAccountBalanceAfterTx(result, instruction.Accounts[8])
return []Swap{
{
Program: SolProgramPumpAMM,
Event: event,
Pool: tx.rawTx.accountList[instruction.Accounts[0]],
BaseMint: baseMint,
QuoteMint: quoteMint,
BaseTokenProgram: baseTokenProgram,
QuoteTokenProgram: quoteTokenProgram,
BaseMintDecimals: baseMintDecimals,
QuoteMintDecimals: quoteMintDecimals,
User: eventUser,
BaseAmount: decimal.NewFromUint64(tokenAmount),
QuoteAmount: decimal.NewFromUint64(quoteAmount),
BaseReserve: baseReserve,
QuoteReserve: quoteReserve,
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
UserBaseBalance: userBase,
UserQuoteBalance: userQuote,
EntryContract: entryContract,
},
}, offset, nil
}
func failedTxAmmSellParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
if tx.Err == nil || tx.Err.UnKnown != "" {
return nil, increaseOffset(offset), fmt.Errorf("tx pump amm sell failed but error is nil, offset, %d, %d", offset[0], offset[1])
}
if tx.Err.Variant != InstructionError {
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but error variant is not instruction error, offset, %d, %d", offset[0], offset[1])
}
if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded {
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but error is not custom or computational budget exceeded, offset, %d, %d", offset[0], offset[1])
}
if tx.Err.Enum == Custom {
if !(tx.Err.CustomCode == 1 || tx.Err.CustomCode == 6004 ||
tx.Err.CustomCode == 6040 ||
tx.Err.CustomCode == 6039 ||
tx.Err.CustomCode == 6016 ||
tx.Err.CustomCode == 6014) {
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but custom error code is unexpected, offset, %d, %d, code: %d", offset[0], offset[1], tx.Err.CustomCode)
}
}
result := tx.rawTx
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
var args PumpSwapArgs
err := agbinary.NewBorshDecoder(instruction.Data[:]).Decode(&args)
if err != nil {
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm buy failed decode error: %v, offset, %d, %d", err, offset[0], offset[1])
}
var event string
var (
quoteAmount, tokenAmount uint64
)
if bytes.Equal(args.Discriminator[:], pumpAmmSellDiscriminator[:]) {
event = "sell_failed"
tokenAmount = args.Amount1
quoteAmount = args.Amount2
} else {
return nil, increaseOffset(offset), fmt.Errorf("unknown pump amm trade instruction discriminator, offset, %d, %d", offset[0], offset[1])
}
baseMint := result.accountList[instruction.Accounts[3]]
quoteMint := result.accountList[instruction.Accounts[4]]
baseTokenProgram := result.accountList[instruction.Accounts[11]]
quoteTokenProgram := result.accountList[instruction.Accounts[12]]
poolBaseAccountIdx := instruction.Accounts[7]
poolQuoteAccountIdx := instruction.Accounts[8]
var (
baseMintDecimals uint8
quoteMintDecimals uint8
)
for _, meta := range result.Meta.PostTokenBalances {
if meta.AccountIndex == poolBaseAccountIdx {
baseMintDecimals = uint8(meta.UITokenAmount.Decimals)
} else if meta.AccountIndex == poolQuoteAccountIdx {
quoteMintDecimals = uint8(meta.UITokenAmount.Decimals)
}
}
if _, exists := tx.Token[baseMint]; !exists && !baseMint.Equals(wSolMint) {
tx.Token[baseMint] = TokenMeta{
Mint: baseMint,
Decimals: baseMintDecimals,
TokenProgram: baseTokenProgram,
}
}
if _, exists := tx.Token[quoteMint]; !exists && !quoteMint.Equals(wSolMint) {
tx.Token[quoteMint] = TokenMeta{
Mint: quoteMint,
Decimals: quoteMintDecimals,
TokenProgram: quoteTokenProgram,
}
}
var eventUser = tx.rawTx.accountList[instruction.Accounts[1]]
baseMintAtaUserIdx := instruction.Accounts[5]
userIndex := instruction.Accounts[1]
if !eventUser.IsOnCurve() && (entryContract.Equals(okxDexRoutersV2) || entryContract.Equals(okxAggregatorV2)) {
userBaseAmount, ataIndex := tokenBalanceChange(result, 0, baseTokenProgram, baseMint)
// && userBaseAmount.BigInt().Uint64() == event.BaseAmountIn
if !userBaseAmount.IsZero() {
eventUser = result.accountList[0]
userIndex = 0
baseMintAtaUserIdx = ataIndex
}
}
userBase := getAccountBalanceAfterTx(result, baseMintAtaUserIdx)
userQuote := GetTokenBalanceAfterTx(result, userIndex, quoteTokenProgram, quoteMint)
if quoteMint.Equals(wSolMint) {
userBalance, _ := GetSolAfterTx(result, userIndex)
userQuote = userQuote.Add(decimal.NewFromUint64(userBalance))
}
baseReserve := getAccountBalanceAfterTx(result, instruction.Accounts[7])
quoteReserve := getAccountBalanceAfterTx(result, instruction.Accounts[8])
return []Swap{
{
Program: SolProgramPumpAMM,
Event: event,
Pool: tx.rawTx.accountList[instruction.Accounts[0]],
BaseMint: baseMint,
QuoteMint: quoteMint,
BaseTokenProgram: baseTokenProgram,
QuoteTokenProgram: quoteTokenProgram,
BaseMintDecimals: baseMintDecimals,
QuoteMintDecimals: quoteMintDecimals,
User: eventUser,
BaseAmount: decimal.NewFromUint64(tokenAmount),
QuoteAmount: decimal.NewFromUint64(quoteAmount),
BaseReserve: baseReserve,
QuoteReserve: quoteReserve,
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
UserBaseBalance: userBase,
UserQuoteBalance: userQuote,
EntryContract: entryContract,
},
}, offset, nil
}
func ammBuyParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
result := tx.rawTx
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]