Nethereum Raw Transaction – How to Retrieve

nethereumraw-transactionweb3js

How do I get a raw transaction for a transaction on the blockchain using web3/Nethereum?

An example of a raw transaction can be seen here:
https://etherscan.io/getRawTx?tx=0x3866bd52ab1273b9b014e81f981fb35e6b1ec3254514e09c604921ecc17a6ad6

Best Answer

The Nethereum does not support it directly but all versions of Parity wallet supports it and recent versions of geth also supports it, so we can send an RPC call directly without using any Nethereum RPC call. The RPC method in question is eth_getRawTransactionByHash.

Like so

        /// <summary>
        /// Attempt to get the raw hex directly from the geth node
        /// </summary>
        /// <param name="txid"></param>
        /// <returns></returns>
        internal string GetRawTxHexByGetRawTransactionByHash(string txid)
        {
            // Extract the HttpClient object from the web3 object
            FieldInfo field = typeof(Nethereum.JsonRpc.Client.RpcClient).GetField("_httpClient", BindingFlags.NonPublic | BindingFlags.Instance);
            HttpClient httpClient = (HttpClient)field.GetValue(_web3.Client);

            // Make the post request to our own Geth node and deserialize the result
            RpcRequest rpcRequest = new RpcRequest
            {
                method = "eth_getRawTransactionByHash",
                @params = new string[] { txid },
            };
            HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri("/", UriKind.Relative));
            requestMessage.Content = new StringContent(JsonConvert.SerializeObject(rpcRequest), Encoding.UTF8, "application/json");
            HttpResponseMessage resp = httpClient.SendAsync(requestMessage).Result;
            if (!resp.IsSuccessStatusCode)
            {
                _logger.LogWarning($"Failed to call to eth_getRawTransactionByHash for txid={txid}.");
                return null;
            }

            EthGetRawTransactionByHashResponse txResp =
                JsonConvert.DeserializeObject<EthGetRawTransactionByHashResponse>(resp.Content.ReadAsStringAsync().Result);

            return txResp.result;
        }

where the following helper classes have been used

        private abstract class Web3RpcBase
        {
            public int id { get; set; } = 67;
            public string jsonrpc { get; set; } = "2.0";
        }

        private class RpcRequest : Web3RpcBase
        {
            public string method { get; set; }
            public string[] @params { get; set; }
        }

        private class EthGetRawTransactionByHashResponse : Web3RpcBase
        {
            public string result { get; set; }
        }

