596 lines
22 KiB
Go
596 lines
22 KiB
Go
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
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func pumpAmmParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
|
if !tx.rawTx.accountList[instruction.ProgramIDIndex].Equals(pumpAmmProgram) {
|
|
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:
|
|
return ammCreatePoolParser(tx, instruction, innerInstructions, offset)
|
|
case pumpAmmBuyDiscriminator:
|
|
return ammBuyParser(tx, instruction, innerInstructions, offset)
|
|
case pumpAmmSellDiscriminator:
|
|
return ammSellParser(tx, instruction, innerInstructions, offset)
|
|
case pumpAmmDepositDiscriminator:
|
|
return depositParse(tx, instruction, innerInstructions, offset)
|
|
case pumpAmmWithdrawDiscriminator:
|
|
return withdrawParse(tx, instruction, innerInstructions, offset)
|
|
default:
|
|
return nil, increaseOffset(offset), InstructionIgnoredError
|
|
}
|
|
}
|
|
|
|
func ammCreatePoolParser(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]
|
|
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 {
|
|
offset[1] += uint(innerIndex) + 1 + prefixLen
|
|
}
|
|
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]]
|
|
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,
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
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]
|
|
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 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 {
|
|
offset[1] += uint(innerIndex) + 1 + prefixLen
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
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,
|
|
}
|
|
}
|
|
return []Swap{
|
|
{
|
|
Program: SolProgramPumpAMM,
|
|
Event: "buy",
|
|
Pool: event.Pool,
|
|
BaseMint: baseMint,
|
|
QuoteMint: quoteMint,
|
|
BaseTokenProgram: baseTokenProgram,
|
|
QuoteTokenProgram: quoteTokenProgram,
|
|
Creator: event.CoinCreator,
|
|
BaseMintDecimals: baseMintDecimals,
|
|
QuoteMintDecimals: quoteMintDecimals,
|
|
User: event.User,
|
|
BaseAmount: decimal.NewFromUint64(event.BaseAmountOut),
|
|
QuoteAmount: decimal.NewFromUint64(event.UserQuoteAmountIn),
|
|
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserve - event.BaseAmountOut),
|
|
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserve + event.QuoteAmountIn),
|
|
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
|
UserBaseBalance: decimal.NewFromUint64(event.UserBaseTokenReserve + event.BaseAmountOut),
|
|
UserQuoteBalance: decimal.NewFromUint64(event.UserQuoteTokenReserve - event.UserQuoteAmountIn),
|
|
EntryContract: entryContract,
|
|
},
|
|
}, offset, nil
|
|
}
|
|
|
|
func ammSellParser(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]
|
|
var err error
|
|
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)
|
|
}
|
|
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 {
|
|
offset[1] += uint(innerIndex) + 1 + prefixLen
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
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,
|
|
}
|
|
}
|
|
return []Swap{
|
|
{
|
|
Program: SolProgramPumpAMM,
|
|
Event: "sell",
|
|
Pool: event.Pool,
|
|
BaseMint: baseMint,
|
|
QuoteMint: quoteMint,
|
|
BaseTokenProgram: baseTokenProgram,
|
|
QuoteTokenProgram: quoteTokenProgram,
|
|
Creator: event.CoinCreator,
|
|
BaseMintDecimals: baseMintDecimals,
|
|
QuoteMintDecimals: quoteMintDecimals,
|
|
User: event.User,
|
|
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]]),
|
|
UserBaseBalance: decimal.NewFromUint64(event.UserBaseTokenReserves - event.BaseAmountIn),
|
|
UserQuoteBalance: decimal.NewFromUint64(event.UserQuoteTokenReserves + event.UserQuoteAmountOut),
|
|
EntryContract: entryContract,
|
|
},
|
|
}, offset, nil
|
|
}
|
|
|
|
func depositParse(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]
|
|
var err error
|
|
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 {
|
|
offset[1] += uint(innerIndex) + 1 + prefixLen
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
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,
|
|
}
|
|
}
|
|
|
|
if _, exists := tx.Token[quoteMint]; !exists && !quoteMint.Equals(wSolMint) {
|
|
tx.Token[quoteMint] = TokenMeta{
|
|
Mint: quoteMint,
|
|
Decimals: quoteMintDecimals,
|
|
TokenProgram: quoteMintProgram,
|
|
}
|
|
}
|
|
return []Swap{
|
|
{
|
|
Program: SolProgramPumpAMM,
|
|
Event: "deposit",
|
|
Pool: event.Pool,
|
|
BaseMint: baseMint,
|
|
QuoteMint: quoteMint,
|
|
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
|
|
}
|
|
|
|
func withdrawParse(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]
|
|
var err error
|
|
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 {
|
|
offset[1] += uint(innerIndex) + 1 + prefixLen
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
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,
|
|
}
|
|
}
|
|
|
|
if _, exists := tx.Token[quoteMint]; !exists && !quoteMint.Equals(wSolMint) {
|
|
tx.Token[quoteMint] = TokenMeta{
|
|
Mint: quoteMint,
|
|
Decimals: quoteMintDecimals,
|
|
TokenProgram: quoteMintProgram,
|
|
}
|
|
}
|
|
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
|
|
}
|