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 }