Skip to content

Instantly share code, notes, and snippets.

@goodylili
Created August 27, 2024 08:09
Show Gist options
  • Save goodylili/495d8b8a4dc2d63f5d9e66f2c0e5d397 to your computer and use it in GitHub Desktop.
Save goodylili/495d8b8a4dc2d63f5d9e66f2c0e5d397 to your computer and use it in GitHub Desktop.
SwapTokensforETH
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