Merge pull request #97 from cypherstack/wow

Wow
This commit is contained in:
Rylee Davis 2022-09-28 11:45:02 -06:00 committed by GitHub
commit ef4dab5db6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1704 additions and 38 deletions

View file

@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 23.8456C18.5421 23.8456 23.8456 18.5421 23.8456 12C23.8456 5.45778 18.5421 0.154297 12 0.154297C5.45778 0.154297 0.154297 5.45778 0.154297 12C0.154297 18.5421 5.45778 23.8456 12 23.8456Z" fill="white"/>
<path d="M12 24C5.38264 24 0 18.6174 0 12C0 5.38264 5.38264 0 12 0C18.6174 0 24 5.38264 24 12C24 18.6174 18.6174 24 12 24ZM12 0.327974C5.55627 0.327974 0.327974 5.57556 0.327974 12C0.327974 18.4244 5.57556 23.672 12 23.672C18.4244 23.672 23.672 18.4244 23.672 12C23.672 5.57556 18.4437 0.327974 12 0.327974Z" fill="#D0509D"/>
<path d="M11.9995 0.154297C6.75195 0.154297 2.29536 3.58838 0.751953 8.31507H6.73266V15.7427L11.961 10.0707H12.0381L17.2664 15.762V8.31507H23.2471C21.7037 3.58838 17.2471 0.154297 11.9995 0.154297Z" fill="#FFCD05"/>
<path d="M6.56007 16.1672V8.48875H0.521484L0.598655 8.27653C2.19994 3.31833 6.79158 0 12.0006 0C17.2096 0 21.8012 3.31833 23.4025 8.27653L23.4797 8.48875H17.4411V16.1672L12.0006 10.283L6.56007 16.1672ZM11.9041 9.9164H12.1163L17.1131 15.3376V8.16077H23.036C21.3961 3.47267 16.9974 0.327974 12.0006 0.327974C7.0038 0.327974 2.60509 3.47267 0.965214 8.16077H6.88804V15.3376L11.9041 9.9164Z" fill="#D0509D"/>
<path d="M18.2473 18.2508L16.5495 16.418L11.9965 11.4791L7.4434 16.418L5.74565 18.2508V15.762V9.5498H1.59774C1.40482 10.3601 1.28906 11.209 1.28906 12.0964C1.28906 18 6.07363 22.8038 11.9965 22.8038C17.9193 22.8038 22.7039 18.0193 22.7039 12.0964C22.7039 11.2283 22.5881 10.3794 22.3952 9.5498H18.228V15.762V18.2508H18.2473Z" fill="#D0519D"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -1 +1 @@
Subproject commit 4bffa40cb60ad3d98cf0ea5b5d819f3f4895dcd6 Subproject commit 66c311b9c9799d3d81e2297a9982797603ee989f

View file

