Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc03798ff6 |
@@ -100,8 +100,7 @@ Each `Swap` describes one protocol-level swap, liquidity, migration, or pool ope
|
|||||||
| `BaseMintDecimals` / `QuoteMintDecimals` | Decimals used to interpret raw token amounts. |
|
| `BaseMintDecimals` / `QuoteMintDecimals` | Decimals used to interpret raw token amounts. |
|
||||||
| `User` | User or effective owner account for the action. If the parsed user is not on-curve, the parser may fall back to the transaction signer. |
|
| `User` | User or effective owner account for the action. If the parsed user is not on-curve, the parser may fall back to the transaction signer. |
|
||||||
| `BaseAmount` / `QuoteAmount` | Actual parsed base-side and quote-side amounts, stored as `decimal.Decimal`. |
|
| `BaseAmount` / `QuoteAmount` | Actual parsed base-side and quote-side amounts, stored as `decimal.Decimal`. |
|
||||||
| `BaseReserve` / `QuoteReserve` | Pool reserves when the protocol event or accounts expose them. For Pump AMM buy/sell events, `QuoteReserve` is the post-trade effective quote reserve. |
|
| `BaseReserve` / `QuoteReserve` | Pool reserves when the protocol event or accounts expose them. |
|
||||||
| `RealQuoteReserve` / `VirtualQuoteReserve` | Quote-reserve components. Pump AMM buy/sell events expose the post-trade real reserve and the signed virtual reserve separately. |
|
|
||||||
| `UserBaseBalance` / `UserQuoteBalance` | User token balances after the transaction when available from token balance metadata. |
|
| `UserBaseBalance` / `UserQuoteBalance` | User token balances after the transaction when available from token balance metadata. |
|
||||||
| `AfterSOLBalance` | User or signer SOL balance after the transaction. |
|
| `AfterSOLBalance` | User or signer SOL balance after the transaction. |
|
||||||
| `EntryContract` | Known router / entry contract account when detected. |
|
| `EntryContract` | Known router / entry contract account when detected. |
|
||||||
@@ -180,7 +179,6 @@ Parsed transaction binary:
|
|||||||
- `EncodeTxsBinary` / `DecodeTxsBinary` for a batch of parsed `Tx`.
|
- `EncodeTxsBinary` / `DecodeTxsBinary` for a batch of parsed `Tx`.
|
||||||
- `DecodeTxsBinaryReader` for streaming `PTXS` reads.
|
- `DecodeTxsBinaryReader` for streaming `PTXS` reads.
|
||||||
- `MergeTxsBinaryBytes` and `MergeTxsBinarySourcesToWriter` for merging `PTXS` batches.
|
- `MergeTxsBinaryBytes` and `MergeTxsBinarySourcesToWriter` for merging `PTXS` batches.
|
||||||
- Schema v5 persists `RealQuoteReserve` and signed `VirtualQuoteReserve`; v3/v4 inputs remain readable and default these components to legacy `QuoteReserve` and zero.
|
|
||||||
|
|
||||||
Raw transaction binary:
|
Raw transaction binary:
|
||||||
|
|
||||||
|
|||||||
17
consts.go
17
consts.go
@@ -116,6 +116,20 @@ var mevAgentFeeAddresses = map[solana.PublicKey]string{
|
|||||||
solana.MustPublicKeyFromBase58("HZTmLyC683y74TW3HtGbNX5orxjm2sPuZBEYwwSgAM8v"): MevAgentBlocxRoute,
|
solana.MustPublicKeyFromBase58("HZTmLyC683y74TW3HtGbNX5orxjm2sPuZBEYwwSgAM8v"): MevAgentBlocxRoute,
|
||||||
solana.MustPublicKeyFromBase58("FogxVNs6Mm2w9rnGL1vkARSwJxvLE8mujTv3LK8RnUhF"): MevAgentBlocxRoute,
|
solana.MustPublicKeyFromBase58("FogxVNs6Mm2w9rnGL1vkARSwJxvLE8mujTv3LK8RnUhF"): MevAgentBlocxRoute,
|
||||||
solana.MustPublicKeyFromBase58("3UQUKjhMKaY2S6bjcQD6yHB7utcZt5bfarRCmctpRtUd"): MevAgentBlocxRoute,
|
solana.MustPublicKeyFromBase58("3UQUKjhMKaY2S6bjcQD6yHB7utcZt5bfarRCmctpRtUd"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLx7XBqSg3LUPVf1bRgCnkJmgVZR8QEgDJBPqcRLHvp"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLx8KeZxinPwy6kkUgyzMLeqb2ARNsWjADG1dhSsVba"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLxADBknoNj8WAGw2W6GBYeq848Xx6ajhaymV1YvrHm"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLxAc88vRBwvcUQJEgcxNfBLvHPikY4csNsUmPeWea2"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLxQ88oCiTsL8Xj4YWekKi1hjrgmbE3J3FFZ2xZHR3h"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLxS7NoLuynNRJ4mCnEE2YbtwJFttYsEyp2ME7rp2yt"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLxW6mCov7VEbrKc3S9tcBRcfSzRnLCbNp3Dfn3SJG5"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLxXSGXs4mYPTC5okZXed1qzvjNwNJ48QJ82hT2V7w7"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLxYi3vojbbB7hVzVDVTdBLVPhp7GJ3ZB3BwdK5sFXi"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLxhLPgBXtUpX4b1bH3HatuMGMSKT9GnwtuCGiMSAqe"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLxpY1mniuFW4PgkNA4JiNxoeKHFszryi6tNgyZAiAA"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLxuETxd2tgWxBALNwPzAfHhsik4BzD3nrEBCiPNZQD"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLxuL2gK5FW7xfahvwLrxLyW76vcCpNsKQY2CmnE6kV"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("bLxv4Hnub7nDJWHs8s17o9bGU65Bnx6Yqp2fqtMgHmm"): MevAgentBlocxRoute,
|
||||||
solana.MustPublicKeyFromBase58("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq"): MevAgentNozomi,
|
solana.MustPublicKeyFromBase58("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq"): MevAgentNozomi,
|
||||||
solana.MustPublicKeyFromBase58("noz3jAjPiHuBPqiSPkkugaJDkJscPuRhYnSpbi8UvC4"): MevAgentNozomi,
|
solana.MustPublicKeyFromBase58("noz3jAjPiHuBPqiSPkkugaJDkJscPuRhYnSpbi8UvC4"): MevAgentNozomi,
|
||||||
solana.MustPublicKeyFromBase58("noz3str9KXfpKknefHji8L1mPgimezaiUyCHYMDv1GE"): MevAgentNozomi,
|
solana.MustPublicKeyFromBase58("noz3str9KXfpKknefHji8L1mPgimezaiUyCHYMDv1GE"): MevAgentNozomi,
|
||||||
@@ -343,7 +357,10 @@ var mevAgentFeeAddresses = map[solana.PublicKey]string{
|
|||||||
solana.MustPublicKeyFromBase58("bgDETv6tnt9mwYqAKebLXY5B5o6akiKJmAdU7Gd9G7H"): MevAgentNozomi,
|
solana.MustPublicKeyFromBase58("bgDETv6tnt9mwYqAKebLXY5B5o6akiKJmAdU7Gd9G7H"): MevAgentNozomi,
|
||||||
solana.MustPublicKeyFromBase58("bgH7YhymSykyvMa3nAZpzvrn73owJHU5iB75S1aiLT9"): MevAgentNozomi,
|
solana.MustPublicKeyFromBase58("bgH7YhymSykyvMa3nAZpzvrn73owJHU5iB75S1aiLT9"): MevAgentNozomi,
|
||||||
solana.MustPublicKeyFromBase58("pfngGVVQLiVRFbLWw3Ektiv17ef9NiRZbcgdAhh4ZEW"): MevAgentNozomi,
|
solana.MustPublicKeyFromBase58("pfngGVVQLiVRFbLWw3Ektiv17ef9NiRZbcgdAhh4ZEW"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("mwGELGMgGGrNL1UibNCQeJHDE7qdPptWRYB6noUHmTj"): MevAgentNozomi,
|
||||||
solana.MustPublicKeyFromBase58("nEFs3jph8HJt7honu3k7XtGUufMnwAvSXmXcKSPxryP"): MevAgentNozomi,
|
solana.MustPublicKeyFromBase58("nEFs3jph8HJt7honu3k7XtGUufMnwAvSXmXcKSPxryP"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("nzUMeyyucuHSCZLw1aaX14d1si2mffT4PjVNdZcybot"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("n6Tc3GoTheA1pYF4MPV57KGmn3DHJCCuk3wHZmcUyp"): MevAgentNozomi,
|
||||||
solana.MustPublicKeyFromBase58("Fa1con11xLjPddfzRwRUB16sbFZggp2JeJkCeWREyR8X"): MevagentFa1con,
|
solana.MustPublicKeyFromBase58("Fa1con11xLjPddfzRwRUB16sbFZggp2JeJkCeWREyR8X"): MevagentFa1con,
|
||||||
solana.MustPublicKeyFromBase58("Fa1con11TM1RuAQzbQzYjTy4Ekfap9Lnc9fnEbQYEd6Q"): MevagentFa1con,
|
solana.MustPublicKeyFromBase58("Fa1con11TM1RuAQzbQzYjTy4Ekfap9Lnc9fnEbQYEd6Q"): MevagentFa1con,
|
||||||
solana.MustPublicKeyFromBase58("Fa1con113Bvi76nS5AzUiRDC2fqjfzkNMUNRLgQybMYt"): MevagentFa1con,
|
solana.MustPublicKeyFromBase58("Fa1con113Bvi76nS5AzUiRDC2fqjfzkNMUNRLgQybMYt"): MevagentFa1con,
|
||||||
|
|||||||
22
parser.go
22
parser.go
@@ -268,12 +268,10 @@ func (tx *Tx) Parser() error {
|
|||||||
lastReserve := make(map[solana.PublicKey]reserveSnapshot, len(tx.Swaps))
|
lastReserve := make(map[solana.PublicKey]reserveSnapshot, len(tx.Swaps))
|
||||||
for _, swap := range tx.Swaps {
|
for _, swap := range tx.Swaps {
|
||||||
lastReserve[pairKey(swap)] = reserveSnapshot{
|
lastReserve[pairKey(swap)] = reserveSnapshot{
|
||||||
baseMint: swap.BaseMint,
|
baseMint: swap.BaseMint,
|
||||||
quoteMint: swap.QuoteMint,
|
quoteMint: swap.QuoteMint,
|
||||||
baseReserve: swap.BaseReserve,
|
baseReserve: swap.BaseReserve,
|
||||||
quoteReserve: swap.QuoteReserve,
|
quoteReserve: swap.QuoteReserve,
|
||||||
realQuoteReserve: swap.RealQuoteReserve,
|
|
||||||
virtualQuoteReserve: swap.VirtualQuoteReserve,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,8 +281,6 @@ func (tx *Tx) Parser() error {
|
|||||||
if tx.Swaps[i].BaseMint == v.baseMint && tx.Swaps[i].QuoteMint == v.quoteMint {
|
if tx.Swaps[i].BaseMint == v.baseMint && tx.Swaps[i].QuoteMint == v.quoteMint {
|
||||||
tx.Swaps[i].BaseReserve = v.baseReserve
|
tx.Swaps[i].BaseReserve = v.baseReserve
|
||||||
tx.Swaps[i].QuoteReserve = v.quoteReserve
|
tx.Swaps[i].QuoteReserve = v.quoteReserve
|
||||||
tx.Swaps[i].RealQuoteReserve = v.realQuoteReserve
|
|
||||||
tx.Swaps[i].VirtualQuoteReserve = v.virtualQuoteReserve
|
|
||||||
} else if tx.Swaps[i].BaseMint == v.quoteMint && tx.Swaps[i].QuoteMint == v.baseMint {
|
} else if tx.Swaps[i].BaseMint == v.quoteMint && tx.Swaps[i].QuoteMint == v.baseMint {
|
||||||
tx.Swaps[i].BaseReserve = v.quoteReserve
|
tx.Swaps[i].BaseReserve = v.quoteReserve
|
||||||
tx.Swaps[i].QuoteReserve = v.baseReserve
|
tx.Swaps[i].QuoteReserve = v.baseReserve
|
||||||
@@ -301,12 +297,10 @@ func (tx *Tx) Parser() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type reserveSnapshot struct {
|
type reserveSnapshot struct {
|
||||||
baseMint solana.PublicKey
|
baseMint solana.PublicKey
|
||||||
quoteMint solana.PublicKey
|
quoteMint solana.PublicKey
|
||||||
baseReserve decimal.Decimal
|
baseReserve decimal.Decimal
|
||||||
quoteReserve decimal.Decimal
|
quoteReserve decimal.Decimal
|
||||||
realQuoteReserve decimal.Decimal
|
|
||||||
virtualQuoteReserve decimal.Decimal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cloneSwapPrograms(src map[solana.PublicKey]swapParser) map[solana.PublicKey]swapParser {
|
func cloneSwapPrograms(src map[solana.PublicKey]swapParser) map[solana.PublicKey]swapParser {
|
||||||
|
|||||||
211
pumpamm.go
211
pumpamm.go
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ammBuyEventPrefix struct {
|
type ammBuyEvent struct {
|
||||||
TimeStamp int64
|
TimeStamp int64
|
||||||
BaseAmountOut uint64
|
BaseAmountOut uint64
|
||||||
MaxQuoteAmountIn uint64
|
MaxQuoteAmountIn uint64
|
||||||
@@ -45,38 +45,6 @@ type ammBuyEventPrefix struct {
|
|||||||
Cashback uint64
|
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 {
|
type ammCreatePoolEvent struct {
|
||||||
TimeStamp int64
|
TimeStamp int64
|
||||||
Index uint16
|
Index uint16
|
||||||
@@ -122,7 +90,7 @@ type ammDepositEvent struct {
|
|||||||
UserPoolTokenAccount solana.PublicKey
|
UserPoolTokenAccount solana.PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
type ammSellEventPrefix struct {
|
type ammSellEvent struct {
|
||||||
Timestamp int64
|
Timestamp int64
|
||||||
BaseAmountIn uint64
|
BaseAmountIn uint64
|
||||||
MinQuoteAmountOut uint64
|
MinQuoteAmountOut uint64
|
||||||
@@ -151,86 +119,6 @@ type ammSellEventPrefix struct {
|
|||||||
Cashback uint64
|
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 {
|
type ammWithdrawEvent struct {
|
||||||
Timestamp int64
|
Timestamp int64
|
||||||
LpTokenAmountIn uint64
|
LpTokenAmountIn uint64
|
||||||
@@ -361,7 +249,6 @@ func ammCreatePoolParser(tx *Tx, instruction Instruction, innerInstructions Inne
|
|||||||
QuoteAmount: decimal.NewFromUint64(createEvent.QuoteAmountIn),
|
QuoteAmount: decimal.NewFromUint64(createEvent.QuoteAmountIn),
|
||||||
BaseReserve: decimal.NewFromUint64(createEvent.PoolBaseAmount),
|
BaseReserve: decimal.NewFromUint64(createEvent.PoolBaseAmount),
|
||||||
QuoteReserve: decimal.NewFromUint64(createEvent.PoolQuoteAmount),
|
QuoteReserve: decimal.NewFromUint64(createEvent.PoolQuoteAmount),
|
||||||
RealQuoteReserve: decimal.NewFromUint64(createEvent.PoolQuoteAmount),
|
|
||||||
UserBaseBalance: decimal.Decimal{},
|
UserBaseBalance: decimal.Decimal{},
|
||||||
UserQuoteBalance: decimal.Decimal{},
|
UserQuoteBalance: decimal.Decimal{},
|
||||||
EntryContract: entryContract,
|
EntryContract: entryContract,
|
||||||
@@ -508,7 +395,6 @@ func failedTxAmmBuyParser(tx *Tx, instruction Instruction, innerInstructions Inn
|
|||||||
QuoteAmount: decimal.NewFromUint64(quoteAmount),
|
QuoteAmount: decimal.NewFromUint64(quoteAmount),
|
||||||
BaseReserve: baseReserve,
|
BaseReserve: baseReserve,
|
||||||
QuoteReserve: quoteReserve,
|
QuoteReserve: quoteReserve,
|
||||||
RealQuoteReserve: quoteReserve,
|
|
||||||
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
||||||
UserBaseBalance: userBase,
|
UserBaseBalance: userBase,
|
||||||
UserQuoteBalance: userQuote,
|
UserQuoteBalance: userQuote,
|
||||||
@@ -632,7 +518,6 @@ func failedTxAmmSellParser(tx *Tx, instruction Instruction, innerInstructions In
|
|||||||
QuoteAmount: decimal.NewFromUint64(quoteAmount),
|
QuoteAmount: decimal.NewFromUint64(quoteAmount),
|
||||||
BaseReserve: baseReserve,
|
BaseReserve: baseReserve,
|
||||||
QuoteReserve: quoteReserve,
|
QuoteReserve: quoteReserve,
|
||||||
RealQuoteReserve: quoteReserve,
|
|
||||||
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
||||||
UserBaseBalance: userBase,
|
UserBaseBalance: userBase,
|
||||||
UserQuoteBalance: userQuote,
|
UserQuoteBalance: userQuote,
|
||||||
@@ -672,7 +557,7 @@ func ammBuyParser(tx *Tx, instruction Instruction, innerInstructions InnerInstru
|
|||||||
if innerInstr.ProgramIDIndex == instruction.ProgramIDIndex &&
|
if innerInstr.ProgramIDIndex == instruction.ProgramIDIndex &&
|
||||||
bytes.Equal(innerInstr.Data[:8], pumpAmmEventDiscriminator[:]) &&
|
bytes.Equal(innerInstr.Data[:8], pumpAmmEventDiscriminator[:]) &&
|
||||||
bytes.Equal(innerInstr.Data[8:16], pumpAmmBuyEventDiscriminator[:]) {
|
bytes.Equal(innerInstr.Data[8:16], pumpAmmBuyEventDiscriminator[:]) {
|
||||||
event, err = decodeAmmBuyEvent(innerInstr.Data[16:])
|
err = agbinary.NewBorshDecoder(innerInstr.Data[16:]).Decode(&event)
|
||||||
if offset[1] == 0 {
|
if offset[1] == 0 {
|
||||||
offset[0] += 1
|
offset[0] += 1
|
||||||
} else {
|
} else {
|
||||||
@@ -747,30 +632,27 @@ func ammBuyParser(tx *Tx, instruction Instruction, innerInstructions InnerInstru
|
|||||||
if event.IxName == "buy" {
|
if event.IxName == "buy" {
|
||||||
quoteAmount = decimal.NewFromUint64(event.QuoteAmountIn)
|
quoteAmount = decimal.NewFromUint64(event.QuoteAmountIn)
|
||||||
}
|
}
|
||||||
realQuoteReserve, effectiveQuoteReserve := pumpAmmBuyPostQuoteReserves(event)
|
|
||||||
swap := Swap{
|
swap := Swap{
|
||||||
Program: SolProgramPumpAMM,
|
Program: SolProgramPumpAMM,
|
||||||
Event: "buy",
|
Event: "buy",
|
||||||
Pool: event.Pool,
|
Pool: event.Pool,
|
||||||
BaseMint: baseMint,
|
BaseMint: baseMint,
|
||||||
QuoteMint: quoteMint,
|
QuoteMint: quoteMint,
|
||||||
BaseTokenProgram: baseTokenProgram,
|
BaseTokenProgram: baseTokenProgram,
|
||||||
QuoteTokenProgram: quoteTokenProgram,
|
QuoteTokenProgram: quoteTokenProgram,
|
||||||
Creator: event.CoinCreator,
|
Creator: event.CoinCreator,
|
||||||
BaseMintDecimals: baseMintDecimals,
|
BaseMintDecimals: baseMintDecimals,
|
||||||
QuoteMintDecimals: quoteMintDecimals,
|
QuoteMintDecimals: quoteMintDecimals,
|
||||||
User: eventUser,
|
User: eventUser,
|
||||||
BaseAmount: decimal.NewFromUint64(event.BaseAmountOut),
|
BaseAmount: decimal.NewFromUint64(event.BaseAmountOut),
|
||||||
QuoteAmount: quoteAmount,
|
QuoteAmount: quoteAmount,
|
||||||
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserve - event.BaseAmountOut),
|
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserve - event.BaseAmountOut),
|
||||||
QuoteReserve: effectiveQuoteReserve,
|
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserve + event.QuoteAmountIn),
|
||||||
RealQuoteReserve: realQuoteReserve,
|
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
||||||
VirtualQuoteReserve: pumpAmmVirtualQuoteReserve(event.VirtualQuoteReserves),
|
Cashback: isCashbackCoin,
|
||||||
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
UserBaseBalance: userBase,
|
||||||
Cashback: isCashbackCoin,
|
UserQuoteBalance: userQuote,
|
||||||
UserBaseBalance: userBase,
|
EntryContract: entryContract,
|
||||||
UserQuoteBalance: userQuote,
|
|
||||||
EntryContract: entryContract,
|
|
||||||
}
|
}
|
||||||
if bytes.Equal(instruction.Data[:8], pumpAmmBuyV2Discriminator[:]) {
|
if bytes.Equal(instruction.Data[:8], pumpAmmBuyV2Discriminator[:]) {
|
||||||
swap.SetSwapAmountInfo(
|
swap.SetSwapAmountInfo(
|
||||||
@@ -817,7 +699,7 @@ func ammSellParser(tx *Tx, instruction Instruction, innerInstructions InnerInstr
|
|||||||
if innerInstr.ProgramIDIndex == instruction.ProgramIDIndex &&
|
if innerInstr.ProgramIDIndex == instruction.ProgramIDIndex &&
|
||||||
bytes.Equal(innerInstr.Data[:8], pumpAmmEventDiscriminator[:]) &&
|
bytes.Equal(innerInstr.Data[:8], pumpAmmEventDiscriminator[:]) &&
|
||||||
bytes.Equal(innerInstr.Data[8:16], pumpAmmSellEventDiscriminator[:]) {
|
bytes.Equal(innerInstr.Data[8:16], pumpAmmSellEventDiscriminator[:]) {
|
||||||
event, err = decodeAmmSellEvent(innerInstr.Data[16:])
|
err = agbinary.NewBorshDecoder(innerInstr.Data[16:]).Decode(&event)
|
||||||
if offset[1] == 0 {
|
if offset[1] == 0 {
|
||||||
offset[0] += 1
|
offset[0] += 1
|
||||||
} else {
|
} else {
|
||||||
@@ -888,30 +770,27 @@ func ammSellParser(tx *Tx, instruction Instruction, innerInstructions InnerInstr
|
|||||||
userQuote = userQuote.Add(decimal.NewFromUint64(userBalance))
|
userQuote = userQuote.Add(decimal.NewFromUint64(userBalance))
|
||||||
}
|
}
|
||||||
isCashbackCoin := event.CashbackFeeBasisPoints > 0 || event.Cashback > 0
|
isCashbackCoin := event.CashbackFeeBasisPoints > 0 || event.Cashback > 0
|
||||||
realQuoteReserve, effectiveQuoteReserve := pumpAmmSellPostQuoteReserves(event)
|
|
||||||
swap := Swap{
|
swap := Swap{
|
||||||
Program: SolProgramPumpAMM,
|
Program: SolProgramPumpAMM,
|
||||||
Event: "sell",
|
Event: "sell",
|
||||||
Pool: event.Pool,
|
Pool: event.Pool,
|
||||||
BaseMint: baseMint,
|
BaseMint: baseMint,
|
||||||
QuoteMint: quoteMint,
|
QuoteMint: quoteMint,
|
||||||
BaseTokenProgram: baseTokenProgram,
|
BaseTokenProgram: baseTokenProgram,
|
||||||
QuoteTokenProgram: quoteTokenProgram,
|
QuoteTokenProgram: quoteTokenProgram,
|
||||||
Creator: event.CoinCreator,
|
Creator: event.CoinCreator,
|
||||||
BaseMintDecimals: baseMintDecimals,
|
BaseMintDecimals: baseMintDecimals,
|
||||||
QuoteMintDecimals: quoteMintDecimals,
|
QuoteMintDecimals: quoteMintDecimals,
|
||||||
User: eventUser,
|
User: eventUser,
|
||||||
BaseAmount: decimal.NewFromUint64(event.BaseAmountIn),
|
BaseAmount: decimal.NewFromUint64(event.BaseAmountIn),
|
||||||
QuoteAmount: decimal.NewFromUint64(event.UserQuoteAmountOut),
|
QuoteAmount: decimal.NewFromUint64(event.UserQuoteAmountOut),
|
||||||
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves + event.BaseAmountIn),
|
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves + event.BaseAmountIn),
|
||||||
QuoteReserve: effectiveQuoteReserve,
|
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves - event.QuoteAmountOut),
|
||||||
RealQuoteReserve: realQuoteReserve,
|
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
||||||
VirtualQuoteReserve: pumpAmmVirtualQuoteReserve(event.VirtualQuoteReserves),
|
Cashback: isCashbackCoin,
|
||||||
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
UserBaseBalance: userBase,
|
||||||
Cashback: isCashbackCoin,
|
UserQuoteBalance: userQuote,
|
||||||
UserBaseBalance: userBase,
|
EntryContract: entryContract,
|
||||||
UserQuoteBalance: userQuote,
|
|
||||||
EntryContract: entryContract,
|
|
||||||
}
|
}
|
||||||
swap.SetSwapAmountInfo(
|
swap.SetSwapAmountInfo(
|
||||||
SwapModeExactIn,
|
SwapModeExactIn,
|
||||||
@@ -1014,7 +893,6 @@ func depositParse(tx *Tx, instruction Instruction, innerInstructions InnerInstru
|
|||||||
QuoteAmount: decimal.NewFromUint64(event.QuoteAmountIn),
|
QuoteAmount: decimal.NewFromUint64(event.QuoteAmountIn),
|
||||||
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves + event.BaseAmountIn),
|
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves + event.BaseAmountIn),
|
||||||
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves + event.QuoteAmountIn),
|
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves + event.QuoteAmountIn),
|
||||||
RealQuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves + event.QuoteAmountIn),
|
|
||||||
//Mayhem: false,
|
//Mayhem: false,
|
||||||
UserBaseBalance: decimal.NewFromUint64(event.UserBaseTokenReserves - event.BaseAmountIn),
|
UserBaseBalance: decimal.NewFromUint64(event.UserBaseTokenReserves - event.BaseAmountIn),
|
||||||
UserQuoteBalance: decimal.NewFromUint64(event.UserQuoteTokenReserves - event.QuoteAmountIn),
|
UserQuoteBalance: decimal.NewFromUint64(event.UserQuoteTokenReserves - event.QuoteAmountIn),
|
||||||
@@ -1116,7 +994,6 @@ func withdrawParse(tx *Tx, instruction Instruction, innerInstructions InnerInstr
|
|||||||
QuoteAmount: decimal.NewFromUint64(event.QuoteAmountOut),
|
QuoteAmount: decimal.NewFromUint64(event.QuoteAmountOut),
|
||||||
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves - event.BaseAmountOut),
|
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves - event.BaseAmountOut),
|
||||||
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves - event.QuoteAmountOut),
|
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves - event.QuoteAmountOut),
|
||||||
RealQuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves - event.QuoteAmountOut),
|
|
||||||
//Mayhem: false,
|
//Mayhem: false,
|
||||||
UserBaseBalance: decimal.NewFromUint64(event.UserBaseTokenReserves + event.BaseAmountOut),
|
UserBaseBalance: decimal.NewFromUint64(event.UserBaseTokenReserves + event.BaseAmountOut),
|
||||||
UserQuoteBalance: decimal.NewFromUint64(event.UserQuoteTokenReserves + event.QuoteAmountOut),
|
UserQuoteBalance: decimal.NewFromUint64(event.UserQuoteTokenReserves + event.QuoteAmountOut),
|
||||||
|
|||||||
127
pumpamm_test.go
127
pumpamm_test.go
@@ -1,13 +1,11 @@
|
|||||||
package pump_parser
|
package pump_parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
agbinary "github.com/gagliardetto/binary"
|
agbinary "github.com/gagliardetto/binary"
|
||||||
"github.com/gagliardetto/solana-go"
|
"github.com/gagliardetto/solana-go"
|
||||||
"github.com/mr-tron/base58"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -42,128 +40,3 @@ func TestAmmBuyEvent(t *testing.T) {
|
|||||||
fmt.Println(pumpAmmBuyEventDiscriminator)
|
fmt.Println(pumpAmmBuyEventDiscriminator)
|
||||||
fmt.Println(pumpGetFeesDiscriminator)
|
fmt.Println(pumpGetFeesDiscriminator)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecodeAmmBuyEventSuffixCompatibility(t *testing.T) {
|
|
||||||
prefix := ammBuyEventPrefix{TimeStamp: 123, PoolQuoteTokenReserve: 456}
|
|
||||||
|
|
||||||
legacy, err := decodeAmmBuyEvent(encodeAmmEventParts(t, prefix))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("decodeAmmBuyEvent(legacy) error = %v", err)
|
|
||||||
}
|
|
||||||
if legacy.TimeStamp != prefix.TimeStamp || legacy.PoolQuoteTokenReserve != prefix.PoolQuoteTokenReserve {
|
|
||||||
t.Fatalf("legacy prefix mismatch: %+v", legacy.ammBuyEventPrefix)
|
|
||||||
}
|
|
||||||
if legacy.BuybackFeeBasisPoints != 0 || legacy.BuybackFee != 0 || legacy.CanBoost || legacy.BaseSupply != 0 || !pumpAmmVirtualQuoteReserve(legacy.VirtualQuoteReserves).IsZero() {
|
|
||||||
t.Fatalf("legacy suffix is not zero-valued: %+v", legacy)
|
|
||||||
}
|
|
||||||
|
|
||||||
buyback := ammTradeEventBuybackSuffix{BuybackFeeBasisPoints: 1000, BuybackFee: 77}
|
|
||||||
withBuyback, err := decodeAmmBuyEvent(encodeAmmEventParts(t, prefix, buyback))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("decodeAmmBuyEvent(buyback) error = %v", err)
|
|
||||||
}
|
|
||||||
if withBuyback.BuybackFeeBasisPoints != buyback.BuybackFeeBasisPoints || withBuyback.BuybackFee != buyback.BuybackFee {
|
|
||||||
t.Fatalf("buyback suffix mismatch: %+v", withBuyback)
|
|
||||||
}
|
|
||||||
if withBuyback.CanBoost || withBuyback.BaseSupply != 0 || !pumpAmmVirtualQuoteReserve(withBuyback.VirtualQuoteReserves).IsZero() {
|
|
||||||
t.Fatalf("pre-virtual suffix has unexpected virtual fields: %+v", withBuyback)
|
|
||||||
}
|
|
||||||
|
|
||||||
negativeVirtual := agbinary.Int128(agbinary.Uint128{Lo: ^uint64(122), Hi: ^uint64(0)})
|
|
||||||
virtual := ammTradeEventVirtualSuffix{
|
|
||||||
VirtualQuoteReserves: negativeVirtual,
|
|
||||||
CanBoost: true,
|
|
||||||
BaseSupply: 1_000_000,
|
|
||||||
}
|
|
||||||
current, err := decodeAmmBuyEvent(encodeAmmEventParts(t, prefix, buyback, virtual))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("decodeAmmBuyEvent(current) error = %v", err)
|
|
||||||
}
|
|
||||||
if got := pumpAmmVirtualQuoteReserve(current.VirtualQuoteReserves).String(); got != "-123" {
|
|
||||||
t.Fatalf("VirtualQuoteReserves = %s, want -123", got)
|
|
||||||
}
|
|
||||||
if !current.CanBoost || current.BaseSupply != virtual.BaseSupply {
|
|
||||||
t.Fatalf("current suffix mismatch: %+v", current)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeAmmSellEventSuffixCompatibility(t *testing.T) {
|
|
||||||
prefix := ammSellEventPrefix{Timestamp: 321, PoolQuoteTokenReserves: 654, QuoteAmountOut: 54}
|
|
||||||
buyback := ammTradeEventBuybackSuffix{BuybackFeeBasisPoints: 900, BuybackFee: 88}
|
|
||||||
virtual := ammTradeEventVirtualSuffix{
|
|
||||||
VirtualQuoteReserves: agbinary.Int128(agbinary.Uint128{Lo: 987}),
|
|
||||||
CanBoost: true,
|
|
||||||
BaseSupply: 2_000_000,
|
|
||||||
}
|
|
||||||
|
|
||||||
event, err := decodeAmmSellEvent(encodeAmmEventParts(t, prefix, buyback, virtual))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("decodeAmmSellEvent() error = %v", err)
|
|
||||||
}
|
|
||||||
if event.Timestamp != prefix.Timestamp || event.BuybackFee != buyback.BuybackFee || !event.CanBoost || event.BaseSupply != virtual.BaseSupply {
|
|
||||||
t.Fatalf("decoded sell event mismatch: %+v", event)
|
|
||||||
}
|
|
||||||
if got := pumpAmmVirtualQuoteReserve(event.VirtualQuoteReserves).String(); got != "987" {
|
|
||||||
t.Fatalf("VirtualQuoteReserves = %s, want 987", got)
|
|
||||||
}
|
|
||||||
real, effective := pumpAmmSellPostQuoteReserves(event)
|
|
||||||
if real.String() != "600" || effective.String() != "1587" {
|
|
||||||
t.Fatalf("post reserves = real %s effective %s", real, effective)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeAmmTradeEventRejectsTruncatedSuffix(t *testing.T) {
|
|
||||||
prefix := encodeAmmEventParts(t, ammBuyEventPrefix{})
|
|
||||||
for _, suffixLength := range []int{1, 15, 17, 40} {
|
|
||||||
data := append(append([]byte(nil), prefix...), make([]byte, suffixLength)...)
|
|
||||||
if _, err := decodeAmmBuyEvent(data); err == nil {
|
|
||||||
t.Fatalf("decodeAmmBuyEvent() error = nil for %d-byte suffix", suffixLength)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeAmmBuyEventDevnetVirtualReserve(t *testing.T) {
|
|
||||||
const eventDataBase58 = "6MF1ykxMQW5eFmo1ErWgds8V2wfEyqJPjEsoz4yRoKQCktPze5U297neF7huAQQ9DhxEJqHPwezxUh9mcQEsfdmnwW84YVxcfEzDUduRqc1zty2dLQZcFGzFeFC6fmpX75tkvuMvTN89HekHa9TEikGRuXhtWkeacjNoVqfbxXUp3FQxGSFoyeBaSdAfPMvXJ7zN4ALTDtn3a5g7HxJCPPWeUtYGcJig6RpKrC3DzAQ3x5SiiJiVhK1zdocmkkRXuTezt5L5Svrf9pbM4QkkwgvxrYqfUs5YeaLNqWYtNZfimLtENhQJcfrhQh2svCosCJ2Ux5iyyLZwJu7VLZibD41CwxuF6ngmrhde8BmEegmQ9f3L4qYhqgW5RJSiu1JnKMFqmTckuwGydQfTiyA9RsCtFxiazaAVim9SMARU2fqe9zfAPdRjBetAvTGkBxrERmmzhWVhER6g448mV1E1eeQjFWYRhtR33hSUpNUUP6dUF8CWae2BuJxaKexpiY3pyqUwbH7osvexbff8jpfpZGubP5UWjqhwijtF8tdkoEdAnb2TgWXqXMu4by5VDsEzwbgNK1DbkGbxrcXYwFTNskqoUqoNLPRaAcfnjTMA9cVme62HAj5WvxuzpL9Gpw7ZzSE2LYnVvzTa6PnN2bbpuqJpZ3d"
|
|
||||||
data, err := base58.Decode(eventDataBase58)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("base58.Decode() error = %v", err)
|
|
||||||
}
|
|
||||||
if len(data) < 16 || !bytes.Equal(data[:8], pumpAmmEventDiscriminator[:]) || !bytes.Equal(data[8:16], pumpAmmBuyEventDiscriminator[:]) {
|
|
||||||
t.Fatalf("unexpected event discriminator: len=%d", len(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
event, err := decodeAmmBuyEvent(data[16:])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("decodeAmmBuyEvent(devnet) error = %v", err)
|
|
||||||
}
|
|
||||||
if event.TimeStamp != 1784271481 || event.PoolQuoteTokenReserve != 2_213_159_021 || event.QuoteAmountIn != 100_000_000 || event.QuoteAmountInWithLpFee != 98_785_184 {
|
|
||||||
t.Fatalf("devnet buy event core fields mismatch: %+v", event)
|
|
||||||
}
|
|
||||||
if event.BuybackFeeBasisPoints != 1000 || event.BuybackFee != 91_851 {
|
|
||||||
t.Fatalf("devnet buyback fields mismatch: bps=%d fee=%d", event.BuybackFeeBasisPoints, event.BuybackFee)
|
|
||||||
}
|
|
||||||
if got := pumpAmmVirtualQuoteReserve(event.VirtualQuoteReserves).String(); got != "583150126" {
|
|
||||||
t.Fatalf("VirtualQuoteReserves = %s, want 583150126", got)
|
|
||||||
}
|
|
||||||
if !event.CanBoost || event.BaseSupply != 1_000_000_000_000_000 {
|
|
||||||
t.Fatalf("devnet boost metadata mismatch: can_boost=%t base_supply=%d", event.CanBoost, event.BaseSupply)
|
|
||||||
}
|
|
||||||
|
|
||||||
real, effective := pumpAmmBuyPostQuoteReserves(event)
|
|
||||||
if real.String() != "2313159021" || effective.String() != "2896309147" {
|
|
||||||
t.Fatalf("post reserves = real %s effective %s", real, effective)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeAmmEventParts(t *testing.T, parts ...any) []byte {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
encoder := agbinary.NewBorshEncoder(&buf)
|
|
||||||
for _, part := range parts {
|
|
||||||
if err := encoder.Encode(part); err != nil {
|
|
||||||
t.Fatalf("borsh encode %T: %v", part, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf.Bytes()
|
|
||||||
}
|
|
||||||
|
|||||||
10
tx.go
10
tx.go
@@ -43,12 +43,10 @@ type Swap struct {
|
|||||||
ActualLimitAmountSide SwapAmountSide
|
ActualLimitAmountSide SwapAmountSide
|
||||||
SlippageBps decimal.Decimal
|
SlippageBps decimal.Decimal
|
||||||
|
|
||||||
BaseReserve decimal.Decimal
|
BaseReserve decimal.Decimal
|
||||||
QuoteReserve decimal.Decimal
|
QuoteReserve decimal.Decimal
|
||||||
RealQuoteReserve decimal.Decimal
|
Mayhem bool
|
||||||
VirtualQuoteReserve decimal.Decimal
|
Cashback bool
|
||||||
Mayhem bool
|
|
||||||
Cashback bool
|
|
||||||
|
|
||||||
UserBaseBalance decimal.Decimal
|
UserBaseBalance decimal.Decimal
|
||||||
UserQuoteBalance decimal.Decimal
|
UserQuoteBalance decimal.Decimal
|
||||||
|
|||||||
102
tx_binary.go
102
tx_binary.go
@@ -8,7 +8,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"iter"
|
"iter"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@@ -19,9 +18,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
txBinarySchemaVersionV3 uint16 = 3
|
txBinarySchemaVersionV3 uint16 = 3
|
||||||
txBinarySchemaVersionV4 uint16 = 4
|
txBinarySchemaVersionCurrent uint16 = 4
|
||||||
txBinarySchemaVersionV5 uint16 = 5
|
|
||||||
txBinarySchemaVersionCurrent = txBinarySchemaVersionV5
|
|
||||||
txBinaryEnumVersionV1 uint16 = 1
|
txBinaryEnumVersionV1 uint16 = 1
|
||||||
|
|
||||||
txBinarySOLScale int32 = 9
|
txBinarySOLScale int32 = 9
|
||||||
@@ -105,9 +102,6 @@ type SwapBinary struct {
|
|||||||
LpMint uint32
|
LpMint uint32
|
||||||
|
|
||||||
AfterSOLBalance float64
|
AfterSOLBalance float64
|
||||||
|
|
||||||
RealQuoteReserve uint64
|
|
||||||
VirtualQuoteReserve [16]byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TxsBinary struct {
|
type TxsBinary struct {
|
||||||
@@ -665,7 +659,7 @@ func (tx *TxBinary) ToTx() (*Tx, error) {
|
|||||||
if len(tx.Swaps) > 0 {
|
if len(tx.Swaps) > 0 {
|
||||||
out.Swaps = make([]Swap, 0, len(tx.Swaps))
|
out.Swaps = make([]Swap, 0, len(tx.Swaps))
|
||||||
for i, swap := range tx.Swaps {
|
for i, swap := range tx.Swaps {
|
||||||
decodedSwap, err := swap.toSwap(tx.AddressTable, i, tx.SchemaVersion)
|
decodedSwap, err := swap.toSwap(tx.AddressTable, i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -803,12 +797,6 @@ func newSwapBinary(swap Swap, index int, addressIndex *txBinaryAddressIndex) (Sw
|
|||||||
if out.QuoteReserve, err = txBinaryDecimalToFloat64Raw(swap.QuoteReserve, fmt.Sprintf("swap[%d].quote_reserve", index)); err != nil {
|
if out.QuoteReserve, err = txBinaryDecimalToFloat64Raw(swap.QuoteReserve, fmt.Sprintf("swap[%d].quote_reserve", index)); err != nil {
|
||||||
return SwapBinary{}, err
|
return SwapBinary{}, err
|
||||||
}
|
}
|
||||||
if out.RealQuoteReserve, err = txBinaryDecimalToUint64(swap.RealQuoteReserve, fmt.Sprintf("swap[%d].real_quote_reserve", index)); err != nil {
|
|
||||||
return SwapBinary{}, err
|
|
||||||
}
|
|
||||||
if out.VirtualQuoteReserve, err = txBinaryDecimalToInt128(swap.VirtualQuoteReserve, fmt.Sprintf("swap[%d].virtual_quote_reserve", index)); err != nil {
|
|
||||||
return SwapBinary{}, err
|
|
||||||
}
|
|
||||||
if out.UserBaseBalance, err = txBinaryDecimalToUint64(swap.UserBaseBalance, fmt.Sprintf("swap[%d].user_base_balance", index)); err != nil {
|
if out.UserBaseBalance, err = txBinaryDecimalToUint64(swap.UserBaseBalance, fmt.Sprintf("swap[%d].user_base_balance", index)); err != nil {
|
||||||
return SwapBinary{}, err
|
return SwapBinary{}, err
|
||||||
}
|
}
|
||||||
@@ -822,7 +810,7 @@ func newSwapBinary(swap Swap, index int, addressIndex *txBinaryAddressIndex) (Sw
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (swap SwapBinary) toSwap(addressTable []solana.PublicKey, index int, schemaVersion uint16) (Swap, error) {
|
func (swap SwapBinary) toSwap(addressTable []solana.PublicKey, index int) (Swap, error) {
|
||||||
pool, err := txBinaryAddressAt(addressTable, swap.Pool, fmt.Sprintf("swap[%d].pool", index))
|
pool, err := txBinaryAddressAt(addressTable, swap.Pool, fmt.Sprintf("swap[%d].pool", index))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Swap{}, err
|
return Swap{}, err
|
||||||
@@ -876,20 +864,6 @@ func (swap SwapBinary) toSwap(addressTable []solana.PublicKey, index int, schema
|
|||||||
return Swap{}, err
|
return Swap{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
quoteReserve := txBinaryFloat64ToDecimalRaw(swap.QuoteReserve)
|
|
||||||
realQuoteReserve := quoteReserve
|
|
||||||
virtualQuoteReserve := decimal.Zero
|
|
||||||
if schemaVersion >= txBinarySchemaVersionV5 {
|
|
||||||
realQuoteReserve = decimal.NewFromUint64(swap.RealQuoteReserve)
|
|
||||||
virtualQuoteReserve = txBinaryInt128ToDecimal(swap.VirtualQuoteReserve)
|
|
||||||
// A v3/v4 batch upgraded by the streaming merger has no component
|
|
||||||
// bytes to carry forward. Preserve its legacy meaning after the
|
|
||||||
// merged output is written as v5.
|
|
||||||
if realQuoteReserve.IsZero() && virtualQuoteReserve.IsZero() && !quoteReserve.IsZero() {
|
|
||||||
realQuoteReserve = quoteReserve
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Swap{
|
return Swap{
|
||||||
Program: swap.Program,
|
Program: swap.Program,
|
||||||
Event: swap.Event,
|
Event: swap.Event,
|
||||||
@@ -919,9 +893,7 @@ func (swap SwapBinary) toSwap(addressTable []solana.PublicKey, index int, schema
|
|||||||
ActualLimitAmountSide: swap.ActualLimitAmountSide,
|
ActualLimitAmountSide: swap.ActualLimitAmountSide,
|
||||||
SlippageBps: decimal.NewFromUint64(swap.SlippageBps),
|
SlippageBps: decimal.NewFromUint64(swap.SlippageBps),
|
||||||
BaseReserve: txBinaryFloat64ToDecimalRaw(swap.BaseReserve),
|
BaseReserve: txBinaryFloat64ToDecimalRaw(swap.BaseReserve),
|
||||||
QuoteReserve: quoteReserve,
|
QuoteReserve: txBinaryFloat64ToDecimalRaw(swap.QuoteReserve),
|
||||||
RealQuoteReserve: realQuoteReserve,
|
|
||||||
VirtualQuoteReserve: virtualQuoteReserve,
|
|
||||||
Mayhem: swap.Mayhem,
|
Mayhem: swap.Mayhem,
|
||||||
Cashback: swap.Cashback,
|
Cashback: swap.Cashback,
|
||||||
UserBaseBalance: decimal.NewFromUint64(swap.UserBaseBalance),
|
UserBaseBalance: decimal.NewFromUint64(swap.UserBaseBalance),
|
||||||
@@ -1099,44 +1071,6 @@ func txBinaryDecimalToUint64(value decimal.Decimal, field string) (uint64, error
|
|||||||
return bigInt.Uint64(), nil
|
return bigInt.Uint64(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func txBinaryDecimalToInt128(value decimal.Decimal, field string) ([16]byte, error) {
|
|
||||||
var out [16]byte
|
|
||||||
if !value.Equal(value.Truncate(0)) {
|
|
||||||
return out, fmt.Errorf("%s must be an integer, got %s", field, value.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
integer := value.BigInt()
|
|
||||||
limit := new(big.Int).Lsh(big.NewInt(1), 127)
|
|
||||||
minimum := new(big.Int).Neg(new(big.Int).Set(limit))
|
|
||||||
maximum := new(big.Int).Sub(new(big.Int).Set(limit), big.NewInt(1))
|
|
||||||
if integer.Cmp(minimum) < 0 || integer.Cmp(maximum) > 0 {
|
|
||||||
return out, fmt.Errorf("%s overflows int128: %s", field, value.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned := new(big.Int).Set(integer)
|
|
||||||
if unsigned.Sign() < 0 {
|
|
||||||
unsigned.Add(unsigned, new(big.Int).Lsh(big.NewInt(1), 128))
|
|
||||||
}
|
|
||||||
var bigEndian [16]byte
|
|
||||||
unsigned.FillBytes(bigEndian[:])
|
|
||||||
for i := range out {
|
|
||||||
out[i] = bigEndian[len(bigEndian)-1-i]
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func txBinaryInt128ToDecimal(raw [16]byte) decimal.Decimal {
|
|
||||||
var bigEndian [16]byte
|
|
||||||
for i := range raw {
|
|
||||||
bigEndian[i] = raw[len(raw)-1-i]
|
|
||||||
}
|
|
||||||
integer := new(big.Int).SetBytes(bigEndian[:])
|
|
||||||
if raw[len(raw)-1]&0x80 != 0 {
|
|
||||||
integer.Sub(integer, new(big.Int).Lsh(big.NewInt(1), 128))
|
|
||||||
}
|
|
||||||
return decimal.NewFromBigInt(integer, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func txBinaryScaledDecimalToUint64(value decimal.Decimal, scale int32, field string) (uint64, error) {
|
func txBinaryScaledDecimalToUint64(value decimal.Decimal, scale int32, field string) (uint64, error) {
|
||||||
return txBinaryDecimalToUint64(value.Shift(scale), field)
|
return txBinaryDecimalToUint64(value.Shift(scale), field)
|
||||||
}
|
}
|
||||||
@@ -1245,7 +1179,7 @@ func (enc *txBinaryEncoder) writeTxBinaryBody(tx *TxBinary, enumTable *txBinaryE
|
|||||||
enc.writeUint32(tx.Signer)
|
enc.writeUint32(tx.Signer)
|
||||||
enc.writeUint64(tx.Block)
|
enc.writeUint64(tx.Block)
|
||||||
enc.writeUint64(tx.BlockIndex)
|
enc.writeUint64(tx.BlockIndex)
|
||||||
if tx.SchemaVersion >= txBinarySchemaVersionV4 {
|
if tx.SchemaVersion >= txBinarySchemaVersionCurrent {
|
||||||
enc.writeUint64(uint64(tx.BlockAt))
|
enc.writeUint64(uint64(tx.BlockAt))
|
||||||
}
|
}
|
||||||
enc.writeBool(tx.TxHash != nil)
|
enc.writeBool(tx.TxHash != nil)
|
||||||
@@ -1264,7 +1198,7 @@ func (enc *txBinaryEncoder) writeTxBinaryBody(tx *TxBinary, enumTable *txBinaryE
|
|||||||
if err := enc.writeMevAgentEntries(tx.MevAgent, enumTable); err != nil {
|
if err := enc.writeMevAgentEntries(tx.MevAgent, enumTable); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := enc.writeSwaps(tx.Swaps, enumTable, tx.SchemaVersion); err != nil {
|
if err := enc.writeSwaps(tx.Swaps, enumTable); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -1296,7 +1230,7 @@ func (enc *txBinaryEncoder) writeMevAgentEntries(entries []MevAgentBinary, enumT
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (enc *txBinaryEncoder) writeSwaps(swaps []SwapBinary, enumTable *txBinaryEnumTable, schemaVersion uint16) error {
|
func (enc *txBinaryEncoder) writeSwaps(swaps []SwapBinary, enumTable *txBinaryEnumTable) error {
|
||||||
enc.writeUint32(uint32(len(swaps)))
|
enc.writeUint32(uint32(len(swaps)))
|
||||||
for i, swap := range swaps {
|
for i, swap := range swaps {
|
||||||
programID, err := enumTable.programs.id(swap.Program)
|
programID, err := enumTable.programs.id(swap.Program)
|
||||||
@@ -1346,10 +1280,6 @@ func (enc *txBinaryEncoder) writeSwaps(swaps []SwapBinary, enumTable *txBinaryEn
|
|||||||
enc.writeUint32(swap.MigrateTopProgram)
|
enc.writeUint32(swap.MigrateTopProgram)
|
||||||
enc.writeUint32(swap.LpMint)
|
enc.writeUint32(swap.LpMint)
|
||||||
enc.writeFloat64(swap.AfterSOLBalance)
|
enc.writeFloat64(swap.AfterSOLBalance)
|
||||||
if schemaVersion >= txBinarySchemaVersionV5 {
|
|
||||||
enc.writeUint64(swap.RealQuoteReserve)
|
|
||||||
enc.writeBytes(swap.VirtualQuoteReserve[:])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1465,7 +1395,7 @@ func (dec *txBinaryDecoder) readMevAgentEntries(enumTable *txBinaryEnumTable) ([
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dec *txBinaryDecoder) readSwaps(enumTable *txBinaryEnumTable, _ []solana.PublicKey) ([]SwapBinary, error) {
|
func (dec *txBinaryDecoder) readSwaps(enumTable *txBinaryEnumTable, _ []solana.PublicKey) ([]SwapBinary, error) {
|
||||||
return txBinaryReadSwaps(dec, enumTable, txBinarySchemaVersionCurrent)
|
return txBinaryReadSwaps(dec, enumTable)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dec *txBinaryDecoder) readTxBinaryBody(tx *TxBinary, enumTable *txBinaryEnumTable, addressTable []solana.PublicKey) error {
|
func (dec *txBinaryDecoder) readTxBinaryBody(tx *TxBinary, enumTable *txBinaryEnumTable, addressTable []solana.PublicKey) error {
|
||||||
@@ -1721,7 +1651,7 @@ func txBinaryReadMevAgentEntries(dec txBinaryBodyReader, enumTable *txBinaryEnum
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func txBinaryReadSwaps(dec txBinaryBodyReader, enumTable *txBinaryEnumTable, schemaVersion uint16) ([]SwapBinary, error) {
|
func txBinaryReadSwaps(dec txBinaryBodyReader, enumTable *txBinaryEnumTable) ([]SwapBinary, error) {
|
||||||
count, err := dec.readUint32()
|
count, err := dec.readUint32()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -1868,16 +1798,6 @@ func txBinaryReadSwaps(dec txBinaryBodyReader, enumTable *txBinaryEnumTable, sch
|
|||||||
if swap.AfterSOLBalance, err = dec.readFloat64(); err != nil {
|
if swap.AfterSOLBalance, err = dec.readFloat64(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if schemaVersion >= txBinarySchemaVersionV5 {
|
|
||||||
if swap.RealQuoteReserve, err = dec.readUint64(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rawVirtualQuoteReserve, err := dec.readN(len(swap.VirtualQuoteReserve))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
copy(swap.VirtualQuoteReserve[:], rawVirtualQuoteReserve)
|
|
||||||
}
|
|
||||||
out = append(out, swap)
|
out = append(out, swap)
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
@@ -1895,7 +1815,7 @@ func txBinaryReadTxBody(dec txBinaryBodyReader, tx *TxBinary, enumTable *txBinar
|
|||||||
if tx.BlockIndex, err = dec.readUint64(); err != nil {
|
if tx.BlockIndex, err = dec.readUint64(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if tx.SchemaVersion >= txBinarySchemaVersionV4 {
|
if tx.SchemaVersion >= txBinarySchemaVersionCurrent {
|
||||||
blockAt, err := dec.readUint64()
|
blockAt, err := dec.readUint64()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -1943,7 +1863,7 @@ func txBinaryReadTxBody(dec txBinaryBodyReader, tx *TxBinary, enumTable *txBinar
|
|||||||
if tx.MevAgent, err = txBinaryReadMevAgentEntries(dec, enumTable); err != nil {
|
if tx.MevAgent, err = txBinaryReadMevAgentEntries(dec, enumTable); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if tx.Swaps, err = txBinaryReadSwaps(dec, enumTable, tx.SchemaVersion); err != nil {
|
if tx.Swaps, err = txBinaryReadSwaps(dec, enumTable); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -82,8 +82,6 @@ func TestTxBinaryRoundTrip(t *testing.T) {
|
|||||||
SlippageBps: decimal.RequireFromString("833.3333"),
|
SlippageBps: decimal.RequireFromString("833.3333"),
|
||||||
BaseReserve: decimal.NewFromInt(5555),
|
BaseReserve: decimal.NewFromInt(5555),
|
||||||
QuoteReserve: decimal.NewFromInt(9999),
|
QuoteReserve: decimal.NewFromInt(9999),
|
||||||
RealQuoteReserve: decimal.NewFromInt(10122),
|
|
||||||
VirtualQuoteReserve: decimal.NewFromInt(-123),
|
|
||||||
Mayhem: true,
|
Mayhem: true,
|
||||||
Cashback: false,
|
Cashback: false,
|
||||||
UserBaseBalance: decimal.NewFromInt(777),
|
UserBaseBalance: decimal.NewFromInt(777),
|
||||||
@@ -204,12 +202,6 @@ func TestTxBinaryRoundTrip(t *testing.T) {
|
|||||||
if !swap.QuoteReserve.Equal(original.Swaps[0].QuoteReserve) {
|
if !swap.QuoteReserve.Equal(original.Swaps[0].QuoteReserve) {
|
||||||
t.Fatalf("swap.QuoteReserve = %s, want %s", swap.QuoteReserve, original.Swaps[0].QuoteReserve)
|
t.Fatalf("swap.QuoteReserve = %s, want %s", swap.QuoteReserve, original.Swaps[0].QuoteReserve)
|
||||||
}
|
}
|
||||||
if !swap.RealQuoteReserve.Equal(original.Swaps[0].RealQuoteReserve) {
|
|
||||||
t.Fatalf("swap.RealQuoteReserve = %s, want %s", swap.RealQuoteReserve, original.Swaps[0].RealQuoteReserve)
|
|
||||||
}
|
|
||||||
if !swap.VirtualQuoteReserve.Equal(original.Swaps[0].VirtualQuoteReserve) {
|
|
||||||
t.Fatalf("swap.VirtualQuoteReserve = %s, want %s", swap.VirtualQuoteReserve, original.Swaps[0].VirtualQuoteReserve)
|
|
||||||
}
|
|
||||||
if !swap.UserBaseBalance.Equal(original.Swaps[0].UserBaseBalance) {
|
if !swap.UserBaseBalance.Equal(original.Swaps[0].UserBaseBalance) {
|
||||||
t.Fatalf("swap.UserBaseBalance = %s, want %s", swap.UserBaseBalance, original.Swaps[0].UserBaseBalance)
|
t.Fatalf("swap.UserBaseBalance = %s, want %s", swap.UserBaseBalance, original.Swaps[0].UserBaseBalance)
|
||||||
}
|
}
|
||||||
@@ -1025,89 +1017,6 @@ func TestTxBinaryDecodeSchemaV3LeavesBlockAtZero(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTxBinaryDecodeSchemaV4DefaultsQuoteReserveComponents(t *testing.T) {
|
|
||||||
original := &Tx{
|
|
||||||
Signer: mustPubKey("So11111111111111111111111111111111111111112"),
|
|
||||||
Block: 42,
|
|
||||||
BlockIndex: 2,
|
|
||||||
BlockAt: 1710000042,
|
|
||||||
Swaps: []Swap{
|
|
||||||
{
|
|
||||||
Program: SolProgramPumpAMM,
|
|
||||||
Event: TxEventBuy,
|
|
||||||
QuoteReserve: decimal.NewFromInt(123456789),
|
|
||||||
RealQuoteReserve: decimal.NewFromInt(123000000),
|
|
||||||
VirtualQuoteReserve: decimal.NewFromInt(456789),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
binaryTx, err := NewTxBinary(original)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("NewTxBinary() error = %v", err)
|
|
||||||
}
|
|
||||||
binaryTx.SchemaVersion = txBinarySchemaVersionV4
|
|
||||||
encoded, err := binaryTx.MarshalBinary()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("MarshalBinary(v4) error = %v", err)
|
|
||||||
}
|
|
||||||
decoded, err := DecodeTxBinary(encoded)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("DecodeTxBinary(v4) error = %v", err)
|
|
||||||
}
|
|
||||||
if decoded.BlockAt != original.BlockAt {
|
|
||||||
t.Fatalf("BlockAt = %d, want %d", decoded.BlockAt, original.BlockAt)
|
|
||||||
}
|
|
||||||
if len(decoded.Swaps) != 1 {
|
|
||||||
t.Fatalf("Swaps len = %d, want 1", len(decoded.Swaps))
|
|
||||||
}
|
|
||||||
swap := decoded.Swaps[0]
|
|
||||||
if !swap.QuoteReserve.Equal(original.Swaps[0].QuoteReserve) {
|
|
||||||
t.Fatalf("QuoteReserve = %s, want %s", swap.QuoteReserve, original.Swaps[0].QuoteReserve)
|
|
||||||
}
|
|
||||||
if !swap.RealQuoteReserve.Equal(swap.QuoteReserve) {
|
|
||||||
t.Fatalf("RealQuoteReserve = %s, want legacy QuoteReserve %s", swap.RealQuoteReserve, swap.QuoteReserve)
|
|
||||||
}
|
|
||||||
if !swap.VirtualQuoteReserve.IsZero() {
|
|
||||||
t.Fatalf("VirtualQuoteReserve = %s, want 0", swap.VirtualQuoteReserve)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTxBinarySignedInt128RoundTrip(t *testing.T) {
|
|
||||||
values := []string{
|
|
||||||
"-170141183460469231731687303715884105728",
|
|
||||||
"-123",
|
|
||||||
"-1",
|
|
||||||
"0",
|
|
||||||
"1",
|
|
||||||
"123",
|
|
||||||
"170141183460469231731687303715884105727",
|
|
||||||
}
|
|
||||||
for _, value := range values {
|
|
||||||
t.Run(value, func(t *testing.T) {
|
|
||||||
want := decimal.RequireFromString(value)
|
|
||||||
raw, err := txBinaryDecimalToInt128(want, "value")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("txBinaryDecimalToInt128() error = %v", err)
|
|
||||||
}
|
|
||||||
if got := txBinaryInt128ToDecimal(raw); !got.Equal(want) {
|
|
||||||
t.Fatalf("round trip = %s, want %s", got, want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
invalid := []string{
|
|
||||||
"-170141183460469231731687303715884105729",
|
|
||||||
"170141183460469231731687303715884105728",
|
|
||||||
"1.5",
|
|
||||||
}
|
|
||||||
for _, value := range invalid {
|
|
||||||
if _, err := txBinaryDecimalToInt128(decimal.RequireFromString(value), "value"); err == nil {
|
|
||||||
t.Fatalf("txBinaryDecimalToInt128(%s) error = nil", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMergeTxsBinaryBytesUpgradesSchemaV3AndPreservesV4BlockAt(t *testing.T) {
|
func TestMergeTxsBinaryBytesUpgradesSchemaV3AndPreservesV4BlockAt(t *testing.T) {
|
||||||
legacyTx := Tx{
|
legacyTx := Tx{
|
||||||
Signer: mustPubKey("So11111111111111111111111111111111111111112"),
|
Signer: mustPubKey("So11111111111111111111111111111111111111112"),
|
||||||
@@ -1120,18 +1029,11 @@ func TestMergeTxsBinaryBytesUpgradesSchemaV3AndPreservesV4BlockAt(t *testing.T)
|
|||||||
Block: 52,
|
Block: 52,
|
||||||
BlockIndex: 2,
|
BlockIndex: 2,
|
||||||
BlockAt: 1710000052,
|
BlockAt: 1710000052,
|
||||||
Swaps: []Swap{
|
|
||||||
{
|
|
||||||
Program: SolProgramPumpAMM,
|
|
||||||
Event: TxEventBuy,
|
|
||||||
QuoteReserve: decimal.NewFromInt(222),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
merged, err := MergeTxsBinaryBytes([][]byte{
|
merged, err := MergeTxsBinaryBytes([][]byte{
|
||||||
mustEncodeTxsBinaryV3(t, []Tx{legacyTx}),
|
mustEncodeTxsBinaryV3(t, []Tx{legacyTx}),
|
||||||
mustEncodeTxsBinaryV4(t, []Tx{currentTx}),
|
mustEncodeTxsBinary(t, []Tx{currentTx}),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("MergeTxsBinaryBytes(v3,v4) error = %v", err)
|
t.Fatalf("MergeTxsBinaryBytes(v3,v4) error = %v", err)
|
||||||
@@ -1158,9 +1060,6 @@ func TestMergeTxsBinaryBytesUpgradesSchemaV3AndPreservesV4BlockAt(t *testing.T)
|
|||||||
if decoded[1].BlockAt != currentTx.BlockAt {
|
if decoded[1].BlockAt != currentTx.BlockAt {
|
||||||
t.Fatalf("current BlockAt = %d, want %d", decoded[1].BlockAt, currentTx.BlockAt)
|
t.Fatalf("current BlockAt = %d, want %d", decoded[1].BlockAt, currentTx.BlockAt)
|
||||||
}
|
}
|
||||||
if len(decoded[1].Swaps) != 1 || !decoded[1].Swaps[0].RealQuoteReserve.Equal(currentTx.Swaps[0].QuoteReserve) || !decoded[1].Swaps[0].VirtualQuoteReserve.IsZero() {
|
|
||||||
t.Fatalf("v4 quote reserve components were not preserved: %+v", decoded[1].Swaps)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustPubKey(value string) solana.PublicKey {
|
func mustPubKey(value string) solana.PublicKey {
|
||||||
@@ -1210,24 +1109,6 @@ func mustEncodeTxsBinaryV3(t *testing.T, txs []Tx) []byte {
|
|||||||
return encoded
|
return encoded
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustEncodeTxsBinaryV4(t *testing.T, txs []Tx) []byte {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
binaryTxs, err := NewTxsBinary(txs)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("NewTxsBinary() error = %v", err)
|
|
||||||
}
|
|
||||||
binaryTxs.SchemaVersion = txBinarySchemaVersionV4
|
|
||||||
for i := range binaryTxs.Txs {
|
|
||||||
binaryTxs.Txs[i].SchemaVersion = txBinarySchemaVersionV4
|
|
||||||
}
|
|
||||||
encoded, err := binaryTxs.MarshalBinary()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("MarshalBinary(v4) error = %v", err)
|
|
||||||
}
|
|
||||||
return encoded
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustTxBinary(t *testing.T, data []byte) *TxsBinary {
|
func mustTxBinary(t *testing.T, data []byte) *TxsBinary {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user