update amm virtual quotes reserves

This commit is contained in:
thloyi
2026-07-17 16:56:44 +08:00
parent 387d6ee10b
commit 895230aaac
7 changed files with 528 additions and 69 deletions

View File

@@ -9,7 +9,7 @@ import (
"github.com/shopspring/decimal"
)
type ammBuyEvent struct {
type ammBuyEventPrefix struct {
TimeStamp int64
BaseAmountOut uint64
MaxQuoteAmountIn uint64
@@ -45,6 +45,38 @@ type ammBuyEvent struct {
Cashback uint64
}
type ammBuyEvent struct {
ammBuyEventPrefix
BuybackFeeBasisPoints uint64
BuybackFee uint64
VirtualQuoteReserves agbinary.Int128
CanBoost bool
BaseSupply uint64
}
type ammTradeEventBuybackSuffix struct {
BuybackFeeBasisPoints uint64
BuybackFee uint64
}
type ammTradeEventVirtualSuffix struct {
VirtualQuoteReserves agbinary.Int128
CanBoost bool
BaseSupply uint64
}
func decodeAmmBuyEvent(data []byte) (ammBuyEvent, error) {
var event ammBuyEvent
decoder := agbinary.NewBorshDecoder(data)
if err := decoder.Decode(&event.ammBuyEventPrefix); err != nil {
return ammBuyEvent{}, err
}
if err := decodeAmmTradeEventSuffix(decoder, &event.BuybackFeeBasisPoints, &event.BuybackFee, &event.VirtualQuoteReserves, &event.CanBoost, &event.BaseSupply); err != nil {
return ammBuyEvent{}, err
}
return event, nil
}
type ammCreatePoolEvent struct {
TimeStamp int64
Index uint16
@@ -90,7 +122,7 @@ type ammDepositEvent struct {
UserPoolTokenAccount solana.PublicKey
}
type ammSellEvent struct {
type ammSellEventPrefix struct {
Timestamp int64
BaseAmountIn uint64
MinQuoteAmountOut uint64
@@ -119,6 +151,86 @@ type ammSellEvent struct {
Cashback uint64
}
type ammSellEvent struct {
ammSellEventPrefix
BuybackFeeBasisPoints uint64
BuybackFee uint64
VirtualQuoteReserves agbinary.Int128
CanBoost bool
BaseSupply uint64
}
func decodeAmmSellEvent(data []byte) (ammSellEvent, error) {
var event ammSellEvent
decoder := agbinary.NewBorshDecoder(data)
if err := decoder.Decode(&event.ammSellEventPrefix); err != nil {
return ammSellEvent{}, err
}
if err := decodeAmmTradeEventSuffix(decoder, &event.BuybackFeeBasisPoints, &event.BuybackFee, &event.VirtualQuoteReserves, &event.CanBoost, &event.BaseSupply); err != nil {
return ammSellEvent{}, err
}
return event, nil
}
func decodeAmmTradeEventSuffix(
decoder *agbinary.Decoder,
buybackFeeBasisPoints *uint64,
buybackFee *uint64,
virtualQuoteReserves *agbinary.Int128,
canBoost *bool,
baseSupply *uint64,
) error {
remaining := decoder.Remaining()
if remaining == 0 {
return nil
}
if remaining < 16 {
return fmt.Errorf("pump amm trade event buyback suffix truncated: %d bytes", remaining)
}
var buyback ammTradeEventBuybackSuffix
if err := decoder.Decode(&buyback); err != nil {
return err
}
*buybackFeeBasisPoints = buyback.BuybackFeeBasisPoints
*buybackFee = buyback.BuybackFee
remaining = decoder.Remaining()
if remaining == 0 {
return nil
}
if remaining < 25 {
return fmt.Errorf("pump amm trade event virtual reserve suffix truncated: %d bytes", remaining)
}
var virtual ammTradeEventVirtualSuffix
if err := decoder.Decode(&virtual); err != nil {
return err
}
*virtualQuoteReserves = virtual.VirtualQuoteReserves
*canBoost = virtual.CanBoost
*baseSupply = virtual.BaseSupply
return nil
}
func pumpAmmVirtualQuoteReserve(value agbinary.Int128) decimal.Decimal {
return decimal.NewFromBigInt(value.BigInt(), 0)
}
func pumpAmmBuyPostQuoteReserves(event ammBuyEvent) (real, effective decimal.Decimal) {
// The event reserve is the pre-trade real vault balance. Keep the
// parser's existing LP-fee treatment by applying QuoteAmountIn here.
real = decimal.NewFromUint64(event.PoolQuoteTokenReserve).Add(decimal.NewFromUint64(event.QuoteAmountIn))
return real, real.Add(pumpAmmVirtualQuoteReserve(event.VirtualQuoteReserves))
}
func pumpAmmSellPostQuoteReserves(event ammSellEvent) (real, effective decimal.Decimal) {
// The event reserve is the pre-trade real vault balance. Keep the
// parser's existing LP-fee treatment by applying QuoteAmountOut here.
real = decimal.NewFromUint64(event.PoolQuoteTokenReserves).Sub(decimal.NewFromUint64(event.QuoteAmountOut))
return real, real.Add(pumpAmmVirtualQuoteReserve(event.VirtualQuoteReserves))
}
type ammWithdrawEvent struct {
Timestamp int64
LpTokenAmountIn uint64
@@ -249,6 +361,7 @@ func ammCreatePoolParser(tx *Tx, instruction Instruction, innerInstructions Inne
QuoteAmount: decimal.NewFromUint64(createEvent.QuoteAmountIn),
BaseReserve: decimal.NewFromUint64(createEvent.PoolBaseAmount),
QuoteReserve: decimal.NewFromUint64(createEvent.PoolQuoteAmount),
RealQuoteReserve: decimal.NewFromUint64(createEvent.PoolQuoteAmount),
UserBaseBalance: decimal.Decimal{},
UserQuoteBalance: decimal.Decimal{},
EntryContract: entryContract,
@@ -395,6 +508,7 @@ func failedTxAmmBuyParser(tx *Tx, instruction Instruction, innerInstructions Inn
QuoteAmount: decimal.NewFromUint64(quoteAmount),
BaseReserve: baseReserve,
QuoteReserve: quoteReserve,
RealQuoteReserve: quoteReserve,
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
UserBaseBalance: userBase,
UserQuoteBalance: userQuote,
@@ -518,6 +632,7 @@ func failedTxAmmSellParser(tx *Tx, instruction Instruction, innerInstructions In
QuoteAmount: decimal.NewFromUint64(quoteAmount),
BaseReserve: baseReserve,
QuoteReserve: quoteReserve,
RealQuoteReserve: quoteReserve,
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
UserBaseBalance: userBase,
UserQuoteBalance: userQuote,
@@ -557,7 +672,7 @@ func ammBuyParser(tx *Tx, instruction Instruction, innerInstructions InnerInstru
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)
event, err = decodeAmmBuyEvent(innerInstr.Data[16:])
if offset[1] == 0 {
offset[0] += 1
} else {
@@ -632,27 +747,30 @@ func ammBuyParser(tx *Tx, instruction Instruction, innerInstructions InnerInstru
if event.IxName == "buy" {
quoteAmount = decimal.NewFromUint64(event.QuoteAmountIn)
}
realQuoteReserve, effectiveQuoteReserve := pumpAmmBuyPostQuoteReserves(event)
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),
QuoteAmount: quoteAmount,
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,
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),
QuoteAmount: quoteAmount,
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserve - event.BaseAmountOut),
QuoteReserve: effectiveQuoteReserve,
RealQuoteReserve: realQuoteReserve,
VirtualQuoteReserve: pumpAmmVirtualQuoteReserve(event.VirtualQuoteReserves),
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
Cashback: isCashbackCoin,
UserBaseBalance: userBase,
UserQuoteBalance: userQuote,
EntryContract: entryContract,
}
if bytes.Equal(instruction.Data[:8], pumpAmmBuyV2Discriminator[:]) {
swap.SetSwapAmountInfo(
@@ -699,7 +817,7 @@ func ammSellParser(tx *Tx, instruction Instruction, innerInstructions InnerInstr
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)
event, err = decodeAmmSellEvent(innerInstr.Data[16:])
if offset[1] == 0 {
offset[0] += 1
} else {
@@ -770,27 +888,30 @@ func ammSellParser(tx *Tx, instruction Instruction, innerInstructions InnerInstr
userQuote = userQuote.Add(decimal.NewFromUint64(userBalance))
}
isCashbackCoin := event.CashbackFeeBasisPoints > 0 || event.Cashback > 0
realQuoteReserve, effectiveQuoteReserve := pumpAmmSellPostQuoteReserves(event)
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,
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: effectiveQuoteReserve,
RealQuoteReserve: realQuoteReserve,
VirtualQuoteReserve: pumpAmmVirtualQuoteReserve(event.VirtualQuoteReserves),
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
Cashback: isCashbackCoin,
UserBaseBalance: userBase,
UserQuoteBalance: userQuote,
EntryContract: entryContract,
}
swap.SetSwapAmountInfo(
SwapModeExactIn,
@@ -893,6 +1014,7 @@ func depositParse(tx *Tx, instruction Instruction, innerInstructions InnerInstru
QuoteAmount: decimal.NewFromUint64(event.QuoteAmountIn),
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves + event.BaseAmountIn),
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves + event.QuoteAmountIn),
RealQuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves + event.QuoteAmountIn),
//Mayhem: false,
UserBaseBalance: decimal.NewFromUint64(event.UserBaseTokenReserves - event.BaseAmountIn),
UserQuoteBalance: decimal.NewFromUint64(event.UserQuoteTokenReserves - event.QuoteAmountIn),
@@ -994,6 +1116,7 @@ func withdrawParse(tx *Tx, instruction Instruction, innerInstructions InnerInstr
QuoteAmount: decimal.NewFromUint64(event.QuoteAmountOut),
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves - event.BaseAmountOut),
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves - event.QuoteAmountOut),
RealQuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves - event.QuoteAmountOut),
//Mayhem: false,
UserBaseBalance: decimal.NewFromUint64(event.UserBaseTokenReserves + event.BaseAmountOut),
UserQuoteBalance: decimal.NewFromUint64(event.UserQuoteTokenReserves + event.QuoteAmountOut),