WIP paynym ui and claim process

This commit is contained in:
julian 2022-12-20 17:00:03 -06:00
parent bbd04f46bb
commit a491bfd70f
7 changed files with 218 additions and 27 deletions

View file

@ -1,6 +1,12 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/paynym/dialogs/claiming_paynym_dialog.dart';
import 'package:stackwallet/providers/global/paynym_api_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
@ -9,18 +15,21 @@ import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'dialogs/claiming_paynym_dialog.dart';
class PaynymClaimView extends ConsumerStatefulWidget {
const PaynymClaimView({
Key? key,
required this.walletId,
}) : super(key: key);
class PaynymClaimView extends StatefulWidget {
const PaynymClaimView({Key? key}) : super(key: key);
final String walletId;
static const String routeName = "/claimPaynym";
@override
State<PaynymClaimView> createState() => _PaynymClaimViewState();
ConsumerState<PaynymClaimView> createState() => _PaynymClaimViewState();
}
class _PaynymClaimViewState extends State<PaynymClaimView> {
class _PaynymClaimViewState extends ConsumerState<PaynymClaimView> {
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
@ -81,6 +90,50 @@ class _PaynymClaimViewState extends State<PaynymClaimView> {
);
// generate and submit paynym to api
final wallet = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as DogecoinWallet;
final pCode = await wallet.getPaymentCode();
final result = await ref
.read(paynymAPIProvider)
.create(pCode.toString());
// final result =
// await ref.read(paynymAPIProvider).token(pCode.toString());
// final token =
// "IlBNOFRKWWt1U2RZWEpud0RCcThDaGZpbmZYdjNzcnhoUXJ4M2VvRXdiU3c1MXdNamRvOUpKMkRzeWN3VDNndDN6SFE3Y1YxZ3J2YWJNbW1mMUJ0ajZmWTd0Z2tnU3o5QjhNWnVSM2tqWWZnTUxNVVJKQ1hOIg.FoPF3g.KUMZDC4U_ek-B6cqPLYilXniQv8";
//
// print("======================");
// print(token);
// print(token.codeUnits);
// print(utf8.encode(token));
// print(utf8.decode(token.codeUnits));
//
// print("======================");
//
// final signed = await wallet.signWithNotificationKey(
// Uint8List.fromList(token.codeUnits));
//
// final signedString = Format.uint8listToString(signed);
//
// print("======================");
// print(signed);
// print(signedString);
//
// print("======================");
// final result2 = await ref
// .read(paynymAPIProvider)
// .claim(token, signedString);
// print("======================");
// print(
// result2); // {claimed: PM8TJYkuSdYXJnwDBq8ChfinfXv3srxhQrx3eoEwbSw51wMjdo9JJ2DsycwT3gt3zHQ7cV1grvabMmmf1Btj6fY7tgkgSz9B8MZuR3kjYfgMLMURJCXN, token: IlBNOFRKWWt1U2RZWEpud0RCcThDaGZpbmZYdjNzcnhoUXJ4M2VvRXdiU3c1MXdNamRvOUpKMkRzeWN3VDNndDN6SFE3Y1YxZ3J2YWJNbW1mMUJ0ajZmWTd0Z2tnU3o5QjhNWnVSM2tqWWZnTUxNVVJKQ1hOIg.FoPF3g.KUMZDC4U_ek-B6cqPLYilXniQv8}
// print("======================");
await Future<void>.delayed(const Duration(seconds: 3));
if (mounted && !shouldCancel) {

View file

@ -0,0 +1,72 @@
import 'package:flutter/material.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
class PaynymHomeView extends StatefulWidget {
const PaynymHomeView({
Key? key,
required this.walletId,
required this.paymentCodeString,
}) : super(key: key);
final String walletId;
final String paymentCodeString;
static const String routeName = "/paynymHome";
@override
State<PaynymHomeView> createState() => _PaynymHomeViewState();
}
class _PaynymHomeViewState extends State<PaynymHomeView> {
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final isDesktop = Util.isDesktop;
return MasterScaffold(
isDesktop: isDesktop,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () {
Navigator.of(context).pop();
},
),
titleSpacing: 0,
title: Text(
"PayNym",
style: STextStyles.navBarTitle(context),
overflow: TextOverflow.ellipsis,
),
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
PayNymBot(
paymentCodeString: widget.paymentCodeString,
),
],
),
),
),
);
}
}
class PayNymBot extends StatelessWidget {
const PayNymBot({
Key? key,
required this.paymentCodeString,
}) : super(key: key);
final String paymentCodeString;
@override
Widget build(BuildContext context) {
return Image.network("https://paynym.is/$paymentCodeString/avatar");
}
}

