Merge remote-tracking branch 'origin/staging' into fusion

This commit is contained in:
sneurlax 2023-07-27 12:03:55 -05:00
commit 9b4c1abf35
29 changed files with 1916 additions and 676 deletions

Binary file not shown.

Binary file not shown.

@ -1 +1 @@
Subproject commit 407425c9fcf7a30c81f1345246c7225bc18b5cd5 Subproject commit e48952185556a10f182184fd572bcb04365f5831

View file

@ -45,7 +45,7 @@ class StackTheme {
case "dark": case "dark":
return Brightness.dark; return Brightness.dark;
default: default:
// just return light instead of a possible crash causing error // just return light instead of a possible crash causing error
return Brightness.light; return Brightness.light;
} }
} }
@ -131,8 +131,8 @@ class StackTheme {
@ignore @ignore
Color get accentColorBlue => _accentColorBlue ??= Color( Color get accentColorBlue => _accentColorBlue ??= Color(
accentColorBlueInt, accentColorBlueInt,
); );
@ignore @ignore
Color? _accentColorBlue; Color? _accentColorBlue;
late final int accentColorBlueInt; late final int accentColorBlueInt;
@ -141,8 +141,8 @@ class StackTheme {
@ignore @ignore
Color get accentColorGreen => _accentColorGreen ??= Color( Color get accentColorGreen => _accentColorGreen ??= Color(
accentColorGreenInt, accentColorGreenInt,
); );
@ignore @ignore
Color? _accentColorGreen; Color? _accentColorGreen;
late final int accentColorGreenInt; late final int accentColorGreenInt;
@ -151,8 +151,8 @@ class StackTheme {
@ignore @ignore
Color get accentColorYellow => _accentColorYellow ??= Color( Color get accentColorYellow => _accentColorYellow ??= Color(
accentColorYellowInt, accentColorYellowInt,
); );
@ignore @ignore
Color? _accentColorYellow; Color? _accentColorYellow;
late final int accentColorYellowInt; late final int accentColorYellowInt;
@ -161,8 +161,8 @@ class StackTheme {
@ignore @ignore
Color get accentColorRed => _accentColorRed ??= Color( Color get accentColorRed => _accentColorRed ??= Color(
accentColorRedInt, accentColorRedInt,
); );
@ignore @ignore
Color? _accentColorRed; Color? _accentColorRed;
late final int accentColorRedInt; late final int accentColorRedInt;
@ -171,8 +171,8 @@ class StackTheme {
@ignore @ignore
Color get accentColorOrange => _accentColorOrange ??= Color( Color get accentColorOrange => _accentColorOrange ??= Color(
accentColorOrangeInt, accentColorOrangeInt,
); );
@ignore @ignore
Color? _accentColorOrange; Color? _accentColorOrange;
late final int accentColorOrangeInt; late final int accentColorOrangeInt;
@ -181,8 +181,8 @@ class StackTheme {
@ignore @ignore
Color get accentColorDark => _accentColorDark ??= Color( Color get accentColorDark => _accentColorDark ??= Color(
accentColorDarkInt, accentColorDarkInt,
); );
@ignore @ignore
Color? _accentColorDark; Color? _accentColorDark;
late final int accentColorDarkInt; late final int accentColorDarkInt;
@ -191,8 +191,8 @@ class StackTheme {
@ignore @ignore
Color get shadow => _shadow ??= Color( Color get shadow => _shadow ??= Color(
shadowInt, shadowInt,
); );
@ignore @ignore
Color? _shadow; Color? _shadow;
late final int shadowInt; late final int shadowInt;
@ -201,8 +201,8 @@ class StackTheme {
@ignore @ignore
Color get textDark => _textDark ??= Color( Color get textDark => _textDark ??= Color(
textDarkInt, textDarkInt,
); );
@ignore @ignore
Color? _textDark; Color? _textDark;
late final int textDarkInt; late final int textDarkInt;
@ -211,8 +211,8 @@ class StackTheme {
@ignore @ignore
Color get textDark2 => _textDark2 ??= Color( Color get textDark2 => _textDark2 ??= Color(
textDark2Int, textDark2Int,
); );
@ignore @ignore
Color? _textDark2; Color? _textDark2;
late final int textDark2Int; late final int textDark2Int;
@ -221,8 +221,8 @@ class StackTheme {
@ignore @ignore
Color get textDark3 => _textDark3 ??= Color( Color get textDark3 => _textDark3 ??= Color(
textDark3Int, textDark3Int,
); );
@ignore @ignore
Color? _textDark3; Color? _textDark3;
late final int textDark3Int; late final int textDark3Int;
@ -231,8 +231,8 @@ class StackTheme {
@ignore @ignore
Color get textSubtitle1 => _textSubtitle1 ??= Color( Color get textSubtitle1 => _textSubtitle1 ??= Color(
textSubtitle1Int, textSubtitle1Int,
); );
@ignore @ignore
Color? _textSubtitle1; Color? _textSubtitle1;
late final int textSubtitle1Int; late final int textSubtitle1Int;
@ -241,8 +241,8 @@ class StackTheme {
@ignore @ignore
Color get textSubtitle2 => _textSubtitle2 ??= Color( Color get textSubtitle2 => _textSubtitle2 ??= Color(
textSubtitle2Int, textSubtitle2Int,
); );
@ignore @ignore
Color? _textSubtitle2; Color? _textSubtitle2;
late final int textSubtitle2Int; late final int textSubtitle2Int;
@ -251,8 +251,8 @@ class StackTheme {
@ignore @ignore
Color get textSubtitle3 => _textSubtitle3 ??= Color( Color get textSubtitle3 => _textSubtitle3 ??= Color(
textSubtitle3Int, textSubtitle3Int,
); );
@ignore @ignore
Color? _textSubtitle3; Color? _textSubtitle3;
late final int textSubtitle3Int; late final int textSubtitle3Int;
@ -261,8 +261,8 @@ class StackTheme {
@ignore @ignore
Color get textSubtitle4 => _textSubtitle4 ??= Color( Color get textSubtitle4 => _textSubtitle4 ??= Color(
textSubtitle4Int, textSubtitle4Int,
); );
@ignore @ignore
Color? _textSubtitle4; Color? _textSubtitle4;
late final int textSubtitle4Int; late final int textSubtitle4Int;
@ -271,8 +271,8 @@ class StackTheme {
@ignore @ignore
Color get textSubtitle5 => _textSubtitle5 ??= Color( Color get textSubtitle5 => _textSubtitle5 ??= Color(
textSubtitle5Int, textSubtitle5Int,
); );
@ignore @ignore
Color? _textSubtitle5; Color? _textSubtitle5;
late final int textSubtitle5Int; late final int textSubtitle5Int;
@ -281,8 +281,8 @@ class StackTheme {
@ignore @ignore
Color get textSubtitle6 => _textSubtitle6 ??= Color( Color get textSubtitle6 => _textSubtitle6 ??= Color(
textSubtitle6Int, textSubtitle6Int,
); );
@ignore @ignore
Color? _textSubtitle6; Color? _textSubtitle6;
late final int textSubtitle6Int; late final int textSubtitle6Int;
@ -291,8 +291,8 @@ class StackTheme {
@ignore @ignore
Color get textWhite => _textWhite ??= Color( Color get textWhite => _textWhite ??= Color(
textWhiteInt, textWhiteInt,
); );
@ignore @ignore
Color? _textWhite; Color? _textWhite;
late final int textWhiteInt; late final int textWhiteInt;
@ -301,8 +301,8 @@ class StackTheme {
@ignore @ignore
Color get textFavoriteCard => _textFavoriteCard ??= Color( Color get textFavoriteCard => _textFavoriteCard ??= Color(
textFavoriteCardInt, textFavoriteCardInt,
); );
@ignore @ignore
Color? _textFavoriteCard; Color? _textFavoriteCard;
late final int textFavoriteCardInt; late final int textFavoriteCardInt;
@ -311,8 +311,8 @@ class StackTheme {
@ignore @ignore
Color get textError => _textError ??= Color( Color get textError => _textError ??= Color(
textErrorInt, textErrorInt,
); );
@ignore @ignore
Color? _textError; Color? _textError;
late final int textErrorInt; late final int textErrorInt;
@ -321,8 +321,8 @@ class StackTheme {
@ignore @ignore
Color get textRestore => _textRestore ??= Color( Color get textRestore => _textRestore ??= Color(
textRestoreInt, textRestoreInt,
); );
@ignore @ignore
Color? _textRestore; Color? _textRestore;
late final int textRestoreInt; late final int textRestoreInt;
@ -331,8 +331,8 @@ class StackTheme {
@ignore @ignore
Color get buttonBackPrimary => _buttonBackPrimary ??= Color( Color get buttonBackPrimary => _buttonBackPrimary ??= Color(
buttonBackPrimaryInt, buttonBackPrimaryInt,
); );
@ignore @ignore
Color? _buttonBackPrimary; Color? _buttonBackPrimary;
late final int buttonBackPrimaryInt; late final int buttonBackPrimaryInt;
@ -341,8 +341,8 @@ class StackTheme {
@ignore @ignore
Color get buttonBackSecondary => _buttonBackSecondary ??= Color( Color get buttonBackSecondary => _buttonBackSecondary ??= Color(
buttonBackSecondaryInt, buttonBackSecondaryInt,
); );
@ignore @ignore
Color? _buttonBackSecondary; Color? _buttonBackSecondary;
late final int buttonBackSecondaryInt; late final int buttonBackSecondaryInt;
@ -351,8 +351,8 @@ class StackTheme {
@ignore @ignore
Color get buttonBackPrimaryDisabled => _buttonBackPrimaryDisabled ??= Color( Color get buttonBackPrimaryDisabled => _buttonBackPrimaryDisabled ??= Color(
buttonBackPrimaryDisabledInt, buttonBackPrimaryDisabledInt,
); );
@ignore @ignore
Color? _buttonBackPrimaryDisabled; Color? _buttonBackPrimaryDisabled;
late final int buttonBackPrimaryDisabledInt; late final int buttonBackPrimaryDisabledInt;
@ -372,8 +372,8 @@ class StackTheme {
@ignore @ignore
Color get buttonBackBorder => _buttonBackBorder ??= Color( Color get buttonBackBorder => _buttonBackBorder ??= Color(
buttonBackBorderInt, buttonBackBorderInt,
); );
@ignore @ignore
Color? _buttonBackBorder; Color? _buttonBackBorder;
late final int buttonBackBorderInt; late final int buttonBackBorderInt;
@ -382,8 +382,8 @@ class StackTheme {
@ignore @ignore
Color get buttonBackBorderDisabled => _buttonBackBorderDisabled ??= Color( Color get buttonBackBorderDisabled => _buttonBackBorderDisabled ??= Color(
buttonBackBorderDisabledInt, buttonBackBorderDisabledInt,
); );
@ignore @ignore
Color? _buttonBackBorderDisabled; Color? _buttonBackBorderDisabled;
late final int buttonBackBorderDisabledInt; late final int buttonBackBorderDisabledInt;
@ -392,8 +392,8 @@ class StackTheme {
@ignore @ignore
Color get buttonBackBorderSecondary => _buttonBackBorderSecondary ??= Color( Color get buttonBackBorderSecondary => _buttonBackBorderSecondary ??= Color(
buttonBackBorderSecondaryInt, buttonBackBorderSecondaryInt,
); );
@ignore @ignore
Color? _buttonBackBorderSecondary; Color? _buttonBackBorderSecondary;
late final int buttonBackBorderSecondaryInt; late final int buttonBackBorderSecondaryInt;
@ -413,8 +413,8 @@ class StackTheme {
@ignore @ignore
Color get numberBackDefault => _numberBackDefault ??= Color( Color get numberBackDefault => _numberBackDefault ??= Color(
numberBackDefaultInt, numberBackDefaultInt,
); );
@ignore @ignore
Color? _numberBackDefault; Color? _numberBackDefault;
late final int numberBackDefaultInt; late final int numberBackDefaultInt;
@ -423,8 +423,8 @@ class StackTheme {
@ignore @ignore
Color get numpadBackDefault => _numpadBackDefault ??= Color( Color get numpadBackDefault => _numpadBackDefault ??= Color(
numpadBackDefaultInt, numpadBackDefaultInt,
); );
@ignore @ignore
Color? _numpadBackDefault; Color? _numpadBackDefault;
late final int numpadBackDefaultInt; late final int numpadBackDefaultInt;
@ -433,8 +433,8 @@ class StackTheme {
@ignore @ignore
Color get bottomNavBack => _bottomNavBack ??= Color( Color get bottomNavBack => _bottomNavBack ??= Color(
bottomNavBackInt, bottomNavBackInt,
); );
@ignore @ignore
Color? _bottomNavBack; Color? _bottomNavBack;
late final int bottomNavBackInt; late final int bottomNavBackInt;
@ -443,8 +443,8 @@ class StackTheme {
@ignore @ignore
Color get buttonTextPrimary => _buttonTextPrimary ??= Color( Color get buttonTextPrimary => _buttonTextPrimary ??= Color(
buttonTextPrimaryInt, buttonTextPrimaryInt,
); );
@ignore @ignore
Color? _buttonTextPrimary; Color? _buttonTextPrimary;
late final int buttonTextPrimaryInt; late final int buttonTextPrimaryInt;
@ -453,8 +453,8 @@ class StackTheme {
@ignore @ignore
Color get buttonTextSecondary => _buttonTextSecondary ??= Color( Color get buttonTextSecondary => _buttonTextSecondary ??= Color(
buttonTextSecondaryInt, buttonTextSecondaryInt,
); );
@ignore @ignore
Color? _buttonTextSecondary; Color? _buttonTextSecondary;
late final int buttonTextSecondaryInt; late final int buttonTextSecondaryInt;
@ -463,8 +463,8 @@ class StackTheme {
@ignore @ignore
Color get buttonTextPrimaryDisabled => _buttonTextPrimaryDisabled ??= Color( Color get buttonTextPrimaryDisabled => _buttonTextPrimaryDisabled ??= Color(
buttonTextPrimaryDisabledInt, buttonTextPrimaryDisabledInt,
); );
@ignore @ignore
Color? _buttonTextPrimaryDisabled; Color? _buttonTextPrimaryDisabled;
late final int buttonTextPrimaryDisabledInt; late final int buttonTextPrimaryDisabledInt;
@ -1517,117 +1517,117 @@ class StackTheme {
..version = version ..version = version
..assetsV1 = version == 1 ..assetsV1 = version == 1
? ThemeAssets.fromJson( ? ThemeAssets.fromJson(
json: Map<String, dynamic>.from(json["assets"] as Map), json: Map<String, dynamic>.from(json["assets"] as Map),
themeId: json["id"] as String, themeId: json["id"] as String,
) )
: null : null
..assetsV2 = version == 2 ..assetsV2 = version == 2
? ThemeAssetsV2.fromJson( ? ThemeAssetsV2.fromJson(
json: Map<String, dynamic>.from(json["assets"] as Map), json: Map<String, dynamic>.from(json["assets"] as Map),
themeId: json["id"] as String, themeId: json["id"] as String,
) )
: null : null
..assetsV3 = version >= 3 ..assetsV3 = version >= 3
? ThemeAssetsV3.fromJson( ? ThemeAssetsV3.fromJson(
json: Map<String, dynamic>.from(json["assets"] as Map), json: Map<String, dynamic>.from(json["assets"] as Map),
themeId: json["id"] as String, themeId: json["id"] as String,
) )
: null : null
..themeId = json["id"] as String ..themeId = json["id"] as String
..name = json["name"] as String ..name = json["name"] as String
..brightnessString = json["brightness"] as String ..brightnessString = json["brightness"] as String
..backgroundInt = parseColor(json["colors"]["background"] as String) ..backgroundInt = parseColor(json["colors"]["background"] as String)
..backgroundAppBarInt = ..backgroundAppBarInt =
parseColor(json["colors"]["background_app_bar"] as String) parseColor(json["colors"]["background_app_bar"] as String)
..gradientBackgroundString = json["colors"]["gradients"] != null ..gradientBackgroundString = json["colors"]["gradients"] != null
? jsonEncode(json["colors"]["gradients"]) ? jsonEncode(json["colors"]["gradients"])
: null : null
..standardBoxShadowString = ..standardBoxShadowString =
jsonEncode(json["colors"]["box_shadows"]["standard"] as Map) jsonEncode(json["colors"]["box_shadows"]["standard"] as Map)
..homeViewButtonBarBoxShadowString = ..homeViewButtonBarBoxShadowString =
json["colors"]["box_shadows"]["home_view_button_bar"] == null json["colors"]["box_shadows"]["home_view_button_bar"] == null
? null ? null
: jsonEncode( : jsonEncode(
json["colors"]["box_shadows"]["home_view_button_bar"] as Map) json["colors"]["box_shadows"]["home_view_button_bar"] as Map)
..coinColorsJsonString = jsonEncode(json["colors"]['coin'] as Map) ..coinColorsJsonString = jsonEncode(json["colors"]['coin'] as Map)
..overlayInt = parseColor(json["colors"]["overlay"] as String) ..overlayInt = parseColor(json["colors"]["overlay"] as String)
..accentColorBlueInt = ..accentColorBlueInt =
parseColor(json["colors"]["accent_color_blue"] as String) parseColor(json["colors"]["accent_color_blue"] as String)
..accentColorGreenInt = ..accentColorGreenInt =
parseColor(json["colors"]["accent_color_green"] as String) parseColor(json["colors"]["accent_color_green"] as String)
..accentColorYellowInt = ..accentColorYellowInt =
parseColor(json["colors"]["accent_color_yellow"] as String) parseColor(json["colors"]["accent_color_yellow"] as String)
..accentColorRedInt = ..accentColorRedInt =
parseColor(json["colors"]["accent_color_red"] as String) parseColor(json["colors"]["accent_color_red"] as String)
..accentColorOrangeInt = ..accentColorOrangeInt =
parseColor(json["colors"]["accent_color_orange"] as String) parseColor(json["colors"]["accent_color_orange"] as String)
..accentColorDarkInt = ..accentColorDarkInt =
parseColor(json["colors"]["accent_color_dark"] as String) parseColor(json["colors"]["accent_color_dark"] as String)
..shadowInt = parseColor(json["colors"]["shadow"] as String) ..shadowInt = parseColor(json["colors"]["shadow"] as String)
..textDarkInt = parseColor(json["colors"]["text_dark_one"] as String) ..textDarkInt = parseColor(json["colors"]["text_dark_one"] as String)
..textDark2Int = parseColor(json["colors"]["text_dark_two"] as String) ..textDark2Int = parseColor(json["colors"]["text_dark_two"] as String)
..textDark3Int = parseColor(json["colors"]["text_dark_three"] as String) ..textDark3Int = parseColor(json["colors"]["text_dark_three"] as String)
..textWhiteInt = parseColor(json["colors"]["text_white"] as String) ..textWhiteInt = parseColor(json["colors"]["text_white"] as String)
..textFavoriteCardInt = ..textFavoriteCardInt =
parseColor(json["colors"]["text_favorite"] as String) parseColor(json["colors"]["text_favorite"] as String)
..textErrorInt = parseColor(json["colors"]["text_error"] as String) ..textErrorInt = parseColor(json["colors"]["text_error"] as String)
..textRestoreInt = parseColor(json["colors"]["text_restore"] as String) ..textRestoreInt = parseColor(json["colors"]["text_restore"] as String)
..buttonBackPrimaryInt = ..buttonBackPrimaryInt =
parseColor(json["colors"]["button_back_primary"] as String) parseColor(json["colors"]["button_back_primary"] as String)
..buttonBackSecondaryInt = ..buttonBackSecondaryInt =
parseColor(json["colors"]["button_back_secondary"] as String) parseColor(json["colors"]["button_back_secondary"] as String)
..buttonBackPrimaryDisabledInt = ..buttonBackPrimaryDisabledInt =
parseColor(json["colors"]["button_back_primary_disabled"] as String) parseColor(json["colors"]["button_back_primary_disabled"] as String)
..buttonBackSecondaryDisabledInt = ..buttonBackSecondaryDisabledInt =
parseColor(json["colors"]["button_back_secondary_disabled"] as String) parseColor(json["colors"]["button_back_secondary_disabled"] as String)
..buttonBackBorderInt = ..buttonBackBorderInt =
parseColor(json["colors"]["button_back_border"] as String) parseColor(json["colors"]["button_back_border"] as String)
..buttonBackBorderDisabledInt = ..buttonBackBorderDisabledInt =
parseColor(json["colors"]["button_back_border_disabled"] as String) parseColor(json["colors"]["button_back_border_disabled"] as String)
..buttonBackBorderSecondaryInt = ..buttonBackBorderSecondaryInt =
parseColor(json["colors"]["button_back_border_secondary"] as String) parseColor(json["colors"]["button_back_border_secondary"] as String)
..buttonBackBorderSecondaryDisabledInt = parseColor( ..buttonBackBorderSecondaryDisabledInt = parseColor(
json["colors"]["button_back_border_secondary_disabled"] as String) json["colors"]["button_back_border_secondary_disabled"] as String)
..numberBackDefaultInt = ..numberBackDefaultInt =
parseColor(json["colors"]["number_back_default"] as String) parseColor(json["colors"]["number_back_default"] as String)
..numpadBackDefaultInt = ..numpadBackDefaultInt =
parseColor(json["colors"]["numpad_back_default"] as String) parseColor(json["colors"]["numpad_back_default"] as String)
..bottomNavBackInt = ..bottomNavBackInt =
parseColor(json["colors"]["bottom_nav_back"] as String) parseColor(json["colors"]["bottom_nav_back"] as String)
..textSubtitle1Int = ..textSubtitle1Int =
parseColor(json["colors"]["text_subtitle_one"] as String) parseColor(json["colors"]["text_subtitle_one"] as String)
..textSubtitle2Int = ..textSubtitle2Int =
parseColor(json["colors"]["text_subtitle_two"] as String) parseColor(json["colors"]["text_subtitle_two"] as String)
..textSubtitle3Int = ..textSubtitle3Int =
parseColor(json["colors"]["text_subtitle_three"] as String) parseColor(json["colors"]["text_subtitle_three"] as String)
..textSubtitle4Int = ..textSubtitle4Int =
parseColor(json["colors"]["text_subtitle_four"] as String) parseColor(json["colors"]["text_subtitle_four"] as String)
..textSubtitle5Int = ..textSubtitle5Int =
parseColor(json["colors"]["text_subtitle_five"] as String) parseColor(json["colors"]["text_subtitle_five"] as String)
..textSubtitle6Int = ..textSubtitle6Int =
parseColor(json["colors"]["text_subtitle_six"] as String) parseColor(json["colors"]["text_subtitle_six"] as String)
..buttonTextPrimaryInt = ..buttonTextPrimaryInt =
parseColor(json["colors"]["button_text_primary"] as String) parseColor(json["colors"]["button_text_primary"] as String)
..buttonTextSecondaryInt = ..buttonTextSecondaryInt =
parseColor(json["colors"]["button_text_secondary"] as String) parseColor(json["colors"]["button_text_secondary"] as String)
..buttonTextPrimaryDisabledInt = ..buttonTextPrimaryDisabledInt =
parseColor(json["colors"]["button_text_primary_disabled"] as String) parseColor(json["colors"]["button_text_primary_disabled"] as String)
..buttonTextSecondaryDisabledInt = ..buttonTextSecondaryDisabledInt =
parseColor(json["colors"]["button_text_secondary_disabled"] as String) parseColor(json["colors"]["button_text_secondary_disabled"] as String)
..buttonTextBorderInt = ..buttonTextBorderInt =
parseColor(json["colors"]["button_text_border"] as String) parseColor(json["colors"]["button_text_border"] as String)
..buttonTextDisabledInt = ..buttonTextDisabledInt =
parseColor(json["colors"]["button_text_disabled"] as String) parseColor(json["colors"]["button_text_disabled"] as String)
..buttonTextBorderlessInt = ..buttonTextBorderlessInt =
parseColor(json["colors"]["button_text_borderless"] as String) parseColor(json["colors"]["button_text_borderless"] as String)
..buttonTextBorderlessDisabledInt = parseColor( ..buttonTextBorderlessDisabledInt = parseColor(
json["colors"]["button_text_borderless_disabled"] as String) json["colors"]["button_text_borderless_disabled"] as String)
..numberTextDefaultInt = ..numberTextDefaultInt =
parseColor(json["colors"]["number_text_default"] as String) parseColor(json["colors"]["number_text_default"] as String)
..numpadTextDefaultInt = ..numpadTextDefaultInt =
parseColor(json["colors"]["numpad_text_default"] as String) parseColor(json["colors"]["numpad_text_default"] as String)
..bottomNavTextInt = ..bottomNavTextInt =
parseColor(json["colors"]["bottom_nav_text"] as String) parseColor(json["colors"]["bottom_nav_text"] as String)
..customTextButtonEnabledTextInt = parseColor( ..customTextButtonEnabledTextInt = parseColor(
json["colors"]["custom_text_button_enabled_text"] as String) json["colors"]["custom_text_button_enabled_text"] as String)
..customTextButtonDisabledTextInt = parseColor( ..customTextButtonDisabledTextInt = parseColor(
@ -1635,87 +1635,87 @@ class StackTheme {
..switchBGOnInt = parseColor(json["colors"]["switch_bg_on"] as String) ..switchBGOnInt = parseColor(json["colors"]["switch_bg_on"] as String)
..switchBGOffInt = parseColor(json["colors"]["switch_bg_off"] as String) ..switchBGOffInt = parseColor(json["colors"]["switch_bg_off"] as String)
..switchBGDisabledInt = ..switchBGDisabledInt =
parseColor(json["colors"]["switch_bg_disabled"] as String) parseColor(json["colors"]["switch_bg_disabled"] as String)
..switchCircleOnInt = ..switchCircleOnInt =
parseColor(json["colors"]["switch_circle_on"] as String) parseColor(json["colors"]["switch_circle_on"] as String)
..switchCircleOffInt = ..switchCircleOffInt =
parseColor(json["colors"]["switch_circle_off"] as String) parseColor(json["colors"]["switch_circle_off"] as String)
..switchCircleDisabledInt = ..switchCircleDisabledInt =
parseColor(json["colors"]["switch_circle_disabled"] as String) parseColor(json["colors"]["switch_circle_disabled"] as String)
..stepIndicatorBGCheckInt = ..stepIndicatorBGCheckInt =
parseColor(json["colors"]["step_indicator_bg_check"] as String) parseColor(json["colors"]["step_indicator_bg_check"] as String)
..stepIndicatorBGNumberInt = ..stepIndicatorBGNumberInt =
parseColor(json["colors"]["step_indicator_bg_number"] as String) parseColor(json["colors"]["step_indicator_bg_number"] as String)
..stepIndicatorBGInactiveInt = ..stepIndicatorBGInactiveInt =
parseColor(json["colors"]["step_indicator_bg_inactive"] as String) parseColor(json["colors"]["step_indicator_bg_inactive"] as String)
..stepIndicatorBGLinesInt = ..stepIndicatorBGLinesInt =
parseColor(json["colors"]["step_indicator_bg_lines"] as String) parseColor(json["colors"]["step_indicator_bg_lines"] as String)
..stepIndicatorBGLinesInactiveInt = parseColor( ..stepIndicatorBGLinesInactiveInt = parseColor(
json["colors"]["step_indicator_bg_lines_inactive"] as String) json["colors"]["step_indicator_bg_lines_inactive"] as String)
..stepIndicatorIconTextInt = ..stepIndicatorIconTextInt =
parseColor(json["colors"]["step_indicator_icon_text"] as String) parseColor(json["colors"]["step_indicator_icon_text"] as String)
..stepIndicatorIconNumberInt = ..stepIndicatorIconNumberInt =
parseColor(json["colors"]["step_indicator_icon_number"] as String) parseColor(json["colors"]["step_indicator_icon_number"] as String)
..stepIndicatorIconInactiveInt = ..stepIndicatorIconInactiveInt =
parseColor(json["colors"]["step_indicator_icon_inactive"] as String) parseColor(json["colors"]["step_indicator_icon_inactive"] as String)
..checkboxBGCheckedInt = ..checkboxBGCheckedInt =
parseColor(json["colors"]["checkbox_bg_checked"] as String) parseColor(json["colors"]["checkbox_bg_checked"] as String)
..checkboxBorderEmptyInt = ..checkboxBorderEmptyInt =
parseColor(json["colors"]["checkbox_border_empty"] as String) parseColor(json["colors"]["checkbox_border_empty"] as String)
..checkboxBGDisabledInt = ..checkboxBGDisabledInt =
parseColor(json["colors"]["checkbox_bg_disabled"] as String) parseColor(json["colors"]["checkbox_bg_disabled"] as String)
..checkboxIconCheckedInt = ..checkboxIconCheckedInt =
parseColor(json["colors"]["checkbox_icon_checked"] as String) parseColor(json["colors"]["checkbox_icon_checked"] as String)
..checkboxIconDisabledInt = ..checkboxIconDisabledInt =
parseColor(json["colors"]["checkbox_icon_disabled"] as String) parseColor(json["colors"]["checkbox_icon_disabled"] as String)
..checkboxTextLabelInt = ..checkboxTextLabelInt =
parseColor(json["colors"]["checkbox_text_label"] as String) parseColor(json["colors"]["checkbox_text_label"] as String)
..snackBarBackSuccessInt = ..snackBarBackSuccessInt =
parseColor(json["colors"]["snack_bar_back_success"] as String) parseColor(json["colors"]["snack_bar_back_success"] as String)
..snackBarBackErrorInt = ..snackBarBackErrorInt =
parseColor(json["colors"]["snack_bar_back_error"] as String) parseColor(json["colors"]["snack_bar_back_error"] as String)
..snackBarBackInfoInt = ..snackBarBackInfoInt =
parseColor(json["colors"]["snack_bar_back_info"] as String) parseColor(json["colors"]["snack_bar_back_info"] as String)
..snackBarTextSuccessInt = ..snackBarTextSuccessInt =
parseColor(json["colors"]["snack_bar_text_success"] as String) parseColor(json["colors"]["snack_bar_text_success"] as String)
..snackBarTextErrorInt = ..snackBarTextErrorInt =
parseColor(json["colors"]["snack_bar_text_error"] as String) parseColor(json["colors"]["snack_bar_text_error"] as String)
..snackBarTextInfoInt = ..snackBarTextInfoInt =
parseColor(json["colors"]["snack_bar_text_info"] as String) parseColor(json["colors"]["snack_bar_text_info"] as String)
..bottomNavIconBackInt = ..bottomNavIconBackInt =
parseColor(json["colors"]["bottom_nav_icon_back"] as String) parseColor(json["colors"]["bottom_nav_icon_back"] as String)
..bottomNavIconIconInt = ..bottomNavIconIconInt =
parseColor(json["colors"]["bottom_nav_icon_icon"] as String) parseColor(json["colors"]["bottom_nav_icon_icon"] as String)
..bottomNavIconIconHighlightedInt = parseColor( ..bottomNavIconIconHighlightedInt = parseColor(
json["colors"]["bottom_nav_icon_icon_highlighted"] as String) json["colors"]["bottom_nav_icon_icon_highlighted"] as String)
..topNavIconPrimaryInt = ..topNavIconPrimaryInt =
parseColor(json["colors"]["top_nav_icon_primary"] as String) parseColor(json["colors"]["top_nav_icon_primary"] as String)
..topNavIconGreenInt = ..topNavIconGreenInt =
parseColor(json["colors"]["top_nav_icon_green"] as String) parseColor(json["colors"]["top_nav_icon_green"] as String)
..topNavIconYellowInt = ..topNavIconYellowInt =
parseColor(json["colors"]["top_nav_icon_yellow"] as String) parseColor(json["colors"]["top_nav_icon_yellow"] as String)
..topNavIconRedInt = ..topNavIconRedInt =
parseColor(json["colors"]["top_nav_icon_red"] as String) parseColor(json["colors"]["top_nav_icon_red"] as String)
..settingsIconBackInt = ..settingsIconBackInt =
parseColor(json["colors"]["settings_icon_back"] as String) parseColor(json["colors"]["settings_icon_back"] as String)
..settingsIconIconInt = ..settingsIconIconInt =
parseColor(json["colors"]["settings_icon_icon"] as String) parseColor(json["colors"]["settings_icon_icon"] as String)
..settingsIconBack2Int = ..settingsIconBack2Int =
parseColor(json["colors"]["settings_icon_back_two"] as String) parseColor(json["colors"]["settings_icon_back_two"] as String)
..settingsIconElementInt = ..settingsIconElementInt =
parseColor(json["colors"]["settings_icon_element"] as String) parseColor(json["colors"]["settings_icon_element"] as String)
..textFieldActiveBGInt = ..textFieldActiveBGInt =
parseColor(json["colors"]["text_field_active_bg"] as String) parseColor(json["colors"]["text_field_active_bg"] as String)
..textFieldDefaultBGInt = ..textFieldDefaultBGInt =
parseColor(json["colors"]["text_field_default_bg"] as String) parseColor(json["colors"]["text_field_default_bg"] as String)
..textFieldErrorBGInt = ..textFieldErrorBGInt =
parseColor(json["colors"]["text_field_error_bg"] as String) parseColor(json["colors"]["text_field_error_bg"] as String)
..textFieldSuccessBGInt = ..textFieldSuccessBGInt =
parseColor(json["colors"]["text_field_success_bg"] as String) parseColor(json["colors"]["text_field_success_bg"] as String)
..textFieldErrorBorderInt = ..textFieldErrorBorderInt =
parseColor(json["colors"]["text_field_error_border"] as String) parseColor(json["colors"]["text_field_error_border"] as String)
..textFieldSuccessBorderInt = ..textFieldSuccessBorderInt =
parseColor(json["colors"]["text_field_success_border"] as String) parseColor(json["colors"]["text_field_success_border"] as String)
..textFieldActiveSearchIconLeftInt = parseColor( ..textFieldActiveSearchIconLeftInt = parseColor(
json["colors"]["text_field_active_search_icon_left"] as String) json["colors"]["text_field_active_search_icon_left"] as String)
..textFieldDefaultSearchIconLeftInt = parseColor( ..textFieldDefaultSearchIconLeftInt = parseColor(
@ -1725,19 +1725,19 @@ class StackTheme {
..textFieldSuccessSearchIconLeftInt = parseColor( ..textFieldSuccessSearchIconLeftInt = parseColor(
json["colors"]["text_field_success_search_icon_left"] as String) json["colors"]["text_field_success_search_icon_left"] as String)
..textFieldActiveTextInt = ..textFieldActiveTextInt =
parseColor(json["colors"]["text_field_active_text"] as String) parseColor(json["colors"]["text_field_active_text"] as String)
..textFieldDefaultTextInt = ..textFieldDefaultTextInt =
parseColor(json["colors"]["text_field_default_text"] as String) parseColor(json["colors"]["text_field_default_text"] as String)
..textFieldErrorTextInt = ..textFieldErrorTextInt =
parseColor(json["colors"]["text_field_error_text"] as String) parseColor(json["colors"]["text_field_error_text"] as String)
..textFieldSuccessTextInt = ..textFieldSuccessTextInt =
parseColor(json["colors"]["text_field_success_text"] as String) parseColor(json["colors"]["text_field_success_text"] as String)
..textFieldActiveLabelInt = ..textFieldActiveLabelInt =
parseColor(json["colors"]["text_field_active_label"] as String) parseColor(json["colors"]["text_field_active_label"] as String)
..textFieldErrorLabelInt = ..textFieldErrorLabelInt =
parseColor(json["colors"]["text_field_error_label"] as String) parseColor(json["colors"]["text_field_error_label"] as String)
..textFieldSuccessLabelInt = ..textFieldSuccessLabelInt =
parseColor(json["colors"]["text_field_success_label"] as String) parseColor(json["colors"]["text_field_success_label"] as String)
..textFieldActiveSearchIconRightInt = parseColor( ..textFieldActiveSearchIconRightInt = parseColor(
json["colors"]["text_field_active_search_icon_right"] as String) json["colors"]["text_field_active_search_icon_right"] as String)
..textFieldDefaultSearchIconRightInt = parseColor( ..textFieldDefaultSearchIconRightInt = parseColor(
@ -1753,61 +1753,61 @@ class StackTheme {
..settingsItem2ActiveSubInt = parseColor( ..settingsItem2ActiveSubInt = parseColor(
json["colors"]["settings_item_level_two_active_sub"] as String) json["colors"]["settings_item_level_two_active_sub"] as String)
..radioButtonIconBorderInt = ..radioButtonIconBorderInt =
parseColor(json["colors"]["radio_button_icon_border"] as String) parseColor(json["colors"]["radio_button_icon_border"] as String)
..radioButtonIconBorderDisabledInt = parseColor( ..radioButtonIconBorderDisabledInt = parseColor(
json["colors"]["radio_button_icon_border_disabled"] as String) json["colors"]["radio_button_icon_border_disabled"] as String)
..radioButtonBorderEnabledInt = ..radioButtonBorderEnabledInt =
parseColor(json["colors"]["radio_button_border_enabled"] as String) parseColor(json["colors"]["radio_button_border_enabled"] as String)
..radioButtonBorderDisabledInt = ..radioButtonBorderDisabledInt =
parseColor(json["colors"]["radio_button_border_disabled"] as String) parseColor(json["colors"]["radio_button_border_disabled"] as String)
..radioButtonIconCircleInt = ..radioButtonIconCircleInt =
parseColor(json["colors"]["radio_button_icon_circle"] as String) parseColor(json["colors"]["radio_button_icon_circle"] as String)
..radioButtonIconEnabledInt = ..radioButtonIconEnabledInt =
parseColor(json["colors"]["radio_button_icon_enabled"] as String) parseColor(json["colors"]["radio_button_icon_enabled"] as String)
..radioButtonTextEnabledInt = ..radioButtonTextEnabledInt =
parseColor(json["colors"]["radio_button_text_enabled"] as String) parseColor(json["colors"]["radio_button_text_enabled"] as String)
..radioButtonTextDisabledInt = ..radioButtonTextDisabledInt =
parseColor(json["colors"]["radio_button_text_disabled"] as String) parseColor(json["colors"]["radio_button_text_disabled"] as String)
..radioButtonLabelEnabledInt = ..radioButtonLabelEnabledInt =
parseColor(json["colors"]["radio_button_label_enabled"] as String) parseColor(json["colors"]["radio_button_label_enabled"] as String)
..radioButtonLabelDisabledInt = ..radioButtonLabelDisabledInt =
parseColor(json["colors"]["radio_button_label_disabled"] as String) parseColor(json["colors"]["radio_button_label_disabled"] as String)
..infoItemBGInt = parseColor(json["colors"]["info_item_bg"] as String) ..infoItemBGInt = parseColor(json["colors"]["info_item_bg"] as String)
..infoItemLabelInt = ..infoItemLabelInt =
parseColor(json["colors"]["info_item_label"] as String) parseColor(json["colors"]["info_item_label"] as String)
..infoItemTextInt = parseColor(json["colors"]["info_item_text"] as String) ..infoItemTextInt = parseColor(json["colors"]["info_item_text"] as String)
..infoItemIconsInt = ..infoItemIconsInt =
parseColor(json["colors"]["info_item_icons"] as String) parseColor(json["colors"]["info_item_icons"] as String)
..popupBGInt = parseColor(json["colors"]["popup_bg"] as String) ..popupBGInt = parseColor(json["colors"]["popup_bg"] as String)
..currencyListItemBGInt = ..currencyListItemBGInt =
parseColor(json["colors"]["currency_list_item_bg"] as String) parseColor(json["colors"]["currency_list_item_bg"] as String)
..stackWalletBGInt = parseColor(json["colors"]["sw_bg"] as String) ..stackWalletBGInt = parseColor(json["colors"]["sw_bg"] as String)
..stackWalletMidInt = parseColor(json["colors"]["sw_mid"] as String) ..stackWalletMidInt = parseColor(json["colors"]["sw_mid"] as String)
..stackWalletBottomInt = parseColor(json["colors"]["sw_bottom"] as String) ..stackWalletBottomInt = parseColor(json["colors"]["sw_bottom"] as String)
..bottomNavShadowInt = ..bottomNavShadowInt =
parseColor(json["colors"]["bottom_nav_shadow"] as String) parseColor(json["colors"]["bottom_nav_shadow"] as String)
..splashInt = parseColor(json["colors"]["splash"] as String) ..splashInt = parseColor(json["colors"]["splash"] as String)
..highlightInt = parseColor(json["colors"]["highlight"] as String) ..highlightInt = parseColor(json["colors"]["highlight"] as String)
..warningForegroundInt = ..warningForegroundInt =
parseColor(json["colors"]["warning_foreground"] as String) parseColor(json["colors"]["warning_foreground"] as String)
..warningBackgroundInt = ..warningBackgroundInt =
parseColor(json["colors"]["warning_background"] as String) parseColor(json["colors"]["warning_background"] as String)
..loadingOverlayTextColorInt = ..loadingOverlayTextColorInt =
parseColor(json["colors"]["loading_overlay_text_color"] as String) parseColor(json["colors"]["loading_overlay_text_color"] as String)
..myStackContactIconBGInt = ..myStackContactIconBGInt =
parseColor(json["colors"]["my_stack_contact_icon_bg"] as String) parseColor(json["colors"]["my_stack_contact_icon_bg"] as String)
..textConfirmTotalAmountInt = ..textConfirmTotalAmountInt =
parseColor(json["colors"]["text_confirm_total_amount"] as String) parseColor(json["colors"]["text_confirm_total_amount"] as String)
..textSelectedWordTableItemInt = ..textSelectedWordTableItemInt =
parseColor(json["colors"]["text_selected_word_table_iterm"] as String) parseColor(json["colors"]["text_selected_word_table_iterm"] as String)
..favoriteStarActiveInt = ..favoriteStarActiveInt =
parseColor(json["colors"]["favorite_star_active"] as String) parseColor(json["colors"]["favorite_star_active"] as String)
..favoriteStarInactiveInt = ..favoriteStarInactiveInt =
parseColor(json["colors"]["favorite_star_inactive"] as String) parseColor(json["colors"]["favorite_star_inactive"] as String)
..rateTypeToggleColorOnInt = ..rateTypeToggleColorOnInt =
parseColor(json["colors"]["rate_type_toggle_color_on"] as String) parseColor(json["colors"]["rate_type_toggle_color_on"] as String)
..rateTypeToggleColorOffInt = ..rateTypeToggleColorOffInt =
parseColor(json["colors"]["rate_type_toggle_color_off"] as String) parseColor(json["colors"]["rate_type_toggle_color_off"] as String)
..rateTypeToggleDesktopColorOnInt = parseColor( ..rateTypeToggleDesktopColorOnInt = parseColor(
json["colors"]["rate_type_toggle_desktop_color_on"] as String) json["colors"]["rate_type_toggle_desktop_color_on"] as String)
..rateTypeToggleDesktopColorOffInt = parseColor( ..rateTypeToggleDesktopColorOffInt = parseColor(
@ -1815,19 +1815,19 @@ class StackTheme {
..ethTagTextInt = parseColor(json["colors"]["eth_tag_text"] as String) ..ethTagTextInt = parseColor(json["colors"]["eth_tag_text"] as String)
..ethTagBGInt = parseColor(json["colors"]["eth_tag_bg"] as String) ..ethTagBGInt = parseColor(json["colors"]["eth_tag_bg"] as String)
..ethWalletTagTextInt = ..ethWalletTagTextInt =
parseColor(json["colors"]["eth_wallet_tag_text"] as String) parseColor(json["colors"]["eth_wallet_tag_text"] as String)
..ethWalletTagBGInt = ..ethWalletTagBGInt =
parseColor(json["colors"]["eth_wallet_tag_bg"] as String) parseColor(json["colors"]["eth_wallet_tag_bg"] as String)
..tokenSummaryTextPrimaryInt = ..tokenSummaryTextPrimaryInt =
parseColor(json["colors"]["token_summary_text_primary"] as String) parseColor(json["colors"]["token_summary_text_primary"] as String)
..tokenSummaryTextSecondaryInt = ..tokenSummaryTextSecondaryInt =
parseColor(json["colors"]["token_summary_text_secondary"] as String) parseColor(json["colors"]["token_summary_text_secondary"] as String)
..tokenSummaryBGInt = ..tokenSummaryBGInt =
parseColor(json["colors"]["token_summary_bg"] as String) parseColor(json["colors"]["token_summary_bg"] as String)
..tokenSummaryButtonBGInt = ..tokenSummaryButtonBGInt =
parseColor(json["colors"]["token_summary_button_bg"] as String) parseColor(json["colors"]["token_summary_button_bg"] as String)
..tokenSummaryIconInt = ..tokenSummaryIconInt =
parseColor(json["colors"]["token_summary_icon"] as String); parseColor(json["colors"]["token_summary_icon"] as String);
} }
/// Grab the int value of the hex color string. /// Grab the int value of the hex color string.
@ -1840,7 +1840,7 @@ class StackTheme {
} else { } else {
throw ArgumentError( throw ArgumentError(
'"$colorHex" and corresponding int ' '"$colorHex" and corresponding int '
'value "$colorValue" is not a valid color.', 'value "$colorValue" is not a valid color.',
); );
} }
} catch (_) { } catch (_) {
@ -2078,18 +2078,18 @@ class ThemeAssetsV2 implements IThemeAssets {
@ignore @ignore
Map<Coin, String> get coinIcons => _coinIcons ??= parseCoinAssetsString( Map<Coin, String> get coinIcons => _coinIcons ??= parseCoinAssetsString(
coinIconsString, coinIconsString,
placeHolder: coinPlaceholder, placeHolder: coinPlaceholder,
); );
@ignore @ignore
Map<Coin, String>? _coinIcons; Map<Coin, String>? _coinIcons;
late final String coinIconsString; late final String coinIconsString;
@ignore @ignore
Map<Coin, String> get coinImages => _coinImages ??= parseCoinAssetsString( Map<Coin, String> get coinImages => _coinImages ??= parseCoinAssetsString(
coinImagesString, coinImagesString,
placeHolder: coinPlaceholder, placeHolder: coinPlaceholder,
); );
@ignore @ignore
Map<Coin, String>? _coinImages; Map<Coin, String>? _coinImages;
late final String coinImagesString; late final String coinImagesString;
@ -2164,9 +2164,9 @@ class ThemeAssetsV2 implements IThemeAssets {
} }
static Map<Coin, String> parseCoinAssetsString( static Map<Coin, String> parseCoinAssetsString(
String jsonString, { String jsonString, {
required String placeHolder, required String placeHolder,
}) { }) {
final json = jsonDecode(jsonString) as Map; final json = jsonDecode(jsonString) as Map;
final map = Map<String, dynamic>.from(json); final map = Map<String, dynamic>.from(json);
@ -2348,18 +2348,18 @@ class ThemeAssetsV3 implements IThemeAssets {
@ignore @ignore
Map<Coin, String> get coinIcons => _coinIcons ??= parseCoinAssetsString( Map<Coin, String> get coinIcons => _coinIcons ??= parseCoinAssetsString(
coinIconsString, coinIconsString,
placeHolder: coinPlaceholder, placeHolder: coinPlaceholder,
); );
@ignore @ignore
Map<Coin, String>? _coinIcons; Map<Coin, String>? _coinIcons;
late final String coinIconsString; late final String coinIconsString;
@ignore @ignore
Map<Coin, String> get coinImages => _coinImages ??= parseCoinAssetsString( Map<Coin, String> get coinImages => _coinImages ??= parseCoinAssetsString(
coinImagesString, coinImagesString,
placeHolder: coinPlaceholder, placeHolder: coinPlaceholder,
); );
@ignore @ignore
Map<Coin, String>? _coinImages; Map<Coin, String>? _coinImages;
late final String coinImagesString; late final String coinImagesString;
@ -2379,9 +2379,9 @@ class ThemeAssetsV3 implements IThemeAssets {
_coinCardImages ??= coinCardImagesString == null _coinCardImages ??= coinCardImagesString == null
? null ? null
: parseCoinAssetsString( : parseCoinAssetsString(
coinCardImagesString!, coinCardImagesString!,
placeHolder: coinPlaceholder, placeHolder: coinPlaceholder,
); );
@ignore @ignore
Map<Coin, String>? _coinCardImages; Map<Coin, String>? _coinCardImages;
late final String? coinCardImagesString; late final String? coinCardImagesString;
@ -2391,9 +2391,9 @@ class ThemeAssetsV3 implements IThemeAssets {
_coinCardFavoritesImages ??= coinCardFavoritesImagesString == null _coinCardFavoritesImages ??= coinCardFavoritesImagesString == null
? null ? null
: parseCoinAssetsString( : parseCoinAssetsString(
coinCardFavoritesImagesString!, coinCardFavoritesImagesString!,
placeHolder: coinPlaceholder, placeHolder: coinPlaceholder,
); );
@ignore @ignore
Map<Coin, String>? _coinCardFavoritesImages; Map<Coin, String>? _coinCardFavoritesImages;
@Name("otherStringParam1") @Name("otherStringParam1")
@ -2450,15 +2450,15 @@ class ThemeAssetsV3 implements IThemeAssets {
) )
..coinCardImagesString = json["coins"]["cards"] is Map ..coinCardImagesString = json["coins"]["cards"] is Map
? createCoinAssetsString( ? createCoinAssetsString(
"$themeId/assets", "$themeId/assets",
Map<String, dynamic>.from(json["coins"]["cards"] as Map), Map<String, dynamic>.from(json["coins"]["cards"] as Map),
) )
: null : null
..coinCardFavoritesImagesString = json["coins"]["favoriteCards"] is Map ..coinCardFavoritesImagesString = json["coins"]["favoriteCards"] is Map
? createCoinAssetsString( ? createCoinAssetsString(
"$themeId/assets", "$themeId/assets",
Map<String, dynamic>.from(json["coins"]["favoriteCards"] as Map), Map<String, dynamic>.from(json["coins"]["favoriteCards"] as Map),
) )
: null : null
..loadingGifRelative = json["loading_gif"] is String ..loadingGifRelative = json["loading_gif"] is String
? "$themeId/assets/${json["loading_gif"] as String}" ? "$themeId/assets/${json["loading_gif"] as String}"
@ -2499,9 +2499,9 @@ class ThemeAssetsV3 implements IThemeAssets {
} }
static Map<Coin, String> parseCoinAssetsString( static Map<Coin, String> parseCoinAssetsString(
String jsonString, { String jsonString, {
required String placeHolder, required String placeHolder,
}) { }) {
final json = jsonDecode(jsonString) as Map; final json = jsonDecode(jsonString) as Map;
final map = Map<String, dynamic>.from(json); final map = Map<String, dynamic>.from(json);
@ -2571,4 +2571,4 @@ abstract class IThemeAssets {
String? get loadingGif; String? get loadingGif;
String? get background; String? get background;
} }

View file

@ -4,11 +4,12 @@ import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/banano/banano_wallet.dart'; import 'package:stackwallet/services/coins/banano/banano_wallet.dart';
import 'package:stackwallet/services/monkey_service.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
@ -20,6 +21,8 @@ import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart';
@ -44,75 +47,14 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
late final String walletId; late final String walletId;
List<int>? imageBytes; List<int>? imageBytes;
String receivingAddress = ""; Future<void> _updateWalletMonKey(Uint8List monKeyBytes) async {
final manager =
Future<void> getMonkeyImage(String address) async { ref.read(walletsChangeNotifierProvider).getManager(walletId);
if (address.isEmpty) { await (manager.wallet as BananoWallet)
//address shouldn't be empty .updateMonkeyImageBytes(monKeyBytes.toList());
return;
}
final http.Response response = await http
.get(Uri.parse('https://monkey.banano.cc/api/v1/monkey/$address'));
if (response.statusCode == 200) {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
final decodedResponse = response.bodyBytes;
await (manager.wallet as BananoWallet)
.updateMonkeyImageBytes(decodedResponse);
} else {
throw Exception("Failed to get MonKey");
}
} }
// void getMonkeySVG(String address) async { Future<Directory?> _getDocsDir() async {
// if (address.isEmpty) {
// //address shouldn't be empty
// return;
// }
//
// final http.Response response = await http
// .get(Uri.parse('https://monkey.banano.cc/api/v1/monkey/$address'));
//
// if (response.statusCode == 200) {
// final decodedResponse = response.bodyBytes;
// Directory directory = await getApplicationDocumentsDirectory();
// late Directory sampleFolder;
//
// if (Platform.isAndroid) {
// directory = Directory("/storage/emulated/0/");
// sampleFolder = Directory('${directory!.path}Documents');
// } else if (Platform.isIOS) {
// sampleFolder = Directory(directory!.path);
// } else if (Platform.isLinux) {
// sampleFolder = Directory('${directory!.path}Documents');
// } else if (Platform.isWindows) {
// sampleFolder = Directory('${directory!.path}Documents');
// } else if (Platform.isMacOS) {
// sampleFolder = Directory('${directory!.path}Documents');
// }
//
// try {
// if (!sampleFolder.existsSync()) {
// sampleFolder.createSync(recursive: true);
// }
// } catch (e, s) {
// // todo: come back to this
// debugPrint("$e $s");
// }
//
// final docPath = sampleFolder.path;
// final filePath = "$docPath/monkey.svg";
//
// File imgFile = File(filePath);
// await imgFile.writeAsBytes(decodedResponse);
// } else {
// throw Exception("Failed to get MonKey");
// }
// }
Future<Directory?> getDocsDir() async {
try { try {
if (Platform.isAndroid) { if (Platform.isAndroid) {
return Directory("/storage/emulated/0/Documents"); return Directory("/storage/emulated/0/Documents");
@ -124,80 +66,45 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
} }
} }
Future<void> downloadMonkey(String address, bool isPNG) async { Future<void> _saveMonKeyToFile({
if (address.isEmpty) { required Uint8List bytes,
//address shouldn't be empty bool isPNG = false,
return; bool overwrite = false,
}) async {
if (Platform.isAndroid) {
await Permission.storage.request();
} }
String url = "https://monkey.banano.cc/api/v1/monkey/$address"; final dir = await _getDocsDir();
if (dir == null) {
if (isPNG) { throw Exception("Failed to get documents directory to save monKey image");
url += '?format=png&size=512&background=false';
} }
final http.Response response = await http.get(Uri.parse(url)); final address = await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.currentReceivingAddress;
final docPath = dir.path;
String filePath = "$docPath/monkey_$address";
if (response.statusCode == 200) { filePath += isPNG ? ".png" : ".svg";
if (Platform.isAndroid) {
await Permission.storage.request();
}
final decodedResponse = response.bodyBytes; File imgFile = File(filePath);
final Directory? sampleFolder = await getDocsDir();
print("PATH: ${sampleFolder?.path}"); if (imgFile.existsSync() && !overwrite) {
throw Exception("File already exists");
if (sampleFolder == null) {
print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
return;
}
// try {
// if (!sampleFolder.existsSync()) {
// sampleFolder.createSync(recursive: true);
// }
// } catch (e, s) {
// // todo: come back to this
// debugPrint("$e $s");
// }
final docPath = sampleFolder.path;
String filePath = "$docPath/monkey_$address";
filePath += isPNG ? ".png" : ".svg";
// todo check if monkey.png exists
File imgFile = File(filePath);
await imgFile.writeAsBytes(decodedResponse);
} else {
throw Exception("Failed to get MonKey");
} }
await imgFile.writeAsBytes(bytes);
} }
@override @override
void initState() { void initState() {
walletId = widget.walletId; walletId = widget.walletId;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final address = await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.currentReceivingAddress;
setState(() {
receivingAddress = address;
});
});
super.initState(); super.initState();
} }
@override
void dispose() {
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final manager = ref.watch(walletsChangeNotifierProvider final manager = ref.watch(walletsChangeNotifierProvider
@ -208,7 +115,6 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
imageBytes ??= (manager.wallet as BananoWallet).getMonkeyImageBytes(); imageBytes ??= (manager.wallet as BananoWallet).getMonkeyImageBytes();
//edit for desktop
return Background( return Background(
child: ConditionalParent( child: ConditionalParent(
condition: isDesktop, condition: isDesktop,
@ -235,64 +141,116 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
.extension<StackColors>()! .extension<StackColors>()!
.topNavIconPrimary, .topNavIconPrimary,
), ),
onPressed: () { onPressed: Navigator.of(context).pop,
if (mounted) {
Navigator.of(context).pop();
}
},
), ),
const SizedBox( const SizedBox(
width: 15, width: 15,
), ),
SvgPicture.asset(Assets.svg.monkey), SvgPicture.asset(
Assets.svg.monkey,
width: 32,
height: 32,
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
const SizedBox( const SizedBox(
width: 12, width: 12,
), ),
Text( Text(
"MonKey", "MonKey",
style: STextStyles.navBarTitle(context), style: STextStyles.desktopH3(context),
), ),
], ],
), ),
), ),
trailing: Padding( trailing: RawMaterialButton(
padding: const EdgeInsets.all(8.0), shape: RoundedRectangleBorder(
child: MouseRegion( borderRadius: BorderRadius.circular(1000),
cursor: SystemMouseCursors.click, ),
child: GestureDetector( onPressed: () {
onTap: () { showDialog<void>(
showDialog<dynamic>( context: context,
context: context, useSafeArea: false,
useSafeArea: false, barrierDismissible: true,
barrierDismissible: true, builder: (context) {
builder: (context) { return DesktopDialog(
return const StackDialog( maxHeight: double.infinity,
title: "About MonKeys", child: Column(
message: children: [
"A MonKey is a visual representation of your Banano address.", Row(
); mainAxisAlignment: MainAxisAlignment.spaceBetween,
}); children: [
}, Padding(
child: Row( padding: const EdgeInsets.only(left: 32),
children: [ child: Text(
SvgPicture.asset( "About MonKeys",
Assets.svg.circleQuestion, style: STextStyles.desktopH3(context),
color: Colors.blue[800], ),
), ),
const SizedBox( const DesktopDialogCloseButton(),
width: 6, ],
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
"What is MonKey?",
style: STextStyles.desktopTextSmall(context).copyWith(
color: Colors.blue[800],
), ),
), Text(
"A MonKey is a visual representation of your Banano address.",
style:
STextStyles.desktopTextMedium(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark3,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(
32,
),
child: PrimaryButton(
width: 272.5,
label: "OK",
onPressed: () {
Navigator.of(context).pop();
},
),
),
],
),
],
), ),
], );
), },
);
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 19,
horizontal: 32,
),
child: Row(
children: [
SvgPicture.asset(
Assets.svg.circleQuestion,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.customTextButtonEnabledText,
),
const SizedBox(
width: 8,
),
Text(
"What is MonKey?",
style:
STextStyles.desktopMenuItemSelected(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.customTextButtonEnabledText,
),
)
],
), ),
), ),
), ),
@ -318,22 +276,24 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
AspectRatio( AspectRatio(
aspectRatio: 1, aspectRatio: 1,
child: AppBarIconButton( child: AppBarIconButton(
icon: SvgPicture.asset( icon: SvgPicture.asset(
Assets.svg.circleQuestion, Assets.svg.circleQuestion,
), ),
onPressed: () { onPressed: () {
showDialog<dynamic>( showDialog<dynamic>(
context: context, context: context,
useSafeArea: false, useSafeArea: false,
barrierDismissible: true, barrierDismissible: true,
builder: (context) { builder: (context) {
return const StackOkDialog( return const StackOkDialog(
title: "About MonKeys", title: "About MonKeys",
message: message:
"A MonKey is a visual representation of your Banano address.", "A MonKey is a visual representation of your Banano address.",
); );
}); },
}), );
},
),
), ),
], ],
), ),
@ -376,26 +336,95 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
SecondaryButton( SecondaryButton(
label: "Save as SVG", label: "Save as SVG",
onPressed: () async { onPressed: () async {
bool didError = false;
await showLoading( await showLoading(
whileFuture: whileFuture: Future.wait([
downloadMonkey(receivingAddress, false), _saveMonKeyToFile(
bytes: Uint8List.fromList(
(manager.wallet as BananoWallet)
.getMonkeyImageBytes()!),
),
Future<void>.delayed(
const Duration(seconds: 2),
),
]),
context: context, context: context,
isDesktop: Util.isDesktop, isDesktop: Util.isDesktop,
message: "Saving MonKey svg", message: "Saving MonKey svg",
onException: (e) {
didError = true;
String msg = e.toString();
while (msg.isNotEmpty &&
msg.startsWith("Exception:")) {
msg = msg.substring(10).trim();
}
showFloatingFlushBar(
type: FlushBarType.warning,
message: msg,
context: context,
);
},
); );
if (!didError && mounted) {
await showFloatingFlushBar(
type: FlushBarType.success,
message: "SVG MonKey image saved",
context: context,
);
}
}, },
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
SecondaryButton( SecondaryButton(
label: "Download as PNG", label: "Download as PNG",
onPressed: () async { onPressed: () async {
bool didError = false;
await showLoading( await showLoading(
whileFuture: whileFuture: Future.wait([
downloadMonkey(receivingAddress, true), manager.currentReceivingAddress.then(
(address) async => await ref
.read(pMonKeyService)
.fetchMonKey(
address: address,
png: true,
)
.then(
(monKeyBytes) async =>
await _saveMonKeyToFile(
bytes: monKeyBytes,
isPNG: true,
),
),
),
Future<void>.delayed(
const Duration(seconds: 2)),
]),
context: context, context: context,
isDesktop: Util.isDesktop, isDesktop: Util.isDesktop,
message: "Downloading MonKey png", message: "Downloading MonKey png",
onException: (e) {
didError = true;
String msg = e.toString();
while (msg.isNotEmpty &&
msg.startsWith("Exception:")) {
msg = msg.substring(10).trim();
}
showFloatingFlushBar(
type: FlushBarType.warning,
message: msg,
context: context,
);
},
); );
if (!didError && mounted) {
await showFloatingFlushBar(
type: FlushBarType.success,
message: "PNG MonKey image saved",
context: context,
);
}
}, },
), ),
], ],
@ -453,17 +482,37 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
child: PrimaryButton( child: PrimaryButton(
label: "Fetch MonKey", label: "Fetch MonKey",
onPressed: () async { onPressed: () async {
final future = Future.wait([
getMonkeyImage(receivingAddress),
Future<void>.delayed(const Duration(seconds: 2)),
]);
await showLoading( await showLoading(
whileFuture: future, whileFuture: Future.wait([
manager.currentReceivingAddress.then(
(address) async => await ref
.read(pMonKeyService)
.fetchMonKey(address: address)
.then(
(monKeyBytes) async =>
await _updateWalletMonKey(
monKeyBytes,
),
),
),
Future<void>.delayed(const Duration(seconds: 2)),
]),
context: context, context: context,
isDesktop: Util.isDesktop, isDesktop: Util.isDesktop,
message: "Fetching MonKey", message: "Fetching MonKey",
subMessage: "We are fetching your MonKey", subMessage: "We are fetching your MonKey",
onException: (e) {
String msg = e.toString();
while (msg.isNotEmpty &&
msg.startsWith("Exception:")) {
msg = msg.substring(10).trim();
}
showFloatingFlushBar(
type: FlushBarType.warning,
message: msg,
context: context,
);
},
); );
imageBytes = (manager.wallet as BananoWallet) imageBytes = (manager.wallet as BananoWallet)
@ -472,17 +521,6 @@ class _MonkeyViewState extends ConsumerState<MonkeyView> {
if (imageBytes != null) { if (imageBytes != null) {
setState(() {}); setState(() {});
} }
// if (isDesktop) {
// Navigator.of(context).popUntil(
// ModalRoute.withName(
// DesktopWalletView.routeName),
// );
// } else {
// Navigator.of(context).popUntil(
// ModalRoute.withName(WalletView.routeName),
// );
// }
}, },
), ),
), ),

View file

@ -1,21 +1,32 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:http/http.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
import 'package:stackwallet/models/isar/ordinal.dart'; import 'package:stackwallet/models/isar/ordinal.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/ordinals/widgets/dialogs.dart'; import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/background.dart'; import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
class OrdinalDetailsView extends StatefulWidget { class OrdinalDetailsView extends ConsumerStatefulWidget {
const OrdinalDetailsView({ const OrdinalDetailsView({
Key? key, Key? key,
required this.walletId, required this.walletId,
@ -28,14 +39,25 @@ class OrdinalDetailsView extends StatefulWidget {
static const routeName = "/ordinalDetailsView"; static const routeName = "/ordinalDetailsView";
@override @override
State<OrdinalDetailsView> createState() => _OrdinalDetailsViewState(); ConsumerState<OrdinalDetailsView> createState() => _OrdinalDetailsViewState();
} }
class _OrdinalDetailsViewState extends State<OrdinalDetailsView> { class _OrdinalDetailsViewState extends ConsumerState<OrdinalDetailsView> {
static const _spacing = 12.0; static const _spacing = 12.0;
late final UTXO? utxo;
@override
void initState() {
utxo = widget.ordinal.getUTXO(ref.read(mainDBProvider));
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId).coin));
return Background( return Background(
child: SafeArea( child: SafeArea(
child: Scaffold( child: Scaffold(
@ -73,26 +95,33 @@ class _OrdinalDetailsViewState extends State<OrdinalDetailsView> {
height: _spacing, height: _spacing,
), ),
_DetailsItemWCopy( _DetailsItemWCopy(
title: "ID", title: "Inscription ID",
data: widget.ordinal.inscriptionId, data: widget.ordinal.inscriptionId,
), ),
// const SizedBox(
// height: _spacing,
// ),
// // todo: add utxo status
const SizedBox( const SizedBox(
height: _spacing, height: _spacing,
), ),
// todo: add utxo status _DetailsItemWCopy(
const SizedBox(
height: _spacing,
),
const _DetailsItemWCopy(
title: "Amount", title: "Amount",
data: "TODO", // TODO infer from utxo utxoTXID:utxoVOUT data: utxo == null
? "ERROR"
: ref.watch(pAmountFormatter(coin)).format(
Amount(
rawValue: BigInt.from(utxo!.value),
fractionDigits: coin.decimals,
),
),
), ),
const SizedBox( const SizedBox(
height: _spacing, height: _spacing,
), ),
const _DetailsItemWCopy( _DetailsItemWCopy(
title: "Owner address", title: "Owner address",
data: "TODO", // infer from address associated w utxoTXID data: utxo?.address ?? "ERROR",
), ),
const SizedBox( const SizedBox(
height: _spacing, height: _spacing,
@ -150,11 +179,27 @@ class _DetailsItemWCopy extends StatelessWidget {
); );
} }
}, },
child: SvgPicture.asset( child: Row(
Assets.svg.copy, children: [
color: SvgPicture.asset(
Theme.of(context).extension<StackColors>()!.infoItemIcons, Assets.svg.copy,
width: 12, color: Theme.of(context)
.extension<StackColors>()!
.infoItemIcons,
width: 12,
),
const SizedBox(
width: 6,
),
Text(
"Copy",
style: STextStyles.infoSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.infoItemIcons,
),
),
],
), ),
), ),
], ],
@ -184,6 +229,36 @@ class _OrdinalImageGroup extends StatelessWidget {
static const _spacing = 12.0; static const _spacing = 12.0;
Future<void> _savePngToFile() async {
final response = await get(Uri.parse(ordinal.content));
if (response.statusCode != 200) {
throw Exception(
"statusCode=${response.statusCode} body=${response.bodyBytes}");
}
final bytes = response.bodyBytes;
if (Platform.isAndroid) {
await Permission.storage.request();
}
final dir = Platform.isAndroid
? Directory("/storage/emulated/0/Documents")
: await getApplicationDocumentsDirectory();
final docPath = dir.path;
final filePath = "$docPath/ordinal_${ordinal.inscriptionNumber}.png";
File imgFile = File(filePath);
if (imgFile.existsSync()) {
throw Exception("File already exists");
}
await imgFile.writeAsBytes(bytes);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
@ -197,15 +272,20 @@ class _OrdinalImageGroup extends StatelessWidget {
// const SizedBox( // const SizedBox(
// height: _spacing, // height: _spacing,
// ), // ),
AspectRatio( ClipRRect(
aspectRatio: 1, borderRadius: BorderRadius.circular(
child: Container( Constants.size.circularBorderRadius,
color: Colors.transparent, ),
child: Image.network( child: AspectRatio(
ordinal.content, // Use the preview URL as the image source aspectRatio: 1,
fit: BoxFit.cover, child: Container(
filterQuality: color: Colors.transparent,
FilterQuality.none, // Set the filter mode to nearest child: Image.network(
ordinal.content, // Use the preview URL as the image source
fit: BoxFit.cover,
filterQuality:
FilterQuality.none, // Set the filter mode to nearest
),
), ),
), ),
), ),
@ -227,8 +307,37 @@ class _OrdinalImageGroup extends StatelessWidget {
), ),
buttonHeight: ButtonHeight.l, buttonHeight: ButtonHeight.l,
iconSpacing: 4, iconSpacing: 4,
onPressed: () { onPressed: () async {
// TODO: save and download image to device bool didError = false;
await showLoading(
whileFuture: Future.wait([
_savePngToFile(),
Future<void>.delayed(const Duration(seconds: 2)),
]),
context: context,
isDesktop: true,
message: "Saving ordinal image",
onException: (e) {
didError = true;
String msg = e.toString();
while (msg.isNotEmpty && msg.startsWith("Exception:")) {
msg = msg.substring(10).trim();
}
showFloatingFlushBar(
type: FlushBarType.warning,
message: msg,
context: context,
);
},
);
if (!didError && context.mounted) {
await showFloatingFlushBar(
type: FlushBarType.success,
message: "Image saved",
context: context,
);
}
}, },
), ),
), ),

View file

@ -196,7 +196,10 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
case Coin.nano: case Coin.nano:
case Coin.banano: case Coin.banano:
//TODO: check network/node case Coin.stellar:
case Coin.stellarTestnet:
throw UnimplementedError();
//TODO: check network/node
} }
if (showFlushBar && mounted) { if (showFlushBar && mounted) {
@ -736,6 +739,8 @@ class _NodeFormState extends ConsumerState<NodeForm> {
case Coin.nano: case Coin.nano:
case Coin.banano: case Coin.banano:
case Coin.eCash: case Coin.eCash:
case Coin.stellar:
case Coin.stellarTestnet:
return false; return false;
case Coin.ethereum: case Coin.ethereum:

View file

@ -172,7 +172,10 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
case Coin.nano: case Coin.nano:
case Coin.banano: case Coin.banano:
//TODO: check network/node case Coin.stellar:
case Coin.stellarTestnet:
throw UnimplementedError();
//TODO: check network/node
} }
if (testPassed) { if (testPassed) {

View file

@ -1,12 +1,15 @@
import 'dart:async'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:http/http.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
import 'package:stackwallet/models/isar/ordinal.dart'; import 'package:stackwallet/models/isar/ordinal.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart'; import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/stack_colors.dart';
@ -15,10 +18,12 @@ import 'package:stackwallet/utilities/amount/amount_formatter.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
class DesktopOrdinalDetailsView extends ConsumerStatefulWidget { class DesktopOrdinalDetailsView extends ConsumerStatefulWidget {
@ -44,6 +49,36 @@ class _DesktopOrdinalDetailsViewState
late final UTXO? utxo; late final UTXO? utxo;
Future<void> _savePngToFile() async {
final response = await get(Uri.parse(widget.ordinal.content));
if (response.statusCode != 200) {
throw Exception(
"statusCode=${response.statusCode} body=${response.bodyBytes}");
}
final bytes = response.bodyBytes;
if (Platform.isAndroid) {
await Permission.storage.request();
}
final dir = Platform.isAndroid
? Directory("/storage/emulated/0/Documents")
: await getApplicationDocumentsDirectory();
final docPath = dir.path;
final filePath = "$docPath/ordinal_${widget.ordinal.inscriptionNumber}.png";
File imgFile = File(filePath);
if (imgFile.existsSync()) {
throw Exception("File already exists");
}
await imgFile.writeAsBytes(bytes);
}
@override @override
void initState() { void initState() {
utxo = widget.ordinal.getUTXO(ref.read(mainDBProvider)); utxo = widget.ordinal.getUTXO(ref.read(mainDBProvider));
@ -137,7 +172,7 @@ class _DesktopOrdinalDetailsViewState
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( SelectableText(
"INSC. ${widget.ordinal.inscriptionNumber}", "INSC. ${widget.ordinal.inscriptionNumber}",
style: STextStyles.w600_20(context), style: STextStyles.w600_20(context),
), ),
@ -174,74 +209,102 @@ class _DesktopOrdinalDetailsViewState
// const SizedBox( // const SizedBox(
// width: 16, // width: 16,
// ), // ),
// SecondaryButton( SecondaryButton(
// width: 150, width: 150,
// label: "Download", label: "Download",
// icon: SvgPicture.asset( icon: SvgPicture.asset(
// Assets.svg.arrowDown, Assets.svg.arrowDown,
// width: 13, width: 13,
// height: 18, height: 18,
// color: Theme.of(context) color: Theme.of(context)
// .extension<StackColors>()! .extension<StackColors>()!
// .buttonTextSecondary, .buttonTextSecondary,
// ), ),
// buttonHeight: ButtonHeight.l, buttonHeight: ButtonHeight.l,
// iconSpacing: 8, iconSpacing: 8,
// onPressed: () { onPressed: () async {
// // TODO: save and download image to device bool didError = false;
// }, await showLoading(
// ), whileFuture: Future.wait([
_savePngToFile(),
Future<void>.delayed(
const Duration(seconds: 2)),
]),
context: context,
isDesktop: true,
message: "Saving ordinal image",
onException: (e) {
didError = true;
String msg = e.toString();
while (msg.isNotEmpty &&
msg.startsWith("Exception:")) {
msg = msg.substring(10).trim();
}
showFloatingFlushBar(
type: FlushBarType.warning,
message: msg,
context: context,
);
},
);
if (!didError && mounted) {
await showFloatingFlushBar(
type: FlushBarType.success,
message: "Image saved",
context: context,
);
}
},
),
], ],
), ),
), ),
const SizedBox( const SizedBox(
height: 16, height: 16,
), ),
_DetailsItemWCopy( RoundedWhiteContainer(
title: "Inscription number", padding: const EdgeInsets.all(16),
data: widget.ordinal.inscriptionNumber.toString(), child: Column(
), mainAxisSize: MainAxisSize.min,
const SizedBox( children: [
height: _spacing, _DetailsItemWCopy(
), title: "Inscription number",
_DetailsItemWCopy( data: widget.ordinal.inscriptionNumber.toString(),
title: "Inscription ID", ),
data: widget.ordinal.inscriptionId, const _Divider(),
), _DetailsItemWCopy(
const SizedBox( title: "Inscription ID",
height: _spacing, data: widget.ordinal.inscriptionId,
), ),
// todo: add utxo status // const SizedBox(
const SizedBox( // height: _spacing,
height: _spacing, // ),
), // // todo: add utxo status
_DetailsItemWCopy( const _Divider(),
title: "Amount", _DetailsItemWCopy(
data: utxo == null title: "Amount",
? "ERROR" data: utxo == null
: ref.watch(pAmountFormatter(coin)).format( ? "ERROR"
Amount( : ref.watch(pAmountFormatter(coin)).format(
rawValue: BigInt.from(utxo!.value), Amount(
fractionDigits: coin.decimals, rawValue: BigInt.from(utxo!.value),
), fractionDigits: coin.decimals,
), ),
), ),
const SizedBox( ),
height: _spacing, const _Divider(),
), _DetailsItemWCopy(
_DetailsItemWCopy( title: "Owner address",
title: "Owner address", data: utxo?.address ?? "ERROR",
data: utxo?.address ?? "ERROR", ),
), const _Divider(),
const SizedBox( _DetailsItemWCopy(
height: _spacing, title: "Transaction ID",
), data: widget.ordinal.utxoTXID,
_DetailsItemWCopy( ),
title: "Transaction ID", ],
data: widget.ordinal.utxoTXID, ),
),
const SizedBox(
height: _spacing,
), ),
], ],
), ),
@ -255,6 +318,23 @@ class _DesktopOrdinalDetailsViewState
} }
} }
class _Divider extends StatelessWidget {
const _Divider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 16,
),
child: Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.backgroundAppBar,
),
);
}
}
class _DetailsItemWCopy extends StatelessWidget { class _DetailsItemWCopy extends StatelessWidget {
const _DetailsItemWCopy({ const _DetailsItemWCopy({
Key? key, Key? key,
@ -267,48 +347,29 @@ class _DetailsItemWCopy extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return RoundedWhiteContainer( return Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ Row(
Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
children: [ Text(
Text( title,
title, style: STextStyles.itemSubtitle(context),
style: STextStyles.itemSubtitle(context), ),
), IconCopyButton(
GestureDetector( data: data,
onTap: () async { ),
await Clipboard.setData(ClipboardData(text: data)); ],
if (context.mounted) { ),
unawaited( const SizedBox(
showFloatingFlushBar( height: 4,
type: FlushBarType.info, ),
message: "Copied to clipboard", SelectableText(
context: context, data,
), style: STextStyles.itemSubtitle12(context),
); ),
} ],
},
child: SvgPicture.asset(
Assets.svg.copy,
color:
Theme.of(context).extension<StackColors>()!.infoItemIcons,
width: 12,
),
),
],
),
const SizedBox(
height: 4,
),
SelectableText(
data,
style: STextStyles.itemSubtitle12(context),
),
],
),
); );
} }
} }

View file

@ -16,17 +16,12 @@ import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/mixins/ordinals_interface.dart'; import 'package:stackwallet/services/mixins/ordinals_interface.dart';
import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/show_loading.dart'; import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class DesktopOrdinalsView extends ConsumerStatefulWidget { class DesktopOrdinalsView extends ConsumerStatefulWidget {
const DesktopOrdinalsView({ const DesktopOrdinalsView({
@ -102,6 +97,8 @@ class _DesktopOrdinals extends ConsumerState<DesktopOrdinalsView> {
Assets.svg.ordinal, Assets.svg.ordinal,
width: 32, width: 32,
height: 32, height: 32,
color:
Theme.of(context).extension<StackColors>()!.textSubtitle1,
), ),
const SizedBox( const SizedBox(
width: 12, width: 12,

View file

@ -27,6 +27,7 @@ import 'package:stackwallet/services/coins/monero/monero_wallet.dart';
import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart'; import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart';
import 'package:stackwallet/services/coins/nano/nano_wallet.dart'; import 'package:stackwallet/services/coins/nano/nano_wallet.dart';
import 'package:stackwallet/services/coins/particl/particl_wallet.dart'; import 'package:stackwallet/services/coins/particl/particl_wallet.dart';
import 'package:stackwallet/services/coins/stellar/stellar_wallet.dart';
import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart'; import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/amount/amount.dart';
@ -218,6 +219,15 @@ abstract class CoinServiceAPI {
cachedClient: cachedClient, cachedClient: cachedClient,
tracker: tracker); tracker: tracker);
case Coin.stellar:
return StellarWallet(
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
tracker: tracker,
);
case Coin.wownero: case Coin.wownero:
return WowneroWallet( return WowneroWallet(
walletId: walletId, walletId: walletId,
@ -275,6 +285,15 @@ abstract class CoinServiceAPI {
cachedClient: cachedClient, cachedClient: cachedClient,
tracker: tracker, tracker: tracker,
); );
case Coin.stellarTestnet:
return StellarWallet(
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
tracker: tracker,
);
} }
} }

View file

@ -0,0 +1,809 @@
import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:isar/isar.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/balance.dart' as SWBalance;
import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'
as SWAddress;
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'
as SWTransaction;
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/services/mixins/wallet_cache.dart';
import 'package:stackwallet/services/mixins/wallet_db.dart';
import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart';
import 'package:tuple/tuple.dart';
const int MINIMUM_CONFIRMATIONS = 1;
class StellarWallet extends CoinServiceAPI with WalletCache, WalletDB {
late StellarSDK stellarSdk;
late Network stellarNetwork;
StellarWallet({
required String walletId,
required String walletName,
required Coin coin,
required TransactionNotificationTracker tracker,
required SecureStorageInterface secureStore,
MainDB? mockableOverride,
}) {
txTracker = tracker;
_walletId = walletId;
_walletName = walletName;
_coin = coin;
_secureStore = secureStore;
initCache(walletId, coin);
initWalletDB(mockableOverride: mockableOverride);
if (coin.name == "stellarTestnet") {
stellarSdk = StellarSDK.TESTNET;
stellarNetwork = Network.TESTNET;
} else {
stellarSdk = StellarSDK.PUBLIC;
stellarNetwork = Network.PUBLIC;
}
}
late final TransactionNotificationTracker txTracker;
late SecureStorageInterface _secureStore;
// final StellarSDK stellarSdk = StellarSDK.PUBLIC;
@override
bool get isFavorite => _isFavorite ??= getCachedIsFavorite();
bool? _isFavorite;
@override
set isFavorite(bool isFavorite) {
_isFavorite = isFavorite;
updateCachedIsFavorite(isFavorite);
}
@override
bool get shouldAutoSync => _shouldAutoSync;
bool _shouldAutoSync = true;
Timer? timer;
final _prefs = Prefs.instance;
@override
set shouldAutoSync(bool shouldAutoSync) {
if (_shouldAutoSync != shouldAutoSync) {
_shouldAutoSync = shouldAutoSync;
if (!shouldAutoSync) {
timer?.cancel();
timer = null;
stopNetworkAlivePinging();
} else {
startNetworkAlivePinging();
refresh();
}
}
}
Timer? _networkAliveTimer;
void startNetworkAlivePinging() {
// call once on start right away
_periodicPingCheck();
// then periodically check
_networkAliveTimer = Timer.periodic(
Constants.networkAliveTimerDuration,
(_) async {
_periodicPingCheck();
},
);
}
void stopNetworkAlivePinging() {
_networkAliveTimer?.cancel();
_networkAliveTimer = null;
}
void _periodicPingCheck() async {
bool hasNetwork = await testNetworkConnection();
if (_isConnected != hasNetwork) {
NodeConnectionStatus status = hasNetwork
? NodeConnectionStatus.connected
: NodeConnectionStatus.disconnected;
GlobalEventBus.instance.fire(
NodeConnectionStatusChangedEvent(
status,
walletId,
coin,
),
);
_isConnected = hasNetwork;
if (hasNetwork) {
unawaited(refresh());
}
}
}
@override
String get walletName => _walletName;
late String _walletName;
@override
set walletName(String name) => _walletName = name;
@override
SWBalance.Balance get balance => _balance ??= getCachedBalance();
SWBalance.Balance? _balance;
@override
Coin get coin => _coin;
late Coin _coin;
Future<bool> _accountExists(String accountId) async {
bool exists = false;
try {
AccountResponse receiverAccount =
await stellarSdk.accounts.account(accountId);
if (receiverAccount.accountId != "") {
exists = true;
}
} catch (e, s) {
Logging.instance.log(
"Error getting account ${e.toString()} - ${s.toString()}",
level: LogLevel.Error);
}
return exists;
}
@override
Future<String> confirmSend({required Map<String, dynamic> txData}) async {
final secretSeed = await _secureStore.read(key: '${_walletId}_secretSeed');
KeyPair senderKeyPair = KeyPair.fromSecretSeed(secretSeed!);
AccountResponse sender =
await stellarSdk.accounts.account(senderKeyPair.accountId);
final amountToSend = txData['recipientAmt'] as Amount;
//First check if account exists, can be skipped, but if the account does not exist,
// the transaction fee will be charged when the transaction fails.
bool validAccount = await _accountExists(txData['address'] as String);
Transaction transaction;
if (!validAccount) {
//Fund the account, user must ensure account is correct
CreateAccountOperationBuilder createAccBuilder =
CreateAccountOperationBuilder(
txData['address'] as String, amountToSend.decimal.toString());
transaction = TransactionBuilder(sender)
.addOperation(createAccBuilder.build())
.build();
} else {
transaction = TransactionBuilder(sender)
.addOperation(PaymentOperationBuilder(txData['address'] as String,
Asset.NATIVE, amountToSend.decimal.toString())
.build())
.build();
}
transaction.sign(senderKeyPair, stellarNetwork);
try {
SubmitTransactionResponse response =
await stellarSdk.submitTransaction(transaction);
if (!response.success) {
throw ("Unable to send transaction");
}
return response.hash!;
} catch (e, s) {
Logging.instance.log("Error sending TX $e - $s", level: LogLevel.Error);
rethrow;
}
}
Future<SWAddress.Address?> get _currentReceivingAddress => db
.getAddresses(walletId)
.filter()
.typeEqualTo(SWAddress.AddressType.unknown)
.and()
.subTypeEqualTo(SWAddress.AddressSubType.unknown)
.sortByDerivationIndexDesc()
.findFirst();
@override
Future<String> get currentReceivingAddress async =>
(await _currentReceivingAddress)?.value ?? await getAddressSW();
Future<int> getBaseFee() async {
// final nodeURI = Uri.parse("${getCurrentNode().host}:${getCurrentNode().port}");
final nodeURI = Uri.parse(getCurrentNode().host);
final httpClient = http.Client();
FeeStatsResponse fsp =
await FeeStatsRequestBuilder(httpClient, nodeURI).execute();
return int.parse(fsp.lastLedgerBaseFee);
}
@override
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
var baseFee = await getBaseFee();
int fee = 100;
switch (feeRate) {
case 0:
fee = baseFee * 10;
case 1:
case 2:
fee = baseFee * 50;
case 3:
fee = baseFee * 100;
case 4:
fee = baseFee * 200;
default:
fee = baseFee * 50;
}
return Amount(rawValue: BigInt.from(fee), fractionDigits: coin.decimals);
}
@override
Future<void> exit() async {
_hasCalledExit = true;
timer?.cancel();
timer = null;
stopNetworkAlivePinging();
}
NodeModel? _xlmNode;
NodeModel getCurrentNode() {
if (_xlmNode != null) {
return _xlmNode!;
} else if (NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin) !=
null) {
return NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin)!;
} else {
return DefaultNodes.getNodeFor(coin);
}
}
@override
Future<FeeObject> get fees async {
// final nodeURI = Uri.parse("${getCurrentNode().host}:${getCurrentNode().port}");
final nodeURI = Uri.parse(getCurrentNode().host);
final httpClient = http.Client();
FeeStatsResponse fsp =
await FeeStatsRequestBuilder(httpClient, nodeURI).execute();
return FeeObject(
numberOfBlocksFast: 0,
numberOfBlocksAverage: 0,
numberOfBlocksSlow: 0,
fast: int.parse(fsp.lastLedgerBaseFee) * 100,
medium: int.parse(fsp.lastLedgerBaseFee) * 50,
slow: int.parse(fsp.lastLedgerBaseFee) * 10);
}
@override
Future<void> fullRescan(
int maxUnusedAddressGap, int maxNumberOfIndexesToCheck) async {
await _prefs.init();
await updateTransactions();
await updateChainHeight();
await updateBalance();
}
@override
Future<bool> generateNewAddress() {
// TODO: implement generateNewAddress
throw UnimplementedError();
}
@override
bool get hasCalledExit => _hasCalledExit;
bool _hasCalledExit = false;
@override
Future<void> initializeExisting() async {
await _prefs.init();
}
@override
Future<void> initializeNew() async {
if ((await mnemonicString) != null || (await mnemonicPassphrase) != null) {
throw Exception(
"Attempted to overwrite mnemonic on generate new wallet!");
}
await _prefs.init();
String mnemonic = await Wallet.generate24WordsMnemonic();
await _secureStore.write(key: '${_walletId}_mnemonic', value: mnemonic);
await _secureStore.write(key: '${_walletId}_mnemonicPassphrase', value: "");
Wallet wallet = await Wallet.from(mnemonic);
KeyPair keyPair = await wallet.getKeyPair(index: 0);
String address = keyPair.accountId;
String secretSeed =
keyPair.secretSeed; //This will be required for sending a tx
await _secureStore.write(key: '${_walletId}_secretSeed', value: secretSeed);
final swAddress = SWAddress.Address(
walletId: walletId,
value: address,
publicKey: keyPair.publicKey,
derivationIndex: 0,
derivationPath: null,
type: SWAddress.AddressType.unknown, // TODO: set type
subType: SWAddress.AddressSubType.unknown);
await db.putAddress(swAddress);
await Future.wait(
[updateCachedId(walletId), updateCachedIsFavorite(false)]);
}
Future<String> getAddressSW() async {
var mnemonic = await _secureStore.read(key: '${_walletId}_mnemonic');
Wallet wallet = await Wallet.from(mnemonic!);
KeyPair keyPair = await wallet.getKeyPair(index: 0);
return Future.value(keyPair.accountId);
}
@override
bool get isConnected => _isConnected;
bool _isConnected = false;
@override
bool get isRefreshing => refreshMutex;
bool refreshMutex = false;
@override
// TODO: implement maxFee
Future<int> get maxFee => throw UnimplementedError();
@override
Future<List<String>> get mnemonic =>
mnemonicString.then((value) => value!.split(" "));
@override
Future<String?> get mnemonicPassphrase =>
_secureStore.read(key: '${_walletId}_mnemonicPassphrase');
@override
Future<String?> get mnemonicString =>
_secureStore.read(key: '${_walletId}_mnemonic');
@override
Future<Map<String, dynamic>> prepareSend(
{required String address,
required Amount amount,
Map<String, dynamic>? args}) async {
try {
final feeRate = args?["feeRate"];
var fee = 1000;
if (feeRate is FeeRateType) {
final theFees = await fees;
switch (feeRate) {
case FeeRateType.fast:
fee = theFees.fast;
case FeeRateType.slow:
fee = theFees.slow;
case FeeRateType.average:
default:
fee = theFees.medium;
}
}
Map<String, dynamic> txData = {
"fee": fee,
"address": address,
"recipientAmt": amount,
};
Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
return txData;
} catch (e, s) {
Logging.instance.log("Error getting fees $e - $s", level: LogLevel.Error);
rethrow;
}
}
@override
Future<void> recoverFromMnemonic(
{required String mnemonic,
String? mnemonicPassphrase,
required int maxUnusedAddressGap,
required int maxNumberOfIndexesToCheck,
required int height}) async {
if ((await mnemonicString) != null ||
(await this.mnemonicPassphrase) != null) {
throw Exception("Attempted to overwrite mnemonic on restore!");
}
var wallet = await Wallet.from(mnemonic);
var keyPair = await wallet.getKeyPair(index: 0);
var address = keyPair.accountId;
var secretSeed = keyPair.secretSeed;
await _secureStore.write(
key: '${_walletId}_mnemonic', value: mnemonic.trim());
await _secureStore.write(
key: '${_walletId}_mnemonicPassphrase',
value: mnemonicPassphrase ?? "",
);
await _secureStore.write(key: '${_walletId}_secretSeed', value: secretSeed);
final swAddress = SWAddress.Address(
walletId: walletId,
value: address,
publicKey: keyPair.publicKey,
derivationIndex: 0,
derivationPath: null,
type: SWAddress.AddressType.unknown, // TODO: set type
subType: SWAddress.AddressSubType.unknown);
await db.putAddress(swAddress);
await Future.wait(
[updateCachedId(walletId), updateCachedIsFavorite(false)]);
}
Future<void> updateChainHeight() async {
final height = await stellarSdk.ledgers
.order(RequestBuilderOrder.DESC)
.limit(1)
.execute()
.then((value) => value.records!.first.sequence)
.onError((error, stackTrace) => throw ("Error getting chain height"));
await updateCachedChainHeight(height);
}
Future<void> updateTransactions() async {
try {
List<Tuple2<SWTransaction.Transaction, SWAddress.Address?>>
transactionList = [];
Page<OperationResponse> payments = await stellarSdk.payments
.forAccount(await getAddressSW())
.order(RequestBuilderOrder.DESC)
.execute()
.onError(
(error, stackTrace) => throw ("Could not fetch transactions"));
for (OperationResponse response in payments.records!) {
// PaymentOperationResponse por;
if (response is PaymentOperationResponse) {
PaymentOperationResponse por = response;
Logging.instance.log(
"ALL TRANSACTIONS IS ${por.transactionSuccessful}",
level: LogLevel.Info);
Logging.instance.log("THIS TX HASH IS ${por.transactionHash}",
level: LogLevel.Info);
SWTransaction.TransactionType type;
if (por.sourceAccount == await getAddressSW()) {
type = SWTransaction.TransactionType.outgoing;
} else {
type = SWTransaction.TransactionType.incoming;
}
final amount = Amount(
rawValue: BigInt.parse(float
.parse(por.amount!)
.toStringAsFixed(coin.decimals)
.replaceAll(".", "")),
fractionDigits: coin.decimals,
);
int fee = 0;
int height = 0;
//Query the transaction linked to the payment,
// por.transaction returns a null sometimes
TransactionResponse tx =
await stellarSdk.transactions.transaction(por.transactionHash!);
if (tx.hash.isNotEmpty) {
fee = tx.feeCharged!;
height = tx.ledger;
}
var theTransaction = SWTransaction.Transaction(
walletId: walletId,
txid: por.transactionHash!,
timestamp:
DateTime.parse(por.createdAt!).millisecondsSinceEpoch ~/ 1000,
type: type,
subType: SWTransaction.TransactionSubType.none,
amount: 0,
amountString: amount.toJsonString(),
fee: fee,
height: height,
isCancelled: false,
isLelantus: false,
slateId: "",
otherData: "",
inputs: [],
outputs: [],
nonce: 0,
numberOfMessages: null,
);
SWAddress.Address? receivingAddress = await _currentReceivingAddress;
SWAddress.Address address =
type == SWTransaction.TransactionType.incoming
? receivingAddress!
: SWAddress.Address(
walletId: walletId,
value: por.sourceAccount!,
publicKey:
KeyPair.fromAccountId(por.sourceAccount!).publicKey,
derivationIndex: 0,
derivationPath: null,
type: SWAddress.AddressType.unknown, // TODO: set type
subType: SWAddress.AddressSubType.unknown);
Tuple2<SWTransaction.Transaction, SWAddress.Address> tuple =
Tuple2(theTransaction, address);
transactionList.add(tuple);
} else if (response is CreateAccountOperationResponse) {
CreateAccountOperationResponse caor = response;
SWTransaction.TransactionType type;
if (caor.sourceAccount == await getAddressSW()) {
type = SWTransaction.TransactionType.outgoing;
} else {
type = SWTransaction.TransactionType.incoming;
}
final amount = Amount(
rawValue: BigInt.parse(float
.parse(caor.startingBalance!)
.toStringAsFixed(coin.decimals)
.replaceAll(".", "")),
fractionDigits: coin.decimals,
);
int fee = 0;
int height = 0;
TransactionResponse tx =
await stellarSdk.transactions.transaction(caor.transactionHash!);
if (tx.hash.isNotEmpty) {
fee = tx.feeCharged!;
height = tx.ledger;
}
var theTransaction = SWTransaction.Transaction(
walletId: walletId,
txid: caor.transactionHash!,
timestamp:
DateTime.parse(caor.createdAt!).millisecondsSinceEpoch ~/ 1000,
type: type,
subType: SWTransaction.TransactionSubType.none,
amount: 0,
amountString: amount.toJsonString(),
fee: fee,
height: height,
isCancelled: false,
isLelantus: false,
slateId: "",
otherData: "",
inputs: [],
outputs: [],
nonce: 0,
numberOfMessages: null,
);
SWAddress.Address? receivingAddress = await _currentReceivingAddress;
SWAddress.Address address =
type == SWTransaction.TransactionType.incoming
? receivingAddress!
: SWAddress.Address(
walletId: walletId,
value: caor.sourceAccount!,
publicKey:
KeyPair.fromAccountId(caor.sourceAccount!).publicKey,
derivationIndex: 0,
derivationPath: null,
type: SWAddress.AddressType.unknown, // TODO: set type
subType: SWAddress.AddressSubType.unknown);
Tuple2<SWTransaction.Transaction, SWAddress.Address> tuple =
Tuple2(theTransaction, address);
transactionList.add(tuple);
}
}
await db.addNewTransactionData(transactionList, walletId);
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from updateTransactions(): $e\n$s",
level: LogLevel.Error);
}
}
Future<void> updateBalance() async {
try {
AccountResponse accountResponse =
await stellarSdk.accounts.account(await getAddressSW());
for (Balance balance in accountResponse.balances) {
switch (balance.assetType) {
case Asset.TYPE_NATIVE:
_balance = SWBalance.Balance(
total: Amount(
rawValue: BigInt.from(float.parse(balance.balance) * 10000000),
fractionDigits: coin.decimals,
),
spendable: Amount(
rawValue: BigInt.from(float.parse(balance.balance) * 10000000),
fractionDigits: coin.decimals,
),
blockedTotal: Amount(
rawValue: BigInt.from(0),
fractionDigits: coin.decimals,
),
pendingSpendable: Amount(
rawValue: BigInt.from(0),
fractionDigits: coin.decimals,
),
);
Logging.instance.log(_balance, level: LogLevel.Info);
await updateCachedBalance(_balance!);
}
}
} catch (e, s) {
Logging.instance.log(
"ERROR GETTING BALANCE $e\n$s",
level: LogLevel.Info,
);
}
}
@override
Future<void> refresh() async {
if (refreshMutex) {
Logging.instance.log(
"$walletId $walletName refreshMutex denied",
level: LogLevel.Info,
);
return;
} else {
refreshMutex = true;
}
try {
await _prefs.init();
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.syncing,
walletId,
coin,
),
);
await updateChainHeight();
await updateTransactions();
await updateBalance();
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.synced,
walletId,
coin,
),
);
if (shouldAutoSync) {
timer ??= Timer.periodic(const Duration(seconds: 30), (timer) async {
Logging.instance.log(
"Periodic refresh check for $walletId $walletName in object instance: $hashCode",
level: LogLevel.Info);
await refresh();
GlobalEventBus.instance.fire(
UpdatedInBackgroundEvent(
"New data found in $walletId $walletName in background!",
walletId,
),
);
});
}
} catch (e, s) {
Logging.instance.log(
"Failed to refresh stellar wallet $walletId: '$walletName': $e\n$s",
level: LogLevel.Warning,
);
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.unableToSync,
walletId,
coin,
),
);
}
refreshMutex = false;
}
@override
int get storedChainHeight => getCachedChainHeight();
@override
Future<bool> testNetworkConnection() {
// TODO: implement testNetworkConnection
throw UnimplementedError();
}
@override
Future<List<SWTransaction.Transaction>> get transactions =>
db.getTransactions(walletId).findAll();
@override
Future<void> updateNode(bool shouldRefresh) async {
_xlmNode = NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin) ??
DefaultNodes.getNodeFor(coin);
if (shouldRefresh) {
unawaited(refresh());
}
}
@override
Future<void> updateSentCachedTxData(Map<String, dynamic> txData) async {
final transaction = SWTransaction.Transaction(
walletId: walletId,
txid: txData["txid"] as String,
timestamp: DateTime.now().millisecondsSinceEpoch ~/ 1000,
type: SWTransaction.TransactionType.outgoing,
subType: SWTransaction.TransactionSubType.none,
// precision may be lost here hence the following amountString
amount: (txData["recipientAmt"] as Amount).raw.toInt(),
amountString: (txData["recipientAmt"] as Amount).toJsonString(),
fee: txData["fee"] as int,
height: null,
isCancelled: false,
isLelantus: false,
otherData: null,
slateId: null,
nonce: null,
inputs: [],
outputs: [],
numberOfMessages: null,
);
final address = txData["address"] is String
? await db.getAddress(walletId, txData["address"] as String)
: null;
await db.addNewTransactionData(
[
Tuple2(transaction, address),
],
walletId,
);
}
@override
// TODO: implement utxos
Future<List<UTXO>> get utxos => throw UnimplementedError();
@override
bool validateAddress(String address) {
return RegExp(r"^[G][A-Z0-9]{55}$").hasMatch(address);
}
@override
String get walletId => _walletId;
late String _walletId;
}

View file

@ -0,0 +1,40 @@
import 'dart:typed_data';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:stackwallet/utilities/logger.dart';
final pMonKeyService = Provider((ref) => MonKeyService());
class MonKeyService {
static const baseURL = "https://monkey.banano.cc/api/v1/monkey/";
Future<Uint8List> fetchMonKey({
required String address,
bool png = false,
}) async {
try {
String url = "https://monkey.banano.cc/api/v1/monkey/$address";
if (png) {
url += '?format=png&size=512&background=false';
}
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
return response.bodyBytes;
} else {
throw Exception(
"statusCode=${response.statusCode} body=${response.body}",
);
}
} catch (e, s) {
Logging.instance.log(
"Failed fetchMonKey($address): $e\n$s",
level: LogLevel.Error,
);
rethrow;
}
}
}

