Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion lib/wallets/crypto_currency/coins/monero.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import '../../../wl_gen/interfaces/cs_monero_interface.dart';
import '../crypto_currency.dart';
import '../intermediate/cryptonote_currency.dart';

int moneroNetworkType(CryptoCurrencyNetwork network) => switch (network) {
CryptoCurrencyNetwork.main => 0,
CryptoCurrencyNetwork.test => 1,
CryptoCurrencyNetwork.stage => 2,
_ => throw ArgumentError.value(network, "network", "Unsupported network"),
};

class Monero extends CryptonoteCurrency {
Monero(super.network) {
_idMain = "monero";
Expand All @@ -14,6 +21,10 @@ class Monero extends CryptonoteCurrency {
_id = _idMain;
_name = "Monero";
_ticker = "XMR";
case CryptoCurrencyNetwork.stage:
_id = "${_idMain}Stagenet";
_name = "sMonero";
_ticker = "sXMR";
default:
throw Exception("Unsupported network: $network");
}
Expand Down Expand Up @@ -52,7 +63,8 @@ class Monero extends CryptonoteCurrency {
}
switch (network) {
case CryptoCurrencyNetwork.main:
return csMonero.validateAddress(address, 0);
case CryptoCurrencyNetwork.stage:
return csMonero.validateAddress(address, moneroNetworkType(network));
default:
throw Exception("Unsupported network: $network");
}
Expand All @@ -78,6 +90,25 @@ class Monero extends CryptonoteCurrency {
isPrimary: isPrimary,
);

case CryptoCurrencyNetwork.stage:
// Public stagenet RPC; the wallet treats the remote daemon as
// untrusted.
return NodeModel(
host: "http://node3.monerodevs.org",
port: 38089,
name: DefaultNodes.defaultName,
id: DefaultNodes.buildId(this),
useSSL: false,
enabled: true,
coinName: identifier,
isFailover: true,
isDown: false,
trusted: false,
torEnabled: true,
clearnetEnabled: true,
isPrimary: isPrimary,
);

default:
throw UnimplementedError();
}
Expand Down Expand Up @@ -114,6 +145,8 @@ class Monero extends CryptonoteCurrency {
switch (network) {
case CryptoCurrencyNetwork.main:
return Uri.parse("https://xmrchain.net/tx/$txid");
case CryptoCurrencyNetwork.stage:
return Uri.parse("https://stagenet.xmrchain.net/tx/$txid");
default:
throw Exception(
"Unsupported network for defaultBlockExplorer(): $network",
Expand Down
2 changes: 1 addition & 1 deletion lib/wallets/crypto_currency/coins/xelis.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class Xelis extends ElectrumCurrency {
isPrimary: isPrimary,
);

case CryptoCurrencyNetwork.test:
case CryptoCurrencyNetwork.stage:
return NodeModel(
host: "stagenet-node.xelis.io",
port: 443,
Expand Down
4 changes: 3 additions & 1 deletion lib/wallets/crypto_currency/crypto_currency.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ enum CryptoCurrencyNetwork {
test4;

bool get isTestNet =>
this == CryptoCurrencyNetwork.test || this == CryptoCurrencyNetwork.test4;
this == CryptoCurrencyNetwork.test ||
this == CryptoCurrencyNetwork.test4 ||
this == CryptoCurrencyNetwork.stage;
}

abstract class CryptoCurrency {
Expand Down
14 changes: 13 additions & 1 deletion lib/wallets/wallet/impl/monero_wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,27 @@ class MoneroWallet extends LibMoneroWallet {
Future<WrappedWallet> loadWallet({
required String path,
required String password,
}) => csMonero.loadWallet(walletId, path: path, password: password);
required int network,
}) => csMonero.loadWallet(
walletId,
path: path,
password: password,
network: network,
);

@override
Future<WrappedWallet> getCreatedWallet({
required String path,
required String password,
required int wordCount,
required String seedOffset,
required int network,
}) => csMonero.getCreatedWallet(
path: path,
password: password,
wordCount: wordCount,
seedOffset: seedOffset,
network: network,
);

@override
Expand All @@ -62,13 +70,15 @@ class MoneroWallet extends LibMoneroWallet {
required String password,
required String mnemonic,
required String seedOffset,
required int network,
int height = 0,
}) => csMonero.getRestoredWallet(
path: path,
password: password,
mnemonic: mnemonic,
height: height,
seedOffset: seedOffset,
network: network,
walletId: walletId,
);

Expand All @@ -78,13 +88,15 @@ class MoneroWallet extends LibMoneroWallet {
required String password,
required String address,
required String privateViewKey,
required int network,
int height = 0,
}) => csMonero.getRestoredFromViewKeyWallet(
walletId: walletId,
path: path,
password: password,
address: address,
privateViewKey: privateViewKey,
network: network,
height: height,
);

Expand Down
22 changes: 20 additions & 2 deletions lib/wallets/wallet/intermediate/lib_monero_wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import '../../../utilities/stack_file_system.dart';
import '../../../wl_gen/interfaces/cs_monero_interface.dart';
import '../../../wl_gen/interfaces/cs_salvium_interface.dart'
show WrappedWallet;
import '../../crypto_currency/coins/monero.dart';
import '../../crypto_currency/intermediate/cryptonote_currency.dart';
import '../../isar/models/wallet_info.dart';
import '../../models/tx_data.dart';
Expand Down Expand Up @@ -110,6 +111,8 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>

final lib_monero_compat.WalletType compatType;

int getNetworkType() => moneroNetworkType(cryptoCurrency.network);

lib_monero_compat.SyncStatus? get syncStatus => _syncStatus;
lib_monero_compat.SyncStatus? _syncStatus;
int _syncedCount = 0;
Expand Down Expand Up @@ -138,20 +141,23 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
Future<WrappedWallet> loadWallet({
required String path,
required String password,
required int network,
});

Future<WrappedWallet> getCreatedWallet({
required String path,
required String password,
required int wordCount,
required String seedOffset,
required int network,
});

Future<WrappedWallet> getRestoredWallet({
required String path,
required String password,
required String mnemonic,
required String seedOffset,
required int network,
int height = 0,
});

Expand All @@ -160,6 +166,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
required String password,
required String address,
required String privateViewKey,
required int network,
int height = 0,
});

Expand Down Expand Up @@ -209,7 +216,11 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
throw Exception("Password not found $e, $s");
}

