Skip to content

Commit

Permalink
app, dapps: Improve UI handling of very small token values.
Browse files Browse the repository at this point in the history
  • Loading branch information
patniemeyer committed Aug 22, 2024
1 parent 56ff964 commit 505d42f
Show file tree
Hide file tree
Showing 42 changed files with 723 additions and 303 deletions.
2 changes: 1 addition & 1 deletion app-shared/engine
Submodule engine updated 4025 files
2 changes: 1 addition & 1 deletion app-shared/flutter
Submodule flutter updated 4500 files
26 changes: 19 additions & 7 deletions gui-orchid/lib/api/orchid_eth/token_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import 'package:flutter_svg/svg.dart';
import 'package:orchid/api/pricing/orchid_pricing.dart';
import '../orchid_crypto.dart';
import 'chains.dart';
import 'package:orchid/util/format_currency.dart' as units;
import 'package:orchid/util/format_decimal.dart' as units;
import 'package:decimal/decimal.dart';

// Token type
// Note: Unfortunately Dart does not have a polyomorphic 'this' type so the
Expand Down Expand Up @@ -47,7 +48,8 @@ class TokenType {
required this.iconPath,
});

// Return 1eN where N is the decimal count.
// Return 1eN where N is the 'decimals' value.
// (Note that Dart int can handle up to 2^53-1 on JS and 2^64-1 elsewhere.)
int get multiplier {
return Math.pow(10, this.decimals).toInt();
}
Expand All @@ -72,6 +74,11 @@ class TokenType {
return fromInt(BigInt.from(val * this.multiplier));
}

// From a number representing the nominal token denomination, e.g. 1.0 OXT
Token fromDecimal(Decimal val) {
return fromInt((val * Decimal.fromInt(this.multiplier)).toBigInt());
}

