diff --git a/asset_sources/svg/campfire/rsfiro.svg b/asset_sources/svg/campfire/rsfiro.svg
new file mode 100644
index 0000000000..2fb405f45e
--- /dev/null
+++ b/asset_sources/svg/campfire/rsfiro.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/asset_sources/svg/stack_duo/rsfiro.svg b/asset_sources/svg/stack_duo/rsfiro.svg
new file mode 100644
index 0000000000..3f1f885e17
--- /dev/null
+++ b/asset_sources/svg/stack_duo/rsfiro.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/asset_sources/svg/stack_wallet/rsfiro.svg b/asset_sources/svg/stack_wallet/rsfiro.svg
new file mode 100644
index 0000000000..2fb405f45e
--- /dev/null
+++ b/asset_sources/svg/stack_wallet/rsfiro.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lib/app_config.dart b/lib/app_config.dart
index 4e926ad8ea..d594cf5d9e 100644
--- a/lib/app_config.dart
+++ b/lib/app_config.dart
@@ -1,6 +1,8 @@
// ignore: unused_import
import 'dart:io';
+import 'models/isar/models/ethereum/eth_contract.dart';
+import 'utilities/default_eth_tokens.dart';
import 'wallets/crypto_currency/crypto_currency.dart';
import 'wallets/crypto_currency/intermediate/frost_currency.dart';
@@ -28,6 +30,8 @@ abstract class AppConfig {
static List get coins => _supportedCoins;
+ static List get defaultEthTokens => _defaultEthTokens;
+
static ({String from, String fromFuzzyNet, String to, String toFuzzyNet})
get swapDefaults => _swapDefaults;
diff --git a/lib/db/db_version_migration.dart b/lib/db/db_version_migration.dart
index 2df1213c5c..15ad3b9bcb 100644
--- a/lib/db/db_version_migration.dart
+++ b/lib/db/db_version_migration.dart
@@ -24,6 +24,7 @@ import '../services/mixins/wallet_db.dart';
import '../services/wallets_service.dart';
import '../utilities/amount/amount.dart';
import '../utilities/constants.dart';
+import '../utilities/default_eth_tokens.dart';
import '../utilities/flutter_secure_storage_interface.dart';
import '../utilities/logger.dart';
import '../utilities/prefs.dart';
@@ -379,9 +380,8 @@ class DbVersionMigrator with WalletDB {
final mwcMigrationWalletsService = WalletsService();
final mwcMigrationWalletNames =
await mwcMigrationWalletsService.walletNames;
- final mwcIdentifier = Mimblewimblecoin(
- CryptoCurrencyNetwork.main,
- ).identifier;
+ final mwcIdentifier = Mimblewimblecoin(CryptoCurrencyNetwork.main)
+ .identifier;
for (final walletId in mwcMigrationWalletNames.keys) {
if (mwcMigrationWalletNames[walletId]!.coinIdentifier ==
mwcIdentifier) {
@@ -399,6 +399,30 @@ class DbVersionMigrator with WalletDB {
// try to continue migrating
return await migrate(16, secureStore: secureStore);
+ case 16:
+ if (AppConfig.defaultEthTokens.contains(DefaultTokens.rsFiro)) {
+ final db = MainDB.instance;
+ await db.initMainDB();
+ if (await db.getEthContracts().isNotEmpty() &&
+ await db
+ .getEthContracts()
+ .filter()
+ .addressEqualTo(
+ DefaultTokens.rsFiro.address,
+ caseSensitive: false,
+ )
+ .isEmpty()) {
+ await db.putEthContract(DefaultTokens.rsFiro);
+ }
+ }
+
+ await DB.instance.put(
+ boxName: DB.boxNameDBInfo,
+ key: "hive_data_version",
+ value: 17,
+ );
+ return await migrate(17, secureStore: secureStore);
+
default:
// finally return
return;
diff --git a/lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart b/lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart
index d81afe51e5..749f2351f3 100644
--- a/lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart
+++ b/lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart
@@ -15,6 +15,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:isar_community/isar.dart';
+import '../../../app_config.dart';
import '../../../db/isar/main_db.dart';
import '../../../models/isar/models/ethereum/eth_contract.dart';
import '../../../models/isar/models/solana/sol_contract.dart';
@@ -25,7 +26,6 @@ import '../../../providers/global/wallets_provider.dart';
import '../../../themes/stack_colors.dart';
import '../../../utilities/assets.dart';
import '../../../utilities/constants.dart';
-import '../../../utilities/default_eth_tokens.dart';
import '../../../utilities/default_sol_tokens.dart';
import '../../../utilities/text_styles.dart';
import '../../../utilities/util.dart';
@@ -100,11 +100,10 @@ class _EditWalletTokensViewState extends ConsumerState {
}
Future onNextPressed() async {
- final selectedTokens =
- tokenEntities
- .where((e) => e.selected)
- .map((e) => e.token.address)
- .toList();
+ final selectedTokens = tokenEntities
+ .where((e) => e.selected)
+ .map((e) => e.token.address)
+ .toList();
final wallet = ref.read(pWallets).getWallet(widget.walletId);
@@ -119,13 +118,11 @@ class _EditWalletTokensViewState extends ConsumerState {
Navigator.of(context).pop(42);
} else {
if (isDesktop) {
- Navigator.of(
- context,
- ).popUntil(ModalRoute.withName(DesktopHomeView.routeName));
+ Navigator.of(context)
+ .popUntil(ModalRoute.withName(DesktopHomeView.routeName));
} else {
- await Navigator.of(
- context,
- ).pushNamedAndRemoveUntil(HomeView.routeName, (route) => false);
+ await Navigator.of(context)
+ .pushNamedAndRemoveUntil(HomeView.routeName, (route) => false);
}
if (mounted) {
unawaited(
@@ -160,9 +157,8 @@ class _EditWalletTokensViewState extends ConsumerState {
),
);
} else {
- final result = await Navigator.of(
- context,
- ).pushNamed(AddCustomTokenView.routeName);
+ final result = await Navigator.of(context)
+ .pushNamed(AddCustomTokenView.routeName);
contract = result as EthContract?;
}
@@ -177,7 +173,9 @@ class _EditWalletTokensViewState extends ConsumerState {
tokenEntities.add(
AddTokenListElementData(contract!)..selected = true,
);
- tokenEntities.sort((a, b) => a.token.name.compareTo(b.token.name));
+ tokenEntities.sort(
+ (a, b) => a.token.name.compareTo(b.token.name),
+ );
}
});
}
@@ -199,9 +197,7 @@ class _EditWalletTokensViewState extends ConsumerState {
),
);
} else {
- final result = await Navigator.of(
- context,
- ).pushNamed(
+ final result = await Navigator.of(context).pushNamed(
AddCustomSolanaTokenView.routeName,
arguments: widget.walletId,
);
@@ -228,9 +224,7 @@ class _EditWalletTokensViewState extends ConsumerState {
if (tokenEntities
.where((e) => e.token.address == token!.address)
.isEmpty) {
- tokenEntities.add(
- AddTokenListElementData(token!)..selected = true,
- );
+ tokenEntities.add(AddTokenListElementData(token!)..selected = true);
tokenEntities.sort((a, b) => a.token.name.compareTo(b.token.name));
}
});
@@ -268,7 +262,7 @@ class _EditWalletTokensViewState extends ConsumerState {
.findAllSync();
if (contracts.isEmpty) {
- contracts.addAll(DefaultTokens.list);
+ contracts.addAll(AppConfig.defaultEthTokens);
MainDB.instance
.putEthContracts(contracts)
.then(
@@ -448,9 +442,8 @@ class _EditWalletTokensViewState extends ConsumerState {
_searchTerm = value;
});
},
- style: STextStyles.desktopTextMedium(
- context,
- ).copyWith(height: 2),
+ style: STextStyles.desktopTextMedium(context)
+ .copyWith(height: 2),
decoration:
standardInputDecoration(
"Search",
@@ -516,9 +509,9 @@ class _EditWalletTokensViewState extends ConsumerState {
} else {
return Background(
child: Scaffold(
- backgroundColor: Theme.of(
- context,
- ).extension()!.background,
+ backgroundColor: Theme.of(context)
+ .extension()!
+ .background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () {
@@ -533,14 +526,14 @@ class _EditWalletTokensViewState extends ConsumerState {
child: AppBarIconButton(
size: 36,
shadows: const [],
- color: Theme.of(
- context,
- ).extension()!.background,
+ color: Theme.of(context)
+ .extension()!
+ .background,
icon: SvgPicture.asset(
Assets.svg.circlePlusFilled,
- color: Theme.of(
- context,
- ).extension()!.topNavIconPrimary,
+ color: Theme.of(context)
+ .extension()!
+ .topNavIconPrimary,
width: 20,
height: 20,
),
diff --git a/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list_element.dart b/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list_element.dart
index eecf914d73..65088fbc94 100644
--- a/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list_element.dart
+++ b/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list_element.dart
@@ -20,6 +20,7 @@ import '../../../../services/exchange/change_now/change_now_exchange.dart';
import '../../../../services/exchange/exchange_data_loading_service.dart';
import '../../../../themes/stack_colors.dart';
import '../../../../utilities/assets.dart';
+import '../../../../utilities/default_eth_tokens.dart';
import '../../../../utilities/text_styles.dart';
import '../../../../utilities/util.dart';
import '../../../../widgets/app_icon.dart';
@@ -54,18 +55,14 @@ class _AddTokenListElementState extends ConsumerState {
super.initState();
ExchangeDataLoadingService.instance.isar.then((isar) async {
- final currency =
- await isar.currencies
- .where()
- .exchangeNameEqualTo(ChangeNowExchange.exchangeName)
- .filter()
- .tokenContractEqualTo(
- widget.data.token.address,
- caseSensitive: false,
- )
- .and()
- .imageIsNotEmpty()
- .findFirst();
+ final currency = await isar.currencies
+ .where()
+ .exchangeNameEqualTo(ChangeNowExchange.exchangeName)
+ .filter()
+ .tokenContractEqualTo(widget.data.token.address, caseSensitive: false)
+ .and()
+ .imageIsNotEmpty()
+ .findFirst();
if (mounted) {
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -86,50 +83,54 @@ class _AddTokenListElementState extends ConsumerState {
return RoundedWhiteContainer(
padding: EdgeInsets.all(isDesktop ? 16 : 12),
- borderColor:
- isDesktop
- ? Theme.of(context).extension()!.backgroundAppBar
- : null,
+ borderColor: isDesktop
+ ? Theme.of(context).extension()!.backgroundAppBar
+ : null,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
- currency != null
- ? SvgPicture.network(
- currency!.image,
- width: iconSize,
- height: iconSize,
- placeholderBuilder:
- (_) => AppIcon(width: iconSize, height: iconSize),
- )
- : AppIcon(width: iconSize, height: iconSize),
+ if (widget.data.token is EthContract &&
+ widget.data.token.address.toLowerCase() ==
+ DefaultTokens.rsFiro.address)
+ SvgPicture.asset(
+ Assets.svg.rsFiro,
+ width: iconSize,
+ height: iconSize,
+ )
+ else
+ currency != null
+ ? SvgPicture.network(
+ currency!.image,
+ width: iconSize,
+ height: iconSize,
+ placeholderBuilder: (_) =>
+ AppIcon(width: iconSize, height: iconSize),
+ )
+ : AppIcon(width: iconSize, height: iconSize),
const SizedBox(width: 12),
ConditionalParent(
condition: isDesktop,
- builder:
- (child) => Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- child,
- const SizedBox(height: 2),
- Text(
- widget.data.token.symbol,
- style: STextStyles.desktopTextExtraExtraSmall(
- context,
- ),
- overflow: TextOverflow.ellipsis,
- ),
- ],
+ builder: (child) => Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ child,
+ const SizedBox(height: 2),
+ Text(
+ widget.data.token.symbol,
+ style: STextStyles.desktopTextExtraExtraSmall(context),
+ overflow: TextOverflow.ellipsis,
),
+ ],
+ ),
child: Text(
isDesktop
? mainLabel
: "$mainLabel (${widget.data.token.symbol})",
- style:
- isDesktop
- ? STextStyles.desktopTextSmall(context)
- : STextStyles.w600_14(context),
+ style: isDesktop
+ ? STextStyles.desktopTextSmall(context)
+ : STextStyles.w600_14(context),
overflow: TextOverflow.ellipsis,
),
),
@@ -138,21 +139,20 @@ class _AddTokenListElementState extends ConsumerState {
const SizedBox(width: 4),
isDesktop
? Checkbox(
- value: widget.data.selected,
- onChanged:
- (newValue) =>
- setState(() => widget.data.selected = newValue!),
- )
+ value: widget.data.selected,
+ onChanged: (newValue) =>
+ setState(() => widget.data.selected = newValue!),
+ )
: SizedBox(
- height: 20,
- width: 40,
- child: DraggableSwitchButton(
- isOn: widget.data.selected,
- onValueChanged: (newValue) {
- widget.data.selected = newValue;
- },
+ height: 20,
+ width: 40,
+ child: DraggableSwitchButton(
+ isOn: widget.data.selected,
+ onValueChanged: (newValue) {
+ widget.data.selected = newValue;
+ },
+ ),
),
- ),
],
),
);
diff --git a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart
index 13cb7a022e..6c8b8079c8 100644
--- a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart
+++ b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart
@@ -28,7 +28,6 @@ import '../../../providers/providers.dart';
import '../../../themes/stack_colors.dart';
import '../../../utilities/assets.dart';
import '../../../utilities/constants.dart';
-import '../../../utilities/default_eth_tokens.dart';
import '../../../utilities/default_sol_tokens.dart';
import '../../../utilities/text_styles.dart';
import '../../../utilities/util.dart';
@@ -103,17 +102,15 @@ class _AddWalletViewState extends ConsumerState {
if (isDesktop) {
contract = await showDialog(
context: context,
- builder:
- (context) => const DesktopDialog(
- maxWidth: 580,
- maxHeight: 500,
- child: AddCustomTokenView(),
- ),
+ builder: (context) => const DesktopDialog(
+ maxWidth: 580,
+ maxHeight: 500,
+ child: AddCustomTokenView(),
+ ),
);
} else {
- contract = await Navigator.of(
- context,
- ).pushNamed(AddCustomTokenView.routeName);
+ contract = await Navigator.of(context)
+ .pushNamed(AddCustomTokenView.routeName);
}
if (contract != null) {
@@ -137,17 +134,15 @@ class _AddWalletViewState extends ConsumerState {
if (isDesktop) {
token = await showDialog(
context: context,
- builder:
- (context) => const DesktopDialog(
- maxWidth: 580,
- maxHeight: 500,
- child: AddCustomSolanaTokenView(),
- ),
+ builder: (context) => const DesktopDialog(
+ maxWidth: 580,
+ maxHeight: 500,
+ child: AddCustomSolanaTokenView(),
+ ),
);
} else {
- token = await Navigator.of(
- context,
- ).pushNamed(AddCustomSolanaTokenView.routeName);
+ token = await Navigator.of(context)
+ .pushNamed(AddCustomSolanaTokenView.routeName);
}
if (token != null) {
@@ -176,11 +171,13 @@ class _AddWalletViewState extends ConsumerState {
}
if (AppConfig.coins.whereType().isNotEmpty) {
- final contracts =
- MainDB.instance.getEthContracts().sortByName().findAllSync();
+ final contracts = MainDB.instance
+ .getEthContracts()
+ .sortByName()
+ .findAllSync();
if (contracts.isEmpty) {
- contracts.addAll(DefaultTokens.list);
+ contracts.addAll(AppConfig.defaultEthTokens);
MainDB.instance
.putEthContracts(contracts)
.then(
@@ -274,60 +271,60 @@ class _AddWalletViewState extends ConsumerState {
_searchTerm = value;
});
},
- style: STextStyles.desktopTextMedium(
- context,
- ).copyWith(height: 2),
- decoration: standardInputDecoration(
- "Search",
- _searchFocusNode,
- context,
- ).copyWith(
- contentPadding: const EdgeInsets.symmetric(
- vertical: 10,
- ),
- prefixIcon: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 16,
- // vertical: 20,
- ),
- child: SvgPicture.asset(
- Assets.svg.search,
- width: 24,
- height: 24,
- color:
- Theme.of(context)
+ style: STextStyles.desktopTextMedium(context)
+ .copyWith(height: 2),
+ decoration:
+ standardInputDecoration(
+ "Search",
+ _searchFocusNode,
+ context,
+ ).copyWith(
+ contentPadding: const EdgeInsets.symmetric(
+ vertical: 10,
+ ),
+ prefixIcon: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 16,
+ // vertical: 20,
+ ),
+ child: SvgPicture.asset(
+ Assets.svg.search,
+ width: 24,
+ height: 24,
+ color: Theme.of(context)
.extension()!
.textFieldDefaultSearchIconLeft,
- ),
- ),
- suffixIcon:
- _searchFieldController.text.isNotEmpty
+ ),
+ ),
+ suffixIcon:
+ _searchFieldController.text.isNotEmpty
? Padding(
- padding: const EdgeInsets.only(
- right: 10,
- ),
- child: UnconstrainedBox(
- child: Row(
- children: [
- TextFieldIconButton(
- child: const XIcon(
- width: 24,
- height: 24,
+ padding: const EdgeInsets.only(
+ right: 10,
+ ),
+ child: UnconstrainedBox(
+ child: Row(
+ children: [
+ TextFieldIconButton(
+ child: const XIcon(
+ width: 24,
+ height: 24,
+ ),
+ onTap: () async {
+ setState(() {
+ _searchFieldController
+ .text =
+ "";
+ _searchTerm = "";
+ });
+ },
),
- onTap: () async {
- setState(() {
- _searchFieldController
- .text = "";
- _searchTerm = "";
- });
- },
- ),
- ],
+ ],
+ ),
),
- ),
- )
+ )
: null,
- ),
+ ),
),
),
),
@@ -365,7 +362,10 @@ class _AddWalletViewState extends ConsumerState {
if (solTokenEntities.isNotEmpty)
ExpandingSubListItem(
title: "Solana tokens",
- entities: filter(_searchTerm, solTokenEntities),
+ entities: filter(
+ _searchTerm,
+ solTokenEntities,
+ ),
initialState: ExpandableState.expanded,
animationDurationMultiplier: 0.5,
trailing: AddCustomTokenSelector(
@@ -395,8 +395,9 @@ class _AddWalletViewState extends ConsumerState {
} else {
return Background(
child: Scaffold(
- backgroundColor:
- Theme.of(context).extension()!.background,
+ backgroundColor: Theme.of(context)
+ .extension()!
+ .background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () {
@@ -419,8 +420,7 @@ class _AddWalletViewState extends ConsumerState {
Constants.size.circularBorderRadius,
),
child: Semantics(
- label:
- "Search Text Field. Inputs Text To Search In Wallets.",
+ label: "Search Text Field. Inputs Text To Search In Wallets.",
excludeSemantics: true,
child: TextField(
autofocus: isDesktop,
@@ -428,49 +428,53 @@ class _AddWalletViewState extends ConsumerState {
enableSuggestions: !isDesktop,
controller: _searchFieldController,
focusNode: _searchFocusNode,
- onChanged:
- (value) => setState(() => _searchTerm = value),
+ onChanged: (value) =>
+ setState(() => _searchTerm = value),
style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Search",
- _searchFocusNode,
- context,
- desktopMed: isDesktop,
- ).copyWith(
- prefixIcon: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 10,
- vertical: 16,
- ),
- child: SvgPicture.asset(
- Assets.svg.search,
- width: 16,
- height: 16,
- ),
- ),
- suffixIcon:
- _searchFieldController.text.isNotEmpty
+ decoration:
+ standardInputDecoration(
+ "Search",
+ _searchFocusNode,
+ context,
+ desktopMed: isDesktop,
+ ).copyWith(
+ prefixIcon: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 10,
+ vertical: 16,
+ ),
+ child: SvgPicture.asset(
+ Assets.svg.search,
+ width: 16,
+ height: 16,
+ ),
+ ),
+ suffixIcon:
+ _searchFieldController.text.isNotEmpty
? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- children: [
- TextFieldIconButton(
- child: const XIcon(),
- onTap: () async {
- setState(() {
- _searchFieldController.text =
- "";
- _searchTerm = "";
- });
- },
- ),
- ],
+ padding: const EdgeInsets.only(
+ right: 0,
+ ),
+ child: UnconstrainedBox(
+ child: Row(
+ children: [
+ TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () async {
+ setState(() {
+ _searchFieldController
+ .text =
+ "";
+ _searchTerm = "";
+ });
+ },
+ ),
+ ],
+ ),
),
- ),
- )
+ )
: null,
- ),
+ ),
),
),
),
diff --git a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart
index 9254847fcb..95f3f4a790 100644
--- a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart
+++ b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart
@@ -26,6 +26,7 @@ import '../../../../themes/coin_icon_provider.dart';
import '../../../../themes/stack_colors.dart';
import '../../../../utilities/assets.dart';
import '../../../../utilities/constants.dart';
+import '../../../../utilities/default_eth_tokens.dart';
import '../../../../utilities/text_styles.dart';
import '../../../../utilities/util.dart';
import '../../../../widgets/app_icon.dart';
@@ -137,40 +138,46 @@ class _CoinSelectItemState extends ConsumerState {
constraints: BoxConstraints(minHeight: isDesktop ? 70 : 0),
child: Row(
children: [
- tokenImageUri != null
- ? tokenImageUri!.toLowerCase().endsWith(".svg")
- ? SvgPicture.network(
- tokenImageUri!,
- width: 26,
- height: 26,
- placeholderBuilder: (_) =>
- const AppIcon(width: 26, height: 26),
- )
- : Image.network(
- tokenImageUri!,
- width: 26,
- height: 26,
- errorBuilder: (_, _, _) => SvgPicture.file(
- File(
- ref.watch(
- coinIconProvider(
- widget.entity.cryptoCurrency,
+ if (widget.entity is EthTokenEntity &&
+ (widget.entity as EthTokenEntity).token.address
+ .toLowerCase() ==
+ DefaultTokens.rsFiro.address)
+ SvgPicture.asset(Assets.svg.rsFiro, width: 26, height: 26)
+ else
+ tokenImageUri != null
+ ? tokenImageUri!.toLowerCase().endsWith(".svg")
+ ? SvgPicture.network(
+ tokenImageUri!,
+ width: 26,
+ height: 26,
+ placeholderBuilder: (_) =>
+ const AppIcon(width: 26, height: 26),
+ )
+ : Image.network(
+ tokenImageUri!,
+ width: 26,
+ height: 26,
+ errorBuilder: (_, _, _) => SvgPicture.file(
+ File(
+ ref.watch(
+ coinIconProvider(
+ widget.entity.cryptoCurrency,
+ ),
),
),
+ width: 26,
+ height: 26,
),
- width: 26,
- height: 26,
- ),
- )
- : SvgPicture.file(
- File(
- ref.watch(
- coinIconProvider(widget.entity.cryptoCurrency),
+ )
+ : SvgPicture.file(
+ File(
+ ref.watch(
+ coinIconProvider(widget.entity.cryptoCurrency),
+ ),
),
+ width: 26,
+ height: 26,
),
- width: 26,
- height: 26,
- ),
SizedBox(width: isDesktop ? 12 : 10),
Text(
"${widget.entity.name} (${widget.entity.ticker})",
@@ -187,9 +194,9 @@ class _CoinSelectItemState extends ConsumerState {
height: 24,
child: SvgPicture.asset(
Assets.svg.check,
- color: Theme.of(
- context,
- ).extension()!.accentColorDark,
+ color: Theme.of(context)
+ .extension()!
+ .accentColorDark,
),
),
),
diff --git a/lib/pages/shopinbit/shopinbit_payment_shared.dart b/lib/pages/shopinbit/shopinbit_payment_shared.dart
index db5172ccd8..5eaef38019 100644
--- a/lib/pages/shopinbit/shopinbit_payment_shared.dart
+++ b/lib/pages/shopinbit/shopinbit_payment_shared.dart
@@ -20,9 +20,7 @@ import '../../widgets/background.dart';
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
import 'shopinbit_send_from_view.dart';
-final String kShopInBitUsdtContractAddress = DefaultTokens.list
- .firstWhere((t) => t.symbol == "USDT")
- .address;
+final String kShopInBitUsdtContractAddress = DefaultTokens.usdt.address;
// Address + amount pulled out of one of the API's payment_links entries.
class ShopInBitPaymentTarget {
diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart
index ac6693a02e..509c9d6c01 100644
--- a/lib/utilities/assets.dart
+++ b/lib/utilities/assets.dart
@@ -117,6 +117,8 @@ class _SVG {
final coinControl = const _COIN_CONTROL();
+ String get rsFiro => "assets/svg/rsfiro.svg";
+
String get peers => "assets/svg/peers.svg";
String get fusing => "assets/svg/fusing.svg";
String get upFromLine => "assets/svg/up-from-line.svg";
diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart
index f7b495ee06..51de11e062 100644
--- a/lib/utilities/constants.dart
+++ b/lib/utilities/constants.dart
@@ -40,7 +40,7 @@ abstract class Constants {
// Enable Logger.print statements
static const bool disableLogger = false;
- static const int currentDataVersion = 16;
+ static const int currentDataVersion = 17;
static const int rescanV1 = 1;
diff --git a/lib/utilities/default_eth_tokens.dart b/lib/utilities/default_eth_tokens.dart
index 63cd7f62b3..98ccbd42bf 100644
--- a/lib/utilities/default_eth_tokens.dart
+++ b/lib/utilities/default_eth_tokens.dart
@@ -11,41 +11,46 @@
import '../models/isar/models/ethereum/eth_contract.dart';
abstract class DefaultTokens {
- static List list = [
- EthContract(
- address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
- name: "USD Coin",
- symbol: "USDC",
- decimals: 6,
- type: EthContractType.erc20,
- ),
- EthContract(
- address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
- name: "Tether",
- symbol: "USDT",
- decimals: 6,
- type: EthContractType.erc20,
- ),
- EthContract(
- address: "0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce",
- name: "Shiba Inu",
- symbol: "SHIB",
- decimals: 18,
- type: EthContractType.erc20,
- ),
- EthContract(
- address: "0x514910771af9ca656af840dff83e8264ecf986ca",
- name: "Chainlink",
- symbol: "LINK",
- decimals: 18,
- type: EthContractType.erc20,
- ),
- EthContract(
- address: "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
- name: "Uniswap",
- symbol: "UNI",
- decimals: 18,
- type: EthContractType.erc20,
- ),
- ];
+ static final usdc = EthContract(
+ address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
+ name: "USD Coin",
+ symbol: "USDC",
+ decimals: 6,
+ type: EthContractType.erc20,
+ );
+ static final usdt = EthContract(
+ address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
+ name: "Tether",
+ symbol: "USDT",
+ decimals: 6,
+ type: EthContractType.erc20,
+ );
+ static final shib = EthContract(
+ address: "0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce",
+ name: "Shiba Inu",
+ symbol: "SHIB",
+ decimals: 18,
+ type: EthContractType.erc20,
+ );
+ static final link = EthContract(
+ address: "0x514910771af9ca656af840dff83e8264ecf986ca",
+ name: "Chainlink",
+ symbol: "LINK",
+ decimals: 18,
+ type: EthContractType.erc20,
+ );
+ static final uni = EthContract(
+ address: "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
+ name: "Uniswap",
+ symbol: "UNI",
+ decimals: 18,
+ type: EthContractType.erc20,
+ );
+ static final rsFiro = EthContract(
+ address: "0x2744ea5ac9b11cb5e3cd63d3a88e858336aeddc2",
+ name: "rsFIRO",
+ symbol: "rsFIRO",
+ decimals: 8,
+ type: EthContractType.erc20,
+ );
}
diff --git a/lib/widgets/icon_widgets/eth_token_icon.dart b/lib/widgets/icon_widgets/eth_token_icon.dart
index 856474283e..eb88200916 100644
--- a/lib/widgets/icon_widgets/eth_token_icon.dart
+++ b/lib/widgets/icon_widgets/eth_token_icon.dart
@@ -19,6 +19,8 @@ import '../../models/isar/exchange_cache/currency.dart';
import '../../services/exchange/change_now/change_now_exchange.dart';
import '../../services/exchange/exchange_data_loading_service.dart';
import '../../themes/coin_icon_provider.dart';
+import '../../utilities/assets.dart';
+import '../../utilities/default_eth_tokens.dart';
import '../../wallets/crypto_currency/crypto_currency.dart';
import '../loading_indicator.dart';
@@ -38,10 +40,13 @@ class EthTokenIcon extends ConsumerStatefulWidget {
class _EthTokenIconState extends ConsumerState {
String? imageUrl;
+ bool get _isRsFiro =>
+ widget.contractAddress.toLowerCase() == DefaultTokens.rsFiro.address;
@override
void initState() {
super.initState();
+ if (_isRsFiro) return;
ExchangeDataLoadingService.instance.isar.then((isar) async {
final currency = await isar.currencies
@@ -67,6 +72,14 @@ class _EthTokenIconState extends ConsumerState {
@override
Widget build(BuildContext context) {
+ if (_isRsFiro) {
+ return SvgPicture.asset(
+ Assets.svg.rsFiro,
+ width: widget.size,
+ height: widget.size,
+ );
+ }
+
if (imageUrl == null || imageUrl!.isEmpty) {
return SvgPicture.file(
File(ref.watch(coinIconProvider(Ethereum(.main)))),
diff --git a/scripts/app_config/configure_campfire.sh b/scripts/app_config/configure_campfire.sh
index 40e03dfe26..6af9e01ec8 100755
--- a/scripts/app_config/configure_campfire.sh
+++ b/scripts/app_config/configure_campfire.sh
@@ -75,8 +75,15 @@ const ({String light, String dark})? _appIconAsset = (
final List _supportedCoins = List.unmodifiable([
Firo(CryptoCurrencyNetwork.main),
+ Ethereum(CryptoCurrencyNetwork.main),
]);
+final List _defaultEthTokens = [
+ DefaultTokens.rsFiro,
+ DefaultTokens.usdc,
+ DefaultTokens.usdt,
+];
+
final ({String from, String fromFuzzyNet, String to, String toFuzzyNet})
_swapDefaults = (
from: "BTC",
@@ -85,4 +92,4 @@ _swapDefaults = (
toFuzzyNet: "firo",
);
-EOF
\ No newline at end of file
+EOF
diff --git a/scripts/app_config/configure_stack_duo.sh b/scripts/app_config/configure_stack_duo.sh
index d0bf3694e6..2949d80cd0 100755
--- a/scripts/app_config/configure_stack_duo.sh
+++ b/scripts/app_config/configure_stack_duo.sh
@@ -80,6 +80,8 @@ final List _supportedCoins = List.unmodifiable([
BitcoinFrost(CryptoCurrencyNetwork.test4),
]);
+const List _defaultEthTokens = [];
+
final ({String from, String fromFuzzyNet, String to, String toFuzzyNet})
_swapDefaults = (
from: "BTC",
@@ -88,4 +90,4 @@ _swapDefaults = (
toFuzzyNet: "xmr",
);
-EOF
\ No newline at end of file
+EOF
diff --git a/scripts/app_config/configure_stack_wallet.sh b/scripts/app_config/configure_stack_wallet.sh
index ae16b70ae1..6f41d27b7c 100755
--- a/scripts/app_config/configure_stack_wallet.sh
+++ b/scripts/app_config/configure_stack_wallet.sh
@@ -135,6 +135,15 @@ final List _supportedCoins = List.unmodifiable([
Xelis(CryptoCurrencyNetwork.test),
]);
+final List _defaultEthTokens = [
+ DefaultTokens.usdc,
+ DefaultTokens.usdt,
+ DefaultTokens.shib,
+ DefaultTokens.link,
+ DefaultTokens.uni,
+ DefaultTokens.rsFiro,
+];
+
final ({String from, String fromFuzzyNet, String to, String toFuzzyNet})
_swapDefaults = (
from: "BTC",
@@ -143,4 +152,4 @@ _swapDefaults = (
toFuzzyNet: "xmr",
);
-EOF
\ No newline at end of file
+EOF
diff --git a/scripts/ensure_test_app_config.sh b/scripts/ensure_test_app_config.sh
index 417da291a3..696c853b4e 100755
--- a/scripts/ensure_test_app_config.sh
+++ b/scripts/ensure_test_app_config.sh
@@ -80,6 +80,15 @@ final List _supportedCoins = List.unmodifiable([
Xelis(CryptoCurrencyNetwork.test),
]);
+final List _defaultEthTokens = [
+ DefaultTokens.usdc,
+ DefaultTokens.usdt,
+ DefaultTokens.shib,
+ DefaultTokens.link,
+ DefaultTokens.uni,
+ DefaultTokens.rsFiro,
+];
+
final ({String from, String fromFuzzyNet, String to, String toFuzzyNet})
_swapDefaults = (
from: "BTC",
diff --git a/test/db_version_migration_test.dart b/test/db_version_migration_test.dart
index 4f4ac0d4ed..cd4454b5c7 100644
--- a/test/db_version_migration_test.dart
+++ b/test/db_version_migration_test.dart
@@ -1,14 +1,61 @@
-// import 'package:flutter_test/flutter_test.dart';
-// import 'package:hive_test/hive_test.dart';
+import 'dart:io';
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:isar_community/isar.dart';
+import 'package:stackwallet/db/db_version_migration.dart';
+import 'package:stackwallet/db/hive/db.dart';
+import 'package:stackwallet/db/isar/main_db.dart';
+import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
+import 'package:stackwallet/utilities/constants.dart';
+import 'package:stackwallet/utilities/default_eth_tokens.dart';
+import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
+
+import 'hive/hive_ce_test_utils.dart';
void main() {
- // setUp(() async {
- // await setUpTestHive();
- // });
- //
- // // no migration to test yet
- //
- // tearDown(() async {
- // await tearDownTestHive();
- // });
+ test('v17 adds rsFIRO once and preserves the token catalog', () async {
+ await setUpHiveCeTest();
+ addTearDown(tearDownHiveCeTest);
+ final info = await DB.instance.hive.openBox(DB.boxNameDBInfo);
+ final directory = await Directory.systemTemp.createTemp(
+ 'rsfiro_migration_',
+ );
+ addTearDown(() => directory.delete(recursive: true));
+ await Isar.initializeIsarCore(download: true);
+ final isar = await Isar.open(
+ [EthContractSchema],
+ directory: directory.path,
+ inspector: false,
+ );
+ addTearDown(() => isar.close(deleteFromDisk: true));
+ final db = MainDB.instance;
+ await db.initMainDB(mock: isar);
+ Future migrate(int version) =>
+ DbVersionMigrator().migrate(version, secureStore: FakeSecureStorage());
+
+ await migrate(16);
+ expect(Constants.currentDataVersion, 17);
+ expect(info.get('hive_data_version'), 17);
+ expect(await db.getEthContracts().isEmpty(), isTrue);
+
+ final custom = DefaultTokens.usdc.copyWith(name: 'Custom token');
+ await db.putEthContract(custom);
+ await migrate(16);
+ expect(await db.getEthContracts().count(), 2);
+ expect((await db.getEthContract(custom.address))!.name, 'Custom token');
+
+ final existing = (await db.getEthContract(DefaultTokens.rsFiro.address))!
+ .copyWith(
+ address: DefaultTokens.rsFiro.address.toUpperCase(),
+ abi: 'keep',
+ );
+ await db.putEthContract(existing);
+ await migrate(16);
+ expect(await db.getEthContracts().count(), 2);
+ expect((await db.getEthContract(existing.address))!.abi, 'keep');
+
+ await isar.writeTxn(() => isar.ethContracts.delete(existing.id));
+ await migrate(info.get('hive_data_version') as int);
+ expect(await db.getEthContracts().count(), 1);
+ });
}
diff --git a/test/widgets/eth_token_icon_test.dart b/test/widgets/eth_token_icon_test.dart
new file mode 100644
index 0000000000..af6a062e05
--- /dev/null
+++ b/test/widgets/eth_token_icon_test.dart
@@ -0,0 +1,32 @@
+import 'package:flutter/widgets.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:stackwallet/utilities/assets.dart';
+import 'package:stackwallet/utilities/default_eth_tokens.dart';
+import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.dart';
+
+void main() {
+ testWidgets('rsFIRO uses the per-app SVG by contract address', (
+ tester,
+ ) async {
+ final token = DefaultTokens.rsFiro;
+ expect(token.address, '0x2744ea5ac9b11cb5e3cd63d3a88e858336aeddc2');
+ expect(token.decimals, 8);
+ await tester.pumpWidget(
+ ProviderScope(
+ child: Center(
+ child: EthTokenIcon(
+ contractAddress: token.address.toUpperCase(),
+ size: 26,
+ ),
+ ),
+ ),
+ );
+ await tester.pumpAndSettle();
+ final svg = tester.widget(find.byType(SvgPicture));
+ expect((svg.bytesLoader as SvgAssetLoader).assetName, Assets.svg.rsFiro);
+ expect(tester.getSize(find.byType(SvgPicture)), const Size.square(26));
+ expect(tester.takeException(), isNull);
+ });
+}