Created
August 27, 2024 08:09
-
-
Save goodylili/495d8b8a4dc2d63f5d9e66f2c0e5d397 to your computer and use it in GitHub Desktop.
SwapTokensforETH
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"crypto/ecdsa" | |
"errors" | |
"fmt" | |
"github.com/ethereum/go-ethereum" | |
"github.com/ethereum/go-ethereum/accounts/abi/bind" | |
"github.com/ethereum/go-ethereum/core/types" | |
"log" | |
"math/big" | |
"strings" | |
"time" | |
"github.com/ethereum/go-ethereum/accounts/abi" | |
"github.com/ethereum/go-ethereum/common" | |
"github.com/ethereum/go-ethereum/crypto" | |
"github.com/ethereum/go-ethereum/ethclient" | |
) | |
const ( | |
infuraURL = "https://mainnet.base.org" | |
uniswapRouterAddr = "0x4752ba5dbc23f44d87826276bf6fd6b1c372ad24" | |
usdcAddress = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" | |
wethAddress = "0x4200000000000000000000000000000000000006" | |
) | |
// ApproveToken approves the Uniswap router to spend USDC tokens on behalf of the user. | |
func ApproveToken(client *ethclient.Client, userWalletPrivateKey string, tokenAddress, spender common.Address, amount *big.Int) error { | |
privateKey, err := crypto.HexToECDSA(userWalletPrivateKey) | |
if err != nil { | |
return err | |
} | |
publicKey := privateKey.Public() | |
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) | |
if !ok { | |
return errors.New("cannot assert type: publicKey is not of type *ecdsa.PublicKey") | |
} | |
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) | |
// Define the ERC20 token ABI (only the approve function) | |
erc20ABI := `[{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]` | |
parsedABI, err := abi.JSON(strings.NewReader(erc20ABI)) | |
if err != nil { | |
return err | |
} | |
data, err := parsedABI.Pack("approve", spender, amount) | |
if err != nil { | |
return err | |
} | |
// Create the transaction | |
nonce, err := client.PendingNonceAt(context.Background(), fromAddress) | |
if err != nil { | |
return err | |
} | |
gasPrice, err := client.SuggestGasPrice(context.Background()) | |
if err != nil { | |
return err | |
} | |
msg := ethereum.CallMsg{ | |
From: fromAddress, | |
To: &tokenAddress, | |
Data: data, | |
} | |
gasLimit, err := estimateGas(client, msg) | |
if err != nil { | |
return err | |
} | |
tx := types.NewTransaction(nonce, tokenAddress, big.NewInt(0), gasLimit, gasPrice, data) | |
// Sign the transaction | |
chainID, err := client.NetworkID(context.Background()) | |
if err != nil { | |
return err | |
} | |
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey) | |
if err != nil { | |
return err | |
} | |
// Send the transaction | |
err = client.SendTransaction(context.Background(), signedTx) | |
if err != nil { | |
return err | |
} | |
fmt.Printf("Approval Transaction sent: %s\n", signedTx.Hash().Hex()) | |
// Wait for confirmation | |
receipt, err := bind.WaitMined(context.Background(), client, signedTx) | |
if err != nil { | |
return err | |
} | |
if receipt.Status != types.ReceiptStatusSuccessful { | |
return errors.New("approval transaction failed") | |
} | |
return nil | |
} | |
// SwapTokensForETH performs a swap from USDC to ETH using Uniswap. | |
func SwapTokensForETH(client *ethclient.Client, userWalletPrivateKey string, amountIn *big.Int) error { | |
privateKey, err := crypto.HexToECDSA(userWalletPrivateKey) | |
if err != nil { | |
return err | |
} | |
publicKey := privateKey.Public() | |
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) | |
if !ok { | |
return errors.New("cannot assert type: publicKey is not of type *ecdsa.PublicKey") | |
} | |
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) | |
// Load Uniswap Router ABI | |
uniswapABI := `[{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"}]` | |
parsedABI, err := abi.JSON(strings.NewReader(uniswapABI)) | |
if err != nil { | |
return err | |
} | |
amountOutMin := big.NewInt(0) // Set to 0 or calculate based on slippage | |
deadline := big.NewInt(time.Now().Add(10 * time.Minute).Unix()) | |
path := []common.Address{ | |
common.HexToAddress(usdcAddress), | |
common.HexToAddress(wethAddress), | |
} | |
data, err := parsedABI.Pack("swapExactTokensForETH", amountIn, amountOutMin, path, fromAddress, deadline) | |
if err != nil { | |
return err | |
} | |
// Create the transaction | |
nonce, err := client.PendingNonceAt(context.Background(), fromAddress) | |
if err != nil { | |
return err | |
} | |
gasPrice, err := client.SuggestGasPrice(context.Background()) | |
if err != nil { | |
return err | |
} | |
tx := types.NewTransaction(nonce, common.HexToAddress(uniswapRouterAddr), big.NewInt(0), 300000, gasPrice, data) | |
// Sign the transaction | |
chainID, err := client.NetworkID(context.Background()) | |
if err != nil { | |
return err | |
} | |
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey) | |
if err != nil { | |
return err | |
} | |
// Send the transaction | |
err = client.SendTransaction(context.Background(), signedTx) | |
if err != nil { | |
return err | |
} | |
fmt.Printf("Swap Transaction sent: %s\n", signedTx.Hash().Hex()) | |
// Wait for confirmation | |
receipt, err := bind.WaitMined(context.Background(), client, signedTx) | |
if err != nil { | |
return err | |
} | |
if receipt.Status != types.ReceiptStatusSuccessful { | |
return errors.New("swap transaction failed") | |
} | |
return nil | |
} | |
func estimateGas(client *ethclient.Client, msg ethereum.CallMsg) (uint64, error) { | |
gasLimit, err := client.EstimateGas(context.Background(), msg) | |
if err != nil { | |
return 0, err | |
} | |
return gasLimit, nil | |
} | |
func main() { | |
client, err := ethclient.Dial(infuraURL) | |
if err != nil { | |
log.Fatalf("Failed to connect to the Ethereum client: %v", err) | |
} | |
privateKey := "" | |
// Amount to approve and swap (2.7 USDC) | |
amountIn := big.NewInt(2700000) // 2.7 USDC (considering USDC has 6 decimals) | |
// Approve USDC for Uniswap Router | |
err = ApproveToken(client, privateKey, common.HexToAddress(usdcAddress), common.HexToAddress(uniswapRouterAddr), amountIn) | |
if err != nil { | |
log.Fatalf("Failed to approve USDC: %v", err) | |
} | |
// Swap USDC for ETH | |
err = SwapTokensForETH(client, privateKey, amountIn) | |
if err != nil { | |
log.Fatalf("Failed to swap USDC for ETH: %v", err) | |
} | |
fmt.Println("Swap successful!") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment