mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 19:49:22 +00:00
zano.dart test app
This commit is contained in:
parent
dda6d4c750
commit
2d886e1213
19 changed files with 1206 additions and 145 deletions
|
@ -3,6 +3,8 @@ import 'dart:convert';
|
|||
|
||||
import 'package:cw_zano/api/convert_utf8_to_string.dart';
|
||||
import 'package:cw_zano/api/model.dart';
|
||||
import 'package:cw_zano/api/model/get_recent_txs_and_info_params.dart';
|
||||
import 'package:cw_zano/api/model/transfer_params.dart';
|
||||
import 'package:cw_zano/api/zano_api.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -65,6 +67,22 @@ final _closeWalletNative = zanoApi
|
|||
typedef _close_wallet = Void Function(Int64);
|
||||
typedef _closeWalletStatus = void Function(int hWallet);
|
||||
|
||||
// get_current_tx_fee
|
||||
final _getCurrentTxFeeNative = zanoApi
|
||||
.lookup<NativeFunction<_get_current_tx_fee>>('get_current_tx_fee')
|
||||
.asFunction<_getCurrentTxFee>();
|
||||
typedef _get_current_tx_fee = Int64 Function(Int64);
|
||||
typedef _getCurrentTxFee = int Function(int priority);
|
||||
|
||||
final _restoreWalletFromSeedNative = zanoApi
|
||||
.lookup<NativeFunction<_restore_wallet_from_seed>>(
|
||||
'restore_wallet_from_seed')
|
||||
.asFunction<_RestoreWalletFromSeed>();
|
||||
typedef _restore_wallet_from_seed = Pointer<Utf8> Function(
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
|
||||
typedef _RestoreWalletFromSeed = Pointer<Utf8> Function(
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
|
||||
|
||||
String doAsyncCall(
|
||||
{required String methodName,
|
||||
required int hWallet,
|
||||
|
@ -73,7 +91,7 @@ String doAsyncCall(
|
|||
final paramsPointer = params.toNativeUtf8();
|
||||
|
||||
debugPrint(
|
||||
"async_call method_name $methodName hWallet $hWallet params $params");
|
||||
'async_call method_name $methodName hWallet $hWallet params $params');
|
||||
final result = convertUTF8ToString(
|
||||
pointer: _asyncCallNative(methodNamePointer, hWallet, paramsPointer));
|
||||
|
||||
|
@ -95,33 +113,34 @@ Future<String> invokeMethod(
|
|||
}));
|
||||
debugPrint('invoke result $invokeResult');
|
||||
final map = json.decode(invokeResult);
|
||||
if (map["job_id"] != null) {
|
||||
bool done = false;
|
||||
do {
|
||||
await Future.delayed(Duration(seconds: 3));
|
||||
final result = tryPullResult(map["job_id"] as int);
|
||||
final map2 = json.decode(result);
|
||||
done = map2["result"] == null || map2["result"]["error"] == null;
|
||||
} while (!done);
|
||||
if (map['job_id'] != null) {
|
||||
await Future.delayed(Duration(seconds: 3));
|
||||
final result = tryPullResult(map['job_id'] as int);
|
||||
return result;
|
||||
}
|
||||
return "";
|
||||
return invokeResult;
|
||||
}
|
||||
|
||||
Future<String> store(int hWallet) async {
|
||||
// debugPrint("store hWallet $hWallet");
|
||||
// final result = doAsyncCall(
|
||||
// methodName: 'invoke',
|
||||
// hWallet: hWallet,
|
||||
// params: "{method: 'store', params: {}}");
|
||||
// debugPrint('store result $result');
|
||||
// final map = json.decode(result);
|
||||
// if (map["job_id"] != null) {
|
||||
// await Future.delayed(Duration(seconds: 1));
|
||||
// tryPullResult(map["job_id"] as int);
|
||||
// }
|
||||
return await invokeMethod(hWallet, 'store', '{}');
|
||||
}
|
||||
|
||||
Future<String> transfer(int hWallet, TransferParams params) async {
|
||||
final invokeResult = await doAsyncCall(
|
||||
methodName: 'invoke',
|
||||
hWallet: hWallet,
|
||||
params: '{"method": "transfer","params": ${jsonEncode(params)}}',
|
||||
);
|
||||
debugPrint('invoke result $invokeResult');
|
||||
var map = json.decode(invokeResult);
|
||||
if (map['job_id'] != null) {
|
||||
await Future.delayed(Duration(seconds: 3));
|
||||
final result = tryPullResult(map['job_id'] as int);
|
||||
return result;
|
||||
}
|
||||
return invokeResult;
|
||||
}
|
||||
|
||||
Future<String> getRecentTxsAndInfo(
|
||||
{required int hWallet,
|
||||
required int offset,
|
||||
|
@ -140,17 +159,24 @@ Future<String> getRecentTxsAndInfo(
|
|||
}
|
||||
|
||||
String getWalletStatus(int hWallet) {
|
||||
debugPrint("get_wallet_status hWallet $hWallet");
|
||||
debugPrint('get_wallet_status hWallet $hWallet');
|
||||
final result = convertUTF8ToString(pointer: _getWalletStatusNative(hWallet));
|
||||
debugPrint('get_wallet_status result $result');
|
||||
return result;
|
||||
}
|
||||
|
||||
void closeWallet(int hWallet) {
|
||||
debugPrint("close_wallet hWallet $hWallet");
|
||||
debugPrint('close_wallet hWallet $hWallet');
|
||||
_closeWalletNative(hWallet);
|
||||
}
|
||||
|
||||
int getCurrentTxFee(int priority) {
|
||||
debugPrint('get_current_tx_fee priority $priority');
|
||||
final result = _getCurrentTxFeeNative(priority);
|
||||
debugPrint('get_current_tx_fee result $result');
|
||||
return result;
|
||||
}
|
||||
|
||||
String getWalletInfo(int hWallet) {
|
||||
debugPrint('get_wallet_info hWallet $hWallet');
|
||||
final result = convertUTF8ToString(pointer: _getWalletInfoNative(hWallet));
|
||||
|
@ -170,14 +196,25 @@ String getVersion() {
|
|||
return result;
|
||||
}
|
||||
|
||||
String restoreWalletFromSeed(String path, String password, String seed) {
|
||||
debugPrint('restore_wallet_from_seed path $path password $password seed $seed');
|
||||
final pathPointer = path.toNativeUtf8();
|
||||
final passwordPointer = password.toNativeUtf8();
|
||||
final seedPointer = seed.toNativeUtf8();
|
||||
final errorMessagePointer = ''.toNativeUtf8();
|
||||
final result = convertUTF8ToString(pointer: _restoreWalletFromSeedNative(pathPointer,
|
||||
passwordPointer, seedPointer, 0, 0, errorMessagePointer));
|
||||
return result;
|
||||
}
|
||||
|
||||
String loadWallet(String path, String password, int nettype) {
|
||||
debugPrint("load_wallet path $path password $password nettype $nettype");
|
||||
debugPrint('load_wallet path $path password $password nettype $nettype');
|
||||
final pathPointer = path.toNativeUtf8();
|
||||
final passwordPointer = password.toNativeUtf8();
|
||||
final result = convertUTF8ToString(
|
||||
pointer: _loadWalletNative(pathPointer, passwordPointer, nettype),
|
||||
);
|
||||
debugPrint("load_wallet result $result");
|
||||
debugPrint('load_wallet result $result');
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,49 +1,49 @@
|
|||
class Destination {
|
||||
final String amount;
|
||||
final String address;
|
||||
final String assetId;
|
||||
// class Destination {
|
||||
// final String amount;
|
||||
// final String address;
|
||||
// final String assetId;
|
||||
|
||||
Destination({required this.amount, required this.address, required this.assetId});
|
||||
// Destination({required this.amount, required this.address, required this.assetId});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"amount": amount,
|
||||
"address": address,
|
||||
"asset_id": assetId,
|
||||
};
|
||||
}
|
||||
// Map<String, dynamic> toJson() => {
|
||||
// "amount": amount,
|
||||
// "address": address,
|
||||
// "asset_id": assetId,
|
||||
// };
|
||||
// }
|
||||
|
||||
class TransferParams {
|
||||
final List<Destination> destinations;
|
||||
final int fee;
|
||||
final int mixin;
|
||||
final String paymentId;
|
||||
final String comment;
|
||||
final bool pushPayer;
|
||||
final bool hideReceiver;
|
||||
// class TransferParams {
|
||||
// final List<Destination> destinations;
|
||||
// final int fee;
|
||||
// final int mixin;
|
||||
// final String paymentId;
|
||||
// final String comment;
|
||||
// final bool pushPayer;
|
||||
// final bool hideReceiver;
|
||||
|
||||
TransferParams({required this.destinations, required this.fee, required this.mixin, required this.paymentId, required this.comment, required this.pushPayer, required this.hideReceiver});
|
||||
// TransferParams({required this.destinations, required this.fee, required this.mixin, required this.paymentId, required this.comment, required this.pushPayer, required this.hideReceiver});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"destinations": destinations,
|
||||
"fee": fee,
|
||||
"mixin": mixin,
|
||||
"payment_id": paymentId,
|
||||
"comment": comment,
|
||||
"push_payer": pushPayer,
|
||||
"hide_receiver": hideReceiver,
|
||||
};
|
||||
}
|
||||
// Map<String, dynamic> toJson() => {
|
||||
// "destinations": destinations,
|
||||
// "fee": fee,
|
||||
// "mixin": mixin,
|
||||
// "payment_id": paymentId,
|
||||
// "comment": comment,
|
||||
// "push_payer": pushPayer,
|
||||
// "hide_receiver": hideReceiver,
|
||||
// };
|
||||
// }
|
||||
|
||||
class GetRecentTxsAndInfoParams {
|
||||
final int offset;
|
||||
final int count;
|
||||
final bool updateProvisionInfo;
|
||||
// class GetRecentTxsAndInfoParams {
|
||||
// final int offset;
|
||||
// final int count;
|
||||
// final bool updateProvisionInfo;
|
||||
|
||||
GetRecentTxsAndInfoParams({required this.offset, required this.count, required this.updateProvisionInfo});
|
||||
// GetRecentTxsAndInfoParams({required this.offset, required this.count, required this.updateProvisionInfo});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"offset": offset,
|
||||
"count": count,
|
||||
"update_provision_info": updateProvisionInfo,
|
||||
};
|
||||
}
|
||||
// Map<String, dynamic> toJson() => {
|
||||
// "offset": offset,
|
||||
// "count": count,
|
||||
// "update_provision_info": updateProvisionInfo,
|
||||
// };
|
||||
// }
|
34
cw_zano/lib/api/model/asset_info.dart
Normal file
34
cw_zano/lib/api/model/asset_info.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
class AssetInfo {
|
||||
final String assetId;
|
||||
final int currentSupply;
|
||||
final int decimalPoint;
|
||||
final String fullName;
|
||||
final bool hiddenSupply;
|
||||
final String metaInfo;
|
||||
final String owner;
|
||||
final String ticker;
|
||||
final int totalMaxSupply;
|
||||
|
||||
AssetInfo(
|
||||
{required this.assetId,
|
||||
required this.currentSupply,
|
||||
required this.decimalPoint,
|
||||
required this.fullName,
|
||||
required this.hiddenSupply,
|
||||
required this.metaInfo,
|
||||
required this.owner,
|
||||
required this.ticker,
|
||||
required this.totalMaxSupply});
|
||||
|
||||
factory AssetInfo.fromJson(Map<String, dynamic> json) => AssetInfo(
|
||||
assetId: json['asset_id'] as String,
|
||||
currentSupply: json['current_supply'] as int,
|
||||
decimalPoint: json['decimal_point'] as int,
|
||||
fullName: json['full_name'] as String,
|
||||
hiddenSupply: json['hidden_supply'] as bool,
|
||||
metaInfo: json['meta_info'] as String,
|
||||
owner: json['owner'] as String,
|
||||
ticker: json['ticker'] as String,
|
||||
totalMaxSupply: json['total_max_supply'] as int,
|
||||
);
|
||||
}
|
27
cw_zano/lib/api/model/balance.dart
Normal file
27
cw_zano/lib/api/model/balance.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_zano/api/model/asset_info.dart';
|
||||
|
||||
class Balance {
|
||||
final AssetInfo assetInfo;
|
||||
final int awaitingIn;
|
||||
final int awaitingOut;
|
||||
final int total;
|
||||
final int unlocked;
|
||||
|
||||
Balance(
|
||||
{required this.assetInfo,
|
||||
required this.awaitingIn,
|
||||
required this.awaitingOut,
|
||||
required this.total,
|
||||
required this.unlocked});
|
||||
|
||||
factory Balance.fromJson(Map<String, dynamic> json) => Balance(
|
||||
assetInfo:
|
||||
AssetInfo.fromJson(json['asset_info'] as Map<String, dynamic>),
|
||||
awaitingIn: json['awaiting_in'] as int,
|
||||
awaitingOut: json['awaiting_out'] as int,
|
||||
total: json['total'] as int,
|
||||
unlocked: json['unlocked'] as int,
|
||||
);
|
||||
}
|
20
cw_zano/lib/api/model/destination.dart
Normal file
20
cw_zano/lib/api/model/destination.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
class Destination {
|
||||
final String amount;
|
||||
final String address;
|
||||
final String assetId;
|
||||
|
||||
Destination(
|
||||
{required this.amount, required this.address, required this.assetId});
|
||||
|
||||
factory Destination.fromJson(Map<String, dynamic> json) => Destination(
|
||||
amount: json['amount'] as String,
|
||||
address: json['address'] as String,
|
||||
assetId: json['asset_id'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"amount": amount,
|
||||
"address": address,
|
||||
"asset_id": assetId,
|
||||
};
|
||||
}
|
20
cw_zano/lib/api/model/employed_entries.dart
Normal file
20
cw_zano/lib/api/model/employed_entries.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_zano/api/model/receive.dart';
|
||||
|
||||
class EmployedEntries {
|
||||
final List<Receive> receive;
|
||||
final List<Receive> send;
|
||||
|
||||
EmployedEntries({required this.receive, required this.send});
|
||||
|
||||
factory EmployedEntries.fromJson(Map<String, dynamic> json) =>
|
||||
EmployedEntries(
|
||||
receive: json['receive'] == null ? [] : (json['receive'] as List<dynamic>)
|
||||
.map((e) => Receive.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
send: json['spent'] == null ? [] : (json['spent'] as List<dynamic>)
|
||||
.map((e) => Receive.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
}
|
13
cw_zano/lib/api/model/get_recent_txs_and_info_params.dart
Normal file
13
cw_zano/lib/api/model/get_recent_txs_and_info_params.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
class GetRecentTxsAndInfoParams {
|
||||
final int offset;
|
||||
final int count;
|
||||
final bool updateProvisionInfo;
|
||||
|
||||
GetRecentTxsAndInfoParams({required this.offset, required this.count, required this.updateProvisionInfo});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"offset": offset,
|
||||
"count": count,
|
||||
"update_provision_info": updateProvisionInfo,
|
||||
};
|
||||
}
|
13
cw_zano/lib/api/model/get_wallet_info_result.dart
Normal file
13
cw_zano/lib/api/model/get_wallet_info_result.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
import 'package:cw_zano/api/model/wi.dart';
|
||||
import 'package:cw_zano/api/model/wi_extended.dart';
|
||||
|
||||
class GetWalletInfoResult {
|
||||
final Wi wi;
|
||||
final WiExtended wiExtended;
|
||||
|
||||
GetWalletInfoResult({required this.wi, required this.wiExtended});
|
||||
|
||||
factory GetWalletInfoResult.fromJson(Map<String, dynamic> json) => GetWalletInfoResult(
|
||||
wi: Wi.fromJson(json['wi'] as Map<String, dynamic>),
|
||||
wiExtended: WiExtended.fromJson(json['wi_extended'] as Map<String, dynamic>));
|
||||
}
|
35
cw_zano/lib/api/model/get_wallet_status_result.dart
Normal file
35
cw_zano/lib/api/model/get_wallet_status_result.dart
Normal file
|
@ -0,0 +1,35 @@
|
|||
class GetWalletStatusResult {
|
||||
final int currentDaemonHeight;
|
||||
final int currentWalletHeight;
|
||||
final bool isDaemonConnected;
|
||||
final bool isInLongRefresh;
|
||||
final int progress;
|
||||
final int walletState;
|
||||
|
||||
GetWalletStatusResult(
|
||||
{required this.currentDaemonHeight,
|
||||
required this.currentWalletHeight,
|
||||
required this.isDaemonConnected,
|
||||
required this.isInLongRefresh,
|
||||
required this.progress,
|
||||
required this.walletState});
|
||||
|
||||
factory GetWalletStatusResult.fromJson(Map<String, dynamic> json) =>
|
||||
GetWalletStatusResult(
|
||||
currentDaemonHeight: json['current_daemon_height'] as int,
|
||||
currentWalletHeight: json['current_wallet_height'] as int,
|
||||
isDaemonConnected: json['is_daemon_connected'] as bool,
|
||||
isInLongRefresh: json['is_in_long_refresh'] as bool,
|
||||
progress: json['progress'] as int,
|
||||
walletState: json['wallet_state'] as int,
|
||||
);
|
||||
/*
|
||||
"current_daemon_height": 238049,
|
||||
"current_wallet_height": 238038,
|
||||
"is_daemon_connected": true,
|
||||
"is_in_long_refresh": true,
|
||||
"progress": 0,
|
||||
"wallet_state": 1
|
||||
|
||||
*/
|
||||
}
|
71
cw_zano/lib/api/model/history.dart
Normal file
71
cw_zano/lib/api/model/history.dart
Normal file
|
@ -0,0 +1,71 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_zano/api/model/employed_entries.dart';
|
||||
import 'package:cw_zano/api/model/subtransfer.dart';
|
||||
|
||||
class History {
|
||||
final String comment;
|
||||
final EmployedEntries employedEntries;
|
||||
final int fee;
|
||||
final int height;
|
||||
final bool isMining;
|
||||
final bool isMixing;
|
||||
final bool isService;
|
||||
final String paymentId;
|
||||
final List<String> remoteAddresses;
|
||||
final List<String> remoteAliases;
|
||||
final bool showSender;
|
||||
final List<Subtransfer> subtransfers;
|
||||
final int timestamp;
|
||||
final int transferInternalIndex;
|
||||
final int txBlobSize;
|
||||
final String txHash;
|
||||
final int txType;
|
||||
final int unlockTime;
|
||||
|
||||
History({
|
||||
required this.comment,
|
||||
required this.employedEntries,
|
||||
required this.fee,
|
||||
required this.height,
|
||||
required this.isMining,
|
||||
required this.isMixing,
|
||||
required this.isService,
|
||||
required this.paymentId,
|
||||
required this.remoteAddresses,
|
||||
required this.remoteAliases,
|
||||
required this.showSender,
|
||||
required this.subtransfers,
|
||||
required this.timestamp,
|
||||
required this.transferInternalIndex,
|
||||
required this.txBlobSize,
|
||||
required this.txHash,
|
||||
required this.txType,
|
||||
required this.unlockTime,
|
||||
});
|
||||
|
||||
factory History.fromJson(Map<String, dynamic> json) => History(
|
||||
comment: json['comment'] as String,
|
||||
employedEntries: EmployedEntries.fromJson(
|
||||
json['employed_entries'] as Map<String, dynamic>),
|
||||
fee: json['fee'] as int,
|
||||
height: json['height'] as int,
|
||||
isMining: json['is_mining'] as bool,
|
||||
isMixing: json['is_mixing'] as bool,
|
||||
isService: json['is_service'] as bool,
|
||||
paymentId: json['payment_id'] as String,
|
||||
remoteAddresses: json['remote_addresses'] == null ? [] :
|
||||
(json['remote_addresses'] as List<dynamic>).cast<String>(),
|
||||
remoteAliases: json['remote_aliases'] == null ? [] : (json['remote_aliases'] as List<dynamic>).cast<String>(),
|
||||
showSender: json['show_sender'] as bool,
|
||||
subtransfers: (json['subtransfers'] as List<dynamic>)
|
||||
.map((e) => Subtransfer.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
timestamp: json['timestamp'] as int,
|
||||
transferInternalIndex: json['transfer_internal_index'] as int,
|
||||
txBlobSize: json['tx_blob_size'] as int,
|
||||
txHash: json['tx_hash'] as String,
|
||||
txType: json['tx_type'] as int,
|
||||
unlockTime: json['unlock_time'] as int,
|
||||
);
|
||||
}
|
41
cw_zano/lib/api/model/load_wallet_result.dart
Normal file
41
cw_zano/lib/api/model/load_wallet_result.dart
Normal file
|
@ -0,0 +1,41 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_zano/api/model/recent_history.dart';
|
||||
import 'package:cw_zano/api/model/wi.dart';
|
||||
|
||||
class CreateLoadRestoreWalletResult {
|
||||
final String name;
|
||||
final String pass;
|
||||
final RecentHistory recentHistory;
|
||||
final bool recovered;
|
||||
final String seed;
|
||||
final int walletFileSize;
|
||||
final int walletId;
|
||||
final int walletLocalBcSize;
|
||||
final Wi wi;
|
||||
|
||||
CreateLoadRestoreWalletResult(
|
||||
{required this.name,
|
||||
required this.pass,
|
||||
required this.recentHistory,
|
||||
required this.recovered,
|
||||
required this.seed,
|
||||
required this.walletFileSize,
|
||||
required this.walletId,
|
||||
required this.walletLocalBcSize,
|
||||
required this.wi});
|
||||
|
||||
factory CreateLoadRestoreWalletResult.fromJson(Map<String, dynamic> json) =>
|
||||
CreateLoadRestoreWalletResult(
|
||||
name: json['name'] as String,
|
||||
pass: json['pass'] as String,
|
||||
recentHistory: RecentHistory.fromJson(
|
||||
json['recent_history'] as Map<String, dynamic>),
|
||||
recovered: json['recovered'] as bool,
|
||||
seed: json['seed'] as String,
|
||||
walletFileSize: json['wallet_file_size'] as int,
|
||||
walletId: json['wallet_id'] as int,
|
||||
walletLocalBcSize: json['wallet_local_bc_size'] as int,
|
||||
wi: Wi.fromJson(json['wi'] as Map<String, dynamic>),
|
||||
);
|
||||
}
|
13
cw_zano/lib/api/model/receive.dart
Normal file
13
cw_zano/lib/api/model/receive.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
class Receive {
|
||||
final int amount;
|
||||
final String assetId;
|
||||
final int index;
|
||||
|
||||
Receive({required this.amount, required this.assetId, required this.index});
|
||||
|
||||
factory Receive.fromJson(Map<String, dynamic> json) => Receive(
|
||||
amount: json['amount'] as int,
|
||||
assetId: json['asset_id'] as String,
|
||||
index: json['index'] as int,
|
||||
);
|
||||
}
|
22
cw_zano/lib/api/model/recent_history.dart
Normal file
22
cw_zano/lib/api/model/recent_history.dart
Normal file
|
@ -0,0 +1,22 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_zano/api/model/history.dart';
|
||||
|
||||
class RecentHistory {
|
||||
final List<History>? history;
|
||||
final int lastItemIndex;
|
||||
final int totalHistoryItems;
|
||||
|
||||
RecentHistory(
|
||||
{required this.history,
|
||||
required this.lastItemIndex,
|
||||
required this.totalHistoryItems});
|
||||
|
||||
factory RecentHistory.fromJson(Map<String, dynamic> json) => RecentHistory(
|
||||
history: json['history'] == null ? null : (json['history'] as List<dynamic>)
|
||||
.map((e) => History.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
lastItemIndex: json['last_item_index'] as int,
|
||||
totalHistoryItems: json['total_history_items'] as int,
|
||||
);
|
||||
}
|
14
cw_zano/lib/api/model/subtransfer.dart
Normal file
14
cw_zano/lib/api/model/subtransfer.dart
Normal file
|
@ -0,0 +1,14 @@
|
|||
class Subtransfer {
|
||||
final int amount;
|
||||
final String assetId;
|
||||
final bool isIncome;
|
||||
|
||||
Subtransfer(
|
||||
{required this.amount, required this.assetId, required this.isIncome});
|
||||
|
||||
factory Subtransfer.fromJson(Map<String, dynamic> json) => Subtransfer(
|
||||
amount: json['amount'] as int,
|
||||
assetId: json['asset_id'] as String,
|
||||
isIncome: json['is_income'] as bool,
|
||||
);
|
||||
}
|
41
cw_zano/lib/api/model/transfer_params.dart
Normal file
41
cw_zano/lib/api/model/transfer_params.dart
Normal file
|
@ -0,0 +1,41 @@
|
|||
import 'package:cw_zano/api/model/destination.dart';
|
||||
|
||||
class TransferParams {
|
||||
final List<Destination> destinations;
|
||||
final int fee;
|
||||
final int mixin;
|
||||
final String paymentId;
|
||||
final String comment;
|
||||
final bool pushPayer;
|
||||
final bool hideReceiver;
|
||||
|
||||
TransferParams({
|
||||
required this.destinations,
|
||||
required this.fee,
|
||||
required this.mixin,
|
||||
required this.paymentId,
|
||||
required this.comment,
|
||||
required this.pushPayer,
|
||||
required this.hideReceiver,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"destinations": destinations,
|
||||
"fee": fee,
|
||||
"mixin": mixin,
|
||||
"payment_id": paymentId,
|
||||
"comment": comment,
|
||||
"push_payer": pushPayer,
|
||||
"hide_receiver": hideReceiver,
|
||||
};
|
||||
|
||||
factory TransferParams.fromJson(Map<String, dynamic> json) => TransferParams(
|
||||
destinations: (json['destinations'] as List<dynamic>).map((e) => Destination.fromJson(e as Map<String, dynamic>)).toList(),
|
||||
fee: json['fee'] as int,
|
||||
mixin: json['mixin'] as int,
|
||||
paymentId: json['payment_id'] as String,
|
||||
comment: json['comment'] as String,
|
||||
pushPayer: json["push_payer"] as bool,
|
||||
hideReceiver: json["hide_receiver"] as bool,
|
||||
);
|
||||
}
|
34
cw_zano/lib/api/model/wi.dart
Normal file
34
cw_zano/lib/api/model/wi.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_zano/api/model/balance.dart';
|
||||
|
||||
class Wi {
|
||||
final String address;
|
||||
final List<Balance> balances;
|
||||
final bool isAuditable;
|
||||
final bool isWatchOnly;
|
||||
final int minedTotal;
|
||||
final String path;
|
||||
final String viewSecKey;
|
||||
|
||||
Wi(
|
||||
{required this.address,
|
||||
required this.balances,
|
||||
required this.isAuditable,
|
||||
required this.isWatchOnly,
|
||||
required this.minedTotal,
|
||||
required this.path,
|
||||
required this.viewSecKey});
|
||||
|
||||
factory Wi.fromJson(Map<String, dynamic> json) => Wi(
|
||||
address: json['address'] as String,
|
||||
balances: (json['balances'] as List<dynamic>)
|
||||
.map((e) => Balance.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
isAuditable: json['is_auditable'] as bool,
|
||||
isWatchOnly: json['is_watch_only'] as bool,
|
||||
minedTotal: json['mined_total'] as int,
|
||||
path: json['path'] as String,
|
||||
viewSecKey: json['view_sec_key'] as String,
|
||||
);
|
||||
}
|
17
cw_zano/lib/api/model/wi_extended.dart
Normal file
17
cw_zano/lib/api/model/wi_extended.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
class WiExtended {
|
||||
final String seed;
|
||||
final String spendPrivateKey;
|
||||
final String spendPublicKey;
|
||||
final String viewPrivateKey;
|
||||
final String viewPublicKey;
|
||||
|
||||
WiExtended({required this.seed, required this.spendPrivateKey, required this.spendPublicKey, required this.viewPrivateKey, required this.viewPublicKey});
|
||||
|
||||
factory WiExtended.fromJson(Map<String, dynamic> json) => WiExtended(
|
||||
seed: json["seed"] as String,
|
||||
spendPrivateKey: json["spend_private_key"] as String,
|
||||
spendPublicKey: json["spend_public_key"] as String,
|
||||
viewPrivateKey: json["view_private_key"] as String,
|
||||
viewPublicKey: json["view_public_key"] as String,
|
||||
);
|
||||
}
|
298
lib/zano.dart
298
lib/zano.dart
|
@ -4,16 +4,20 @@ import 'dart:convert';
|
|||
import 'package:cake_wallet/core/generate_wallet_password.dart';
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||
import 'package:cake_wallet/zano_connected_widget.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_zano/api/calls.dart' as calls;
|
||||
import 'package:cw_zano/api/model/balance.dart';
|
||||
import 'package:cw_zano/api/model/load_wallet_result.dart';
|
||||
import 'package:cw_zano/api/wallet.dart' as zano_wallet;
|
||||
import 'package:cw_zano/api/wallet_manager.dart' as zano_wallet_manager;
|
||||
import 'package:cw_zano/api/calls.dart' as calls;
|
||||
import 'package:cw_zano/zano_wallet_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
await runZonedGuarded(() async {
|
||||
|
@ -51,96 +55,232 @@ class App extends StatefulWidget {
|
|||
State<App> createState() => _AppState();
|
||||
}
|
||||
|
||||
class HomeWidget extends StatefulWidget {
|
||||
const HomeWidget({super.key});
|
||||
// class HomeWidget extends StatefulWidget {
|
||||
// const HomeWidget({super.key});
|
||||
|
||||
@override
|
||||
State<HomeWidget> createState() => _HomeWidgetState();
|
||||
}
|
||||
// @override
|
||||
// State<HomeWidget> createState() => _HomeWidgetState();
|
||||
// }
|
||||
|
||||
class _AppState extends State<App> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(home: HomeWidget());
|
||||
return MaterialApp(
|
||||
home: DisconnectedWidget(), //HomeWidget(),
|
||||
routes: {
|
||||
ConnectedWidget.route: (context) {
|
||||
final address = ModalRoute.of(context)!.settings.arguments! as String;
|
||||
return ConnectedWidget(address: address);
|
||||
},
|
||||
DisconnectedWidget.route: (context) => DisconnectedWidget(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HomeWidgetState extends State<HomeWidget> {
|
||||
int hWallet = 0;
|
||||
CreateLoadRestoreWalletResult? lwr;
|
||||
List<Balance> balances = [];
|
||||
String seed = '', version = '';
|
||||
final assetIds = <String, String>{};
|
||||
const walletWrongId = 'WALLET_WRONG_ID';
|
||||
const walletName = 'walletName';
|
||||
|
||||
Future<void> init() async {
|
||||
version = calls.getVersion();
|
||||
final setupNode = await zano_wallet.setupNode(
|
||||
address: '195.201.107.230:33336',
|
||||
login: '',
|
||||
password: '',
|
||||
useSSL: false,
|
||||
isLightWallet: false);
|
||||
if (!setupNode) {
|
||||
debugPrint('error setting up node!');
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> create(String name) async {
|
||||
debugPrint('create $name');
|
||||
await init();
|
||||
final path = await pathForWallet(name: name, type: WalletType.zano);
|
||||
final credentials = ZanoNewWalletCredentials(name: name);
|
||||
final keyService = KeyService(FlutterSecureStorage());
|
||||
final password = generateWalletPassword();
|
||||
credentials.password = password;
|
||||
await keyService.saveWalletPassword(
|
||||
password: password, walletName: credentials.name);
|
||||
debugPrint('path $path password $password');
|
||||
final result = zano_wallet_manager.createWalletSync(
|
||||
path: path, password: password, language: '');
|
||||
debugPrint('create result $result');
|
||||
return _parseResult(result);
|
||||
}
|
||||
|
||||
Future<String?> connect(String name) async {
|
||||
debugPrint('connect');
|
||||
await init();
|
||||
final path = await pathForWallet(name: name, type: WalletType.zano);
|
||||
final credentials = ZanoNewWalletCredentials(name: name);
|
||||
final keyService = KeyService(FlutterSecureStorage());
|
||||
final password =
|
||||
await keyService.getWalletPassword(walletName: credentials.name);
|
||||
debugPrint('path $path password $password');
|
||||
final result = await calls.loadWallet(path, password, 0);
|
||||
return _parseResult(result);
|
||||
}
|
||||
|
||||
Future<String?> restore(String name, String seed) async {
|
||||
debugPrint("restore");
|
||||
await init();
|
||||
final path = await pathForWallet(name: name, type: WalletType.zano);
|
||||
final credentials = ZanoNewWalletCredentials(name: name);
|
||||
final keyService = KeyService(FlutterSecureStorage());
|
||||
final password = generateWalletPassword();
|
||||
credentials.password = password;
|
||||
await keyService.saveWalletPassword(
|
||||
password: password, walletName: credentials.name);
|
||||
debugPrint('path $path password $password');
|
||||
var result = calls.restoreWalletFromSeed(path, password, seed);
|
||||
debugPrint('restore result $result');
|
||||
//result = await calls.loadWallet(path, password, 0);
|
||||
return _parseResult(result);
|
||||
}
|
||||
|
||||
String? _parseResult(String result) {
|
||||
final map = json.decode(result) as Map<String, dynamic>;
|
||||
if (map['result'] != null) {
|
||||
lwr = CreateLoadRestoreWalletResult.fromJson(map['result'] as Map<String, dynamic>);
|
||||
balances = lwr!.wi.balances;
|
||||
hWallet = lwr!.walletId;
|
||||
assetIds.clear();
|
||||
for (final balance in lwr!.wi.balances) {
|
||||
assetIds[balance.assetInfo.assetId] = balance.assetInfo.ticker;
|
||||
}
|
||||
return lwr!.wi.address;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void close() {
|
||||
calls.closeWallet(hWallet);
|
||||
}
|
||||
|
||||
class DisconnectedWidget extends StatefulWidget {
|
||||
const DisconnectedWidget({super.key});
|
||||
static const route = 'disconnected';
|
||||
|
||||
@override
|
||||
State<DisconnectedWidget> createState() => _DisconnectedWidgetState();
|
||||
}
|
||||
|
||||
class _DisconnectedWidgetState extends State<DisconnectedWidget> {
|
||||
late final TextEditingController _name = TextEditingController(text: "wallet");
|
||||
late final TextEditingController _seed = TextEditingController(
|
||||
text:
|
||||
"palm annoy brush task almost through here sent doll guilty smart horse mere canvas flirt advice fruit known shower happiness steel autumn beautiful approach anymore canvas");
|
||||
bool _loading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
() async {
|
||||
final preferences = await SharedPreferences.getInstance();
|
||||
final value = preferences.getString(walletName);
|
||||
if (value != null && value.isNotEmpty) _name.text = value;
|
||||
}();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: FutureBuilder(
|
||||
future: connect(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) return CircularProgressIndicator();
|
||||
return Center(child: Text("connected"));
|
||||
},
|
||||
appBar: AppBar(title: Text('Disconnected')),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: Stack(
|
||||
children: [
|
||||
Opacity(
|
||||
opacity: _loading ? 0.5 : 1,
|
||||
child: Column(
|
||||
children: [
|
||||
TextField(
|
||||
controller: _name,
|
||||
decoration: InputDecoration(labelText: 'Wallet name')),
|
||||
TextButton(
|
||||
child: Text('Connect and Open Wallet'),
|
||||
onPressed: () async {
|
||||
//setState(() => _loading = true);
|
||||
final preferences =
|
||||
await SharedPreferences.getInstance();
|
||||
await preferences.setString(walletName, _name.text);
|
||||
final result = await connect(_name.text);
|
||||
//setState(() => _loading = false);
|
||||
if (result != null) {
|
||||
debugPrint("navigated to connected");
|
||||
Navigator.of(context).pushReplacementNamed(
|
||||
ConnectedWidget.route,
|
||||
arguments: result,
|
||||
);
|
||||
} else {
|
||||
debugPrint('connect no result');
|
||||
}
|
||||
}),
|
||||
SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
TextButton(
|
||||
child: Text('Create and Open Wallet'),
|
||||
onPressed: () async {
|
||||
//setState(() => _loading = true);
|
||||
final preferences =
|
||||
await SharedPreferences.getInstance();
|
||||
await preferences.setString(walletName, _name.text);
|
||||
final result = await create(_name.text);
|
||||
//setState(() => _loading = false);
|
||||
if (result != null) {
|
||||
debugPrint("navigating to connected");
|
||||
Navigator.of(context).pushReplacementNamed(
|
||||
ConnectedWidget.route,
|
||||
arguments: result,
|
||||
);
|
||||
} else {
|
||||
debugPrint('create no result');
|
||||
}
|
||||
}),
|
||||
SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
TextField(
|
||||
controller: _seed,
|
||||
decoration: InputDecoration(labelText: 'Wallet seed')),
|
||||
TextButton(
|
||||
child: Text('Restore from seed'),
|
||||
onPressed: () async {
|
||||
final preferences =
|
||||
await SharedPreferences.getInstance();
|
||||
await preferences.setString(walletName, _name.text);
|
||||
final result = await restore(_name.text, _seed.text);
|
||||
if (result != null) {
|
||||
Navigator.of(context).pushReplacementNamed(
|
||||
ConnectedWidget.route,
|
||||
arguments: result,
|
||||
);
|
||||
} else {
|
||||
debugPrint('restore no result');
|
||||
}
|
||||
}),
|
||||
SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
TextButton(child: Text('Close Wallet'), onPressed: close),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (_loading) Center(child: CircularProgressIndicator()),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static const name = "leo1";
|
||||
|
||||
Future<bool> connect() async {
|
||||
calls.getVersion();
|
||||
final setupNode = await zano_wallet.setupNode(
|
||||
address: "195.201.107.230:33336",
|
||||
login: "",
|
||||
password: "",
|
||||
useSSL: false,
|
||||
isLightWallet: false);
|
||||
final path = await pathForWallet(name: name, type: WalletType.zano);
|
||||
final credentials = ZanoNewWalletCredentials(name: name);
|
||||
final keyService = KeyService(FlutterSecureStorage());
|
||||
final password = await keyService.getWalletPassword(walletName: credentials.name);
|
||||
debugPrint("path $path password $password");
|
||||
final result = await calls.loadWallet(path, password, 0);
|
||||
final map = json.decode(result) as Map<String, dynamic>;
|
||||
int hWallet = 0;
|
||||
if (map["result"] != null) {
|
||||
hWallet = (map["result"] as Map<String, dynamic>)["wallet_id"] as int;
|
||||
debugPrint("hWallet $hWallet");
|
||||
}
|
||||
Future.delayed(Duration(seconds: 10));
|
||||
await calls.getWalletStatus(hWallet);
|
||||
Future.delayed(Duration(seconds: 10));
|
||||
await calls.getRecentTxsAndInfo(hWallet: hWallet, offset: 0, count: 30);
|
||||
Future.delayed(Duration(seconds: 2));
|
||||
calls.closeWallet(hWallet);
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<bool> _connect() async {
|
||||
calls.getVersion();
|
||||
final result = await zano_wallet.setupNode(
|
||||
address: "195.201.107.230:33336",
|
||||
login: "",
|
||||
password: "",
|
||||
useSSL: false,
|
||||
isLightWallet: false);
|
||||
//debugPrint("setup node result ${result}");
|
||||
//final name = "leo1";
|
||||
final path = await pathForWallet(name: name, type: WalletType.zano);
|
||||
final credentials = ZanoNewWalletCredentials(name: name);
|
||||
final keyService = KeyService(FlutterSecureStorage());
|
||||
final password = generateWalletPassword();
|
||||
credentials.password = password;
|
||||
await keyService.saveWalletPassword(
|
||||
password: password, walletName: credentials.name);
|
||||
final createResult = await zano_wallet_manager.createWallet(
|
||||
language: "", path: path, password: credentials.password!);
|
||||
debugPrint("createWallet result $createResult");
|
||||
final map = json.decode(createResult) as Map<String, dynamic>;
|
||||
int hWallet = -1;
|
||||
if (map["result"] != null) {
|
||||
hWallet = (map["result"] as Map<String, dynamic>)["wallet_id"] as int;
|
||||
debugPrint("hWallet $hWallet");
|
||||
}
|
||||
//await calls.loadWallet(path, password, 0);
|
||||
calls.getConnectivityStatus();
|
||||
await calls.store(hWallet);
|
||||
calls.getWalletInfo(hWallet);
|
||||
calls.getWalletStatus(hWallet);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
469
lib/zano_connected_widget.dart
Normal file
469
lib/zano_connected_widget.dart
Normal file
|
@ -0,0 +1,469 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cake_wallet/zano.dart';
|
||||
import 'package:cw_zano/api/model/destination.dart';
|
||||
import 'package:cw_zano/api/model/get_wallet_info_result.dart';
|
||||
import 'package:cw_zano/api/model/get_wallet_status_result.dart';
|
||||
import 'package:cw_zano/api/model/history.dart';
|
||||
import 'package:cw_zano/api/model/transfer_params.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cw_zano/api/calls.dart' as calls;
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class ConnectedWidget extends StatefulWidget {
|
||||
final String address;
|
||||
const ConnectedWidget({super.key, required this.address});
|
||||
static const route = 'connected';
|
||||
|
||||
@override
|
||||
State<ConnectedWidget> createState() => _ConnectedWidgetState();
|
||||
}
|
||||
|
||||
class _ConnectedWidgetState extends State<ConnectedWidget> {
|
||||
Timer? _longRefreshTimer;
|
||||
GetWalletStatusResult? _gwsr;
|
||||
int? _txFee;
|
||||
final int _mixin = 10;
|
||||
late final TextEditingController _destinationAddress =
|
||||
TextEditingController(text: widget.address);
|
||||
static const defaultAmount = 1.0;
|
||||
late final TextEditingController _amount = TextEditingController(text: defaultAmount.toString());
|
||||
late String _amountFormatted = _mulBy10_12(defaultAmount);
|
||||
late final TextEditingController _paymentId = TextEditingController();
|
||||
late final TextEditingController _comment = TextEditingController(text: "test");
|
||||
bool _pushPayer = false;
|
||||
bool _hideReceiver = true;
|
||||
String _transferResult = '';
|
||||
List<History>? _transactions;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// _getWalletStatus returning true if it's in long refresh
|
||||
// in a long refresh we keep requesting _getWalletStatus until we get false
|
||||
if (_getWalletStatus()) {
|
||||
_longRefreshTimer = Timer.periodic(Duration(milliseconds: 1000), (timer) {
|
||||
if (!_getWalletStatus()) {
|
||||
_longRefreshTimer!.cancel();
|
||||
debugPrint('cancelling get wallet status timer');
|
||||
_getWalletInfo();
|
||||
}
|
||||
});
|
||||
}
|
||||
//_getWalletInfo();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
//_timer.cancel();
|
||||
// _myAddress.dispose();
|
||||
// _seed.dispose();
|
||||
_destinationAddress.dispose();
|
||||
_amount.dispose();
|
||||
_paymentId.dispose();
|
||||
_comment.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
length: 4,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Version $version'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
onPressed: () {
|
||||
close();
|
||||
Navigator.of(context).pushReplacementNamed(DisconnectedWidget.route);
|
||||
},
|
||||
)
|
||||
],
|
||||
bottom: TabBar(
|
||||
tabs: [
|
||||
Tab(text: 'Main'),
|
||||
Tab(text: 'Transfer'),
|
||||
Builder(builder: (context) {
|
||||
if (lwr != null && lwr!.recentHistory.history != null) {
|
||||
return Tab(text: 'History (${lwr!.recentHistory.history!.length})');
|
||||
}
|
||||
return Tab(text: 'History');
|
||||
}),
|
||||
Tab(text: 'Transactions')
|
||||
],
|
||||
)),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TabBarView(
|
||||
children: [
|
||||
_mainTab(context),
|
||||
_transferTab(context),
|
||||
_historyTab(),
|
||||
_transactionsTab(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _transactionsTab() {
|
||||
return Column(children: [
|
||||
TextButton(onPressed: _getTransactions, child: Text('Update list of Transactions')),
|
||||
Expanded(child: _transactionsListView(_transactions)),
|
||||
]);
|
||||
}
|
||||
|
||||
Widget _historyTab() {
|
||||
if (lwr == null) return Text("Empty");
|
||||
return _transactionsListView(lwr!.recentHistory.history);
|
||||
}
|
||||
|
||||
ListView _transactionsListView(List<History>? list) {
|
||||
return ListView.builder(
|
||||
itemCount: list != null ? list.length : 0,
|
||||
itemBuilder: (context, index) {
|
||||
final item = list![index];
|
||||
late String addr;
|
||||
if (item.remoteAddresses.isNotEmpty) {
|
||||
addr = _shorten(item.remoteAddresses.first);
|
||||
} else {
|
||||
addr = "???";
|
||||
}
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text("${index + 1}. ${_dateTime(item.timestamp)} Remote addr: $addr"),
|
||||
if (item.remoteAddresses.isNotEmpty)
|
||||
IconButton(
|
||||
onPressed: () =>
|
||||
Clipboard.setData(ClipboardData(text: item.remoteAddresses.first)),
|
||||
icon: Icon(Icons.copy),
|
||||
),
|
||||
if (item.remoteAliases.isNotEmpty) Text(" (${item.remoteAliases.first})"),
|
||||
],
|
||||
),
|
||||
Text(" txHash: ${item.txHash} comment: ${item.comment}"),
|
||||
Text(
|
||||
" paymentId: ${item.paymentId} height: ${item.height} fee: ${_divBy10_12(item.fee)}"),
|
||||
if (item.employedEntries.receive.isNotEmpty)
|
||||
Text(" Receive", style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
for (int i = 0; i < item.employedEntries.receive.length; i++)
|
||||
Text(
|
||||
' ${item.employedEntries.receive[i].index}. ${_assetName(item.employedEntries.receive[i].assetId)} ${_divBy10_12(item.employedEntries.receive[i].amount)}'),
|
||||
if (item.employedEntries.send.isNotEmpty)
|
||||
Text(" Spent", style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
for (int i = 0; i < item.employedEntries.send.length; i++)
|
||||
Text(
|
||||
' ${item.employedEntries.send[i].index}. ${_assetName(item.employedEntries.send[i].assetId)} ${_divBy10_12(item.employedEntries.send[i].amount)}'),
|
||||
if (item.subtransfers.isNotEmpty)
|
||||
Text(" Subtransfers", style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
for (int i = 0; i < item.subtransfers.length; i++)
|
||||
Text(
|
||||
' ${item.subtransfers[i].isIncome ? 'In' : 'Out'}. ${_assetName(item.subtransfers[i].assetId)} ${_divBy10_12(item.subtransfers[i].amount)}'),
|
||||
Divider(),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _transferTab(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text('Remote Address ', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _destinationAddress,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => Clipboard.setData(ClipboardData(text: _destinationAddress.text)),
|
||||
icon: Icon(Icons.copy)),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
final clipboard = await Clipboard.getData("text/plain");
|
||||
if (clipboard == null || clipboard.text == null) return;
|
||||
setState(() {
|
||||
_destinationAddress.text = clipboard.text!;
|
||||
});
|
||||
},
|
||||
icon: Icon(Icons.paste)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
// ${lwr!.wi.address}
|
||||
Text('Amount ', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _amount,
|
||||
onChanged: (value) => setState(() {
|
||||
_amountFormatted = _mulBy10_12(double.parse(value));
|
||||
}),
|
||||
),
|
||||
),
|
||||
Text("= ${_amountFormatted}"),
|
||||
IconButton(
|
||||
onPressed: () => Clipboard.setData(ClipboardData(text: _amount.text)),
|
||||
icon: Icon(Icons.copy)),
|
||||
],
|
||||
),
|
||||
if (_txFee != null)
|
||||
Text('Fee: ${_divBy10_12(_txFee!)} (${_txFee!})')
|
||||
else
|
||||
Text("Pls get Tx Fee before transfer!"),
|
||||
Text('Mixin: $_mixin'),
|
||||
Row(children: [
|
||||
Text('Payment Id ', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Expanded(child: TextField(controller: _paymentId)),
|
||||
]),
|
||||
Row(children: [
|
||||
Text('Comment ', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Expanded(child: TextField(controller: _comment)),
|
||||
]),
|
||||
Row(
|
||||
children: [
|
||||
Text('Push Payer ', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Checkbox(
|
||||
value: _pushPayer,
|
||||
onChanged: (value) => setState(() => _pushPayer = value ?? false)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text('Hide Receiver ', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Checkbox(
|
||||
value: _hideReceiver,
|
||||
onChanged: (value) => setState(() => _hideReceiver = value ?? false)),
|
||||
],
|
||||
),
|
||||
TextButton(onPressed: _transfer, child: Text('Transfer')),
|
||||
const SizedBox(height: 16),
|
||||
Text('Transfer result $_transferResult'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _mainTab(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text('Wallet Info', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(width: 16),
|
||||
TextButton(onPressed: _getWalletInfo, child: Text('Update WI & TxFee')),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text('My Address ', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Expanded(
|
||||
child: Text(
|
||||
widget.address,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)),
|
||||
IconButton(
|
||||
onPressed: () => Clipboard.setData(ClipboardData(text: widget.address)),
|
||||
icon: Icon(Icons.copy)),
|
||||
],
|
||||
),
|
||||
for (final balance in balances)
|
||||
Text(
|
||||
'Balance (${balance.assetInfo.ticker}) total: ${_divBy10_12(balance.total)}, unlocked: ${_divBy10_12(balance.unlocked)}'),
|
||||
Row(
|
||||
children: [
|
||||
Text('Seed ', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Expanded(child: Text(seed, maxLines: 1, overflow: TextOverflow.ellipsis)),
|
||||
IconButton(
|
||||
onPressed: () => Clipboard.setData(ClipboardData(text: seed)),
|
||||
icon: Icon(Icons.copy)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Text('Wallet Status', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(width: 16),
|
||||
TextButton(onPressed: _getWalletStatus, child: Text('Update')),
|
||||
],
|
||||
),
|
||||
if (_gwsr != null) ...[
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: Text('Daemon Height ${_gwsr!.currentDaemonHeight}')),
|
||||
Expanded(child: Text('Wallet Height ${_gwsr!.currentWalletHeight}')),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: Text('Daemon Connected ${_gwsr!.isDaemonConnected}')),
|
||||
Expanded(child: Text('In Long Refresh ${_gwsr!.isInLongRefresh}')),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: Text('Progress ${_gwsr!.progress}')),
|
||||
Expanded(child: Text('WalletState ${_gwsr!.walletState}')),
|
||||
],
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
if (_txFee != null) Text('Tx Fee: ${_divBy10_12(_txFee!)} (${_txFee!})'),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
close();
|
||||
Navigator.of(context).pushReplacementNamed(DisconnectedWidget.route);
|
||||
},
|
||||
child: Text('Disconnect')),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _transfer() async {
|
||||
final result = await calls.transfer(
|
||||
hWallet,
|
||||
TransferParams(
|
||||
destinations: [
|
||||
Destination(
|
||||
amount: _mulBy10_12(double.parse(_amount.text)),
|
||||
address: _destinationAddress.text,
|
||||
assetId: assetIds.keys.first,
|
||||
)
|
||||
],
|
||||
fee: _txFee!,
|
||||
mixin: _mixin,
|
||||
paymentId: _paymentId.text,
|
||||
comment: _comment.text,
|
||||
pushPayer: _pushPayer,
|
||||
hideReceiver: _hideReceiver,
|
||||
));
|
||||
debugPrint('transfer result $result');
|
||||
final map = jsonDecode(result);
|
||||
if (map['result'] == null) {
|
||||
setState(() => _transferResult = 'empty result');
|
||||
} else {
|
||||
if (map['result']['error'] != null) {
|
||||
setState(() => _transferResult =
|
||||
"error code ${map['result']['error']['code']} message ${map['result']['error']['message']} ");
|
||||
} else if (map['result']['result'] != null) {
|
||||
setState(() => _transferResult =
|
||||
"transfer tx hash ${map['result']['result']['tx_hash']} size ${map['result']['result']['tx_size']} ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _getWalletStatus() {
|
||||
final json = calls.getWalletStatus(hWallet);
|
||||
if (json == walletWrongId) {
|
||||
debugPrint('error $walletWrongId');
|
||||
setState(() => _gwsr = null);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
setState(() {
|
||||
_gwsr = GetWalletStatusResult.fromJson(jsonDecode(json) as Map<String, dynamic>);
|
||||
});
|
||||
return _gwsr!.isInLongRefresh;
|
||||
} catch (e) {
|
||||
debugPrint('exception $e');
|
||||
setState(() => _gwsr = null);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void _getWalletInfo() {
|
||||
final result = GetWalletInfoResult.fromJson(
|
||||
jsonDecode(calls.getWalletInfo(hWallet)) as Map<String, dynamic>);
|
||||
final fee = calls.getCurrentTxFee(0);
|
||||
setState(() {
|
||||
balances = result.wi.balances;
|
||||
seed = result.wiExtended.seed;
|
||||
_txFee = fee;
|
||||
});
|
||||
// setState(() {
|
||||
// _gwsr = GetWalletStatusResult.fromJson(
|
||||
// jsonDecode(calls.getWalletStatus(hWallet)) as Map<String, dynamic>);
|
||||
// });
|
||||
}
|
||||
|
||||
Future<void> _getTransactions() async {
|
||||
final result = await calls.getRecentTxsAndInfo(hWallet: hWallet, offset: 0, count: 30);
|
||||
final map = jsonDecode(result);
|
||||
if (map == null || map["result"] == null || map["result"]["result"] == null) {
|
||||
setState(() => _transactions = null);
|
||||
return;
|
||||
}
|
||||
setState(() => _transactions = map["result"]["result"]["transfers"] == null
|
||||
? null
|
||||
: (map["result"]["result"]["transfers"] as List<dynamic>)
|
||||
.map((e) => History.fromJson(e as Map<String, dynamic>))
|
||||
.toList());
|
||||
}
|
||||
|
||||
String _divBy10_12(int value) {
|
||||
return (value / pow(10, 12)).toString();
|
||||
}
|
||||
|
||||
String _mulBy10_12(double value) {
|
||||
var str = (value * pow(10, 12)).toString();
|
||||
if (str.contains('.')) str = str.split('.')[0];
|
||||
return str;
|
||||
}
|
||||
|
||||
String _shorten(String someId) {
|
||||
if (someId.length < 9) return someId;
|
||||
return '${someId.substring(0, 4).toUpperCase()}...${someId.substring(someId.length - 2)}';
|
||||
}
|
||||
|
||||
String _assetName(String assetId) {
|
||||
if (assetIds[assetId] != null) {
|
||||
return assetIds[assetId]!;
|
||||
} else {
|
||||
return _shorten(assetId);
|
||||
}
|
||||
}
|
||||
|
||||
String _dateTime(int timestamp) {
|
||||
DateTime date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
||||
return '${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')} ${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
Widget _row(
|
||||
String first, String second, String third, String forth, String fifth, String sixth) =>
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: Text(first)),
|
||||
Expanded(flex: 2, child: Text(second)),
|
||||
Expanded(flex: 2, child: Text(third)),
|
||||
Expanded(flex: 3, child: Text(forth)),
|
||||
Expanded(flex: 3, child: Text(fifth)),
|
||||
Expanded(child: Text(sixth)),
|
||||
],
|
||||
);
|
||||
}
|
Loading…
Reference in a new issue