Skip to content

Commit

Permalink
feat: add cardano
Browse files Browse the repository at this point in the history
  • Loading branch information
detherminal committed Aug 26, 2024
1 parent 2145334 commit 27d319c
Show file tree
Hide file tree
Showing 12 changed files with 802 additions and 5 deletions.
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,11 +562,16 @@ 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
// xmr/wow libraries handling their
// own mnemonic generation
} else if (coin is Cardano) {
mnemonicPassphrase = "";
mnemonic = Bip39MnemonicGenerator().fromWordsNumber(Bip39WordsNum.wordsNum15).toList().join(" ");
} else if (wordCount > 0) {
if (ref
.read(
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 @@ -190,7 +195,7 @@ Future<bool> testNodeConnection({
case Stellar():
try {
testPassed =
await testStellarNodeConnection(formData.host!, formData.port!);
await testStellarNodeConnection(formData.host!, formData.port!);
} catch (_) {}
break;

Expand All @@ -206,7 +211,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 @@ -243,6 +250,29 @@ Future<bool> testNodeConnection({
testPassed = false;
}
break;

case Cardano():
try {
final blockfrostProvider = BlockforestProvider(
BlockfrostHttpProvider(
url: "${formData.host!}:${formData.port!}/api/v0",
),
);

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)})
: 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

0 comments on commit 27d319c

Please sign in to comment.