github.com/0xsequence/ethkit@v1.25.0/cmd/chain-blast/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"math/big"
     9  	"os"
    10  	"time"
    11  
    12  	"github.com/0xsequence/ethkit/ethrpc"
    13  	"github.com/0xsequence/ethkit/ethwallet"
    14  	"github.com/0xsequence/ethkit/go-ethereum"
    15  	"github.com/0xsequence/ethkit/go-ethereum/common"
    16  	"github.com/0xsequence/ethkit/go-ethereum/core/types"
    17  	"github.com/0xsequence/ethkit/util"
    18  )
    19  
    20  // https://explorer-mainnet.maticvigil.com/address/0xb59ba5A13f0fb106EA6094a1F69786AA69be1424/transactions
    21  
    22  var (
    23  	ETH_NODE_URL = "http://localhost:8545"
    24  
    25  	// keep this private, but requires a wallet with ETH or MATIC (depending on network)
    26  	PRIVATE_WALLET_MNEMONIC = ""
    27  
    28  	// token contract for testing
    29  	ERC20_TEST_CONTRACT = "0xCCCD8b34e94F52eDFAdA6e6Ae4AE1C1ab43F9D67"
    30  )
    31  
    32  func init() {
    33  	testConfig, err := util.ReadTestConfig("../../ethkit-test.json")
    34  	if err != nil {
    35  		panic(err)
    36  	}
    37  
    38  	if testConfig["POLYGON_MAINNET_URL"] != "" {
    39  		ETH_NODE_URL = testConfig["POLYGON_MAINNET_URL"]
    40  	}
    41  
    42  	PRIVATE_WALLET_MNEMONIC = testConfig["PRIVATE_WALLET_MNEMONIC"]
    43  }
    44  
    45  func main() {
    46  	fmt.Println("chain-blast start")
    47  	fmt.Println("")
    48  
    49  	// Provider
    50  	provider, err := ethrpc.NewProvider(ETH_NODE_URL)
    51  	if err != nil {
    52  		fatal(err, "provider setup")
    53  	}
    54  
    55  	// Wallet
    56  	wallet, err := ethwallet.NewWalletFromMnemonic(PRIVATE_WALLET_MNEMONIC)
    57  	// wallet, err := ethwallet.NewWalletFromRandomEntropy()
    58  	if err != nil {
    59  		fatal(err, "wallet setup")
    60  	}
    61  	wallet.SetProvider(provider)
    62  
    63  	// Check wallet balance
    64  	balance, err := provider.BalanceAt(context.Background(), wallet.Address(), nil)
    65  	if err != nil {
    66  		log.Fatal(err)
    67  	}
    68  
    69  	fmt.Println("=> wallet address", wallet.Address().String())
    70  	fmt.Println("=> wallet balance", balance.Int64())
    71  
    72  	if balance.Cmp(big.NewInt(0)) == 0 {
    73  		fatal(nil, "wallet balance is 0")
    74  	}
    75  
    76  	// deploy new contract
    77  	if ERC20_TEST_CONTRACT == "" {
    78  		fmt.Println("")
    79  		fmt.Println("ERC20_TEST_CONTRACT var is empty, need to deploy a new contract..")
    80  		promptAreYouSure()
    81  
    82  		contractAddress, err := deployERC20(wallet)
    83  		if err != nil {
    84  			fatal(err, "deployERC20 failed")
    85  		}
    86  
    87  		fmt.Println("Set ERC20_TEST_CONTRACT and rerun:", contractAddress.Hex())
    88  
    89  		ERC20_TEST_CONTRACT = contractAddress.String()
    90  	}
    91  
    92  	// confirm contract is deployed
    93  	code, err := provider.CodeAt(context.Background(), common.HexToAddress(ERC20_TEST_CONTRACT), nil)
    94  	if err != nil {
    95  		fatal(err, "codeAt %s contract failed", ERC20_TEST_CONTRACT)
    96  	}
    97  	if len(code) == 0 {
    98  		fatal(nil, "codeAt %s contract failed", ERC20_TEST_CONTRACT)
    99  	}
   100  	fmt.Println("=> erc20 test contract: deployed")
   101  
   102  	// run transfer test
   103  	err = transferERC20s(wallet)
   104  	if err != nil {
   105  		fatal(err, "transferERC20s")
   106  	}
   107  }
   108  
   109  func deployERC20(wallet *ethwallet.Wallet) (common.Address, error) {
   110  	provider := wallet.GetProvider()
   111  	auth, err := wallet.Transactor(context.Background())
   112  	if err != nil {
   113  		return common.Address{}, err
   114  	}
   115  
   116  	address, contractTxn, erc20, err := DeployERC20Mock(auth, provider)
   117  	if err != nil {
   118  		return common.Address{}, err
   119  	}
   120  	fmt.Println("Contract creation txn hash:", contractTxn.Hash().Hex())
   121  	err = waitForTxn(provider, contractTxn.Hash())
   122  	if err != nil {
   123  		return common.Address{}, err
   124  	}
   125  
   126  	txn, err := erc20.MockMint(auth, wallet.Address(), big.NewInt(1000000000000))
   127  	fmt.Println("erc20 mint txn hash:", txn.Hash().Hex())
   128  	err = waitForTxn(provider, txn.Hash())
   129  	if err != nil {
   130  		return common.Address{}, err
   131  	}
   132  
   133  	return address, nil
   134  }
   135  
   136  var randomRecipient = "0x1234567890123456789012345678901234567890"
   137  
   138  func transferERC20s(wallet *ethwallet.Wallet) error {
   139  	provider := wallet.GetProvider()
   140  
   141  	fmt.Println("")
   142  
   143  	erc20, err := NewERC20Mock(common.HexToAddress(ERC20_TEST_CONTRACT), provider)
   144  	if err != nil {
   145  		fatal(err, "NewERC20Mock")
   146  	}
   147  
   148  	balance, err := erc20.BalanceOf(nil, wallet.Address())
   149  	if err != nil {
   150  		fatal(err, "balanceOf")
   151  	}
   152  	fmt.Println("=> wallet token balance", balance.Int64())
   153  
   154  	if balance.Cmp(big.NewInt(0)) == 0 {
   155  		fatal(nil, "wallet token balance is 0")
   156  	}
   157  
   158  	// get ready to blast
   159  	txnCount, err := provider.NonceAt(context.Background(), wallet.Address(), nil)
   160  	if err != nil {
   161  		return err
   162  	}
   163  	nonce, err := provider.PendingNonceAt(context.Background(), wallet.Address())
   164  	if err != nil {
   165  		return err
   166  	}
   167  	fmt.Println("=> wallet txn count:", txnCount)
   168  	fmt.Println("=> wallet latest nonce:", nonce)
   169  
   170  	fmt.Println("")
   171  
   172  	// blastNum := 5 // num txns at a time to dispatch
   173  	numTxns := 10 // will send this many parallel txns
   174  
   175  	// marks that we send a txn at a time, and wait for it..
   176  	var waitForEachTxn bool
   177  	// waitForEachTxn = true
   178  	waitForEachTxn = false
   179  
   180  	auth, err := wallet.Transactor(context.Background())
   181  	if err != nil {
   182  		return err
   183  	}
   184  
   185  	// TODO: lets use ethmempool + subscribeWithFilter, and listen for transactions as they come in
   186  
   187  	for i := 0; i < numTxns; i++ {
   188  		// increment nonce ourselves to send parallel txns
   189  		auth.Nonce = big.NewInt(0).SetUint64(nonce)
   190  
   191  		// dispatch the txn
   192  		txn, err := erc20.Transfer(auth, common.HexToAddress(randomRecipient), big.NewInt(8))
   193  		if err != nil {
   194  			fatal(err, "transfer #%d failed", i)
   195  		}
   196  		fmt.Printf("Sent txn %d with hash %s\n", i, txn.Hash().Hex())
   197  
   198  		if waitForEachTxn {
   199  			startTime := time.Now()
   200  			err = waitForTxn(provider, txn.Hash())
   201  			if err != nil {
   202  				fatal(err, "transfer wait failed for txn %s", txn.Hash().Hex())
   203  			}
   204  			fmt.Printf("Txn mined in %s\n", time.Now().Sub(startTime))
   205  			fmt.Println("")
   206  		}
   207  
   208  		// increment nonce for next txn
   209  		nonce += 1
   210  	}
   211  
   212  	// wallet balance is now..:
   213  	balance, err = erc20.BalanceOf(nil, wallet.Address())
   214  	if err != nil {
   215  		fatal(err, "balanceOf")
   216  	}
   217  	fmt.Println("=> wallet token balance", balance.Int64())
   218  
   219  	return nil
   220  }
   221  
   222  func promptAreYouSure() {
   223  	fmt.Println("")
   224  	fmt.Printf("Are you sure you'd like to deploy a new ERC20 contract? [y/n]: ")
   225  
   226  	resp := ""
   227  	fmt.Scanln(&resp)
   228  
   229  	fmt.Println("")
   230  
   231  	if resp != "y" {
   232  		fmt.Println("okay, exiting..")
   233  		os.Exit(0)
   234  	}
   235  }
   236  
   237  func waitForTxn(provider *ethrpc.Provider, hash common.Hash) error {
   238  	for {
   239  		receipt, err := provider.TransactionReceipt(context.Background(), hash)
   240  		if errors.Is(err, ethereum.NotFound) {
   241  			time.Sleep(1 * time.Second)
   242  			continue
   243  		}
   244  		if err != nil {
   245  			return err
   246  		}
   247  
   248  		if receipt.Status == types.ReceiptStatusSuccessful {
   249  			return nil
   250  		} else {
   251  			fmt.Printf("txnHash %s failed", hash.Hex())
   252  			return errors.New("txn failed")
   253  		}
   254  	}
   255  }
   256  
   257  func fatal(err error, msg string, a ...interface{}) {
   258  	if err != nil {
   259  		fmt.Println(fmt.Sprintf("fatal error! %s: %v", fmt.Sprintf(msg, a...), err))
   260  	} else {
   261  		fmt.Println(fmt.Sprintf("fatal error! %s", fmt.Sprintf(msg, a...)))
   262  	}
   263  	os.Exit(1)
   264  }