github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/test/scripts/uniswap/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/big"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/0xPolygon/supernets2-node/encoding"
    11  	"github.com/0xPolygon/supernets2-node/log"
    12  	ERC20 "github.com/0xPolygon/supernets2-node/test/contracts/bin/ERC20"
    13  	WETH "github.com/0xPolygon/supernets2-node/test/contracts/bin/WETH"
    14  	"github.com/0xPolygon/supernets2-node/test/contracts/bin/uniswap/v2/core/UniswapV2Factory"
    15  	"github.com/0xPolygon/supernets2-node/test/contracts/bin/uniswap/v2/core/UniswapV2Pair"
    16  	"github.com/0xPolygon/supernets2-node/test/contracts/bin/uniswap/v2/interface/UniswapInterfaceMulticall"
    17  	"github.com/0xPolygon/supernets2-node/test/contracts/bin/uniswap/v2/periphery/UniswapV2Router02"
    18  	"github.com/0xPolygon/supernets2-node/test/operations"
    19  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    20  	"github.com/ethereum/go-ethereum/common"
    21  	"github.com/ethereum/go-ethereum/core/types"
    22  	"github.com/ethereum/go-ethereum/crypto"
    23  	"github.com/ethereum/go-ethereum/ethclient"
    24  )
    25  
    26  const (
    27  	// if you want to test using goerli network
    28  	// replace this by your goerli infura url
    29  	networkURL = "http://localhost:8123"
    30  	// replace this by your account private key
    31  	pk        = "0xdfd01798f92667dbf91df722434e8fbe96af0211d4d1b82bbbbc8f1def7a814f"
    32  	txTimeout = 60 * time.Second
    33  )
    34  
    35  func main() {
    36  	ctx := context.Background()
    37  	log.Infof("connecting to %v", networkURL)
    38  	client, err := ethclient.Dial(networkURL)
    39  	chkErr(err)
    40  	log.Infof("connected")
    41  	chainID, err := client.ChainID(ctx)
    42  	chkErr(err)
    43  	log.Infof("chainID: %v", chainID)
    44  	auth := getAuth(ctx, client, pk)
    45  	fmt.Println()
    46  	balance, err := client.BalanceAt(ctx, auth.From, nil)
    47  	chkErr(err)
    48  	log.Debugf("ETH Balance for %v: %v", auth.From, balance)
    49  	// Deploy ERC20 Tokens to be swapped
    50  	aCoinAddr, aCoin := deployERC20(auth, client, "A COIN", "ACO")
    51  	fmt.Println()
    52  	bCoinAddr, bCoin := deployERC20(auth, client, "B COIN", "BCO")
    53  	fmt.Println()
    54  	cCoinAddr, cCoin := deployERC20(auth, client, "C COIN", "CCO")
    55  	fmt.Println()
    56  	// Deploy wETH Token, it's required by uniswap to swap ETH by tokens
    57  	log.Debugf("Deploying wEth SC")
    58  	wEthAddr, tx, wethSC, err := WETH.DeployWETH(auth, client)
    59  	chkErr(err)
    60  	err = operations.WaitTxToBeMined(ctx, client, tx, txTimeout)
    61  	chkErr(err)
    62  	log.Debugf("wEth SC tx: %v", tx.Hash().Hex())
    63  	log.Debugf("wEth SC addr: %v", wEthAddr.Hex())
    64  	fmt.Println()
    65  	// Deploy Uniswap Factory
    66  	log.Debugf("Deploying Uniswap Factory")
    67  	factoryAddr, tx, factory, err := UniswapV2Factory.DeployUniswapV2Factory(auth, client, auth.From)
    68  	chkErr(err)
    69  	err = operations.WaitTxToBeMined(ctx, client, tx, txTimeout)
    70  	chkErr(err)
    71  	log.Debugf("Uniswap Factory SC tx: %v", tx.Hash().Hex())
    72  	log.Debugf("Uniswap Factory SC addr: %v", factoryAddr.Hex())
    73  	fmt.Println()
    74  	// Deploy Uniswap Router
    75  	log.Debugf("Deploying Uniswap Router")
    76  	routerAddr, tx, router, err := UniswapV2Router02.DeployUniswapV2Router02(auth, client, factoryAddr, wEthAddr)
    77  	chkErr(err)
    78  	err = operations.WaitTxToBeMined(ctx, client, tx, txTimeout)
    79  	chkErr(err)
    80  	log.Debugf("Uniswap Router SC tx: %v", tx.Hash().Hex())
    81  	log.Debugf("Uniswap Router SC addr: %v", routerAddr.Hex())
    82  	fmt.Println()
    83  	// Deploy Uniswap Interface Multicall
    84  	log.Debugf("Deploying Uniswap Multicall")
    85  	multicallAddr, tx, _, err := UniswapInterfaceMulticall.DeployUniswapInterfaceMulticall(auth, client)
    86  	chkErr(err)
    87  	err = operations.WaitTxToBeMined(ctx, client, tx, txTimeout)
    88  	chkErr(err)
    89  	log.Debugf("Uniswap Interface Multicall SC tx: %v", tx.Hash().Hex())
    90  	log.Debugf("Uniswap Interface Multicall SC addr: %v", multicallAddr.Hex())
    91  	fmt.Println()
    92  	// Mint balance to tokens
    93  	log.Debugf("Minting ERC20 Tokens")
    94  	aMintAmount := "1000000000000000000000"
    95  	tx = mintERC20(auth, client, aCoin, aMintAmount)
    96  	log.Debugf("Mint A Coin tx: %v", tx.Hash().Hex())
    97  	fmt.Println()
    98  	bMintAmount := "1000000000000000000000"
    99  	tx = mintERC20(auth, client, bCoin, bMintAmount)
   100  	log.Debugf("Mint B Coin tx: %v", tx.Hash().Hex())
   101  	fmt.Println()
   102  	cMintAmount := "1000000000000000000000"
   103  	tx = mintERC20(auth, client, cCoin, cMintAmount)
   104  	log.Debugf("Mint C Coin tx: %v", tx.Hash().Hex())
   105  	fmt.Println()
   106  	// wrapping eth
   107  	wethDepositoAmount := "20000000000000000"
   108  	log.Debugf("Depositing %v ETH for account %v on token wEth", wethDepositoAmount, auth.From)
   109  	wAuth := getAuth(ctx, client, pk)
   110  	wAuth.Value, _ = big.NewInt(0).SetString(wethDepositoAmount, encoding.Base10)
   111  	tx, err = wethSC.Deposit(auth)
   112  	chkErr(err)
   113  	err = operations.WaitTxToBeMined(ctx, client, tx, txTimeout)
   114  	chkErr(err)
   115  	value, err := aCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   116  	chkErr(err)
   117  	log.Debugf("before allowance aCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   118  	value, err = bCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   119  	chkErr(err)
   120  	log.Debugf("before allowance bCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   121  	value, err = cCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   122  	chkErr(err)
   123  	log.Debugf("before allowance cCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   124  	// Add allowance
   125  	approveERC20(auth, client, aCoin, routerAddr, aMintAmount)
   126  	fmt.Println()
   127  	approveERC20(auth, client, bCoin, routerAddr, bMintAmount)
   128  	fmt.Println()
   129  	approveERC20(auth, client, cCoin, routerAddr, cMintAmount)
   130  	fmt.Println()
   131  	approveERC20(auth, client, wethSC, routerAddr, wethDepositoAmount)
   132  	fmt.Println()
   133  	const liquidityAmount = "10000000000000000000"
   134  	value, err = aCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   135  	chkErr(err)
   136  	log.Debugf("before adding liquidity A, B aCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   137  	value, err = bCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   138  	chkErr(err)
   139  	log.Debugf("before adding liquidity A, B bCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   140  	value, err = cCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   141  	chkErr(err)
   142  	log.Debugf("before adding liquidity A, B cCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   143  	// Add liquidity to the pool
   144  	tx = addLiquidity(auth, client, router, aCoinAddr, bCoinAddr, liquidityAmount)
   145  	log.Debugf("Add Liquidity to Pair A <-> B tx: %v", tx.Hash().Hex())
   146  	fmt.Println()
   147  	value, err = aCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   148  	chkErr(err)
   149  	log.Debugf("before adding liquidity B, C aCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   150  	value, err = bCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   151  	chkErr(err)
   152  	log.Debugf("before adding liquidity B, C bCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   153  	value, err = cCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   154  	chkErr(err)
   155  	log.Debugf("before adding liquidity B, C cCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   156  	tx = addLiquidity(auth, client, router, bCoinAddr, cCoinAddr, liquidityAmount)
   157  	log.Debugf("Add Liquidity to Pair B <-> C tx: %v", tx.Hash().Hex())
   158  	fmt.Println()
   159  	// Execute swaps
   160  	const swapExactAmountInNumber = 1000000000000000000
   161  	swapExactAmountIn := big.NewInt(swapExactAmountInNumber)
   162  	value, err = aCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   163  	chkErr(err)
   164  	log.Debugf("before first swap aCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   165  	value, err = bCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   166  	chkErr(err)
   167  	log.Debugf("before first swap bCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   168  	value, err = cCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   169  	chkErr(err)
   170  	log.Debugf("before first swap cCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   171  	log.Debugf("Swaping tokens from A <-> B")
   172  	swapExactTokensForTokens(auth, client, factory, router, aCoinAddr, bCoinAddr, swapExactAmountIn)
   173  	fmt.Println()
   174  	value, err = aCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   175  	chkErr(err)
   176  	log.Debugf("after first swap aCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   177  	value, err = bCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   178  	chkErr(err)
   179  	log.Debugf("after first swap bCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   180  	value, err = cCoin.BalanceOf(&bind.CallOpts{}, auth.From)
   181  	chkErr(err)
   182  	log.Debugf("after first swap cCoin.balanceOf[%s]: %d", auth.From.Hex(), value)
   183  	log.Debugf("Swaping tokens from B <-> C")
   184  	swapExactTokensForTokens(auth, client, factory, router, bCoinAddr, cCoinAddr, swapExactAmountIn)
   185  	fmt.Println()
   186  }
   187  func swapExactTokensForTokens(auth *bind.TransactOpts, client *ethclient.Client,
   188  	factory *UniswapV2Factory.UniswapV2Factory, router *UniswapV2Router02.UniswapV2Router02,
   189  	tokenA, tokenB common.Address, exactAmountIn *big.Int) {
   190  	ctx := context.Background()
   191  	logPrefix := fmt.Sprintf("swapExactTokensForTokens %v <-> %v", tokenA.Hex(), tokenB.Hex())
   192  	pairAddr, err := factory.GetPair(nil, tokenA, tokenB)
   193  	chkErr(err)
   194  	log.Debug(logPrefix, " pair: ", pairAddr.Hex())
   195  	pairSC, err := UniswapV2Pair.NewUniswapV2Pair(pairAddr, client)
   196  	chkErr(err)
   197  	pairReserves, err := pairSC.GetReserves(nil)
   198  	chkErr(err)
   199  	log.Debug(logPrefix, " reserves 0: ", pairReserves.Reserve0, " 1: ", pairReserves.Reserve1, " Block Timestamp: ", pairReserves.BlockTimestampLast)
   200  	amountOut, err := router.GetAmountOut(nil, exactAmountIn, pairReserves.Reserve0, pairReserves.Reserve1)
   201  	chkErr(err)
   202  	log.Debug(logPrefix, " exactAmountIn: ", exactAmountIn, " amountOut: ", amountOut)
   203  	tx, err := router.SwapExactTokensForTokens(auth, exactAmountIn, amountOut, []common.Address{tokenA, tokenB}, auth.From, getDeadline())
   204  	chkErr(err)
   205  	log.Debug(logPrefix, " tx: ", tx.Hash().Hex())
   206  	err = operations.WaitTxToBeMined(ctx, client, tx, txTimeout)
   207  	chkErr(err)
   208  }
   209  func getAuth(ctx context.Context, client *ethclient.Client, pkHex string) *bind.TransactOpts {
   210  	chainID, err := client.ChainID(ctx)
   211  	chkErr(err)
   212  	privateKey, err := crypto.HexToECDSA(strings.TrimPrefix(pkHex, "0x"))
   213  	chkErr(err)
   214  	auth, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID)
   215  	chkErr(err)
   216  	return auth
   217  }
   218  func deployERC20(auth *bind.TransactOpts, client *ethclient.Client, name, symbol string) (common.Address, *ERC20.ERC20) {
   219  	ctx := context.Background()
   220  	log.Debugf("Deploying ERC20 Token: [%v]%v", symbol, name)
   221  	addr, tx, instance, err := ERC20.DeployERC20(auth, client, name, symbol)
   222  	chkErr(err)
   223  	err = operations.WaitTxToBeMined(ctx, client, tx, txTimeout)
   224  	chkErr(err)
   225  	log.Debugf("%v SC tx: %v", name, tx.Hash().Hex())
   226  	log.Debugf("%v SC addr: %v", name, addr.Hex())
   227  	return addr, instance
   228  }
   229  func mintERC20(auth *bind.TransactOpts, client *ethclient.Client, erc20sc *ERC20.ERC20, amount string) *types.Transaction {
   230  	ctx := context.Background()
   231  	name, err := erc20sc.Name(nil)
   232  	chkErr(err)
   233  	log.Debugf("Minting %v tokens for account %v on token %v", amount, auth.From, name)
   234  	mintAmount, _ := big.NewInt(0).SetString(amount, encoding.Base10)
   235  	tx, err := erc20sc.Mint(auth, mintAmount)
   236  	chkErr(err)
   237  	err = operations.WaitTxToBeMined(ctx, client, tx, txTimeout)
   238  	chkErr(err)
   239  	return tx
   240  }
   241  func approveERC20(auth *bind.TransactOpts, client *ethclient.Client,
   242  	sc interface {
   243  		Name(opts *bind.CallOpts) (string, error)
   244  		Approve(opts *bind.TransactOpts, spender common.Address, amount *big.Int) (*types.Transaction, error)
   245  	},
   246  	routerAddr common.Address,
   247  	amount string) {
   248  	ctx := context.Background()
   249  	name, err := sc.Name(nil)
   250  	chkErr(err)
   251  	a, _ := big.NewInt(0).SetString(amount, encoding.Base10)
   252  	log.Debugf("Approving %v tokens to be used by the router for %v on behalf of account %v", a.Text(encoding.Base10), name, auth.From)
   253  	tx, err := sc.Approve(auth, routerAddr, a)
   254  	chkErr(err)
   255  	err = operations.WaitTxToBeMined(ctx, client, tx, txTimeout)
   256  	chkErr(err)
   257  	log.Debugf("Approval %v tx: %v", name, tx.Hash().Hex())
   258  }
   259  func addLiquidity(auth *bind.TransactOpts, client *ethclient.Client, router *UniswapV2Router02.UniswapV2Router02, tokenA, tokenB common.Address, amount string) *types.Transaction {
   260  	ctx := context.Background()
   261  	a, _ := big.NewInt(0).SetString(amount, encoding.Base10)
   262  	log.Debugf("Adding liquidity(%v) for tokens A: %v, B:%v, Recipient: %v", amount, tokenA.Hex(), tokenB.Hex(), auth.From.Hex())
   263  	tx, err := router.AddLiquidity(auth, tokenA, tokenB, a, a, a, a, auth.From, getDeadline())
   264  	chkErr(err)
   265  	err = operations.WaitTxToBeMined(ctx, client, tx, txTimeout)
   266  	chkErr(err)
   267  	return tx
   268  }
   269  func getDeadline() *big.Int {
   270  	const deadLinelimit = 5 * time.Minute
   271  	return big.NewInt(time.Now().UTC().Add(deadLinelimit).Unix())
   272  }
   273  func chkErr(err error) {
   274  	if err != nil {
   275  		log.Fatal(err)
   276  	}
   277  }