wallet = await loadWallet(path: path, password: password);
wallet = await loadWallet(
path: path,
password: password,
network: getNetworkType(),
);

_setListener();

Expand Down Expand Up @@ -329,7 +340,11 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
} catch (e, s) {
throw Exception("Password not found $e, $s");
}
wallet = await loadWallet(path: path, password: password);
wallet = await loadWallet(
path: path,
password: password,
network: getNetworkType(),
);
return (
await csMonero.getAddress(wallet!),
await csMonero.getPrivateViewKey(wallet!),
Expand All @@ -355,6 +370,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
password: password,
wordCount: wordCount,
seedOffset: "", // default for non restored wallets for now
network: getNetworkType(),
);

await info.updateRestoreHeight(
Expand Down Expand Up @@ -438,6 +454,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
mnemonic: mnemonic,
height: height,
seedOffset: seedOffset,
network: getNetworkType(),
);

if (this.wallet != null) {
Expand Down Expand Up @@ -1575,6 +1592,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
address: data.address,
privateViewKey: data.privateViewKey,
height: height,
network: getNetworkType(),
);

if (this.wallet == null) {
Expand Down
4 changes: 4 additions & 0 deletions lib/wl_gen/interfaces/cs_monero_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ abstract class CsMoneroInterface {
String walletId, {
required String path,
required String password,
required int network,
});

Future<String> getAddress(
Expand All @@ -38,6 +39,7 @@ abstract class CsMoneroInterface {
required String password,
required int wordCount,
required String seedOffset,
required int network,
});

Future<WrappedWallet> getRestoredWallet({
Expand All @@ -46,6 +48,7 @@ abstract class CsMoneroInterface {
required String password,
required String mnemonic,
required String seedOffset,
required int network,
int height = 0,
});

Expand All @@ -55,6 +58,7 @@ abstract class CsMoneroInterface {
required String password,
required String address,
required String privateViewKey,
required int network,
int height = 0,
});

Expand Down
1 change: 1 addition & 0 deletions scripts/app_config/configure_stack_duo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ final List<CryptoCurrency> _supportedCoins = List.unmodifiable([
Bitcoin(CryptoCurrencyNetwork.test4),
BitcoinFrost(CryptoCurrencyNetwork.test),
BitcoinFrost(CryptoCurrencyNetwork.test4),
Monero(CryptoCurrencyNetwork.stage),
]);

final ({String from, String fromFuzzyNet, String to, String toFuzzyNet})
Expand Down
1 change: 1 addition & 0 deletions scripts/app_config/configure_stack_wallet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ final List<CryptoCurrency> _supportedCoins = List.unmodifiable([
Salvium(CryptoCurrencyNetwork.test),
Stellar(CryptoCurrencyNetwork.test),
Xelis(CryptoCurrencyNetwork.test),
Monero(CryptoCurrencyNetwork.stage),
]);

final ({String from, String fromFuzzyNet, String to, String toFuzzyNet})
Expand Down
63 changes: 63 additions & 0 deletions test/wallets/crypto_currency/monero_stagenet_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import "package:flutter_test/flutter_test.dart";
import "package:stackwallet/app_config.dart";
import "package:stackwallet/wallets/crypto_currency/crypto_currency.dart";

void main() {
group("Monero stagenet", () {
final coin = Monero(CryptoCurrencyNetwork.stage);

test("uses a distinct test-network identity", () {
expect(coin.identifier, "moneroStagenet");
expect(coin.mainNetId, "monero");
expect(coin.prettyName, "sMonero");
expect(coin.ticker, "sXMR");
expect(coin.network.isTestNet, isTrue);
expect(
AppConfig.getCryptoCurrencyFor(coin.identifier)?.network,
CryptoCurrencyNetwork.stage,
);
});

test("uses the stagenet native network type", () {
expect(moneroNetworkType(CryptoCurrencyNetwork.main), 0);
expect(moneroNetworkType(CryptoCurrencyNetwork.test), 1);
expect(moneroNetworkType(CryptoCurrencyNetwork.stage), 2);
expect(
() => moneroNetworkType(CryptoCurrencyNetwork.test4),
throwsArgumentError,
);
});

test("ships an enabled untrusted stagenet node", () {
final node = coin.defaultNode(isPrimary: true);

expect(node.host, "http://node3.monerodevs.org");
expect(node.port, 38089);
expect(node.useSSL, isFalse);
expect(node.enabled, isTrue);
expect(node.trusted, isFalse);
expect(node.torEnabled, isTrue);
expect(node.clearnetEnabled, isTrue);
expect(node.coinName, coin.identifier);
expect(node.isPrimary, isTrue);
});

test("uses the stagenet block explorer", () {
expect(
coin.defaultBlockExplorer("abc").toString(),
"https://stagenet.xmrchain.net/tx/abc",
);
});
});

test("Xelis stagenet has a selectable default node", () {
final coin = Xelis(CryptoCurrencyNetwork.stage);
final node = coin.defaultNode(isPrimary: false);

expect(coin.network.isTestNet, isTrue);
expect(node.host, "stagenet-node.xelis.io");
expect(node.port, 443);
expect(node.useSSL, isTrue);
expect(node.coinName, coin.identifier);
});
}
Loading