Go实现以太坊代币转账

用Go语言实现以太坊代币转账

Posted by HonorJoey on March 12, 2020

打包交易

  • 传入私钥,接收方地址,金额,合约地址,节点地址,GasPrice即可返回打包完成的交易字符串rawtx
  • 然后用写代码或者用postman用POST方式发送交易,
    {"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":["rawtx"],"id":1}
//私钥,要转往的地址,数量(ether),代币地址,请求的以太坊节点地址(https://mainnet.infura.io),gasprice
func SignTokenTx(privateKey, toAddress, value, tokenAddress, ethNet , gas string) (string, error) {
	client, err := ethclient.Dial(ethNet)
	if err != nil {
		log.Println("set ethclient failed!", err)
		return "", err
	}
	privKey, err := crypto.HexToECDSA(privateKey)
	if err != nil {
		log.Println("hextoECDSA privateKey failed!", err)
		return "", err
	}

	pubKey := privKey.Public()
	publicKeyECDSA, ok := pubKey.(*ecdsa.PublicKey)
	if !ok {
		log.Println("cannot assert type: publicKey is not of type *ecdsa.PublicKey", err)
		return "", err
	}

	fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
	nn, err := client.PendingNonceAt(context.Background(), fromAddress)
	if err != nil {
		log.Println(err)
		return "", err
	}

	//单位转换ether=>Wei
	valuef, err := strconv.ParseFloat(value, 64)
	if err != nil {
		log.Println("is not a number")
	}
	valueWei, isOk := new(big.Int).SetString(fmt.Sprintf("%.0f", valuef*1000000000000000000), 10)
	if !isOk {
		log.Println("float to bigInt failed!")
	}

	gasPrice, err := client.SuggestGasPrice(context.Background())
	if err != nil {
		log.Println("set the gasPrice failed!", err)
		return "", err
	}

	toAddr := common.HexToAddress(toAddress)
	tokenAddr := common.HexToAddress(tokenAddress)

	transferFnSignature := []byte("transfer(address,uint256)")
	hash := sha3.NewLegacyKeccak256()
	hash.Write(transferFnSignature)
	methodID := hash.Sum(nil)[:4]

	paddedToAddress := common.LeftPadBytes(toAddr.Bytes(), 32)

	paddedValue := common.LeftPadBytes(valueWei.Bytes(), 32)

	var data []byte
	data = append(data, methodID...)
	data = append(data, paddedToAddress...)
	data = append(data, paddedValue...)

	gasLimit, err := client.EstimateGas(context.Background(), ethereum.CallMsg{
		To:   &toAddr,
		Data: data,
	})
	if err != nil {
		log.Println("set the gasLimit failed!", err)
		return "", err
	}

	gasF, err := strconv.ParseFloat(gas, 64)
	if err != nil {
		return "", err
	}
	gasPrice, isSuccess := new(big.Int).SetString(fmt.Sprintf("%.0f", gasF*1000000000000000), 10)
	if !isSuccess {
		return "", err
	}
	gasLimitInt := new(big.Int).SetInt64(int64(gasLimit))
	gasPrice = gasPrice.Div(gasPrice, gasLimitInt)

	ethValue := big.NewInt(0)

	//需要覆盖前面的交易时
	//nn = uint64(44)

	gasLimit = uint64(90000)
	tx := types.NewTransaction(nn, tokenAddr, ethValue, gasLimit, gasPrice, data)

	chainID, err := client.NetworkID(context.Background())
	if err != nil {
		log.Println("sign the tx failed!", err)
	}
	signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privKey)

	if err != nil {
		log.Println("sign the tx failed!", err)
	}

	/*var buff bytes.Buffer
	if err := signedTx.EncodeRLP(&buff); err != nil {
		return "", err
	}
	sTx := fmt.Sprintf("0x%x", buff.Bytes())*/

	err = client.SendTransaction(context.Background(), signedTx)
	if err != nil {
		log.Println("client send tx failed!", err)
		return "", err
	}
	//log.Printf("tx sent: %s\n", signedTx.Hash().Hex())

	return signedTx.Hash().Hex(), nil
	//return sTx, nil
}