@override
bool operator ==(Object other) =>
identical(this, other) ||
Expand All @@ -97,10 +104,14 @@ class Token {
Token(this.type,
this.intValue); // The float value in nominal units (e.g. ETH, OXT)

double get floatValue {
double get doubleValue {
return intValue.toDouble() / type.multiplier;
}

Decimal get decimalValue {
return Decimal.fromBigInt(intValue).shift(-type.decimals);
}

/// No token symbol
String toFixedLocalized({
required Locale locale,
Expand All @@ -109,8 +120,8 @@ class Token {
int? minPrecision,
bool showPrecisionIndicator = false,
}) {
return units.formatCurrency(
floatValue,
return units.formatDecimal(
decimalValue,
locale: locale,
minPrecision: minPrecision,
maxPrecision: maxPrecision,
Expand All @@ -128,7 +139,8 @@ class Token {
int? minPrecision,
bool showPrecisionIndicator = false,
}) {
return units.formatCurrency(floatValue,
print("XXX: format decimal value: $decimalValue");
return units.formatDecimal(decimalValue,
locale: locale,
precision: precision,
minPrecision: minPrecision,
Expand Down Expand Up @@ -260,7 +272,7 @@ class Token {

@override
String toString() {
return 'Token{type: ${type.symbol}, floatValue: $floatValue}';
return 'Token{type: ${type.symbol}, doubleValue: $doubleValue}';
}
}

8 changes: 4 additions & 4 deletions gui-orchid/lib/api/orchid_eth/v0/orchid_market_v0.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ class MarketConditionsV0 implements MarketConditions {
// log("eth v0: Fetch market conditions");
// TODO: Add refresh option
var costToRedeem = await getCostToRedeemTicketV0();
var limitedByBalance = balance.floatValue <= (escrow / 2.0).floatValue;
var limitedByBalance = balance.doubleValue <= (escrow / 2.0).doubleValue;
Token maxFaceValue = LotteryPot.maxTicketFaceValueFor(balance, escrow);

// value received as a fraction of ticket face value
double efficiency = maxFaceValue.floatValue == 0
double efficiency = maxFaceValue.doubleValue == 0
? 0
: max(
0,
(maxFaceValue - costToRedeem.oxtCostToRedeem).floatValue /
maxFaceValue.floatValue)
(maxFaceValue - costToRedeem.oxtCostToRedeem).doubleValue /
maxFaceValue.doubleValue)
.toDouble();

return new MarketConditionsV0(
Expand Down
8 changes: 4 additions & 4 deletions gui-orchid/lib/api/orchid_eth/v1/orchid_market_v1.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ class MarketConditionsV1 implements MarketConditions {
// Infer the chain from the balance token type.
Chain chain = balance.type.chain;
var costToRedeem = await getCostToRedeemTicket(chain, refresh: refresh);
var limitedByBalance = balance.floatValue <= (escrow / 2.0).floatValue;
var limitedByBalance = balance.doubleValue <= (escrow / 2.0).doubleValue;
var maxFaceValue = LotteryPot.maxTicketFaceValueFor(balance, escrow);

// value received as a fraction of ticket face value
double efficiency = maxFaceValue.floatValue == 0
double efficiency = maxFaceValue.doubleValue == 0
? 0
: max(
0,
(maxFaceValue - costToRedeem).floatValue /
maxFaceValue.floatValue)
(maxFaceValue - costToRedeem).doubleValue /
maxFaceValue.doubleValue)
.toDouble();

//log("market conditions for: $balance, $escrow, costToRedeem = $costToRedeem, maxFaceValue=$maxFaceValue");
Expand Down
4 changes: 2 additions & 2 deletions gui-orchid/lib/api/pricing/orchid_pricing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ class OrchidPricing {

/// The USD value of the token quantity.
Future<USD> tokenToUSD(Token token) async {
return USD(token.floatValue * await tokenToUsdRate(token.type));
return USD(token.doubleValue * await tokenToUsdRate(token.type));
}

/// Convert value of from token to equivalant USD value in 'to' token type.
Future<Token> tokenToToken(Token fromToken, TokenType toType) async {
return toType.fromDouble(
fromToken.floatValue * await tokenToTokenRate(fromToken.type, toType));
fromToken.doubleValue * await tokenToTokenRate(fromToken.type, toType));
}

/// (toType / fromType): The conversion rate from fromType to toType
Expand Down
2 changes: 1 addition & 1 deletion gui-orchid/lib/api/pricing/orchid_pricing_v0.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class PricingV0 {
if (oxt == null) {
return null;
}
return USD(oxt.floatValue * oxtPriceUSD);
return USD(oxt.doubleValue * oxtPriceUSD);
}

OXT? toOXT(USD? usd) {
Expand Down
6 changes: 3 additions & 3 deletions gui-orchid/lib/api/pricing/usd.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:flutter/widgets.dart';
import 'package:orchid/api/orchid_eth/token_type.dart';
import 'package:orchid/api/orchid_eth/tokens.dart';
import 'package:orchid/util/localization.dart';
import '../../util/format_currency.dart';
import '../../util/format_decimal.dart';

class USD extends ScalarValue<double> {
static const zero = USD(0.0);
Expand Down Expand Up @@ -50,7 +50,7 @@ class USD extends ScalarValue<double> {
USD? price,
bool showSuffix = true,
}) {
return ((price ?? USD.zero) * (tokenAmount ?? Tokens.TOK.zero).floatValue)
return ((price ?? USD.zero) * (tokenAmount ?? Tokens.TOK.zero).doubleValue)
.formatCurrency(
locale: context.locale,
precision: 2,
Expand All @@ -59,7 +59,7 @@ class USD extends ScalarValue<double> {
}
}

final _formatCurrency = formatCurrency;
final _formatCurrency = formatDouble;

class ScalarValue<T extends num> {
final T value;
Expand Down
7 changes: 3 additions & 4 deletions gui-orchid/lib/common/app_buttons_deprecated.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class FlatButtonDeprecated extends StatelessWidget {
@override
Widget build(BuildContext context) {
final ButtonStyle flatButtonStyle = TextButton.styleFrom(
primary: Colors.black87,
foregroundColor: Colors.black87,
minimumSize: Size(88, 36),
padding: padding ?? EdgeInsets.symmetric(horizontal: 16.0),
shape: shape ??
Expand Down Expand Up @@ -61,15 +61,14 @@ class RaisedButtonDeprecated extends StatelessWidget {
@override
Widget build(BuildContext context) {
final ButtonStyle raisedButtonStyle = ElevatedButton.styleFrom(
onPrimary: Colors.black87,
primary: Colors.grey[300],
foregroundColor: Colors.black87,
minimumSize: Size(88, 36),
padding: padding ?? EdgeInsets.symmetric(horizontal: 16),
shape: shape ??
const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(2)),
),
backgroundColor: color,
backgroundColor: color ?? Colors.grey[300],
elevation: elevation,
tapTargetSize: materialTapTargetSize,
disabledBackgroundColor: disabledColor,
Expand Down
32 changes: 25 additions & 7 deletions gui-orchid/lib/orchid/account/account_card.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:orchid/api/orchid_platform.dart';
import 'package:orchid/orchid/orchid.dart';
import 'package:orchid/api/orchid_crypto.dart';
import 'package:orchid/api/orchid_eth/tokens.dart';
Expand All @@ -15,7 +16,7 @@ import 'package:orchid/orchid/orchid_circular_identicon.dart';
import 'package:orchid/orchid/orchid_circular_progress.dart';
import 'package:orchid/orchid/orchid_gradients.dart';
import 'package:orchid/util/timed_builder.dart';
import 'package:orchid/util/format_currency.dart';
import 'package:orchid/util/format_decimal.dart';
import 'package:orchid/api/pricing/usd.dart';
import '../orchid_panel.dart';
import '../../api/orchid_eth/orchid_account_detail.dart';
Expand Down Expand Up @@ -360,7 +361,7 @@ class _AccountCardState extends State<AccountCard>

String? _balanceText() {
return widget.accountDetail == null
? formatCurrency(0.0, locale: context.locale, precision: 2)
? formatDouble(0.0, locale: context.locale, precision: 2)
: (pot?.balance.formatCurrency(locale: context.locale, precision: 2));
}

Expand Down Expand Up @@ -505,15 +506,32 @@ class _AccountCardState extends State<AccountCard>

// display token value and symbol on a row with usd price in a row below
Widget _buildTokenValueTextRow({Token? value, USD? price, Color? textColor}) {
final valueText = ((value ?? (tokenType ?? Tokens.TOK).zero).formatCurrency(
final valueOrZero = value ?? (tokenType ?? Tokens.TOK).zero;

final valueText = valueOrZero.formatCurrency(
locale: context.locale,
minPrecision: 1,
maxPrecision: 5,
maxPrecision: OrchidPlatform.isWeb ? 18 : 4,
showPrecisionIndicator: true,
showSuffix: false,
));
final valueWidget =
Text(valueText).extra_large.withColor(textColor ?? Colors.white);
);

final fullValueText = valueOrZero.formatCurrency(
locale: context.locale,
minPrecision: 1,
maxPrecision: (tokenType ?? Tokens.TOK).decimals,
showSuffix: false,
);

final style =
OrchidText.extra_large.copyWith(color: textColor ?? Colors.white);
final valueWidget = TapToCopyText(
padding: EdgeInsets.zero,
fullValueText,
displayText: valueText,
style: style,
);

return TokenValueWidgetRow(
context: context,
child: valueWidget,
Expand Down
14 changes: 7 additions & 7 deletions gui-orchid/lib/orchid/account/market_stats_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:orchid/api/pricing/orchid_pricing_v0.dart';
import 'package:orchid/common/app_dialogs.dart';
import 'package:orchid/common/link_text.dart';
import 'package:orchid/orchid/orchid.dart';
import 'package:orchid/util/format_currency.dart';
import 'package:orchid/util/format_decimal.dart';

class MarketStatsDialog {
static Future<void> show({
Expand All @@ -29,9 +29,9 @@ class MarketStatsDialog {
if (pricing == null) {
return;
}
var ethPriceText = formatCurrency(1.0 / pricing.ethPriceUSD,
var ethPriceText = formatDouble(1.0 / pricing.ethPriceUSD,
locale: context.locale, suffix: 'USD');
var oxtPriceText = formatCurrency(1.0 / pricing.oxtPriceUSD,
var oxtPriceText = formatDouble(1.0 / pricing.oxtPriceUSD,
locale: context.locale, suffix: 'USD');
tokenPrices = [
Text(s.ethPrice + " " + ethPriceText).body2,
Expand All @@ -41,24 +41,24 @@ class MarketStatsDialog {
var tokenType = account.chain.nativeCurrency;
var tokenPrice = await OrchidPricing().tokenToUsdRate(tokenType);
var priceText =
formatCurrency(tokenPrice, locale: context.locale, suffix: 'USD');
formatDouble(tokenPrice, locale: context.locale, suffix: 'USD');
tokenPrices = [
Text(tokenType.symbol + ' ' + s.price + ': ' + priceText).body2,
];
}

// Show gas prices as "GWEI" regardless of token type.
var gasPriceGwei = gasPrice.multiplyDouble(1e9);
var gasPriceText = formatCurrency(gasPriceGwei.floatValue,
var gasPriceText = formatDouble(gasPriceGwei.doubleValue,
locale: context.locale, suffix: 'GWEI');

String maxFaceValueText =
marketConditions.maxFaceValue.formatCurrency(locale: context.locale);
String costToRedeemText =
marketConditions.costToRedeem.formatCurrency(locale: context.locale);

bool ticketUnderwater = marketConditions.costToRedeem.floatValue >=
marketConditions.maxFaceValue.floatValue;
bool ticketUnderwater = marketConditions.costToRedeem.doubleValue >=
marketConditions.maxFaceValue.doubleValue;

String limitedByText = marketConditions.limitedByBalance
? s.yourMaxTicketValueIsCurrentlyLimitedByYourBalance +
Expand Down
8 changes: 4 additions & 4 deletions gui-orchid/lib/orchid/account_chart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,16 @@ class AccountBalanceChartTicketModel {
int get availableTicketsCurrentMax {
return pot.maxTicketFaceValue.lteZero()
? 0
: (pot.balance.floatValue / pot.maxTicketFaceValue.floatValue).floor();
: (pot.balance.doubleValue / pot.maxTicketFaceValue.doubleValue).floor();
}

// The number of tickets that could be written using the max possible face value
// at the time of the last high-water mark
int get availableTicketsHighWatermarkMax {
return pot.maxTicketFaceValue.floatValue == 0
return pot.maxTicketFaceValue.doubleValue == 0
? 0
: (_lastBalanceHighWatermark.floatValue /
pot.maxTicketFaceValue.floatValue)
: (_lastBalanceHighWatermark.doubleValue /
pot.maxTicketFaceValue.doubleValue)
.floor();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
import 'package:orchid/orchid/field/orchid_labeled_text_field.dart';
import 'package:orchid/orchid/field/value_field_controller.dart';

/// A styled numeric decimal or integer text field.
class OrchidLabeledNumericField extends StatefulWidget {
final String label;
final NumericValueFieldController? controller;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:decimal/decimal.dart';
import 'package:orchid/common/rounded_rect.dart';
import 'package:orchid/orchid/orchid.dart';
import 'package:orchid/api/orchid_eth/token_type.dart';
Expand Down Expand Up @@ -132,18 +133,18 @@ class TypedTokenValueFieldController extends ValueFieldController<Token> {
return type!.zero;
}
try {
var value = double.parse(
var value = Decimal.parse(
// Allow comma as decimal separator for localization
text.replaceAll(',', '.'),
);
return type!.fromDouble(value);
return type!.fromDecimal(value);
} catch (err) {
return null;
}
}

@override
set value(Token? value) {
textController.text = value == null ? '' : value.floatValue.toString();
textController.text = value == null ? '' : value.doubleValue.toString();
}
}
2 changes: 2 additions & 0 deletions gui-orchid/lib/orchid/field/orchid_text_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import 'package:orchid/orchid/orchid.dart';
import 'package:flutter/services.dart';

/// A styled text field with an optional custom trailing component.
/// The text field can be optionally configured as numeric (integer or decimal) with text output.
/// @see OrchidLabledNumericField for a typed numeric field.
class OrchidTextField extends StatelessWidget {
final String? hintText;
final Widget? trailing;
Expand Down
Loading

0 comments on commit 505d42f

Please sign in to comment.