View file

@ -16,6 +16,7 @@ class WalletNavigationBar extends StatefulWidget {
required this.height,
required this.enableExchange,
required this.coin,
required this.walletId,
}) : super(key: key);
final VoidCallback onReceivePressed;
@ -25,6 +26,7 @@ class WalletNavigationBar extends StatefulWidget {
final double height;
final bool enableExchange;
final Coin coin;
final String walletId;
@override
State<WalletNavigationBar> createState() => _WalletNavigationBarState();
@ -91,7 +93,10 @@ class _WalletNavigationBarState extends State<WalletNavigationBar> {
setState(() {
scale = 0;
});
Navigator.of(context).pushNamed(PaynymClaimView.routeName);
Navigator.of(context).pushNamed(
PaynymClaimView.routeName,
arguments: widget.walletId,
);
},
child: Container(
padding: const EdgeInsets.all(16),

View file

@ -716,6 +716,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
right: 16,
),
child: WalletNavigationBar(
walletId: widget.walletId,
coin: ref.watch(managerProvider
.select((value) => value.coin)),
enableExchange: Constants.enableExchange &&

View file

@ -37,6 +37,7 @@ import 'package:stackwallet/pages/intro_view.dart';
import 'package:stackwallet/pages/manage_favorites_view/manage_favorites_view.dart';
import 'package:stackwallet/pages/notification_views/notifications_view.dart';
import 'package:stackwallet/pages/paynym/paynym_claim_view.dart';
import 'package:stackwallet/pages/paynym/paynym_home_view.dart';
import 'package:stackwallet/pages/pinpad_views/create_pin_view.dart';
import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart';
import 'package:stackwallet/pages/receive_view/receive_view.dart';
@ -189,10 +190,33 @@ class RouteGenerator {
settings: RouteSettings(name: settings.name));
case PaynymClaimView.routeName:
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const PaynymClaimView(),
settings: RouteSettings(name: settings.name));
builder: (_) => PaynymClaimView(
walletId: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case PaynymHomeView.routeName:
if (args is Tuple2<String, String>) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => PaynymHomeView(
walletId: args.item1,
paymentCodeString: args.item2,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case GlobalSettingsView.routeName:
return getRoute(

View file

@ -0,0 +1,37 @@
import 'dart:typed_data';
import 'package:bip47/bip47.dart';
import 'package:bitcoindart/bitcoindart.dart';
import 'package:pointycastle/digests/sha256.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
extension PayNym on DogecoinWallet {
// fetch or generate this wallet's bip47 payment code
Future<PaymentCode> getPaymentCode() async {
final paymentCodeString = DB.instance
.get<dynamic>(boxName: walletId, key: "paymentCodeString") as String?;
PaymentCode paymentCode;
if (paymentCodeString == null) {
final node = getBip32Root((await mnemonic).join(" "), network)
.derivePath("m/47'/0'/0'");
paymentCode =
PaymentCode.initFromPubKey(node.publicKey, node.chainCode, network);
await DB.instance.put<dynamic>(
boxName: walletId,
key: "paymentCodeString",
value: paymentCode.toString());
} else {
paymentCode = PaymentCode.fromPaymentCode(paymentCodeString, network);
}
return paymentCode;
}
Future<Uint8List> signWithNotificationKey(Uint8List data) async {
final node = getBip32Root((await mnemonic).join(" "), network)
.derivePath("m/47'/0'/0'");
final pair = ECPair.fromPrivateKey(node.privateKey!, network: network);
final signed = pair.sign(SHA256Digest().process(data));
return signed;
}
}

View file

@ -135,7 +135,7 @@ class DogecoinWallet extends CoinServiceAPI {
late final TransactionNotificationTracker txTracker;
NetworkType get _network {
NetworkType get network {
switch (coin) {
case Coin.dogecoin:
return dogecoin;
@ -266,7 +266,7 @@ class DogecoinWallet extends CoinServiceAPI {
// Base58check decode fail
}
if (decodeBase58 != null) {
if (decodeBase58[0] == _network.pubKeyHash) {
if (decodeBase58[0] == network.pubKeyHash) {
// P2PKH
return DerivePathType.bip44;
}
@ -277,7 +277,7 @@ class DogecoinWallet extends CoinServiceAPI {
} catch (err) {
// Bech32 decode fail
}
if (_network.bech32 != decodeBech32!.hrp) {
if (network.bech32 != decodeBech32!.hrp) {
throw ArgumentError('Invalid prefix or Network mismatch');
}
if (decodeBech32.version != 0) {
@ -385,8 +385,7 @@ class DogecoinWallet extends CoinServiceAPI {
switch (type) {
case DerivePathType.bip44:
address = P2PKH(
data: PaymentData(pubkey: node.publicKey),
network: _network)
data: PaymentData(pubkey: node.publicKey), network: network)
.data
.address!;
break;
@ -472,7 +471,7 @@ class DogecoinWallet extends CoinServiceAPI {
Map<String, Map<String, String>> p2pkhReceiveDerivations = {};
Map<String, Map<String, String>> p2pkhChangeDerivations = {};
final root = await compute(getBip32RootWrapper, Tuple2(mnemonic, _network));
final root = await compute(getBip32RootWrapper, Tuple2(mnemonic, network));
List<String> p2pkhReceiveAddressArray = [];
int p2pkhReceiveIndex = -1;
@ -1104,7 +1103,7 @@ class DogecoinWallet extends CoinServiceAPI {
@override
bool validateAddress(String address) {
return Address.validateAddress(address, _network);
return Address.validateAddress(address, network);
}
@override
@ -1358,7 +1357,7 @@ class DogecoinWallet extends CoinServiceAPI {
chain,
index,
mnemonic!,
_network,
network,
derivePathType,
),
);
@ -1367,7 +1366,7 @@ class DogecoinWallet extends CoinServiceAPI {
switch (derivePathType) {
case DerivePathType.bip44:
address = P2PKH(data: data, network: _network).data.address!;
address = P2PKH(data: data, network: network).data.address!;
break;
// default:
// // should never hit this due to all enum cases handled
@ -1601,7 +1600,7 @@ class DogecoinWallet extends CoinServiceAPI {
if (batches[batchNumber] == null) {
batches[batchNumber] = {};
}
final scripthash = _convertToScriptHash(allAddresses[i], _network);
final scripthash = _convertToScriptHash(allAddresses[i], network);
batches[batchNumber]!.addAll({
scripthash: [scripthash]
});
@ -1761,7 +1760,7 @@ class DogecoinWallet extends CoinServiceAPI {
Future<int> getTxCount({required String address}) async {
String? scripthash;
try {
scripthash = _convertToScriptHash(address, _network);
scripthash = _convertToScriptHash(address, network);
final transactions =
await electrumXClient.getHistory(scripthash: scripthash);
return transactions.length;
@ -1779,7 +1778,7 @@ class DogecoinWallet extends CoinServiceAPI {
try {
final Map<String, List<dynamic>> args = {};
for (final entry in addresses.entries) {
args[entry.key] = [_convertToScriptHash(entry.value, _network)];
args[entry.key] = [_convertToScriptHash(entry.value, network)];
}
final response = await electrumXClient.getBatchHistory(args: args);
@ -1971,7 +1970,7 @@ class DogecoinWallet extends CoinServiceAPI {
if (batches[batchNumber] == null) {
batches[batchNumber] = {};
}
final scripthash = _convertToScriptHash(allAddresses[i], _network);
final scripthash = _convertToScriptHash(allAddresses[i], network);
final id = Logger.isTestEnv ? "$i" : const Uuid().v1();
requestIdToAddressMap[id] = allAddresses[i];
batches[batchNumber]!.addAll({
@ -2746,7 +2745,7 @@ class DogecoinWallet extends CoinServiceAPI {
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: _network,
network: network,
).data;
for (String tx in addressTxid[addressesP2PKH[i]]!) {
@ -2754,7 +2753,7 @@ class DogecoinWallet extends CoinServiceAPI {
"output": data.output,
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String,
network: _network,
network: network,
),
};
}
@ -2767,7 +2766,7 @@ class DogecoinWallet extends CoinServiceAPI {
data: PaymentData(
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
network: _network,
network: network,
).data;
for (String tx in addressTxid[addressesP2PKH[i]]!) {
@ -2775,7 +2774,7 @@ class DogecoinWallet extends CoinServiceAPI {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
network: network,
),
};
}
@ -2802,7 +2801,7 @@ class DogecoinWallet extends CoinServiceAPI {
Logging.instance
.log("Starting buildTransaction ----------", level: LogLevel.Info);
final txb = TransactionBuilder(network: _network);
final txb = TransactionBuilder(network: network);
txb.setVersion(1);
// Add transaction inputs