feat(ui): treat transparent addresses as mining-only addresses
DragonX is a private chain (-ac_private=1): consensus rejects any
non-coinbase output to a transparent address, so a t-address can only
ever receive mining rewards, and its coins can only move to a shielded
address. The wallet treated them like ordinary recipients.
Block paying one:
- Send: a transparent recipient shows "Mining address - can't be paid"
and disables Review Send (tooltip explains; ask for a zs... address).
Typing, paste, contacts and payment URIs all go through this field.
- Overview drag-to-transfer: a mining-address row is never a transfer
target (it only reorders).
- Pool payout must be shielded (pools pay with ordinary transactions):
field tooltip, disabled Start button and the start check say why.
- isValidRecipientAddress() is now shielded-only; new isDragonXAddress()
and classifyRecipient() (Shielded / MiningOnly / Invalid).
Call them mining addresses:
- Overview: "Mining rewards" card (amber "Not yet shielded" when there
is a balance; hover explains; click opens the Shield dialog), "New
mining address" button with a tooltip, rows typed "Mining".
- QR / label dialogs, portfolio filter, contact badge ("Mining only"),
Receive notifications.
i18n: 17 new keys in all 8 languages, 12 dead keys removed, CJK subset
rebuilt. Tests: recipient classification, translation completeness.
UI sweep: new "send-mining-recipient" target.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -693,6 +693,14 @@ static void RenderTxProgress(ImDrawList* dl, float x, float y, float w,
|
||||
// ============================================================================
|
||||
// Confirmation dialog
|
||||
// ============================================================================
|
||||
void SweepSetSendRecipient(App* app, const char* to) {
|
||||
(void)app;
|
||||
std::snprintf(s_from_address, sizeof(s_from_address), "%s",
|
||||
to ? "zs1demoprimaryshieldedaddressforuisweep000000000000000000000000000" : "");
|
||||
std::snprintf(s_to_address, sizeof(s_to_address), "%s", to ? to : "");
|
||||
s_amount = to ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
void SweepShowSendConfirm(App* app, bool show) {
|
||||
(void)app;
|
||||
if (show) {
|
||||
@@ -975,7 +983,7 @@ static bool RenderZeroBalanceCTA(App* app, ImDrawList* dl, float width) {
|
||||
// Action buttons (Send + Clear + Undo)
|
||||
// ============================================================================
|
||||
static void RenderActionButtons(App* app, float width, float vScale,
|
||||
bool is_valid_address, double available,
|
||||
bool is_valid_address, bool is_mining_addr, double available,
|
||||
const char* suffix = "") {
|
||||
auto& S = schema::UI();
|
||||
const float dp = Layout::dpiScale();
|
||||
@@ -1027,6 +1035,8 @@ static void RenderActionButtons(App* app, float width, float vScale,
|
||||
material::Tooltip("%s", TR("send_tooltip_syncing"));
|
||||
else if (s_from_address[0] == '\0')
|
||||
material::Tooltip("%s", TR("send_tooltip_select_source"));
|
||||
else if (is_mining_addr)
|
||||
material::Tooltip("%s", TR("send_mining_addr_blocked"));
|
||||
else if (!is_valid_address)
|
||||
material::Tooltip("%s", TR("send_tooltip_invalid_address"));
|
||||
else if (s_amount <= 0)
|
||||
@@ -1255,8 +1265,10 @@ void RenderSendTab(App* app)
|
||||
|
||||
double available = GetAvailableBalance(app);
|
||||
bool is_valid_z = IsValidShieldedAddr(s_to_address);
|
||||
// A transparent address is a mining address: it only ever receives mining rewards, and the network
|
||||
// rejects a payment to it (DragonX is a private chain). Recognise it, but never send to it.
|
||||
bool is_valid_t = IsValidTransparentAddr(s_to_address);
|
||||
bool is_valid_address = is_valid_z || is_valid_t;
|
||||
bool is_valid_address = is_valid_z;
|
||||
float sectionGap = Layout::spacingXl() * vScale;
|
||||
|
||||
char buf[128];
|
||||
@@ -1365,7 +1377,9 @@ void RenderSendTab(App* app)
|
||||
if (vz)
|
||||
Type().textColored(TypeStyle::Caption, Success(), TR("send_valid_shielded"));
|
||||
else
|
||||
Type().textColored(TypeStyle::Caption, Warning(), TR("send_valid_transparent"));
|
||||
Type().textColored(TypeStyle::Caption, Error(), TR("send_mining_addr_short"));
|
||||
if (vt && ImGui::IsItemHovered())
|
||||
material::Tooltip("%s", TR("send_mining_addr_blocked"));
|
||||
} else if (!checkPreview) {
|
||||
ImGui::SameLine();
|
||||
Type().textColored(TypeStyle::Caption, Error(), TR("invalid_address"));
|
||||
@@ -1405,7 +1419,8 @@ void RenderSendTab(App* app)
|
||||
trimmed.erase(trimmed.begin());
|
||||
while (!trimmed.empty() && (trimmed.back() == ' ' || trimmed.back() == '\n' || trimmed.back() == '\r' || trimmed.back() == '\t'))
|
||||
trimmed.pop_back();
|
||||
bool looksValid = dragonx::util::isValidRecipientAddress(trimmed);
|
||||
// Preview any address, a mining one too: once pasted, the field explains why it can't be paid.
|
||||
bool looksValid = dragonx::util::isDragonXAddress(trimmed);
|
||||
if (looksValid && s_to_address[0] == '\0') {
|
||||
s_preview_text = trimmed;
|
||||
s_paste_previewing = true;
|
||||
@@ -1626,7 +1641,7 @@ void RenderSendTab(App* app)
|
||||
ImGui::Dummy(ImVec2(0, innerGap * 0.5f));
|
||||
|
||||
// ---- ACTION BUTTONS (full width) ----
|
||||
RenderActionButtons(app, innerW, vScale, is_valid_address, available, "##main");
|
||||
RenderActionButtons(app, innerW, vScale, is_valid_address, is_valid_t, available, "##main");
|
||||
|
||||
// Add bottom padding
|
||||
ImGui::Dummy(ImVec2(0, pad * vScale));
|
||||
|
||||
Reference in New Issue
Block a user