diff --git a/pkg/arbitrum/l2_parser.go b/pkg/arbitrum/l2_parser.go index cc1dc9b..aecb996 100644 --- a/pkg/arbitrum/l2_parser.go +++ b/pkg/arbitrum/l2_parser.go @@ -308,9 +308,12 @@ func (p *ArbitrumL2Parser) parseDEXTransaction(tx RawL2Transaction) *DEXTransact inputData = []byte{} } - p.logger.Info(fmt.Sprintf("DEX Transaction detected: %s -> %s (%s) calling %s (%s), Value: %s ETH", + // Decode function parameters based on function type + swapDetails := p.decodeFunctionData(funcInfo, inputData) + + p.logger.Info(fmt.Sprintf("DEX Transaction detected: %s -> %s (%s) calling %s (%s), Value: %s ETH%s", tx.From, tx.To, contractName, funcInfo.Name, funcInfo.Protocol, - new(big.Float).Quo(new(big.Float).SetInt(value), big.NewFloat(1e18)).String())) + new(big.Float).Quo(new(big.Float).SetInt(value), big.NewFloat(1e18)).String(), swapDetails)) return &DEXTransaction{ Hash: tx.Hash, @@ -335,6 +338,148 @@ func (p *ArbitrumL2Parser) parseDEXTransaction(tx RawL2Transaction) *DEXTransact return nil } +// decodeFunctionData extracts parameters from transaction input data +func (p *ArbitrumL2Parser) decodeFunctionData(funcInfo DEXFunctionSignature, inputData []byte) string { + if len(inputData) < 4 { + return "" + } + + // Skip the 4-byte function selector + params := inputData[4:] + + switch funcInfo.Name { + case "swapExactTokensForTokens": + return p.decodeSwapExactTokensForTokens(params) + case "swapTokensForExactTokens": + return p.decodeSwapTokensForExactTokens(params) + case "swapExactETHForTokens": + return p.decodeSwapExactETHForTokens(params) + case "swapExactTokensForETH": + return p.decodeSwapExactTokensForETH(params) + case "exactInputSingle": + return p.decodeExactInputSingle(params) + case "exactInput": + return p.decodeExactInput(params) + case "exactOutputSingle": + return p.decodeExactOutputSingle(params) + case "multicall": + return p.decodeMulticall(params) + default: + return fmt.Sprintf(", Raw input: %d bytes", len(inputData)) + } +} + +// decodeSwapExactTokensForTokens decodes UniswapV2 swapExactTokensForTokens parameters +// function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) +func (p *ArbitrumL2Parser) decodeSwapExactTokensForTokens(params []byte) string { + if len(params) < 160 { // 5 parameters * 32 bytes each + return ", Invalid parameters" + } + + // Decode parameters (simplified - real ABI decoding would be more robust) + amountIn := new(big.Int).SetBytes(params[0:32]) + amountOutMin := new(big.Int).SetBytes(params[32:64]) + + // Convert to readable format + amountInEth := new(big.Float).Quo(new(big.Float).SetInt(amountIn), big.NewFloat(1e18)) + amountOutMinEth := new(big.Float).Quo(new(big.Float).SetInt(amountOutMin), big.NewFloat(1e18)) + + return fmt.Sprintf(", AmountIn: %s tokens, MinOut: %s tokens", + amountInEth.Text('f', 6), amountOutMinEth.Text('f', 6)) +} + +// decodeSwapTokensForExactTokens decodes UniswapV2 swapTokensForExactTokens parameters +func (p *ArbitrumL2Parser) decodeSwapTokensForExactTokens(params []byte) string { + if len(params) < 160 { + return ", Invalid parameters" + } + + amountOut := new(big.Int).SetBytes(params[0:32]) + amountInMax := new(big.Int).SetBytes(params[32:64]) + + amountOutEth := new(big.Float).Quo(new(big.Float).SetInt(amountOut), big.NewFloat(1e18)) + amountInMaxEth := new(big.Float).Quo(new(big.Float).SetInt(amountInMax), big.NewFloat(1e18)) + + return fmt.Sprintf(", AmountOut: %s tokens, MaxIn: %s tokens", + amountOutEth.Text('f', 6), amountInMaxEth.Text('f', 6)) +} + +// decodeSwapExactETHForTokens decodes UniswapV2 swapExactETHForTokens parameters +func (p *ArbitrumL2Parser) decodeSwapExactETHForTokens(params []byte) string { + if len(params) < 32 { + return ", Invalid parameters" + } + + amountOutMin := new(big.Int).SetBytes(params[0:32]) + amountOutMinEth := new(big.Float).Quo(new(big.Float).SetInt(amountOutMin), big.NewFloat(1e18)) + + return fmt.Sprintf(", MinOut: %s tokens", amountOutMinEth.Text('f', 6)) +} + +// decodeSwapExactTokensForETH decodes UniswapV2 swapExactTokensForETH parameters +func (p *ArbitrumL2Parser) decodeSwapExactTokensForETH(params []byte) string { + if len(params) < 64 { + return ", Invalid parameters" + } + + amountIn := new(big.Int).SetBytes(params[0:32]) + amountOutMin := new(big.Int).SetBytes(params[32:64]) + + amountInEth := new(big.Float).Quo(new(big.Float).SetInt(amountIn), big.NewFloat(1e18)) + amountOutMinEth := new(big.Float).Quo(new(big.Float).SetInt(amountOutMin), big.NewFloat(1e18)) + + return fmt.Sprintf(", AmountIn: %s tokens, MinETH: %s", + amountInEth.Text('f', 6), amountOutMinEth.Text('f', 6)) +} + +// decodeExactInputSingle decodes UniswapV3 exactInputSingle parameters +func (p *ArbitrumL2Parser) decodeExactInputSingle(params []byte) string { + if len(params) < 160 { // ExactInputSingleParams struct + return ", Invalid parameters" + } + + // Simplified decoding - real implementation would parse the struct properly + amountIn := new(big.Int).SetBytes(params[128:160]) // approximation + amountInEth := new(big.Float).Quo(new(big.Float).SetInt(amountIn), big.NewFloat(1e18)) + + return fmt.Sprintf(", AmountIn: %s tokens", amountInEth.Text('f', 6)) +} + +// decodeExactInput decodes UniswapV3 exactInput parameters +func (p *ArbitrumL2Parser) decodeExactInput(params []byte) string { + if len(params) < 128 { + return ", Invalid parameters" + } + + amountIn := new(big.Int).SetBytes(params[64:96]) // approximation + amountInEth := new(big.Float).Quo(new(big.Float).SetInt(amountIn), big.NewFloat(1e18)) + + return fmt.Sprintf(", AmountIn: %s tokens (multi-hop)", amountInEth.Text('f', 6)) +} + +// decodeExactOutputSingle decodes UniswapV3 exactOutputSingle parameters +func (p *ArbitrumL2Parser) decodeExactOutputSingle(params []byte) string { + if len(params) < 160 { + return ", Invalid parameters" + } + + amountOut := new(big.Int).SetBytes(params[160:192]) // approximation + amountOutEth := new(big.Float).Quo(new(big.Float).SetInt(amountOut), big.NewFloat(1e18)) + + return fmt.Sprintf(", AmountOut: %s tokens", amountOutEth.Text('f', 6)) +} + +// decodeMulticall decodes UniswapV3 multicall parameters +func (p *ArbitrumL2Parser) decodeMulticall(params []byte) string { + if len(params) < 32 { + return ", Invalid parameters" + } + + // Multicall contains an array of encoded function calls + // This is complex to decode without full ABI parsing + return fmt.Sprintf(", Multicall with %d bytes of data", len(params)) +} + // Close closes the RPC connection func (p *ArbitrumL2Parser) Close() { if p.client != nil {