Merge branch 'main' into CAKE-272-implement-localization-generation-script

This commit is contained in:
OleksandrSobol 2021-03-24 13:34:14 +02:00
commit 8603e3f2bf
52 changed files with 1506 additions and 94 deletions

2
.gitignore vendored
View file

@ -87,6 +87,8 @@ cw_monero/cw_monero/android/.cxx/
android/key.properties
**/tool/.secrets-prod.json
**/tool/.secrets-test.json
**/tool/.secrets-config.json
**/lib/.secrets.g.dart
vendor/

View file

@ -1,5 +1,8 @@
# Cake Wallet
# CakeWallet for Android and iOS
The project description, motivation, build scripts, instructions, tests will be added soon (February 2021);
## Open Source Monero and Bitcoin Wallet
Copyright (c) 2021 Cake Technologies LLC.
More instructions to follow
For instructions on how to build for Android: please view file `howto-build-android.md`

View file

@ -7,9 +7,9 @@ import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterFragmentActivity {
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
}
}
}

BIN
assets/images/wyre-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -17,7 +17,7 @@ set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/../ios/External/android)
add_library(sodium STATIC IMPORTED)
set_target_properties(sodium PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/libsodium/lib/${ANDROID_ABI}/libsodium.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libsodium.a)
############
# OpenSSL
@ -25,11 +25,11 @@ set_target_properties(sodium PROPERTIES IMPORTED_LOCATION
add_library(crypto STATIC IMPORTED)
set_target_properties(crypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libcrypto.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libcrypto.a)
add_library(ssl STATIC IMPORTED)
set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libssl.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libssl.a)
############
# Boost
@ -37,39 +37,39 @@ set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
add_library(boost_chrono STATIC IMPORTED)
set_target_properties(boost_chrono PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_chrono.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_chrono.a)
add_library(boost_date_time STATIC IMPORTED)
set_target_properties(boost_date_time PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_date_time.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_date_time.a)
add_library(boost_filesystem STATIC IMPORTED)
set_target_properties(boost_filesystem PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_filesystem.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_filesystem.a)
add_library(boost_program_options STATIC IMPORTED)
set_target_properties(boost_program_options PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_program_options.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_program_options.a)
add_library(boost_regex STATIC IMPORTED)
set_target_properties(boost_regex PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_regex.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_regex.a)
add_library(boost_serialization STATIC IMPORTED)
set_target_properties(boost_serialization PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_serialization.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_serialization.a)
add_library(boost_system STATIC IMPORTED)
set_target_properties(boost_system PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_system.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_system.a)
add_library(boost_thread STATIC IMPORTED)
set_target_properties(boost_thread PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_thread.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_thread.a)
add_library(boost_wserialization STATIC IMPORTED)
set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_wserialization.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_wserialization.a)
#############
# Monero
@ -77,101 +77,111 @@ set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
add_library(wallet_api STATIC IMPORTED)
set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet_api.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libwallet_api.a)
add_library(wallet STATIC IMPORTED)
set_target_properties(wallet PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libwallet.a)
add_library(cryptonote_core STATIC IMPORTED)
set_target_properties(cryptonote_core PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_core.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libcryptonote_core.a)
add_library(cryptonote_basic STATIC IMPORTED)
set_target_properties(cryptonote_basic PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_basic.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libcryptonote_basic.a)
add_library(mnemonics STATIC IMPORTED)
set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmnemonics.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libmnemonics.a)
add_library(common STATIC IMPORTED)
set_target_properties(common PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcommon.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libcommon.a)
add_library(cncrypto STATIC IMPORTED)
set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcncrypto.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libcncrypto.a)
add_library(ringct STATIC IMPORTED)
set_target_properties(ringct PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libringct.a)
add_library(ringct_basic STATIC IMPORTED)
set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct_basic.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libringct_basic.a)
add_library(blockchain_db STATIC IMPORTED)
set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblockchain_db.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libblockchain_db.a)
add_library(lmdb STATIC IMPORTED)
set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/liblmdb.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/liblmdb.a)
add_library(easylogging STATIC IMPORTED)
set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libeasylogging.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libeasylogging.a)
add_library(unbound STATIC IMPORTED)
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libunbound.a)
add_library(epee STATIC IMPORTED)
set_target_properties(epee PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libepee.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libepee.a)
add_library(blocks STATIC IMPORTED)
set_target_properties(blocks PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblocks.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libblocks.a)
add_library(checkpoints STATIC IMPORTED)
set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcheckpoints.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libcheckpoints.a)
add_library(device STATIC IMPORTED)
set_target_properties(device PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libdevice.a)
add_library(device_trezor STATIC IMPORTED)
set_target_properties(device_trezor PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice_trezor.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libdevice_trezor.a)
add_library(multisig STATIC IMPORTED)
set_target_properties(multisig PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmultisig.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libmultisig.a)
add_library(version STATIC IMPORTED)
set_target_properties(version PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libversion.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libversion.a)
add_library(net STATIC IMPORTED)
set_target_properties(net PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libnet.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libnet.a)
add_library(hardforks STATIC IMPORTED)
set_target_properties(hardforks PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libhardforks.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libhardforks.a)
add_library(randomx STATIC IMPORTED)
set_target_properties(randomx PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/librandomx.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/librandomx.a)
add_library(rpc_base STATIC IMPORTED)
set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/librpc_base.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/librpc_base.a)
include_directories( ${EXTERNAL_LIBS_DIR}/monero/include )
add_library(wallet-crypto STATIC IMPORTED)
set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/monero/libwallet-crypto.a)
set(WALLET_CRYPTO "")
if(${ANDROID_ABI} STREQUAL "x86_64")
set(WALLET_CRYPTO "wallet-crypto")
endif()
include_directories( ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/include )
target_link_libraries( cw_monero
@ -199,6 +209,7 @@ target_link_libraries( cw_monero
randomx
hardforks
rpc_base
${WALLET_CRYPTO}
boost_chrono
boost_date_time

View file

@ -6,7 +6,7 @@
#include <unistd.h>
#include "thread"
#include "CwWalletListener.h"
#include "../External/android/monero/include/wallet2_api.h"
#include "../External/android/x86/include/wallet2_api.h"
using namespace std::chrono_literals;

136
howto-build-android.md Normal file
View file

@ -0,0 +1,136 @@
# Building CakeWallet for Android
## Requirements and Setup
The following are the system requirements to build CakeWallet for your Android device.
```
Ubuntu >= 16.04
Android SDK 28
Android NDK 17c
Flutter 1.22.6
```
## Building CakeWallet on Android
These steps will help you configure and execute a build of CakeWallet from its source code.
### 1. Installing Package Dependencies
CakeWallet cannot be built without the following packages installed on your build system.
- unzip
- automake
- build-essential
- file
- pkg-config
- git
- python
- libtool
- libtinfo5
- cmake
- openjdk-8-jre-headless
You may easily install them on your build sytem with the following command:
`$ sudo apt-get install -y unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless`
### 2. Installing Android Studio and Android toolchain
You may download and install the latest version of Android Studio [here](https://developer.android.com/studio#downloads). After installing, start Android Studio, and go through the "Setup Wizard." This installs the latest Android SDK, Android SDK Command-line Tools, and Android SDK Build-Tools, which are required by CakeWallet. **Be sure you are installing SDK version 28 or later when stepping through the wizard**
### 3. Installing Flutter
CakeWallet requires **EXACTLY** Flutter version `1.22.6` to build properly.
To install this version of Flutter on your Ubuntu system, please use [these instructions](https://flutter.dev/docs/get-started/install/linux#install-flutter-manually).
### 4. Verify Installations
Verify that the Android toolchain, Flutter, and Android Studio have been correctly installed on your system with the following command:
`$ flutter doctor`
The output of this command will appear like this, indicating successful installations. If there are problems with your installation, they **must** be corrected before proceeding.
```
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 1.22.6, on Linux, locale en_US.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 28)
[✓] Android Studio (version 4.0)
```
### 5. Generate a secure keystore for Android
`$ keytool -genkey -v -keystore $HOME/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key`
You will be prompted to create two passwords. First you will be prompted for the "store password", followed by a "key password" towards the end of the creation process. **TAKE NOTE OF THESE PASSWORDS!** You will need them in later steps.
### 6. Acquiring the CakeWallet Source Code
Create the directory that will be use to store the CakeWallet source...
```
$ sudo mkdir -p /opt/android
$ sudo chown $USER /opt/android
$ cd /opt/android
```
..and download the source code into that directory.
`$ git clone https://github.com/cake-tech/cake_wallet.git --branch deploy`
Proceed into the source code before proceeding with the next steps:
`$ cd cake_wallet/scripts/android/`
### 7. Installing Android NDK
`$ ./install_ndk.sh`
### 8. Execute Build & Setup Commands for CakeWallet
Build the Monero libraries and their dependencies:
`$ ./build_all.sh`
Now the dependencies need to be copied into the CakeWallet project with this command:
`$ ./copy_monero_deps.sh`
It is now time to change back to the base directory of the CakeWallet source code:
`$ cd ../../`
Install Flutter package dependencies with this command:
`$ flutter pub get`
Your CakeWallet binary will be built with cryptographic salts, which are used for secure encryption of your data. You may generate these secret salts with the following command:
`$ flutter packages pub run tool/generate_new_secrets.dart`
Next, we must generate key properties based on the secure keystore you generated for Android (in step 5). **MODIFY THE FOLLOWING COMMAND** with the "store password" and "key password" you assigned when creating your keystore (in step 5).
`$ flutter packages pub run tool/generate_android_key_properties.dart keyAlias=key storeFile=$HOME/key.jks storePassword=<store password> keyPassword=<key password>`
**REMINDER:** The *above* command will **not** succeed unless you replaced the `storePassword` and `keyPassword` variables with the correct passwords for your keystore.
Lastly, we will generate mobx models for the project.
`$ flutter packages pub run build_runner build --delete-conflicting-outputs`
### 9. Build!
`$ flutter build apk —release`
Copyright (c) 2021 Cake Technologies LLC.

View file

@ -86,6 +86,8 @@ PODS:
- SwiftyGif (5.3.0)
- url_launcher (0.0.1):
- Flutter
- webview_flutter (0.0.1):
- Flutter
DEPENDENCIES:
- barcode_scan (from `.symlinks/plugins/barcode_scan/ios`)
@ -104,6 +106,7 @@ DEPENDENCIES:
- share (from `.symlinks/plugins/share/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
- webview_flutter (from `.symlinks/plugins/webview_flutter/ios`)
SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
@ -147,6 +150,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/shared_preferences/ios"
url_launcher:
:path: ".symlinks/plugins/url_launcher/ios"
webview_flutter:
:path: ".symlinks/plugins/webview_flutter/ios"
SPEC CHECKSUMS:
barcode_scan: a5c27959edfafaa0c771905bad0b29d6d39e4479
@ -172,6 +177,7 @@ SPEC CHECKSUMS:
SwiftProtobuf: 4ef85479c18ca85b5482b343df9c319c62bda699
SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
webview_flutter: d2b4d6c66968ad042ad94cbb791f5b72b4678a96
PODFILE CHECKSUM: 5b5f101b119a1b6eb857c967d462832a9062dec4

View file

@ -357,7 +357,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 26;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -374,7 +374,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 4.1.2;
MARKETING_VERSION = 4.1.3;
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -498,7 +498,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 26;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -515,7 +515,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 4.1.2;
MARKETING_VERSION = 4.1.3;
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -533,7 +533,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 26;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -550,7 +550,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 4.1.2;
MARKETING_VERSION = 4.1.3;
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";

View file

@ -4,8 +4,10 @@ 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/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';
@ -22,6 +24,7 @@ import 'package:cake_wallet/src/screens/faq/faq_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/nodes/nodes_list_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
@ -39,6 +42,8 @@ import 'package:cake_wallet/src/screens/transaction_details/transaction_details_
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';
import 'package:cake_wallet/store/settings_store.dart';
@ -63,6 +68,7 @@ import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/order_details_view_model.dart';
import 'package:cake_wallet/view_model/rescan_view_model.dart';
import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart';
import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart';
@ -83,6 +89,7 @@ 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';
@ -104,6 +111,7 @@ import 'package:cake_wallet/store/templates/send_template_store.dart';
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;
final getIt = GetIt.instance;
@ -115,6 +123,7 @@ Box<Trade> _tradesSource;
Box<Template> _templates;
Box<ExchangeTemplate> _exchangeTemplates;
Box<TransactionDescription> _transactionDescriptionBox;
Box<Order> _ordersSource;
Future setup(
{Box<WalletInfo> walletInfoSource,
@ -123,7 +132,8 @@ Future setup(
Box<Trade> tradesSource,
Box<Template> templates,
Box<ExchangeTemplate> exchangeTemplates,
Box<TransactionDescription> transactionDescriptionBox}) async {
Box<TransactionDescription> transactionDescriptionBox,
Box<Order> ordersSource}) async {
_walletInfoSource = walletInfoSource;
_nodeSource = nodeSource;
_contactSource = contactSource;
@ -131,13 +141,18 @@ Future setup(
_templates = templates;
_exchangeTemplates = exchangeTemplates;
_transactionDescriptionBox = transactionDescriptionBox;
_ordersSource = ordersSource;
if (!_isSetupFinished) {
getIt.registerSingletonAsync<SharedPreferences>(
() => SharedPreferences.getInstance());
() => SharedPreferences.getInstance());
}
final settingsStore = await SettingsStoreBase.load(nodeSource: _nodeSource);
final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) &&
(secrets.wyreApiKey?.isNotEmpty ?? false) &&
(secrets.wyreAccountId?.isNotEmpty ?? false);
final settingsStore = await SettingsStoreBase.load(
nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled);
if (_isSetupFinished) {
return;
@ -157,6 +172,8 @@ Future setup(
nodeListStore: getIt.get<NodeListStore>()));
getIt.registerSingleton<TradesStore>(TradesStore(
tradesSource: _tradesSource, settingsStore: getIt.get<SettingsStore>()));
getIt.registerSingleton<OrdersStore>(OrdersStore(
ordersSource: _ordersSource, settingsStore: getIt.get<SettingsStore>()));
getIt.registerSingleton<TradeFilterStore>(TradeFilterStore());
getIt.registerSingleton<TransactionFilterStore>(TransactionFilterStore());
getIt.registerSingleton<FiatConversionStore>(FiatConversionStore());
@ -219,7 +236,11 @@ Future setup(
appStore: getIt.get<AppStore>(),
tradesStore: getIt.get<TradesStore>(),
tradeFilterStore: getIt.get<TradeFilterStore>(),
transactionFilterStore: getIt.get<TransactionFilterStore>()));
transactionFilterStore: getIt.get<TransactionFilterStore>(),
settingsStore: settingsStore,
ordersSource: _ordersSource,
ordersStore: getIt.get<OrdersStore>(),
wyreViewModel: getIt.get<WyreViewModel>()));
getIt.registerFactory<AuthService>(() => AuthService(
secureStorage: getIt.get<FlutterSecureStorage>(),
@ -511,6 +532,30 @@ Future setup(
getIt.registerFactoryParam<TradeDetailsPage, Trade, void>((Trade trade, _) =>
TradeDetailsPage(getIt.get<TradeDetailsViewModel>(param1: trade)));
getIt.registerFactory(() {
final wallet = getIt.get<AppStore>().wallet;
return WyreService(walletType: wallet.type, walletAddress: wallet.address);
});
getIt.registerFactory(() {
final wallet = getIt.get<AppStore>().wallet;
return WyreViewModel(ordersSource, getIt.get<OrdersStore>(),
walletId: wallet.id, address: wallet.address, type: wallet.type,
wyreService: getIt.get<WyreService>());
});
getIt.registerFactoryParam<WyrePage, String, void>((String url, _) =>
WyrePage(getIt.get<WyreViewModel>(),
ordersStore: getIt.get<OrdersStore>(), url: url));
getIt.registerFactoryParam<OrderDetailsViewModel, Order, void>(
(order, _) => OrderDetailsViewModel(
wyreViewModel: getIt.get<WyreViewModel>(),
orderForDetails: order));
getIt.registerFactoryParam<OrderDetailsPage, Order, void>((Order order, _) =>
OrderDetailsPage(getIt.get<OrderDetailsViewModel>(param1: order)));
getIt.registerFactory(() => SupportViewModel());
getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>()));

55
lib/entities/order.dart Normal file
View file

@ -0,0 +1,55 @@
import 'package:hive/hive.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/entities/format_amount.dart';
part 'order.g.dart';
@HiveType(typeId: Order.typeId)
class Order extends HiveObject {
Order(
{this.id,
this.transferId,
this.from,
this.to,
TradeState state,
this.createdAt,
this.amount,
this.receiveAddress,
this.walletId})
: stateRaw = state?.raw;
static const typeId = 8;
static const boxName = 'Orders';
static const boxKey = 'ordersBoxKey';
@HiveField(0)
String id;
@HiveField(1)
String transferId;
@HiveField(2)
String from;
@HiveField(3)
String to;
@HiveField(4)
String stateRaw;
TradeState get state => TradeState.deserialize(raw: stateRaw);
@HiveField(5)
DateTime createdAt;
@HiveField(6)
String amount;
@HiveField(7)
String receiveAddress;
@HiveField(8)
String walletId;
String amountFormatted() => formatAmount(amount);
}

View file

@ -0,0 +1,8 @@
class WyreException implements Exception {
WyreException(this.description);
String description;
@override
String toString() => description;
}

View file

@ -0,0 +1,111 @@
import 'dart:convert';
import 'package:cake_wallet/entities/wyre_exception.dart';
import 'package:cake_wallet/exchange/trade_state.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.walletType,
@required this.walletAddress,
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 WalletType walletType;
final String walletAddress;
String baseApiUrl;
String trackUrl;
Future<String> 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<String, dynamic>;
final urlFromResponse = responseJSON['url'] as String;
return urlFromResponse;
}
Future<Order> 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<String, dynamic>;
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<String, dynamic>;
final amount = transferResponseJSON['destAmount'] as double;
return Order(
id: id,
transferId: transferId,
from: from,
to: to,
state: state,
createdAt: createdAt,
amount: amount.toString()
);
}
}

View file

@ -25,7 +25,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
.toList());
static const apiUri = 'https://changenow.io/api/v1';
static const apiKey = secrets.change_now_api_key;
static const apiKey = secrets.changeNowApiKey;
static const _exchangeAmountUriSufix = '/exchange-amount/';
static const _transactionsUriSufix = '/transactions/';
static const _minAmountUriSufix = '/min-amount/';

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/entities/order.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hive/hive.dart';
@ -69,11 +70,17 @@ Future<void> main() async {
Hive.registerAdapter(ExchangeTemplateAdapter());
}
if (!Hive.isAdapterRegistered(Order.typeId)) {
Hive.registerAdapter(OrderAdapter());
}
final secureStorage = FlutterSecureStorage();
final transactionDescriptionsBoxKey = await getEncryptionKey(
secureStorage: secureStorage, forKey: TransactionDescription.boxKey);
final tradesBoxKey = await getEncryptionKey(
secureStorage: secureStorage, forKey: Trade.boxKey);
final ordersBoxKey = await getEncryptionKey(
secureStorage: secureStorage, forKey: Order.boxKey);
final contacts = await Hive.openBox<Contact>(Contact.boxName);
final nodes = await Hive.openBox<Node>(Node.boxName);
final transactionDescriptions = await Hive.openBox<TransactionDescription>(
@ -81,6 +88,8 @@ Future<void> main() async {
encryptionKey: transactionDescriptionsBoxKey);
final trades =
await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
final orders =
await Hive.openBox<Order>(Order.boxName, encryptionKey: ordersBoxKey);
final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
final templates = await Hive.openBox<Template>(Template.boxName);
final exchangeTemplates =
@ -91,6 +100,7 @@ Future<void> main() async {
walletInfoSource: walletInfoSource,
contactSource: contacts,
tradesSource: trades,
ordersSource: orders,
// fiatConvertationService: fiatConvertationService,
templates: templates,
exchangeTemplates: exchangeTemplates,
@ -118,6 +128,7 @@ Future<void> initialSetup(
@required Box<WalletInfo> walletInfoSource,
@required Box<Contact> contactSource,
@required Box<Trade> tradesSource,
@required Box<Order> ordersSource,
// @required FiatConvertationService fiatConvertationService,
@required Box<Template> templates,
@required Box<ExchangeTemplate> exchangeTemplates,
@ -139,7 +150,8 @@ Future<void> initialSetup(
tradesSource: tradesSource,
templates: templates,
exchangeTemplates: exchangeTemplates,
transactionDescriptionBox: transactionDescriptions);
transactionDescriptionBox: transactionDescriptions,
ordersSource: ordersSource);
await bootstrap(navigatorKey);
monero_wallet.onStartup();
}

View file

@ -1,12 +1,15 @@
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/order.dart';
import 'package:cake_wallet/entities/transaction_description.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/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/wyre/wyre_page.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:flutter/cupertino.dart';
@ -285,6 +288,16 @@ Route<dynamic> createRoute(RouteSettings settings) {
builder: (_) =>
getIt.get<TradeDetailsPage>(param1: settings.arguments as Trade));
case Routes.orderDetails:
return MaterialPageRoute<void>(
builder: (_) =>
getIt.get<OrderDetailsPage>(param1: settings.arguments as Order));
case Routes.wyre:
return MaterialPageRoute<void>(
builder: (_) =>
getIt.get<WyrePage>(param1: settings.arguments as String));
case Routes.restoreWalletFromSeedDetails:
final args = settings.arguments as List;
final walletRestorationFromSeedVM =

View file

@ -52,4 +52,6 @@ class Routes {
static const editBackupPassword = '/edit_backup_passowrd';
static const restoreFromBackup = '/restore_from_backup';
static const support = '/support';
static const orderDetails = '/order_details';
static const wyre = '/wyre';
}

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/themes/theme_base.dart';
@ -12,7 +13,9 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/address_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
class DashboardPage extends BasePage {
DashboardPage({
@ -81,7 +84,7 @@ class DashboardPage extends BasePage {
final exchangeImage = Image.asset('assets/images/transfer.png',
height: 24.27, width: 22.25,
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
final receiveImage = Image.asset('assets/images/download.png',
final buyImage = Image.asset('assets/images/coins.png',
height: 22.24, width: 24,
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
_setEffects();
@ -111,7 +114,7 @@ class DashboardPage extends BasePage {
)),
Container(
padding: EdgeInsets.only(left: 45, right: 45, bottom: 24),
child: Row(
child: Observer(builder: (_) => Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
ActionButton(
@ -122,8 +125,41 @@ class DashboardPage extends BasePage {
image: exchangeImage,
title: S.of(context).exchange,
route: Routes.exchange),
if (walletViewModel.type == WalletType.bitcoin) Observer(
builder: (_) => Stack(
clipBehavior: Clip.none,
alignment: Alignment.topCenter,
children: [
if (walletViewModel.isRunningWebView) Positioned(
top: -5,
child: SpinKitRing(
color: Theme.of(context).buttonColor,
lineWidth: 3,
size: 70.0,
),
),
ActionButton(
image: buyImage,
title: S.of(context).buy,
onClick: walletViewModel.isRunningWebView
? null
: () async {
try {
walletViewModel.isRunningWebView = true;
final url =
await walletViewModel.wyreViewModel.wyreUrl;
await Navigator.of(context)
.pushNamed(Routes.wyre, arguments: url);
walletViewModel.isRunningWebView = false;
} catch(e) {
print(e.toString());
walletViewModel.isRunningWebView = false;
}
})
],
)),
],
),
)),
)
],
));

View file

@ -3,14 +3,16 @@ import 'package:flutter/material.dart';
class ActionButton extends StatelessWidget {
ActionButton(
{@required this.image,
@required this.title,
@required this.route,
this.alignment = Alignment.center});
@required this.title,
this.route,
this.onClick,
this.alignment = Alignment.center});
final Image image;
final String title;
final String route;
final Alignment alignment;
final void Function() onClick;
@override
Widget build(BuildContext context) {
@ -23,8 +25,10 @@ class ActionButton extends StatelessWidget {
children: <Widget>[
GestureDetector(
onTap: () {
if (route.isNotEmpty) {
if (route?.isNotEmpty ?? false) {
Navigator.of(context, rootNavigator: true).pushNamed(route);
} else {
onClick?.call();
}
},
child: Container(
@ -48,4 +52,4 @@ class ActionButton extends StatelessWidget {
),
);
}
}
}

View file

@ -0,0 +1,72 @@
import 'package:flutter/material.dart';
class OrderRow extends StatelessWidget {
OrderRow({
@required this.onTap,
this.from,
this.to,
this.createdAtFormattedDate,
this.formattedAmount});
final VoidCallback onTap;
final String from;
final String to;
final String createdAtFormattedDate;
final String formattedAmount;
final wyreImage =
Image.asset('assets/images/wyre-icon.png', width: 36, height: 36);
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
padding: EdgeInsets.fromLTRB(24, 8, 24, 8),
color: Colors.transparent,
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
wyreImage,
SizedBox(width: 12),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('$from$to',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme.
display3.backgroundColor
)),
formattedAmount != null
? Text(formattedAmount + ' ' + to,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme.
display3.backgroundColor
))
: Container()
]),
SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(createdAtFormattedDate,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).textTheme
.overline.backgroundColor))
])
],
)
)
],
),
));
}
}

View file

@ -1,3 +1,5 @@
import 'package:cake_wallet/src/screens/dashboard/widgets/order_row.dart';
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@ -75,6 +77,21 @@ class TransactionsPage extends StatelessWidget {
);
}
if (item is OrderListItem) {
final order = item.order;
return OrderRow(
onTap: () => Navigator.of(context).pushNamed(
Routes.orderDetails,
arguments: order),
from: order.from,
to: order.to,
createdAtFormattedDate:
DateFormat('HH:mm').format(order.createdAt),
formattedAmount: item.orderFormattedAmount,
);
}
return Container(
color: Colors.transparent,
height: 1);

View file

@ -0,0 +1,47 @@
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/view_model/order_details_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
class OrderDetailsPage extends BasePage {
OrderDetailsPage(this.orderDetailsViewModel);
@override
String get title => 'Order Details';
final OrderDetailsViewModel orderDetailsViewModel;
@override
Widget body(BuildContext context) {
return Observer(builder: (_) {
return SectionStandardList(
sectionCount: 1,
itemCounter: (int _) => orderDetailsViewModel.items.length,
itemBuilder: (_, __, index) {
final item = orderDetailsViewModel.items[index];
if (item is TrackTradeListItem) {
return GestureDetector(
onTap: item.onTap,
child: StandartListRow(
title: '${item.title}', value: '${item.value}'));
} else {
return GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: '${item.value}'));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
child: StandartListRow(
title: '${item.title}', value: '${item.value}'));
}
});
});
}
}

View file

@ -0,0 +1,97 @@
import 'dart:async';
import 'dart:io';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/store/dashboard/orders_store.dart';
import 'package:cake_wallet/view_model/wyre_view_model.dart';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WyrePage extends BasePage {
WyrePage(this.wyreViewModel,
{@required this.ordersStore, @required this.url});
final OrdersStore ordersStore;
final String url;
final WyreViewModel wyreViewModel;
@override
String get title => S.current.buy;
@override
Color get backgroundDarkColor => Colors.white;
@override
Color get titleColor => Palette.darkBlueCraiola;
@override
Widget body(BuildContext context) =>
WyrePageBody(wyreViewModel, ordersStore: ordersStore, url: url);
}
class WyrePageBody extends StatefulWidget {
WyrePageBody(this.wyreViewModel, {this.ordersStore, this.url});
final OrdersStore ordersStore;
final String url;
final WyreViewModel wyreViewModel;
@override
WyrePageBodyState createState() => WyrePageBodyState();
}
class WyrePageBodyState extends State<WyrePageBody> {
String orderId;
WebViewController _webViewController;
GlobalKey _webViewkey;
Timer _timer;
bool _isSaving;
@override
void initState() {
super.initState();
_webViewkey = GlobalKey();
_isSaving = false;
widget.ordersStore.orderId = '';
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
_timer?.cancel();
_timer = Timer.periodic(Duration(seconds: 1), (timer) async {
try {
if (_webViewController == null || _isSaving) {
return;
}
final url = await _webViewController.currentUrl();
if (url.contains('completed')) {
final urlParts = url.split('/');
orderId = urlParts.last;
widget.ordersStore.orderId = orderId;
if (orderId.isNotEmpty) {
_isSaving = true;
await widget.wyreViewModel.saveOrder(orderId);
timer.cancel();
}
}
} catch (e) {
_isSaving = false;
print(e);
}
});
}
@override
Widget build(BuildContext context) {
return WebView(
key: _webViewkey,
initialUrl: widget.url,
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController controller) =>
setState(() => _webViewController = controller));
}
}

View file

@ -0,0 +1,45 @@
import 'dart:async';
import 'package:cake_wallet/entities/order.dart';
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/store/settings_store.dart';
part 'orders_store.g.dart';
class OrdersStore = OrdersStoreBase with _$OrdersStore;
abstract class OrdersStoreBase with Store {
OrdersStoreBase({this.ordersSource, this.settingsStore}) {
orders = <OrderListItem>[];
orderId = '';
_onOrdersChanged =
ordersSource.watch().listen((_) async => await updateOrderList());
updateOrderList();
}
Box<Order> ordersSource;
StreamSubscription<BoxEvent> _onOrdersChanged;
SettingsStore settingsStore;
@observable
List<OrderListItem> orders;
@observable
Order order;
@observable
String orderId;
@action
void setOrder(Order order) => this.order = order;
@action
Future updateOrderList() async => orders =
ordersSource.values.map((order) => OrderListItem(
order: order,
displayMode: settingsStore.balanceDisplayMode)).toList();
}

View file

@ -38,6 +38,7 @@ abstract class SettingsStoreBase with Store {
@required Map<WalletType, Node> nodes,
@required TransactionPriority initialBitcoinTransactionPriority,
@required TransactionPriority initialMoneroTransactionPriority,
@required this.isBitcoinBuyEnabled,
this.actionlistDisplayMode}) {
fiatCurrency = initialFiatCurrency;
balanceDisplayMode = initialBalanceDisplayMode;
@ -144,8 +145,11 @@ abstract class SettingsStoreBase with Store {
Node getCurrentNode(WalletType walletType) => nodes[walletType];
bool isBitcoinBuyEnabled;
static Future<SettingsStore> load(
{@required Box<Node> nodeSource,
@required bool isBitcoinBuyEnabled,
FiatCurrency initialFiatCurrency = FiatCurrency.usd,
MoneroTransactionPriority initialMoneroTransactionPriority =
MoneroTransactionPriority.slow,
@ -212,6 +216,7 @@ abstract class SettingsStoreBase with Store {
WalletType.bitcoin: bitcoinElectrumServer
},
appVersion: packageInfo.version,
isBitcoinBuyEnabled: isBitcoinBuyEnabled,
initialFiatCurrency: currentFiatCurrency,
initialBalanceDisplayMode: currentBalanceDisplayMode,
initialSaveRecipientAddress: shouldSaveRecipientAddress,

View file

@ -1,7 +1,12 @@
import 'dart:convert';
import 'dart:io';
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
import 'package:cake_wallet/entities/balance.dart';
import 'package:cake_wallet/entities/order.dart';
import 'package:cake_wallet/entities/transaction_history.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/monero/account.dart';
import 'package:cake_wallet/monero/monero_balance.dart';
import 'package:cake_wallet/monero/monero_transaction_history.dart';
@ -13,13 +18,21 @@ import 'package:cake_wallet/entities/transaction_direction.dart';
import 'package:cake_wallet/entities/transaction_info.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/store/dashboard/orders_store.dart';
import 'package:cake_wallet/utils/mobx.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/action_list_display_mode.dart';
import 'package:cake_wallet/view_model/wyre_view_model.dart';
import 'package:crypto/crypto.dart';
import 'package:flutter/services.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/entities/sync_status.dart';
@ -30,6 +43,8 @@ import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
import 'package:cake_wallet/view_model/dashboard/formatted_item_list.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:convert/convert.dart';
part 'dashboard_view_model.g.dart';
@ -41,7 +56,11 @@ abstract class DashboardViewModelBase with Store {
this.appStore,
this.tradesStore,
this.tradeFilterStore,
this.transactionFilterStore}) {
this.transactionFilterStore,
this.settingsStore,
this.ordersSource,
this.ordersStore,
this.wyreViewModel}) {
filterItems = {
S.current.transactions: [
FilterItem(
@ -76,6 +95,8 @@ abstract class DashboardViewModelBase with Store {
]
};
isRunningWebView = false;
name = appStore.wallet?.name;
wallet ??= appStore.wallet;
type = wallet.type;
@ -141,6 +162,9 @@ abstract class DashboardViewModelBase with Store {
@observable
String subname;
@observable
bool isRunningWebView;
@computed
String get address => wallet.address;
@ -171,6 +195,11 @@ abstract class DashboardViewModelBase with Store {
.where((trade) => trade.trade.walletId == wallet.id)
.toList();
@computed
List<OrderListItem> get orders => ordersStore.orders
.where((item) => item.order.walletId == wallet.id)
.toList();
@computed
double get price => balanceViewModel.price;
@ -180,6 +209,7 @@ abstract class DashboardViewModelBase with Store {
_items.addAll(transactionFilterStore.filtered(transactions: transactions));
_items.addAll(tradeFilterStore.filtered(trades: trades, wallet: wallet));
_items.addAll(orders);
return formattedItemsList(_items);
}
@ -189,18 +219,28 @@ abstract class DashboardViewModelBase with Store {
bool get hasRescan => wallet.type == WalletType.monero;
Box<Order> ordersSource;
BalanceViewModel balanceViewModel;
AppStore appStore;
SettingsStore settingsStore;
TradesStore tradesStore;
OrdersStore ordersStore;
TradeFilterStore tradeFilterStore;
TransactionFilterStore transactionFilterStore;
WyreViewModel wyreViewModel;
Map<String, List<FilterItem>> filterItems;
bool get isBuyEnabled => settingsStore.isBitcoinBuyEnabled;
ReactionDisposer _reaction;
ReactionDisposer _onMoneroAccountChangeReaction;
@ -279,4 +319,6 @@ abstract class DashboardViewModelBase with Store {
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
}
}

View file

@ -0,0 +1,21 @@
import 'package:cake_wallet/entities/order.dart';
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart';
class OrderListItem extends ActionListItem {
OrderListItem({this.order, this.displayMode});
final Order order;
final BalanceDisplayMode displayMode;
String get orderFormattedAmount {
return order.amount != null
? displayMode == BalanceDisplayMode.hiddenBalance
? '---'
: order.amountFormatted()
: order.amount;
}
@override
DateTime get date => order.createdAt;
}

View file

@ -0,0 +1,85 @@
import 'dart:async';
import 'package:cake_wallet/entities/order.dart';
import 'package:cake_wallet/utils/date_formatter.dart';
import 'package:cake_wallet/view_model/wyre_view_model.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
import 'package:url_launcher/url_launcher.dart';
part 'order_details_view_model.g.dart';
class OrderDetailsViewModel = OrderDetailsViewModelBase
with _$OrderDetailsViewModel;
abstract class OrderDetailsViewModelBase with Store {
OrderDetailsViewModelBase({this.wyreViewModel, Order orderForDetails}) {
order = orderForDetails;
items = ObservableList<StandartListItem>();
_updateItems();
_updateOrder();
_timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateOrder());
}
@observable
Order order;
@observable
ObservableList<StandartListItem> items;
WyreViewModel wyreViewModel;
Timer _timer;
@action
Future<void> _updateOrder() async {
try {
final updatedOrder =
await wyreViewModel.wyreService.findOrderById(order.id);
updatedOrder.receiveAddress = order.receiveAddress;
updatedOrder.walletId = order.walletId;
order = updatedOrder;
_updateItems();
} catch (e) {
print(e.toString());
}
}
void _updateItems() {
final dateFormat = DateFormatter.withCurrentLocal();
final buildURL =
wyreViewModel.trackUrl + '${order.transferId}';
items?.clear();
items.addAll([
StandartListItem(
title: 'Transfer ID',
value: order.transferId),
StandartListItem(
title: S.current.trade_details_state,
value: order.state != null
? order.state.toString()
: S.current.trade_details_fetching),
TrackTradeListItem(
title: 'Track',
value: buildURL,
onTap: () {
launch(buildURL);
}),
StandartListItem(
title: S.current.trade_details_created_at,
value: dateFormat.format(order.createdAt).toString()),
StandartListItem(
title: S.current.trade_details_pair,
value: '${order.from}${order.to}')
]);
}
}

View file

@ -188,6 +188,8 @@ abstract class SettingsViewModelBase with Store {
@computed
ThemeBase get theme => _settingsStore.currentTheme;
bool get isBitcoinBuyEnabled => _settingsStore.isBitcoinBuyEnabled;
final Map<String, String> itemHeaders;
List<List<SettingsListItem>> sections;
final SettingsStore _settingsStore;

View file

@ -0,0 +1,42 @@
import 'package:cake_wallet/entities/wyre_service.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:cake_wallet/entities/order.dart';
import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/store/dashboard/orders_store.dart';
import 'package:mobx/mobx.dart';
part 'wyre_view_model.g.dart';
class WyreViewModel = WyreViewModelBase with _$WyreViewModel;
abstract class WyreViewModelBase with Store {
WyreViewModelBase(this.ordersSource, this.ordersStore,
{@required this.walletId, @required this.address, @required this.type,
@required this.wyreService});
Future<String> get wyreUrl => wyreService.getWyreUrl();
String get trackUrl => wyreService.trackUrl;
final Box<Order> ordersSource;
final OrdersStore ordersStore;
final String walletId;
final WalletType type;
final String address;
final WyreService wyreService;
Future<void> saveOrder(String orderId) async {
try {
final order = await wyreService.findOrderById(orderId);
order.receiveAddress = address;
order.walletId = walletId;
await ordersSource.add(order);
ordersStore.setOrder(order);
} catch (e) {
print(e.toString());
}
}
}

View file

@ -426,6 +426,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.7"
flutter_spinkit:
dependency: "direct main"
description:
name: flutter_spinkit
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.2+1"
flutter_test:
dependency: "direct dev"
description: flutter
@ -1029,6 +1036,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
webview_flutter:
dependency: "direct main"
description:
name: webview_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.7"
win32:
dependency: transitive
description:

View file

@ -11,7 +11,7 @@ description: Cake Wallet.
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 4.1.2+41
version: 4.1.3+42
environment:
sdk: ">=2.7.0 <3.0.0"
@ -55,6 +55,8 @@ dependencies:
auto_size_text: ^2.1.0
dotted_border: ^1.0.5
smooth_page_indicator: ^0.2.0
webview_flutter: ^1.0.7
flutter_spinkit: ^4.1.2
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
@ -112,7 +114,7 @@ flutter:
fonts:
- asset: assets/fonts/Lato-Regular.ttf
- asset: assets/fonts/Lato-Medium.ttf
- asset: assets/fonts/Lato-SemiBold.ttf
- asset: assets/fonts/Lato-Semibold.ttf
- asset: assets/fonts/Lato-Bold.ttf

8
scripts/android/build_all.sh Executable file
View file

@ -0,0 +1,8 @@
# /bin/bash
./build_iconv.sh
./build_boost.sh
./build_openssl.sh
./build_sodium.sh
./build_zmq.sh
./build_monero.sh

16
scripts/android/build_boost.sh Executable file
View file

@ -0,0 +1,16 @@
# /bin/bash
WORKDIR=/opt/android
TOOLCHAIN_BASE_DIR=${WORKDIR}/toolchain
ORIGINAL_PATH=$PATH
for arch in "aarch" "aarch64" "i686" "x86_64"
do
PREFIX=$WORKDIR/prefix_${arch}
PATH="${TOOLCHAIN_BASE_DIR}_${arch}/bin:${ORIGINAL_PATH}"
./init_boost.sh $arch
./finish_boost.sh $arch
done

38
scripts/android/build_iconv.sh Executable file
View file

@ -0,0 +1,38 @@
# /bin/bash
export WORKDIR=/opt/android
export ICONV_FILENAME=libiconv-1.15.tar.gz
export ICONV_FILE_PATH=$WORKDIR/$ICONV_FILENAME
export ICONV_SRC_DIR=$WORKDIR/libiconv-1.15
wget http://ftp.gnu.org/pub/gnu/libiconv/$ICONV_FILENAME -O $ICONV_FILE_PATH
ORIGINAL_PATH=$PATH
TOOLCHAIN_BASE_DIR=${WORKDIR}/toolchain
for arch in aarch aarch64 i686 x86_64
do
PREFIX=${WORKDIR}/prefix_${arch}
PATH="${TOOLCHAIN_BASE_DIR}_${arch}/bin:${ORIGINAL_PATH}"
case $arch in
"aarch" )
CLANG=arm-linux-androideabi-clang
CXXLANG=arm-linux-androideabi-clang++
HOST="arm-linux-android";;
* )
CLANG=${arch}-linux-android-clang
CXXLANG=${arch}-linux-android-clang++
HOST="${arch}-linux-android";;
esac
cd $WORKDIR
rm -rf $ICONV_SRC_DIR
tar -xzf $ICONV_FILE_PATH -C $WORKDIR
cd $ICONV_SRC_DIR
CC=${CLANG} CXX=${CXXLANG} ./configure --build=x86_64-linux-gnu --host=${HOST} --prefix=${PREFIX} --disable-rpath
make
make install
done

70
scripts/android/build_monero.sh Executable file
View file

@ -0,0 +1,70 @@
# /bin/bash
WORKDIR=/opt/android
TOOLCHAIN_BASE_DIR=${WORKDIR}/toolchain
ORIGINAL_PATH=$PATH
MONERO_BRANCH=v0.17.1.9-android
MONERO_SRC_DIR=${WORKDIR}/monero
git clone https://github.com/cake-tech/monero.git ${MONERO_SRC_DIR} --branch ${MONERO_BRANCH}
cd $MONERO_SRC_DIR
git submodule init
git submodule update
for arch in "aarch" "aarch64" "i686" "x86_64"
do
FLAGS=""
PREFIX=${WORKDIR}/prefix_${arch}
ANDROID_STANDALONE_TOOLCHAIN_PATH="${TOOLCHAIN_BASE_DIR}_${arch}"
PATH="${ANDROID_STANDALONE_TOOLCHAIN_PATH}/bin:${ORIGINAL_PATH}"
DEST_LIB_DIR=${PREFIX}/lib/monero
DEST_INCLUDE_DIR=${PREFIX}/include
export CMAKE_INCLUDE_PATH="${PREFIX}/include"
export CMAKE_LIBRARY_PATH="${PREFIX}/lib"
mkdir -p $DEST_LIB_DIR
mkdir -p $DEST_INCLUDE_DIR
case $arch in
"aarch" )
CLANG=arm-linux-androideabi-clang
CXXLANG=arm-linux-androideabi-clang++
BUILD_64=OFF
TAG="android-armv7"
ARCH="armv7-a"
ARCH_ABI="armeabi-v7a"
FLAGS="-D CMAKE_ANDROID_ARM_MODE=ON -D NO_AES=true";;
"aarch64" )
CLANG=aarch64-linux-androideabi-clang
CXXLANG=aarch64-linux-androideabi-clang++
BUILD_64=ON
TAG="android-armv8"
ARCH="armv8-a"
ARCH_ABI="arm64-v8a";;
"i686" )
CLANG=i686-linux-androideabi-clang
CXXLANG=i686-linux-androideabi-clang++
BUILD_64=OFF
TAG="android-x86"
ARCH="i686"
ARCH_ABI="x86";;
"x86_64" )
CLANG=x86_64-linux-androideabi-clang
CXXLANG=x86_64-linux-androideabi-clang++
BUILD_64=ON
TAG="android-x86_64"
ARCH="x86-64"
ARCH_ABI="x86_64";;
esac
cd $MONERO_SRC_DIR
rm -rf ./build/release
mkdir -p ./build/release
cd ./build/release
CC=${CLANG} CXX=${CXXLANG} cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH=${ARCH} -D STATIC=ON -D BUILD_64=${BUILD_64} -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG=${TAG} -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI=${ARCH_ABI} $FLAGS ../..
make wallet_api -j4
find . -path ./lib -prune -o -name '*.a' -exec cp '{}' lib \;
cp -r ./lib/* $DEST_LIB_DIR
cp ../../src/wallet/api/wallet2_api.h $DEST_INCLUDE_DIR
done

View file

@ -0,0 +1,44 @@
# /bin/bash
WORKDIR=/opt/android
OPENSSL_FILENAME=openssl-1.0.2p.tar.gz
OPENSSL_FILE_PATH=$WORKDIR/$OPENSSL_FILENAME
OPENSSL_SRC_DIR=$WORKDIR/openssl-1.0.2p
ZLIB_FILENAME=zlib-1.2.11.tar.gz
ZLIB_FILE_PATH=$WORKDIR/$ZLIB_FILENAME
ZLIB_SRC_DIR=$WORKDIR/zlib-1.2.11
ORIGINAL_PATH=$PATH
TOOLCHAIN_BASE_DIR=${WORKDIR}/toolchain
wget https://zlib.net/$ZLIB_FILENAME -O $ZLIB_FILE_PATH
tar -xzf $ZLIB_FILE_PATH -C $WORKDIR
cd $ZLIB_SRC_DIR
CC=clang CXX=clang++ ./configure --static
make
wget https://www.openssl.org/source/$OPENSSL_FILENAME -O $OPENSSL_FILE_PATH
for arch in "aarch" "aarch64" "i686" "x86_64"
do
PREFIX=$WORKDIR/prefix_${arch}
PATH="${TOOLCHAIN_BASE_DIR}_${arch}/bin:${ORIGINAL_PATH}"
case $arch in
"aarch" ) TARGET="armv7";;
* ) TARGET="${arch}";;
esac
cd $WORKDIR
rm -rf $OPENSSL_SRC_DIR
tar -xzf $OPENSSL_FILE_PATH -C $WORKDIR
cd $OPENSSL_SRC_DIR
sed -i -e "s/mandroid/target\ ${TARGET}\-linux\-android/" Configure
CC=clang CXX=clang++ ./Configure android no-asm no-shared --static --with-zlib-include=${WORKDIR}/zlib --with-zlib-lib=${ZLIB_SRC_DIR} --prefix=${PREFIX} --openssldir=${PREFIX}
make
make install
done

31
scripts/android/build_sodium.sh Executable file
View file

@ -0,0 +1,31 @@
# /bin/bash
WORKDIR=/opt/android
TOOLCHAIN_BASE_DIR=${WORKDIR}/toolchain
SODIUM_SRC_DIR=${WORKDIR}/libsodium
SODIUM_BRANCH=1.0.16
ORIGINAL_PATH=$PATH
for arch in "aarch" "aarch64" "i686" "x86_64"
do
PREFIX=$WORKDIR/prefix_${arch}
PATH="${TOOLCHAIN_BASE_DIR}_${arch}/bin:${ORIGINAL_PATH}"
case $arch in
"aarch" ) TARGET="arm";;
"i686" ) TARGET="x86";;
* ) TARGET="${arch}";;
esac
HOST="${TARGET}-linux-android"
cd $WORKDIR
rm -rf $SODIUM_SRC_DIR
git clone https://github.com/jedisct1/libsodium.git $SODIUM_SRC_DIR -b $SODIUM_BRANCH
cd $SODIUM_SRC_DIR
./autogen.sh
CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=${HOST} --enable-static --disable-shared
make
make install
done

34
scripts/android/build_zmq.sh Executable file
View file

@ -0,0 +1,34 @@
# /bin/bash
WORKDIR=/opt/android
TOOLCHAIN_BASE_DIR=${WORKDIR}/toolchain
ZMQ_SRC_DIR=$WORKDIR/libzmq
ZMQ_BRANCH=master
ZMQ_COMMIT_HASH=501d0815bf2b0abb93be8214fc66519918ef6c40
ORIGINAL_PATH=$PATH
for arch in "aarch" "aarch64" "i686" "x86_64"
do
PREFIX=$WORKDIR/prefix_${arch}
PATH="${TOOLCHAIN_BASE_DIR}_${arch}/bin:${ORIGINAL_PATH}"
case $arch in
"aarch" ) TARGET="arm";;
"i686" ) TARGET="x86";;
* ) TARGET="${arch}";;
esac
HOST="${TARGET}-linux-android"
cd $WORKDIR
rm -rf $ZMQ_SRC_DIR
git clone https://github.com/zeromq/libzmq.git ${ZMQ_SRC_DIR} -b ${ZMQ_BRANCH}
cd $ZMQ_SRC_DIR
git checkout ${ZMQ_COMMIT_HASH}
./autogen.sh
CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=${HOST} --enable-static --disable-shared
make
make install
done

View file

@ -0,0 +1,34 @@
# /bin/bash
WORKDIR=/opt/android
CW_DIR=${WORKDIR}/cake_wallet
CW_EXRTERNAL_DIR=${CW_DIR}/cw_monero/ios/External/android
for arch in "aarch" "aarch64" "i686" "x86_64"
do
PREFIX=${WORKDIR}/prefix_${arch}
ABI=""
case $arch in
"aarch" )
ABI="armeabi-v7a";;
"aarch64" )
ABI="arm64-v8a";;
"i686" )
ABI="x86";;
"x86_64" )
ABI="x86_64";;
esac
LIB_DIR=${CW_EXRTERNAL_DIR}/${ABI}/lib
INCLUDE_DIR=${CW_EXRTERNAL_DIR}/${ABI}/include
mkdir -p $LIB_DIR
mkdir -p $INCLUDE_DIR
cp -r ${PREFIX}/lib/* $LIB_DIR
cp -r ${PREFIX}/include/* $INCLUDE_DIR
done

View file

@ -0,0 +1,9 @@
# /bin/bash
WORKDIR=/opt/android
ARCH=$1
PREFIX="${WORKDIR}/prefix_${ARCH}"
BOOST_SRC_DIR=$WORKDIR/boost_1_68_0
cd $BOOST_SRC_DIR
./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --with-locale --build-dir=android --stagedir=android toolset=clang threading=multi threadapi=pthread target-os=android -sICONV_PATH=${PREFIX} install

18
scripts/android/init_boost.sh Executable file
View file

@ -0,0 +1,18 @@
# /bin/bash
WORKDIR=/opt/android
ARCH=$1
PREFIX="${WORKDIR}/prefix_${ARCH}"
BOOST_FILENAME=boost_1_68_0.tar.bz2
BOOST_FILE_PATH=$WORKDIR/$BOOST_FILENAME
BOOST_SRC_DIR=$WORKDIR/boost_1_68_0
if [ ! -e "$BOOST_FILE_PATH" ]; then
wget https://dl.bintray.com/boostorg/release/1.68.0/source/$BOOST_FILENAME -O $BOOST_FILE_PATH
fi
cd $WORKDIR
rm -rf $BOOST_SRC_DIR
tar -xvf $BOOST_FILE_PATH -C $WORKDIR
cd $BOOST_SRC_DIR
./bootstrap.sh --prefix=${PREFIX}

17
scripts/android/install_ndk.sh Executable file
View file

@ -0,0 +1,17 @@
# /bin/bash
WORKDIR=/opt/android
ANDROID_NDK_ZIP=${WORKDIR}/android-ndk-r17c.zip
ANDROID_NDK_ROOT=${WORKDIR}/android-ndk-r17c
TOOLCHAIN_DIR=${WORKDIR}/toolchain
TOOLCHAIN_A32_DIR=${TOOLCHAIN_DIR}_aarch
TOOLCHAIN_A64_DIR=${TOOLCHAIN_DIR}_aarch64
TOOLCHAIN_x86_DIR=${TOOLCHAIN_DIR}_i686
TOOLCHAIN_x86_64_DIR=${TOOLCHAIN_DIR}_x86_64
wget https://dl.google.com/android/repository/android-ndk-r17c-linux-x86_64.zip -O /opt/android/android-ndk-r17c.zip
unzip /opt/android/android-ndk-r17c.zip -d $WORKDIR
${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py --arch arm64 --api 21 --install-dir ${TOOLCHAIN_A64_DIR} --stl=libc++
${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py --arch arm --api 21 --install-dir ${TOOLCHAIN_A32_DIR} --stl=libc++
${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py --arch x86 --api 21 --install-dir ${TOOLCHAIN_x86_DIR} --stl=libc++
${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py --arch x86_64 --api 21 --install-dir ${TOOLCHAIN_x86_64_DIR} --stl=libc++

View file

@ -1,8 +0,0 @@
{
"salt": "",
"keychainSalt": "",
"key": "",
"walletSalt": "",
"shortKey": "",
"change_now_api_key": ""
}

View file

@ -0,0 +1,14 @@
import 'dart:io';
const outputPath = 'android/key.properties';
Future<void> main(List<String> args) async {
final output = args.fold('', (String acc, String arg) => acc + arg + '\n');
final outputFile = File(outputPath);
if (outputFile.existsSync()) {
await outputFile.delete();
}
await outputFile.writeAsString(output);
}

View file

@ -0,0 +1,12 @@
import 'generate_secrets_config.dart';
import 'import_secrets_config.dart';
const configPath = 'tool/.secrets-config.json';
const outputPath = 'lib/.secrets.g.dart';
Future<void> main(List<String> args) async => generateSecrets(args);
Future<void> generateSecrets(List<String> args) async {
await generateSecretsConfig(args);
await importSecretsConfig();
}

View file

@ -0,0 +1,49 @@
import 'dart:convert';
import 'dart:io';
import 'utils/secret_key.dart';
import 'utils/utils.dart';
const configPath = 'tool/.secrets-config.json';
Future<void> main(List<String> args) async => generateSecretsConfig(args);
Future<void> generateSecretsConfig(List<String> args) async {
final extraInfo =
args.fold(<String, dynamic>{}, (Map<String, dynamic> acc, String arg) {
final parts = arg.split('=');
final key = normalizeKeyName(parts[0]);
acc[key] = acc[key] = parts.length > 1 ? parts[1] : 1;
return acc;
});
final configFile = File(configPath);
final secrets = <String, dynamic>{};
secrets.addAll(extraInfo);
secrets.removeWhere((key, dynamic value) {
if (key.contains('--')) {
return true;
}
return false;
});
if (configFile.existsSync()) {
if (extraInfo['--force'] == 1) {
await configFile.delete();
} else {
return;
}
}
SecretKey.base.forEach((sec) {
if (secrets[sec.name] != null) {
return;
}
secrets[sec.name] = sec.generate();
});
final secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
await configFile.writeAsString(secretsJson);
}

View file

@ -0,0 +1,23 @@
import 'dart:convert';
import 'dart:io';
import 'utils/utils.dart';
const configPath = 'tool/.secrets-config.json';
const outputPath = 'lib/.secrets.g.dart';
Future<void> main(List<String> args) async => importSecretsConfig();
Future<void> importSecretsConfig() async {
final outputFile = File(outputPath);
final input = json.decode(File(configPath).readAsStringSync())
as Map<String, dynamic> ??
<String, dynamic>{};
final output = input.keys
.fold('', (String acc, String val) => acc + generateConst(val, input));
if (outputFile.existsSync()) {
await outputFile.delete();
}
await outputFile.writeAsString(output);
}

View file

@ -1,20 +0,0 @@
import 'dart:convert';
import 'dart:io';
const secretsProdPath = 'tool/.secrets-prod.json';
const secretsTestPath = 'tool/.secrets-test.json';
const outputPath = 'lib/.secrets.g.dart';
Future<void> main() async {
final inputPath = FileSystemEntity.typeSync(secretsProdPath) !=
FileSystemEntityType.notFound
? secretsProdPath
: secretsTestPath;
final inoutContent = File(inputPath).readAsStringSync();
final config = json.decode(inoutContent) as Map<String, dynamic>;
final output =
'const salt = \'${config["salt"]}\';const keychainSalt = \'${config["keychainSalt"]}\';\nconst key = \'${config["key"]}\';\nconst walletSalt = \'${config["walletSalt"]}\';\nconst shortKey = \'${config["shortKey"]}\';\nconst change_now_api_key = \'${config["change_now_api_key"]}\';';
await File(outputPath).writeAsString(output);
}

47
tool/update_secrets.dart Normal file
View file

@ -0,0 +1,47 @@
import 'dart:convert';
import 'dart:io';
import 'generate_new_secrets.dart';
import 'import_secrets_config.dart';
import 'utils/utils.dart';
const configPath = 'tool/.secrets-config.json';
Future<void> main(List<String> args) async {
await updateSecretsConfig(args);
await importSecretsConfig();
}
Future<void> updateSecretsConfig(List<String> args) async {
final extraInfo =
args.fold(<String, dynamic>{}, (Map<String, dynamic> acc, String arg) {
final parts = arg.split('=');
final key = normalizeKeyName(parts[0]);
acc[key] = parts.length > 1 ? parts[1] : 1;
return acc;
});
final configFile = File(configPath);
final secrets = <String, dynamic>{};
secrets.addAll(extraInfo);
secrets.removeWhere((key, dynamic value) {
if (key.contains('--')) {
return true;
}
return false;
});
final fileConfig =
json.decode(configFile.readAsStringSync()) as Map<String, dynamic> ??
<String, dynamic>{};
fileConfig.forEach((key, dynamic value) {
if (secrets[key] == null) {
secrets[key] = value;
}
});
final secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
await configFile.writeAsString(secretsJson);
await generateSecrets(args);
}

View file

@ -0,0 +1,28 @@
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:convert/convert.dart';
class SecretKey {
const SecretKey(this.name, this.generate);
static final base = [
SecretKey('salt', () => hex.encode(encrypt.Key.fromSecureRandom(16).bytes)),
SecretKey('keychainSalt',
() => hex.encode(encrypt.Key.fromSecureRandom(12).bytes)),
SecretKey('key', () => hex.encode(encrypt.Key.fromSecureRandom(16).bytes)),
SecretKey(
'walletSalt', () => hex.encode(encrypt.Key.fromSecureRandom(4).bytes)),
SecretKey(
'shortKey', () => hex.encode(encrypt.Key.fromSecureRandom(12).bytes)),
SecretKey(
'backupSalt', () => hex.encode(encrypt.Key.fromSecureRandom(8).bytes)),
SecretKey('backupKeychainSalt',
() => hex.encode(encrypt.Key.fromSecureRandom(12).bytes)),
SecretKey('changeNowApiKey', () => ''),
SecretKey('wyreSecretKey', () => ''),
SecretKey('wyreApiKey', () => ''),
SecretKey('wyreAccountId', () => ''),
];
final String name;
final String Function() generate;
}

13
tool/utils/utils.dart Normal file
View file

@ -0,0 +1,13 @@
import 'package:intl/intl.dart';
String normalizeKeyName(String key) {
final parts = key.split('_');
final firstWord = parts.removeAt(0);
final capitalized = parts
.map((e) => toBeginningOfSentenceCase(e))
.fold('', (String acc, String word) => acc + word);
return firstWord + capitalized;
}
String generateConst(String name, Map<String, dynamic> config) =>
'const $name = \'${config["$name"]}\';\n';