If you are so unfortunate to use a node that does not support eth_getRawTransactionByHash (old geth versions), you can still get the raw hex using the following functions to construct the raw hex from available data.

        private abstract class Web3RpcBase
        {
            public int id { get; set; } = 67;
            public string jsonrpc { get; set; } = "2.0";
        }

        private class RpcRequest : Web3RpcBase
        {
            public string method { get; set; }
            public string[] @params { get; set; }
        }


        private class EthGetTransactionByHashResponseResult

        {
            public string blockHash { get; set; }
            public string blockNumber { get; set; }
            public string from { get; set; }

            // This is what we usually call gas limit
            public string gas { get; set; }
            public string gasPrice { get; set; }
            public string hash { get; set; }

            // This is what we usually call data
            public string input { get; set; }
            public string nonce { get; set; }
            public string to { get; set; }
            public string transactionIndex { get; set; }
            public string value { get; set; }
            public string v { get; set; }
            public string r { get; set; }
            public string s { get; set; }
        }


        private class EthGetTransactionByHashResponse : Web3RpcBase

        {
            public EthGetTransactionByHashResponseResult result { get; set; }
        }

        private class EthGetRawTransactionByHashResponse : Web3RpcBase
        {
            public string result { get; set; }
        }


        /// <summary>
        /// Should only be used for Ethereum, not BTC.
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        private static byte[] HexToByteArrayWrapper(string hexString)
        {
            // Prevent "0" from becoming "00" represented by[0]. "0" should be[], and 1 should be [0x01]
            if (string.IsNullOrEmpty(hexString) || hexString == "0" || hexString == "0x0")
            {
                return Array.Empty<byte>();
            }

            return hexString.HexToByteArray();
        }

        /// <summary>
        /// Attempt to get the raw hex directly from the geth node
        /// </summary>
        /// <param name="txid"></param>
        /// <returns></returns>
        internal string GetRawTxHexByGetRawTransactionByHash(string txid)
        {
            // Extract the HttpClient object from the web3 object
            FieldInfo field = typeof(Nethereum.JsonRpc.Client.RpcClient).GetField("_httpClient", BindingFlags.NonPublic | BindingFlags.Instance);
            HttpClient httpClient = (HttpClient)field.GetValue(_web3.Client);

            // Make the post request to our own Geth node and deserialize the result
            RpcRequest rpcRequest = new RpcRequest
            {
                method = "eth_getRawTransactionByHash",
                @params = new string[] { txid },
            };
            HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri("/", UriKind.Relative));
            requestMessage.Content = new StringContent(JsonConvert.SerializeObject(rpcRequest), Encoding.UTF8, "application/json");
            HttpResponseMessage resp = httpClient.SendAsync(requestMessage).Result;
            if (!resp.IsSuccessStatusCode)
            {
                _logger.LogWarning($"Failed to call to eth_getRawTransactionByHash for txid={txid}.");
                return null;
            }

            EthGetRawTransactionByHashResponse txResp =
                JsonConvert.DeserializeObject<EthGetRawTransactionByHashResponse>(resp.Content.ReadAsStringAsync().Result);

            return txResp.result;
        }

        /// <summary>
        /// Attempt to reconstruct the raw hex by getting the transaction info from the geth node
        /// </summary>
        /// <param name="txid"></param>
        /// <returns></returns>
        internal string GetRawTxHexByGetTransactionByHash(string txid)
        {
            // Extract the HttpClient object from the web3 object
            FieldInfo field = typeof(Nethereum.JsonRpc.Client.RpcClient).GetField("_httpClient", BindingFlags.NonPublic | BindingFlags.Instance);
            HttpClient httpClient = (HttpClient)field.GetValue(_web3.Client);

            // Make the post request to our own Geth node and deserialize the result
            RpcRequest rpcRequest = new RpcRequest
            {
                method = "eth_getTransactionByHash",
                @params = new string[] { txid },
            };
            HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri("/", UriKind.Relative));
            requestMessage.Content = new StringContent(JsonConvert.SerializeObject(rpcRequest), Encoding.UTF8, "application/json");
            HttpResponseMessage resp = httpClient.SendAsync(requestMessage).Result;
            if (!resp.IsSuccessStatusCode)
            {
                _logger.LogWarning($"Failed to call to eth_getTransactionByHash for txid={txid}.");
                return null;
            }

            EthGetTransactionByHashResponse txResp = JsonConvert.DeserializeObject<EthGetTransactionByHashResponse>(resp.Content.ReadAsStringAsync().Result);

            // Create the signature object to insert in the transaction (needed to support chain IDs above 0xff)
            Org.BouncyCastle.Math.BigInteger r = new Org.BouncyCastle.Math.BigInteger(txResp.result.r.Substring(2), 0x10);
            Org.BouncyCastle.Math.BigInteger s = new Org.BouncyCastle.Math.BigInteger(txResp.result.s.Substring(2), 0x10);
            byte[] v = new byte[] { byte.Parse(txResp.result.v.Substring(2), System.Globalization.NumberStyles.HexNumber, CultureInfo.GetCultureInfo("en-US")) };
            EthECDSASignature signature = new Nethereum.Signer.EthECDSASignature(r, s, v);

            // Create unsigned transaction object
            Nethereum.Signer.Transaction transaction = new Nethereum.Signer.Transaction(
                HexToByteArrayWrapper(txResp.result.nonce),
                HexToByteArrayWrapper(txResp.result.gasPrice),
                HexToByteArrayWrapper(txResp.result.gas),
                HexToByteArrayWrapper(txResp.result.to),
                HexToByteArrayWrapper(txResp.result.value),
                HexToByteArrayWrapper(txResp.result.input),
                Array.Empty<byte>(),
                Array.Empty<byte>(),
                0);
            transaction.SetSignature(signature);

            // Get the hex format of the RLP-encoded transaction, the rawtx
            string rawTxHex = "0x" + transaction.GetRLPEncoded().Select(x => x.ToString("x2", NumberFormatInfo.InvariantInfo)).Aggregate((acc, elem) => acc + elem);
            return rawTxHex;
        }

        /// <summary>
        /// Given a txid return the RLP-encoded raw hex that represents the transcation on the blockchain.
        /// </summary>
        /// <param name="txid"></param>
        /// <returns></returns>
        internal string GetRawHex(string txid)
        {
            // First try to get the raw hex directly from the node
            string res = GetRawTxHexByGetRawTransactionByHash(txid);
            if (!String.IsNullOrWhiteSpace(res) && res != "0x")
            {
                return res;
            }

            // If that did not work, attempt to construct it from tx details
            _logger.LogInformation("Failed to get raw hex from node, calculating it locally instead.");
            return GetRawTxHexByGetTransactionByHash(txid);
        }