private void ValidateTx(
            RelayResponse returnedTx,
            TransactionInput transaction,
            BigInteger txFee,
            BigInteger gasPrice,
            BigInteger gasLimit,
            BigInteger nonce,
            string relayHubAddress,
            string relayAddress)
        {
            var tx     = returnedTx.ToTransaction();
            var signer = tx.Key.GetPublicAddress();

            var functionEncodingService = new FunctionMessageEncodingService <RelayCallFunction>();
            var relayCall = functionEncodingService.DecodeInput(new RelayCallFunction(), returnedTx.Input);

            var returnedTxHash = GetTransactionHash(
                relayCall.From,
                relayCall.To,
                relayCall.EncodedFunction.ToHex(),
                relayCall.TransactionFee,
                relayCall.GasPriceParam,
                relayCall.GasLimit,
                relayCall.NonceParam,
                returnedTx.To,
                signer);

            var hash = GetTransactionHash(
                transaction.From,
                transaction.To,
                transaction.Data,
                txFee,
                transaction.GasPrice.Value,
                transaction.Gas.Value,
                nonce,
                relayHubAddress,
                relayAddress);

            if (returnedTxHash.EnsureHexPrefix().ToLower() != hash.EnsureHexPrefix().ToLower() ||
                signer.EnsureHexPrefix().ToLower() != relayAddress.EnsureHexPrefix().ToLower())
            {
                throw new GSNRelayInvalidResponseException();
            }
        }
        private async Task <string> SendViaRelay(
            string relayUrl,
            TransactionInput transaction,
            BigInteger txFee,
            BigInteger gasPrice,
            BigInteger gasLimit,
            BigInteger nonce,
            string relayHubAddress,
            string relayAddress,
            string signature,
            string approvalData,
            BigInteger relayMaxNonce)
        {
            var requestData = new RelayRequest
            {
                EncodedFunction = transaction.Data,
                Signature       = signature.HexToByteArray(),
                ApprovalData    = approvalData.HexToByteArray(),
                From            = transaction.From,
                To              = transaction.To,
                GasPrice        = gasPrice,
                GasLimit        = gasLimit,
                RelayFee        = txFee,
                RecipientNonce  = nonce,
                RelayMaxNonce   = relayMaxNonce,
                RelayHubAddress = relayHubAddress,
                UserAgent       = _options.UserAgent
            };

            RelayResponse relayResponse = await _relayClient.RelayAsync(new Uri(relayUrl), requestData)
                                          .ConfigureAwait(false);

            if (!string.IsNullOrEmpty(relayResponse.Error))
            {
                throw new Exception(relayResponse.Error);
            }

            if (relayResponse.Nonce.Value.IsZero)
            {
                throw new Exception("Empty body received from server, or neither 'error' nor 'nonce' fields present.");
            }

            ValidateTx(
                relayResponse,
                transaction,
                txFee,
                gasPrice,
                gasLimit,
                nonce,
                relayHubAddress,
                relayAddress);

            var    tx   = relayResponse.ToTransaction();
            string hash = string.Empty;
            var    ethSendTransaction = new EthSendRawTransaction(_client);

            try
            {
                hash = await ethSendTransaction.SendRequestAsync(tx.GetRLPEncoded().ToHex().EnsureHexPrefix())
                       .ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                if (!ex.Message.Contains("the tx doesn't have the correct nonce") &&
                    !ex.Message.Contains("known transaction") &&
                    !ex.Message.Contains("nonce too low"))
                {
                    throw ex;
                }
            }

            var txHash = relayResponse.Hash;

            if (!string.IsNullOrEmpty(hash) &&
                relayResponse.Hash.EnsureHexPrefix().ToLower() != hash.EnsureHexPrefix().ToLower())
            {
                txHash = hash;
            }

            return(txHash);
        }