diff --git a/.gitignore b/.gitignore
index 0580d006c..b5c6f1e12 100644
--- a/.gitignore
+++ b/.gitignore
@@ -97,4 +97,7 @@ android/app/.cxx/**
ios/Flutter/.last_build_id
/lib/generated/**
#**#
-/**/#**#
\ No newline at end of file
+/**/#**#
+
+**/google-services.json
+**/GoogleService-Info.plist
\ No newline at end of file
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 0c6a46dc4..ee68ec0db 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -24,6 +24,10 @@ if (flutterVersionName == null) {
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+if (file("./google-services.json").exists()) {
+ apply plugin: 'com.google.gms.google-services'
+}
+
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
@@ -83,4 +87,6 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+ implementation 'com.google.firebase:firebase-core:19.0.0'
+ implementation 'com.google.firebase:firebase-messaging:19.0.0'
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index abf27576f..c53d1ea7b 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -7,6 +7,7 @@
-
diff --git a/android/app/src/main/java/com/cakewallet/cake_wallet/Application.java b/android/app/src/main/java/com/cakewallet/cake_wallet/Application.java
new file mode 100644
index 000000000..c3b51c14c
--- /dev/null
+++ b/android/app/src/main/java/com/cakewallet/cake_wallet/Application.java
@@ -0,0 +1,21 @@
+package com.cakewallet.cake_wallet;
+
+import io.flutter.app.FlutterApplication;
+import io.flutter.plugin.common.PluginRegistry;
+import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
+import io.flutter.plugins.GeneratedPluginRegistrant;
+import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService;
+import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin;
+
+public class Application extends FlutterApplication implements PluginRegistrantCallback {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ FlutterFirebaseMessagingService.setPluginRegistrant(this);
+ }
+
+ @Override
+ public void registerWith(PluginRegistry registry) {
+ FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
+ }
+}
\ No newline at end of file
diff --git a/android/build.gradle b/android/build.gradle
index 0baed694d..0d11c6b94 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -6,6 +6,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.5.4'
+ classpath 'com.google.gms:google-services:4.3.8'
}
}
diff --git a/assets/images/2.0x/github.png b/assets/images/2.0x/github.png
new file mode 100644
index 000000000..e80b428e5
Binary files /dev/null and b/assets/images/2.0x/github.png differ
diff --git a/assets/images/3.0x/github.png b/assets/images/3.0x/github.png
new file mode 100644
index 000000000..86b48e446
Binary files /dev/null and b/assets/images/3.0x/github.png differ
diff --git a/assets/images/github.png b/assets/images/github.png
new file mode 100644
index 000000000..847a269a2
Binary files /dev/null and b/assets/images/github.png differ
diff --git a/assets/images/moonpay-icon.png b/assets/images/moonpay-icon.png
new file mode 100644
index 000000000..3c0260108
Binary files /dev/null and b/assets/images/moonpay-icon.png differ
diff --git a/cw_monero/ios/Classes/monero_api.cpp b/cw_monero/ios/Classes/monero_api.cpp
index dd1ab3cbb..d58f4e509 100644
--- a/cw_monero/ios/Classes/monero_api.cpp
+++ b/cw_monero/ios/Classes/monero_api.cpp
@@ -134,6 +134,7 @@ extern "C"
uint32_t subaddrAccount;
int8_t direction;
int8_t isPending;
+ uint32_t subaddrIndex;
char *hash;
char *paymentId;
@@ -146,6 +147,8 @@ extern "C"
fee = transaction->fee();
blockHeight = transaction->blockHeight();
subaddrAccount = transaction->subaddrAccount();
+ std::set::iterator it = transaction->subaddrIndex().begin();
+ subaddrIndex = *it;
confirmations = transaction->confirmations();
datetime = static_cast(transaction->timestamp());
direction = transaction->direction();
diff --git a/cw_monero/lib/structs/transaction_info_row.dart b/cw_monero/lib/structs/transaction_info_row.dart
index 0a0613a42..37b0d02e8 100644
--- a/cw_monero/lib/structs/transaction_info_row.dart
+++ b/cw_monero/lib/structs/transaction_info_row.dart
@@ -23,6 +23,9 @@ class TransactionInfoRow extends Struct {
@Int8()
int isPending;
+ @Uint32()
+ int subaddrIndex;
+
Pointer hash;
Pointer paymentId;
diff --git a/ios/Podfile b/ios/Podfile
index e26de900c..3cfff7b1d 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -36,6 +36,7 @@ target 'Runner' do
# Cake Wallet (Legacy)
pod 'CryptoSwift'
+ pod 'Firebase/Messaging', '6.33.0'
end
post_install do |installer|
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 879041cac..50f9b20d2 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -62,18 +62,84 @@ PODS:
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
+ - Firebase/CoreOnly (6.33.0):
+ - FirebaseCore (= 6.10.3)
+ - Firebase/Messaging (6.33.0):
+ - Firebase/CoreOnly
+ - FirebaseMessaging (~> 4.7.0)
+ - firebase_core (0.5.3):
+ - Firebase/CoreOnly (~> 6.33.0)
+ - Flutter
+ - firebase_messaging (7.0.3):
+ - Firebase/CoreOnly (~> 6.33.0)
+ - Firebase/Messaging (~> 6.33.0)
+ - firebase_core
+ - Flutter
+ - FirebaseCore (6.10.3):
+ - FirebaseCoreDiagnostics (~> 1.6)
+ - GoogleUtilities/Environment (~> 6.7)
+ - GoogleUtilities/Logger (~> 6.7)
+ - FirebaseCoreDiagnostics (1.7.0):
+ - GoogleDataTransport (~> 7.4)
+ - GoogleUtilities/Environment (~> 6.7)
+ - GoogleUtilities/Logger (~> 6.7)
+ - nanopb (~> 1.30906.0)
+ - FirebaseInstallations (1.7.0):
+ - FirebaseCore (~> 6.10)
+ - GoogleUtilities/Environment (~> 6.7)
+ - GoogleUtilities/UserDefaults (~> 6.7)
+ - PromisesObjC (~> 1.2)
+ - FirebaseInstanceID (4.8.0):
+ - FirebaseCore (~> 6.10)
+ - FirebaseInstallations (~> 1.6)
+ - GoogleUtilities/Environment (~> 6.7)
+ - GoogleUtilities/UserDefaults (~> 6.7)
+ - FirebaseMessaging (4.7.1):
+ - FirebaseCore (~> 6.10)
+ - FirebaseInstanceID (~> 4.7)
+ - GoogleUtilities/AppDelegateSwizzler (~> 6.7)
+ - GoogleUtilities/Environment (~> 6.7)
+ - GoogleUtilities/Reachability (~> 6.7)
+ - GoogleUtilities/UserDefaults (~> 6.7)
+ - Protobuf (>= 3.9.2, ~> 3.9)
- Flutter (1.0.0)
- flutter_secure_storage (3.3.1):
- Flutter
+ - GoogleDataTransport (7.5.1):
+ - nanopb (~> 1.30906.0)
+ - GoogleUtilities/AppDelegateSwizzler (6.7.2):
+ - GoogleUtilities/Environment
+ - GoogleUtilities/Logger
+ - GoogleUtilities/Network
+ - GoogleUtilities/Environment (6.7.2):
+ - PromisesObjC (~> 1.2)
+ - GoogleUtilities/Logger (6.7.2):
+ - GoogleUtilities/Environment
+ - GoogleUtilities/Network (6.7.2):
+ - GoogleUtilities/Logger
+ - "GoogleUtilities/NSData+zlib"
+ - GoogleUtilities/Reachability
+ - "GoogleUtilities/NSData+zlib (6.7.2)"
+ - GoogleUtilities/Reachability (6.7.2):
+ - GoogleUtilities/Logger
+ - GoogleUtilities/UserDefaults (6.7.2):
+ - GoogleUtilities/Logger
- local_auth (0.0.1):
- Flutter
- MTBBarcodeScanner (5.0.11)
+ - nanopb (1.30906.0):
+ - nanopb/decode (= 1.30906.0)
+ - nanopb/encode (= 1.30906.0)
+ - nanopb/decode (1.30906.0)
+ - nanopb/encode (1.30906.0)
- package_info (0.0.1):
- Flutter
- path_provider (0.0.1):
- Flutter
- "permission_handler (5.1.0+2)":
- Flutter
+ - PromisesObjC (1.2.12)
+ - Protobuf (3.17.0)
- Reachability (3.2)
- SDWebImage (5.9.1):
- SDWebImage/Core (= 5.9.1)
@@ -97,6 +163,9 @@ DEPENDENCIES:
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
- esys_flutter_share (from `.symlinks/plugins/esys_flutter_share/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
+ - Firebase/Messaging (= 6.33.0)
+ - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
+ - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- local_auth (from `.symlinks/plugins/local_auth/ios`)
@@ -113,7 +182,18 @@ SPEC REPOS:
- CryptoSwift
- DKImagePickerController
- DKPhotoGallery
+ - Firebase
+ - FirebaseCore
+ - FirebaseCoreDiagnostics
+ - FirebaseInstallations
+ - FirebaseInstanceID
+ - FirebaseMessaging
+ - GoogleDataTransport
+ - GoogleUtilities
- MTBBarcodeScanner
+ - nanopb
+ - PromisesObjC
+ - Protobuf
- Reachability
- SDWebImage
- SwiftProtobuf
@@ -132,6 +212,10 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/esys_flutter_share/ios"
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
+ firebase_core:
+ :path: ".symlinks/plugins/firebase_core/ios"
+ firebase_messaging:
+ :path: ".symlinks/plugins/firebase_messaging/ios"
Flutter:
:path: Flutter
flutter_secure_storage:
@@ -163,13 +247,26 @@ SPEC CHECKSUMS:
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
esys_flutter_share: 403498dab005b36ce1f8d7aff377e81f0621b0b4
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
+ Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5
+ firebase_core: 5d6a02f3d85acd5f8321c2d6d62877626a670659
+ firebase_messaging: 0aea2cd5885b65e19ede58ee3507f485c992cc75
+ FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd
+ FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1
+ FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2
+ FirebaseInstanceID: bd3ffc24367f901a43c063b36c640b345a4a5dd1
+ FirebaseMessaging: 5eca4ef173de76253352511aafef774caa1cba2a
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
+ GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833
+ GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
local_auth: 25938960984c3a7f6e3253e3f8d962fdd16852bd
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
+ nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0
+ PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
+ Protobuf: 7327d4444215b5f18e560a97f879ff5503c4581c
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
SDWebImage: a990c053fff71e388a10f3357edb0be17929c9c5
share: 0b2c3e82132f5888bccca3351c504d0003b3b410
@@ -179,6 +276,6 @@ SPEC CHECKSUMS:
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
webview_flutter: 9f491a9b5a66f2573946a389b2677987b0ff8c0b
-PODFILE CHECKSUM: 5b5f101b119a1b6eb857c967d462832a9062dec4
+PODFILE CHECKSUM: f316539722a6f9dbb0d0f1065a27fa7ea38c6f88
COCOAPODS: 1.9.3
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 79c69b9ed..6f6193063 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 0C37B8B926678E9100C6E5A9 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0C37B8B826678E9100C6E5A9 /* GoogleService-Info.plist */; };
0C44A71A2518EF8000B570ED /* decrypt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C44A7192518EF8000B570ED /* decrypt.swift */; };
0C9D68C9264854B60011B691 /* secRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9D68C8264854B60011B691 /* secRandom.swift */; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
@@ -19,6 +20,7 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
+ 0C37B8B826678E9100C6E5A9 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; };
0C400E0F25B21ABB0025E469 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; };
0C44A7192518EF8000B570ED /* decrypt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = decrypt.swift; sourceTree = ""; };
0C9986A3251A932F00D566FD /* CryptoSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CryptoSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -96,6 +98,7 @@
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
+ 0C37B8B826678E9100C6E5A9 /* GoogleService-Info.plist */,
0C44A7182518EF4A00B570ED /* CakeWallet */,
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
@@ -191,6 +194,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 0C37B8B926678E9100C6E5A9 /* GoogleService-Info.plist in Resources */,
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
@@ -362,9 +366,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CURRENT_PROJECT_VERSION = 40;
+ CURRENT_PROJECT_VERSION = 49;
DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO;
+ EXCLUDED_SOURCE_FILE_NAMES = "";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@@ -379,7 +384,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
- MARKETING_VERSION = 4.2.0;
+ MARKETING_VERSION = 4.2.3;
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -505,9 +510,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CURRENT_PROJECT_VERSION = 40;
+ CURRENT_PROJECT_VERSION = 49;
DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO;
+ EXCLUDED_SOURCE_FILE_NAMES = "";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@@ -522,7 +528,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
- MARKETING_VERSION = 4.2.0;
+ MARKETING_VERSION = 4.2.3;
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -540,9 +546,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CURRENT_PROJECT_VERSION = 40;
+ CURRENT_PROJECT_VERSION = 49;
DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO;
+ EXCLUDED_SOURCE_FILE_NAMES = "";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@@ -557,7 +564,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
- MARKETING_VERSION = 4.2.0;
+ MARKETING_VERSION = 4.2.3;
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index e7c300079..2a3505ef9 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -7,6 +7,10 @@ import Flutter
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
+ if #available(iOS 10.0, *) {
+ UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
+ }
+
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let legacyMigrationChannel = FlutterMethodChannel(
name: "com.cakewallet.cakewallet/legacy_wallet_migration",
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 970c892cd..267805951 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -37,6 +37,11 @@
Enable Face ID for fast and secure access to wallets and private keys
NSPhotoLibraryUsageDescription
We need access to documents folder for get acces to open/save backup file
+ UIBackgroundModes
+
+ fetch
+ remote-notification
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements
index 0c67376eb..903def2af 100644
--- a/ios/Runner/Runner.entitlements
+++ b/ios/Runner/Runner.entitlements
@@ -1,5 +1,8 @@
-
+
+ aps-environment
+ development
+
diff --git a/lib/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart b/lib/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart
index 5171a4f05..8d0583ce5 100644
--- a/lib/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart
+++ b/lib/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart
@@ -1,5 +1,5 @@
class BitcoinMnemonicIsIncorrectException implements Exception {
@override
String toString() =>
- 'Bitcoin mnemonic has incorrect format. Mnemonic should contain 24 words separated by space.';
+ 'Bitcoin mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
}
diff --git a/lib/bitcoin/electrum_transaction_history.dart b/lib/bitcoin/electrum_transaction_history.dart
index 1d5e894c0..2aabf1304 100644
--- a/lib/bitcoin/electrum_transaction_history.dart
+++ b/lib/bitcoin/electrum_transaction_history.dart
@@ -35,7 +35,7 @@ abstract class ElectrumTransactionHistoryBase
@override
void addMany(Map transactions) =>
- this.transactions.addAll(transactions);
+ transactions.forEach((_, tx) => _updateOrInsert(tx));
@override
Future save() async {
diff --git a/lib/buy/buy_amount.dart b/lib/buy/buy_amount.dart
new file mode 100644
index 000000000..3cf3543d8
--- /dev/null
+++ b/lib/buy/buy_amount.dart
@@ -0,0 +1,12 @@
+import 'package:flutter/foundation.dart';
+
+class BuyAmount {
+ BuyAmount({
+ @required this.sourceAmount,
+ @required this.destAmount,
+ this.minAmount = 0});
+
+ final double sourceAmount;
+ final double destAmount;
+ final int minAmount;
+}
\ No newline at end of file
diff --git a/lib/buy/buy_exception.dart b/lib/buy/buy_exception.dart
new file mode 100644
index 000000000..28064fdfc
--- /dev/null
+++ b/lib/buy/buy_exception.dart
@@ -0,0 +1,12 @@
+import 'package:flutter/foundation.dart';
+import 'package:cake_wallet/buy/buy_provider_description.dart';
+
+class BuyException implements Exception {
+ BuyException({@required this.description, @required this.text});
+
+ final BuyProviderDescription description;
+ final String text;
+
+ @override
+ String toString() => '${description.title}: $text';
+}
\ No newline at end of file
diff --git a/lib/buy/buy_provider.dart b/lib/buy/buy_provider.dart
new file mode 100644
index 000000000..b908d7bc5
--- /dev/null
+++ b/lib/buy/buy_provider.dart
@@ -0,0 +1,27 @@
+import 'package:cake_wallet/buy/buy_amount.dart';
+import 'package:cake_wallet/buy/buy_provider_description.dart';
+import 'package:cake_wallet/buy/order.dart';
+import 'package:cake_wallet/core/wallet_base.dart';
+import 'package:cake_wallet/entities/wallet_type.dart';
+
+abstract class BuyProvider {
+ BuyProvider({this.wallet, this.isTestEnvironment});
+
+ final WalletBase wallet;
+ final bool isTestEnvironment;
+
+ String get title;
+ BuyProviderDescription get description;
+ String get trackUrl;
+
+ WalletType get walletType => wallet.type;
+ String get walletAddress => wallet.address;
+ String get walletId => wallet.id;
+
+ @override
+ String toString() => title;
+
+ Future requestUrl(String amount, String sourceCurrency);
+ Future findOrderById(String id);
+ Future calculateAmount(String amount, String sourceCurrency);
+}
\ No newline at end of file
diff --git a/lib/buy/buy_provider_description.dart b/lib/buy/buy_provider_description.dart
new file mode 100644
index 000000000..46b3bcdc1
--- /dev/null
+++ b/lib/buy/buy_provider_description.dart
@@ -0,0 +1,21 @@
+import 'package:cake_wallet/entities/enumerable_item.dart';
+
+class BuyProviderDescription extends EnumerableItem
+ with Serializable {
+ const BuyProviderDescription({String title, int raw})
+ : super(title: title, raw: raw);
+
+ static const wyre = BuyProviderDescription(title: 'Wyre', raw: 0);
+ static const moonPay = BuyProviderDescription(title: 'MoonPay', raw: 1);
+
+ static BuyProviderDescription deserialize({int raw}) {
+ switch (raw) {
+ case 0:
+ return wyre;
+ case 1:
+ return moonPay;
+ default:
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/buy/get_buy_provider_icon.dart b/lib/buy/get_buy_provider_icon.dart
new file mode 100644
index 000000000..f284b6f04
--- /dev/null
+++ b/lib/buy/get_buy_provider_icon.dart
@@ -0,0 +1,28 @@
+import 'package:flutter/material.dart';
+import 'package:cake_wallet/buy/buy_provider_description.dart';
+
+Image getBuyProviderIcon(BuyProviderDescription providerDescription,
+ {bool isWhiteIconColor = false}) {
+
+ final _wyreIcon =
+ Image.asset('assets/images/wyre-icon.png', width: 36, height: 36);
+ final _moonPayWhiteIcon =
+ Image.asset('assets/images/moonpay-icon.png', color: Colors.white,
+ width: 36, height: 34);
+ final _moonPayBlackIcon =
+ Image.asset('assets/images/moonpay-icon.png', color: Colors.black,
+ width: 36, height: 34);
+
+ if (providerDescription != null) {
+ switch (providerDescription) {
+ case BuyProviderDescription.wyre:
+ return _wyreIcon;
+ case BuyProviderDescription.moonPay:
+ return isWhiteIconColor ? _moonPayWhiteIcon : _moonPayBlackIcon;
+ default:
+ return null;
+ }
+ } else {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/lib/buy/moonpay/moonpay_buy_provider.dart b/lib/buy/moonpay/moonpay_buy_provider.dart
new file mode 100644
index 000000000..65effe483
--- /dev/null
+++ b/lib/buy/moonpay/moonpay_buy_provider.dart
@@ -0,0 +1,144 @@
+import 'dart:convert';
+import 'package:crypto/crypto.dart';
+import 'package:cake_wallet/buy/buy_exception.dart';
+import 'package:http/http.dart';
+import 'package:cake_wallet/buy/buy_amount.dart';
+import 'package:cake_wallet/buy/buy_provider.dart';
+import 'package:cake_wallet/buy/buy_provider_description.dart';
+import 'package:cake_wallet/buy/order.dart';
+import 'package:cake_wallet/core/wallet_base.dart';
+import 'package:cake_wallet/entities/wallet_type.dart';
+import 'package:cake_wallet/exchange/trade_state.dart';
+import 'package:cake_wallet/.secrets.g.dart' as secrets;
+
+class MoonPayBuyProvider extends BuyProvider {
+ MoonPayBuyProvider({WalletBase wallet, bool isTestEnvironment = false})
+ : super(wallet: wallet, isTestEnvironment: isTestEnvironment) {
+ baseUrl = isTestEnvironment ? _baseTestUrl : _baseProductUrl;
+ }
+
+ static const _baseTestUrl = 'https://buy-staging.moonpay.com';
+ static const _baseProductUrl = 'https://buy.moonpay.com';
+ static const _apiUrl = 'https://api.moonpay.com';
+ static const _currenciesSuffix = '/v3/currencies';
+ static const _quoteSuffix = '/buy_quote';
+ static const _transactionsSuffix = '/v1/transactions';
+ static const _ipAddressSuffix = '/v4/ip_address';
+ static const _apiKey = secrets.moonPayApiKey;
+ static const _secretKey = secrets.moonPaySecretKey;
+
+ @override
+ String get title => 'MoonPay';
+
+ @override
+ BuyProviderDescription get description => BuyProviderDescription.moonPay;
+
+ String get currencyCode =>
+ walletTypeToCryptoCurrency(walletType).title.toLowerCase();
+
+ @override
+ String get trackUrl => baseUrl + '/transaction_receipt?transactionId=';
+
+ String baseUrl;
+
+ @override
+ Future requestUrl(String amount, String sourceCurrency) async {
+ final enabledPaymentMethods =
+ 'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay'
+ '%2Csepa_bank_transfer%2Cgbp_bank_transfer%2Cgbp_open_banking_payment';
+
+ final suffix = '?apiKey=' + _apiKey + '¤cyCode=' +
+ currencyCode + '&enabledPaymentMethods=' + enabledPaymentMethods +
+ '&walletAddress=' + walletAddress +
+ '&baseCurrencyCode=' + sourceCurrency.toLowerCase() +
+ '&baseCurrencyAmount=' + amount + '&lockAmount=true' +
+ '&showAllCurrencies=false' + '&showWalletAddressForm=false';
+
+ final originalUrl = baseUrl + suffix;
+
+ final messageBytes = utf8.encode(suffix);
+ final key = utf8.encode(_secretKey);
+ final hmac = Hmac(sha256, key);
+ final digest = hmac.convert(messageBytes);
+ final signature = base64.encode(digest.bytes);
+ final urlWithSignature = originalUrl +
+ '&signature=${Uri.encodeComponent(signature)}';
+
+ return isTestEnvironment ? originalUrl : urlWithSignature;
+ }
+
+ @override
+ Future calculateAmount(String amount, String sourceCurrency) async {
+ final url = _apiUrl + _currenciesSuffix + '/$currencyCode' +
+ _quoteSuffix + '/?apiKey=' + _apiKey +
+ '&baseCurrencyAmount=' + amount +
+ '&baseCurrencyCode=' + sourceCurrency.toLowerCase();
+
+ final response = await get(url);
+
+ if (response.statusCode != 200) {
+ throw BuyException(
+ description: description,
+ text: 'Quote is not found!');
+ }
+
+ final responseJSON = json.decode(response.body) as Map;
+ final sourceAmount = responseJSON['totalAmount'] as double;
+ final destAmount = responseJSON['quoteCurrencyAmount'] as double;
+ final minSourceAmount = responseJSON['baseCurrency']['minAmount'] as int;
+
+ return BuyAmount(
+ sourceAmount: sourceAmount,
+ destAmount: destAmount,
+ minAmount: minSourceAmount);
+ }
+
+ @override
+ Future findOrderById(String id) async {
+ final url = _apiUrl + _transactionsSuffix + '/$id' +
+ '?apiKey=' + _apiKey;
+
+ final response = await get(url);
+
+ if (response.statusCode != 200) {
+ throw BuyException(
+ description: description,
+ text: 'Transaction $id is not found!');
+ }
+
+ final responseJSON = json.decode(response.body) as Map;
+ final status = responseJSON['status'] as String;
+ final state = TradeState.deserialize(raw: status);
+ final createdAtRaw = responseJSON['createdAt'] as String;
+ final createdAt = DateTime.parse(createdAtRaw).toLocal();
+ final amount = responseJSON['quoteCurrencyAmount'] as double;
+
+ return Order(
+ id: id,
+ provider: description,
+ transferId: id,
+ state: state,
+ createdAt: createdAt,
+ amount: amount.toString(),
+ receiveAddress: walletAddress,
+ walletId: walletId
+ );
+ }
+
+ static Future onEnabled() async {
+ final url = _apiUrl + _ipAddressSuffix + '?apiKey=' + _apiKey;
+ var isBuyEnable = false;
+
+ final response = await get(url);
+
+ try {
+ final responseJSON = json.decode(response.body) as Map;
+ isBuyEnable = responseJSON['isBuyAllowed'] as bool;
+ } catch (e) {
+ isBuyEnable = false;
+ print(e.toString());
+ }
+
+ return isBuyEnable;
+ }
+}
\ No newline at end of file
diff --git a/lib/entities/order.dart b/lib/buy/order.dart
similarity index 76%
rename from lib/entities/order.dart
rename to lib/buy/order.dart
index 1676edd99..54c13bd37 100644
--- a/lib/entities/order.dart
+++ b/lib/buy/order.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/buy/buy_provider_description.dart';
import 'package:hive/hive.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/entities/format_amount.dart';
@@ -8,6 +9,7 @@ part 'order.g.dart';
class Order extends HiveObject {
Order(
{this.id,
+ BuyProviderDescription provider,
this.transferId,
this.from,
this.to,
@@ -16,7 +18,8 @@ class Order extends HiveObject {
this.amount,
this.receiveAddress,
this.walletId})
- : stateRaw = state?.raw;
+ : providerRaw = provider?.raw,
+ stateRaw = state?.raw;
static const typeId = 8;
static const boxName = 'Orders';
@@ -51,5 +54,11 @@ class Order extends HiveObject {
@HiveField(8)
String walletId;
+ @HiveField(9)
+ int providerRaw;
+
+ BuyProviderDescription get provider =>
+ BuyProviderDescription.deserialize(raw: providerRaw);
+
String amountFormatted() => formatAmount(amount);
}
\ No newline at end of file
diff --git a/lib/buy/wyre/wyre_buy_provider.dart b/lib/buy/wyre/wyre_buy_provider.dart
new file mode 100644
index 000000000..f1100d130
--- /dev/null
+++ b/lib/buy/wyre/wyre_buy_provider.dart
@@ -0,0 +1,163 @@
+import 'dart:convert';
+import 'package:cake_wallet/buy/buy_exception.dart';
+import 'package:http/http.dart';
+import 'package:cake_wallet/buy/buy_amount.dart';
+import 'package:cake_wallet/buy/buy_provider.dart';
+import 'package:cake_wallet/buy/buy_provider_description.dart';
+import 'package:cake_wallet/buy/order.dart';
+import 'package:cake_wallet/core/wallet_base.dart';
+import 'package:cake_wallet/entities/wallet_type.dart';
+import 'package:cake_wallet/exchange/trade_state.dart';
+import 'package:cake_wallet/.secrets.g.dart' as secrets;
+
+class WyreBuyProvider extends BuyProvider {
+ WyreBuyProvider({WalletBase wallet, bool isTestEnvironment = false})
+ : super(wallet: wallet, isTestEnvironment: isTestEnvironment) {
+ baseApiUrl = isTestEnvironment
+ ? _baseTestApiUrl
+ : _baseProductApiUrl;
+ }
+
+ static const _baseTestApiUrl = 'https://api.testwyre.com';
+ static const _baseProductApiUrl = 'https://api.sendwyre.com';
+ static const _trackTestUrl = 'https://dash.testwyre.com/track/';
+ static const _trackProductUrl = 'https://dash.sendwyre.com/track/';
+ static const _ordersSuffix = '/v3/orders';
+ static const _reserveSuffix = '/reserve';
+ static const _quoteSuffix = '/quote/partner';
+ static const _timeStampSuffix = '?timestamp=';
+ static const _transferSuffix = '/v2/transfer/';
+ static const _trackSuffix = '/track';
+ static const _countryCode = 'US';
+ static const _secretKey = secrets.wyreSecretKey;
+ static const _accountId = secrets.wyreAccountId;
+
+ @override
+ String get title => 'Wyre';
+
+ @override
+ BuyProviderDescription get description => BuyProviderDescription.wyre;
+
+ @override
+ String get trackUrl => isTestEnvironment
+ ? _trackTestUrl
+ : _trackProductUrl;
+
+ String baseApiUrl;
+
+ @override
+ Future requestUrl(String amount, String sourceCurrency) async {
+ final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
+ final url = baseApiUrl + _ordersSuffix + _reserveSuffix +
+ _timeStampSuffix + timestamp;
+ final body = {
+ 'amount': amount,
+ 'sourceCurrency': sourceCurrency,
+ 'destCurrency': walletTypeToCryptoCurrency(walletType).title,
+ 'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress,
+ 'referrerAccountId': _accountId,
+ 'lockFields': ['amount', 'sourceCurrency', 'destCurrency', 'dest']
+ };
+
+ final response = await post(url,
+ headers: {
+ 'Authorization': 'Bearer $_secretKey',
+ 'Content-Type': 'application/json',
+ 'cache-control': 'no-cache'
+ },
+ body: json.encode(body));
+
+ if (response.statusCode != 200) {
+ throw BuyException(
+ description: description,
+ text: 'Url $url is not found!');
+ }
+
+ final responseJSON = json.decode(response.body) as Map;
+ final urlFromResponse = responseJSON['url'] as String;
+ return urlFromResponse;
+ }
+
+ @override
+ Future calculateAmount(String amount, String sourceCurrency) async {
+ final quoteUrl = _baseProductApiUrl + _ordersSuffix + _quoteSuffix;
+ final body = {
+ 'amount': amount,
+ 'sourceCurrency': sourceCurrency,
+ 'destCurrency': walletTypeToCryptoCurrency(walletType).title,
+ 'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress,
+ 'accountId': _accountId,
+ 'country': _countryCode
+ };
+
+ final response = await post(quoteUrl,
+ headers: {
+ 'Authorization': 'Bearer $_secretKey',
+ 'Content-Type': 'application/json',
+ 'cache-control': 'no-cache'
+ },
+ body: json.encode(body));
+
+ if (response.statusCode != 200) {
+ throw BuyException(
+ description: description,
+ text: 'Quote is not found!');
+ }
+
+ final responseJSON = json.decode(response.body) as Map;
+ final sourceAmount = responseJSON['sourceAmount'] as double;
+ final destAmount = responseJSON['destAmount'] as double;
+
+ return BuyAmount(sourceAmount: sourceAmount, destAmount: destAmount);
+ }
+
+ @override
+ Future findOrderById(String id) async {
+ final orderUrl = baseApiUrl + _ordersSuffix + '/$id';
+ final orderResponse = await get(orderUrl);
+
+ if (orderResponse.statusCode != 200) {
+ throw BuyException(
+ description: description,
+ text: 'Order $id is not found!');
+ }
+
+ final orderResponseJSON =
+ json.decode(orderResponse.body) as Map;
+ final transferId = orderResponseJSON['transferId'] as String;
+ final from = orderResponseJSON['sourceCurrency'] as String;
+ final to = orderResponseJSON['destCurrency'] as String;
+ final status = orderResponseJSON['status'] as String;
+ final state = TradeState.deserialize(raw: status.toLowerCase());
+ final createdAtRaw = orderResponseJSON['createdAt'] as int;
+ final createdAt =
+ DateTime.fromMillisecondsSinceEpoch(createdAtRaw).toLocal();
+
+ final transferUrl =
+ baseApiUrl + _transferSuffix + transferId + _trackSuffix;
+ final transferResponse = await get(transferUrl);
+
+ if (transferResponse.statusCode != 200) {
+ throw BuyException(
+ description: description,
+ text: 'Transfer $transferId is not found!');
+ }
+
+ final transferResponseJSON =
+ json.decode(transferResponse.body) as Map;
+ final amount = transferResponseJSON['destAmount'] as double;
+
+ return Order(
+ id: id,
+ provider: description,
+ transferId: transferId,
+ from: from,
+ to: to,
+ state: state,
+ createdAt: createdAt,
+ amount: amount.toString(),
+ receiveAddress: walletAddress,
+ walletId: walletId
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/di.dart b/lib/di.dart
index 70f5b4a51..d887d8832 100644
--- a/lib/di.dart
+++ b/lib/di.dart
@@ -4,11 +4,9 @@ import 'package:cake_wallet/core/backup_service.dart';
import 'package:cake_wallet/core/wallet_service.dart';
import 'package:cake_wallet/entities/biometric_auth.dart';
import 'package:cake_wallet/entities/contact_record.dart';
-import 'package:cake_wallet/entities/load_current_wallet.dart';
-import 'package:cake_wallet/entities/order.dart';
+import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/entities/transaction_description.dart';
import 'package:cake_wallet/entities/transaction_info.dart';
-import 'package:cake_wallet/entities/wyre_service.dart';
import 'package:cake_wallet/monero/monero_wallet_service.dart';
import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/node.dart';
@@ -16,7 +14,8 @@ import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/reactions/on_authentication_state_change.dart';
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
-
+import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
+import 'package:cake_wallet/src/screens/buy/pre_order_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
@@ -45,7 +44,6 @@ import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.da
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
-import 'package:cake_wallet/src/screens/wyre/wyre_page.dart';
import 'package:cake_wallet/store/dashboard/orders_store.dart';
import 'package:cake_wallet/store/node_list_store.dart';
import 'package:cake_wallet/store/secret_store.dart';
@@ -64,6 +62,8 @@ import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.d
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
import 'package:cake_wallet/store/wallet_list_store.dart';
import 'package:cake_wallet/view_model/backup_view_model.dart';
+import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart';
+import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart';
@@ -95,7 +95,6 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
-import 'package:cake_wallet/view_model/wyre_view_model.dart';
import 'package:flutter/widgets.dart';
import 'package:get_it/get_it.dart';
import 'package:hive/hive.dart';
@@ -118,6 +117,7 @@ import 'package:cake_wallet/store/templates/exchange_template_store.dart';
import 'package:cake_wallet/entities/template.dart';
import 'package:cake_wallet/exchange/exchange_template.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
+import 'package:cake_wallet/entities/push_notifications_service.dart';
final getIt = GetIt.instance;
@@ -157,6 +157,7 @@ Future setup(
final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) &&
(secrets.wyreApiKey?.isNotEmpty ?? false) &&
(secrets.wyreAccountId?.isNotEmpty ?? false);
+
final settingsStore = await SettingsStoreBase.load(
nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled);
@@ -237,6 +238,9 @@ Future setup(
settingsStore: getIt.get(),
fiatConvertationStore: getIt.get()));
+
+ getIt.registerFactory(() => PushNotificationsService());
+
getIt.registerFactory(() => DashboardViewModel(
balanceViewModel: getIt.get(),
appStore: getIt.get(),
@@ -244,9 +248,8 @@ Future setup(
tradeFilterStore: getIt.get(),
transactionFilterStore: getIt.get(),
settingsStore: settingsStore,
- ordersSource: _ordersSource,
ordersStore: getIt.get(),
- wyreViewModel: getIt.get()));
+ pushNotificationsService: getIt.get()));
getIt.registerFactory(() => AuthService(
secureStorage: getIt.get(),
@@ -492,10 +495,14 @@ Future setup(
getIt
.registerFactoryParam(
- (TransactionInfo transactionInfo, _) => TransactionDetailsViewModel(
- transactionInfo: transactionInfo,
- transactionDescriptionBox: _transactionDescriptionBox,
- settingsStore: getIt.get()));
+ (TransactionInfo transactionInfo, _) {
+ final wallet = getIt.get().wallet;
+ return TransactionDetailsViewModel(
+ transactionInfo: transactionInfo,
+ transactionDescriptionBox: _transactionDescriptionBox,
+ wallet: wallet,
+ settingsStore: getIt.get());
+ });
getIt.registerFactoryParam(
(TransactionInfo transactionInfo, _) => TransactionDetailsPage(
@@ -542,22 +549,37 @@ Future setup(
getIt.registerFactoryParam((Trade trade, _) =>
TradeDetailsPage(getIt.get(param1: trade)));
+ getIt.registerFactory(() => BuyAmountViewModel());
+
getIt.registerFactory(() {
- return WyreService(appStore: getIt.get());
+ final wallet = getIt.get().wallet;
+
+ return BuyViewModel(_ordersSource, getIt.get(),
+ getIt.get(), getIt.get(),
+ wallet: wallet);
});
getIt.registerFactory(() {
- return WyreViewModel(ordersSource, getIt.get(),
- wyreService: getIt.get());
+ return PreOrderPage(buyViewModel: getIt.get());
});
- getIt.registerFactoryParam((String url, _) =>
- WyrePage(getIt.get(),
- ordersStore: getIt.get(), url: url));
+ getIt.registerFactoryParam(
+ (List args, _) {
+ final url = args.first as String;
+ final buyViewModel = args[1] as BuyViewModel;
- getIt.registerFactoryParam((order, _) =>
- OrderDetailsViewModel(
- wyreViewModel: getIt.get(), orderForDetails: order));
+ return BuyWebViewPage(buyViewModel: buyViewModel,
+ ordersStore: getIt.get(), url: url);
+ });
+
+ getIt.registerFactoryParam(
+ (order, _) {
+ final wallet = getIt.get().wallet;
+
+ return OrderDetailsViewModel(
+ wallet: wallet,
+ orderForDetails: order);
+ });
getIt.registerFactoryParam((Order order, _) =>
OrderDetailsPage(getIt.get(param1: order)));
diff --git a/lib/entities/biometric_auth.dart b/lib/entities/biometric_auth.dart
index ab5d62445..10a05ebff 100644
--- a/lib/entities/biometric_auth.dart
+++ b/lib/entities/biometric_auth.dart
@@ -7,7 +7,7 @@ class BiometricAuth {
Future isAuthenticated() async {
try {
- return await _localAuth.authenticateWithBiometrics(
+ return await _localAuth.authenticate(
localizedReason: S.current.biometric_auth_reason,
useErrorDialogs: true,
stickyAuth: false);
diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart
index 6d55748cc..82db7d5d9 100644
--- a/lib/entities/preferences_key.dart
+++ b/lib/entities/preferences_key.dart
@@ -19,4 +19,5 @@ class PreferencesKey {
'current_default_settings_migration_version';
static const moneroTransactionPriority = 'current_fee_priority_monero';
static const bitcoinTransactionPriority = 'current_fee_priority_bitcoin';
+ static const shouldShowReceiveWarning = 'should_show_receive_warning';
}
diff --git a/lib/entities/push_notifications_service.dart b/lib/entities/push_notifications_service.dart
new file mode 100644
index 000000000..bf5226af3
--- /dev/null
+++ b/lib/entities/push_notifications_service.dart
@@ -0,0 +1,49 @@
+import 'dart:io';
+import 'package:cake_wallet/main.dart';
+import 'package:cake_wallet/utils/show_bar.dart';
+import 'package:firebase_messaging/firebase_messaging.dart';
+
+class PushNotificationsService {
+ PushNotificationsService._();
+
+ factory PushNotificationsService() => _instance;
+
+ static final PushNotificationsService _instance = PushNotificationsService._();
+ static Future _onBackgroundMessage(Map message) async {}
+ static Future _showNotification(Map message) async {
+ Map alert = {};
+ String msg = '';
+ String title = '';
+
+ if (Platform.isIOS) {
+ alert = message['aps']['alert'] as Map ?? {};
+ msg = alert['body'] as String ?? '';
+ title = alert['title'] as String ?? '';
+ }
+
+ if (Platform.isAndroid) {
+ msg = message['notification']['body'] as String ?? '';
+ title = message['notification']['title'] as String ?? '';
+ }
+
+ await showBar(navigatorKey.currentContext, msg, titleText: title, duration: null);
+ }
+
+ final _firebaseMessaging = FirebaseMessaging();
+ bool _initialized = false;
+
+ Future init() async {
+ if (_initialized) {
+ return;
+ }
+
+ _firebaseMessaging.requestNotificationPermissions();
+ _firebaseMessaging.configure(
+ onMessage: (message) async => _showNotification(message),
+ onLaunch: (message) async => _showNotification(message),
+ onResume: (message) async => _showNotification(message),
+ onBackgroundMessage: _onBackgroundMessage);
+
+ _initialized = true;
+ }
+}
\ No newline at end of file
diff --git a/lib/entities/wallet_type.dart b/lib/entities/wallet_type.dart
index d59f336c0..b8a36e709 100644
--- a/lib/entities/wallet_type.dart
+++ b/lib/entities/wallet_type.dart
@@ -71,7 +71,7 @@ String walletTypeToDisplayName(WalletType type) {
case WalletType.bitcoin:
return 'Bitcoin (Electrum)';
case WalletType.litecoin:
- return 'Litecoin';
+ return 'Litecoin (Electrum)';
default:
return '';
}
diff --git a/lib/entities/wyre_exception.dart b/lib/entities/wyre_exception.dart
deleted file mode 100644
index 85d9f5b60..000000000
--- a/lib/entities/wyre_exception.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-class WyreException implements Exception {
- WyreException(this.description);
-
- String description;
-
- @override
- String toString() => description;
-}
\ No newline at end of file
diff --git a/lib/entities/wyre_service.dart b/lib/entities/wyre_service.dart
deleted file mode 100644
index 63346f0e4..000000000
--- a/lib/entities/wyre_service.dart
+++ /dev/null
@@ -1,113 +0,0 @@
-import 'dart:convert';
-import 'package:cake_wallet/entities/wyre_exception.dart';
-import 'package:cake_wallet/exchange/trade_state.dart';
-import 'package:cake_wallet/store/app_store.dart';
-import 'package:flutter/foundation.dart';
-import 'package:http/http.dart';
-import 'package:cake_wallet/.secrets.g.dart' as secrets;
-import 'package:cake_wallet/entities/order.dart';
-import 'package:cake_wallet/entities/wallet_type.dart';
-
-class WyreService {
- WyreService({@required this.appStore, this.isTestEnvironment = false}) {
- baseApiUrl = isTestEnvironment ? _baseTestApiUrl : _baseProductApiUrl;
- trackUrl = isTestEnvironment ? _trackTestUrl : _trackProductUrl;
- }
-
- static const _baseTestApiUrl = 'https://api.testwyre.com';
- static const _baseProductApiUrl = 'https://api.sendwyre.com';
- static const _trackTestUrl = 'https://dash.testwyre.com/track/';
- static const _trackProductUrl = 'https://dash.sendwyre.com/track/';
- static const _ordersSuffix = '/v3/orders';
- static const _reserveSuffix = '/reserve';
- static const _timeStampSuffix = '?timestamp=';
- static const _transferSuffix = '/v2/transfer/';
- static const _trackSuffix = '/track';
-
- final bool isTestEnvironment;
- final AppStore appStore;
-
- WalletType get walletType => appStore.wallet.type;
- String get walletAddress => appStore.wallet.address;
- String get walletId => appStore.wallet.id;
-
- String baseApiUrl;
- String trackUrl;
-
- Future getWyreUrl() async {
- final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
- final url = baseApiUrl +
- _ordersSuffix +
- _reserveSuffix +
- _timeStampSuffix +
- timestamp;
- final secretKey = secrets.wyreSecretKey;
- final accountId = secrets.wyreAccountId;
- final body = {
- 'destCurrency': walletTypeToCryptoCurrency(walletType).title,
- 'dest':
- walletTypeToString(walletType).toLowerCase() + ':' + walletAddress,
- 'referrerAccountId': accountId,
- 'lockFields': ['destCurrency', 'dest']
- };
-
- final response = await post(url,
- headers: {
- 'Authorization': 'Bearer $secretKey',
- 'Content-Type': 'application/json',
- 'cache-control': 'no-cache'
- },
- body: json.encode(body));
-
- if (response.statusCode != 200) {
- throw WyreException('Url $url is not found!');
- }
-
- final responseJSON = json.decode(response.body) as Map;
- final urlFromResponse = responseJSON['url'] as String;
- return urlFromResponse;
- }
-
- Future findOrderById(String id) async {
- final orderUrl = baseApiUrl + _ordersSuffix + '/$id';
- final orderResponse = await get(orderUrl);
-
- if (orderResponse.statusCode != 200) {
- throw WyreException('Order $id is not found!');
- }
-
- final orderResponseJSON =
- json.decode(orderResponse.body) as Map;
- final transferId = orderResponseJSON['transferId'] as String;
- final from = orderResponseJSON['sourceCurrency'] as String;
- final to = orderResponseJSON['destCurrency'] as String;
- final status = orderResponseJSON['status'] as String;
- final state = TradeState.deserialize(raw: status.toLowerCase());
- final createdAtRaw = orderResponseJSON['createdAt'] as int;
- final createdAt =
- DateTime.fromMillisecondsSinceEpoch(createdAtRaw).toLocal();
-
- final transferUrl =
- baseApiUrl + _transferSuffix + transferId + _trackSuffix;
- final transferResponse = await get(transferUrl);
-
- if (transferResponse.statusCode != 200) {
- throw WyreException('Transfer $transferId is not found!');
- }
-
- final transferResponseJSON =
- json.decode(transferResponse.body) as Map;
- final amount = transferResponseJSON['destAmount'] as double;
-
- return Order(
- id: id,
- transferId: transferId,
- from: from,
- to: to,
- state: state,
- createdAt: createdAt,
- amount: amount.toString(),
- receiveAddress: walletAddress,
- walletId: walletId);
- }
-}
diff --git a/lib/exchange/trade_state.dart b/lib/exchange/trade_state.dart
index 7095626b9..9f08b4df2 100644
--- a/lib/exchange/trade_state.dart
+++ b/lib/exchange/trade_state.dart
@@ -27,6 +27,12 @@ class TradeState extends EnumerableItem with Serializable {
static const finished = TradeState(raw: 'finished', title: 'Finished');
static const waiting = TradeState(raw: 'waiting', title: 'Waiting');
static const processing = TradeState(raw: 'processing', title: 'Processing');
+ static const waitingPayment =
+ TradeState(raw: 'waitingPayment', title: 'Waiting payment');
+ static const waitingAuthorization =
+ TradeState(raw: 'waitingAuthorization', title: 'Waiting authorization');
+ static const failed = TradeState(raw: 'failed', title: 'Failed');
+ static const completed = TradeState(raw: 'completed', title: 'Completed');
static TradeState deserialize({String raw}) {
switch (raw) {
@@ -62,6 +68,14 @@ class TradeState extends EnumerableItem with Serializable {
return waiting;
case 'processing':
return processing;
+ case 'waitingPayment':
+ return waitingPayment;
+ case 'waitingAuthorization':
+ return waitingAuthorization;
+ case 'failed':
+ return failed;
+ case 'completed':
+ return completed;
default:
return null;
}
diff --git a/lib/main.dart b/lib/main.dart
index 1a1724e3e..b24943f3d 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,5 +1,5 @@
import 'package:cake_wallet/entities/language_service.dart';
-import 'package:cake_wallet/entities/order.dart';
+import 'package:cake_wallet/buy/order.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hive/hive.dart';
diff --git a/lib/monero/monero_transaction_info.dart b/lib/monero/monero_transaction_info.dart
index 6f099fbfa..0a700835a 100644
--- a/lib/monero/monero_transaction_info.dart
+++ b/lib/monero/monero_transaction_info.dart
@@ -8,7 +8,7 @@ import 'package:cw_monero/transaction_history.dart';
class MoneroTransactionInfo extends TransactionInfo {
MoneroTransactionInfo(this.id, this.height, this.direction, this.date,
- this.isPending, this.amount, this.accountIndex, this.fee);
+ this.isPending, this.amount, this.accountIndex, this.addressIndex, this.fee);
MoneroTransactionInfo.fromMap(Map map)
: id = (map['hash'] ?? '') as String,
@@ -21,6 +21,7 @@ class MoneroTransactionInfo extends TransactionInfo {
isPending = parseBoolFromString(map['isPending'] as String),
amount = map['amount'] as int,
accountIndex = int.parse(map['accountIndex'] as String),
+ addressIndex = map['addressIndex'] as int,
key = getTxKey((map['hash'] ?? '') as String),
fee = map['fee'] as int ?? 0;
@@ -33,6 +34,7 @@ class MoneroTransactionInfo extends TransactionInfo {
isPending = row.isPending != 0,
amount = row.getAmount(),
accountIndex = row.subaddrAccount,
+ addressIndex = row.subaddrIndex,
key = getTxKey(row.getHash()),
fee = row.fee;
@@ -44,6 +46,7 @@ class MoneroTransactionInfo extends TransactionInfo {
final bool isPending;
final int amount;
final int fee;
+ final int addressIndex;
String recipientAddress;
String key;
diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart
index 92742273c..e902f1080 100644
--- a/lib/monero/monero_wallet.dart
+++ b/lib/monero/monero_wallet.dart
@@ -271,6 +271,11 @@ abstract class MoneroWalletBase extends WalletBase
+ monero_wallet.getAddress(
+ accountIndex: accountIndex,
+ addressIndex: addressIndex);
+
@override
Future