View file

@ -100,7 +100,7 @@ class PriceAPI {
Uri.parse("https://api.coingecko.com/api/v3/coins/markets?vs_currency" Uri.parse("https://api.coingecko.com/api/v3/coins/markets?vs_currency"
"=${baseCurrency.toLowerCase()}" "=${baseCurrency.toLowerCase()}"
"&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano" "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"); "&order=market_cap_desc&per_page=50&page=1&sparkline=false");
final coinGeckoResponse = await client.get( final coinGeckoResponse = await client.get(

View file

@ -28,6 +28,7 @@ class CoinThemeColorDefault {
Color get namecoin => const Color(0xFF91B1E1); Color get namecoin => const Color(0xFF91B1E1);
Color get wownero => const Color(0xFFED80C1); Color get wownero => const Color(0xFFED80C1);
Color get particl => const Color(0xFF8175BD); Color get particl => const Color(0xFF8175BD);
Color get stellar => const Color(0xFF6600FF);
Color get nano => const Color(0xFF209CE9); Color get nano => const Color(0xFF209CE9);
Color get banano => const Color(0xFFFBDD11); Color get banano => const Color(0xFFFBDD11);
@ -62,6 +63,9 @@ class CoinThemeColorDefault {
return wownero; return wownero;
case Coin.particl: case Coin.particl:
return particl; return particl;
case Coin.stellar:
case Coin.stellarTestnet:
return stellar;
case Coin.nano: case Coin.nano:
return nano; return nano;
case Coin.banano: case Coin.banano:

View file

@ -1707,6 +1707,9 @@ class StackColors extends ThemeExtension<StackColors> {
return _coin.wownero; return _coin.wownero;
case Coin.particl: case Coin.particl:
return _coin.particl; return _coin.particl;
case Coin.stellar:
case Coin.stellarTestnet:
return _coin.stellar;
case Coin.nano: case Coin.nano:
return _coin.nano; return _coin.nano;
case Coin.banano: case Coin.banano:

View file

@ -27,7 +27,7 @@ final pThemeService = Provider<ThemeService>((ref) {
}); });
class ThemeService { class ThemeService {
static const _currentDefaultThemeVersion = 3; static const _currentDefaultThemeVersion = 4;
ThemeService._(); ThemeService._();
static ThemeService? _instance; static ThemeService? _instance;
static ThemeService get instance => _instance ??= ThemeService._(); static ThemeService get instance => _instance ??= ThemeService._();

View file

@ -105,6 +105,8 @@ class AddressUtils {
return Address.validateAddress(address, namecoin, namecoin.bech32!); return Address.validateAddress(address, namecoin, namecoin.bech32!);
case Coin.particl: case Coin.particl:
return Address.validateAddress(address, particl); return Address.validateAddress(address, particl);
case Coin.stellar:
return RegExp(r"^[G][A-Z0-9]{55}$").hasMatch(address);
case Coin.nano: case Coin.nano:
return NanoAccounts.isValid(NanoAccountType.NANO, address); return NanoAccounts.isValid(NanoAccountType.NANO, address);
case Coin.banano: case Coin.banano:
@ -139,6 +141,8 @@ class AddressUtils {
return Address.validateAddress(address, firoTestNetwork); return Address.validateAddress(address, firoTestNetwork);
case Coin.dogecoinTestNet: case Coin.dogecoinTestNet:
return Address.validateAddress(address, dogecointestnet); return Address.validateAddress(address, dogecointestnet);
case Coin.stellarTestnet:
return RegExp(r"^[G][A-Z0-9]{55}$").hasMatch(address);
} }
} }

View file

@ -50,6 +50,8 @@ enum AmountUnit {
case Coin.dogecoin: case Coin.dogecoin:
case Coin.eCash: case Coin.eCash:
case Coin.epicCash: case Coin.epicCash:
case Coin.stellar: // TODO: check if this is correct
case Coin.stellarTestnet:
return AmountUnit.values.sublist(0, 4); return AmountUnit.values.sublist(0, 4);
case Coin.monero: case Coin.monero:

View file

@ -54,10 +54,14 @@ Uri getDefaultBlockExplorerUrlFor({
return Uri.parse("https://chainz.cryptoid.info/nmc/tx.dws?$txid.htm"); return Uri.parse("https://chainz.cryptoid.info/nmc/tx.dws?$txid.htm");
case Coin.particl: case Coin.particl:
return Uri.parse("https://chainz.cryptoid.info/part/tx.dws?$txid.htm"); return Uri.parse("https://chainz.cryptoid.info/part/tx.dws?$txid.htm");
case Coin.stellar:
return Uri.parse("https://stellarchain.io/tx/$txid");
case Coin.nano: case Coin.nano:
return Uri.parse("https://www.nanolooker.com/block/$txid"); return Uri.parse("https://www.nanolooker.com/block/$txid");
case Coin.banano: case Coin.banano:
return Uri.parse("https://www.bananolooker.com/block/$txid"); return Uri.parse("https://www.bananolooker.com/block/$txid");
case Coin.stellarTestnet:
return Uri.parse("https://testnet.stellarchain.io/transactions/$txid");
} }
} }

View file

@ -43,6 +43,7 @@ abstract class Constants {
BigInt.parse("1000000000000000000000000000000"); // 1*10^30 BigInt.parse("1000000000000000000000000000000"); // 1*10^30
static final BigInt _satsPerCoinBanano = static final BigInt _satsPerCoinBanano =
BigInt.parse("100000000000000000000000000000"); // 1*10^29 BigInt.parse("100000000000000000000000000000"); // 1*10^29
static final BigInt _satsPerCoinStellar = BigInt.from(10000000); // https://developers.stellar.org/docs/fundamentals-and-concepts/stellar-data-structures/assets#amount-precision
static final BigInt _satsPerCoin = BigInt.from(100000000); static final BigInt _satsPerCoin = BigInt.from(100000000);
static const int _decimalPlaces = 8; static const int _decimalPlaces = 8;
static const int _decimalPlacesNano = 30; static const int _decimalPlacesNano = 30;
@ -51,6 +52,7 @@ abstract class Constants {
static const int _decimalPlacesMonero = 12; static const int _decimalPlacesMonero = 12;
static const int _decimalPlacesEthereum = 18; static const int _decimalPlacesEthereum = 18;
static const int _decimalPlacesECash = 2; static const int _decimalPlacesECash = 2;
static const int _decimalPlacesStellar = 7;
static const int notificationsMax = 0xFFFFFFFF; static const int notificationsMax = 0xFFFFFFFF;
static const Duration networkAliveTimerDuration = Duration(seconds: 10); static const Duration networkAliveTimerDuration = Duration(seconds: 10);
@ -96,6 +98,10 @@ abstract class Constants {
case Coin.eCash: case Coin.eCash:
return _satsPerCoinECash; return _satsPerCoinECash;
case Coin.stellar:
case Coin.stellarTestnet:
return _satsPerCoinStellar;
} }
} }
@ -133,6 +139,10 @@ abstract class Constants {
case Coin.eCash: case Coin.eCash:
return _decimalPlacesECash; return _decimalPlacesECash;
case Coin.stellar:
case Coin.stellarTestnet:
return _decimalPlacesStellar;
} }
} }
@ -155,6 +165,8 @@ abstract class Constants {
case Coin.namecoin: case Coin.namecoin:
case Coin.particl: case Coin.particl:
case Coin.nano: case Coin.nano:
case Coin.stellar:
case Coin.stellarTestnet:
values.addAll([24, 12]); values.addAll([24, 12]);
break; break;
case Coin.banano: case Coin.banano:
@ -214,6 +226,10 @@ abstract class Constants {
case Coin.nano: // TODO: Verify this case Coin.nano: // TODO: Verify this
case Coin.banano: // TODO: Verify this case Coin.banano: // TODO: Verify this
return 1; return 1;
case Coin.stellar:
case Coin.stellarTestnet:
return 5;
} }
} }
@ -241,6 +257,8 @@ abstract class Constants {
case Coin.nano: case Coin.nano:
case Coin.banano: case Coin.banano:
case Coin.stellar:
case Coin.stellarTestnet:
return 24; return 24;
case Coin.monero: case Coin.monero:

View file

@ -34,6 +34,7 @@ abstract class DefaultNodes {
bitcoincashTestnet, bitcoincashTestnet,
dogecoinTestnet, dogecoinTestnet,
firoTestnet, firoTestnet,
stellarTestnet,
]; ];
static NodeModel get bitcoin => NodeModel( static NodeModel get bitcoin => NodeModel(
@ -181,6 +182,18 @@ abstract class DefaultNodes {
isFailover: true, isFailover: true,
isDown: false); isDown: false);
static NodeModel get stellar => NodeModel(
host: "https://horizon.stellar.org",
port: 443,
name: defaultName,
id: _nodeId(Coin.stellar),
useSSL: false,
enabled: true,
coinName: Coin.stellar.name,
isFailover: true,
isDown: false
);
static NodeModel get nano => NodeModel( static NodeModel get nano => NodeModel(
host: "https://rainstorm.city/api", host: "https://rainstorm.city/api",
port: 443, port: 443,
@ -263,6 +276,18 @@ abstract class DefaultNodes {
isDown: false, isDown: false,
); );
static NodeModel get stellarTestnet => NodeModel(
host: "https://horizon-testnet.stellar.org/",
port: 50022,
name: defaultName,
id: _nodeId(Coin.stellarTestnet),
useSSL: true,
enabled: true,
coinName: Coin.stellarTestnet.name,
isFailover: true,
isDown: false,
);
static NodeModel getNodeFor(Coin coin) { static NodeModel getNodeFor(Coin coin) {
switch (coin) { switch (coin) {
case Coin.bitcoin: case Coin.bitcoin:
@ -301,6 +326,9 @@ abstract class DefaultNodes {
case Coin.particl: case Coin.particl:
return particl; return particl;
case Coin.stellar:
return stellar;
case Coin.nano: case Coin.nano:
return nano; return nano;
@ -321,6 +349,9 @@ abstract class DefaultNodes {
case Coin.dogecoinTestNet: case Coin.dogecoinTestNet:
return dogecoinTestnet; return dogecoinTestnet;
case Coin.stellarTestnet:
return stellarTestnet;
} }
} }
} }

View file

@ -27,6 +27,7 @@ import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart'
import 'package:stackwallet/services/coins/nano/nano_wallet.dart' as nano; import 'package:stackwallet/services/coins/nano/nano_wallet.dart' as nano;
import 'package:stackwallet/services/coins/particl/particl_wallet.dart' import 'package:stackwallet/services/coins/particl/particl_wallet.dart'
as particl; as particl;
import 'package:stackwallet/services/coins/stellar/stellar_wallet.dart' as xlm;
import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart' as wow; import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart' as wow;
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
@ -44,6 +45,7 @@ enum Coin {
namecoin, namecoin,
nano, nano,
particl, particl,
stellar,
wownero, wownero,
/// ///
@ -56,6 +58,7 @@ enum Coin {
dogecoinTestNet, dogecoinTestNet,
firoTestNet, firoTestNet,
litecoinTestNet, litecoinTestNet,
stellarTestnet,
} }
final int kTestNetCoinCount = 4; // Util.isDesktop ? 5 : 4; final int kTestNetCoinCount = 4; // Util.isDesktop ? 5 : 4;
@ -84,6 +87,8 @@ extension CoinExt on Coin {
return "Monero"; return "Monero";
case Coin.particl: case Coin.particl:
return "Particl"; return "Particl";
case Coin.stellar:
return "Stellar";
case Coin.wownero: case Coin.wownero:
return "Wownero"; return "Wownero";
case Coin.namecoin: case Coin.namecoin:
@ -102,6 +107,8 @@ extension CoinExt on Coin {
return "tFiro"; return "tFiro";
case Coin.dogecoinTestNet: case Coin.dogecoinTestNet:
return "tDogecoin"; return "tDogecoin";
case Coin.stellarTestnet:
return "tStellar";
} }
} }
@ -127,6 +134,8 @@ extension CoinExt on Coin {
return "XMR"; return "XMR";
case Coin.particl: case Coin.particl:
return "PART"; return "PART";
case Coin.stellar:
return "XLM";
case Coin.wownero: case Coin.wownero:
return "WOW"; return "WOW";
case Coin.namecoin: case Coin.namecoin:
@ -145,6 +154,8 @@ extension CoinExt on Coin {
return "tFIRO"; return "tFIRO";
case Coin.dogecoinTestNet: case Coin.dogecoinTestNet:
return "tDOGE"; return "tDOGE";
case Coin.stellarTestnet:
return "tXLM";
} }
} }
@ -171,6 +182,8 @@ extension CoinExt on Coin {
return "monero"; return "monero";
case Coin.particl: case Coin.particl:
return "particl"; return "particl";
case Coin.stellar:
return "stellar";
case Coin.wownero: case Coin.wownero:
return "wownero"; return "wownero";
case Coin.namecoin: case Coin.namecoin:
@ -189,6 +202,8 @@ extension CoinExt on Coin {
return "firo"; return "firo";
case Coin.dogecoinTestNet: case Coin.dogecoinTestNet:
return "dogecoin"; return "dogecoin";
case Coin.stellarTestnet:
return "stellar";
} }
} }
@ -215,6 +230,8 @@ extension CoinExt on Coin {
case Coin.wownero: case Coin.wownero:
case Coin.nano: case Coin.nano:
case Coin.banano: case Coin.banano:
case Coin.stellar:
case Coin.stellarTestnet:
return false; return false;
} }
} }
@ -242,6 +259,8 @@ extension CoinExt on Coin {
case Coin.firoTestNet: case Coin.firoTestNet:
case Coin.nano: case Coin.nano:
case Coin.banano: case Coin.banano:
case Coin.stellar:
case Coin.stellarTestnet:
return false; return false;
} }
} }
@ -262,6 +281,7 @@ extension CoinExt on Coin {
case Coin.nano: case Coin.nano:
case Coin.banano: case Coin.banano:
case Coin.eCash: case Coin.eCash:
case Coin.stellar:
return false; return false;
case Coin.dogecoinTestNet: case Coin.dogecoinTestNet:
@ -269,6 +289,7 @@ extension CoinExt on Coin {
case Coin.litecoinTestNet: case Coin.litecoinTestNet:
case Coin.bitcoincashTestnet: case Coin.bitcoincashTestnet:
case Coin.firoTestNet: case Coin.firoTestNet:
case Coin.stellarTestnet:
return true; return true;
} }
} }
@ -289,6 +310,7 @@ extension CoinExt on Coin {
case Coin.nano: case Coin.nano:
case Coin.banano: case Coin.banano:
case Coin.eCash: case Coin.eCash:
case Coin.stellar:
return this; return this;
case Coin.dogecoinTestNet: case Coin.dogecoinTestNet:
@ -305,6 +327,9 @@ extension CoinExt on Coin {
case Coin.firoTestNet: case Coin.firoTestNet:
return Coin.firo; return Coin.firo;
case Coin.stellarTestnet:
return Coin.stellar;
} }
} }
@ -345,6 +370,10 @@ extension CoinExt on Coin {
case Coin.particl: case Coin.particl:
return particl.MINIMUM_CONFIRMATIONS; return particl.MINIMUM_CONFIRMATIONS;
case Coin.stellar:
case Coin.stellarTestnet:
return xlm.MINIMUM_CONFIRMATIONS;
case Coin.wownero: case Coin.wownero:
return wow.MINIMUM_CONFIRMATIONS; return wow.MINIMUM_CONFIRMATIONS;
@ -404,6 +433,10 @@ Coin coinFromPrettyName(String name) {
case "particl": case "particl":
return Coin.particl; return Coin.particl;
case "Stellar":
case "stellar":
return Coin.stellar;
case "Namecoin": case "Namecoin":
case "namecoin": case "namecoin":
return Coin.namecoin; return Coin.namecoin;
@ -448,6 +481,11 @@ Coin coinFromPrettyName(String name) {
case "banano": case "banano":
return Coin.banano; return Coin.banano;
case "Stellar Testnet":
case "stellarTestnet":
case "tStellar":
return Coin.stellarTestnet;
default: default:
throw ArgumentError.value( throw ArgumentError.value(
name, name,
@ -481,6 +519,8 @@ Coin coinFromTickerCaseInsensitive(String ticker) {
return Coin.namecoin; return Coin.namecoin;
case "part": case "part":
return Coin.particl; return Coin.particl;
case "xlm":
return Coin.stellar;
case "tltc": case "tltc":
return Coin.litecoinTestNet; return Coin.litecoinTestNet;
case "tbtc": case "tbtc":
@ -497,6 +537,8 @@ Coin coinFromTickerCaseInsensitive(String ticker) {
return Coin.nano; return Coin.nano;
case "ban": case "ban":
return Coin.banano; return Coin.banano;
case "txlm":
return Coin.stellarTestnet;
default: default:
throw ArgumentError.value( throw ArgumentError.value(
ticker, "name", "No Coin enum value with that ticker"); ticker, "name", "No Coin enum value with that ticker");

View file

@ -49,6 +49,8 @@ extension DerivePathTypeExt on DerivePathType {
case Coin.wownero: case Coin.wownero:
case Coin.nano: case Coin.nano:
case Coin.banano: case Coin.banano:
case Coin.stellar:
case Coin.stellarTestnet:
throw UnsupportedError( throw UnsupportedError(
"$coin does not use bitcoin style derivation paths"); "$coin does not use bitcoin style derivation paths");
} }

View file

@ -194,7 +194,10 @@ class _NodeCardState extends ConsumerState<NodeCard> {
case Coin.nano: case Coin.nano:
case Coin.banano: case Coin.banano:
//TODO: check network/node case Coin.stellar:
case Coin.stellarTestnet:
throw UnimplementedError();
//TODO: check network/node
} }
if (testPassed) { if (testPassed) {

View file

@ -177,7 +177,10 @@ class NodeOptionsSheet extends ConsumerWidget {
case Coin.nano: case Coin.nano:
case Coin.banano: case Coin.banano:
//TODO: check network/node case Coin.stellar:
case Coin.stellarTestnet:
throw UnimplementedError();
//TODO: check network/node
} }
if (testPassed) { if (testPassed) {

View file

@ -37,18 +37,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: args name: args
sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.1" version: "2.4.2"
asn1lib: asn1lib:
dependency: transitive dependency: transitive
description: description:
name: asn1lib name: asn1lib
sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039 sha256: b74e3842a52c61f8819a1ec8444b4de5419b41a7465e69d4aa681445377398b0
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.1"
async: async:
dependency: "direct main" dependency: "direct main"
description: description:
@ -146,10 +146,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: build name: build
sha256: "43865b79fbb78532e4bff7c33087aa43b1d488c4fdef014eaef568af6d8016dc" sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.0" version: "2.4.1"
build_config: build_config:
dependency: transitive dependency: transitive
description: description:
@ -170,26 +170,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: build_resolvers name: build_resolvers
sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95 sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.1"
build_runner: build_runner:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
sha256: "220ae4553e50d7c21a17c051afc7b183d28a24a420502e842f303f8e4e6edced" sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.4" version: "2.4.6"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
name: build_runner_core name: build_runner_core
sha256: "88a57f2ac99849362e73878334caa9f06ee25f31d2adced882b8337838c84e1e" sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.2.9" version: "7.2.10"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
@ -202,10 +202,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: built_value name: built_value
sha256: "7dd62d9faf105c434f3d829bbe9c4be02ec67f5ed94832222116122df67c5452" sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.6.0" version: "8.6.1"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@ -242,10 +242,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: code_builder name: code_builder
sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.4.0" version: "4.5.0"
collection: collection:
dependency: transitive dependency: transitive
description: description:
@ -298,10 +298,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: crypto name: crypto
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.2" version: "3.0.3"
cryptography: cryptography:
dependency: transitive dependency: transitive
description: description:
@ -314,10 +314,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: csslib name: csslib
sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f" sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.17.3" version: "1.0.0"
cw_core: cw_core:
dependency: "direct main" dependency: "direct main"
description: description:
@ -382,18 +382,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: dart_style name: dart_style
sha256: f4f1f73ab3fd2afcbcca165ee601fe980d966af6a21b5970c6c9376955c528ad sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.1" version: "2.3.2"
dartx: dartx:
dependency: transitive dependency: transitive
description: description:
name: dartx name: dartx
sha256: "45d7176701f16c5a5e00a4798791c1964bc231491b879369c818dd9a9c764871" sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.2.0"
dbus: dbus:
dependency: transitive dependency: transitive
description: description:
@ -406,10 +406,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: decimal name: decimal
sha256: eece91944f523657c75a3a008a90ec7f7eb3986191153a78570c7d0ac8ef3d01 sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.3.3"
dependency_validator: dependency_validator:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -450,14 +450,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.0" version: "0.6.0"
dio:
dependency: transitive
description:
name: dio
sha256: "3866d67f93523161b643187af65f5ac08bc991a5bcdaf41a2d587fe4ccb49993"
url: "https://pub.dev"
source: hosted
version: "5.3.0"
dropdown_button2: dropdown_button2:
dependency: "direct main" dependency: "direct main"
description: description:
name: dropdown_button2 name: dropdown_button2
sha256: "374f2390161bf782b4896f0b1b24cbb2b5daaa1cfb11047c3307461dcdf44e07" sha256: "83c54a5022f898d63e3abe21240b64b937e676103207287e6705d3f9bb04d654"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.3" version: "2.3.6"
eip1559: eip1559:
dependency: transitive dependency: transitive
description: description:
@ -542,10 +550,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: file_picker name: file_picker
sha256: "9d6e95ec73abbd31ec54d0e0df8a961017e165aba1395e462e5b31ea0c165daf" sha256: b1729fc96627dd44012d0a901558177418818d6bd428df59dcfeb594e5f66432
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.3.1" version: "5.3.2"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
@ -606,10 +614,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.0.2"
flutter_local_notifications: flutter_local_notifications:
dependency: "direct main" dependency: "direct main"
description: description:
@ -670,10 +678,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_rounded_date_picker name: flutter_rounded_date_picker
sha256: e7143cc5cbf3aec1536286653e38b0809abc99fb76c91bd910dbd98ae003d890 sha256: e6aa2dc5d3b44e8bbe85ef901be69eac59ba4136427f11f4c8b2a303e1e774e7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.3" version: "3.0.4"
flutter_secure_storage: flutter_secure_storage:
dependency: "direct main" dependency: "direct main"
description: description:
@ -821,10 +829,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: html name: html
sha256: "58e3491f7bf0b6a4ea5110c0c688877460d1a6366731155c4a4580e7ded773e8" sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.15.3" version: "0.15.4"
http: http:
dependency: "direct main" dependency: "direct main"
description: description:
@ -953,10 +961,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: lints name: lints
sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.0" version: "2.1.1"
local_auth: local_auth:
dependency: "direct main" dependency: "direct main"
description: description:
@ -977,10 +985,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: lottie name: lottie
sha256: "23522951540d20a57a60202ed7022e6376bed206a4eee1c347a91f58bd57eb9f" sha256: "0793a5866062e5cc8a8b24892fa94c3095953ea914a7fdf790f550dd7537fe60"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.5.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -1033,10 +1041,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: mockito name: mockito
sha256: "8b46d7eb40abdda92d62edd01546051f0c27365e65608c284de336dccfef88cc" sha256: "7d5b53bcd556c1bc7ffbe4e4d5a19c3e112b7e925e9e172dd7c6ad0630812616"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.4.1" version: "5.4.2"
mocktail: mocktail:
dependency: transitive dependency: transitive
description: description:
@ -1145,10 +1153,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_foundation name: path_provider_foundation
sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.3" version: "2.2.4"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@ -1169,50 +1177,50 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_windows name: path_provider_windows
sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6 sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.6" version: "2.1.7"
permission_handler: permission_handler:
dependency: "direct main" dependency: "direct main"
description: description:
name: permission_handler name: permission_handler
sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8" sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.2.0" version: "10.4.3"
permission_handler_android: permission_handler_android:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_android name: permission_handler_android
sha256: d8cc6a62ded6d0f49c6eac337e080b066ee3bce4d405bd9439a61e1f1927bfe8 sha256: "2ffaf52a21f64ac9b35fe7369bb9533edbd4f698e5604db8645b1064ff4cf221"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.2.1" version: "10.3.3"
permission_handler_apple: permission_handler_apple:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_apple name: permission_handler_apple
sha256: ee96ac32f5a8e6f80756e25b25b9f8e535816c8e6665a96b6d70681f8c4f7e85 sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.0.8" version: "9.1.4"
permission_handler_platform_interface: permission_handler_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_platform_interface name: permission_handler_platform_interface
sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.9.0" version: "3.11.3"
permission_handler_windows: permission_handler_windows:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_windows name: permission_handler_windows
sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.2" version: "0.1.3"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
@ -1221,6 +1229,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.4.0" version: "5.4.0"
pinenacl:
dependency: transitive
description:
name: pinenacl
sha256: "3a5503637587d635647c93ea9a8fecf48a420cc7deebe6f1fc85c2a5637ab327"
url: "https://pub.dev"
source: hosted
version: "0.5.1"
platform: platform:
dependency: transitive dependency: transitive
description: description:
@ -1233,10 +1249,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: plugin_platform_interface name: plugin_platform_interface
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.5"
pointycastle: pointycastle:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1390,18 +1406,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: source_gen name: source_gen
sha256: "373f96cf5a8744bc9816c1ff41cf5391bbdbe3d7a96fe98c622b6738a8a7bd33" sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.2" version: "1.4.0"
source_helper: source_helper:
dependency: transitive dependency: transitive
description: description:
name: source_helper name: source_helper
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.3" version: "1.3.4"
source_map_stack_trace: source_map_stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -1451,6 +1467,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.2+1" version: "0.7.2+1"
stellar_flutter_sdk:
dependency: "direct main"
description:
name: stellar_flutter_sdk
sha256: "7a9b7dc76018bbd0b9c828045cf0e26e07ec44208fb1a1733273de2390205475"
url: "https://pub.dev"
source: hosted
version: "1.6.0"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
@ -1555,14 +1579,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
toml:
dependency: transitive
description:
name: toml
sha256: "69756bc12eccf279b72217a87310d217efc4b3752f722e890f672801f19ac485"
url: "https://pub.dev"
source: hosted
version: "0.13.1"
tuple: tuple:
dependency: "direct main" dependency: "direct main"
description: description:
name: tuple name: tuple
sha256: "0ea99cd2f9352b2586583ab2ce6489d1f95a5f6de6fb9492faaf97ae2060f0aa" sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.0.2"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -1579,22 +1611,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.2" version: "2.2.2"
unorm_dart:
dependency: transitive
description:
name: unorm_dart
sha256: "5b35bff83fce4d76467641438f9e867dc9bcfdb8c1694854f230579d68cd8f4b"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
url_launcher: url_launcher:
dependency: "direct main" dependency: "direct main"
description: description:
name: url_launcher name: url_launcher
sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.11" version: "6.1.12"
url_launcher_android: url_launcher_android:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: eed4e6a1164aa9794409325c3b707ff424d4d1c2a785e7db67f8bbda00e36e51 sha256: "78cb6dea3e93148615109e58e42c35d1ffbf5ef66c44add673d0ab75f12ff3af"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.35" version: "6.0.37"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
@ -1615,34 +1655,34 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_macos name: url_launcher_macos
sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e" sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.5" version: "3.0.6"
url_launcher_platform_interface: url_launcher_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_platform_interface name: url_launcher_platform_interface
sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.3"
url_launcher_web: url_launcher_web:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_web name: url_launcher_web
sha256: "6bb1e5d7fe53daf02a8fee85352432a40b1f868a81880e99ec7440113d5cfcab" sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.17" version: "2.0.18"
url_launcher_windows: url_launcher_windows:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_windows name: url_launcher_windows
sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771" sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.6" version: "3.0.7"
uuid: uuid:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1768,18 +1808,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.4" version: "5.0.5"
win32_registry: win32_registry:
dependency: transitive dependency: transitive
description: description:
name: win32_registry name: win32_registry
sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae" sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
window_size: window_size:
dependency: "direct main" dependency: "direct main"
description: description:

View file

@ -138,6 +138,7 @@ dependencies:
desktop_drop: ^0.4.1 desktop_drop: ^0.4.1
nanodart: ^2.0.0 nanodart: ^2.0.0
basic_utils: ^5.5.4 basic_utils: ^5.5.4
stellar_flutter_sdk: ^1.6.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View file

@ -28,7 +28,7 @@ void main() {
Uri.parse( Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids" "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids"
"=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,bitcoin-cash" "=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,bitcoin-cash"
",namecoin,wownero,ethereum,particl,nano,banano&order=market_cap_desc&per_page=50" ",namecoin,wownero,ethereum,particl,nano,banano,stellar&order=market_cap_desc&per_page=50"
"&page=1&sparkline=false"), "&page=1&sparkline=false"),
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@ -113,19 +113,21 @@ void main() {
'Coin.namecoin: [0, 0.0], ' 'Coin.namecoin: [0, 0.0], '
'Coin.nano: [0, 0.0], ' 'Coin.nano: [0, 0.0], '
'Coin.particl: [0, 0.0], ' 'Coin.particl: [0, 0.0], '
'Coin.stellar: [0, 0.0], '
'Coin.wownero: [0, 0.0], ' 'Coin.wownero: [0, 0.0], '
'Coin.bitcoinTestNet: [0, 0.0], ' 'Coin.bitcoinTestNet: [0, 0.0], '
'Coin.bitcoincashTestnet: [0, 0.0], ' 'Coin.bitcoincashTestnet: [0, 0.0], '
'Coin.dogecoinTestNet: [0, 0.0], ' 'Coin.dogecoinTestNet: [0, 0.0], '
'Coin.firoTestNet: [0, 0.0], ' 'Coin.firoTestNet: [0, 0.0], '
'Coin.litecoinTestNet: [0, 0.0]' 'Coin.litecoinTestNet: [0, 0.0], '
'Coin.stellarTestnet: [0, 0.0]'
'}', '}',
); );
verify(client.get( verify(client.get(
Uri.parse( Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc" "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
"&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano" "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false", "&order=market_cap_desc&per_page=50&page=1&sparkline=false",
), ),
headers: {'Content-Type': 'application/json'})).called(1); headers: {'Content-Type': 'application/json'})).called(1);
@ -140,7 +142,7 @@ void main() {
Uri.parse( Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&" "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&"
"ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," "ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano" "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"), "&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@ -219,23 +221,20 @@ void main() {
'{' '{'
'Coin.bitcoin: [1, 0.0], ' 'Coin.bitcoin: [1, 0.0], '
'Coin.monero: [0.00717236, -0.77656], ' 'Coin.monero: [0.00717236, -0.77656], '
'Coin.banano: [0, 0.0], ' 'Coin.banano: [0, 0.0], Coin.bitcoincash: [0, 0.0], '
'Coin.bitcoincash: [0, 0.0], '
'Coin.dogecoin: [0.00000315, -2.68533], ' 'Coin.dogecoin: [0.00000315, -2.68533], '
'Coin.eCash: [0, 0.0], ' 'Coin.eCash: [0, 0.0], '
'Coin.epicCash: [0.00002803, 7.27524], ' 'Coin.epicCash: [0.00002803, 7.27524], Coin.ethereum: [0, 0.0], '
'Coin.ethereum: [0, 0.0], '
'Coin.firo: [0.0001096, -0.89304], ' 'Coin.firo: [0.0001096, -0.89304], '
'Coin.litecoin: [0, 0.0], ' 'Coin.litecoin: [0, 0.0], '
'Coin.namecoin: [0, 0.0], ' 'Coin.namecoin: [0, 0.0], '
'Coin.nano: [0, 0.0], ' 'Coin.nano: [0, 0.0], Coin.particl: [0, 0.0], Coin.stellar: [0, 0.0], '
'Coin.particl: [0, 0.0], '
'Coin.wownero: [0, 0.0], ' 'Coin.wownero: [0, 0.0], '
'Coin.bitcoinTestNet: [0, 0.0], ' 'Coin.bitcoinTestNet: [0, 0.0], '
'Coin.bitcoincashTestnet: [0, 0.0], ' 'Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], '
'Coin.dogecoinTestNet: [0, 0.0], '
'Coin.firoTestNet: [0, 0.0], ' 'Coin.firoTestNet: [0, 0.0], '
'Coin.litecoinTestNet: [0, 0.0]' 'Coin.litecoinTestNet: [0, 0.0], '
'Coin.stellarTestnet: [0, 0.0]'
'}', '}',
); );
@ -244,7 +243,7 @@ void main() {
Uri.parse( Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids" "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids"
"=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," "=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano" "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"), "&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
headers: {'Content-Type': 'application/json'})).called(1); headers: {'Content-Type': 'application/json'})).called(1);
@ -258,7 +257,7 @@ void main() {
Uri.parse( Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc" "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
"&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano" "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"), "&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@ -331,8 +330,7 @@ void main() {
expect( expect(
price.toString(), price.toString(),
'{' '{'
'Coin.bitcoin: [0, 0.0], ' 'Coin.bitcoin: [0, 0.0], Coin.monero: [0, 0.0], '
'Coin.monero: [0, 0.0], '
'Coin.banano: [0, 0.0], ' 'Coin.banano: [0, 0.0], '
'Coin.bitcoincash: [0, 0.0], ' 'Coin.bitcoincash: [0, 0.0], '
'Coin.dogecoin: [0, 0.0], ' 'Coin.dogecoin: [0, 0.0], '
@ -344,12 +342,14 @@ void main() {
'Coin.namecoin: [0, 0.0], ' 'Coin.namecoin: [0, 0.0], '
'Coin.nano: [0, 0.0], ' 'Coin.nano: [0, 0.0], '
'Coin.particl: [0, 0.0], ' 'Coin.particl: [0, 0.0], '
'Coin.stellar: [0, 0.0], '
'Coin.wownero: [0, 0.0], ' 'Coin.wownero: [0, 0.0], '
'Coin.bitcoinTestNet: [0, 0.0], ' 'Coin.bitcoinTestNet: [0, 0.0], '
'Coin.bitcoincashTestnet: [0, 0.0], ' 'Coin.bitcoincashTestnet: [0, 0.0], '
'Coin.dogecoinTestNet: [0, 0.0], ' 'Coin.dogecoinTestNet: [0, 0.0], '
'Coin.firoTestNet: [0, 0.0], ' 'Coin.firoTestNet: [0, 0.0], '
'Coin.litecoinTestNet: [0, 0.0]' 'Coin.litecoinTestNet: [0, 0.0], '
'Coin.stellarTestnet: [0, 0.0]'
'}', '}',
); );
}); });
@ -361,7 +361,7 @@ void main() {
Uri.parse( Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc" "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
"&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano" "bitcoin-cash,namecoin,wownero,ethereum,particl,nano,banano,stellar"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"), "&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@ -389,12 +389,14 @@ void main() {
'Coin.namecoin: [0, 0.0], ' 'Coin.namecoin: [0, 0.0], '
'Coin.nano: [0, 0.0], ' 'Coin.nano: [0, 0.0], '
'Coin.particl: [0, 0.0], ' 'Coin.particl: [0, 0.0], '
'Coin.stellar: [0, 0.0], '
'Coin.wownero: [0, 0.0], ' 'Coin.wownero: [0, 0.0], '
'Coin.bitcoinTestNet: [0, 0.0], ' 'Coin.bitcoinTestNet: [0, 0.0], '
'Coin.bitcoincashTestnet: [0, 0.0], ' 'Coin.bitcoincashTestnet: [0, 0.0], '
'Coin.dogecoinTestNet: [0, 0.0], ' 'Coin.dogecoinTestNet: [0, 0.0], '
'Coin.firoTestNet: [0, 0.0], ' 'Coin.firoTestNet: [0, 0.0], '
'Coin.litecoinTestNet: [0, 0.0]' 'Coin.litecoinTestNet: [0, 0.0], '
'Coin.stellarTestnet: [0, 0.0]'
'}', '}',
); );
}); });