mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-02-25 20:40:19 +00:00
Merge remote-tracking branch 'detherminal/staging' into cardano
This commit is contained in:
commit
98d7ce3a8e
13 changed files with 854 additions and 29 deletions
lib
models/isar/models/blockchain_data
pages/add_wallet_views/new_wallet_recovery_phrase_warning_view
services
utilities
wallets
api/cardano
crypto_currency
wallet
scripts/app_config
|
@ -174,7 +174,8 @@ enum AddressType {
|
||||||
tezos,
|
tezos,
|
||||||
frostMS,
|
frostMS,
|
||||||
p2tr,
|
p2tr,
|
||||||
solana;
|
solana,
|
||||||
|
cardanoShelley;
|
||||||
|
|
||||||
String get readableName {
|
String get readableName {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
@ -210,6 +211,8 @@ enum AddressType {
|
||||||
return "Solana";
|
return "Solana";
|
||||||
case AddressType.p2tr:
|
case AddressType.p2tr:
|
||||||
return "P2TR (taproot)";
|
return "P2TR (taproot)";
|
||||||
|
case AddressType.cardanoShelley:
|
||||||
|
return "Cardano Shelley";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
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/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
@ -560,6 +562,8 @@ class _NewWalletRecoveryPhraseWarningViewState
|
||||||
wordCount = info
|
wordCount = info
|
||||||
.coin.defaultSeedPhraseLength;
|
.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 ||
|
if (coin is Monero ||
|
||||||
coin is Wownero) {
|
coin is Wownero) {
|
||||||
// currently a special case due to the
|
// currently a special case due to the
|
||||||
|
|
|
@ -30,6 +30,7 @@ class PriceAPI {
|
||||||
BitcoinFrost: "bitcoin",
|
BitcoinFrost: "bitcoin",
|
||||||
Litecoin: "litecoin",
|
Litecoin: "litecoin",
|
||||||
Bitcoincash: "bitcoin-cash",
|
Bitcoincash: "bitcoin-cash",
|
||||||
|
Cardano: "cardano",
|
||||||
Dash: "dash",
|
Dash: "dash",
|
||||||
Dogecoin: "dogecoin",
|
Dogecoin: "dogecoin",
|
||||||
Epiccash: "epic-cash",
|
Epiccash: "epic-cash",
|
||||||
|
|
|
@ -18,7 +18,8 @@ enum DerivePathType {
|
||||||
eth,
|
eth,
|
||||||
eCash44,
|
eCash44,
|
||||||
solana,
|
solana,
|
||||||
bip86;
|
bip86,
|
||||||
|
cardanoShelley;
|
||||||
|
|
||||||
AddressType getAddressType() {
|
AddressType getAddressType() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
@ -41,6 +42,9 @@ enum DerivePathType {
|
||||||
|
|
||||||
case DerivePathType.bip86:
|
case DerivePathType.bip86:
|
||||||
return AddressType.p2tr;
|
return AddressType.p2tr;
|
||||||
|
|
||||||
|
case DerivePathType.cardanoShelley:
|
||||||
|
return AddressType.cardanoShelley;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,15 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.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 '../networking/http.dart';
|
||||||
import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart';
|
import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart';
|
||||||
import '../providers/global/prefs_provider.dart';
|
import '../providers/global/prefs_provider.dart';
|
||||||
import '../services/tor_service.dart';
|
import '../services/tor_service.dart';
|
||||||
|
import '../wallets/api/cardano/blockfrost_http_provider.dart';
|
||||||
import '../wallets/api/tezos/tezos_rpc_api.dart';
|
import '../wallets/api/tezos/tezos_rpc_api.dart';
|
||||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||||
import '../wallets/crypto_currency/interfaces/electrumx_currency_interface.dart';
|
import '../wallets/crypto_currency/interfaces/electrumx_currency_interface.dart';
|
||||||
|
@ -107,7 +111,9 @@ Future<bool> testNodeConnection({
|
||||||
|
|
||||||
case CryptonoteCurrency():
|
case CryptonoteCurrency():
|
||||||
try {
|
try {
|
||||||
final proxyInfo = ref.read(prefsChangeNotifierProvider).useTor
|
final proxyInfo = ref
|
||||||
|
.read(prefsChangeNotifierProvider)
|
||||||
|
.useTor
|
||||||
? ref.read(pTorService).getProxyInfo()
|
? ref.read(pTorService).getProxyInfo()
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
@ -180,7 +186,7 @@ Future<bool> testNodeConnection({
|
||||||
case Stellar():
|
case Stellar():
|
||||||
try {
|
try {
|
||||||
testPassed =
|
testPassed =
|
||||||
await testStellarNodeConnection(formData.host!, formData.port!);
|
await testStellarNodeConnection(formData.host!, formData.port!);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -196,7 +202,9 @@ Future<bool> testNodeConnection({
|
||||||
"action": "version",
|
"action": "version",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
proxyInfo: ref.read(prefsChangeNotifierProvider).useTor
|
proxyInfo: ref
|
||||||
|
.read(prefsChangeNotifierProvider)
|
||||||
|
.useTor
|
||||||
? ref.read(pTorService).getProxyInfo()
|
? ref.read(pTorService).getProxyInfo()
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
|
@ -233,6 +241,41 @@ Future<bool> testNodeConnection({
|
||||||
testPassed = false;
|
testPassed = false;
|
||||||
}
|
}
|
||||||
break;
|
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;
|
return testPassed;
|
||||||
|
|
53
lib/wallets/api/cardano/blockfrost_http_provider.dart
Normal file
53
lib/wallets/api/cardano/blockfrost_http_provider.dart
Normal file
|
@ -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
lib/wallets/crypto_currency/coins/cardano.dart
Normal file
126
lib/wallets/crypto_currency/coins/cardano.dart
Normal file
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ export 'coins/banano.dart';
|
||||||
export 'coins/bitcoin.dart';
|
export 'coins/bitcoin.dart';
|
||||||
export 'coins/bitcoin_frost.dart';
|
export 'coins/bitcoin_frost.dart';
|
||||||
export 'coins/bitcoincash.dart';
|
export 'coins/bitcoincash.dart';
|
||||||
|
export 'coins/cardano.dart';
|
||||||
export 'coins/dash.dart';
|
export 'coins/dash.dart';
|
||||||
export 'coins/dogecoin.dart';
|
export 'coins/dogecoin.dart';
|
||||||
export 'coins/ecash.dart';
|
export 'coins/ecash.dart';
|
||||||
|
|
567
lib/wallets/wallet/impl/cardano_wallet.dart
Normal file
567
lib/wallets/wallet/impl/cardano_wallet.dart
Normal file
|
@ -0,0 +1,567 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:blockchain_utils/bip/bip/bip44/base/bip44_base.dart';
|
||||||
|
import 'package:blockchain_utils/bip/cardano/bip32/cardano_icarus_bip32.dart';
|
||||||
|
import 'package:blockchain_utils/bip/cardano/cip1852/cip1852.dart';
|
||||||
|
import 'package:blockchain_utils/bip/cardano/cip1852/conf/cip1852_coins.dart';
|
||||||
|
import 'package:blockchain_utils/bip/cardano/mnemonic/cardano_icarus_seed_generator.dart';
|
||||||
|
import 'package:blockchain_utils/bip/cardano/shelley/cardano_shelley.dart';
|
||||||
|
import 'package:on_chain/ada/ada.dart';
|
||||||
|
import 'package:socks5_proxy/socks.dart';
|
||||||
|
import '../../../models/balance.dart';
|
||||||
|
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
import '../../../models/isar/models/blockchain_data/transaction.dart' as isar;
|
||||||
|
import '../../../models/paymint/fee_object_model.dart';
|
||||||
|
import '../../../networking/http.dart';
|
||||||
|
import '../../../services/tor_service.dart';
|
||||||
|
import '../../../utilities/amount/amount.dart';
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
|
import '../../../utilities/logger.dart';
|
||||||
|
import '../../../utilities/prefs.dart';
|
||||||
|
import '../../api/cardano/blockfrost_http_provider.dart';
|
||||||
|
import '../../crypto_currency/crypto_currency.dart';
|
||||||
|
import '../../models/tx_data.dart';
|
||||||
|
import '../intermediate/bip39_wallet.dart';
|
||||||
|
|
||||||
|
class CardanoWallet extends Bip39Wallet<Cardano> {
|
||||||
|
CardanoWallet(CryptoCurrencyNetwork network) : super(Cardano(network));
|
||||||
|
|
||||||
|
// Source: https://cips.cardano.org/cip/CIP-1852
|
||||||
|
static const String _addressDerivationPath = "m/1852'/1815'/0'/0/0";
|
||||||
|
static final HTTP _httpClient = HTTP();
|
||||||
|
|
||||||
|
BlockforestProvider? blockfrostProvider;
|
||||||
|
|
||||||
|
@override
|
||||||
|
FilterOperation? get changeAddressFilterOperation => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
FilterOperation? get receivingAddressFilterOperation => null;
|
||||||
|
|
||||||
|
Future<Address> _getAddress() async {
|
||||||
|
final mnemonic = await getMnemonic();
|
||||||
|
final seed = CardanoIcarusSeedGenerator(mnemonic).generate();
|
||||||
|
final cip1852 = Cip1852.fromSeed(seed, Cip1852Coins.cardanoIcarus);
|
||||||
|
final derivationAccount = cip1852.purpose.coin.account(0);
|
||||||
|
final shelley = CardanoShelley.fromCip1852Object(derivationAccount)
|
||||||
|
.change(Bip44Changes.chainExt)
|
||||||
|
.addressIndex(0);
|
||||||
|
final paymentPublicKey = shelley.bip44.publicKey.compressed;
|
||||||
|
final stakePublicKey = shelley.bip44Sk.publicKey.compressed;
|
||||||
|
final addressStr = ADABaseAddress.fromPublicKey(
|
||||||
|
basePubkeyBytes: paymentPublicKey,
|
||||||
|
stakePubkeyBytes: stakePublicKey,
|
||||||
|
).address;
|
||||||
|
return Address(
|
||||||
|
walletId: walletId,
|
||||||
|
value: addressStr,
|
||||||
|
publicKey: paymentPublicKey,
|
||||||
|
derivationIndex: 0,
|
||||||
|
derivationPath: DerivationPath()..value = _addressDerivationPath,
|
||||||
|
type: AddressType.cardanoShelley,
|
||||||
|
subType: AddressSubType.receiving,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> checkSaveInitialReceivingAddress() async {
|
||||||
|
try {
|
||||||
|
final Address? address = await getCurrentReceivingAddress();
|
||||||
|
|
||||||
|
if (address == null) {
|
||||||
|
final address = await _getAddress();
|
||||||
|
|
||||||
|
await mainDB.updateOrPutAddresses([address]);
|
||||||
|
}
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"$runtimeType checkSaveInitialReceivingAddress() failed: $e\n$s",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> pingCheck() async {
|
||||||
|
try {
|
||||||
|
await updateProvider();
|
||||||
|
|
||||||
|
final health = await blockfrostProvider!.request(
|
||||||
|
BlockfrostRequestBackendHealthStatus(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Future.value(health);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"Error ping checking in cardano_wallet.dart: $e\n$s",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
return Future.value(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
|
||||||
|
await updateProvider();
|
||||||
|
|
||||||
|
if (info.cachedBalance.spendable.raw == BigInt.zero) {
|
||||||
|
return Amount(
|
||||||
|
rawValue: BigInt.zero,
|
||||||
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final params = await blockfrostProvider!.request(
|
||||||
|
BlockfrostRequestLatestEpochProtocolParameters(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final fee = params.calculateFee(284);
|
||||||
|
|
||||||
|
return Amount(
|
||||||
|
rawValue: fee,
|
||||||
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<FeeObject> get fees async {
|
||||||
|
try {
|
||||||
|
await updateProvider();
|
||||||
|
|
||||||
|
final params = await blockfrostProvider!.request(
|
||||||
|
BlockfrostRequestLatestEpochProtocolParameters(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 284 is the size of a basic transaction with one input and two outputs (change and recipient)
|
||||||
|
final fee = params.calculateFee(284).toInt();
|
||||||
|
|
||||||
|
return FeeObject(
|
||||||
|
numberOfBlocksFast: 2,
|
||||||
|
numberOfBlocksAverage: 2,
|
||||||
|
numberOfBlocksSlow: 2,
|
||||||
|
fast: fee,
|
||||||
|
medium: fee,
|
||||||
|
slow: fee,
|
||||||
|
);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"Error getting fees in cardano_wallet.dart: $e\n$s",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<TxData> prepareSend({required TxData txData}) async {
|
||||||
|
try {
|
||||||
|
await updateProvider();
|
||||||
|
|
||||||
|
if (txData.amount!.raw < ADAHelper.toLovelaces("1")) {
|
||||||
|
throw Exception("By network rules, you can send minimum 1 ADA");
|
||||||
|
}
|
||||||
|
|
||||||
|
final utxos = await blockfrostProvider!.request(
|
||||||
|
BlockfrostRequestAddressUTXOsOfAGivenAsset(
|
||||||
|
address: ADAAddress.fromAddress(
|
||||||
|
(await getCurrentReceivingAddress())!.value,
|
||||||
|
),
|
||||||
|
asset: "lovelace",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
var leftAmountForUtxos = txData.amount!.raw;
|
||||||
|
final listOfUtxosToBeUsed = <ADAAccountUTXOResponse>[];
|
||||||
|
var totalBalance = BigInt.zero;
|
||||||
|
|
||||||
|
for (final utxo in utxos) {
|
||||||
|
if (!(leftAmountForUtxos <= BigInt.parse("0"))) {
|
||||||
|
leftAmountForUtxos -= BigInt.parse(utxo.amount.first.quantity);
|
||||||
|
listOfUtxosToBeUsed.add(utxo);
|
||||||
|
}
|
||||||
|
totalBalance += BigInt.parse(utxo.amount.first.quantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftAmountForUtxos > BigInt.parse("0") || totalBalance < txData.amount!.raw) {
|
||||||
|
throw Exception("Insufficient balance");
|
||||||
|
}
|
||||||
|
|
||||||
|
final bip32 = CardanoIcarusBip32.fromSeed(CardanoIcarusSeedGenerator(await getMnemonic()).generate());
|
||||||
|
final spend = bip32.derivePath("1852'/1815'/0'/0/0");
|
||||||
|
final privateKey = AdaPrivateKey.fromBytes(spend.privateKey.raw);
|
||||||
|
|
||||||
|
// Calculate fees with example tx
|
||||||
|
final exampleFee = ADAHelper.toLovelaces("0.10");
|
||||||
|
final change = TransactionOutput(address: ADABaseAddress((await getCurrentReceivingAddress())!.value), amount: Value(coin: totalBalance - (txData.amount!.raw)));
|
||||||
|
final body = TransactionBody(
|
||||||
|
inputs: listOfUtxosToBeUsed.map((e) => TransactionInput(transactionId: TransactionHash.fromHex(e.txHash), index: e.outputIndex)).toList(),
|
||||||
|
outputs: [change, TransactionOutput(address: ADABaseAddress(txData.recipients!.first.address), amount: Value(coin: txData.amount!.raw - exampleFee))],
|
||||||
|
fee: exampleFee,
|
||||||
|
);
|
||||||
|
final exampleTx = ADATransaction(
|
||||||
|
body: body,
|
||||||
|
witnessSet: TransactionWitnessSet(vKeys: [
|
||||||
|
privateKey.createSignatureWitness(body.toHash().data),
|
||||||
|
],)
|
||||||
|
,);
|
||||||
|
final params = await blockfrostProvider!.request(BlockfrostRequestLatestEpochProtocolParameters());
|
||||||
|
final fee = params.calculateFee(exampleTx.size);
|
||||||
|
|
||||||
|
// Check if we are sending all balance, which means no change and only one output for recipient.
|
||||||
|
if (totalBalance == txData.amount!.raw) {
|
||||||
|
final List<TxRecipient> newRecipients = [(
|
||||||
|
address: txData.recipients!.first.address,
|
||||||
|
amount: Amount(
|
||||||
|
rawValue: txData.amount!.raw - fee,
|
||||||
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
|
),
|
||||||
|
isChange: txData.recipients!.first.isChange,
|
||||||
|
),];
|
||||||
|
return txData.copyWith(
|
||||||
|
fee: Amount(
|
||||||
|
rawValue: fee,
|
||||||
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
|
),
|
||||||
|
recipients: newRecipients,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (txData.amount!.raw + fee > totalBalance) {
|
||||||
|
throw Exception("Insufficient balance for fee");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimum change in Cardano is 1 ADA and we need to have enough balance for that
|
||||||
|
if (totalBalance - (txData.amount!.raw + fee) < ADAHelper.toLovelaces("1")) {
|
||||||
|
throw Exception("Not enough balance for change. By network rules, please either send all balance or leave at least 1 ADA change.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return txData.copyWith(
|
||||||
|
fee: Amount(
|
||||||
|
rawValue: fee,
|
||||||
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"$runtimeType Cardano prepareSend failed: $e\n$s",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<TxData> confirmSend({required TxData txData}) async {
|
||||||
|
try {
|
||||||
|
await updateProvider();
|
||||||
|
|
||||||
|
final utxos = await blockfrostProvider!.request(
|
||||||
|
BlockfrostRequestAddressUTXOsOfAGivenAsset(
|
||||||
|
address: ADAAddress.fromAddress(
|
||||||
|
(await getCurrentReceivingAddress())!.value,
|
||||||
|
),
|
||||||
|
asset: "lovelace",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
var leftAmountForUtxos = txData.amount!.raw + txData.fee!.raw;
|
||||||
|
final listOfUtxosToBeUsed = <ADAAccountUTXOResponse>[];
|
||||||
|
var totalBalance = BigInt.zero;
|
||||||
|
|
||||||
|
for (final utxo in utxos) {
|
||||||
|
if (!(leftAmountForUtxos <= BigInt.parse("0"))) {
|
||||||
|
leftAmountForUtxos -= BigInt.parse(utxo.amount.first.quantity);
|
||||||
|
listOfUtxosToBeUsed.add(utxo);
|
||||||
|
}
|
||||||
|
totalBalance += BigInt.parse(utxo.amount.first.quantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalUtxoAmount = BigInt.zero;
|
||||||
|
|
||||||
|
for (final utxo in listOfUtxosToBeUsed) {
|
||||||
|
totalUtxoAmount += BigInt.parse(utxo.amount.first.quantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
final bip32 = CardanoIcarusBip32.fromSeed(CardanoIcarusSeedGenerator(await getMnemonic()).generate());
|
||||||
|
final spend = bip32.derivePath("1852'/1815'/0'/0/0");
|
||||||
|
final privateKey = AdaPrivateKey.fromBytes(spend.privateKey.raw);
|
||||||
|
|
||||||
|
final change = TransactionOutput(address: ADABaseAddress((await getCurrentReceivingAddress())!.value), amount: Value(coin: totalUtxoAmount - (txData.amount!.raw + txData.fee!.raw)));
|
||||||
|
List<TransactionOutput> outputs = [];
|
||||||
|
if (totalBalance == (txData.amount!.raw + txData.fee!.raw)) {
|
||||||
|
outputs = [TransactionOutput(address: ADABaseAddress(txData.recipients!.first.address), amount: Value(coin: txData.amount!.raw))];
|
||||||
|
} else {
|
||||||
|
outputs = [change, TransactionOutput(address: ADABaseAddress(txData.recipients!.first.address), amount: Value(coin: txData.amount!.raw))];
|
||||||
|
}
|
||||||
|
final body = TransactionBody(
|
||||||
|
inputs: listOfUtxosToBeUsed.map((e) => TransactionInput(transactionId: TransactionHash.fromHex(e.txHash), index: e.outputIndex)).toList(),
|
||||||
|
outputs: outputs,
|
||||||
|
fee: txData.fee!.raw,
|
||||||
|
);
|
||||||
|
final tx = ADATransaction(
|
||||||
|
body: body,
|
||||||
|
witnessSet: TransactionWitnessSet(vKeys: [
|
||||||
|
privateKey.createSignatureWitness(body.toHash().data),
|
||||||
|
],)
|
||||||
|
,);
|
||||||
|
|
||||||
|
final sentTx = await blockfrostProvider!.request(BlockfrostRequestSubmitTransaction(
|
||||||
|
transactionCborBytes: tx.serialize(),),);
|
||||||
|
return txData.copyWith(
|
||||||
|
txid: sentTx,
|
||||||
|
);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"$runtimeType Cardano confirmSend failed: $e\n$s",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> recover({required bool isRescan}) async {
|
||||||
|
await refreshMutex.protect(() async {
|
||||||
|
final addressStruct = await _getAddress();
|
||||||
|
|
||||||
|
await mainDB.updateOrPutAddresses([addressStruct]);
|
||||||
|
|
||||||
|
if (info.cachedReceivingAddress != addressStruct.value) {
|
||||||
|
await info.updateReceivingAddress(
|
||||||
|
newAddress: addressStruct.value,
|
||||||
|
isar: mainDB.isar,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Future.wait([
|
||||||
|
updateBalance(),
|
||||||
|
updateChainHeight(),
|
||||||
|
updateTransactions(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateBalance() async {
|
||||||
|
try {
|
||||||
|
await updateProvider();
|
||||||
|
|
||||||
|
final addressUtxos = await blockfrostProvider!.request(
|
||||||
|
BlockfrostRequestAddressUTXOsOfAGivenAsset(
|
||||||
|
address: ADAAddress.fromAddress(
|
||||||
|
(await getCurrentReceivingAddress())!.value,
|
||||||
|
),
|
||||||
|
asset: "lovelace",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
BigInt totalBalanceInLovelace = BigInt.parse("0");
|
||||||
|
for (final utxo in addressUtxos) {
|
||||||
|
totalBalanceInLovelace += BigInt.parse(utxo.amount.first.quantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
final balance = Balance(
|
||||||
|
total: Amount(
|
||||||
|
rawValue: totalBalanceInLovelace,
|
||||||
|
fractionDigits: cryptoCurrency.fractionDigits,),
|
||||||
|
spendable: Amount(
|
||||||
|
rawValue: totalBalanceInLovelace,
|
||||||
|
fractionDigits: cryptoCurrency.fractionDigits,),
|
||||||
|
blockedTotal: Amount(
|
||||||
|
rawValue: BigInt.zero,
|
||||||
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
|
),
|
||||||
|
pendingSpendable: Amount(
|
||||||
|
rawValue: BigInt.zero,
|
||||||
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await info.updateBalance(newBalance: balance, isar: mainDB.isar);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"Error getting balance in cardano_wallet.dart: $e\n$s",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateChainHeight() async {
|
||||||
|
try {
|
||||||
|
await updateProvider();
|
||||||
|
|
||||||
|
final latestBlock = await blockfrostProvider!.request(
|
||||||
|
BlockfrostRequestLatestBlock(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await info.updateCachedChainHeight(
|
||||||
|
newHeight: latestBlock.height == null ? 0 : latestBlock.height!,
|
||||||
|
isar: mainDB.isar,);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"Error updating transactions in cardano_wallet.dart: $e\n$s",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateNode() async {
|
||||||
|
await refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateTransactions() async {
|
||||||
|
try {
|
||||||
|
await updateProvider();
|
||||||
|
|
||||||
|
final currentAddr = (await getCurrentReceivingAddress())!.value;
|
||||||
|
|
||||||
|
final txsList = await blockfrostProvider!.request(
|
||||||
|
BlockfrostRequestAddressTransactions(
|
||||||
|
ADAAddress.fromAddress(
|
||||||
|
currentAddr,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final parsedTxsList =
|
||||||
|
List<Tuple2<isar.Transaction, Address>>.empty(growable: true);
|
||||||
|
|
||||||
|
for (final tx in txsList) {
|
||||||
|
final txInfo = await blockfrostProvider!.request(
|
||||||
|
BlockfrostRequestSpecificTransaction(tx.txHash),
|
||||||
|
);
|
||||||
|
final utxoInfo = await blockfrostProvider!.request(
|
||||||
|
BlockfrostRequestTransactionUTXOs(tx.txHash),
|
||||||
|
);
|
||||||
|
var txType = isar.TransactionType.unknown;
|
||||||
|
|
||||||
|
for (final input in utxoInfo.inputs) {
|
||||||
|
if (input.address == currentAddr) {
|
||||||
|
txType = isar.TransactionType.outgoing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (txType == isar.TransactionType.outgoing) {
|
||||||
|
var isSelfTx = true;
|
||||||
|
for (final output in utxoInfo.outputs) {
|
||||||
|
if (output.address != currentAddr) {
|
||||||
|
isSelfTx = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isSelfTx) {
|
||||||
|
txType = isar.TransactionType.sentToSelf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (txType == isar.TransactionType.unknown) {
|
||||||
|
for (final output in utxoInfo.outputs) {
|
||||||
|
if (output.address == currentAddr) {
|
||||||
|
txType = isar.TransactionType.incoming;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var receiverAddr = "Unknown?";
|
||||||
|
var amount = 0;
|
||||||
|
|
||||||
|
if (txType == isar.TransactionType.incoming) {
|
||||||
|
receiverAddr = currentAddr;
|
||||||
|
for (final output in utxoInfo.outputs) {
|
||||||
|
if (output.address == currentAddr && output.amount.first.unit == "lovelace") {
|
||||||
|
amount += int.parse(output.amount.first.quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (txType == isar.TransactionType.outgoing) {
|
||||||
|
for (final output in utxoInfo.outputs) {
|
||||||
|
if (output.address != currentAddr && output.amount.first.unit == "lovelace") {
|
||||||
|
receiverAddr = output.address;
|
||||||
|
amount += int.parse(output.amount.first.quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (txType == isar.TransactionType.sentToSelf) {
|
||||||
|
receiverAddr = currentAddr;
|
||||||
|
for (final output in utxoInfo.outputs) {
|
||||||
|
if (output.amount.first.unit == "lovelace") {
|
||||||
|
amount += int.parse(output.amount.first.quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final transaction = isar.Transaction(
|
||||||
|
walletId: walletId,
|
||||||
|
txid: txInfo.hash,
|
||||||
|
timestamp: tx.blockTime,
|
||||||
|
type: txType,
|
||||||
|
subType: isar.TransactionSubType.none,
|
||||||
|
amount: amount,
|
||||||
|
amountString: Amount(
|
||||||
|
rawValue: BigInt.from(amount),
|
||||||
|
fractionDigits: cryptoCurrency.fractionDigits,
|
||||||
|
).toJsonString(),
|
||||||
|
fee: int.parse(txInfo.fees),
|
||||||
|
height: txInfo.blockHeight,
|
||||||
|
isCancelled: false,
|
||||||
|
isLelantus: false,
|
||||||
|
slateId: null,
|
||||||
|
otherData: null,
|
||||||
|
inputs: [],
|
||||||
|
outputs: [],
|
||||||
|
nonce: null,
|
||||||
|
numberOfMessages: 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
final txAddress = Address(
|
||||||
|
walletId: walletId,
|
||||||
|
value: receiverAddr,
|
||||||
|
publicKey: List<int>.empty(),
|
||||||
|
derivationIndex: 0,
|
||||||
|
derivationPath: DerivationPath()..value = _addressDerivationPath,
|
||||||
|
type: AddressType.cardanoShelley,
|
||||||
|
subType: txType == isar.TransactionType.outgoing
|
||||||
|
? AddressSubType.unknown
|
||||||
|
: AddressSubType.receiving,
|
||||||
|
);
|
||||||
|
|
||||||
|
parsedTxsList.add(Tuple2(transaction, txAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
await mainDB.addNewTransactionData(parsedTxsList, walletId);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"Error updating transactions in cardano_wallet.dart: $e\n$s",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> updateUTXOs() async {
|
||||||
|
// TODO: implement updateUTXOs
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateProvider() async {
|
||||||
|
final currentNode = getCurrentNode();
|
||||||
|
final client = HttpClient();
|
||||||
|
if (prefs.useTor) {
|
||||||
|
final proxyInfo = TorService.sharedInstance.getProxyInfo();
|
||||||
|
final proxySettings = ProxySettings(
|
||||||
|
proxyInfo.host,
|
||||||
|
proxyInfo.port,
|
||||||
|
);
|
||||||
|
SocksTCPClient.assignToHttpClient(client, [proxySettings]);
|
||||||
|
}
|
||||||
|
blockfrostProvider = BlockforestProvider(
|
||||||
|
BlockfrostHttpProvider(
|
||||||
|
url: "${currentNode.host}:${currentNode.port}/",
|
||||||
|
client: client,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ import 'impl/banano_wallet.dart';
|
||||||
import 'impl/bitcoin_frost_wallet.dart';
|
import 'impl/bitcoin_frost_wallet.dart';
|
||||||
import 'impl/bitcoin_wallet.dart';
|
import 'impl/bitcoin_wallet.dart';
|
||||||
import 'impl/bitcoincash_wallet.dart';
|
import 'impl/bitcoincash_wallet.dart';
|
||||||
|
import 'impl/cardano_wallet.dart';
|
||||||
import 'impl/dash_wallet.dart';
|
import 'impl/dash_wallet.dart';
|
||||||
import 'impl/dogecoin_wallet.dart';
|
import 'impl/dogecoin_wallet.dart';
|
||||||
import 'impl/ecash_wallet.dart';
|
import 'impl/ecash_wallet.dart';
|
||||||
|
@ -324,6 +325,9 @@ abstract class Wallet<T extends CryptoCurrency> {
|
||||||
case const (Bitcoincash):
|
case const (Bitcoincash):
|
||||||
return BitcoincashWallet(net);
|
return BitcoincashWallet(net);
|
||||||
|
|
||||||
|
case const (Cardano):
|
||||||
|
return CardanoWallet(net);
|
||||||
|
|
||||||
case const (Dash):
|
case const (Dash):
|
||||||
return DashWallet(net);
|
return DashWallet(net);
|
||||||
|
|
||||||
|
|
64
pubspec.lock
64
pubspec.lock
|
@ -155,6 +155,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.0"
|
version: "0.1.0"
|
||||||
|
blockchain_utils:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: blockchain_utils
|
||||||
|
sha256: aebc3a32b927b34f638817c4bfdb85f86a97e6ad35f0cd962660b0c6e8d5c56b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -527,10 +535,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: device_info_plus
|
name: device_info_plus
|
||||||
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
|
sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.1.2"
|
version: "9.1.2"
|
||||||
device_info_plus_platform_interface:
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1118,18 +1126,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.5"
|
version: "10.0.4"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_flutter_testing
|
name: leak_tracker_flutter_testing
|
||||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.5"
|
version: "3.0.3"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1229,10 +1237,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.8.0"
|
||||||
memoize:
|
memoize:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1245,10 +1253,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.12.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1338,6 +1346,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.2"
|
||||||
|
on_chain:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: on_chain
|
||||||
|
sha256: "4040c187be82f6f6414110139f6e70fe304f23f63111c3ee60438c539b1a1dce"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.1"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1486,18 +1502,18 @@ packages:
|
||||||
dependency: "direct overridden"
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
name: pinenacl
|
name: pinenacl
|
||||||
sha256: "57e907beaacbc3c024a098910b6240758e899674de07d6949a67b52fd984cbdf"
|
sha256: e5fb0bce1717b7f136f35ee98b5c02b3e6383211f8a77ca882fa7812232a07b9
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.3.4"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.5"
|
version: "3.1.4"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1854,32 +1870,32 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test
|
name: test
|
||||||
sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e"
|
sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.25.7"
|
version: "1.25.2"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.2"
|
version: "0.7.0"
|
||||||
test_core:
|
test_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_core
|
name: test_core
|
||||||
sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696"
|
sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.4"
|
version: "0.6.0"
|
||||||
tezart:
|
tezart:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: d000cc245e51d3ff50e6467960fb3d9159d5b2a9
|
ref: "13fa937ea9a9fc34caf047e068df9535f65c27ad"
|
||||||
resolved-ref: d000cc245e51d3ff50e6467960fb3d9159d5b2a9
|
resolved-ref: "13fa937ea9a9fc34caf047e068df9535f65c27ad"
|
||||||
url: "https://github.com/cypherstack/tezart.git"
|
url: "https://github.com/cypherstack/tezart.git"
|
||||||
source: git
|
source: git
|
||||||
version: "2.0.5"
|
version: "2.0.5"
|
||||||
|
@ -2072,7 +2088,7 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.5"
|
version: "14.2.5"
|
||||||
|
@ -2174,7 +2190,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.1"
|
||||||
win32:
|
win32:
|
||||||
dependency: "direct overridden"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
|
sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
|
||||||
|
|
|
@ -57,6 +57,7 @@ final List<CryptoCurrency> _supportedCoins = List.unmodifiable([
|
||||||
Banano(CryptoCurrencyNetwork.main),
|
Banano(CryptoCurrencyNetwork.main),
|
||||||
Bitcoincash(CryptoCurrencyNetwork.main),
|
Bitcoincash(CryptoCurrencyNetwork.main),
|
||||||
BitcoinFrost(CryptoCurrencyNetwork.main),
|
BitcoinFrost(CryptoCurrencyNetwork.main),
|
||||||
|
Cardano(CryptoCurrencyNetwork.main),
|
||||||
Dash(CryptoCurrencyNetwork.main),
|
Dash(CryptoCurrencyNetwork.main),
|
||||||
Dogecoin(CryptoCurrencyNetwork.main),
|
Dogecoin(CryptoCurrencyNetwork.main),
|
||||||
Ecash(CryptoCurrencyNetwork.main),
|
Ecash(CryptoCurrencyNetwork.main),
|
||||||
|
|
|
@ -204,6 +204,8 @@ dependencies:
|
||||||
path: packages/camera/camera_windows
|
path: packages/camera/camera_windows
|
||||||
camera_platform_interface: ^2.8.0
|
camera_platform_interface: ^2.8.0
|
||||||
camera_macos: ^0.0.8
|
camera_macos: ^0.0.8
|
||||||
|
blockchain_utils: ^3.3.0
|
||||||
|
on_chain: ^4.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in a new issue