2025-11-20 17:56:45 +08:00
package pump_parser
import (
"bytes"
"fmt"
agbinary "github.com/gagliardetto/binary"
"github.com/gagliardetto/solana-go"
"github.com/shopspring/decimal"
)
type ammBuyEvent struct {
TimeStamp int64
BaseAmountOut uint64
MaxQuoteAmountIn uint64
UserBaseTokenReserve uint64
UserQuoteTokenReserve uint64
PoolBaseTokenReserve uint64
PoolQuoteTokenReserve uint64
QuoteAmountIn uint64
LpFeeBasisPoints uint64
LpFee uint64
2025-12-02 15:51:53 +08:00
ProtocolFeeBasisPoints uint64
2025-11-20 17:56:45 +08:00
ProtocolFee uint64
QuoteAmountInWithLpFee uint64
UserQuoteAmountIn uint64
Pool solana . PublicKey
User solana . PublicKey
UserBaseTokenAccount solana . PublicKey
UserQuoteTokenAccount solana . PublicKey
ProtocolFeeRecipient solana . PublicKey
ProtocolFeeRecipientTokenAccount solana . PublicKey
CoinCreator solana . PublicKey
2025-12-02 15:51:53 +08:00
CoinCreatorFeeBasisPoints uint64
CoinCreatorFee uint64
TrackVolume bool
TotalUnclaimedTokens uint64
TotalClaimedTokens uint64
CurrentSolVolume uint64
LastUpdateTimestamp int64
MinBaseAmountOut uint64
IxName string
2026-02-27 02:07:52 +08:00
CashbackFeeBasisPoints uint64
Cashback uint64
2025-11-20 17:56:45 +08:00
}
type ammCreatePoolEvent struct {
TimeStamp int64
Index uint16
Creator solana . PublicKey
BaseMint solana . PublicKey
QuoteMint solana . PublicKey
BaseMintDecimals uint8
QuoteMintDecimals uint8
BaseAmountIn uint64
QuoteAmountIn uint64
PoolBaseAmount uint64
PoolQuoteAmount uint64
MinimumLiquidity uint64
InitialLiquidity uint64
LpTokenAmountOut uint64
PoolBump uint8
Pool solana . PublicKey
LpMint solana . PublicKey
UserBaseTokenAccount solana . PublicKey
UserQuoteTokenAccount solana . PublicKey
CoinCreator solana . PublicKey
IsMayhemMode bool
}
type ammDepositEvent struct {
TimeStamp int64
LpTokenAmountOut uint64
MaxBaseAmountIn uint64
MaxQuoteAmountIn uint64
UserBaseTokenReserves uint64
UserQuoteTokenReserves uint64
PoolBaseTokenReserves uint64
PoolQuoteTokenReserves uint64
BaseAmountIn uint64
QuoteAmountIn uint64
LpMintSupply uint64
Pool solana . PublicKey
User solana . PublicKey
UserBaseTokenAccount solana . PublicKey
UserQuoteTokenAccount solana . PublicKey
UserPoolTokenAccount solana . PublicKey
}
type ammSellEvent struct {
Timestamp int64
BaseAmountIn uint64
MinQuoteAmountOut uint64
UserBaseTokenReserves uint64
UserQuoteTokenReserves uint64
PoolBaseTokenReserves uint64
PoolQuoteTokenReserves uint64
QuoteAmountOut uint64
LpFeeBasisPoints uint64
LpFee uint64
ProtocolFeeBasisPoints uint64
ProtocolFee uint64
QuoteAmountOutWithoutLpFee uint64
UserQuoteAmountOut uint64
Pool solana . PublicKey
User solana . PublicKey
UserBaseTokenAccount solana . PublicKey
UserQuoteTokenAccount solana . PublicKey
ProtocolFeeRecipient solana . PublicKey
ProtocolFeeRecipientTokenAccount solana . PublicKey
CoinCreator solana . PublicKey
2025-12-02 15:51:53 +08:00
CoinCreatorFeeBasisPoints uint64
CoinCreatorFee uint64
2026-02-27 02:07:52 +08:00
CashbackFeeBasisPoints uint64
Cashback uint64
2025-11-20 17:56:45 +08:00
}
type ammWithdrawEvent struct {
Timestamp int64
LpTokenAmountIn uint64
MinBaseAmountOut uint64
MinQuoteAmountOut uint64
UserBaseTokenReserves uint64
UserQuoteTokenReserves uint64
PoolBaseTokenReserves uint64
PoolQuoteTokenReserves uint64
BaseAmountOut uint64
QuoteAmountOut uint64
LpMintSupply uint64
Pool solana . PublicKey
User solana . PublicKey
UserBaseTokenAccount solana . PublicKey
UserQuoteTokenAccount solana . PublicKey
UserPoolTokenAccount solana . PublicKey
}
2025-11-21 12:01:44 +08:00
func pumpAmmParser ( tx * Tx , instruction Instruction , innerInstructions InnerInstructions , offset [ 2 ] uint ) ( [ ] Swap , [ 2 ] uint , error ) {
if ! tx . rawTx . accountList [ instruction . ProgramIDIndex ] . Equals ( pumpAmmProgram ) {
2025-11-20 17:56:45 +08:00
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump amm program instruction not found, offset: %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
decode := instruction . Data
if len ( decode ) < 8 {
offset [ 1 ] += 1
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pumpamm program instruction data too short, offset: %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
discriminator := * ( * [ 8 ] byte ) ( decode [ : 8 ] )
switch discriminator {
case pumpAmmCreateDiscriminator :
2026-02-26 16:11:34 +08:00
if tx . Err != nil {
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
2025-11-21 12:01:44 +08:00
return ammCreatePoolParser ( tx , instruction , innerInstructions , offset )
2025-12-23 14:37:12 +08:00
case pumpAmmBuyDiscriminator , pumpAmmBuyV2Discriminator :
2026-02-26 16:11:34 +08:00
if tx . Err != nil {
return failedTxAmmBuyParser ( tx , instruction , innerInstructions , offset )
}
2025-11-21 12:01:44 +08:00
return ammBuyParser ( tx , instruction , innerInstructions , offset )
2025-11-20 17:56:45 +08:00
case pumpAmmSellDiscriminator :
2026-02-26 16:11:34 +08:00
if tx . Err != nil {
return failedTxAmmSellParser ( tx , instruction , innerInstructions , offset )
}
2025-11-21 12:01:44 +08:00
return ammSellParser ( tx , instruction , innerInstructions , offset )
2025-11-20 17:56:45 +08:00
case pumpAmmDepositDiscriminator :
2026-02-26 16:11:34 +08:00
if tx . Err != nil {
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
2025-11-21 12:01:44 +08:00
return depositParse ( tx , instruction , innerInstructions , offset )
2025-11-20 17:56:45 +08:00
case pumpAmmWithdrawDiscriminator :
2026-02-26 16:11:34 +08:00
if tx . Err != nil {
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
2025-11-21 12:01:44 +08:00
return withdrawParse ( tx , instruction , innerInstructions , offset )
2025-11-20 17:56:45 +08:00
default :
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
}
2025-11-21 12:01:44 +08:00
func ammCreatePoolParser ( tx * Tx , instruction Instruction , innerInstructions InnerInstructions , offset [ 2 ] uint ) ( [ ] Swap , [ 2 ] uint , error ) {
2026-05-28 10:23:56 +08:00
if len ( instruction . Accounts ) < 15 {
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
2025-11-21 12:01:44 +08:00
result := tx . rawTx
2025-11-20 17:56:45 +08:00
var entryContract = result . accountList [ result . Transaction . Message . Instructions [ offset [ 0 ] ] . ProgramIDIndex ]
var err error
var prefixLen = offset [ 1 ]
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pumpamm create get inner instructions error: %v, offset: %d, %d" , err , offset [ 0 ] , prefixLen )
}
var createEvent ammCreatePoolEvent
for innerIndex , innerInstr := range inners {
if innerInstr . ProgramIDIndex == instruction . ProgramIDIndex &&
bytes . Equal ( innerInstr . Data [ : 8 ] , pumpAmmEventDiscriminator [ : ] ) &&
bytes . Equal ( innerInstr . Data [ 8 : 16 ] , pumpAmmCreateEventDiscriminator [ : ] ) {
err = agbinary . NewBorshDecoder ( innerInstr . Data [ 16 : ] ) . Decode ( & createEvent )
if offset [ 1 ] == 0 {
offset [ 0 ] += 1
} else {
2026-02-09 14:46:19 +08:00
offset [ 1 ] = uint ( innerIndex ) + 1 + prefixLen
2025-11-20 17:56:45 +08:00
}
if err != nil {
return nil , offset , fmt . Errorf ( "pump amm create pool event decode error: %v, offset: %d, %d" , err , offset [ 0 ] , prefixLen )
}
break
}
}
if createEvent == ( ammCreatePoolEvent { } ) {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump amm create pool event not found, offset: %d, %d" , offset [ 0 ] , prefixLen )
}
baseTokenProgram := result . accountList [ instruction . Accounts [ 13 ] ]
quoteTokenProgram := result . accountList [ instruction . Accounts [ 14 ] ]
2025-11-21 12:01:44 +08:00
if _ , exists := tx . Token [ createEvent . BaseMint ] ; ! exists && ! createEvent . BaseMint . Equals ( wSolMint ) {
tx . Token [ createEvent . BaseMint ] = TokenMeta {
Mint : createEvent . BaseMint ,
Decimals : createEvent . BaseMintDecimals ,
TokenProgram : baseTokenProgram ,
}
}
if _ , exists := tx . Token [ createEvent . QuoteMint ] ; ! exists && ! createEvent . QuoteMint . Equals ( wSolMint ) {
tx . Token [ createEvent . QuoteMint ] = TokenMeta {
Mint : createEvent . QuoteMint ,
Decimals : createEvent . QuoteMintDecimals ,
TokenProgram : quoteTokenProgram ,
}
}
2025-11-20 17:56:45 +08:00
return [ ] Swap {
{
Program : SolProgramPumpAMM ,
Event : "create" ,
Pool : createEvent . Pool ,
BaseMint : createEvent . BaseMint ,
QuoteMint : createEvent . QuoteMint ,
BaseTokenProgram : baseTokenProgram ,
QuoteTokenProgram : quoteTokenProgram ,
Creator : createEvent . CoinCreator ,
BaseMintDecimals : createEvent . BaseMintDecimals ,
QuoteMintDecimals : createEvent . QuoteMintDecimals ,
User : createEvent . Creator ,
BaseAmount : decimal . NewFromUint64 ( createEvent . BaseAmountIn ) ,
QuoteAmount : decimal . NewFromUint64 ( createEvent . QuoteAmountIn ) ,
BaseReserve : decimal . NewFromUint64 ( createEvent . PoolBaseAmount ) ,
QuoteReserve : decimal . NewFromUint64 ( createEvent . PoolQuoteAmount ) ,
UserBaseBalance : decimal . Decimal { } ,
UserQuoteBalance : decimal . Decimal { } ,
EntryContract : entryContract ,
Mayhem : createEvent . IsMayhemMode ,
} ,
} , offset , nil
}
2026-02-26 16:11:34 +08:00
type PumpSwapArgs struct {
Discriminator [ 8 ] byte
Amount1 uint64
Amount2 uint64
}
2026-04-16 14:24:14 +08:00
func pumpAmmSwapAmountInfoFromArgs ( args PumpSwapArgs ) ( swapMode SwapMode , fixedAmount decimal . Decimal , limitAmount decimal . Decimal , ok bool ) {
switch {
case bytes . Equal ( args . Discriminator [ : ] , pumpAmmBuyV2Discriminator [ : ] ) :
return SwapModeExactIn , decimal . NewFromUint64 ( args . Amount1 ) , decimal . NewFromUint64 ( args . Amount2 ) , true
case bytes . Equal ( args . Discriminator [ : ] , pumpAmmBuyDiscriminator [ : ] ) :
return SwapModeExactOut , decimal . NewFromUint64 ( args . Amount1 ) , decimal . NewFromUint64 ( args . Amount2 ) , true
case bytes . Equal ( args . Discriminator [ : ] , pumpAmmSellDiscriminator [ : ] ) :
return SwapModeExactIn , decimal . NewFromUint64 ( args . Amount1 ) , decimal . NewFromUint64 ( args . Amount2 ) , true
default :
return SwapModeUnknown , decimal . Zero , decimal . Zero , false
}
}
2026-02-26 16:11:34 +08:00
func failedTxAmmBuyParser ( tx * Tx , instruction Instruction , innerInstructions InnerInstructions , offset [ 2 ] uint ) ( [ ] Swap , [ 2 ] uint , error ) {
2026-05-28 10:23:56 +08:00
if len ( instruction . Accounts ) < 13 {
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
2026-02-26 16:11:34 +08:00
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 ] )
}
2026-03-02 15:47:11 +08:00
if tx . Err . Enum != Custom && tx . Err . Enum != ComputationalBudgetExceeded && tx . Err . Enum != ProgramFailedToComplete {
2026-02-26 16:11:34 +08:00
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 ] )
2026-04-16 14:24:14 +08:00
swap := 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 ,
}
if swapMode , fixedAmount , limitAmount , ok := pumpAmmSwapAmountInfoFromArgs ( args ) ; ok {
swap . SetSwapAmountInfo ( swapMode , fixedAmount , limitAmount )
}
return [ ] Swap { swap } , offset , nil
2026-02-26 16:11:34 +08:00
}
func failedTxAmmSellParser ( tx * Tx , instruction Instruction , innerInstructions InnerInstructions , offset [ 2 ] uint ) ( [ ] Swap , [ 2 ] uint , error ) {
2026-05-28 10:23:56 +08:00
if len ( instruction . Accounts ) < 13 {
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
2026-02-26 16:11:34 +08:00
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 ] )
}
2026-03-02 15:47:11 +08:00
if tx . Err . Enum != Custom && tx . Err . Enum != ComputationalBudgetExceeded && tx . Err . Enum != ProgramFailedToComplete {
2026-02-26 16:11:34 +08:00
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 ] )
2026-04-16 14:24:14 +08:00
swap := 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 ,
}
if swapMode , fixedAmount , limitAmount , ok := pumpAmmSwapAmountInfoFromArgs ( args ) ; ok {
swap . SetSwapAmountInfo ( swapMode , fixedAmount , limitAmount )
}
return [ ] Swap { swap } , offset , nil
2026-02-26 16:11:34 +08:00
}
2025-11-21 12:01:44 +08:00
func ammBuyParser ( tx * Tx , instruction Instruction , innerInstructions InnerInstructions , offset [ 2 ] uint ) ( [ ] Swap , [ 2 ] uint , error ) {
result := tx . rawTx
2025-11-20 17:56:45 +08:00
var entryContract = result . accountList [ result . Transaction . Message . Instructions [ offset [ 0 ] ] . ProgramIDIndex ]
var err error
var prefixLen = offset [ 1 ]
2026-05-28 10:23:56 +08:00
if len ( instruction . Accounts ) < 13 {
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
2025-11-20 17:56:45 +08:00
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pumpamm create get inner instructions error: %v, offset: %d, %d" , err , offset [ 0 ] , prefixLen )
}
2026-03-02 20:04:38 +08:00
if ! entryContract . Equals ( axiomOuterContract ) {
if instruction . StackHeight != nil && * instruction . StackHeight > 2 {
for _ , innerInstr := range innerInstructions . Instructions {
if innerInstr . StackHeight != nil && * innerInstr . StackHeight == * instruction . StackHeight - 1 {
entryContract = result . accountList [ innerInstr . ProgramIDIndex ]
break
}
2025-12-22 17:56:40 +08:00
}
}
}
2025-11-20 17:56:45 +08:00
var event ammBuyEvent
for innerIndex , innerInstr := range inners {
if innerInstr . ProgramIDIndex == instruction . ProgramIDIndex &&
bytes . Equal ( innerInstr . Data [ : 8 ] , pumpAmmEventDiscriminator [ : ] ) &&
bytes . Equal ( innerInstr . Data [ 8 : 16 ] , pumpAmmBuyEventDiscriminator [ : ] ) {
err = agbinary . NewBorshDecoder ( innerInstr . Data [ 16 : ] ) . Decode ( & event )
if offset [ 1 ] == 0 {
offset [ 0 ] += 1
} else {
2026-02-09 14:46:19 +08:00
offset [ 1 ] = uint ( innerIndex ) + 1 + prefixLen
2025-11-20 17:56:45 +08:00
}
if err != nil {
return nil , offset , fmt . Errorf ( "pump amm buy pool event decode error: %v, offset: %d, %d" , err , offset [ 0 ] , prefixLen )
}
break
}
}
if event == ( ammBuyEvent { } ) {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump amm buy event not found, offset: %d, %d" , offset [ 0 ] , prefixLen )
}
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 )
}
}
2025-11-21 12:01:44 +08:00
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 ,
}
}
2025-12-22 17:56:40 +08:00
var eventUser = event . User
baseMintAtaUserIdx := instruction . Accounts [ 5 ]
userIndex := instruction . Accounts [ 1 ]
if ! event . User . 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 ) )
}
2026-02-27 02:07:52 +08:00
isCashbackCoin := event . CashbackFeeBasisPoints > 0 || event . Cashback > 0
2026-04-27 14:36:03 +08:00
quoteAmount := decimal . NewFromUint64 ( event . UserQuoteAmountIn )
if event . IxName == "buy" {
quoteAmount = decimal . NewFromUint64 ( event . QuoteAmountIn )
}
2026-04-16 14:24:14 +08:00
swap := Swap {
Program : SolProgramPumpAMM ,
Event : "buy" ,
Pool : event . Pool ,
BaseMint : baseMint ,
QuoteMint : quoteMint ,
BaseTokenProgram : baseTokenProgram ,
QuoteTokenProgram : quoteTokenProgram ,
Creator : event . CoinCreator ,
BaseMintDecimals : baseMintDecimals ,
QuoteMintDecimals : quoteMintDecimals ,
User : eventUser ,
BaseAmount : decimal . NewFromUint64 ( event . BaseAmountOut ) ,
2026-04-27 14:36:03 +08:00
QuoteAmount : quoteAmount ,
2026-04-16 14:24:14 +08:00
BaseReserve : decimal . NewFromUint64 ( event . PoolBaseTokenReserve - event . BaseAmountOut ) ,
QuoteReserve : decimal . NewFromUint64 ( event . PoolQuoteTokenReserve + event . QuoteAmountIn ) ,
Mayhem : isMayhemPump ( result . accountList [ instruction . Accounts [ 9 ] ] ) ,
Cashback : isCashbackCoin ,
UserBaseBalance : userBase ,
UserQuoteBalance : userQuote ,
EntryContract : entryContract ,
}
if bytes . Equal ( instruction . Data [ : 8 ] , pumpAmmBuyV2Discriminator [ : ] ) {
swap . SetSwapAmountInfo (
SwapModeExactIn ,
decimal . NewFromUint64 ( event . UserQuoteAmountIn ) ,
decimal . NewFromUint64 ( event . MinBaseAmountOut ) ,
)
} else {
swap . SetSwapAmountInfo (
SwapModeExactOut ,
decimal . NewFromUint64 ( event . BaseAmountOut ) ,
decimal . NewFromUint64 ( event . MaxQuoteAmountIn ) ,
)
}
return [ ] Swap { swap } , offset , nil
2025-11-20 17:56:45 +08:00
}
2025-11-21 12:01:44 +08:00
func ammSellParser ( tx * Tx , instruction Instruction , innerInstructions InnerInstructions , offset [ 2 ] uint ) ( [ ] Swap , [ 2 ] uint , error ) {
result := tx . rawTx
2025-11-20 17:56:45 +08:00
var entryContract = result . accountList [ result . Transaction . Message . Instructions [ offset [ 0 ] ] . ProgramIDIndex ]
var err error
2026-05-28 10:23:56 +08:00
if len ( instruction . Accounts ) < 13 {
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
2025-11-20 17:56:45 +08:00
var prefixLen = offset [ 1 ]
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pumpamm sell get inner instructions error: %v, offset: %d, %d" , err , offset [ 0 ] , prefixLen )
}
2025-12-22 17:56:40 +08:00
2026-03-02 20:04:38 +08:00
if ! entryContract . Equals ( axiomOuterContract ) {
if instruction . StackHeight != nil && * instruction . StackHeight > 2 {
for _ , innerInstr := range innerInstructions . Instructions {
if innerInstr . StackHeight != nil && * innerInstr . StackHeight == * instruction . StackHeight - 1 {
entryContract = result . accountList [ innerInstr . ProgramIDIndex ]
break
}
2025-12-22 17:56:40 +08:00
}
}
}
2025-11-20 17:56:45 +08:00
var event ammSellEvent
for innerIndex , innerInstr := range inners {
if innerInstr . ProgramIDIndex == instruction . ProgramIDIndex &&
bytes . Equal ( innerInstr . Data [ : 8 ] , pumpAmmEventDiscriminator [ : ] ) &&
bytes . Equal ( innerInstr . Data [ 8 : 16 ] , pumpAmmSellEventDiscriminator [ : ] ) {
err = agbinary . NewBorshDecoder ( innerInstr . Data [ 16 : ] ) . Decode ( & event )
if offset [ 1 ] == 0 {
offset [ 0 ] += 1
} else {
2026-02-09 14:46:19 +08:00
offset [ 1 ] = uint ( innerIndex ) + 1 + prefixLen
2025-11-20 17:56:45 +08:00
}
if err != nil {
return nil , offset , fmt . Errorf ( "pump amm sell pool event decode error: %v, offset: %d, %d" , err , offset [ 0 ] , prefixLen )
}
break
}
}
if event == ( ammSellEvent { } ) {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump amm sell event not found, offset: %d, %d" , offset [ 0 ] , prefixLen )
}
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 )
}
}
2025-11-21 12:01:44 +08:00
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 ,
}
}
2025-12-22 17:56:40 +08:00
var eventUser = event . User
baseMintAtaUserIdx := instruction . Accounts [ 5 ]
userIndex := instruction . Accounts [ 1 ]
if ! event . User . 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 ) )
}
2026-02-27 02:07:52 +08:00
isCashbackCoin := event . CashbackFeeBasisPoints > 0 || event . Cashback > 0
2026-04-16 14:24:14 +08:00
swap := Swap {
Program : SolProgramPumpAMM ,
Event : "sell" ,
Pool : event . Pool ,
BaseMint : baseMint ,
QuoteMint : quoteMint ,
BaseTokenProgram : baseTokenProgram ,
QuoteTokenProgram : quoteTokenProgram ,
Creator : event . CoinCreator ,
BaseMintDecimals : baseMintDecimals ,
QuoteMintDecimals : quoteMintDecimals ,
User : eventUser ,
BaseAmount : decimal . NewFromUint64 ( event . BaseAmountIn ) ,
QuoteAmount : decimal . NewFromUint64 ( event . UserQuoteAmountOut ) ,
BaseReserve : decimal . NewFromUint64 ( event . PoolBaseTokenReserves + event . BaseAmountIn ) ,
QuoteReserve : decimal . NewFromUint64 ( event . PoolQuoteTokenReserves - event . QuoteAmountOut ) ,
Mayhem : isMayhemPump ( result . accountList [ instruction . Accounts [ 9 ] ] ) ,
Cashback : isCashbackCoin ,
UserBaseBalance : userBase ,
UserQuoteBalance : userQuote ,
EntryContract : entryContract ,
}
swap . SetSwapAmountInfo (
SwapModeExactIn ,
decimal . NewFromUint64 ( event . BaseAmountIn ) ,
decimal . NewFromUint64 ( event . MinQuoteAmountOut ) ,
)
return [ ] Swap { swap } , offset , nil
2025-11-20 17:56:45 +08:00
}
2025-11-21 12:01:44 +08:00
func depositParse ( tx * Tx , instruction Instruction , innerInstructions InnerInstructions , offset [ 2 ] uint ) ( [ ] Swap , [ 2 ] uint , error ) {
result := tx . rawTx
2025-11-20 17:56:45 +08:00
var entryContract = result . accountList [ result . Transaction . Message . Instructions [ offset [ 0 ] ] . ProgramIDIndex ]
var err error
2026-05-28 10:23:56 +08:00
if len ( instruction . Accounts ) < 11 {
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
2025-11-20 17:56:45 +08:00
var prefixLen = offset [ 1 ]
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pumpamm deposit get inner instructions error: %v, offset: %d, %d" , err , offset [ 0 ] , prefixLen )
}
var event ammDepositEvent
for innerIndex , innerInstr := range inners {
if innerInstr . ProgramIDIndex == instruction . ProgramIDIndex &&
bytes . Equal ( innerInstr . Data [ : 8 ] , pumpAmmEventDiscriminator [ : ] ) &&
bytes . Equal ( innerInstr . Data [ 8 : 16 ] , pumpAmmDepositEventDiscriminator [ : ] ) {
err = agbinary . NewBorshDecoder ( innerInstr . Data [ 16 : ] ) . Decode ( & event )
if offset [ 1 ] == 0 {
offset [ 0 ] += 1
} else {
2026-02-09 14:46:19 +08:00
offset [ 1 ] = uint ( innerIndex ) + 1 + prefixLen
2025-11-20 17:56:45 +08:00
}
if err != nil {
return nil , offset , fmt . Errorf ( "pump amm deposit pool event decode error: %v, offset: %d, %d" , err , offset [ 0 ] , prefixLen )
}
break
}
}
if event == ( ammDepositEvent { } ) {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump amm deposit event not found, offset: %d, %d" , offset [ 0 ] , prefixLen )
}
var (
poolBaseAccountIdx = instruction . Accounts [ 9 ]
poolQuoteAccountIdx = instruction . Accounts [ 10 ]
baseMintDecimals uint8
quoteMintDecimals uint8
baseMintProgram solana . PublicKey
quoteMintProgram solana . PublicKey
)
for _ , meta := range result . Meta . PostTokenBalances {
if meta . AccountIndex == poolBaseAccountIdx {
baseMintDecimals = uint8 ( meta . UITokenAmount . Decimals )
baseMintProgram = meta . ProgramIDAccount
if baseMintProgram . IsZero ( ) && meta . ProgramID != "" {
baseMintProgram = solana . MustPublicKeyFromBase58 ( meta . ProgramID )
}
}
if meta . AccountIndex == poolQuoteAccountIdx {
quoteMintDecimals = uint8 ( meta . UITokenAmount . Decimals )
quoteMintProgram = meta . ProgramIDAccount
if quoteMintProgram . IsZero ( ) && meta . ProgramID != "" {
quoteMintProgram = solana . MustPublicKeyFromBase58 ( meta . ProgramID )
}
}
}
2025-11-21 12:01:44 +08:00
baseMint := result . accountList [ instruction . Accounts [ 3 ] ]
quoteMint := result . accountList [ instruction . Accounts [ 4 ] ]
if _ , exists := tx . Token [ baseMint ] ; ! exists && ! baseMint . Equals ( wSolMint ) {
tx . Token [ baseMint ] = TokenMeta {
Mint : baseMint ,
Decimals : baseMintDecimals ,
TokenProgram : baseMintProgram ,
}
}
2025-11-20 17:56:45 +08:00
2025-11-21 12:01:44 +08:00
if _ , exists := tx . Token [ quoteMint ] ; ! exists && ! quoteMint . Equals ( wSolMint ) {
tx . Token [ quoteMint ] = TokenMeta {
Mint : quoteMint ,
Decimals : quoteMintDecimals ,
TokenProgram : quoteMintProgram ,
}
}
2025-11-20 17:56:45 +08:00
return [ ] Swap {
{
Program : SolProgramPumpAMM ,
Event : "deposit" ,
Pool : event . Pool ,
2025-11-21 12:01:44 +08:00
BaseMint : baseMint ,
QuoteMint : quoteMint ,
2025-11-20 17:56:45 +08:00
BaseTokenProgram : baseMintProgram ,
QuoteTokenProgram : quoteMintProgram ,
//Creator: solana.PublicKey{},
BaseMintDecimals : baseMintDecimals ,
QuoteMintDecimals : quoteMintDecimals ,
User : event . User ,
BaseAmount : decimal . NewFromUint64 ( event . BaseAmountIn ) ,
QuoteAmount : decimal . NewFromUint64 ( event . QuoteAmountIn ) ,
BaseReserve : decimal . NewFromUint64 ( event . PoolBaseTokenReserves + event . BaseAmountIn ) ,
QuoteReserve : decimal . NewFromUint64 ( event . PoolQuoteTokenReserves + event . QuoteAmountIn ) ,
//Mayhem: false,
UserBaseBalance : decimal . NewFromUint64 ( event . UserBaseTokenReserves - event . BaseAmountIn ) ,
UserQuoteBalance : decimal . NewFromUint64 ( event . UserQuoteTokenReserves - event . QuoteAmountIn ) ,
EntryContract : entryContract ,
} ,
} , offset , nil
}
2025-11-21 12:01:44 +08:00
func withdrawParse ( tx * Tx , instruction Instruction , innerInstructions InnerInstructions , offset [ 2 ] uint ) ( [ ] Swap , [ 2 ] uint , error ) {
result := tx . rawTx
2025-11-20 17:56:45 +08:00
var entryContract = result . accountList [ result . Transaction . Message . Instructions [ offset [ 0 ] ] . ProgramIDIndex ]
var err error
2026-05-28 10:23:56 +08:00
if len ( instruction . Accounts ) < 11 {
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
2025-11-20 17:56:45 +08:00
var prefixLen = offset [ 1 ]
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pumpamm withdraw get inner instructions error: %v, offset: %d, %d" , err , offset [ 0 ] , prefixLen )
}
var event ammWithdrawEvent
for innerIndex , innerInstr := range inners {
if innerInstr . ProgramIDIndex == instruction . ProgramIDIndex &&
bytes . Equal ( innerInstr . Data [ : 8 ] , pumpAmmEventDiscriminator [ : ] ) &&
bytes . Equal ( innerInstr . Data [ 8 : 16 ] , pumpAmmWithdrawEventDiscriminator [ : ] ) {
err = agbinary . NewBorshDecoder ( innerInstr . Data [ 16 : ] ) . Decode ( & event )
if offset [ 1 ] == 0 {
offset [ 0 ] += 1
} else {
2026-02-09 14:46:19 +08:00
offset [ 1 ] = uint ( innerIndex ) + 1 + prefixLen
2025-11-20 17:56:45 +08:00
}
if err != nil {
return nil , offset , fmt . Errorf ( "pump amm withdraw pool event decode error: %v, offset: %d, %d" , err , offset [ 0 ] , prefixLen )
}
break
}
}
if event == ( ammWithdrawEvent { } ) {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump amm withdraw event not found, offset: %d, %d" , offset [ 0 ] , prefixLen )
}
var (
poolBaseAccountIdx = instruction . Accounts [ 9 ]
poolQuoteAccountIdx = instruction . Accounts [ 10 ]
baseMintDecimals uint8
quoteMintDecimals uint8
baseMintProgram solana . PublicKey
quoteMintProgram solana . PublicKey
)
for _ , meta := range result . Meta . PostTokenBalances {
if meta . AccountIndex == poolBaseAccountIdx {
baseMintDecimals = uint8 ( meta . UITokenAmount . Decimals )
baseMintProgram = meta . ProgramIDAccount
if baseMintProgram . IsZero ( ) && meta . ProgramID != "" {
baseMintProgram = solana . MustPublicKeyFromBase58 ( meta . ProgramID )
}
}
if meta . AccountIndex == poolQuoteAccountIdx {
quoteMintDecimals = uint8 ( meta . UITokenAmount . Decimals )
quoteMintProgram = meta . ProgramIDAccount
if quoteMintProgram . IsZero ( ) && meta . ProgramID != "" {
quoteMintProgram = solana . MustPublicKeyFromBase58 ( meta . ProgramID )
}
}
}
2025-11-21 12:01:44 +08:00
baseMint := result . accountList [ instruction . Accounts [ 3 ] ]
quoteMint := result . accountList [ instruction . Accounts [ 4 ] ]
if _ , exists := tx . Token [ baseMint ] ; ! exists && ! baseMint . Equals ( wSolMint ) {
tx . Token [ baseMint ] = TokenMeta {
Mint : baseMint ,
Decimals : baseMintDecimals ,
TokenProgram : baseMintProgram ,
}
}
2025-11-20 17:56:45 +08:00
2025-11-21 12:01:44 +08:00
if _ , exists := tx . Token [ quoteMint ] ; ! exists && ! quoteMint . Equals ( wSolMint ) {
tx . Token [ quoteMint ] = TokenMeta {
Mint : quoteMint ,
Decimals : quoteMintDecimals ,
TokenProgram : quoteMintProgram ,
}
}
2025-11-20 17:56:45 +08:00
return [ ] Swap {
{
Program : SolProgramPumpAMM ,
Event : "withdraw" ,
Pool : event . Pool ,
BaseMint : result . accountList [ instruction . Accounts [ 3 ] ] ,
QuoteMint : result . accountList [ instruction . Accounts [ 4 ] ] ,
BaseTokenProgram : baseMintProgram ,
QuoteTokenProgram : quoteMintProgram ,
//Creator: solana.PublicKey{},
BaseMintDecimals : baseMintDecimals ,
QuoteMintDecimals : quoteMintDecimals ,
User : event . User ,
BaseAmount : decimal . NewFromUint64 ( event . BaseAmountOut ) ,
QuoteAmount : decimal . NewFromUint64 ( event . QuoteAmountOut ) ,
BaseReserve : decimal . NewFromUint64 ( event . PoolBaseTokenReserves - event . BaseAmountOut ) ,
QuoteReserve : decimal . NewFromUint64 ( event . PoolQuoteTokenReserves - event . QuoteAmountOut ) ,
//Mayhem: false,
UserBaseBalance : decimal . NewFromUint64 ( event . UserBaseTokenReserves + event . BaseAmountOut ) ,
UserQuoteBalance : decimal . NewFromUint64 ( event . UserQuoteTokenReserves + event . QuoteAmountOut ) ,
EntryContract : entryContract ,
} ,
} , offset , nil
}