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"
)
func increaseOffset ( offset [ 2 ] uint ) [ 2 ] uint {
if offset [ 1 ] == 0 {
return [ 2 ] uint { offset [ 0 ] + 1 , offset [ 1 ] }
}
return [ 2 ] uint { offset [ 0 ] , offset [ 1 ] + 1 }
}
// pumpParser // routes pump program instructions to their respective parsers,
// offset is [outerIndex, innerIndex] index of instructions in the transaction,
// if innerIndex == 0 this is outer instruction,if it's an inner instruction, outerIndex is the index of the parent instruction.
2025-11-21 12:01:44 +08:00
func pumpParser ( tx * Tx , instruction Instruction , innerInstructions InnerInstructions , offset [ 2 ] uint ) ( [ ] Swap , [ 2 ] uint , error ) {
2025-11-20 17:56:45 +08:00
2025-11-21 12:01:44 +08:00
if ! tx . rawTx . accountList [ instruction . ProgramIDIndex ] . Equals ( pumpProgram ) {
2025-11-20 17:56:45 +08:00
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump program instruction not found, offset, %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
decode := instruction . Data
if len ( decode ) < 8 {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump program instruction data too short, offset, %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
discriminator := * ( * [ 8 ] byte ) ( decode [ : 8 ] )
switch discriminator {
case pumpBuyV2Discriminator , pumpBuyDiscriminator , pumpSellDiscriminator :
2026-02-26 16:11:34 +08:00
if tx . Err != nil {
return failedTxBuyOrSellParser ( tx , instruction , innerInstructions , offset )
}
2025-11-21 12:01:44 +08:00
return BuyOrSellParser ( tx , instruction , innerInstructions , offset )
2025-11-20 17:56:45 +08:00
case pumpCreateDiscriminator , pumpCreateV2Discriminator :
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 CreateParser ( tx , instruction , innerInstructions , offset )
2025-11-20 17:56:45 +08:00
case pumpMigrateDiscriminator :
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 MigrateParser ( tx , instruction , innerInstructions , offset )
2025-11-20 17:56:45 +08:00
default :
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
}
type PumpCreateData struct {
Discriminator uint64
Name string
Symbol string
Uri string
Creator solana . PublicKey
}
type PumpCreateV2Data struct {
Discriminator uint64
Name string
Symbol string
Uri string
Creator solana . PublicKey
IsMayhem bool
}
type PumpCreateEvent struct {
Name string
Symbol string
Uri string
Mint solana . PublicKey
BondingCurve solana . PublicKey
User solana . PublicKey
Creator solana . PublicKey
Timestamp int64
VirtualTokenReserves uint64
VirtualSolReserves uint64
RealTokenReserves uint64
TokenTotalSupply uint64
TokenProgram solana . PublicKey
IsMayhemMode bool
2026-02-27 01:43:07 +08:00
IsCashbackEnabled bool
2025-11-20 17:56:45 +08:00
}
2025-11-21 12:01:44 +08:00
func CreateParser ( tx * Tx , instr 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 programIndex = instr . ProgramIDIndex
var err error
var createEvent PumpCreateEvent
var prefixLen = offset [ 1 ]
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump create get inner instructions error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
for innerIndex , innerInstr := range inners {
if innerInstr . ProgramIDIndex == programIndex && bytes . Equal ( innerInstr . Data [ : 8 ] , pumpEventDiscriminator [ : ] ) && bytes . Equal ( innerInstr . Data [ 8 : 16 ] , pumpCreateEventDiscriminator [ : ] ) {
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 create event decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
break
}
}
if createEvent == ( PumpCreateEvent { } ) {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump create event not found, offset, %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
userIndex := 0
if bytes . HasPrefix ( instr . Data , pumpCreateV2Discriminator [ : ] ) {
userIndex = instr . Accounts [ 5 ]
} else if bytes . HasPrefix ( instr . Data , pumpCreateDiscriminator [ : ] ) {
userIndex = instr . Accounts [ 7 ]
}
userBase := getAccountBalanceAfterTx ( result , userIndex )
userQuote , _ := GetSolAfterTx ( result , userIndex )
2025-11-21 12:01:44 +08:00
totalSupply := decimal . NewFromUint64 ( createEvent . TokenTotalSupply ) . Div ( decimal . New ( 1 , 6 ) )
tx . Token [ createEvent . Mint ] = TokenMeta {
Mint : createEvent . Mint ,
TokenProgram : createEvent . TokenProgram ,
Decimals : 6 ,
Name : createEvent . Name ,
Symbol : createEvent . Symbol ,
Url : createEvent . Uri ,
TotalSupply : & totalSupply ,
}
2025-11-20 17:56:45 +08:00
return [ ] Swap {
{
Program : SolProgramPump ,
Event : "create" ,
Pool : createEvent . BondingCurve ,
BaseMint : createEvent . Mint ,
QuoteMint : solana . PublicKey { } ,
BaseTokenProgram : createEvent . TokenProgram ,
QuoteTokenProgram : solana . PublicKey { } ,
Creator : createEvent . Creator ,
BaseMintDecimals : 6 ,
QuoteMintDecimals : 9 ,
User : createEvent . User ,
BaseAmount : decimal . Zero ,
QuoteAmount : decimal . Zero ,
BaseReserve : decimal . NewFromUint64 ( createEvent . RealTokenReserves ) ,
QuoteReserve : decimal . Zero ,
Mayhem : createEvent . IsMayhemMode ,
2026-02-27 01:43:07 +08:00
Cashback : createEvent . IsCashbackEnabled ,
2025-11-20 17:56:45 +08:00
UserBaseBalance : userBase ,
UserQuoteBalance : decimal . NewFromUint64 ( userQuote ) ,
EntryContract : entryContract ,
} ,
} , offset , nil
}
type PumpTradeEvent struct {
Mint solana . PublicKey
SolAmount uint64
TokenAmount uint64
IsBuy bool
User solana . PublicKey
Timestamp int64
VirtualSolReserves uint64
VirtualTokenReserves uint64
RealSolReserves uint64
RealTokenReserves uint64
FeeRecipient solana . PublicKey
FeeBasisPoints uint64
Fee uint64
Creator solana . PublicKey
2025-11-24 17:47:56 +08:00
CreatorFeeBasisPoints uint64
CreatorFee uint64
2026-02-26 16:11:34 +08:00
TrackVolume bool
TotalUnclaimedTokens uint64
TotalClaimedTokens uint64
CurrentSolVolume uint64
LastUpdateTimestamp int64
IxName string
MayhemMode bool
CashbackFeeBasisPoints uint64
Cashback uint64
2025-11-24 17:47:56 +08:00
}
type PumpTradeFeeArg struct {
IsPump bool
MarketCap [ 16 ] byte
TradeSize uint64
2025-11-20 17:56:45 +08:00
}
type CompleteEvent struct {
User solana . PublicKey
Mint solana . PublicKey
BondingCurve solana . PublicKey
Timestamp int64
}
2026-02-26 16:11:34 +08:00
type PumpTradeArgs struct {
Discriminator [ 8 ] byte
Amount1 uint64
Amount2 uint64
}
func failedTxBuyOrSellParser ( 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 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 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 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 == 6042 ||
tx . Err . CustomCode == 6041 ||
tx . Err . CustomCode == 6040 ||
tx . Err . CustomCode == 6023 || tx . Err . CustomCode == 6021 || tx . Err . CustomCode == 6003 || tx . Err . CustomCode == 6002 ) {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "failed tx pump 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 ]
user := result . accountList [ instruction . Accounts [ 6 ] ]
ataUserIdx := instruction . Accounts [ 5 ]
userIndex := instruction . Accounts [ 6 ]
mint := result . accountList [ instruction . Accounts [ 2 ] ]
var args PumpTradeArgs
err := agbinary . NewBorshDecoder ( instruction . Data [ : ] ) . Decode ( & args )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "failed tx pump buy/sell decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
var event string
var (
solAmount , tokenAmount uint64
)
if bytes . Equal ( args . Discriminator [ : ] , pumpBuyV2Discriminator [ : ] ) {
event = "buy_failed"
solAmount = args . Amount1
tokenAmount = args . Amount2
} else if bytes . Equal ( args . Discriminator [ : ] , pumpBuyDiscriminator [ : ] ) {
event = "buy_failed"
solAmount = args . Amount2
tokenAmount = args . Amount1
} else if bytes . Equal ( args . Discriminator [ : ] , pumpSellDiscriminator [ : ] ) {
event = "sell_failed"
solAmount = args . Amount2
tokenAmount = args . Amount1
} else {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "unknown pump trade instruction discriminator, offset, %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
var baseTokenProgram solana . PublicKey
if event == "buy_failed" {
baseTokenProgram = result . accountList [ instruction . Accounts [ 8 ] ]
} else {
baseTokenProgram = result . accountList [ instruction . Accounts [ 9 ] ]
}
if ! user . IsOnCurve ( ) && ( entryContract . Equals ( okxDexRoutersV2 ) || entryContract . Equals ( okxAggregatorV2 ) ) {
userBaseAmount , ataIndex := tokenBalanceChange ( result , 0 , baseTokenProgram , mint )
//&& userBaseAmount.BigInt().Uint64() == tradeEvent.TokenAmount
if ! userBaseAmount . IsZero ( ) {
user = result . accountList [ 0 ]
userIndex = 0
ataUserIdx = ataIndex
}
}
userBase := getAccountBalanceAfterTx ( result , ataUserIdx )
userQuote , _ := GetSolAfterTx ( result , userIndex )
bcIdx := instruction . Accounts [ 3 ]
bcAtaIndex := instruction . Accounts [ 4 ]
solReserves , _ := GetSolAfterTx ( result , bcIdx )
tokenReserves := getAccountBalanceAfterTx ( result , bcAtaIndex )
swaps := [ ] Swap {
{
Program : SolProgramPump ,
Event : event ,
Pool : result . accountList [ instruction . Accounts [ 3 ] ] ,
BaseMint : mint ,
QuoteMint : solana . PublicKey { } ,
BaseTokenProgram : baseTokenProgram ,
QuoteTokenProgram : solana . PublicKey { } ,
BaseMintDecimals : 6 ,
QuoteMintDecimals : 9 ,
User : user ,
BaseAmount : decimal . NewFromUint64 ( tokenAmount ) ,
QuoteAmount : decimal . NewFromUint64 ( solAmount ) ,
BaseReserve : tokenReserves ,
QuoteReserve : decimal . NewFromUint64 ( solReserves ) ,
Mayhem : isMayhemPump ( result . accountList [ instruction . Accounts [ 1 ] ] ) ,
UserBaseBalance : userBase ,
UserQuoteBalance : decimal . NewFromUint64 ( userQuote ) ,
EntryContract : entryContract ,
} ,
}
return swaps , offset , nil
}
2025-11-21 12:01:44 +08:00
func BuyOrSellParser ( 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 programIndex = instruction . ProgramIDIndex
2025-11-24 17:47:56 +08:00
feeEventProgramIndex := 0
for i , b := range result . accountList {
if b . Equals ( pumpFeesProgram ) {
feeEventProgramIndex = i
break
}
}
2025-11-20 17:56:45 +08:00
var (
tradeEvent PumpTradeEvent
2025-11-24 17:47:56 +08:00
tradeFeeArg PumpTradeFeeArg
2025-11-20 17:56:45 +08:00
completeEvent CompleteEvent
completed bool
newoffset [ 2 ] uint
)
var prefixLen = offset [ 1 ]
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump create get inner instructions error: %v,offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
2025-12-22 17:56:40 +08:00
if instruction . StackHeight != nil && * instruction . StackHeight > 2 {
2025-12-31 16:53:39 +08:00
for _ , innerInstr := range innerInstructions . Instructions {
2025-12-22 17:56:40 +08:00
if innerInstr . StackHeight != nil && * innerInstr . StackHeight == * instruction . StackHeight - 1 {
entryContract = result . accountList [ innerInstr . ProgramIDIndex ]
2026-02-09 16:09:31 +08:00
break
2025-12-22 17:56:40 +08:00
}
}
}
2025-11-20 17:56:45 +08:00
for innerIndex , innerInstr := range inners {
2025-11-24 17:47:56 +08:00
if innerInstr . ProgramIDIndex == feeEventProgramIndex && bytes . Equal ( innerInstr . Data [ : 8 ] , pumpGetFeesDiscriminator [ : ] ) {
err = agbinary . NewBorshDecoder ( innerInstr . Data [ 8 : ] ) . Decode ( & tradeFeeArg )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump get fees event decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
continue
}
2025-11-20 17:56:45 +08:00
if innerInstr . ProgramIDIndex == programIndex && bytes . Equal ( innerInstr . Data [ : 8 ] , pumpEventDiscriminator [ : ] ) {
if bytes . Equal ( innerInstr . Data [ 8 : 16 ] , pumpTradeEventDiscriminator [ 8 : 16 ] ) {
err = agbinary . NewBorshDecoder ( innerInstr . Data [ 16 : ] ) . Decode ( & tradeEvent )
if offset [ 1 ] == 0 {
newoffset = [ 2 ] uint { offset [ 0 ] + 1 , offset [ 1 ] }
} else {
newoffset = [ 2 ] uint { offset [ 0 ] , prefixLen + uint ( innerIndex ) + 1 }
}
if err != nil {
return nil , newoffset , fmt . Errorf ( "pump buy event decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
if ! tradeEvent . IsBuy {
break
}
} else if bytes . Equal ( innerInstr . Data [ 8 : 16 ] , pumpCompleteEventDiscriminator [ : ] ) {
err = agbinary . NewBorshDecoder ( innerInstr . Data [ 16 : ] ) . Decode ( & completeEvent )
if offset [ 1 ] == 0 {
newoffset = [ 2 ] uint { offset [ 0 ] + 1 , offset [ 1 ] }
} else {
newoffset = [ 2 ] uint { offset [ 0 ] , prefixLen + uint ( innerIndex ) + 1 }
}
if err != nil {
return nil , newoffset , fmt . Errorf ( "pump completeEvent event decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
completed = true
break
}
}
}
if tradeEvent == ( PumpTradeEvent { } ) {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pmp buy/sell event not found, offset, %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
offset = [ 2 ] uint { newoffset [ 0 ] , newoffset [ 1 ] }
event := ""
baseTokenProgram := solana . TokenProgramID
if tradeEvent . IsBuy {
event = "buy"
baseTokenProgram = result . accountList [ instruction . Accounts [ 8 ] ]
} else {
event = "sell"
baseTokenProgram = result . accountList [ instruction . Accounts [ 9 ] ]
}
2025-11-21 12:01:44 +08:00
if _ , exists := tx . Token [ tradeEvent . Mint ] ; ! exists {
tx . Token [ tradeEvent . Mint ] = TokenMeta {
Mint : tradeEvent . Mint ,
TokenProgram : baseTokenProgram ,
Decimals : 6 ,
}
}
2025-12-22 17:56:40 +08:00
var user = tradeEvent . User
ataUserIdx := instruction . Accounts [ 5 ]
userIndex := instruction . Accounts [ 6 ]
if ! tradeEvent . User . IsOnCurve ( ) && ( entryContract . Equals ( okxDexRoutersV2 ) || entryContract . Equals ( okxAggregatorV2 ) ) {
userBaseAmount , ataIndex := tokenBalanceChange ( result , 0 , baseTokenProgram , tradeEvent . Mint )
//&& userBaseAmount.BigInt().Uint64() == tradeEvent.TokenAmount
if ! userBaseAmount . IsZero ( ) {
user = result . accountList [ 0 ]
userIndex = 0
ataUserIdx = ataIndex
}
}
userBase := getAccountBalanceAfterTx ( result , ataUserIdx )
userQuote , _ := GetSolAfterTx ( result , userIndex )
2025-11-24 17:47:56 +08:00
solAmount := tradeEvent . SolAmount
if tradeEvent . IsBuy && bytes . Equal ( instruction . Data [ : 8 ] , pumpBuyV2Discriminator [ : ] ) {
fee := tradeEvent . Fee + tradeEvent . CreatorFee
solAmount = tradeFeeArg . TradeSize
if solAmount > fee {
solAmount = solAmount - fee
}
}
2026-02-26 16:11:34 +08:00
isCashbackCoin := tradeEvent . CashbackFeeBasisPoints > 0 || tradeEvent . Cashback > 0
2025-11-20 17:56:45 +08:00
swaps := [ ] Swap {
{
Program : SolProgramPump ,
Event : event ,
Pool : result . accountList [ instruction . Accounts [ 3 ] ] ,
BaseMint : tradeEvent . Mint ,
QuoteMint : solana . PublicKey { } ,
BaseTokenProgram : baseTokenProgram ,
QuoteTokenProgram : solana . PublicKey { } ,
Creator : tradeEvent . Creator ,
BaseMintDecimals : 6 ,
QuoteMintDecimals : 9 ,
2025-12-22 17:56:40 +08:00
User : user ,
2025-11-20 17:56:45 +08:00
BaseAmount : decimal . NewFromUint64 ( tradeEvent . TokenAmount ) ,
2025-11-24 17:47:56 +08:00
QuoteAmount : decimal . NewFromUint64 ( solAmount ) ,
2025-11-20 17:56:45 +08:00
BaseReserve : decimal . NewFromUint64 ( tradeEvent . RealTokenReserves ) ,
QuoteReserve : decimal . NewFromUint64 ( tradeEvent . RealSolReserves ) ,
Mayhem : isMayhemPump ( result . accountList [ instruction . Accounts [ 1 ] ] ) ,
UserBaseBalance : userBase ,
UserQuoteBalance : decimal . NewFromUint64 ( userQuote ) ,
EntryContract : entryContract ,
2026-02-26 16:11:34 +08:00
Cashback : isCashbackCoin ,
2025-11-20 17:56:45 +08:00
} ,
}
if completed {
swaps = append ( swaps , Swap {
Program : SolProgramPump ,
Event : "complete" ,
Pool : result . accountList [ instruction . Accounts [ 3 ] ] ,
BaseMint : tradeEvent . Mint ,
QuoteMint : solana . PublicKey { } ,
BaseTokenProgram : result . accountList [ instruction . Accounts [ 8 ] ] ,
QuoteTokenProgram : solana . PublicKey { } ,
Creator : tradeEvent . Creator ,
BaseMintDecimals : 6 ,
QuoteMintDecimals : 9 ,
2025-12-22 17:56:40 +08:00
User : user ,
2026-02-12 10:37:59 +08:00
BaseReserve : decimal . NewFromUint64 ( tradeEvent . RealTokenReserves ) ,
QuoteReserve : decimal . NewFromUint64 ( tradeEvent . RealSolReserves ) ,
2025-11-20 17:56:45 +08:00
Mayhem : isMayhemPump ( result . accountList [ instruction . Accounts [ 1 ] ] ) ,
UserBaseBalance : userBase ,
UserQuoteBalance : decimal . NewFromUint64 ( userQuote ) ,
EntryContract : entryContract ,
} )
}
2025-12-22 17:56:40 +08:00
2025-11-20 17:56:45 +08:00
return swaps , offset , nil
}
type MigrateEvent struct {
User solana . PublicKey
Mint solana . PublicKey
//Creator solana.PublicKey
MintAmount uint64
SolAmount uint64
PoolMigrationFee uint64
//CreatorFee uint64
BondingCurve solana . PublicKey
TimeStamp int64
Pool solana . PublicKey
}
2025-11-21 12:01:44 +08:00
func MigrateParser ( tx * Tx , instr 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
programIndex := instr . ProgramIDIndex
ammprogramIdx := 0
for i , b := range result . accountList {
if b . Equals ( pumpAmmProgram ) {
ammprogramIdx = i
break
}
}
if ammprogramIdx == 0 {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump migrate, amm program id not found in account list, offset, %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
var prefixLen = offset [ 1 ]
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump migrate get inner instructions offset, %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
var (
migrateEvent MigrateEvent
createEvent ammCreatePoolEvent
newoffset [ 2 ] uint
)
for innerIndex , innerInstr := range inners {
if innerInstr . ProgramIDIndex == ammprogramIdx && bytes . Equal ( innerInstr . Data [ : 8 ] , pumpAmmEventDiscriminator [ : ] ) {
if bytes . Equal ( innerInstr . Data [ 8 : 16 ] , pumpAmmCreateEventDiscriminator [ : ] ) {
err = agbinary . NewBorshDecoder ( innerInstr . Data [ 16 : ] ) . Decode ( & createEvent )
if offset [ 1 ] == 0 {
newoffset = [ 2 ] uint { offset [ 0 ] + 1 , offset [ 1 ] }
} else {
newoffset = [ 2 ] uint { offset [ 0 ] , prefixLen + uint ( innerIndex ) + 1 }
}
if err != nil {
return nil , newoffset , fmt . Errorf ( "pump amm createEvent decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
}
//
} else if innerInstr . ProgramIDIndex == programIndex && bytes . Equal ( innerInstr . Data [ : 8 ] , pumpEventDiscriminator [ : ] ) {
if bytes . Equal ( innerInstr . Data [ 8 : 16 ] , pumpMigrateEventDiscriminator [ : ] ) {
err = agbinary . NewBorshDecoder ( innerInstr . Data [ 16 : ] ) . Decode ( & migrateEvent )
if offset [ 1 ] == 0 {
newoffset = [ 2 ] uint { offset [ 0 ] + 1 , offset [ 1 ] }
} else {
newoffset = [ 2 ] uint { offset [ 0 ] , prefixLen + uint ( innerIndex ) + 1 }
}
if err != nil {
return nil , newoffset , fmt . Errorf ( "pump buy event decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
break
}
}
}
if migrateEvent == ( MigrateEvent { } ) || createEvent == ( ammCreatePoolEvent { } ) {
offset = [ 2 ] uint { newoffset [ 0 ] , newoffset [ 1 ] }
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
offset = [ 2 ] uint { newoffset [ 0 ] , newoffset [ 1 ] }
// verify migrate by checking create pool and migrate event
userIndex := instr . Accounts [ 5 ]
ataBondingCurveAccountIndex := instr . Accounts [ 4 ]
bc , err := getTokenBalanceAfterTx ( result , ataBondingCurveAccountIndex )
if err != nil || bc == nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "pump migrate get bonding curve balance error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
baseTokenProgram := bc . ProgramIDAccount
var userBase decimal . Decimal
if result . accountList [ userIndex ] . Equals ( pumpMigrationAccount ) {
userBase = decimal . Zero
} else {
userBase = GetTokenBalanceAfterTx ( result , userIndex , baseTokenProgram , migrateEvent . Mint )
}
userQuote , _ := GetSolAfterTx ( result , userIndex )
2025-11-21 12:01:44 +08:00
if _ , exists := tx . Token [ migrateEvent . Mint ] ; ! exists {
tx . Token [ migrateEvent . Mint ] = TokenMeta {
Mint : migrateEvent . Mint ,
TokenProgram : baseTokenProgram ,
Decimals : 6 ,
}
}
2025-11-20 17:56:45 +08:00
swaps := [ ] Swap {
{
Program : SolProgramPump ,
Event : "migrate" ,
Pool : migrateEvent . BondingCurve ,
BaseMint : migrateEvent . Mint ,
QuoteMint : solana . PublicKey { } ,
BaseTokenProgram : baseTokenProgram ,
QuoteTokenProgram : solana . PublicKey { } ,
Creator : createEvent . Creator ,
BaseMintDecimals : 6 ,
QuoteMintDecimals : 9 ,
User : migrateEvent . User ,
//BaseAmount: decimal.Decimal{},
//QuoteAmount: decimal.Decimal{},
2026-02-09 14:46:19 +08:00
BaseReserve : decimal . NewFromUint64 ( migrateEvent . MintAmount ) ,
QuoteReserve : decimal . NewFromUint64 ( migrateEvent . SolAmount ) ,
Mayhem : createEvent . IsMayhemMode ,
MigrateTopProgram : pumpAmmProgram ,
MigrateToPool : migrateEvent . Pool ,
UserBaseBalance : userBase ,
UserQuoteBalance : decimal . NewFromUint64 ( userQuote ) ,
EntryContract : entryContract ,
2025-11-20 17:56:45 +08:00
} ,
}
swaps = append ( swaps , Swap {
Program : SolProgramPumpAMM ,
Event : "create" ,
Pool : migrateEvent . Pool ,
BaseMint : migrateEvent . Mint ,
QuoteMint : wSolMint ,
BaseTokenProgram : baseTokenProgram ,
QuoteTokenProgram : solana . TokenProgramID ,
Creator : createEvent . Creator ,
BaseMintDecimals : 6 ,
QuoteMintDecimals : 9 ,
User : migrateEvent . User ,
BaseAmount : decimal . NewFromUint64 ( migrateEvent . MintAmount ) ,
QuoteAmount : decimal . NewFromUint64 ( migrateEvent . SolAmount ) ,
BaseReserve : decimal . NewFromUint64 ( migrateEvent . MintAmount ) ,
QuoteReserve : decimal . NewFromUint64 ( migrateEvent . SolAmount ) ,
Mayhem : createEvent . IsMayhemMode ,
UserBaseBalance : userBase ,
UserQuoteBalance : decimal . NewFromUint64 ( userQuote ) ,
EntryContract : entryContract ,
} )
return swaps , offset , nil
}