package dex import ( "context" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" ) // DEXDecoder is the interface that all DEX protocol decoders must implement type DEXDecoder interface { // DecodeSwap decodes a swap transaction DecodeSwap(tx *types.Transaction) (*SwapInfo, error) // GetPoolReserves fetches current pool reserves GetPoolReserves(ctx context.Context, client *ethclient.Client, poolAddress common.Address) (*PoolReserves, error) // CalculateOutput calculates the expected output for a given input CalculateOutput(amountIn *big.Int, reserves *PoolReserves, tokenIn common.Address) (*big.Int, error) // CalculatePriceImpact calculates the price impact of a trade CalculatePriceImpact(amountIn *big.Int, reserves *PoolReserves, tokenIn common.Address) (float64, error) // GetQuote gets a price quote for a swap GetQuote(ctx context.Context, client *ethclient.Client, tokenIn, tokenOut common.Address, amountIn *big.Int) (*PriceQuote, error) // IsValidPool checks if a pool address is valid for this DEX IsValidPool(ctx context.Context, client *ethclient.Client, poolAddress common.Address) (bool, error) // GetProtocol returns the protocol this decoder handles GetProtocol() DEXProtocol } // BaseDecoder provides common functionality for all decoders type BaseDecoder struct { protocol DEXProtocol client *ethclient.Client } // NewBaseDecoder creates a new base decoder func NewBaseDecoder(protocol DEXProtocol, client *ethclient.Client) *BaseDecoder { return &BaseDecoder{ protocol: protocol, client: client, } } // GetProtocol returns the protocol func (bd *BaseDecoder) GetProtocol() DEXProtocol { return bd.protocol } // CalculatePriceImpact is a default implementation of price impact calculation // This works for constant product AMMs (UniswapV2, SushiSwap) func (bd *BaseDecoder) CalculatePriceImpact(amountIn *big.Int, reserves *PoolReserves, tokenIn common.Address) (float64, error) { if amountIn == nil || amountIn.Sign() <= 0 { return 0, nil } var reserveIn, reserveOut *big.Int if tokenIn == reserves.Token0 { reserveIn = reserves.Reserve0 reserveOut = reserves.Reserve1 } else { reserveIn = reserves.Reserve1 reserveOut = reserves.Reserve0 } if reserveIn.Sign() == 0 || reserveOut.Sign() == 0 { return 1.0, nil // 100% price impact if no liquidity } // Price before = reserveOut / reserveIn // Price after = newReserveOut / newReserveIn // Price impact = (priceAfter - priceBefore) / priceBefore // Calculate expected output using constant product formula amountInWithFee := new(big.Int).Mul(amountIn, big.NewInt(997)) // 0.3% fee numerator := new(big.Int).Mul(amountInWithFee, reserveOut) denominator := new(big.Int).Add( new(big.Int).Mul(reserveIn, big.NewInt(1000)), amountInWithFee, ) amountOut := new(big.Int).Div(numerator, denominator) // Calculate price impact priceBefore := new(big.Float).Quo( new(big.Float).SetInt(reserveOut), new(big.Float).SetInt(reserveIn), ) newReserveIn := new(big.Int).Add(reserveIn, amountIn) newReserveOut := new(big.Int).Sub(reserveOut, amountOut) priceAfter := new(big.Float).Quo( new(big.Float).SetInt(newReserveOut), new(big.Float).SetInt(newReserveIn), ) impact := new(big.Float).Quo( new(big.Float).Sub(priceAfter, priceBefore), priceBefore, ) impactFloat, _ := impact.Float64() return impactFloat, nil }