pump and pump swap errTx parser
This commit is contained in:
263
pumpamm.go
263
pumpamm.go
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user