From 51f1511c8f64d7448f90d26d3cf72b20a2f0a31c Mon Sep 17 00:00:00 2001 From: thloyi Date: Mon, 20 Apr 2026 15:09:42 +0800 Subject: [PATCH] fix EncodeTxBinary --- internal/example/cmd/main.go | 6 ++++ tx_binary.go | 37 ++++++++++++++++------ tx_binary_test.go | 61 ++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 10 deletions(-) diff --git a/internal/example/cmd/main.go b/internal/example/cmd/main.go index eaa67e6..b8e33cb 100644 --- a/internal/example/cmd/main.go +++ b/internal/example/cmd/main.go @@ -62,6 +62,12 @@ func main() { swap.BaseAmount.Div(decimal.NewFromInt(1e6)), swap.QuoteAmount.Div(decimal.NewFromInt(1e9)), swap.FixedAmount.String(), swap.LimitAmount.String()) } } + if len(ptx.Swaps) > 0 { + _, err := parser.EncodeTxBinary(ptx) + if err != nil { + fmt.Printf("success tx : %s, , block: %d, tx: %s, err: %s \n", time.Now().Format("2006-01-02 15:04:05"), ptx.Block, ptx.GetTxHash(), err.Error()) + } + } } // currentBlock = ptx.Block diff --git a/tx_binary.go b/tx_binary.go index 796810b..1c64c35 100644 --- a/tx_binary.go +++ b/tx_binary.go @@ -80,8 +80,8 @@ type SwapBinary struct { ActualLimitAmountSide SwapAmountSide SlippageBps uint64 - BaseReserve uint64 - QuoteReserve uint64 + BaseReserve float64 + QuoteReserve float64 Mayhem bool Cashback bool @@ -777,10 +777,10 @@ func newSwapBinary(swap Swap, index int, addressIndex *txBinaryAddressIndex) (Sw if out.SlippageBps, err = txBinaryRoundedDecimalToUint64(swap.SlippageBps, fmt.Sprintf("swap[%d].slippage_bps", index)); err != nil { return SwapBinary{}, err } - if out.BaseReserve, err = txBinaryDecimalToUint64(swap.BaseReserve, fmt.Sprintf("swap[%d].base_reserve", index)); err != nil { + if out.BaseReserve, err = txBinaryDecimalToFloat64Raw(swap.BaseReserve, fmt.Sprintf("swap[%d].base_reserve", index)); err != nil { return SwapBinary{}, err } - if out.QuoteReserve, err = txBinaryDecimalToUint64(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 } if out.UserBaseBalance, err = txBinaryDecimalToUint64(swap.UserBaseBalance, fmt.Sprintf("swap[%d].user_base_balance", index)); err != nil { @@ -878,8 +878,8 @@ func (swap SwapBinary) toSwap(addressTable []solana.PublicKey, index int) (Swap, ActualLimitAmount: decimal.NewFromUint64(swap.ActualLimitAmount), ActualLimitAmountSide: swap.ActualLimitAmountSide, SlippageBps: decimal.NewFromUint64(swap.SlippageBps), - BaseReserve: decimal.NewFromUint64(swap.BaseReserve), - QuoteReserve: decimal.NewFromUint64(swap.QuoteReserve), + BaseReserve: txBinaryFloat64ToDecimalRaw(swap.BaseReserve), + QuoteReserve: txBinaryFloat64ToDecimalRaw(swap.QuoteReserve), Mayhem: swap.Mayhem, Cashback: swap.Cashback, UserBaseBalance: decimal.NewFromUint64(swap.UserBaseBalance), @@ -1063,6 +1063,14 @@ func txBinaryDecimalToFloat64(value decimal.Decimal, scale int32, field string) return f, nil } +func txBinaryDecimalToFloat64Raw(value decimal.Decimal, field string) (float64, error) { + f, exact := value.Float64() + if !exact && math.IsInf(f, 0) { + return 0, fmt.Errorf("%s cannot be represented as float64: %s", field, value.String()) + } + return f, nil +} + func txBinaryFloat64ToDecimal(value float64, scale int32) decimal.Decimal { formatted := strconv.FormatFloat(value, 'f', int(scale), 64) out, err := decimal.NewFromString(formatted) @@ -1072,6 +1080,15 @@ func txBinaryFloat64ToDecimal(value float64, scale int32) decimal.Decimal { return out } +func txBinaryFloat64ToDecimalRaw(value float64) decimal.Decimal { + formatted := strconv.FormatFloat(value, 'f', -1, 64) + out, err := decimal.NewFromString(formatted) + if err != nil { + return decimal.Zero + } + return out +} + type txBinaryEncoder struct { buf bytes.Buffer } @@ -1224,8 +1241,8 @@ func (enc *txBinaryEncoder) writeSwaps(swaps []SwapBinary, enumTable *txBinaryEn enc.writeUint64(swap.ActualLimitAmount) enc.writeUint8(uint8(swap.ActualLimitAmountSide)) enc.writeUint64(swap.SlippageBps) - enc.writeUint64(swap.BaseReserve) - enc.writeUint64(swap.QuoteReserve) + enc.writeFloat64(swap.BaseReserve) + enc.writeFloat64(swap.QuoteReserve) enc.writeBool(swap.Mayhem) enc.writeBool(swap.Cashback) enc.writeUint64(swap.UserBaseBalance) @@ -1720,10 +1737,10 @@ func txBinaryReadSwaps(dec txBinaryBodyReader, enumTable *txBinaryEnumTable) ([] if swap.SlippageBps, err = dec.readUint64(); err != nil { return nil, err } - if swap.BaseReserve, err = dec.readUint64(); err != nil { + if swap.BaseReserve, err = dec.readFloat64(); err != nil { return nil, err } - if swap.QuoteReserve, err = dec.readUint64(); err != nil { + if swap.QuoteReserve, err = dec.readFloat64(); err != nil { return nil, err } if swap.Mayhem, err = dec.readBool(); err != nil { diff --git a/tx_binary_test.go b/tx_binary_test.go index acf4ed0..a588e07 100644 --- a/tx_binary_test.go +++ b/tx_binary_test.go @@ -293,6 +293,67 @@ func TestTxBinaryAcceptsKnownEventEnums(t *testing.T) { } } +func TestTxBinaryPreservesFractionalReserves(t *testing.T) { + tx := &Tx{ + Signer: mustPubKey("So11111111111111111111111111111111111111112"), + Block: 1, + BlockIndex: 1, + CuFee: decimal.NewFromInt(1), + CUPrice: decimal.RequireFromString("0.000001"), + BeforeSolBalance: decimal.RequireFromString("1.000000000"), + AfterSOLBalance: decimal.RequireFromString("0.900000000"), + ComputeUnitsConsumed: 1, + CuLimit: 1, + Swaps: []Swap{ + { + Program: SolProgramMeteoraPools, + Event: TxEventAddLiquidity, + Pool: mustPubKey("11111111111111111111111111111111"), + BaseMint: mustPubKey("3wyAj7RtG72wM1Wv9DkYfL7RAx9X3Jx1sC6E6mN4jWeL"), + QuoteMint: solana.WrappedSol, + BaseTokenProgram: solana.TokenProgramID, + QuoteTokenProgram: solana.TokenProgramID, + Creator: mustPubKey("BPFLoader1111111111111111111111111111111111"), + BaseMintDecimals: 6, + QuoteMintDecimals: 9, + User: mustPubKey("SysvarRent111111111111111111111111111111111"), + BaseAmount: decimal.NewFromInt(10), + QuoteAmount: decimal.NewFromInt(20), + SwapMode: SwapModeExactIn, + FixedAmount: decimal.NewFromInt(20), + FixedAmountSide: SwapAmountSideQuote, + FixedMint: solana.WrappedSol, + LimitAmountType: SwapLimitTypeMinOut, + LimitAmount: decimal.NewFromInt(9), + LimitAmountSide: SwapAmountSideBase, + LimitMint: mustPubKey("3wyAj7RtG72wM1Wv9DkYfL7RAx9X3Jx1sC6E6mN4jWeL"), + ActualLimitAmount: decimal.NewFromInt(10), + ActualLimitAmountSide: SwapAmountSideBase, + BaseReserve: decimal.RequireFromString("123.4"), + QuoteReserve: decimal.RequireFromString("710079483.625409498"), + AfterSOLBalance: decimal.RequireFromString("0.800000000"), + }, + }, + } + + encoded, err := EncodeTxBinary(tx) + if err != nil { + t.Fatalf("EncodeTxBinary() error = %v", err) + } + + decoded, err := DecodeTxBinary(encoded) + if err != nil { + t.Fatalf("DecodeTxBinary() error = %v", err) + } + if got := decoded.Swaps[0].BaseReserve.String(); got != "123.4" { + t.Fatalf("BaseReserve = %s, want 123.4", got) + } + diff := decoded.Swaps[0].QuoteReserve.Sub(decimal.RequireFromString("710079483.625409498")).Abs() + if diff.GreaterThan(decimal.RequireFromString("0.0000001")) { + t.Fatalf("QuoteReserve diff = %s, want <= 0.0000001", diff) + } +} + func TestTxsBinaryRoundTripWithSharedAddressTable(t *testing.T) { tx1 := Tx{ Signer: mustPubKey("So11111111111111111111111111111111111111112"),