Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add cardano #970

Closed
wants to merge 11 commits into from
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 @@ -165,7 +165,8 @@ enum AddressType {
tezos,
frostMS,
p2tr,
solana;
solana,
cardanoShelley;

String get readableName {
switch (this) {
Expand Down Expand Up @@ -201,6 +202,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;
}
}
}
36 changes: 33 additions & 3 deletions lib/utilities/test_node_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ 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 '../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 +110,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 +185,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 +201,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 +240,29 @@ Future<bool> testNodeConnection({
testPassed = false;
}
break;

case Cardano():
try {
final blockfrostProvider = BlockforestProvider(
BlockfrostHttpProvider(
url: "${formData.host!}:${formData.port!}/api/v0",
detherminal marked this conversation as resolved.
Show resolved Hide resolved
),
);

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
51 changes: 51 additions & 0 deletions lib/wallets/api/cardano/blockfrost_http_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
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,
http.Client? client,
this.defaultRequestTimeout = const Duration(seconds: 30)})
detherminal marked this conversation as resolved.
Show resolved Hide resolved
: client = client ?? http.Client();
@override
final String url;
final String version;
final String? projectId;
final http.Client client;
final Duration defaultRequestTimeout;

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

@override
Future<dynamic> post(BlockforestRequestDetails params,
[Duration? timeout]) async {
final response = await client
.post(Uri.parse(params.url(url, "api/$version")),
headers: {
"Accept": "application/json",
if (projectId != null) ...{"project_id": projectId!},
...params.header
},
body: params.body)
.timeout(timeout ?? defaultRequestTimeout);
final data = json.decode(response.body);
return data;
}
}
123 changes: 123 additions & 0 deletions lib/wallets/crypto_currency/coins/cardano.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
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 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
Loading