mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-25 20:16:05 +00:00
remove fees from send screen, send and receive transactions working
This commit is contained in:
parent
3a50b58b09
commit
92d4a6f6d6
8 changed files with 289 additions and 117 deletions
|
@ -8,18 +8,18 @@ import 'package:http/io_client.dart' as ioc;
|
||||||
|
|
||||||
part 'node.g.dart';
|
part 'node.g.dart';
|
||||||
|
|
||||||
Uri createUriFromElectrumAddress(String address) =>
|
Uri createUriFromElectrumAddress(String address) => Uri.tryParse('tcp://$address')!;
|
||||||
Uri.tryParse('tcp://$address')!;
|
|
||||||
|
|
||||||
@HiveType(typeId: Node.typeId)
|
@HiveType(typeId: Node.typeId)
|
||||||
class Node extends HiveObject with Keyable {
|
class Node extends HiveObject with Keyable {
|
||||||
Node(
|
Node({
|
||||||
{this.login,
|
this.login,
|
||||||
this.password,
|
this.password,
|
||||||
this.useSSL,
|
this.useSSL,
|
||||||
this.trusted = false,
|
this.trusted = false,
|
||||||
String? uri,
|
String? uri,
|
||||||
WalletType? type,}) {
|
WalletType? type,
|
||||||
|
}) {
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
uriRaw = uri;
|
uriRaw = uri;
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,12 @@ class Node extends HiveObject with Keyable {
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return Uri.https(uriRaw, '');
|
return Uri.https(uriRaw, '');
|
||||||
case WalletType.nano:
|
case WalletType.nano:
|
||||||
return Uri.https(uriRaw, '');
|
case WalletType.banano:
|
||||||
|
if (uriRaw.contains("https") || uriRaw.endsWith("443") || isSSL) {
|
||||||
|
return Uri.https(uriRaw, '');
|
||||||
|
} else {
|
||||||
|
return Uri.http(uriRaw, '');
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected type ${type.toString()} for Node uri');
|
throw Exception('Unexpected type ${type.toString()} for Node uri');
|
||||||
}
|
}
|
||||||
|
@ -80,12 +85,12 @@ class Node extends HiveObject with Keyable {
|
||||||
@override
|
@override
|
||||||
bool operator ==(other) =>
|
bool operator ==(other) =>
|
||||||
other is Node &&
|
other is Node &&
|
||||||
(other.uriRaw == uriRaw &&
|
(other.uriRaw == uriRaw &&
|
||||||
other.login == login &&
|
other.login == login &&
|
||||||
other.password == password &&
|
other.password == password &&
|
||||||
other.typeRaw == typeRaw &&
|
other.typeRaw == typeRaw &&
|
||||||
other.useSSL == useSSL &&
|
other.useSSL == useSSL &&
|
||||||
other.trusted == trusted);
|
other.trusted == trusted);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
|
@ -133,27 +138,23 @@ class Node extends HiveObject with Keyable {
|
||||||
final path = '/json_rpc';
|
final path = '/json_rpc';
|
||||||
final rpcUri = isSSL ? Uri.https(uri.authority, path) : Uri.http(uri.authority, path);
|
final rpcUri = isSSL ? Uri.https(uri.authority, path) : Uri.http(uri.authority, path);
|
||||||
final realm = 'monero-rpc';
|
final realm = 'monero-rpc';
|
||||||
final body = {
|
final body = {'jsonrpc': '2.0', 'id': '0', 'method': 'get_info'};
|
||||||
'jsonrpc': '2.0',
|
|
||||||
'id': '0',
|
|
||||||
'method': 'get_info'
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final authenticatingClient = HttpClient();
|
final authenticatingClient = HttpClient();
|
||||||
|
|
||||||
authenticatingClient.addCredentials(
|
authenticatingClient.addCredentials(
|
||||||
rpcUri,
|
rpcUri,
|
||||||
realm,
|
realm,
|
||||||
HttpClientDigestCredentials(login ?? '', password ?? ''),
|
HttpClientDigestCredentials(login ?? '', password ?? ''),
|
||||||
);
|
);
|
||||||
|
|
||||||
final http.Client client = ioc.IOClient(authenticatingClient);
|
final http.Client client = ioc.IOClient(authenticatingClient);
|
||||||
|
|
||||||
final response = await client.post(
|
final response = await client.post(
|
||||||
rpcUri,
|
rpcUri,
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: json.encode(body),
|
body: json.encode(body),
|
||||||
);
|
);
|
||||||
|
|
||||||
client.close();
|
client.close();
|
||||||
|
|
|
@ -20,7 +20,6 @@ class NanoClient {
|
||||||
static const String DEFAULT_REPRESENTATIVE =
|
static const String DEFAULT_REPRESENTATIVE =
|
||||||
"nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579";
|
"nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579";
|
||||||
|
|
||||||
// final _httpClient = http.Client();
|
|
||||||
StreamSubscription<Transfer>? subscription;
|
StreamSubscription<Transfer>? subscription;
|
||||||
Node? _node;
|
Node? _node;
|
||||||
|
|
||||||
|
@ -195,6 +194,160 @@ class NanoClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> receiveBlock({
|
||||||
|
required String blockHash,
|
||||||
|
required String source,
|
||||||
|
required String amountRaw,
|
||||||
|
required String destinationAddress,
|
||||||
|
required String privateKey,
|
||||||
|
}) async {
|
||||||
|
bool openBlock = false;
|
||||||
|
|
||||||
|
final headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
|
||||||
|
// first check if the account is open:
|
||||||
|
// get the account info (we need the frontier and representative):
|
||||||
|
final infoBody = jsonEncode({
|
||||||
|
"action": "account_info",
|
||||||
|
"representative": "true",
|
||||||
|
"account": destinationAddress,
|
||||||
|
});
|
||||||
|
final infoResponse = await http.post(
|
||||||
|
_node!.uri,
|
||||||
|
headers: headers,
|
||||||
|
body: infoBody,
|
||||||
|
);
|
||||||
|
final infoData = jsonDecode(infoResponse.body);
|
||||||
|
|
||||||
|
if (infoData["error"] != null) {
|
||||||
|
// account is not open yet, we need to create an open block:
|
||||||
|
openBlock = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first get the account balance:
|
||||||
|
final balanceBody = jsonEncode({
|
||||||
|
"action": "account_balance",
|
||||||
|
"account": destinationAddress,
|
||||||
|
});
|
||||||
|
|
||||||
|
final balanceResponse = await http.post(
|
||||||
|
_node!.uri,
|
||||||
|
headers: headers,
|
||||||
|
body: balanceBody,
|
||||||
|
);
|
||||||
|
|
||||||
|
final balanceData = jsonDecode(balanceResponse.body);
|
||||||
|
final BigInt currentBalance = BigInt.parse(balanceData["balance"].toString());
|
||||||
|
final BigInt txAmount = BigInt.parse(amountRaw);
|
||||||
|
final BigInt balanceAfterTx = currentBalance + txAmount;
|
||||||
|
|
||||||
|
String frontier = infoData["frontier"].toString();
|
||||||
|
String representative = infoData["representative"].toString();
|
||||||
|
|
||||||
|
if (openBlock) {
|
||||||
|
// we don't have a representative set yet:
|
||||||
|
representative = DEFAULT_REPRESENTATIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// link = send block hash:
|
||||||
|
final String link = blockHash;
|
||||||
|
// this "linkAsAccount" is meaningless:
|
||||||
|
final String linkAsAccount = NanoAccounts.createAccount(NanoAccountType.NANO, blockHash);
|
||||||
|
|
||||||
|
// construct the receive block:
|
||||||
|
Map<String, String> receiveBlock = {
|
||||||
|
"type": "state",
|
||||||
|
"account": destinationAddress,
|
||||||
|
"previous":
|
||||||
|
openBlock ? "0000000000000000000000000000000000000000000000000000000000000000" : frontier,
|
||||||
|
"representative": representative,
|
||||||
|
"balance": balanceAfterTx.toString(),
|
||||||
|
"link": link,
|
||||||
|
"link_as_account": linkAsAccount,
|
||||||
|
};
|
||||||
|
|
||||||
|
// sign the receive block:
|
||||||
|
final String hash = NanoBlocks.computeStateHash(
|
||||||
|
NanoAccountType.NANO,
|
||||||
|
receiveBlock["account"]!,
|
||||||
|
receiveBlock["previous"]!,
|
||||||
|
receiveBlock["representative"]!,
|
||||||
|
BigInt.parse(receiveBlock["balance"]!),
|
||||||
|
receiveBlock["link"]!,
|
||||||
|
);
|
||||||
|
final String signature = NanoSignatures.signBlock(hash, privateKey);
|
||||||
|
|
||||||
|
// get PoW for the receive block:
|
||||||
|
String? work;
|
||||||
|
if (openBlock) {
|
||||||
|
work = await requestWork(NanoAccounts.extractPublicKey(destinationAddress));
|
||||||
|
} else {
|
||||||
|
work = await requestWork(frontier);
|
||||||
|
}
|
||||||
|
receiveBlock["link_as_account"] = linkAsAccount;
|
||||||
|
receiveBlock["signature"] = signature;
|
||||||
|
receiveBlock["work"] = work;
|
||||||
|
|
||||||
|
// process the receive block:
|
||||||
|
|
||||||
|
final processBody = jsonEncode({
|
||||||
|
"action": "process",
|
||||||
|
"json_block": "true",
|
||||||
|
"subtype": "receive",
|
||||||
|
"block": receiveBlock,
|
||||||
|
});
|
||||||
|
final processResponse = await http.post(
|
||||||
|
_node!.uri,
|
||||||
|
headers: headers,
|
||||||
|
body: processBody,
|
||||||
|
);
|
||||||
|
|
||||||
|
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
|
||||||
|
if (decoded.containsKey("error")) {
|
||||||
|
throw Exception("Received error ${decoded["error"]}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the number of blocks received:
|
||||||
|
Future<int> confirmAllReceivable({
|
||||||
|
required String destinationAddress,
|
||||||
|
required String privateKey,
|
||||||
|
}) async {
|
||||||
|
final receivableResponse = await http.post(_node!.uri,
|
||||||
|
headers: {"Content-Type": "application/json"},
|
||||||
|
body: jsonEncode({
|
||||||
|
"action": "receivable",
|
||||||
|
"source": "true",
|
||||||
|
"account": destinationAddress,
|
||||||
|
"count": "-1",
|
||||||
|
}));
|
||||||
|
|
||||||
|
final receivableData = await jsonDecode(receivableResponse.body);
|
||||||
|
if (receivableData["blocks"] == "") {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
final blocks = receivableData["blocks"] as Map<String, dynamic>;
|
||||||
|
// confirm all receivable blocks:
|
||||||
|
for (final blockHash in blocks.keys) {
|
||||||
|
final block = blocks[blockHash];
|
||||||
|
final String amountRaw = block["amount"] as String;
|
||||||
|
final String source = block["source"] as String;
|
||||||
|
await receiveBlock(
|
||||||
|
blockHash: blockHash,
|
||||||
|
source: source,
|
||||||
|
amountRaw: amountRaw,
|
||||||
|
privateKey: privateKey,
|
||||||
|
destinationAddress: destinationAddress,
|
||||||
|
);
|
||||||
|
// a bit of a hack:
|
||||||
|
await Future<void>.delayed(const Duration(seconds: 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks.keys.length;
|
||||||
|
}
|
||||||
|
|
||||||
Future<dynamic> getTransactionDetails(String transactionHash) async {
|
Future<dynamic> getTransactionDetails(String transactionHash) async {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
@ -219,8 +372,7 @@ class NanoClient {
|
||||||
// Map the transactions list to NanoTransactionModel using the factory
|
// Map the transactions list to NanoTransactionModel using the factory
|
||||||
// reversed so that the DateTime is correct when local_timestamp is absent
|
// reversed so that the DateTime is correct when local_timestamp is absent
|
||||||
return transactions.reversed
|
return transactions.reversed
|
||||||
.map<NanoTransactionModel>(
|
.map<NanoTransactionModel>((transaction) => NanoTransactionModel.fromJson(transaction))
|
||||||
(transaction) => NanoTransactionModel.fromJson(transaction as Map<String, dynamic>))
|
|
||||||
.toList();
|
.toList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
|
|
|
@ -17,17 +17,17 @@ class NanoTransactionModel {
|
||||||
required this.account,
|
required this.account,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory NanoTransactionModel.fromJson(Map<String, dynamic> json) {
|
factory NanoTransactionModel.fromJson(dynamic json) {
|
||||||
DateTime? local_timestamp;
|
DateTime? localTimestamp;
|
||||||
try {
|
try {
|
||||||
local_timestamp =
|
localTimestamp = DateTime.fromMillisecondsSinceEpoch(
|
||||||
DateTime.fromMillisecondsSinceEpoch(int.parse(json["local_timeStamp"] as String) * 1000);
|
int.parse(json["local_timestamp"] as String) * 1000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
local_timestamp = DateTime.now();
|
localTimestamp = DateTime.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
return NanoTransactionModel(
|
return NanoTransactionModel(
|
||||||
date: local_timestamp,
|
date: localTimestamp,
|
||||||
hash: json["hash"] as String,
|
hash: json["hash"] as String,
|
||||||
height: int.parse(json["height"] as String),
|
height: int.parse(json["height"] as String),
|
||||||
type: json["type"] as String,
|
type: json["type"] as String,
|
||||||
|
|
|
@ -117,7 +117,8 @@ abstract class NanoWalletBase
|
||||||
throw Exception("Ethereum Node connection failed");
|
throw Exception("Ethereum Node connection failed");
|
||||||
}
|
}
|
||||||
// _client.setListeners(_privateKey.address, _onNewTransaction);
|
// _client.setListeners(_privateKey.address, _onNewTransaction);
|
||||||
_updateBalance();
|
await _updateBalance();
|
||||||
|
await _receiveAll();
|
||||||
syncStatus = ConnectedSyncStatus();
|
syncStatus = ConnectedSyncStatus();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
syncStatus = FailedSyncStatus();
|
syncStatus = FailedSyncStatus();
|
||||||
|
@ -184,6 +185,19 @@ abstract class NanoWalletBase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _receiveAll() async {
|
||||||
|
int blocksReceived = await this._client.confirmAllReceivable(
|
||||||
|
destinationAddress: _publicAddress,
|
||||||
|
privateKey: _privateKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (blocksReceived > 0) {
|
||||||
|
await Future<void>.delayed(Duration(seconds: 3));
|
||||||
|
_updateBalance();
|
||||||
|
updateTransactions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> updateTransactions() async {
|
Future<void> updateTransactions() async {
|
||||||
try {
|
try {
|
||||||
if (_isTransactionUpdating) {
|
if (_isTransactionUpdating) {
|
||||||
|
@ -255,6 +269,10 @@ abstract class NanoWalletBase
|
||||||
syncStatus = AttemptingSyncStatus();
|
syncStatus = AttemptingSyncStatus();
|
||||||
await _updateBalance();
|
await _updateBalance();
|
||||||
await updateTransactions();
|
await updateTransactions();
|
||||||
|
Timer.periodic(
|
||||||
|
const Duration(minutes: 1),
|
||||||
|
(timer) async => await _receiveAll(),
|
||||||
|
);
|
||||||
|
|
||||||
syncStatus = SyncedSyncStatus();
|
syncStatus = SyncedSyncStatus();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -1,20 +1,13 @@
|
||||||
part of 'nano.dart';
|
part of 'nano.dart';
|
||||||
|
|
||||||
class CWNano extends Nano {
|
class CWNano extends Nano {
|
||||||
// @override
|
|
||||||
// NanoAccountList getAccountList(Object wallet) {
|
|
||||||
// return CWNanoAccountList(wallet);
|
|
||||||
// }
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<String> getNanoWordList(String language) {
|
List<String> getNanoWordList(String language) {
|
||||||
// throw UnimplementedError();
|
|
||||||
return NanoMnemomics.WORDLIST;
|
return NanoMnemomics.WORDLIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource) {
|
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource) {
|
||||||
print("creating NanoWalletService");
|
|
||||||
return NanoWalletService(walletInfoSource);
|
return NanoWalletService(walletInfoSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,15 +54,11 @@ class CWNano extends Nano {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TransactionHistoryBase getTransactionHistory(Object wallet) {
|
TransactionHistoryBase getTransactionHistory(Object wallet) {
|
||||||
// final moneroWallet = wallet as MoneroWallet;
|
|
||||||
// return moneroWallet.transactionHistory;
|
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onStartup() {
|
void onStartup() {}
|
||||||
// monero_wallet_api.onStartup();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Object createNanoTransactionCredentials(List<Output> outputs) {
|
Object createNanoTransactionCredentials(List<Output> outputs) {
|
||||||
|
|
|
@ -449,78 +449,79 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
Theme.of(context).primaryTextTheme.headlineSmall!.decorationColor!),
|
Theme.of(context).primaryTextTheme.headlineSmall!.decorationColor!),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Observer(
|
if (sendViewModel.hasFees)
|
||||||
builder: (_) => GestureDetector(
|
Observer(
|
||||||
onTap: () => _setTransactionPriority(context),
|
builder: (_) => GestureDetector(
|
||||||
child: Container(
|
onTap: () => _setTransactionPriority(context),
|
||||||
padding: EdgeInsets.only(top: 24),
|
child: Container(
|
||||||
child: Row(
|
padding: EdgeInsets.only(top: 24),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: <Widget>[
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Text(
|
children: <Widget>[
|
||||||
S.of(context).send_estimated_fee,
|
Text(
|
||||||
style: TextStyle(
|
S.of(context).send_estimated_fee,
|
||||||
fontSize: 12,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontSize: 12,
|
||||||
//color: Theme.of(context).primaryTextTheme!.displaySmall!.color!,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.white),
|
//color: Theme.of(context).primaryTextTheme!.displaySmall!.color!,
|
||||||
),
|
color: Colors.white),
|
||||||
Container(
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
output.estimatedFee.toString() +
|
|
||||||
' ' +
|
|
||||||
sendViewModel.selectedCryptoCurrency.toString(),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
//color: Theme.of(context).primaryTextTheme!.displaySmall!.color!,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(top: 5),
|
|
||||||
child: sendViewModel.isFiatDisabled
|
|
||||||
? const SizedBox(height: 14)
|
|
||||||
: Text(
|
|
||||||
output.estimatedFeeFiatAmount +
|
|
||||||
' ' +
|
|
||||||
sendViewModel.fiat.title,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.primaryTextTheme
|
|
||||||
.headlineSmall!
|
|
||||||
.decorationColor!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(top: 2, left: 5),
|
|
||||||
child: Icon(
|
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
size: 12,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
)
|
Container(
|
||||||
],
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
output.estimatedFee.toString() +
|
||||||
|
' ' +
|
||||||
|
sendViewModel.selectedCryptoCurrency.toString(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
//color: Theme.of(context).primaryTextTheme!.displaySmall!.color!,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 5),
|
||||||
|
child: sendViewModel.isFiatDisabled
|
||||||
|
? const SizedBox(height: 14)
|
||||||
|
: Text(
|
||||||
|
output.estimatedFeeFiatAmount +
|
||||||
|
' ' +
|
||||||
|
sendViewModel.fiat.title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headlineSmall!
|
||||||
|
.decorationColor!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 2, left: 5),
|
||||||
|
child: Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 12,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
if (sendViewModel.isElectrumWallet)
|
if (sendViewModel.isElectrumWallet)
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 6),
|
padding: EdgeInsets.only(top: 6),
|
||||||
|
|
|
@ -165,6 +165,9 @@ abstract class SendViewModelBase with Store {
|
||||||
bool get isElectrumWallet =>
|
bool get isElectrumWallet =>
|
||||||
_wallet.type == WalletType.bitcoin || _wallet.type == WalletType.litecoin;
|
_wallet.type == WalletType.bitcoin || _wallet.type == WalletType.litecoin;
|
||||||
|
|
||||||
|
@computed
|
||||||
|
bool get hasFees => _wallet.type != WalletType.nano && _wallet.type != WalletType.banano;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
CryptoCurrency selectedCryptoCurrency;
|
CryptoCurrency selectedCryptoCurrency;
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,10 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
return 'https://explorer.havenprotocol.org/search?value=${txId}';
|
return 'https://explorer.havenprotocol.org/search?value=${txId}';
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return 'https://etherscan.io/tx/${txId}';
|
return 'https://etherscan.io/tx/${txId}';
|
||||||
|
case WalletType.nano:
|
||||||
|
return 'https://nanolooker.com/block/${txId}';
|
||||||
|
case WalletType.banano:
|
||||||
|
return 'https://bananolooker.com/block/${txId}';
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -133,6 +137,10 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
return S.current.view_transaction_on + 'explorer.havenprotocol.org';
|
return S.current.view_transaction_on + 'explorer.havenprotocol.org';
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return S.current.view_transaction_on + 'etherscan.io';
|
return S.current.view_transaction_on + 'etherscan.io';
|
||||||
|
case WalletType.nano:
|
||||||
|
return S.current.view_transaction_on + 'nanolooker.com';
|
||||||
|
case WalletType.banano:
|
||||||
|
return S.current.view_transaction_on + 'bananolooker.com';
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue