swap amount input
This commit is contained in:
387
swap_amounts_oracle_test.go
Normal file
387
swap_amounts_oracle_test.go
Normal file
@@ -0,0 +1,387 @@
|
||||
package pump_parser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gagliardetto/solana-go"
|
||||
"github.com/gagliardetto/solana-go/rpc"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type swapOracleCase struct {
|
||||
name string
|
||||
txHash string
|
||||
index int
|
||||
|
||||
program string
|
||||
event string
|
||||
|
||||
swapMode SwapMode
|
||||
fixedAmount string
|
||||
fixedAmountSide SwapAmountSide
|
||||
fixedMint string
|
||||
limitAmountType SwapLimitType
|
||||
limitAmount string
|
||||
limitAmountSide SwapAmountSide
|
||||
limitMint string
|
||||
actualLimitAmount string
|
||||
actualLimitAmountSide SwapAmountSide
|
||||
slippageBps string
|
||||
}
|
||||
|
||||
func TestSwapAmountOracleSamples(t *testing.T) {
|
||||
EnableAllParsers()
|
||||
|
||||
cases := []swapOracleCase{
|
||||
{
|
||||
name: "pump buy exact out",
|
||||
txHash: "5ybEYcXYhFNfCNAu1o7ovM1Rw5285PBzAwsj4ezwmPRLkYtXX91GhcvAgTVZvdCVV6upsGH8DwYeseNswPhEfVbg",
|
||||
index: 0,
|
||||
program: "Pump",
|
||||
event: TxEventBuy,
|
||||
swapMode: SwapModeExactOut,
|
||||
fixedAmount: "1459556161603",
|
||||
fixedAmountSide: SwapAmountSideBase,
|
||||
fixedMint: "CEwaxx5j1K61JMYXavcxihVQW4NxC6c4NQ27veFpYUYA",
|
||||
limitAmountType: SwapLimitTypeMaxIn,
|
||||
limitAmount: "100000001",
|
||||
limitAmountSide: SwapAmountSideQuote,
|
||||
limitMint: "So11111111111111111111111111111111111111112",
|
||||
actualLimitAmount: "98765431",
|
||||
actualLimitAmountSide: SwapAmountSideQuote,
|
||||
slippageBps: "123.4569987654300123",
|
||||
},
|
||||
{
|
||||
name: "raydium v4 exact out",
|
||||
txHash: "3Q1BhUvqm889oJfCn96AZAJqyHi1uxdeoKQknCc9xQcajhZS5APfwzkHuNTkPJEhyGknj7VyLpkNoMmNPK3No6hC",
|
||||
index: 0,
|
||||
program: "RaydiumV4",
|
||||
event: TxEventSell,
|
||||
swapMode: SwapModeExactOut,
|
||||
fixedAmount: "432588",
|
||||
fixedAmountSide: SwapAmountSideQuote,
|
||||
fixedMint: "2qEHjDLDLbuBgRYvsxhc5D6uDWAivNFZGan56P1tpump",
|
||||
limitAmountType: SwapLimitTypeMaxIn,
|
||||
limitAmount: "18446744073709551615",
|
||||
limitAmountSide: SwapAmountSideBase,
|
||||
limitMint: "So11111111111111111111111111111111111111112",
|
||||
actualLimitAmount: "279099",
|
||||
actualLimitAmountSide: SwapAmountSideBase,
|
||||
slippageBps: "9999.9999999998487001",
|
||||
},
|
||||
{
|
||||
name: "pump amm exact in",
|
||||
txHash: "3Q1BhUvqm889oJfCn96AZAJqyHi1uxdeoKQknCc9xQcajhZS5APfwzkHuNTkPJEhyGknj7VyLpkNoMmNPK3No6hC",
|
||||
index: 1,
|
||||
program: "PumpAMM",
|
||||
event: TxEventSell,
|
||||
swapMode: SwapModeExactIn,
|
||||
fixedAmount: "432588",
|
||||
fixedAmountSide: SwapAmountSideBase,
|
||||
fixedMint: "2qEHjDLDLbuBgRYvsxhc5D6uDWAivNFZGan56P1tpump",
|
||||
limitAmountType: SwapLimitTypeMinOut,
|
||||
limitAmount: "0",
|
||||
limitAmountSide: SwapAmountSideQuote,
|
||||
limitMint: "So11111111111111111111111111111111111111112",
|
||||
actualLimitAmount: "284317",
|
||||
actualLimitAmountSide: SwapAmountSideQuote,
|
||||
slippageBps: "10000",
|
||||
},
|
||||
{
|
||||
name: "meteora dlmm exact in",
|
||||
txHash: "5uw9Uwe9KDLCzUNf1sRr7EjKqE2Xs8qUHaoC1CRFhkE3TMpo5TimwApW65pd6pmB7Cp92XXMaZ9jQav6aXRZGtoS",
|
||||
index: 0,
|
||||
program: "MeteoraDLMM",
|
||||
event: TxEventBuy,
|
||||
swapMode: SwapModeExactIn,
|
||||
fixedAmount: "17684137",
|
||||
fixedAmountSide: SwapAmountSideQuote,
|
||||
fixedMint: "So11111111111111111111111111111111111111112",
|
||||
limitAmountType: SwapLimitTypeMinOut,
|
||||
limitAmount: "0",
|
||||
limitAmountSide: SwapAmountSideBase,
|
||||
limitMint: "METAewgxyPbgwsseH8T16a39CQ5VyVxZi9zXiDPY18m",
|
||||
actualLimitAmount: "50437818",
|
||||
actualLimitAmountSide: SwapAmountSideBase,
|
||||
slippageBps: "10000",
|
||||
},
|
||||
{
|
||||
name: "orca whirlpool exact in",
|
||||
txHash: "5uw9Uwe9KDLCzUNf1sRr7EjKqE2Xs8qUHaoC1CRFhkE3TMpo5TimwApW65pd6pmB7Cp92XXMaZ9jQav6aXRZGtoS",
|
||||
index: 1,
|
||||
program: "OrcaWhirPool",
|
||||
event: TxEventSell,
|
||||
swapMode: SwapModeExactIn,
|
||||
fixedAmount: "50437818",
|
||||
fixedAmountSide: SwapAmountSideBase,
|
||||
fixedMint: "METAewgxyPbgwsseH8T16a39CQ5VyVxZi9zXiDPY18m",
|
||||
limitAmountType: SwapLimitTypeMinOut,
|
||||
limitAmount: "0",
|
||||
limitAmountSide: SwapAmountSideQuote,
|
||||
limitMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
||||
actualLimitAmount: "1438802",
|
||||
actualLimitAmountSide: SwapAmountSideQuote,
|
||||
slippageBps: "10000",
|
||||
},
|
||||
{
|
||||
name: "raydium v4 exact in",
|
||||
txHash: "5uw9Uwe9KDLCzUNf1sRr7EjKqE2Xs8qUHaoC1CRFhkE3TMpo5TimwApW65pd6pmB7Cp92XXMaZ9jQav6aXRZGtoS",
|
||||
index: 2,
|
||||
program: "RaydiumV4",
|
||||
event: TxEventBuy,
|
||||
swapMode: SwapModeExactIn,
|
||||
fixedAmount: "1438802",
|
||||
fixedAmountSide: SwapAmountSideQuote,
|
||||
fixedMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
||||
limitAmountType: SwapLimitTypeMinOut,
|
||||
limitAmount: "0",
|
||||
limitAmountSide: SwapAmountSideBase,
|
||||
limitMint: "So11111111111111111111111111111111111111112",
|
||||
actualLimitAmount: "19059759",
|
||||
actualLimitAmountSide: SwapAmountSideBase,
|
||||
slippageBps: "10000",
|
||||
},
|
||||
{
|
||||
name: "raydium clmm exact in",
|
||||
txHash: "3XoRKna49qCAuF75ctmaYupNmYWuFm5AU73ULQjxNUxz9qJzuKqMRqq5Z88L6DooWTF44UxnxMXwqLn5t9NsoCoZ",
|
||||
index: 2,
|
||||
program: "RaydiumCLMM",
|
||||
event: TxEventSell,
|
||||
swapMode: SwapModeExactIn,
|
||||
fixedAmount: "1569519567845",
|
||||
fixedAmountSide: SwapAmountSideBase,
|
||||
fixedMint: "CiKu4eHsVrc1eueVQeHn7qhXTcVu95gSQmBpX4utjL9z",
|
||||
limitAmountType: SwapLimitTypeMinOut,
|
||||
limitAmount: "0",
|
||||
limitAmountSide: SwapAmountSideQuote,
|
||||
limitMint: "6sQgvhAYtYFrahcjB1hKfB3ZC5YDVdfYvAqK1GKe93c9",
|
||||
actualLimitAmount: "366578",
|
||||
actualLimitAmountSide: SwapAmountSideQuote,
|
||||
slippageBps: "10000",
|
||||
},
|
||||
{
|
||||
name: "raydium cpmm exact in",
|
||||
txHash: "288FAsrj7h6hTKywtVaqCqHAbNZ6x3Xuich9kQMGVarVUnUjkqTabxQE9JHyranGY9eqUivZbBTzC5dH1BEuJ6pa",
|
||||
index: 2,
|
||||
program: "RaydiumCPMM",
|
||||
event: TxEventSell,
|
||||
swapMode: SwapModeExactIn,
|
||||
fixedAmount: "1260040377905",
|
||||
fixedAmountSide: SwapAmountSideBase,
|
||||
fixedMint: "3f7wfg9yHLtGKvy75MmqsVT1ueTFoqyySQbusrX1YAQ4",
|
||||
limitAmountType: SwapLimitTypeMinOut,
|
||||
limitAmount: "0",
|
||||
limitAmountSide: SwapAmountSideQuote,
|
||||
limitMint: "oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp",
|
||||
actualLimitAmount: "802507591",
|
||||
actualLimitAmountSide: SwapAmountSideQuote,
|
||||
slippageBps: "10000",
|
||||
},
|
||||
{
|
||||
name: "raydium launchlab exact in",
|
||||
txHash: "1r3gfEse3WAy5H6h4jMSNq1K5KZNCrMdAtCnpBSE1xkHQEt3EJ2J6Lk6ihQshrfsrS5FbqP5WuUSZG6zPCJB5TE",
|
||||
index: 0,
|
||||
program: "RaydiumLaunchLab",
|
||||
event: TxEventBuy,
|
||||
swapMode: SwapModeExactIn,
|
||||
fixedAmount: "10000000",
|
||||
fixedAmountSide: SwapAmountSideQuote,
|
||||
fixedMint: "So11111111111111111111111111111111111111112",
|
||||
limitAmountType: SwapLimitTypeMinOut,
|
||||
limitAmount: "5976144139694",
|
||||
limitAmountSide: SwapAmountSideBase,
|
||||
limitMint: "Attr2sqaXr76XqaDxdtnQ4QAEsaFdgTGr599F7ytgray",
|
||||
actualLimitAmount: "6129378604814",
|
||||
actualLimitAmountSide: SwapAmountSideBase,
|
||||
slippageBps: "249.9999999994289796",
|
||||
},
|
||||
{
|
||||
name: "meteora pools exact in",
|
||||
txHash: "5jQk6mbhtExpUFskRy2AfKWbLgXDv2USiGkq9tQWauGVKduGdTqscgxyDCPgBryr4kz5hDT5CE9TpVTKDoPhkBmt",
|
||||
index: 0,
|
||||
program: "MeteoraPools",
|
||||
event: TxEventBuy,
|
||||
swapMode: SwapModeExactIn,
|
||||
fixedAmount: "75404052467",
|
||||
fixedAmountSide: SwapAmountSideQuote,
|
||||
fixedMint: "STrikemJEk2tFVYpg7SMo9nGPrnJ56fHnS1K7PV2fPw",
|
||||
limitAmountType: SwapLimitTypeMinOut,
|
||||
limitAmount: "30605141",
|
||||
limitAmountSide: SwapAmountSideBase,
|
||||
limitMint: "So11111111111111111111111111111111111111112",
|
||||
actualLimitAmount: "31556751",
|
||||
actualLimitAmountSide: SwapAmountSideBase,
|
||||
slippageBps: "301.5551252408715967",
|
||||
},
|
||||
{
|
||||
name: "meteora bonding curve exact in",
|
||||
txHash: "5Qsq1ueenSs4KgVRgwXmBVFvMR3Asq9MmXwmqQimxDdWLdiJy6dVfmYqa2YCvkNH1Gx7aCzJqg4t9gN9ECfxH2JS",
|
||||
index: 0,
|
||||
program: "MeteoraBondingCurve",
|
||||
event: TxEventSell,
|
||||
swapMode: SwapModeExactIn,
|
||||
fixedAmount: "11022737683",
|
||||
fixedAmountSide: SwapAmountSideBase,
|
||||
fixedMint: "8FosqFryatEMV4ZeFR1gLmSmxBLcQ2NCibpZxFRPPF34",
|
||||
limitAmountType: SwapLimitTypeMinOut,
|
||||
limitAmount: "49672101",
|
||||
limitAmountSide: SwapAmountSideQuote,
|
||||
limitMint: "So11111111111111111111111111111111111111112",
|
||||
actualLimitAmount: "49672101",
|
||||
actualLimitAmountSide: SwapAmountSideQuote,
|
||||
slippageBps: "0",
|
||||
},
|
||||
{
|
||||
name: "meteora damm v2 exact in",
|
||||
txHash: "43EouSYkeVmLBZSKW1KptiQcAVvB6KX49wjztjgzkQ9iU38A6fF68k77bNy9Wn6fwjykqYsPorUKj8m6SFY7naf1",
|
||||
index: 0,
|
||||
program: "MeteoraAmmV2",
|
||||
event: TxEventSell,
|
||||
swapMode: SwapModeExactIn,
|
||||
fixedAmount: "11846",
|
||||
fixedAmountSide: SwapAmountSideBase,
|
||||
fixedMint: "So11111111111111111111111111111111111111112",
|
||||
limitAmountType: SwapLimitTypeMinOut,
|
||||
limitAmount: "30893426",
|
||||
limitAmountSide: SwapAmountSideQuote,
|
||||
limitMint: "CdDoeyd67nuzmMCF8Dd3RzbxiTRk41Xd922Veu9kGvDE",
|
||||
actualLimitAmount: "33325162",
|
||||
actualLimitAmountSide: SwapAmountSideQuote,
|
||||
slippageBps: "729.6996785792069068",
|
||||
},
|
||||
{
|
||||
name: "meteora damm v2 exact out",
|
||||
txHash: "BD7GZaXaJc2hzSNPe6Q5yeej7rZLQFMpdx4rZwPhGTyHP43iMAR7LxymRSPGXnefAxSqi5sMsEPS1cjyQjup3Eu",
|
||||
index: 0,
|
||||
program: "MeteoraAmmV2",
|
||||
event: TxEventBuy,
|
||||
swapMode: SwapModeExactOut,
|
||||
fixedAmount: "512761043",
|
||||
fixedAmountSide: SwapAmountSideBase,
|
||||
fixedMint: "DPfZc59DLrKyVTJDoKB8CBFgCndsjUzxy6fdbxk4Zms9",
|
||||
limitAmountType: SwapLimitTypeMaxIn,
|
||||
limitAmount: "71386496",
|
||||
limitAmountSide: SwapAmountSideQuote,
|
||||
limitMint: "So11111111111111111111111111111111111111112",
|
||||
actualLimitAmount: "70020377",
|
||||
actualLimitAmountSide: SwapAmountSideQuote,
|
||||
slippageBps: "191.3693872857970225",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tx := mustParseRPCFixtureTx(t, tc.txHash)
|
||||
if tc.index >= len(tx.Swaps) {
|
||||
t.Fatalf("swap index %d out of range, len=%d", tc.index, len(tx.Swaps))
|
||||
}
|
||||
|
||||
swap := tx.Swaps[tc.index]
|
||||
if swap.Program != tc.program {
|
||||
t.Fatalf("program = %q, want %q", swap.Program, tc.program)
|
||||
}
|
||||
if swap.Event != tc.event {
|
||||
t.Fatalf("event = %q, want %q", swap.Event, tc.event)
|
||||
}
|
||||
if swap.SwapMode != tc.swapMode {
|
||||
t.Fatalf("swap mode = %s, want %s", swap.SwapMode.String(), tc.swapMode.String())
|
||||
}
|
||||
|
||||
assertDecimalString(t, "fixed_amount", swap.FixedAmount, tc.fixedAmount)
|
||||
if swap.FixedAmountSide != tc.fixedAmountSide {
|
||||
t.Fatalf("fixed amount side = %s, want %s", swap.FixedAmountSide.String(), tc.fixedAmountSide.String())
|
||||
}
|
||||
assertPublicKey(t, "fixed_mint", swap.FixedMint, tc.fixedMint)
|
||||
|
||||
if swap.LimitAmountType != tc.limitAmountType {
|
||||
t.Fatalf("limit amount type = %s, want %s", swap.LimitAmountType.String(), tc.limitAmountType.String())
|
||||
}
|
||||
assertDecimalString(t, "limit_amount", swap.LimitAmount, tc.limitAmount)
|
||||
if swap.LimitAmountSide != tc.limitAmountSide {
|
||||
t.Fatalf("limit amount side = %s, want %s", swap.LimitAmountSide.String(), tc.limitAmountSide.String())
|
||||
}
|
||||
assertPublicKey(t, "limit_mint", swap.LimitMint, tc.limitMint)
|
||||
|
||||
assertDecimalString(t, "actual_limit_amount", swap.ActualLimitAmount, tc.actualLimitAmount)
|
||||
if swap.ActualLimitAmountSide != tc.actualLimitAmountSide {
|
||||
t.Fatalf("actual limit amount side = %s, want %s", swap.ActualLimitAmountSide.String(), tc.actualLimitAmountSide.String())
|
||||
}
|
||||
assertDecimalString(t, "slippage_bps", swap.SlippageBps, tc.slippageBps)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mustParseRPCFixtureTx(t *testing.T, txHash string) *Tx {
|
||||
t.Helper()
|
||||
|
||||
fixturePath := filepath.Join("testdata", "rpc", txHash+".json")
|
||||
raw, err := os.ReadFile(fixturePath)
|
||||
if err != nil {
|
||||
t.Fatalf("read fixture %s: %v", fixturePath, err)
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Result *rpc.GetTransactionResult `json:"result"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &response); err != nil {
|
||||
t.Fatalf("unmarshal fixture %s: %v", fixturePath, err)
|
||||
}
|
||||
if response.Result == nil || response.Result.Transaction == nil || response.Result.Meta == nil {
|
||||
t.Fatalf("fixture %s is missing transaction data", fixturePath)
|
||||
}
|
||||
|
||||
rawBinary := response.Result.Transaction.GetBinary()
|
||||
if len(rawBinary) == 0 {
|
||||
t.Fatalf("fixture %s has empty transaction bytes", fixturePath)
|
||||
}
|
||||
|
||||
txWithMeta := rpc.TransactionWithMeta{
|
||||
Slot: response.Result.Slot,
|
||||
BlockTime: response.Result.BlockTime,
|
||||
Transaction: rpc.DataBytesOrJSONFromBytes(rawBinary),
|
||||
Meta: response.Result.Meta,
|
||||
Version: response.Result.Version,
|
||||
}
|
||||
|
||||
var blockTime *uint64
|
||||
if response.Result.BlockTime != nil {
|
||||
bt := uint64(*response.Result.BlockTime)
|
||||
blockTime = &bt
|
||||
}
|
||||
|
||||
rawTx, err := FromRpcTransactionWithMeta(txWithMeta, blockTime, response.Result.Slot, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("convert fixture %s: %v", fixturePath, err)
|
||||
}
|
||||
|
||||
tx, err := ParseRawTx(rawTx)
|
||||
if err != nil {
|
||||
t.Fatalf("parse fixture %s: %v", fixturePath, err)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
func assertDecimalString(t *testing.T, field string, got decimal.Decimal, want string) {
|
||||
t.Helper()
|
||||
|
||||
wantDecimal, err := decimal.NewFromString(want)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid expected decimal for %s: %v", field, err)
|
||||
}
|
||||
if !got.Equal(wantDecimal) {
|
||||
t.Fatalf("%s = %s, want %s", field, got.String(), want)
|
||||
}
|
||||
}
|
||||
|
||||
func assertPublicKey(t *testing.T, field string, got solana.PublicKey, want string) {
|
||||
t.Helper()
|
||||
|
||||
wantKey := solana.MustPublicKeyFromBase58(want)
|
||||
if !got.Equals(wantKey) {
|
||||
t.Fatalf("%s = %s, want %s", field, got, wantKey)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user