5

I would like to write the Ethereum blockchain with a contract call. I already found two solutions which are almost the same, but one of them is manipulating the signed transaction, doing some byte encodings before sending it and I couldn't figure out why. My question is that why solution #2 and solution #3 uses the extra lines compared to solution #1? What is the purpose of the extra byte manipulation part? signedTx and txToSend are both *types.Transaction types, I don't understand why is it needed to do the encodings. The documentation of the go-ethereum package states that:

SendTransaction injects a signed transaction into the pending pool for execution.

It doesn't give further information about the tx and types.SignTx() returns *types.Transaction type.

Solution #1

This is the simplest solution without doing any manipulation with signedTx.

tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
    log.Fatal(err)
}
txErr := client.SendTransaction(context.Background(), tx)
if txErr != nil {
    log.Fatalf("Error calling contract: %v", err)
}

Solution #2

This is the implementation used by the Go Ethereum Book's creating raw transaction and sending raw transaction part.

tx_signed := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)

signedTx, err := types.SignTx(tx_signed, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
  log.Fatal(err)
}

ts := types.Transactions{signedTx}
rawTxBytes := ts.GetRlp(0)
rawTxHex := hex.EncodeToString(rawTxBytes)
rawBytes, err := hex.DecodeString(rawTxHex)

tx := new(types.Transaction)
rlp.DecodeBytes(rawBytes, &tx)

txErr := client.SendTransaction(context.Background(), tx)
if txErr != nil {
    log.Fatalf("Error calling contract: %v", err)
}

Solution #3

This implementation is almost the same as the previous, but it uses the newer EncodeIndex(i int, w *bytes.Buffer) function for the byte manipulation. Source

tx_signed := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)

signedTx, err := types.SignTx(tx_signed, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
  log.Fatal(err)
}

ts := types.Transactions{signedTx}
b := new(bytes.Buffer)
ts.EncodeIndex(0, b)
rawTxBytes := b.Bytes()

txToSend := new(types.Transaction)
rlp.DecodeBytes(rawTxBytes, &txToSend)
txErr := client.SendTransaction(context.Background(), tx)
if txErr != nil {
    log.Fatalf("Error calling contract: %v", err)
}
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
rihekopo
  • 3,241
  • 4
  • 34
  • 63

2 Answers2

1

There are different types of ethereum transactions: EIP1559, EIP2711, EIP2718

Not all geth clients supports each transaction type. I think the transactions that encoded in your question are EIP2718.

https://blog.mycrypto.com/new-transaction-types-on-ethereum

From the above article:

EIP-2718 defines a new generalised envelope for typed transactions. In the new standard, transactions look like this:

TransactionType || TransactionPayload

Where the fields are defined as:

TransactionType: a number between 0 and 0x7f, for a total of 128 possible transaction types. TransactionPayload: an arbitrary byte array, defined by the transaction type. These fields are concatenated (combined) to form a typed transaction. The standard does not describe a format for the transaction payload; it can be any arbitrary series of bytes, encoded with any encoder as defined by the new transaction type (e.g., RLP, SSZ, …). Simple byte concatenation was chosen because it's trivial to read the first byte of a byte array without the need for any libraries or tools: You don't need an RLP or SSZ parser to check the transaction type.

or maybe a different EIP type. check your geth client version, and which ones that support

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
0

If the goal is to create a transaction and broadcast it, solution 1 is fine. Behind the scene, the golang library encodes the transaction to a byte array and sends it to the geth node.

I think the book wanted to be more precise and show what happens in the middle (e.g. print the hex of the encoded transaction). This is the reason why he does that encoding. Is it mandatory? No.

It could be useful to be able to encode and decode transaction, for example you create the transaction somewhere else and you want only to sign/broadcast it inside your go program. In this case you have to decode the array of bytes and create the transaction from it.

Alberto Lerda
  • 401
  • 2
  • 7