github.com/0chain/gosdk@v1.17.11/zcncore/ethwallet_base.go (about) 1 package zcncore 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "encoding/json" 7 "fmt" 8 "log" 9 "math" 10 "math/big" 11 "regexp" 12 "sync" 13 14 "github.com/0chain/gosdk/core/tokenrate" 15 "github.com/0chain/gosdk/core/zcncrypto" 16 hdwallet "github.com/0chain/gosdk/zcncore/ethhdwallet" 17 "github.com/ethereum/go-ethereum" 18 "github.com/ethereum/go-ethereum/common" 19 "github.com/ethereum/go-ethereum/common/hexutil" 20 "github.com/ethereum/go-ethereum/core/types" 21 "github.com/ethereum/go-ethereum/crypto" 22 "github.com/ethereum/go-ethereum/ethclient" 23 "github.com/ethereum/go-ethereum/params" 24 "golang.org/x/crypto/sha3" 25 ) 26 27 // TODO change to real wallets 28 const walletAddr = "0xb9EF770B6A5e12E45983C5D80545258aA38F3B78" 29 const tokenAddress = "0x28b149020d2152179873ec60bed6bf7cd705775d" 30 31 var once sync.Once 32 33 var ethClient *ethclient.Client 34 35 var getEthClient = func() (*ethclient.Client, error) { 36 var err error 37 38 once.Do(func() { 39 if len(_config.chain.EthNode) == 0 { 40 err = fmt.Errorf("eth node SDK not initialized") 41 return 42 } 43 44 logging.Info("requesting from ", _config.chain.EthNode) 45 ethClient, err = ethclient.Dial(_config.chain.EthNode) 46 }) 47 48 return ethClient, err 49 } 50 51 // TokensToEth - converting wei to eth tokens 52 func TokensToEth(tokens int64) float64 { 53 fbalance := new(big.Float) 54 fbalance.SetString(fmt.Sprint(tokens)) 55 ethValue, _ := new(big.Float).Quo(fbalance, big.NewFloat(math.Pow10(18))).Float64() 56 return ethValue 57 } 58 59 // TokensToEth - converting eth tokens to wei 60 func EthToTokens(tokens float64) int64 { 61 return int64(tokens * float64(params.Ether)) 62 } 63 64 func GTokensToEth(tokens int64) float64 { 65 return float64(tokens) / float64(params.GWei) 66 } 67 68 func GEthToTokens(gwei float64) int64 { 69 return int64(gwei * float64(params.GWei)) 70 } 71 72 // GetWalletAddrFromEthMnemonic - wallet ETH address from mnemoninnc 73 func GetWalletAddrFromEthMnemonic(mnemonic string) (string, error) { 74 wallet, err := hdwallet.NewFromMnemonic(mnemonic) 75 if err != nil { 76 return "", err 77 } 78 79 path := hdwallet.MustParseDerivationPath("m/44'/60'/0'/0/0") 80 account, err := wallet.Derive(path, false) 81 if err != nil { 82 return "", err 83 } 84 85 privKey, err := wallet.PrivateKeyHex(account) 86 if err != nil { 87 return "", err 88 } 89 90 type ethWalletinfo struct { 91 ID string `json:"ID"` 92 PrivateKey string `json:"PrivateKey"` 93 } 94 95 res, err := json.Marshal(ethWalletinfo{ID: account.Address.Hex(), PrivateKey: privKey}) 96 return string(res), err 97 } 98 99 // GetEthBalance - getting back balance for ETH wallet 100 func GetEthBalance(ethAddr string, cb GetBalanceCallback) error { 101 go func() { 102 value, err := getBalanceFromEthNode(ethAddr) 103 if err != nil { 104 logging.Error(err) 105 cb.OnBalanceAvailable(StatusError, 0, err.Error()) 106 return 107 } 108 cb.OnBalanceAvailable(StatusSuccess, value, "") 109 }() 110 return nil 111 } 112 113 // IsValidEthAddress - multiple checks for valid ETH address 114 func IsValidEthAddress(ethAddr string) (bool, error) { 115 client, err := getEthClient() 116 if err != nil { 117 return false, err 118 } 119 120 return isValidEthAddress(ethAddr, client) 121 } 122 123 // IsEthTransactionVerified checks if the transaction - given its hash - is verified on the ethereum network 124 // - txHash: transaction hash 125 func IsEthTransactionVerified(txHash string) (bool, error) { 126 client, err := getEthClient() 127 if err != nil { 128 return false, err 129 } 130 131 var ( 132 tx *types.Transaction 133 pending bool 134 ) 135 136 tx, pending, err = client.TransactionByHash(context.Background(), common.HexToHash(txHash)) 137 if err != nil { 138 return false, err 139 } 140 return tx != nil && !pending, nil 141 } 142 143 func isValidEthAddress(ethAddr string, client *ethclient.Client) (bool, error) { 144 re := regexp.MustCompile("^0x[0-9a-fA-F]{40}$") 145 if !re.MatchString(ethAddr) { 146 return false, fmt.Errorf("regex error") 147 } 148 149 address := common.HexToAddress(ethAddr) 150 balance, err := client.BalanceAt(context.Background(), address, nil) 151 if err != nil { 152 return false, fmt.Errorf(err.Error()) 153 } 154 155 isContract := balance.Int64() > 0 156 return isContract, nil 157 } 158 159 // CreateWalletFromEthMnemonic - creating new wallet from Eth mnemonics 160 func CreateWalletFromEthMnemonic(mnemonic, password string, statusCb WalletCallback) error { 161 if len(_config.chain.Miners) < 1 || len(_config.chain.Sharders) < 1 { 162 return fmt.Errorf("SDK not initialized") 163 } 164 go func() { 165 sigScheme := zcncrypto.NewSignatureScheme(_config.chain.SignatureScheme) 166 _, err := sigScheme.GenerateKeysWithEth(mnemonic, password) 167 if err != nil { 168 statusCb.OnWalletCreateComplete(StatusError, "", err.Error()) 169 return 170 } 171 }() 172 return nil 173 } 174 175 // CheckEthHashStatus - checking the status of ETH transaction 176 // possible values 0 or 1 177 func CheckEthHashStatus(hash string) int { 178 txHash := common.HexToHash(hash) 179 180 var client *ethclient.Client 181 var err error 182 if client, err = getEthClient(); err != nil { 183 return -1 184 } 185 186 tx, err := client.TransactionReceipt(context.Background(), txHash) 187 if err != nil { 188 return -1 189 } 190 return int(tx.Status) 191 } 192 193 // ConvertZcnTokenToETH - converting Zcn tokens to Eth 194 // - f: ZCN tokens amount 195 func ConvertZcnTokenToETH(f float64) (float64, error) { 196 ethRate, err := tokenrate.GetUSD(context.TODO(), "eth") 197 if err != nil { 198 return 0, err 199 } 200 return f * ethRate, nil 201 } 202 203 // SuggestEthGasPrice - return back suggested price for gas 204 func SuggestEthGasPrice() (int64, error) { 205 var client *ethclient.Client 206 var err error 207 if client, err = getEthClient(); err != nil { 208 return 0, err 209 } 210 211 gasPrice, err := client.SuggestGasPrice(context.Background()) 212 if err != nil { 213 return 0, err 214 } 215 216 return gasPrice.Int64(), nil 217 } 218 219 // TransferEthTokens - transfer ETH tokens to multisign wallet 220 func TransferEthTokens(fromPrivKey string, amountTokens, gasPrice int64) (string, error) { 221 var client *ethclient.Client 222 var err error 223 if client, err = getEthClient(); err != nil { 224 return "", err 225 } 226 227 privateKey, err := crypto.HexToECDSA(fromPrivKey) 228 if err != nil { 229 return "", err 230 } 231 232 publicKey := privateKey.Public() 233 publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey) 234 235 fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) 236 nonce, err := client.PendingNonceAt(context.Background(), fromAddress) 237 if err != nil { 238 return "", err 239 } 240 241 toAddress := common.HexToAddress(walletAddr) 242 tokenAddress := common.HexToAddress(tokenAddress) 243 244 transferFnSignature := []byte("transfer(address,uint256)") 245 hash := sha3.NewLegacyKeccak256() 246 hash.Write(transferFnSignature) 247 methodID := hash.Sum(nil)[:4] 248 fmt.Println(hexutil.Encode(methodID)) // 0xa9059cbb 249 250 paddedAddress := common.LeftPadBytes(toAddress.Bytes(), 32) 251 fmt.Println(hexutil.Encode(paddedAddress)) // 0x0000000000000000000000004592d8f8d7b001e72cb26a73e4fa1806a51ac79d 252 253 amount := new(big.Int) 254 amount.SetInt64(amountTokens) 255 256 paddedAmount := common.LeftPadBytes(amount.Bytes(), 32) 257 fmt.Println(hexutil.Encode(paddedAmount)) // 0x00000000000000000000000000000000000000000000003635c9adc5dea00000 258 259 var data []byte 260 data = append(data, methodID...) 261 data = append(data, paddedAddress...) 262 data = append(data, paddedAmount...) 263 264 gasLimit, err := client.EstimateGas(context.Background(), ethereum.CallMsg{ 265 To: &tokenAddress, 266 Data: data, 267 }) 268 if err != nil { 269 log.Fatal(err) 270 } 271 272 txData := &types.LegacyTx{ 273 Nonce: nonce, 274 GasPrice: big.NewInt(gasPrice), 275 Gas: gasLimit, 276 To: &tokenAddress, 277 Value: amount, 278 Data: data, 279 } 280 tx := types.NewTx(txData) 281 282 chainID, err := client.ChainID(context.Background()) 283 if err != nil { 284 return "", err 285 } 286 287 signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey) 288 if err != nil { 289 return "", err 290 } 291 292 err = client.SendTransaction(context.Background(), signedTx) 293 if err != nil { 294 return "", err 295 } 296 297 return signedTx.Hash().Hex(), nil 298 } 299 300 func getBalanceFromEthNode(ethAddr string) (int64, error) { 301 if client, err := getEthClient(); err == nil { 302 account := common.HexToAddress(ethAddr) 303 logging.Info("for eth address", account) 304 balance, err := client.BalanceAt(context.Background(), account, nil) 305 if err != nil { 306 return 0, err 307 } 308 309 logging.Info("balance", balance.String()) 310 311 return balance.Int64(), nil 312 } else { 313 return 0, err 314 } 315 }