package execution import ( "fmt" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) // CurveEncoder encodes transactions for Curve pools type CurveEncoder struct{} // NewCurveEncoder creates a new Curve encoder func NewCurveEncoder() *CurveEncoder { return &CurveEncoder{} } // EncodeSwap encodes a Curve exchange transaction func (e *CurveEncoder) EncodeSwap( tokenIn common.Address, tokenOut common.Address, amountIn *big.Int, minAmountOut *big.Int, poolAddress common.Address, recipient common.Address, ) (common.Address, []byte, error) { // Curve pools have different interfaces depending on the pool type // Most common: exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) // For newer pools: exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy) // We'll use the int128 version as it's most common // exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) methodID := crypto.Keccak256([]byte("exchange(int128,int128,uint256,uint256)"))[:4] // Note: In production, we'd need to: // 1. Query the pool to determine which tokens correspond to which indices // 2. Handle the newer uint256 index version // For now, we'll assume we know the indices // Placeholder indices - in reality these would be determined from pool state i := big.NewInt(0) // Index of tokenIn j := big.NewInt(1) // Index of tokenOut data := make([]byte, 0) data = append(data, methodID...) // i (int128) data = append(data, padLeft(i.Bytes(), 32)...) // j (int128) data = append(data, padLeft(j.Bytes(), 32)...) // dx (amountIn) data = append(data, padLeft(amountIn.Bytes(), 32)...) // min_dy (minAmountOut) data = append(data, padLeft(minAmountOut.Bytes(), 32)...) // Curve pools typically send tokens to msg.sender // So we return the pool address as the target return poolAddress, data, nil } // EncodeExchangeUnderlying encodes a Curve exchange_underlying transaction // (for metapools or pools with wrapped tokens) func (e *CurveEncoder) EncodeExchangeUnderlying( tokenIn common.Address, tokenOut common.Address, amountIn *big.Int, minAmountOut *big.Int, poolAddress common.Address, recipient common.Address, ) (common.Address, []byte, error) { // exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) methodID := crypto.Keccak256([]byte("exchange_underlying(int128,int128,uint256,uint256)"))[:4] // Placeholder indices i := big.NewInt(0) j := big.NewInt(1) data := make([]byte, 0) data = append(data, methodID...) // i (int128) data = append(data, padLeft(i.Bytes(), 32)...) // j (int128) data = append(data, padLeft(j.Bytes(), 32)...) // dx (amountIn) data = append(data, padLeft(amountIn.Bytes(), 32)...) // min_dy (minAmountOut) data = append(data, padLeft(minAmountOut.Bytes(), 32)...) return poolAddress, data, nil } // EncodeDynamicExchange encodes exchange for newer Curve pools with uint256 indices func (e *CurveEncoder) EncodeDynamicExchange( i *big.Int, j *big.Int, amountIn *big.Int, minAmountOut *big.Int, poolAddress common.Address, ) (common.Address, []byte, error) { // exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy) methodID := crypto.Keccak256([]byte("exchange(uint256,uint256,uint256,uint256)"))[:4] data := make([]byte, 0) data = append(data, methodID...) // i (uint256) data = append(data, padLeft(i.Bytes(), 32)...) // j (uint256) data = append(data, padLeft(j.Bytes(), 32)...) // dx (amountIn) data = append(data, padLeft(amountIn.Bytes(), 32)...) // min_dy (minAmountOut) data = append(data, padLeft(minAmountOut.Bytes(), 32)...) return poolAddress, data, nil } // EncodeGetDy encodes a view call to get expected output amount func (e *CurveEncoder) EncodeGetDy( i *big.Int, j *big.Int, amountIn *big.Int, poolAddress common.Address, ) (common.Address, []byte, error) { // get_dy(int128 i, int128 j, uint256 dx) returns (uint256) methodID := crypto.Keccak256([]byte("get_dy(int128,int128,uint256)"))[:4] data := make([]byte, 0) data = append(data, methodID...) // i (int128) data = append(data, padLeft(i.Bytes(), 32)...) // j (int128) data = append(data, padLeft(j.Bytes(), 32)...) // dx (amountIn) data = append(data, padLeft(amountIn.Bytes(), 32)...) return poolAddress, data, nil } // EncodeCoinIndices encodes a call to get coin indices func (e *CurveEncoder) EncodeCoinIndices( tokenAddress common.Address, poolAddress common.Address, ) (common.Address, []byte, error) { // coins(uint256 i) returns (address) // We'd need to call this multiple times to find the index methodID := crypto.Keccak256([]byte("coins(uint256)"))[:4] data := make([]byte, 0) data = append(data, methodID...) // Index (we'd iterate through 0, 1, 2, 3 to find matching token) data = append(data, padLeft(big.NewInt(0).Bytes(), 32)...) return poolAddress, data, nil } // GetCoinIndex determines the index of a token in a Curve pool // This is a helper function that would need to be called before encoding swaps func (e *CurveEncoder) GetCoinIndex( tokenAddress common.Address, poolCoins []common.Address, ) (int, error) { for i, coin := range poolCoins { if coin == tokenAddress { return i, nil } } return -1, fmt.Errorf("token not found in pool") }