Skip to content

Commit 16a03ee

Browse files
committed
Support for Tonal Bitcoin units (ᵇTBC, ˢTBC, and TBC)
Only available on dropdowns when a Tonal-compatible font is installed
1 parent bbbf89a commit 16a03ee

File tree

6 files changed

+236
-22
lines changed

6 files changed

+236
-22
lines changed

src/Makefile.qt.include

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ BITCOIN_QT_H = \
152152
qt/sendcoinsrecipient.h \
153153
qt/signverifymessagedialog.h \
154154
qt/splashscreen.h \
155+
qt/tonalutils.h \
155156
qt/trafficgraphwidget.h \
156157
qt/transactiondesc.h \
157158
qt/transactiondescdialog.h \
@@ -243,6 +244,7 @@ BITCOIN_QT_BASE_CPP = \
243244
qt/qvaluecombobox.cpp \
244245
qt/rpcconsole.cpp \
245246
qt/splashscreen.cpp \
247+
qt/tonalutils.cpp \
246248
qt/trafficgraphwidget.cpp \
247249
qt/utilitydialog.cpp
248250

src/qt/bitcoinamountfield.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ class AmountSpinBox: public QAbstractSpinBox
9494
{
9595
bool valid = false;
9696
CAmount val = value(&valid);
97-
val = val + steps * singleStep;
97+
CAmount currentSingleStep = singleStep;
98+
if (!currentSingleStep) {
99+
currentSingleStep = BitcoinUnits::singlestep(currentUnit);
100+
}
101+
val = val + steps * currentSingleStep;
98102
val = qBound(m_min_amount, val, m_max_amount);
99103
setValue(val);
100104
}
@@ -151,7 +155,7 @@ class AmountSpinBox: public QAbstractSpinBox
151155

152156
private:
153157
BitcoinUnit currentUnit{BitcoinUnit::BTC};
154-
CAmount singleStep{CAmount(100000)}; // satoshis
158+
CAmount singleStep{CAmount(0)};
155159
mutable QSize cachedMinimumSizeHint;
156160
bool m_allow_empty{true};
157161
CAmount m_min_amount{CAmount(0)};

src/qt/bitcoinunits.cpp

Lines changed: 134 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include <qt/bitcoinunits.h>
6+
#include <qt/tonalutils.h>
67

78
#include <consensus/amount.h>
89

@@ -11,6 +12,7 @@
1112
#include <cassert>
1213

1314
static constexpr auto MAX_DIGITS_BTC = 16;
15+
static constexpr auto MAX_DIGITS_TBC = 13;
1416

1517
BitcoinUnits::BitcoinUnits(QObject *parent):
1618
QAbstractListModel(parent),
@@ -20,11 +22,17 @@ BitcoinUnits::BitcoinUnits(QObject *parent):
2022

2123
QList<BitcoinUnit> BitcoinUnits::availableUnits()
2224
{
23-
QList<BitcoinUnit> unitlist;
25+
static QList<BitcoinUnit> unitlist;
2426
unitlist.append(Unit::BTC);
2527
unitlist.append(Unit::mBTC);
2628
unitlist.append(Unit::uBTC);
2729
unitlist.append(Unit::SAT);
30+
if (TonalUtils::Supported())
31+
{
32+
unitlist.append(Unit::bTBC);
33+
unitlist.append(Unit::sTBC);
34+
unitlist.append(Unit::TBC);
35+
}
2836
return unitlist;
2937
}
3038

@@ -35,6 +43,9 @@ QString BitcoinUnits::longName(Unit unit)
3543
case Unit::mBTC: return QString("mBTC");
3644
case Unit::uBTC: return QString::fromUtf8("µBTC (bits)");
3745
case Unit::SAT: return QString("Satoshi (sat)");
46+
case Unit::bTBC: return QString::fromUtf8("ᵇTBC");
47+
case Unit::sTBC: return QString::fromUtf8("ˢTBC");
48+
case Unit::TBC: return QString("TBC");
3849
} // no default case, so the compiler can warn about missing cases
3950
assert(false);
4051
}
@@ -46,17 +57,23 @@ QString BitcoinUnits::shortName(Unit unit)
4657
case Unit::mBTC: return longName(unit);
4758
case Unit::uBTC: return QString("bits");
4859
case Unit::SAT: return QString("sat");
60+
case Unit::bTBC: return QString::fromUtf8("ᵇTBC");
61+
case Unit::sTBC: return QString::fromUtf8("ˢTBC");
62+
case Unit::TBC: return QString("TBC");
4963
} // no default case, so the compiler can warn about missing cases
5064
assert(false);
5165
}
5266

