mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-24 19:25:52 +00:00
Implement HTTP sending and use Julian's error checking
This commit is contained in:
parent
91b4fdf450
commit
cfcd7a7928
2 changed files with 152 additions and 53 deletions
|
@ -1,12 +1,17 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
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:stackwallet/notifications/show_flush_bar.dart';
|
||||||
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
|
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
|
||||||
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
|
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
|
||||||
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
||||||
import 'package:stackwallet/providers/providers.dart';
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
import 'package:stackwallet/route_generator.dart';
|
import 'package:stackwallet/route_generator.dart';
|
||||||
|
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
|
||||||
import 'package:stackwallet/utilities/cfcolors.dart';
|
import 'package:stackwallet/utilities/cfcolors.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||||
import 'package:stackwallet/utilities/format.dart';
|
import 'package:stackwallet/utilities/format.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
|
@ -40,25 +45,25 @@ class _ConfirmTransactionViewState
|
||||||
late final String routeOnSuccessName;
|
late final String routeOnSuccessName;
|
||||||
|
|
||||||
Future<void> _attemptSend(BuildContext context) async {
|
Future<void> _attemptSend(BuildContext context) async {
|
||||||
showDialog<dynamic>(
|
unawaited(showDialog<dynamic>(
|
||||||
context: context,
|
context: context,
|
||||||
useSafeArea: false,
|
useSafeArea: false,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return const SendingTransactionDialog();
|
return const SendingTransactionDialog();
|
||||||
},
|
},
|
||||||
);
|
));
|
||||||
|
|
||||||
final note = transactionInfo["note"] as String? ?? "";
|
final note = transactionInfo["note"] as String? ?? "";
|
||||||
final manager =
|
final manager =
|
||||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final txid = await manager.confirmSend(txData: transactionInfo);
|
final txid = await manager.confirmSend(txData: transactionInfo);
|
||||||
manager.refresh();
|
unawaited(manager.refresh());
|
||||||
|
|
||||||
// save note
|
// save note
|
||||||
ref
|
await ref
|
||||||
.read(notesServiceChangeNotifierProvider(walletId))
|
.read(notesServiceChangeNotifierProvider(walletId))
|
||||||
.editOrAddNote(txid: txid, note: note);
|
.editOrAddNote(txid: txid, note: note);
|
||||||
|
|
||||||
|
@ -66,12 +71,26 @@ class _ConfirmTransactionViewState
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
Navigator.of(context).popUntil(ModalRoute.withName(routeOnSuccessName));
|
Navigator.of(context).popUntil(ModalRoute.withName(routeOnSuccessName));
|
||||||
}
|
}
|
||||||
|
} on BadEpicHttpAddressException catch (_) {
|
||||||
|
if (mounted) {
|
||||||
|
// pop building dialog
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
unawaited(
|
||||||
|
showFloatingFlushBar(
|
||||||
|
type: FlushBarType.warning,
|
||||||
|
message:
|
||||||
|
"Connection failed. Please check the address and try again.",
|
||||||
|
context: context,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
debugPrint("$e\n$s");
|
debugPrint("$e\n$s");
|
||||||
// pop sending dialog
|
// pop sending dialog
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
showDialog<void>(
|
await showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
useSafeArea: false,
|
useSafeArea: false,
|
||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
|
@ -81,10 +100,10 @@ class _ConfirmTransactionViewState
|
||||||
message: e.toString(),
|
message: e.toString(),
|
||||||
rightButton: TextButton(
|
rightButton: TextButton(
|
||||||
style: Theme.of(context).textButtonTheme.style?.copyWith(
|
style: Theme.of(context).textButtonTheme.style?.copyWith(
|
||||||
backgroundColor: MaterialStateProperty.all<Color>(
|
backgroundColor: MaterialStateProperty.all<Color>(
|
||||||
CFColors.buttonGray,
|
CFColors.buttonGray,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Ok",
|
"Ok",
|
||||||
style: STextStyles.button.copyWith(
|
style: STextStyles.button.copyWith(
|
||||||
|
@ -193,9 +212,9 @@ class _ConfirmTransactionViewState
|
||||||
.select((value) => value.locale),
|
.select((value) => value.locale),
|
||||||
),
|
),
|
||||||
)} ${ref.watch(
|
)} ${ref.watch(
|
||||||
managerProvider
|
managerProvider
|
||||||
.select((value) => value.coin),
|
.select((value) => value.coin),
|
||||||
).ticker}",
|
).ticker}",
|
||||||
style: STextStyles.itemSubtitle12,
|
style: STextStyles.itemSubtitle12,
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
),
|
),
|
||||||
|
@ -221,9 +240,9 @@ class _ConfirmTransactionViewState
|
||||||
.select((value) => value.locale),
|
.select((value) => value.locale),
|
||||||
),
|
),
|
||||||
)} ${ref.watch(
|
)} ${ref.watch(
|
||||||
managerProvider
|
managerProvider
|
||||||
.select((value) => value.coin),
|
.select((value) => value.coin),
|
||||||
).ticker}",
|
).ticker}",
|
||||||
style: STextStyles.itemSubtitle12,
|
style: STextStyles.itemSubtitle12,
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
),
|
),
|
||||||
|
@ -273,9 +292,9 @@ class _ConfirmTransactionViewState
|
||||||
.select((value) => value.locale),
|
.select((value) => value.locale),
|
||||||
),
|
),
|
||||||
)} ${ref.watch(
|
)} ${ref.watch(
|
||||||
managerProvider
|
managerProvider
|
||||||
.select((value) => value.coin),
|
.select((value) => value.coin),
|
||||||
).ticker}",
|
).ticker}",
|
||||||
style: STextStyles.itemSubtitle12,
|
style: STextStyles.itemSubtitle12,
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
),
|
),
|
||||||
|
@ -287,18 +306,18 @@ class _ConfirmTransactionViewState
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
style:
|
style:
|
||||||
Theme.of(context).textButtonTheme.style?.copyWith(
|
Theme.of(context).textButtonTheme.style?.copyWith(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
MaterialStateProperty.all<Color>(
|
MaterialStateProperty.all<Color>(
|
||||||
CFColors.stackAccent,
|
CFColors.stackAccent,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final unlocked = await Navigator.push(
|
final unlocked = await Navigator.push(
|
||||||
context,
|
context,
|
||||||
RouteGenerator.getRoute(
|
RouteGenerator.getRoute(
|
||||||
shouldUseMaterialRoute:
|
shouldUseMaterialRoute:
|
||||||
RouteGenerator.useMaterialPageRoute,
|
RouteGenerator.useMaterialPageRoute,
|
||||||
builder: (_) => const LockscreenView(
|
builder: (_) => const LockscreenView(
|
||||||
showBackButton: true,
|
showBackButton: true,
|
||||||
popOnSuccess: true,
|
popOnSuccess: true,
|
||||||
|
@ -306,9 +325,9 @@ class _ConfirmTransactionViewState
|
||||||
routeOnSuccess: "",
|
routeOnSuccess: "",
|
||||||
biometricsCancelButtonString: "CANCEL",
|
biometricsCancelButtonString: "CANCEL",
|
||||||
biometricsLocalizedReason:
|
biometricsLocalizedReason:
|
||||||
"Authenticate to send transaction",
|
"Authenticate to send transaction",
|
||||||
biometricsAuthenticationTitle:
|
biometricsAuthenticationTitle:
|
||||||
"Confirm Transaction",
|
"Confirm Transaction",
|
||||||
),
|
),
|
||||||
settings: const RouteSettings(
|
settings: const RouteSettings(
|
||||||
name: "/confirmsendlockscreen"),
|
name: "/confirmsendlockscreen"),
|
||||||
|
@ -316,7 +335,7 @@ class _ConfirmTransactionViewState
|
||||||
);
|
);
|
||||||
|
|
||||||
if (unlocked is bool && unlocked && mounted) {
|
if (unlocked is bool && unlocked && mounted) {
|
||||||
_attemptSend(context);
|
unawaited(_attemptSend(context));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|
|
@ -40,6 +40,17 @@ const int MINIMUM_CONFIRMATIONS = 10;
|
||||||
const String GENESIS_HASH_MAINNET = "";
|
const String GENESIS_HASH_MAINNET = "";
|
||||||
const String GENESIS_HASH_TESTNET = "";
|
const String GENESIS_HASH_TESTNET = "";
|
||||||
|
|
||||||
|
class BadEpicHttpAddressException implements Exception {
|
||||||
|
final String? message;
|
||||||
|
|
||||||
|
BadEpicHttpAddressException({this.message});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return "BadEpicHttpAddressException: $message";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// isolate
|
// isolate
|
||||||
|
|
||||||
Map<ReceivePort, Isolate> isolates = {};
|
Map<ReceivePort, Isolate> isolates = {};
|
||||||
|
@ -185,6 +196,29 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
|
||||||
sendPort.send(result);
|
sendPort.send(result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (function == "txHttpSend") {
|
||||||
|
final wallet = arguments['wallet'] as String?;
|
||||||
|
final selectionStrategyIsAll = arguments['selectionStrategyIsAll'] as int?;
|
||||||
|
final minimumConfirmations = arguments['minimumConfirmations'] as int?;
|
||||||
|
final message = arguments['message'] as String?;
|
||||||
|
final amount = arguments['amount'] as int?;
|
||||||
|
final address = arguments['address'] as String?;
|
||||||
|
|
||||||
|
Map<String, dynamic> result = {};
|
||||||
|
|
||||||
|
if (!(wallet == null ||
|
||||||
|
selectionStrategyIsAll == null ||
|
||||||
|
minimumConfirmations == null ||
|
||||||
|
message == null ||
|
||||||
|
amount == null ||
|
||||||
|
address == null)) {
|
||||||
|
var res = await txHttpSend(wallet, selectionStrategyIsAll,
|
||||||
|
minimumConfirmations, message, amount, address);
|
||||||
|
result['result'] = res;
|
||||||
|
sendPort.send(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"Error Arguments for $function not formatted correctly",
|
"Error Arguments for $function not formatted correctly",
|
||||||
|
@ -717,32 +751,67 @@ class EpicCashWallet extends CoinServiceAPI {
|
||||||
|
|
||||||
// TODO determine whether it is worth sending change to a change address.
|
// TODO determine whether it is worth sending change to a change address.
|
||||||
dynamic message;
|
dynamic message;
|
||||||
await m.protect(() async {
|
|
||||||
ReceivePort receivePort = await getIsolate({
|
|
||||||
"function": "createTransaction",
|
|
||||||
"wallet": wallet!,
|
|
||||||
"amount": txData['recipientAmt'],
|
|
||||||
"address": txData['addresss'],
|
|
||||||
"secretKeyIndex": 0,
|
|
||||||
"epicboxConfig": epicboxConfig!,
|
|
||||||
"minimumConfirmations": MINIMUM_CONFIRMATIONS,
|
|
||||||
}, name: walletName);
|
|
||||||
|
|
||||||
message = await receivePort.first;
|
String receiverAddress = txData['addresss'] as String;
|
||||||
if (message is String) {
|
await m.protect(() async {
|
||||||
Logging.instance
|
|
||||||
.log("this is a string $message", level: LogLevel.Error);
|
if (receiverAddress.startsWith("http://") || receiverAddress.startsWith("https://")) {
|
||||||
|
const int selectionStrategyIsAll = 0;
|
||||||
|
ReceivePort receivePort = await getIsolate({
|
||||||
|
"function": "txHttpSend",
|
||||||
|
"wallet": wallet!,
|
||||||
|
"selectionStrategyIsAll": selectionStrategyIsAll,
|
||||||
|
"minimumConfirmations": MINIMUM_CONFIRMATIONS,
|
||||||
|
"message": "",
|
||||||
|
"amount": txData['recipientAmt'],
|
||||||
|
"address": txData['addresss']
|
||||||
|
}, name: walletName);
|
||||||
|
|
||||||
|
message = await receivePort.first;
|
||||||
|
if (message is String) {
|
||||||
|
Logging.instance
|
||||||
|
.log("this is a string $message", level: LogLevel.Error);
|
||||||
|
stop(receivePort);
|
||||||
|
throw Exception("txHttpSend isolate failed");
|
||||||
|
}
|
||||||
stop(receivePort);
|
stop(receivePort);
|
||||||
throw Exception("createTransaction isolate failed");
|
Logging.instance.log('Closing txHttpSend!\n $message',
|
||||||
|
level: LogLevel.Info);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ReceivePort receivePort = await getIsolate({
|
||||||
|
"function": "createTransaction",
|
||||||
|
"wallet": wallet!,
|
||||||
|
"amount": txData['recipientAmt'],
|
||||||
|
"address": txData['addresss'],
|
||||||
|
"secretKeyIndex": 0,
|
||||||
|
"epicboxConfig": epicboxConfig!,
|
||||||
|
"minimumConfirmations": MINIMUM_CONFIRMATIONS,
|
||||||
|
}, name: walletName);
|
||||||
|
|
||||||
|
message = await receivePort.first;
|
||||||
|
if (message is String) {
|
||||||
|
Logging.instance
|
||||||
|
.log("this is a string $message", level: LogLevel.Error);
|
||||||
|
stop(receivePort);
|
||||||
|
throw Exception("createTransaction isolate failed");
|
||||||
|
}
|
||||||
|
stop(receivePort);
|
||||||
|
Logging.instance.log('Closing createTransaction!\n $message',
|
||||||
|
level: LogLevel.Info);
|
||||||
}
|
}
|
||||||
stop(receivePort);
|
|
||||||
Logging.instance.log('Closing createTransaction!\n $message',
|
|
||||||
level: LogLevel.Info);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// return message;
|
// return message;
|
||||||
final String sendTx = message['result'] as String;
|
final String sendTx = message['result'] as String;
|
||||||
await putSendToAddresses(sendTx);
|
if (sendTx.contains("Error")) {
|
||||||
|
throw BadEpicHttpAddressException(message: sendTx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(receiverAddress.startsWith("http://") || receiverAddress.startsWith("https://"))) {
|
||||||
|
await putSendToAddresses(sendTx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Logging.instance.log("CONFIRM_RESULT_IS $sendTx", level: LogLevel.Info);
|
Logging.instance.log("CONFIRM_RESULT_IS $sendTx", level: LogLevel.Info);
|
||||||
|
|
||||||
|
@ -752,11 +821,16 @@ class EpicCashWallet extends CoinServiceAPI {
|
||||||
String errorMessage = decodeData[1] as String;
|
String errorMessage = decodeData[1] as String;
|
||||||
throw Exception("Transaction failed with error code $errorMessage");
|
throw Exception("Transaction failed with error code $errorMessage");
|
||||||
} else {
|
} else {
|
||||||
final postSlateRequest = decodeData[1];
|
|
||||||
final postToServer = await postSlate(
|
//If it's HTTP send no need to post to epicbox
|
||||||
txData['addresss'] as String, postSlateRequest as String);
|
if (!(receiverAddress.startsWith("http://") || receiverAddress.startsWith("https://"))) {
|
||||||
Logging.instance
|
final postSlateRequest = decodeData[1];
|
||||||
.log("POST_SLATE_IS $postToServer", level: LogLevel.Info);
|
final postToServer = await postSlate(
|
||||||
|
txData['addresss'] as String, postSlateRequest as String);
|
||||||
|
Logging.instance
|
||||||
|
.log("POST_SLATE_IS $postToServer", level: LogLevel.Info);
|
||||||
|
}
|
||||||
|
|
||||||
final txCreateResult = decodeData[0];
|
final txCreateResult = decodeData[0];
|
||||||
// //TODO: second problem
|
// //TODO: second problem
|
||||||
final transaction = json.decode(txCreateResult as String);
|
final transaction = json.decode(txCreateResult as String);
|
||||||
|
@ -2195,6 +2269,12 @@ class EpicCashWallet extends CoinServiceAPI {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool validateAddress(String address) {
|
bool validateAddress(String address) {
|
||||||
|
if (address.startsWith("http://") || address.startsWith("https://")) {
|
||||||
|
if (Uri.tryParse(address) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String validate = validateSendAddress(address);
|
String validate = validateSendAddress(address);
|
||||||
if (int.parse(validate) == 1) {
|
if (int.parse(validate) == 1) {
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in a new issue