Skip to content
Closed
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
5 changes: 4 additions & 1 deletion lib/models/isar/models/blockchain_data/address.dart
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ enum AddressType {
tezos,
frostMS,
p2tr,
solana;
solana,
cardanoShelley;

String get readableName {
switch (this) {
Expand Down Expand Up @@ -210,6 +211,8 @@ enum AddressType {
return "Solana";
case AddressType.p2tr:
return "P2TR (taproot)";
case AddressType.cardanoShelley:
return "Cardano Shelley";
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import 'dart:async';
import 'dart:convert';

import 'package:bip39/bip39.dart' as bip39;
import 'package:blockchain_utils/bip/bip/bip39/bip39_mnemonic.dart';
import 'package:blockchain_utils/bip/bip/bip39/bip39_mnemonic_generator.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
Expand Down Expand Up @@ -560,6 +562,8 @@ class _NewWalletRecoveryPhraseWarningViewState
wordCount = info
.coin.defaultSeedPhraseLength;

// TODO: Refactor these to generate each coin in their respective classes
// This code should not be in a random view page file
if (coin is Monero ||
coin is Wownero) {
// currently a special case due to the
Expand Down
1 change: 1 addition & 0 deletions lib/services/price.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class PriceAPI {
BitcoinFrost: "bitcoin",
Litecoin: "litecoin",
Bitcoincash: "bitcoin-cash",
Cardano: "cardano",
Dash: "dash",
Dogecoin: "dogecoin",
Epiccash: "epic-cash",
Expand Down
6 changes: 5 additions & 1 deletion lib/utilities/enums/derive_path_type_enum.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ enum DerivePathType {
eth,
eCash44,
solana,
bip86;
bip86,
cardanoShelley;

AddressType getAddressType() {
switch (this) {
Expand All @@ -41,6 +42,9 @@ enum DerivePathType {

case DerivePathType.bip86:
return AddressType.p2tr;

case DerivePathType.cardanoShelley:
return AddressType.cardanoShelley;
}
}
}
49 changes: 46 additions & 3 deletions lib/utilities/test_node_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:on_chain/ada/ada.dart';
import 'package:on_chain/ada/src/provider/provider/provider.dart';
import 'package:socks5_proxy/socks.dart';

import '../networking/http.dart';
import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart';
import '../providers/global/prefs_provider.dart';
import '../services/tor_service.dart';
import '../wallets/api/cardano/blockfrost_http_provider.dart';
import '../wallets/api/tezos/tezos_rpc_api.dart';
import '../wallets/crypto_currency/crypto_currency.dart';
import '../wallets/crypto_currency/interfaces/electrumx_currency_interface.dart';
Expand Down Expand Up @@ -107,7 +111,9 @@ Future<bool> testNodeConnection({

case CryptonoteCurrency():
try {
final proxyInfo = ref.read(prefsChangeNotifierProvider).useTor
final proxyInfo = ref
.read(prefsChangeNotifierProvider)
.useTor
? ref.read(pTorService).getProxyInfo()
: null;

Expand Down Expand Up @@ -180,7 +186,7 @@ Future<bool> testNodeConnection({
case Stellar():
try {
testPassed =
await testStellarNodeConnection(formData.host!, formData.port!);
await testStellarNodeConnection(formData.host!, formData.port!);
} catch (_) {}
break;

Expand All @@ -196,7 +202,9 @@ Future<bool> testNodeConnection({
"action": "version",
},
),
proxyInfo: ref.read(prefsChangeNotifierProvider).useTor
proxyInfo: ref
.read(prefsChangeNotifierProvider)
.useTor
? ref.read(pTorService).getProxyInfo()
: null,
);
Expand Down Expand Up @@ -233,6 +241,41 @@ Future<bool> testNodeConnection({
testPassed = false;
}
break;

case Cardano():
try {
final client = HttpClient();
if (ref
.read(prefsChangeNotifierProvider)
.useTor) {
final proxyInfo = TorService.sharedInstance.getProxyInfo();
final proxySettings = ProxySettings(
proxyInfo.host,
proxyInfo.port,
);
SocksTCPClient.assignToHttpClient(client, [proxySettings]);
}
final blockfrostProvider = BlockforestProvider(
BlockfrostHttpProvider(
url: "${formData.host!}:${formData.port!}/api/v0",
client: client,
),
);

final health = await blockfrostProvider.request(
BlockfrostRequestBackendHealthStatus(),
);

Logging.instance.log(
"Cardano testNodeConnection \"health=$health\"",
level: LogLevel.Info,
);

return health;
} catch (_) {
testPassed = false;
}
break;
}

return testPassed;
Expand Down
53 changes: 53 additions & 0 deletions lib/wallets/api/cardano/blockfrost_http_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import 'dart:convert';
import 'dart:io';
import 'package:cbor/simple.dart';
import 'package:on_chain/ada/src/provider/blockfrost/core/core.dart';
import 'package:on_chain/ada/src/provider/service/service.dart';

import '../../../utilities/logger.dart';

class BlockfrostHttpProvider implements BlockfrostServiceProvider {
BlockfrostHttpProvider({
required this.url,
this.version = "v0",
this.projectId,
HttpClient? client,
this.defaultRequestTimeout = const Duration(seconds: 30),
}) : client = client ?? HttpClient();
@override
final String url;
final String version;
final String? projectId;
final HttpClient client;
final Duration defaultRequestTimeout;

@override
Future<dynamic> get(BlockforestRequestDetails params,
[Duration? timeout,]) async {
final response = await client.getUrl(Uri.parse(params.url(url, "api/$version"))).timeout(timeout ?? defaultRequestTimeout);
response.headers.add("Content-Type", "application/json");
response.headers.add("Accept", "application/json");
if (projectId != null) {
response.headers.add("project_id", projectId!);
}
final responseStream = await response.close();
final data = json.decode(await responseStream.transform(utf8.decoder).join());
return data;
}

@override
Future<dynamic> post(BlockforestRequestDetails params,
[Duration? timeout,]) async {
final request = await client.postUrl(Uri.parse(params.url(url, "api/$version"))).timeout(timeout ?? defaultRequestTimeout);
// Need to change this for other operations than submitting transactions
request.headers.add("Content-Type", "application/cbor");
request.headers.add("Accept", "application/json");
if (projectId != null) {
request.headers.add("project_id", projectId!);
}
request.add(params.body as List<int>);
final response = await request.close();
final data = json.decode(await response.transform(utf8.decoder).join());
return data;
}
}
126 changes: 126 additions & 0 deletions lib/wallets/crypto_currency/coins/cardano.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import '../../../models/isar/models/blockchain_data/address.dart';
import '../../../models/node_model.dart';
import '../../../utilities/default_nodes.dart';
import '../../../utilities/enums/derive_path_type_enum.dart';
import '../crypto_currency.dart';
import '../intermediate/bip39_currency.dart';

class Cardano extends Bip39Currency {
Cardano(super.network) {
_idMain = "cardano";
_uriScheme = "cardano";
switch (network) {
case CryptoCurrencyNetwork.main:
_id = _idMain;
_name = "Cardano";
_ticker = "ADA";
default:
throw Exception("Unsupported network: $network");
}
}

late final String _id;

@override
String get identifier => _id;

late final String _idMain;

@override
String get mainNetId => _idMain;

late final String _name;

@override
String get prettyName => _name;

late final String _uriScheme;

@override
String get uriScheme => _uriScheme;

late final String _ticker;

@override
String get ticker => _ticker;

@override
AddressType get defaultAddressType => AddressType.cardanoShelley;

@override
Uri defaultBlockExplorer(String txid) {
switch (network) {
case CryptoCurrencyNetwork.main:
return Uri.parse(
"https://explorer.cardano.org/en/transaction?id=$txid");
default:
throw Exception(
"Unsupported network for defaultBlockExplorer(): $network",
);
}
}

@override
DerivePathType get defaultDerivePathType => DerivePathType.cardanoShelley;

@override
NodeModel get defaultNode {
switch (network) {
case CryptoCurrencyNetwork.main:
return NodeModel(
host: "https://cardano.stackwallet.com",
port: 443,
name: DefaultNodes.defaultName,
id: DefaultNodes.buildId(this),
useSSL: true,
enabled: true,
coinName: identifier,
isFailover: true,
isDown: false,
);

default:
throw Exception("Unsupported network: $network");
}
}

@override
int get defaultSeedPhraseLength => 15;

@override
int get fractionDigits => 6;

@override
String get genesisHash => "f0f7892b5c333cffc4b3c4344de48af4cc63f55e44936196f365a9ef2244134f";

@override
bool get hasBuySupport => false;

@override
bool get hasMnemonicPassphraseSupport => false;

@override
int get minConfirms => 2;

@override
List<int> get possibleMnemonicLengths => [defaultSeedPhraseLength];

@override
BigInt get satsPerCoin => BigInt.from(1000000);

@override
int get targetBlockTimeSeconds => 20;

@override
bool get torSupport => true;

@override
bool validateAddress(String address) {
switch (network) {
case CryptoCurrencyNetwork.main:
return RegExp(r"^addr1[0-9a-zA-Z]{98}$").hasMatch(address);
default:
throw Exception("Unsupported network: $network");
}
}
}
1 change: 1 addition & 0 deletions lib/wallets/crypto_currency/crypto_currency.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export 'coins/banano.dart';
export 'coins/bitcoin.dart';
export 'coins/bitcoin_frost.dart';
export 'coins/bitcoincash.dart';
export 'coins/cardano.dart';
export 'coins/dash.dart';
export 'coins/dogecoin.dart';
export 'coins/ecash.dart';
Expand Down
Loading