5367
QString BitcoinUnits::description(Unit unit)
5468
{
5569
switch (unit) {
56-
case Unit::BTC: return QString("Bitcoins");
70+
case Unit::BTC: return QString("Bitcoins (decimal)");
5771
case Unit::mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)");
5872
case Unit::uBTC: return QString("Micro-Bitcoins (bits) (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
5973
case Unit::SAT: return QString("Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
74+
case Unit::bTBC: return QString("Bong-Bitcoins (1,0000 tonal)");
75+
case Unit::sTBC: return QString("San-Bitcoins (100 tonal)");
76+
case Unit::TBC: return QString("Bitcoins (tonal)");
6077
} // no default case, so the compiler can warn about missing cases
6178
assert(false);
6279
}
@@ -68,6 +85,9 @@ qint64 BitcoinUnits::factor(Unit unit)
6885
case Unit::mBTC: return 100'000;
6986
case Unit::uBTC: return 100;
7087
case Unit::SAT: return 1;
88+
case Unit::bTBC: return 0x100000000LL;
89+
case Unit::sTBC: return 0x1000000;
90+
case Unit::TBC: return 0x10000;
7191
} // no default case, so the compiler can warn about missing cases
7292
assert(false);
7393
}
@@ -79,10 +99,57 @@ int BitcoinUnits::decimals(Unit unit)
7999
case Unit::mBTC: return 5;
80100
case Unit::uBTC: return 2;
81101
case Unit::SAT: return 0;
102+
case Unit::bTBC: return 8;
103+
case Unit::sTBC: return 6;
104+
case Unit::TBC: return 4;
82105
} // no default case, so the compiler can warn about missing cases
83106
assert(false);
84107
}
85108

109+
int BitcoinUnits::radix(Unit unit)
110+
{
111+
switch (unit) {
112+
case Unit::bTBC:
113+
case Unit::sTBC:
114+
case Unit::TBC:
115+
return 0x10;
116+
default:
117+
return 10;
118+
}
119+
}
120+
121+
BitcoinUnit BitcoinUnits::numsys(Unit unit)
122+
{
123+
switch (unit) {
124+
case Unit::bTBC:
125+
case Unit::sTBC:
126+
case Unit::TBC:
127+
return Unit::TBC;
128+
default:
129+
return Unit::BTC;
130+
}
131+
}
132+
133+
qint64 BitcoinUnits::max_digits(Unit unit)
134+
{
135+
switch (numsys(unit)) {
136+
case Unit::TBC:
137+
return MAX_DIGITS_TBC;
138+
default:
139+
return MAX_DIGITS_BTC;
140+
}
141+
}
142+
143+
qint64 BitcoinUnits::singlestep(Unit unit)
144+
{
145+
switch (numsys(unit)) {
146+
case Unit::TBC:
147+
return 0x10000;
148+
default:
149+
return 100000;
150+
}
151+
}
152+
86153
QString BitcoinUnits::format(Unit unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators, bool justify)
87154
{
88155
// Note: not using straight sprintf here because we do NOT want
@@ -92,11 +159,22 @@ QString BitcoinUnits::format(Unit unit, const CAmount& nIn, bool fPlus, Separato
92159
int num_decimals = decimals(unit);
93160
qint64 n_abs = (n > 0 ? n : -n);
94161
qint64 quotient = n_abs / coin;
95-
QString quotient_str = QString::number(quotient);
162+
int uradix = radix(unit);
163+
QString quotient_str = QString::number(quotient, uradix);
96164
if (justify) {
97-
quotient_str = quotient_str.rightJustified(MAX_DIGITS_BTC - num_decimals, ' ');
165+
quotient_str = quotient_str.rightJustified(max_digits(unit) - num_decimals, ' ');
98166
}
99167

168+
QString remainder_str;
169+
if (num_decimals > 0) {
170+
const qint64 remainder = n_abs % coin;
171+
remainder_str = QString::number(remainder, uradix).rightJustified(num_decimals, '0');
172+
}
173+
174+
switch (numsys(unit)) {
175+
case Unit::BTC:
176+
{
177+
100178
// Use SI-style thin space separators as these are locale independent and can't be
101179
// confused with the decimal marker.
102180
QChar thin_sp(THIN_SP_CP);
@@ -105,18 +183,28 @@ QString BitcoinUnits::format(Unit unit, const CAmount& nIn, bool fPlus, Separato
105183
for (int i = 3; i < q_size; i += 3)
106184
quotient_str.insert(q_size - i, thin_sp);
107185

186+
break;
187+
}
188+
case Unit::TBC:
189+
{
190+
// Right-trim excess zeros after the decimal point
191+
static const QRegExp tail_zeros("0+$");
192+
remainder_str.remove(tail_zeros);
193+
TonalUtils::ConvertFromHex(quotient_str);
194+
TonalUtils::ConvertFromHex(remainder_str);
195+
break;
196+
}
197+
default: assert(false);
198+
}
199+
108200
if (n < 0)
109201
quotient_str.insert(0, '-');
110202
else if (fPlus && n > 0)
111203
quotient_str.insert(0, '+');
112204

113-
if (num_decimals > 0) {
114-
qint64 remainder = n_abs % coin;
115-
QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0');
116-
return quotient_str + QString(".") + remainder_str;
117-
} else {
118-
return quotient_str;
119-
}
205+
if (!remainder_str.isEmpty())
206+
quotient_str += QString(".") + remainder_str;
207+
return quotient_str;
120208
}
121209

122210

@@ -180,11 +268,18 @@ bool BitcoinUnits::parse(Unit unit, const QString& value, CAmount* val_out)
180268
bool ok = false;
181269
QString str = whole + decimals.leftJustified(num_decimals, '0');
182270

271+
Unit unumsys = numsys(unit);
272+
if (unumsys == Unit::TBC) {
273+
if (str.size() > 15)
274+
return false; // Longer numbers may exceed 63 bits
275+
TonalUtils::ConvertToHex(str);
276+
} else
183277
if(str.size() > 18)
184278
{
185279
return false; // Longer numbers will exceed 63 bits
186280
}
187-
CAmount retvalue(str.toLongLong(&ok));
281+
282+
CAmount retvalue(str.toLongLong(&ok, radix(unit)));
188283
if(val_out)
189284
{
190285
*val_out = retvalue;
@@ -229,13 +324,16 @@ CAmount BitcoinUnits::maxMoney()
229324
}
230325

231326
namespace {
232-
qint8 ToQint8(BitcoinUnit unit)
327+
std::variant<qint8, QString> ToSetting(BitcoinUnit unit)
233328
{
234329
switch (unit) {
235-
case BitcoinUnit::BTC: return 0;
236-
case BitcoinUnit::mBTC: return 1;
237-
case BitcoinUnit::uBTC: return 2;
238-
case BitcoinUnit::SAT: return 3;
330+
case BitcoinUnit::BTC: return qint8{0};
331+
case BitcoinUnit::mBTC: return qint8{1};
332+
case BitcoinUnit::uBTC: return qint8{2};
333+
case BitcoinUnit::SAT: return qint8{3};
334+
case BitcoinUnit::bTBC: return QString("bTBC");
335+
case BitcoinUnit::sTBC: return QString("sTBC");
336+
case BitcoinUnit::TBC: return QString("TBC");
239337
} // no default case, so the compiler can warn about missing cases
240338
assert(false);
241339
}
@@ -250,17 +348,35 @@ BitcoinUnit FromQint8(qint8 num)
250348
}
251349
assert(false);
252350
}
351+
352+
BitcoinUnit FromSetting(const QString& s, BitcoinUnit def)
353+
{
354+
if (s == "bTBC") return BitcoinUnit::bTBC;
355+
if (s == "sTBC") return BitcoinUnit::sTBC;
356+
if (s == "TBC") return BitcoinUnit::TBC;
357+
return def;
358+
}
253359
} // namespace
254360

