Skip to content

Commit 2a5cc88

Browse files
committed
feat: add support for custom HTTP headers in Tron RPC client
1 parent ce8bcea commit 2a5cc88

7 files changed

Lines changed: 93 additions & 2 deletions

File tree

BTCPayServer.Plugins.USDt/Configuration/Tron/TronUSDtLikeConfigurationItem.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23

34
namespace BTCPayServer.Plugins.USDt.Configuration.Tron;
45

@@ -15,4 +16,10 @@ public record TronUSDtLikeConfigurationItem : USDtPluginConfigurationItem
1516
public required string BlockExplorerLink { get; init; }
1617
public required string[] DefaultRateRules { get; init; }
1718
public required string CurrencyDisplayName { get; init; }
19+
20+
/// <summary>
21+
/// Custom HTTP headers to be sent with every RPC request.
22+
/// Useful for API authentication (e.g., TronGrid API key via TRON-PRO-API-KEY header).
23+
/// </summary>
24+
public Dictionary<string, string>? HttpHeaders { get; init; }
1825
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
using System;
2+
using System.Collections.Generic;
23

34
namespace BTCPayServer.Plugins.USDt.Configuration.Tron;
45

56
public class TronUSDtLikeServerSettings
67
{
78
public Uri? JsonRpcUri { get; init; }
89
public string? SmartContractAddress { get; init; }
10+
11+
/// <summary>
12+
/// Custom HTTP headers to be sent with every RPC request.
13+
/// Useful for API authentication (e.g., TronGrid API key via TRON-PRO-API-KEY header).
14+
/// </summary>
15+
public Dictionary<string, string>? HttpHeaders { get; init; }
916
}

BTCPayServer.Plugins.USDt/Controllers/UITronUSDtLikeServerController.cs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Threading.Tasks;
45
using BTCPayServer.Abstractions.Constants;
@@ -45,6 +46,7 @@ public async Task<IActionResult> GetServerConfig()
4546

4647
SmartContractAddress = serverSettings?.SmartContractAddress,
4748
JsonRpcUri = serverSettings?.JsonRpcUri?.AbsoluteUri,
49+
HttpHeaders = HttpHeadersDictionaryToString(serverSettings?.HttpHeaders),
4850
};
4951
return View(viewModel);
5052
}
@@ -69,7 +71,8 @@ public async Task<IActionResult> GetServerConfig(TronUSDtLikeServerConfigViewMod
6971
var serverSettings = new TronUSDtLikeServerSettings()
7072
{
7173
SmartContractAddress = viewModel.SmartContractAddress is null or "" ? null : viewModel.SmartContractAddress,
72-
JsonRpcUri = viewModel.JsonRpcUri is null or "" ? null : new Uri(viewModel.JsonRpcUri)
74+
JsonRpcUri = viewModel.JsonRpcUri is null or "" ? null : new Uri(viewModel.JsonRpcUri),
75+
HttpHeaders = ParseHttpHeadersString(viewModel.HttpHeaders)
7376
};
7477

7578
await settingsRepository.UpdateSetting(serverSettings, USDtPlugin.ServerSettingsKey(currentConfiguration));
@@ -81,4 +84,43 @@ public async Task<IActionResult> GetServerConfig(TronUSDtLikeServerConfigViewMod
8184

8285
return RedirectToAction("GetServerConfig");
8386
}
87+
88+
/// <summary>
89+
/// Parses a newline-separated string of "Header-Name: Value" pairs into a dictionary.
90+
/// </summary>
91+
private static Dictionary<string, string>? ParseHttpHeadersString(string? headersString)
92+
{
93+
if (string.IsNullOrWhiteSpace(headersString))
94+
return null;
95+
96+
var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
97+
var lines = headersString.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
98+
99+
foreach (var line in lines)
100+
{
101+
var colonIndex = line.IndexOf(':');
102+
if (colonIndex <= 0) continue;
103+
104+
var headerName = line[..colonIndex].Trim();
105+
var headerValue = line[(colonIndex + 1)..].Trim();
106+
107+
if (!string.IsNullOrEmpty(headerName) && !string.IsNullOrEmpty(headerValue))
108+
{
109+
headers[headerName] = headerValue;
110+
}
111+
}
112+
113+
return headers.Count > 0 ? headers : null;
114+
}
115+
116+
/// <summary>
117+
/// Converts a dictionary of headers to a newline-separated string of "Header-Name: Value" pairs.
118+
/// </summary>
119+
private static string? HttpHeadersDictionaryToString(Dictionary<string, string>? headers)
120+
{
121+
if (headers == null || headers.Count == 0)
122+
return null;
123+
124+
return string.Join(Environment.NewLine, headers.Select(h => $"{h.Key}: {h.Value}"));
125+
}
84126
}

