description: Tutorial on how to create a raw Ethereum transaction with Go.

Create Raw Transaction

If you’ve read the previous sections, then you know how to load your private key to sign transactions. We’ll assume you know how to do that by now and now you want to get the raw transaction data to be able to broadcast it at a later time.

First construct the transaction object and sign it, for example:

  1. tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)
  2. signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
  3. if err != nil {
  4. log.Fatal(err)
  5. }

Now before we can get the transaction in raw bytes format we’ll need to initialize a types.Transactions type with the signed transaction as the first value.

  1. ts := types.Transactions{signedTx}

The reason for doing this is because the Transactions type provides a GetRlp method for returning the transaction in RLP encoded format. RLP is a special encoding method Ethereum uses for serializing objects. The result of this is raw bytes.

  1. rawTxBytes := ts.GetRlp(0)

Finally we can very easily turn the raw bytes into a hex string.

  1. rawTxHex := hex.EncodeToString(rawTxBytes)
  2. fmt.Printf(rawTxHex)
  3. // f86d8202b38477359400825208944592d8f8d7b001e72cb26a73e4fa1806a51ac79d880de0b6b3a7640000802ba0699ff162205967ccbabae13e07cdd4284258d46ec1051a70a51be51ec2bc69f3a04e6944d508244ea54a62ebf9a72683eeadacb73ad7c373ee542f1998147b220e

And now you have the raw transaction data which you can use to broadcast at a future date. In the next section we’ll learn how to broadcast a raw transaction.

Full code

transaction_raw_create.go

  1. package main
  2. import (
  3. "context"
  4. "crypto/ecdsa"
  5. "encoding/hex"
  6. "fmt"
  7. "log"
  8. "math/big"
  9. "github.com/ethereum/go-ethereum/common"
  10. "github.com/ethereum/go-ethereum/core/types"
  11. "github.com/ethereum/go-ethereum/crypto"
  12. "github.com/ethereum/go-ethereum/ethclient"
  13. )
  14. func main() {
  15. client, err := ethclient.Dial("https://rinkeby.infura.io")
  16. if err != nil {
  17. log.Fatal(err)
  18. }
  19. privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19")
  20. if err != nil {
  21. log.Fatal(err)
  22. }
  23. publicKey := privateKey.Public()
  24. publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
  25. if !ok {
  26. log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
  27. }
  28. fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
  29. nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
  30. if err != nil {
  31. log.Fatal(err)
  32. }
  33. value := big.NewInt(1000000000000000000) // in wei (1 eth)
  34. gasLimit := uint64(21000) // in units
  35. gasPrice, err := client.SuggestGasPrice(context.Background())
  36. if err != nil {
  37. log.Fatal(err)
  38. }
  39. toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
  40. var data []byte
  41. tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)
  42. chainID, err := client.NetworkID(context.Background())
  43. if err != nil {
  44. log.Fatal(err)
  45. }
  46. signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
  47. if err != nil {
  48. log.Fatal(err)
  49. }
  50. ts := types.Transactions{signedTx}
  51. rawTxBytes := ts.GetRlp(0)
  52. rawTxHex := hex.EncodeToString(rawTxBytes)
  53. fmt.Printf(rawTxHex) // f86...772
  54. }