@ -8,6 +8,7 @@ import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_libmonero/monero/monero.dart'; import 'package:flutter_libmonero/monero/monero.dart';
import 'package:flutter_libmonero/wownero/wownero.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:hive_flutter/hive_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart';
@ -84,7 +85,6 @@ void main() async {
appDirectory = (await getLibraryDirectory()); appDirectory = (await getLibraryDirectory());
} }
// FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); // FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
await Hive.initFlutter(appDirectory.path);
if (!(Logging.isArmLinux || Logging.isTestEnv)) { if (!(Logging.isArmLinux || Logging.isTestEnv)) {
final isar = await Isar.open( final isar = await Isar.open(
[LogSchema], [LogSchema],
@ -128,11 +128,14 @@ void main() async {
Hive.registerAdapter(NodeAdapter()); Hive.registerAdapter(NodeAdapter());
if (!Hive.isAdapterRegistered(WalletInfoAdapter().typeId)) {
Hive.registerAdapter(WalletInfoAdapter()); Hive.registerAdapter(WalletInfoAdapter());
}
Hive.registerAdapter(WalletTypeAdapter()); Hive.registerAdapter(WalletTypeAdapter());
Hive.registerAdapter(UnspentCoinsInfoAdapter()); Hive.registerAdapter(UnspentCoinsInfoAdapter());
await Hive.initFlutter(appDirectory.path);
await Hive.openBox<dynamic>(DB.boxNameDBInfo); await Hive.openBox<dynamic>(DB.boxNameDBInfo);
int dbVersion = DB.instance.get<dynamic>( int dbVersion = DB.instance.get<dynamic>(
@ -143,6 +146,7 @@ void main() async {
} }
monero.onStartup(); monero.onStartup();
wownero.onStartup();
await Hive.openBox<dynamic>(DB.boxNameTheme); await Hive.openBox<dynamic>(DB.boxNameTheme);

View file

@ -200,7 +200,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
// TODO: do actual check to make sure it is a valid mnemonic for monero // TODO: do actual check to make sure it is a valid mnemonic for monero
if (bip39.validateMnemonic(mnemonic) == false && if (bip39.validateMnemonic(mnemonic) == false &&
!(widget.coin == Coin.monero)) { !(widget.coin == Coin.monero || widget.coin == Coin.wownero)) {
unawaited(showFloatingFlushBar( unawaited(showFloatingFlushBar(
type: FlushBarType.warning, type: FlushBarType.warning,
message: "Invalid seed phrase!", message: "Invalid seed phrase!",

View file

@ -85,7 +85,7 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
height: 8, height: 8,
), ),
Text( Text(
"You need to send ${amount.toStringAsFixed(coin == Coin.monero ? 12 : 8)} ${coin.ticker}", "You need to send ${amount.toStringAsFixed(coin == Coin.monero || coin == Coin.wownero ? 12 : 8)} ${coin.ticker}",
style: STextStyles.itemSubtitle(context), style: STextStyles.itemSubtitle(context),
), ),
const SizedBox( const SizedBox(
@ -307,7 +307,10 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
"${Format.localizedStringAsFixed( "${Format.localizedStringAsFixed(
value: snapshot.data!, value: snapshot.data!,
locale: locale, locale: locale,
decimalPlaces: coin == Coin.monero ? 12 : 8, decimalPlaces:
coin == Coin.monero || coin == Coin.wownero
? 12
: 8,
)} ${coin.ticker}", )} ${coin.ticker}",
style: STextStyles.itemSubtitle(context), style: STextStyles.itemSubtitle(context),
); );

View file

@ -90,6 +90,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
break; break;
case Coin.monero: case Coin.monero:
case Coin.wownero:
try { try {
final uri = Uri.parse(formData.host!); final uri = Uri.parse(formData.host!);
if (uri.scheme.startsWith("http")) { if (uri.scheme.startsWith("http")) {
@ -384,6 +385,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
// strip unused path // strip unused path
String address = formData.host!; String address = formData.host!;
if (coin == Coin.monero || if (coin == Coin.monero ||
coin == Coin.wownero ||
coin == Coin.epicCash) { coin == Coin.epicCash) {
if (address.startsWith("http")) { if (address.startsWith("http")) {
final uri = Uri.parse(address); final uri = Uri.parse(address);
@ -539,6 +541,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
case Coin.epicCash: case Coin.epicCash:
case Coin.monero: case Coin.monero:
case Coin.wownero:
return true; return true;
} }
} }
@ -699,7 +702,9 @@ class _NodeFormState extends ConsumerState<NodeForm> {
focusNode: _hostFocusNode, focusNode: _hostFocusNode,
style: STextStyles.field(context), style: STextStyles.field(context),
decoration: standardInputDecoration( decoration: standardInputDecoration(
(widget.coin != Coin.monero && widget.coin != Coin.epicCash) (widget.coin != Coin.monero &&
widget.coin != Coin.wownero &&
widget.coin != Coin.epicCash)
? "IP address" ? "IP address"
: "Url", : "Url",
_hostFocusNode, _hostFocusNode,
@ -880,7 +885,9 @@ class _NodeFormState extends ConsumerState<NodeForm> {
const SizedBox( const SizedBox(
height: 8, height: 8,
), ),
if (widget.coin != Coin.monero && widget.coin != Coin.epicCash) if (widget.coin != Coin.monero &&
widget.coin != Coin.wownero &&
widget.coin != Coin.epicCash)
Row( Row(
children: [ children: [
GestureDetector( GestureDetector(
@ -931,11 +938,15 @@ class _NodeFormState extends ConsumerState<NodeForm> {
), ),
], ],
), ),
if (widget.coin != Coin.monero && widget.coin != Coin.epicCash) if (widget.coin != Coin.monero &&
widget.coin != Coin.wownero &&
widget.coin != Coin.epicCash)
const SizedBox( const SizedBox(
height: 8, height: 8,
), ),
if (widget.coin != Coin.monero && widget.coin != Coin.epicCash) if (widget.coin != Coin.monero &&
widget.coin != Coin.wownero &&
widget.coin != Coin.epicCash)
Row( Row(
children: [ children: [
GestureDetector( GestureDetector(

View file

@ -81,6 +81,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
break; break;
case Coin.monero: case Coin.monero:
case Coin.wownero:
try { try {
final uri = Uri.parse(node!.host); final uri = Uri.parse(node!.host);
if (uri.scheme.startsWith("http")) { if (uri.scheme.startsWith("http")) {

View file

@ -12,6 +12,7 @@ import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_net
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart'; import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
import 'package:stackwallet/services/coins/monero/monero_wallet.dart'; import 'package:stackwallet/services/coins/monero/monero_wallet.dart';
import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart';
import 'package:stackwallet/services/event_bus/events/global/blocks_remaining_event.dart'; import 'package:stackwallet/services/event_bus/events/global/blocks_remaining_event.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
@ -205,7 +206,7 @@ class _WalletNetworkSettingsViewState
.getManager(widget.walletId) .getManager(widget.walletId)
.coin; .coin;
if (coin == Coin.monero || coin == Coin.epicCash) { if (coin == Coin.monero || coin == Coin.wownero || coin == Coin.epicCash) {
_blocksRemainingSubscription = eventBus.on<BlocksRemainingEvent>().listen( _blocksRemainingSubscription = eventBus.on<BlocksRemainingEvent>().listen(
(event) async { (event) async {
if (event.walletId == widget.walletId) { if (event.walletId == widget.walletId) {
@ -271,6 +272,15 @@ class _WalletNetworkSettingsViewState
if (_percent < highestPercent) { if (_percent < highestPercent) {
_percent = highestPercent.clamp(0.0, 1.0); _percent = highestPercent.clamp(0.0, 1.0);
} }
} else if (coin == Coin.wownero) {
double highestPercent = (ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as WowneroWallet)
.highestPercentCached;
if (_percent < highestPercent) {
_percent = highestPercent.clamp(0.0, 1.0);
}
} else if (coin == Coin.epicCash) { } else if (coin == Coin.epicCash) {
double highestPercent = (ref double highestPercent = (ref
.read(walletsChangeNotifierProvider) .read(walletsChangeNotifierProvider)
@ -545,6 +555,7 @@ class _WalletNetworkSettingsViewState
), ),
), ),
if (coin == Coin.monero || if (coin == Coin.monero ||
coin == Coin.wownero ||
coin == Coin.epicCash) coin == Coin.epicCash)
Text( Text(
" (Blocks to go: ${_blocksRemaining == -1 ? "?" : _blocksRemaining})", " (Blocks to go: ${_blocksRemaining == -1 ? "?" : _blocksRemaining})",

View file

@ -239,7 +239,7 @@ class _TransactionDetailsViewState
children: [ children: [
SelectableText( SelectableText(
"$amountPrefix${Format.localizedStringAsFixed( "$amountPrefix${Format.localizedStringAsFixed(
value: coin == Coin.monero value: coin == Coin.monero || coin == Coin.wownero
? (amount / 10000.toDecimal()).toDecimal() ? (amount / 10000.toDecimal()).toDecimal()
: amount, : amount,
locale: ref.watch( locale: ref.watch(
@ -254,7 +254,7 @@ class _TransactionDetailsViewState
height: 2, height: 2,
), ),
SelectableText( SelectableText(
"${Format.localizedStringAsFixed(value: (coin == Coin.monero ? (amount / 10000.toDecimal()).toDecimal() : amount) * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getPrice(coin).item1)), locale: ref.watch( "${Format.localizedStringAsFixed(value: (coin == Coin.monero || coin == Coin.wownero ? (amount / 10000.toDecimal()).toDecimal() : amount) * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getPrice(coin).item1)), locale: ref.watch(
localeServiceChangeNotifierProvider localeServiceChangeNotifierProvider
.select((value) => value.locale), .select((value) => value.locale),
), decimalPlaces: 2)} ${ref.watch( ), decimalPlaces: 2)} ${ref.watch(
@ -298,14 +298,14 @@ class _TransactionDetailsViewState
], ],
), ),
), ),
if (!(coin == Coin.monero && if (!((coin == Coin.monero || coin == Coin.wownero) &&
_transaction.txType.toLowerCase() == "sent") && _transaction.txType.toLowerCase() == "sent") &&
!((coin == Coin.firo || coin == Coin.firoTestNet) && !((coin == Coin.firo || coin == Coin.firoTestNet) &&
_transaction.subType == "mint")) _transaction.subType == "mint"))
const SizedBox( const SizedBox(
height: 12, height: 12,
), ),
if (!(coin == Coin.monero && if (!((coin == Coin.monero || coin == Coin.wownero) &&
_transaction.txType.toLowerCase() == "sent") && _transaction.txType.toLowerCase() == "sent") &&
!((coin == Coin.firo || coin == Coin.firoTestNet) && !((coin == Coin.firo || coin == Coin.firoTestNet) &&
_transaction.subType == "mint")) _transaction.subType == "mint"))
@ -462,7 +462,7 @@ class _TransactionDetailsViewState
showFeePending showFeePending
? _transaction.confirmedStatus ? _transaction.confirmedStatus
? Format.localizedStringAsFixed( ? Format.localizedStringAsFixed(
value: coin == Coin.monero value: coin == Coin.monero || coin == Coin.wownero
? (fee / 10000.toDecimal()).toDecimal() ? (fee / 10000.toDecimal()).toDecimal()
: fee, : fee,
locale: ref.watch( locale: ref.watch(
@ -471,7 +471,7 @@ class _TransactionDetailsViewState
decimalPlaces: Constants.decimalPlaces) decimalPlaces: Constants.decimalPlaces)
: "Pending" : "Pending"
: Format.localizedStringAsFixed( : Format.localizedStringAsFixed(
value: coin == Coin.monero value: coin == Coin.monero || coin == Coin.wownero
? (fee / 10000.toDecimal()).toDecimal() ? (fee / 10000.toDecimal()).toDecimal()
: fee, : fee,
locale: ref.watch( locale: ref.watch(

View file

@ -750,7 +750,7 @@ class _TransactionSearchViewState
} }
int? amount; int? amount;
if (amountDecimal != null) { if (amountDecimal != null) {
if (widget.coin == Coin.monero) { if (widget.coin == Coin.monero || widget.coin == Coin.wownero) {
amount = (amountDecimal * Decimal.fromInt(Constants.satsPerCoinMonero)) amount = (amountDecimal * Decimal.fromInt(Constants.satsPerCoinMonero))
.floor() .floor()
.toBigInt() .toBigInt()

View file

@ -9,6 +9,7 @@ import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart'; import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/monero/monero_wallet.dart'; import 'package:stackwallet/services/coins/monero/monero_wallet.dart';
import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart';
import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart'; import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -145,6 +146,14 @@ abstract class CoinServiceAPI {
// tracker: tracker, // tracker: tracker,
); );
case Coin.wownero:
return WowneroWallet(
walletId: walletId,
walletName: walletName,
coin: coin,
// tracker: tracker,
);
case Coin.namecoin: case Coin.namecoin:
return NamecoinWallet( return NamecoinWallet(
walletId: walletId, walletId: walletId,

File diff suppressed because it is too large Load diff

View file

@ -79,7 +79,7 @@ class PriceAPI {
Map<Coin, Tuple2<Decimal, double>> result = {}; Map<Coin, Tuple2<Decimal, double>> result = {};
try { try {
final uri = Uri.parse( final uri = Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=${baseCurrency.toLowerCase()}&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"); "https://api.coingecko.com/api/v3/coins/markets?vs_currency=${baseCurrency.toLowerCase()}&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false");
// final uri = Uri.parse( // final uri = Uri.parse(
// "https://api.coingecko.com/api/v3/coins/markets?vs_currency=${baseCurrency.toLowerCase()}&ids=monero%2Cbitcoin%2Cepic-cash%2Czcoin%2Cdogecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"); // "https://api.coingecko.com/api/v3/coins/markets?vs_currency=${baseCurrency.toLowerCase()}&ids=monero%2Cbitcoin%2Cepic-cash%2Czcoin%2Cdogecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false");

View file

@ -223,7 +223,7 @@ class Wallets extends ChangeNotifier {
final shouldSetAutoSync = shouldAutoSyncAll || final shouldSetAutoSync = shouldAutoSyncAll ||
walletIdsToEnableAutoSync.contains(manager.walletId); walletIdsToEnableAutoSync.contains(manager.walletId);
if (manager.coin == Coin.monero) { if (manager.coin == Coin.monero || manager.coin == Coin.wownero) {
walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
} else { } else {
walletInitFutures.add(manager.initializeExisting().then((value) { walletInitFutures.add(manager.initializeExisting().then((value) {
@ -312,7 +312,7 @@ class Wallets extends ChangeNotifier {
final shouldSetAutoSync = shouldAutoSyncAll || final shouldSetAutoSync = shouldAutoSyncAll ||
walletIdsToEnableAutoSync.contains(manager.walletId); walletIdsToEnableAutoSync.contains(manager.walletId);
if (manager.coin == Coin.monero) { if (manager.coin == Coin.monero || manager.coin == Coin.wownero) {
walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
} else { } else {
walletInitFutures.add(manager.initializeExisting().then((value) { walletInitFutures.add(manager.initializeExisting().then((value) {

View file

@ -53,6 +53,9 @@ class AddressUtils {
case Coin.monero: case Coin.monero:
return RegExp("[a-zA-Z0-9]{95}").hasMatch(address) || return RegExp("[a-zA-Z0-9]{95}").hasMatch(address) ||
RegExp("[a-zA-Z0-9]{106}").hasMatch(address); RegExp("[a-zA-Z0-9]{106}").hasMatch(address);
case Coin.wownero:
return RegExp("[a-zA-Z0-9]{95}").hasMatch(address) ||
RegExp("[a-zA-Z0-9]{106}").hasMatch(address);
case Coin.namecoin: case Coin.namecoin:
return Address.validateAddress(address, namecoin, namecoin.bech32!); return Address.validateAddress(address, namecoin, namecoin.bech32!);
case Coin.bitcoinTestNet: case Coin.bitcoinTestNet:

View file

@ -128,6 +128,7 @@ class _SVG {
String get epicCash => "assets/svg/coin_icons/EpicCash.svg"; String get epicCash => "assets/svg/coin_icons/EpicCash.svg";
String get firo => "assets/svg/coin_icons/Firo.svg"; String get firo => "assets/svg/coin_icons/Firo.svg";
String get monero => "assets/svg/coin_icons/Monero.svg"; String get monero => "assets/svg/coin_icons/Monero.svg";
String get wownero => "assets/svg/coin_icons/Wownero.svg";
String get namecoin => "assets/svg/coin_icons/Namecoin.svg"; String get namecoin => "assets/svg/coin_icons/Namecoin.svg";
String get chevronRight => "assets/svg/chevron-right.svg"; String get chevronRight => "assets/svg/chevron-right.svg";
@ -156,6 +157,8 @@ class _SVG {
return firo; return firo;
case Coin.monero: case Coin.monero:
return monero; return monero;
case Coin.wownero:
return wownero;
case Coin.namecoin: case Coin.namecoin:
return namecoin; return namecoin;
case Coin.bitcoinTestNet: case Coin.bitcoinTestNet:
@ -177,6 +180,7 @@ class _PNG {
String get splash => "assets/images/splash.png"; String get splash => "assets/images/splash.png";
String get monero => "assets/images/monero.png"; String get monero => "assets/images/monero.png";
String get wownero => "assets/images/wownero.png";
String get firo => "assets/images/firo.png"; String get firo => "assets/images/firo.png";
String get dogecoin => "assets/images/doge.png"; String get dogecoin => "assets/images/doge.png";
String get bitcoin => "assets/images/bitcoin.png"; String get bitcoin => "assets/images/bitcoin.png";
@ -203,6 +207,8 @@ class _PNG {
return firo; return firo;
case Coin.monero: case Coin.monero:
return monero; return monero;
case Coin.wownero:
return wownero;
case Coin.namecoin: case Coin.namecoin:
return namecoin; return namecoin;
} }

View file

@ -18,6 +18,8 @@ Uri getBlockExplorerTransactionUrlFor({
throw UnimplementedError("missing block explorer for epic cash"); throw UnimplementedError("missing block explorer for epic cash");
case Coin.monero: case Coin.monero:
return Uri.parse("https://xmrchain.net/tx/$txid"); return Uri.parse("https://xmrchain.net/tx/$txid");
case Coin.wownero:
return Uri.parse("https://explore.wownero.com/search?value=$txid");
case Coin.firo: case Coin.firo:
return Uri.parse("https://explorer.firo.org/tx/$txid"); return Uri.parse("https://explorer.firo.org/tx/$txid");
case Coin.firoTestNet: case Coin.firoTestNet:

View file

@ -54,6 +54,9 @@ abstract class Constants {
case Coin.monero: case Coin.monero:
values.addAll([25]); values.addAll([25]);
break; break;
case Coin.wownero:
values.addAll([14]);
break;
} }
return values; return values;
} }
@ -83,6 +86,9 @@ abstract class Constants {
case Coin.monero: case Coin.monero:
return 120; return 120;
case Coin.wownero:
return 120;
case Coin.namecoin: case Coin.namecoin:
return 600; return 600;
} }

View file

@ -15,6 +15,7 @@ abstract class DefaultNodes {
epicCash, epicCash,
bitcoincash, bitcoincash,
namecoin, namecoin,
wownero,
bitcoinTestnet, bitcoinTestnet,
bitcoincashTestnet, bitcoincashTestnet,
dogecoinTestnet, dogecoinTestnet,
@ -83,6 +84,20 @@ abstract class DefaultNodes {
isDown: false, isDown: false,
); );
// TODO: eventually enable ssl and set scheme to https
// currently get certificate failure
static NodeModel get wownero => NodeModel(
host: "http://eu-west-2.wow.xmr.pm",
port: 34568,
name: defaultName,
id: _nodeId(Coin.wownero),
useSSL: false,
enabled: true,
coinName: Coin.wownero.name,
isFailover: true,
isDown: false,
);
static NodeModel get epicCash => NodeModel( static NodeModel get epicCash => NodeModel(
host: "http://epiccash.stackwallet.com", host: "http://epiccash.stackwallet.com",
port: 3413, port: 3413,
@ -175,6 +190,9 @@ abstract class DefaultNodes {
case Coin.monero: case Coin.monero:
return monero; return monero;
case Coin.wownero:
return wownero;
case Coin.namecoin: case Coin.namecoin:
return namecoin; return namecoin;

View file

@ -9,6 +9,7 @@ import 'package:stackwallet/services/coins/firo/firo_wallet.dart' as firo;
import 'package:stackwallet/services/coins/monero/monero_wallet.dart' as xmr; import 'package:stackwallet/services/coins/monero/monero_wallet.dart' as xmr;
import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart' import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart'
as nmc; as nmc;
import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart' as wow;
enum Coin { enum Coin {
bitcoin, bitcoin,
@ -17,6 +18,7 @@ enum Coin {
epicCash, epicCash,
firo, firo,
monero, monero,
wownero,
namecoin, namecoin,
/// ///
@ -47,6 +49,8 @@ extension CoinExt on Coin {
return "Firo"; return "Firo";
case Coin.monero: case Coin.monero:
return "Monero"; return "Monero";
case Coin.wownero:
return "Wownero";
case Coin.namecoin: case Coin.namecoin:
return "Namecoin"; return "Namecoin";
case Coin.bitcoinTestNet: case Coin.bitcoinTestNet:
@ -74,6 +78,8 @@ extension CoinExt on Coin {
return "FIRO"; return "FIRO";
case Coin.monero: case Coin.monero:
return "XMR"; return "XMR";
case Coin.wownero:
return "WOW";
case Coin.namecoin: case Coin.namecoin:
return "NMC"; return "NMC";
case Coin.bitcoinTestNet: case Coin.bitcoinTestNet:
@ -102,6 +108,8 @@ extension CoinExt on Coin {
return "firo"; return "firo";
case Coin.monero: case Coin.monero:
return "monero"; return "monero";
case Coin.wownero:
return "wownero";
case Coin.namecoin: case Coin.namecoin:
return "namecoin"; return "namecoin";
case Coin.bitcoinTestNet: case Coin.bitcoinTestNet:
@ -130,6 +138,7 @@ extension CoinExt on Coin {
case Coin.epicCash: case Coin.epicCash:
case Coin.monero: case Coin.monero:
case Coin.wownero:
return false; return false;
} }
} }
@ -157,6 +166,10 @@ extension CoinExt on Coin {
case Coin.monero: case Coin.monero:
return xmr.MINIMUM_CONFIRMATIONS; return xmr.MINIMUM_CONFIRMATIONS;
case Coin.wownero:
return wow.MINIMUM_CONFIRMATIONS;
case Coin.namecoin: case Coin.namecoin:
return nmc.MINIMUM_CONFIRMATIONS; return nmc.MINIMUM_CONFIRMATIONS;
} }
@ -204,6 +217,10 @@ Coin coinFromPrettyName(String name) {
case "tDogecoin": case "tDogecoin":
case "dogecoinTestNet": case "dogecoinTestNet":
return Coin.dogecoinTestNet; return Coin.dogecoinTestNet;
case "Wownero":
case "tWownero":
case "wownero":
return Coin.wownero;
default: default:
throw ArgumentError.value( throw ArgumentError.value(
name, "name", "No Coin enum value with that prettyName"); name, "name", "No Coin enum value with that prettyName");
@ -234,6 +251,8 @@ Coin coinFromTickerCaseInsensitive(String ticker) {
return Coin.firoTestNet; return Coin.firoTestNet;
case "tdoge": case "tdoge":
return Coin.dogecoinTestNet; return Coin.dogecoinTestNet;
case "wow":
return Coin.wownero;
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

@ -209,8 +209,8 @@ class CoinThemeColor {
return monero; return monero;
case Coin.namecoin: case Coin.namecoin:
return namecoin; return namecoin;
// case Coin.wownero: case Coin.wownero:
// return wownero; return wownero;
} }
} }
} }

View file

@ -1422,8 +1422,8 @@ class StackColors extends ThemeExtension<StackColors> {
return _coin.monero; return _coin.monero;
case Coin.namecoin: case Coin.namecoin:
return _coin.namecoin; return _coin.namecoin;
// case Coin.wownero: case Coin.wownero:
// return wownero; return _coin.wownero;
} }
} }

View file

@ -85,6 +85,7 @@ class NodeOptionsSheet extends ConsumerWidget {
break; break;
case Coin.monero: case Coin.monero:
case Coin.wownero:
try { try {
final uri = Uri.parse(node.host); final uri = Uri.parse(node.host);
if (uri.scheme.startsWith("http")) { if (uri.scheme.startsWith("http")) {

View file

@ -170,7 +170,7 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,
child: Builder( child: Builder(
builder: (_) { builder: (_) {
final amount = coin == Coin.monero final amount = coin == Coin.monero || coin == Coin.wownero
? (_transaction.amount ~/ 10000) ? (_transaction.amount ~/ 10000)
: _transaction.amount; : _transaction.amount;
return Text( return Text(
@ -210,7 +210,7 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
builder: (_) { builder: (_) {
// TODO: modify Format.<functions> to take optional Coin parameter so this type oif check isn't done in ui // TODO: modify Format.<functions> to take optional Coin parameter so this type oif check isn't done in ui
int value = _transaction.amount; int value = _transaction.amount;
if (coin == Coin.monero) { if (coin == Coin.monero || coin == Coin.wownero) {
value = (value ~/ 10000); value = (value ~/ 10000);
} }

View file

@ -11,7 +11,7 @@ description: Stack Wallet
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.4.49+64 version: 1.4.51+67
environment: environment:
sdk: ">=2.17.0 <3.0.0" sdk: ">=2.17.0 <3.0.0"
@ -31,6 +31,9 @@ dependencies:
cw_monero: cw_monero:
path: ./crypto_plugins/flutter_libmonero/cw_monero path: ./crypto_plugins/flutter_libmonero/cw_monero
cw_wownero:
path: ./crypto_plugins/flutter_libmonero/cw_wownero
cw_core: cw_core:
path: ./crypto_plugins/flutter_libmonero/cw_core path: ./crypto_plugins/flutter_libmonero/cw_core
@ -189,6 +192,7 @@ flutter:
- assets/svg/clipboard.svg - assets/svg/clipboard.svg
- assets/images/stack.png - assets/images/stack.png
- assets/images/monero.png - assets/images/monero.png
- assets/images/wownero.png
- assets/images/firo.png - assets/images/firo.png
- assets/images/doge.png - assets/images/doge.png
- assets/images/bitcoin.png - assets/images/bitcoin.png
@ -292,6 +296,7 @@ flutter:
- assets/svg/coin_icons/EpicCash.svg - assets/svg/coin_icons/EpicCash.svg
- assets/svg/coin_icons/Firo.svg - assets/svg/coin_icons/Firo.svg
- assets/svg/coin_icons/Monero.svg - assets/svg/coin_icons/Monero.svg
- assets/svg/coin_icons/Wownero.svg
- assets/svg/coin_icons/Namecoin.svg - assets/svg/coin_icons/Namecoin.svg
# lottie animations # lottie animations
- assets/lottie/test.json - assets/lottie/test.json

View file

@ -23,7 +23,7 @@ void main() {
when(client.get( when(client.get(
Uri.parse( Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"), "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
})).thenAnswer((_) async => Response( })).thenAnswer((_) async => Response(
@ -36,10 +36,10 @@ void main() {
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc"); final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
expect(price.toString(), expect(price.toString(),
'{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0.00000315, -2.68533], Coin.epicCash: [0.00002803, 7.27524], Coin.firo: [0.0001096, -0.89304], Coin.monero: [0.00717236, -0.77656], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}'); '{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0.00000315, -2.68533], Coin.epicCash: [0.00002803, 7.27524], Coin.firo: [0.0001096, -0.89304], Coin.monero: [0.00717236, -0.77656], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
verify(client.get( verify(client.get(
Uri.parse( Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"), "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
headers: {'Content-Type': 'application/json'})).called(1); headers: {'Content-Type': 'application/json'})).called(1);
verifyNoMoreInteractions(client); verifyNoMoreInteractions(client);
@ -50,7 +50,7 @@ void main() {
when(client.get( when(client.get(
Uri.parse( Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"), "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
})).thenAnswer((_) async => Response( })).thenAnswer((_) async => Response(
@ -68,12 +68,12 @@ void main() {
await priceAPI.getPricesAnd24hChange(baseCurrency: "btc"); await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
expect(cachedPrice.toString(), expect(cachedPrice.toString(),
'{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0.00000315, -2.68533], Coin.epicCash: [0.00002803, 7.27524], Coin.firo: [0.0001096, -0.89304], Coin.monero: [0.00717236, -0.77656], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}'); '{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0.00000315, -2.68533], Coin.epicCash: [0.00002803, 7.27524], Coin.firo: [0.0001096, -0.89304], Coin.monero: [0.00717236, -0.77656], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
// verify only called once during filling of cache // verify only called once during filling of cache
verify(client.get( verify(client.get(
Uri.parse( Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"), "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
headers: {'Content-Type': 'application/json'})).called(1); headers: {'Content-Type': 'application/json'})).called(1);
verifyNoMoreInteractions(client); verifyNoMoreInteractions(client);
@ -84,7 +84,7 @@ void main() {
when(client.get( when(client.get(
Uri.parse( Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"), "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
})).thenAnswer((_) async => Response( })).thenAnswer((_) async => Response(
@ -97,7 +97,7 @@ void main() {
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc"); final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
expect(price.toString(), expect(price.toString(),
'{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.firo: [0, 0.0], Coin.monero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}'); '{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.firo: [0, 0.0], Coin.monero: [0, 0.0], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
}); });
test("no internet available", () async { test("no internet available", () async {
@ -105,7 +105,7 @@ void main() {
when(client.get( when(client.get(
Uri.parse( Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"), "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
})).thenThrow(const SocketException( })).thenThrow(const SocketException(
@ -117,7 +117,7 @@ void main() {
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc"); final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
expect(price.toString(), expect(price.toString(),
'{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.firo: [0, 0.0], Coin.monero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}'); '{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.firo: [0, 0.0], Coin.monero: [0, 0.0], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
}); });
tearDown(() async { tearDown(() async {