255361
QDataStream& operator<<(QDataStream& out, const BitcoinUnit& unit)
256362
{
257-
return out << ToQint8(unit);
363+
auto setting_val = ToSetting(unit);
364+
if (const QString* setting_str = std::get_if<QString>(&setting_val)) {
365+
return out << qint8{0} << *setting_str;
366+
} else {
367+
return out << std::get<qint8>(setting_val);
368+
}
258369
}
259370

260371
QDataStream& operator>>(QDataStream& in, BitcoinUnit& unit)
261372
{
262373
qint8 input;
263374
in >> input;
264375
unit = FromQint8(input);
376+
if (!in.atEnd()) {
377+
QString setting_str;
378+
in >> setting_str;
379+
unit = FromSetting(setting_str, unit);
380+
}
265381
return in;
266382
}

src/qt/bitcoinunits.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ class BitcoinUnits: public QAbstractListModel
4343
BTC,
4444
mBTC,
4545
uBTC,
46-
SAT
46+
SAT,
47+
bTBC,
48+
sTBC,
49+
TBC,
4750
};
4851
Q_ENUM(Unit)
4952

@@ -68,8 +71,16 @@ class BitcoinUnits: public QAbstractListModel
6871
static QString description(Unit unit);
6972
//! Number of Satoshis (1e-8) per unit
7073
static qint64 factor(Unit unit);
71-
//! Number of decimals left
74+
//! Number of fractional places
7275
static int decimals(Unit unit);
76+
//! Radix
77+
static int radix(Unit unit);
78+
//! Number system
79+
static Unit numsys(Unit unit);
80+
//! Number of digits total in maximum value
81+
static qint64 max_digits(Unit unit);
82+
//! "Single step" amount, in satoshis
83+
static qint64 singlestep(Unit unit);
7384
//! Format as string
7485
static QString format(Unit unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false);
7586
//! Format as string (with unit)

0 commit comments

Comments
 (0)