BTCPayServer.Plugins.USDt/Controllers/ViewModels/TronUSDtLikeServerConfigViewModel.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,10 @@ public class TronUSDtLikeServerConfigViewModel
1414
[Uri]
1515
public string? JsonRpcUri { get; init; }
1616
public Uri? DefaultJsonRpcUri { get; set; }
17+
18+
/// <summary>
19+
/// Custom HTTP headers as a newline-separated list of "Header-Name: Header-Value" pairs.
20+
/// Example: "TRON-PRO-API-KEY: your-api-key-here"
21+
/// </summary>
22+
public string? HttpHeaders { get; set; }
1723
}

BTCPayServer.Plugins.USDt/Services/TronUSDtRPCProvider.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ private void LoadClientsFromConfiguration()
5353
pair =>
5454
{
5555
var httpClient = _httpClientFactory.CreateClient($"{pair.Key}client");
56+
57+
// Add custom HTTP headers if configured (e.g., for TronGrid API key)
58+
if (pair.Value.HttpHeaders != null)
59+
{
60+
foreach (var header in pair.Value.HttpHeaders)
61+
{
62+
httpClient.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value);
63+
}
64+
}
65+
5666
var rpcClient = new RpcClient(pair.Value.JsonRpcUri, httpClient);
5767
return rpcClient;
5868
});

BTCPayServer.Plugins.USDt/USDtPlugin.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ public static TronUSDtLikeConfigurationItem OverrideWithServerSettings(TronUSDtL
167167
return config with
168168
{
169169
JsonRpcUri = serverSettings.JsonRpcUri ?? config.JsonRpcUri,
170-
SmartContractAddress = serverSettings.SmartContractAddress ?? config.SmartContractAddress
170+
SmartContractAddress = serverSettings.SmartContractAddress ?? config.SmartContractAddress,
171+
HttpHeaders = serverSettings.HttpHeaders ?? config.HttpHeaders
171172
};
172173
}
173174

BTCPayServer.Plugins.USDt/Views/UITronUSDtLikeServer/GetServerConfig.cshtml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,24 @@
4646
</small> of the node you wish to use for the connection with the TRON chain. By default, it uses the public node.
4747
</div>
4848
</div>
49+
<div class="form-group">
50+
<div class="d-flex flex-wrap align-items-center justify-content-between gap-3">
51+
<label asp-for="HttpHeaders" class="form-label">HTTP Headers</label>
52+
</div>
53+
<textarea asp-for="HttpHeaders" class="form-control" rows="3" placeholder="TRON-PRO-API-KEY: your-api-key-here"></textarea>
54+
<span asp-validation-for="HttpHeaders" class="text-danger"></span>
55+
<div class="form-text">
56+
Optional custom HTTP headers to send with every RPC request (one per line, format: <code>Header-Name: value</code>).
57+
<br/>
58+
For TronGrid API access, add your API key:
59+
<small>
60+
<a href="https://developers.tron.network/reference/select-network" target="_blank" rel="noreferrer noopener">
61+
<vc:icon symbol="info"/>
62+
</a>
63+
</small>
64+
<code>TRON-PRO-API-KEY: your-api-key-here</code>
65+
</div>
66+
</div>
4967
<h4>Advanced</h4>
5068
<div class="alert alert-warning d-flex align-items-center" role="alert">
5169
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Warning:">

0 commit comments

Comments
 (0)