diff --git a/.github/workflows/cache_dependencies.yml b/.github/workflows/cache_dependencies.yml
index c0042bf5c..ce098b7f1 100644
--- a/.github/workflows/cache_dependencies.yml
+++ b/.github/workflows/cache_dependencies.yml
@@ -62,10 +62,22 @@ jobs:
/opt/android/cake_wallet/cw_haven/android/.cxx
/opt/android/cake_wallet/scripts/monero_c/release
key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }}
-
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
name: Generate Externals
run: |
cd /opt/android/cake_wallet/scripts/android/
source ./app_env.sh cakewallet
./build_monero_all.sh
+
+ - name: Cache Keystore
+ id: cache-keystore
+ uses: actions/cache@v3
+ with:
+ path: /opt/android/cake_wallet/android/app/key.jks
+ key: $STORE_PASS
+
+ - if: ${{ steps.cache-keystore.outputs.cache-hit != 'true' }}
+ name: Generate KeyStore
+ run: |
+ cd /opt/android/cake_wallet/android/app
+ keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias testKey -noprompt -dname "CN=CakeWallet, OU=CakeWallet, O=CakeWallet, L=Florida, S=America, C=USA" -storepass $STORE_PASS -keypass $KEY_PASS
diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml
index c9021fac0..774404e3b 100644
--- a/.github/workflows/pr_test_build_android.yml
+++ b/.github/workflows/pr_test_build_android.yml
@@ -115,6 +115,14 @@ jobs:
cd /opt/android/cake_wallet/scripts/android/
./build_mwebd.sh --dont-install
+# - name: Cache Keystore
+# id: cache-keystore
+# uses: actions/cache@v3
+# with:
+# path: /opt/android/cake_wallet/android/app/key.jks
+# key: $STORE_PASS
+#
+# - if: ${{ steps.cache-keystore.outputs.cache-hit != 'true' }}
- name: Generate KeyStore
run: |
cd /opt/android/cake_wallet/android/app
@@ -192,6 +200,8 @@ jobs:
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
+ echo "const meldTestApiKey = '${{ secrets.MELD_TEST_API_KEY }}';" >> lib/.secrets.g.dart
+ echo "const meldTestPublicKey = '${{ secrets.MELD_TEST_PUBLIC_KEY}}';" >> lib/.secrets.g.dar
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
@@ -201,6 +211,36 @@ jobs:
run: |
echo -e "id=com.cakewallet.test_${{ env.PR_NUMBER }}\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties
+ # Step 3: Download previous build number
+ - name: Download previous build number
+ id: download-build-number
+ run: |
+ # Download the artifact if it exists
+ if [[ ! -f build_number.txt ]]; then
+ echo "1" > build_number.txt
+ fi
+
+ # Step 4: Read and Increment Build Number
+ - name: Increment Build Number
+ id: increment-build-number
+ run: |
+ # Read current build number from file
+ BUILD_NUMBER=$(cat build_number.txt)
+ BUILD_NUMBER=$((BUILD_NUMBER + 1))
+ echo "New build number: $BUILD_NUMBER"
+
+ # Save the incremented build number
+ echo "$BUILD_NUMBER" > build_number.txt
+
+ # Export the build number to use in later steps
+ echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV
+
+ # Step 5: Update pubspec.yaml with new build number
+ - name: Update build number
+ run: |
+ cd /opt/android/cake_wallet
+ sed -i "s/^version: .*/version: 1.0.$BUILD_NUMBER/" pubspec.yaml
+
- name: Build
run: |
cd /opt/android/cake_wallet
@@ -231,6 +271,13 @@ jobs:
with:
path: /opt/android/cake_wallet/build/app/outputs/flutter-apk/test-apk/
+ # Re-upload updated build number for the next run
+ - name: Upload updated build number
+ uses: actions/upload-artifact@v3
+ with:
+ name: build_number
+ path: build_number.txt
+
- name: Send Test APK
continue-on-error: true
uses: adrey/slack-file-upload-action@1.0.5
@@ -241,3 +288,4 @@ jobs:
title: "${{ env.BRANCH_NAME }}.apk"
filename: ${{ env.BRANCH_NAME }}.apk
initial_comment: ${{ github.event.head_commit.message }}
+
diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml
index 5ea0cb377..f3ce1045d 100644
--- a/.github/workflows/pr_test_build_linux.yml
+++ b/.github/workflows/pr_test_build_linux.yml
@@ -175,6 +175,8 @@ jobs:
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
+ echo "const meldTestApiKey = '${{ secrets.MELD_TEST_API_KEY }}';" >> lib/.secrets.g.dart
+ echo "const meldTestPublicKey = '${{ secrets.MELD_TEST_PUBLIC_KEY}}';" >> lib/.secrets.g.dar
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 2f5427531..5005a8bab 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -92,3 +92,8 @@ dependencies {
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
+configurations {
+ implementation.exclude module:'proto-google-common-protos'
+ implementation.exclude module:'protolite-well-known-types'
+ implementation.exclude module:'protobuf-javalite'
+}
\ No newline at end of file
diff --git a/android/gradle.properties b/android/gradle.properties
index 38c8d4544..66dd09454 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,4 +1,4 @@
-org.gradle.jvmargs=-Xmx1536M
+org.gradle.jvmargs=-Xmx3072M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
diff --git a/assets/images/apple_pay_logo.png b/assets/images/apple_pay_logo.png
new file mode 100644
index 000000000..346007e3b
Binary files /dev/null and b/assets/images/apple_pay_logo.png differ
diff --git a/assets/images/apple_pay_round_dark.svg b/assets/images/apple_pay_round_dark.svg
new file mode 100644
index 000000000..82443bfb4
--- /dev/null
+++ b/assets/images/apple_pay_round_dark.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/assets/images/apple_pay_round_light.svg b/assets/images/apple_pay_round_light.svg
new file mode 100644
index 000000000..2beb1248f
--- /dev/null
+++ b/assets/images/apple_pay_round_light.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/assets/images/bank.png b/assets/images/bank.png
new file mode 100644
index 000000000..9dc68147a
Binary files /dev/null and b/assets/images/bank.png differ
diff --git a/assets/images/bank_dark.svg b/assets/images/bank_dark.svg
new file mode 100644
index 000000000..670120796
--- /dev/null
+++ b/assets/images/bank_dark.svg
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/assets/images/bank_light.svg b/assets/images/bank_light.svg
new file mode 100644
index 000000000..804716289
--- /dev/null
+++ b/assets/images/bank_light.svg
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/assets/images/buy_sell.png b/assets/images/buy_sell.png
new file mode 100644
index 000000000..0fbffe56f
Binary files /dev/null and b/assets/images/buy_sell.png differ
diff --git a/assets/images/card.svg b/assets/images/card.svg
new file mode 100644
index 000000000..95530cdc9
--- /dev/null
+++ b/assets/images/card.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/images/card_dark.svg b/assets/images/card_dark.svg
new file mode 100644
index 000000000..2e5bcf986
--- /dev/null
+++ b/assets/images/card_dark.svg
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/assets/images/dollar_coin.svg b/assets/images/dollar_coin.svg
new file mode 100644
index 000000000..22218f332
--- /dev/null
+++ b/assets/images/dollar_coin.svg
@@ -0,0 +1,12 @@
+
+
+
\ No newline at end of file
diff --git a/assets/images/google_pay_icon.png b/assets/images/google_pay_icon.png
new file mode 100644
index 000000000..a3ca38311
Binary files /dev/null and b/assets/images/google_pay_icon.png differ
diff --git a/assets/images/meld_logo.svg b/assets/images/meld_logo.svg
new file mode 100644
index 000000000..1d9211d64
--- /dev/null
+++ b/assets/images/meld_logo.svg
@@ -0,0 +1,30 @@
+
diff --git a/assets/images/revolut.png b/assets/images/revolut.png
new file mode 100644
index 000000000..bbe342592
Binary files /dev/null and b/assets/images/revolut.png differ
diff --git a/assets/images/skrill.svg b/assets/images/skrill.svg
new file mode 100644
index 000000000..b264b57eb
--- /dev/null
+++ b/assets/images/skrill.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/assets/images/usd_round_dark.svg b/assets/images/usd_round_dark.svg
new file mode 100644
index 000000000..f329dd617
--- /dev/null
+++ b/assets/images/usd_round_dark.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/assets/images/usd_round_light.svg b/assets/images/usd_round_light.svg
new file mode 100644
index 000000000..f5965c597
--- /dev/null
+++ b/assets/images/usd_round_light.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/assets/images/wallet_new.png b/assets/images/wallet_new.png
new file mode 100644
index 000000000..47c43bfca
Binary files /dev/null and b/assets/images/wallet_new.png differ
diff --git a/assets/node_list.yml b/assets/node_list.yml
index d04a9a2e8..6191129b3 100644
--- a/assets/node_list.yml
+++ b/assets/node_list.yml
@@ -1,6 +1,7 @@
-
uri: xmr-node.cakewallet.com:18081
is_default: true
+ trusted: true
-
uri: cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion:18081
is_default: false
diff --git a/assets/text/Monerocom_Release_Notes.txt b/assets/text/Monerocom_Release_Notes.txt
index 613ea4281..46f21e172 100644
--- a/assets/text/Monerocom_Release_Notes.txt
+++ b/assets/text/Monerocom_Release_Notes.txt
@@ -1,3 +1,3 @@
-Monero enhancements
-Introducing StealthEx and LetxExchange
+Add airgapped Monero wallet support (best used with our new offline app Cupcake)
+New Buy & Sell flow
Bug fixes
\ No newline at end of file
diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt
index 61aafb6e4..6764826a7 100644
--- a/assets/text/Release_Notes.txt
+++ b/assets/text/Release_Notes.txt
@@ -1,7 +1,5 @@
-Added Litecoin MWEB
-Added wallet groups
-Silent Payment enhancements for speed & reliability
-Monero enhancements
-Introducing StealthEx and LetxExchange
-Additional ERC20 tokens scam detection
+Add Litecoin Ledger support
+Add airgapped Monero wallet support (best used with our new offline app Cupcake)
+MWEB fixes and enhancements
+New Buy & Sell flow
Bug fixes
\ No newline at end of file
diff --git a/build-guide-win.md b/build-guide-win.md
new file mode 100644
index 000000000..6ace961af
--- /dev/null
+++ b/build-guide-win.md
@@ -0,0 +1,38 @@
+# Building CakeWallet for Windows
+
+## Requirements and Setup
+
+The following are the system requirements to build CakeWallet for your Windows PC.
+
+```
+Windows 10 or later (64-bit), x86-64 based
+Flutter 3 or above
+```
+
+## Building CakeWallet on Windows
+
+These steps will help you configure and execute a build of CakeWallet from its source code.
+
+### 1. Installing Package Dependencies
+
+For build CakeWallet windows application from sources you will be needed to have:
+> [Install Flutter]Follow installation guide (https://docs.flutter.dev/get-started/install/windows) and install do not miss to dev tools (install https://docs.flutter.dev/get-started/install/windows/desktop#development-tools) which are required for windows desktop development (need to install Git for Windows and Visual Studio 2022). Then install `Desktop development with C++` packages via GUI Visual Studio 2022, or Visual Studio Build Tools 2022 including: `C++ Build Tools core features`, `C++ 2022 Redistributable Update`, `C++ core desktop features`, `MVC v143 - VS 2022 C++ x64/x86 build tools`, `C++ CMake tools for Windwos`, `Testing tools core features - Build Tools`, `C++ AddressSanitizer`.
+> [Install WSL] for building monero dependencies need to install Windows WSL (https://learn.microsoft.com/en-us/windows/wsl/install) and required packages for WSL (Ubuntu):
+`$ sudo apt update `
+`$ sudo apt build-essential cmake gcc-mingw-w64 g++-mingw-w64 autoconf libtool pkg-config`
+
+### 2. Pull CakeWallet source code
+
+You can downlaod CakeWallet source code from our [GitHub repository](github.com/cake-tech/cake_wallet) via git by following next command:
+`$ git clone https://github.com/cake-tech/cake_wallet.git --branch MrCyjaneK-cyjan-monerodart`
+OR you can download it as [Zip archive](https://github.com/cake-tech/cake_wallet/archive/refs/heads/MrCyjaneK-cyjan-monerodart.zip)
+
+### 3. Build Monero, Monero_c and their dependencies
+
+For use monero in the application need to build Monero wrapper - Monero_C which will be used by monero.dart package. For that need to run shell (bash - typically same named utility should be available after WSL is enabled in your system) with previously installed WSL, then change current directory to the application project directory with your used shell and then change current directory to `scripts/windows`: `$ cd scripts/windows`. Run build script: `$ ./build_all.sh`.
+
+### 4. Configure and build CakeWallet application
+
+To configure the application open directory where you have downloaded or unarchived CakeWallet sources and run `cakewallet.bat`.
+Or if you used WSL and have active shell session you can run `$ ./cakewallet.sh` script in `scripts/windows` which will run `cakewallet.bat` in WSL.
+After execution of `cakewallet.bat` you should to get `Cake Wallet.zip` in project root directory which will contains `CakeWallet.exe` file and another needed files for run the application. Now you can extract files from `Cake Wallet.zip` archive and run the application.
diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart
index 6245aa787..e58fad00f 100644
--- a/cw_bitcoin/lib/electrum_wallet.dart
+++ b/cw_bitcoin/lib/electrum_wallet.dart
@@ -574,6 +574,8 @@ abstract class ElectrumWalletBase
Future connectToNode({required Node node}) async {
this.node = node;
+ if (syncStatus is ConnectingSyncStatus) return;
+
try {
syncStatus = ConnectingSyncStatus();
@@ -2216,13 +2218,14 @@ abstract class ElectrumWalletBase
if (syncStatus is NotConnectedSyncStatus ||
syncStatus is LostConnectionSyncStatus ||
syncStatus is ConnectingSyncStatus) {
- syncStatus = AttemptingSyncStatus();
- startSync();
+ syncStatus = ConnectedSyncStatus();
}
break;
case ConnectionStatus.disconnected:
- if (syncStatus is! NotConnectedSyncStatus) {
+ if (syncStatus is! NotConnectedSyncStatus &&
+ syncStatus is! ConnectingSyncStatus &&
+ syncStatus is! SyncronizingSyncStatus) {
syncStatus = NotConnectedSyncStatus();
}
break;
diff --git a/cw_bitcoin/lib/pending_bitcoin_transaction.dart b/cw_bitcoin/lib/pending_bitcoin_transaction.dart
index 140965655..411c7de16 100644
--- a/cw_bitcoin/lib/pending_bitcoin_transaction.dart
+++ b/cw_bitcoin/lib/pending_bitcoin_transaction.dart
@@ -153,4 +153,9 @@ class PendingBitcoinTransaction with PendingTransaction {
inputAddresses: _tx.inputs.map((input) => input.txId).toList(),
outputAddresses: outputAddresses,
fee: fee);
+
+ @override
+ Future commitUR() {
+ throw UnimplementedError();
+ }
}
diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock
index 0e4921a3a..c8a83f90c 100644
--- a/cw_bitcoin/pubspec.lock
+++ b/cw_bitcoin/pubspec.lock
@@ -87,8 +87,8 @@ packages:
dependency: "direct overridden"
description:
path: "."
- ref: cake-update-v8
- resolved-ref: fc045a11db3d85d806ca67f75e8b916c706745a2
+ ref: cake-update-v9
+ resolved-ref: "86969a14e337383e14965f5fb45a72a63e5009bc"
url: "https://github.com/cake-tech/bitcoin_base"
source: git
version: "4.7.0"
@@ -386,10 +386,10 @@ packages:
dependency: transitive
description:
name: flutter_web_bluetooth
- sha256: "52ce64f65d7321c4bf6abfe9dac02fb888731339a5e0ad6de59fb916c20c9f02"
+ sha256: fcd03e2e5f82edcedcbc940f1b6a0635a50757374183254f447640886c53208e
url: "https://pub.dev"
source: hosted
- version: "0.2.3"
+ version: "0.2.4"
flutter_web_plugins:
dependency: transitive
description: flutter
@@ -560,7 +560,7 @@ packages:
description:
path: "packages/ledger-bitcoin"
ref: HEAD
- resolved-ref: dbb5c4956949dc734af3fc8febdbabed89da72aa
+ resolved-ref: "07cd61ef76a2a017b6d5ef233396740163265457"
url: "https://github.com/cake-tech/ledger-flutter-plus-plugins"
source: git
version: "0.0.3"
@@ -577,7 +577,7 @@ packages:
description:
path: "packages/ledger-litecoin"
ref: HEAD
- resolved-ref: dbb5c4956949dc734af3fc8febdbabed89da72aa
+ resolved-ref: "07cd61ef76a2a017b6d5ef233396740163265457"
url: "https://github.com/cake-tech/ledger-flutter-plus-plugins"
source: git
version: "0.0.2"
diff --git a/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart b/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart
index e1fa9d6e0..05483ce54 100644
--- a/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart
+++ b/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart
@@ -85,4 +85,8 @@ class PendingBitcoinCashTransaction with PendingTransaction {
fee: fee,
isReplaced: false,
);
+ @override
+ Future commitUR() {
+ throw UnimplementedError();
+ }
}
diff --git a/cw_core/lib/currency_for_wallet_type.dart b/cw_core/lib/currency_for_wallet_type.dart
index 630078949..af6037a3b 100644
--- a/cw_core/lib/currency_for_wallet_type.dart
+++ b/cw_core/lib/currency_for_wallet_type.dart
@@ -35,3 +35,34 @@ CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
'Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
}
}
+
+WalletType? walletTypeForCurrency(CryptoCurrency currency) {
+ switch (currency) {
+ case CryptoCurrency.btc:
+ return WalletType.bitcoin;
+ case CryptoCurrency.xmr:
+ return WalletType.monero;
+ case CryptoCurrency.ltc:
+ return WalletType.litecoin;
+ case CryptoCurrency.xhv:
+ return WalletType.haven;
+ case CryptoCurrency.eth:
+ return WalletType.ethereum;
+ case CryptoCurrency.bch:
+ return WalletType.bitcoinCash;
+ case CryptoCurrency.nano:
+ return WalletType.nano;
+ case CryptoCurrency.banano:
+ return WalletType.banano;
+ case CryptoCurrency.maticpoly:
+ return WalletType.polygon;
+ case CryptoCurrency.sol:
+ return WalletType.solana;
+ case CryptoCurrency.trx:
+ return WalletType.tron;
+ case CryptoCurrency.wow:
+ return WalletType.wownero;
+ default:
+ return null;
+ }
+}
diff --git a/cw_core/lib/hardware/device_connection_type.dart b/cw_core/lib/hardware/device_connection_type.dart
index 9a3069552..466d58e2a 100644
--- a/cw_core/lib/hardware/device_connection_type.dart
+++ b/cw_core/lib/hardware/device_connection_type.dart
@@ -7,6 +7,7 @@ enum DeviceConnectionType {
static List supportedConnectionTypes(WalletType walletType,
[bool isIOS = false]) {
switch (walletType) {
+ // case WalletType.monero:
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.ethereum:
diff --git a/cw_core/lib/monero_wallet_keys.dart b/cw_core/lib/monero_wallet_keys.dart
index 1435002a8..fe3784986 100644
--- a/cw_core/lib/monero_wallet_keys.dart
+++ b/cw_core/lib/monero_wallet_keys.dart
@@ -1,10 +1,12 @@
class MoneroWalletKeys {
const MoneroWalletKeys(
- {required this.privateSpendKey,
+ {required this.primaryAddress,
+ required this.privateSpendKey,
required this.privateViewKey,
required this.publicSpendKey,
required this.publicViewKey});
+ final String primaryAddress;
final String publicViewKey;
final String privateViewKey;
final String publicSpendKey;
diff --git a/cw_core/lib/pending_transaction.dart b/cw_core/lib/pending_transaction.dart
index 0a6103a5f..78eba68a3 100644
--- a/cw_core/lib/pending_transaction.dart
+++ b/cw_core/lib/pending_transaction.dart
@@ -14,5 +14,8 @@ mixin PendingTransaction {
int? get outputCount => null;
PendingChange? change;
+ bool shouldCommitUR() => false;
+
Future commit();
+ Future commitUR();
}
diff --git a/cw_core/lib/wallet_service.dart b/cw_core/lib/wallet_service.dart
index d90ae30bc..85126853e 100644
--- a/cw_core/lib/wallet_service.dart
+++ b/cw_core/lib/wallet_service.dart
@@ -61,4 +61,8 @@ abstract class WalletService false;
}
diff --git a/cw_evm/lib/pending_evm_chain_transaction.dart b/cw_evm/lib/pending_evm_chain_transaction.dart
index 0b367da68..6861b41f8 100644
--- a/cw_evm/lib/pending_evm_chain_transaction.dart
+++ b/cw_evm/lib/pending_evm_chain_transaction.dart
@@ -47,4 +47,9 @@ class PendingEVMChainTransaction with PendingTransaction {
return '0x${Hex.HEX.encode(txid)}';
}
+
+ @override
+ Future commitUR() {
+ throw UnimplementedError();
+ }
}
diff --git a/cw_haven/lib/api/exceptions/wallet_restore_from_keys_exception.dart b/cw_haven/lib/api/exceptions/wallet_restore_from_keys_exception.dart
index c6b6c6ef7..3ff5f2438 100644
--- a/cw_haven/lib/api/exceptions/wallet_restore_from_keys_exception.dart
+++ b/cw_haven/lib/api/exceptions/wallet_restore_from_keys_exception.dart
@@ -2,4 +2,9 @@ class WalletRestoreFromKeysException implements Exception {
WalletRestoreFromKeysException({required this.message});
final String message;
+
+ @override
+ String toString() {
+ return message;
+ }
}
\ No newline at end of file
diff --git a/cw_haven/lib/haven_wallet.dart b/cw_haven/lib/haven_wallet.dart
index 06a838100..e2e598bbe 100644
--- a/cw_haven/lib/haven_wallet.dart
+++ b/cw_haven/lib/haven_wallet.dart
@@ -73,6 +73,7 @@ abstract class HavenWalletBase
@override
MoneroWalletKeys get keys => MoneroWalletKeys(
+ primaryAddress: haven_wallet.getAddress(accountIndex: 0, addressIndex: 0),
privateSpendKey: haven_wallet.getSecretSpendKey(),
privateViewKey: haven_wallet.getSecretViewKey(),
publicSpendKey: haven_wallet.getPublicSpendKey(),
diff --git a/cw_haven/lib/pending_haven_transaction.dart b/cw_haven/lib/pending_haven_transaction.dart
index 1d95de08c..dbf075044 100644
--- a/cw_haven/lib/pending_haven_transaction.dart
+++ b/cw_haven/lib/pending_haven_transaction.dart
@@ -48,4 +48,9 @@ class PendingHavenTransaction with PendingTransaction {
rethrow;
}
}
+
+ @override
+ Future commitUR() {
+ throw UnimplementedError();
+ }
}
diff --git a/cw_monero/lib/api/account_list.dart b/cw_monero/lib/api/account_list.dart
index 7cb95a507..e3bb25c97 100644
--- a/cw_monero/lib/api/account_list.dart
+++ b/cw_monero/lib/api/account_list.dart
@@ -2,6 +2,7 @@ import 'package:cw_monero/api/wallet.dart';
import 'package:monero/monero.dart' as monero;
monero.wallet? wptr = null;
+bool get isViewOnly => int.tryParse(monero.Wallet_secretSpendKey(wptr!)) == 0;
int _wlptrForW = 0;
monero.WalletListener? _wlptr = null;
diff --git a/cw_monero/lib/api/transaction_history.dart b/cw_monero/lib/api/transaction_history.dart
index a308b682e..7748a16af 100644
--- a/cw_monero/lib/api/transaction_history.dart
+++ b/cw_monero/lib/api/transaction_history.dart
@@ -13,7 +13,13 @@ import 'package:mutex/mutex.dart';
String getTxKey(String txId) {
- return monero.Wallet_getTxKey(wptr!, txid: txId);
+ final txKey = monero.Wallet_getTxKey(wptr!, txid: txId);
+ final status = monero.Wallet_status(wptr!);
+ if (status != 0) {
+ final error = monero.Wallet_errorString(wptr!);
+ return txId+"_"+error;
+ }
+ return txKey;
}
final txHistoryMutex = Mutex();
monero.TransactionHistory? txhistory;
@@ -178,12 +184,13 @@ PendingTransactionDescription createTransactionMultDestSync(
);
}
-void commitTransactionFromPointerAddress({required int address}) =>
- commitTransaction(transactionPointer: monero.PendingTransaction.fromAddress(address));
+String? commitTransactionFromPointerAddress({required int address, required bool useUR}) =>
+ commitTransaction(transactionPointer: monero.PendingTransaction.fromAddress(address), useUR: useUR);
-void commitTransaction({required monero.PendingTransaction transactionPointer}) {
-
- final txCommit = monero.PendingTransaction_commit(transactionPointer, filename: '', overwrite: false);
+String? commitTransaction({required monero.PendingTransaction transactionPointer, required bool useUR}) {
+ final txCommit = useUR
+ ? monero.PendingTransaction_commitUR(transactionPointer, 120)
+ : monero.PendingTransaction_commit(transactionPointer, filename: '', overwrite: false);
final String? error = (() {
final status = monero.PendingTransaction_status(transactionPointer.cast());
@@ -196,6 +203,11 @@ void commitTransaction({required monero.PendingTransaction transactionPointer})
if (error != null) {
throw CreationTransactionException(message: error);
}
+ if (useUR) {
+ return txCommit as String?;
+ } else {
+ return null;
+ }
}
Future _createTransactionSync(Map args) async {
diff --git a/cw_monero/lib/api/wallet.dart b/cw_monero/lib/api/wallet.dart
index 8e03cff3e..928fd7ef1 100644
--- a/cw_monero/lib/api/wallet.dart
+++ b/cw_monero/lib/api/wallet.dart
@@ -119,7 +119,7 @@ Future setupNodeSync(
daemonUsername: login ?? '',
daemonPassword: password ?? '');
});
- // monero.Wallet_init3(wptr!, argv0: '', defaultLogBaseName: 'moneroc', console: true);
+ // monero.Wallet_init3(wptr!, argv0: '', defaultLogBaseName: 'moneroc', console: true, logPath: '');
final status = monero.Wallet_status(wptr!);
@@ -330,4 +330,4 @@ String signMessage(String message, {String address = ""}) {
bool verifyMessage(String message, String address, String signature) {
return monero.Wallet_verifySignedMessage(wptr!, message: message, address: address, signature: signature);
-}
\ No newline at end of file
+}
diff --git a/cw_monero/lib/api/wallet_manager.dart b/cw_monero/lib/api/wallet_manager.dart
index dca7586fb..7f9dbd8fa 100644
--- a/cw_monero/lib/api/wallet_manager.dart
+++ b/cw_monero/lib/api/wallet_manager.dart
@@ -7,19 +7,18 @@ import 'package:cw_monero/api/exceptions/wallet_creation_exception.dart';
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart';
import 'package:cw_monero/api/exceptions/wallet_restore_from_seed_exception.dart';
-import 'package:cw_monero/api/wallet.dart';
import 'package:cw_monero/api/transaction_history.dart';
+import 'package:cw_monero/api/wallet.dart';
+import 'package:cw_monero/ledger.dart';
import 'package:monero/monero.dart' as monero;
class MoneroCException implements Exception {
final String message;
MoneroCException(this.message);
-
+
@override
- String toString() {
- return message;
- }
+ String toString() => message;
}
void checkIfMoneroCIsFine() {
@@ -43,7 +42,6 @@ void checkIfMoneroCIsFine() {
throw MoneroCException("monero_c and monero.dart wrapper export list mismatch.\nLogic errors can occur.\nRefusing to run in release mode.\ncpp: '$cppCsExp'\ndart: '$dartCsExp'");
}
}
-
monero.WalletManager? _wmPtr;
final monero.WalletManager wmPtr = Pointer.fromAddress((() {
try {
@@ -60,6 +58,13 @@ final monero.WalletManager wmPtr = Pointer.fromAddress((() {
return _wmPtr!.address;
})());
+void createWalletPointer() {
+ final newWptr = monero.WalletManager_createWallet(wmPtr,
+ path: "", password: "", language: "", networkType: 0);
+
+ wptr = newWptr;
+}
+
void createWalletSync(
{required String path,
required String password,
@@ -124,24 +129,24 @@ void restoreWalletFromKeysSync(
int restoreHeight = 0}) {
txhistory = null;
var newWptr = (spendKey != "")
- ? monero.WalletManager_createDeterministicWalletFromSpendKey(
- wmPtr,
- path: path,
- password: password,
- language: language,
- spendKeyString: spendKey,
- newWallet: true, // TODO(mrcyjanek): safe to remove
- restoreHeight: restoreHeight)
- : monero.WalletManager_createWalletFromKeys(
- wmPtr,
- path: path,
- password: password,
- restoreHeight: restoreHeight,
- addressString: address,
- viewKeyString: viewKey,
- spendKeyString: spendKey,
- nettype: 0,
- );
+ ? monero.WalletManager_createDeterministicWalletFromSpendKey(wmPtr,
+ path: path,
+ password: password,
+ language: language,
+ spendKeyString: spendKey,
+ newWallet: true,
+ // TODO(mrcyjanek): safe to remove
+ restoreHeight: restoreHeight)
+ : monero.WalletManager_createWalletFromKeys(
+ wmPtr,
+ path: path,
+ password: password,
+ restoreHeight: restoreHeight,
+ addressString: address,
+ viewKeyString: viewKey,
+ spendKeyString: spendKey,
+ nettype: 0,
+ );
final status = monero.Wallet_status(newWptr);
if (status != 0) {
@@ -156,7 +161,7 @@ void restoreWalletFromKeysSync(
if (viewKey != viewKeyRestored && viewKey != "") {
monero.WalletManager_closeWallet(wmPtr, newWptr, false);
File(path).deleteSync();
- File(path+".keys").deleteSync();
+ File(path + ".keys").deleteSync();
newWptr = monero.WalletManager_createWalletFromKeys(
wmPtr,
path: path,
@@ -199,7 +204,7 @@ void restoreWalletFromSpendKeySync(
// viewKeyString: '',
// nettype: 0,
// );
-
+
txhistory = null;
final newWptr = monero.WalletManager_createDeterministicWalletFromSpendKey(
wmPtr,
@@ -230,41 +235,39 @@ void restoreWalletFromSpendKeySync(
String _lastOpenedWallet = "";
-// void restoreMoneroWalletFromDevice(
-// {required String path,
-// required String password,
-// required String deviceName,
-// int nettype = 0,
-// int restoreHeight = 0}) {
-//
-// final pathPointer = path.toNativeUtf8();
-// final passwordPointer = password.toNativeUtf8();
-// final deviceNamePointer = deviceName.toNativeUtf8();
-// final errorMessagePointer = ''.toNativeUtf8();
-//
-// final isWalletRestored = restoreWalletFromDeviceNative(
-// pathPointer,
-// passwordPointer,
-// deviceNamePointer,
-// nettype,
-// restoreHeight,
-// errorMessagePointer) != 0;
-//
-// calloc.free(pathPointer);
-// calloc.free(passwordPointer);
-//
-// storeSync();
-//
-// if (!isWalletRestored) {
-// throw WalletRestoreFromKeysException(
-// message: convertUTF8ToString(pointer: errorMessagePointer));
-// }
-// }
+Future restoreWalletFromHardwareWallet(
+ {required String path,
+ required String password,
+ required String deviceName,
+ int nettype = 0,
+ int restoreHeight = 0}) async {
+ txhistory = null;
+
+ final newWptrAddr = await Isolate.run(() {
+ return monero.WalletManager_createWalletFromDevice(wmPtr,
+ path: path,
+ password: password,
+ restoreHeight: restoreHeight,
+ deviceName: deviceName)
+ .address;
+ });
+ final newWptr = Pointer.fromAddress(newWptrAddr);
+
+ final status = monero.Wallet_status(newWptr);
+
+ if (status != 0) {
+ final error = monero.Wallet_errorString(newWptr);
+ throw WalletRestoreFromSeedException(message: error);
+ }
+ wptr = newWptr;
+
+ openedWalletsByPath[path] = wptr!;
+}
Map openedWalletsByPath = {};
-void loadWallet(
- {required String path, required String password, int nettype = 0}) {
+Future loadWallet(
+ {required String path, required String password, int nettype = 0}) async {
if (openedWalletsByPath[path] != null) {
txhistory = null;
wptr = openedWalletsByPath[path]!;
@@ -278,8 +281,29 @@ void loadWallet(
});
}
txhistory = null;
- final newWptr = monero.WalletManager_openWallet(wmPtr,
- path: path, password: password);
+
+ /// Get the device type
+ /// 0: Software Wallet
+ /// 1: Ledger
+ /// 2: Trezor
+ final deviceType = monero.WalletManager_queryWalletDevice(wmPtr,
+ keysFileName: "$path.keys", password: password, kdfRounds: 1);
+
+ if (deviceType == 1) {
+ final dummyWPtr = wptr ??
+ monero.WalletManager_openWallet(wmPtr, path: '', password: '');
+ enableLedgerExchange(dummyWPtr, gLedger!);
+ }
+
+ final addr = wmPtr.address;
+ final newWptrAddr = await Isolate.run(() {
+ return monero.WalletManager_openWallet(Pointer.fromAddress(addr),
+ path: path, password: password)
+ .address;
+ });
+
+ final newWptr = Pointer.fromAddress(newWptrAddr);
+
_lastOpenedWallet = path;
final status = monero.Wallet_status(newWptr);
if (status != 0) {
@@ -287,6 +311,7 @@ void loadWallet(
print(err);
throw WalletOpeningException(message: err);
}
+
wptr = newWptr;
openedWalletsByPath[path] = wptr!;
}
@@ -351,7 +376,7 @@ Future _openWallet(Map args) async => loadWallet(
bool _isWalletExist(String path) => isWalletExistSync(path: path);
-void openWallet(
+Future openWallet(
{required String path,
required String password,
int nettype = 0}) async =>
@@ -425,3 +450,5 @@ Future restoreFromSpendKey(
});
bool isWalletExist({required String path}) => _isWalletExist(path);
+
+bool isViewOnlyBySpendKey() => int.tryParse(monero.Wallet_secretSpendKey(wptr!)) == 0;
\ No newline at end of file
diff --git a/cw_monero/lib/ledger.dart b/cw_monero/lib/ledger.dart
new file mode 100644
index 000000000..c947d0944
--- /dev/null
+++ b/cw_monero/lib/ledger.dart
@@ -0,0 +1,84 @@
+import 'dart:async';
+import 'dart:ffi';
+import 'dart:typed_data';
+
+import 'package:ffi/ffi.dart';
+import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
+import 'package:ledger_flutter_plus/ledger_flutter_plus_dart.dart';
+import 'package:monero/monero.dart' as monero;
+// import 'package:polyseed/polyseed.dart';
+
+LedgerConnection? gLedger;
+
+Timer? _ledgerExchangeTimer;
+Timer? _ledgerKeepAlive;
+
+void enableLedgerExchange(monero.wallet ptr, LedgerConnection connection) {
+ _ledgerExchangeTimer?.cancel();
+ _ledgerExchangeTimer = Timer.periodic(Duration(milliseconds: 1), (_) async {
+ final ledgerRequestLength = monero.Wallet_getSendToDeviceLength(ptr);
+ final ledgerRequest = monero.Wallet_getSendToDevice(ptr)
+ .cast()
+ .asTypedList(ledgerRequestLength);
+ if (ledgerRequestLength > 0) {
+ _ledgerKeepAlive?.cancel();
+
+ final Pointer emptyPointer = malloc(0);
+ monero.Wallet_setDeviceSendData(
+ ptr, emptyPointer.cast(), 0);
+ malloc.free(emptyPointer);
+
+ // print("> ${ledgerRequest.toHexString()}");
+ final response = await exchange(connection, ledgerRequest);
+ // print("< ${response.toHexString()}");
+
+ final Pointer result = malloc(response.length);
+ for (var i = 0; i < response.length; i++) {
+ result.asTypedList(response.length)[i] = response[i];
+ }
+
+ monero.Wallet_setDeviceReceivedData(
+ ptr, result.cast(), response.length);
+ malloc.free(result);
+ keepAlive(connection);
+ }
+ });
+}
+
+void keepAlive(LedgerConnection connection) {
+ if (connection.connectionType == ConnectionType.ble) {
+ _ledgerKeepAlive = Timer.periodic(Duration(seconds: 10), (_) async {
+ try {
+ UniversalBle.setNotifiable(
+ connection.device.id,
+ connection.device.deviceInfo.serviceId,
+ connection.device.deviceInfo.notifyCharacteristicKey,
+ BleInputProperty.notification,
+ );
+ } catch (_) {}
+ });
+ }
+}
+
+void disableLedgerExchange() {
+ _ledgerExchangeTimer?.cancel();
+ _ledgerKeepAlive?.cancel();
+ gLedger?.disconnect();
+ gLedger = null;
+}
+
+Future exchange(LedgerConnection connection, Uint8List data) async =>
+ connection.sendOperation(ExchangeOperation(data));
+
+class ExchangeOperation extends LedgerRawOperation {
+ final Uint8List inputData;
+
+ ExchangeOperation(this.inputData);
+
+ @override
+ Future read(ByteDataReader reader) async =>
+ reader.read(reader.remainingLength);
+
+ @override
+ Future> write(ByteDataWriter writer) async => [inputData];
+}
diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart
index 0ae2202ba..5f53b30ba 100644
--- a/cw_monero/lib/monero_wallet.dart
+++ b/cw_monero/lib/monero_wallet.dart
@@ -19,6 +19,7 @@ import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
+import 'package:cw_monero/api/account_list.dart';
import 'package:cw_monero/api/coins_info.dart';
import 'package:cw_monero/api/monero_output.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart';
@@ -27,6 +28,7 @@ import 'package:cw_monero/api/wallet.dart' as monero_wallet;
import 'package:cw_monero/api/wallet_manager.dart';
import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart';
import 'package:cw_monero/exceptions/monero_transaction_no_inputs_exception.dart';
+import 'package:cw_monero/ledger.dart';
import 'package:cw_monero/monero_transaction_creation_credentials.dart';
import 'package:cw_monero/monero_transaction_history.dart';
import 'package:cw_monero/monero_transaction_info.dart';
@@ -35,6 +37,7 @@ import 'package:cw_monero/monero_wallet_addresses.dart';
import 'package:cw_monero/pending_monero_transaction.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
+import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
import 'package:mobx/mobx.dart';
import 'package:monero/monero.dart' as monero;
@@ -121,6 +124,7 @@ abstract class MoneroWalletBase extends WalletBase MoneroWalletKeys(
+ primaryAddress: monero_wallet.getAddress(accountIndex: 0, addressIndex: 0),
privateSpendKey: monero_wallet.getSecretSpendKey(),
privateViewKey: monero_wallet.getSecretViewKey(),
publicSpendKey: monero_wallet.getPublicSpendKey(),
@@ -230,6 +234,36 @@ abstract class MoneroWalletBase extends WalletBase submitTransactionUR(String ur) async {
+ final retStatus = monero.Wallet_submitTransactionUR(wptr!, ur);
+ final status = monero.Wallet_status(wptr!);
+ if (status != 0) {
+ final err = monero.Wallet_errorString(wptr!);
+ throw MoneroTransactionCreationException("unable to broadcast signed transaction: $err");
+ }
+ return retStatus;
+ }
+
+ bool importKeyImagesUR(String ur) {
+ final retStatus = monero.Wallet_importKeyImagesUR(wptr!, ur);
+ final status = monero.Wallet_status(wptr!);
+ if (status != 0) {
+ final err = monero.Wallet_errorString(wptr!);
+ throw Exception("unable to import key images: $err");
+ }
+ return retStatus;
+ }
+
+ String exportOutputsUR(bool all) {
+ final str = monero.Wallet_exportOutputsUR(wptr!, all: all);
+ final status = monero.Wallet_status(wptr!);
+ if (status != 0) {
+ final err = monero.Wallet_errorString(wptr!);
+ throw MoneroTransactionCreationException("unable to export UR: $err");
+ }
+ return str;
+ }
+
@override
Future createTransaction(Object credentials) async {
final _credentials = credentials as MoneroTransactionCreationCredentials;
@@ -796,4 +830,9 @@ abstract class MoneroWalletBase extends WalletBase {
+ MoneroRestoreWalletFromHardwareCredentials> {
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
final Box walletInfoSource;
@@ -81,7 +92,7 @@ class MoneroWalletService extends WalletService<
final lang = PolyseedLang.getByEnglishName(credentials.language);
final heightOverride =
- getMoneroHeigthByDate(date: DateTime.now().subtract(Duration(days: 2)));
+ getMoneroHeigthByDate(date: DateTime.now().subtract(Duration(days: 2)));
return _restoreFromPolyseed(
path, credentials.password!, polyseed, credentials.walletInfo!, lang,
@@ -91,9 +102,9 @@ class MoneroWalletService extends WalletService<
await monero_wallet_manager.createWallet(
path: path, password: credentials.password!, language: credentials.language);
final wallet = MoneroWallet(
- walletInfo: credentials.walletInfo!,
- unspentCoinsInfo: unspentCoinsInfoSource,
- password: credentials.password!);
+ walletInfo: credentials.walletInfo!,
+ unspentCoinsInfo: unspentCoinsInfoSource,
+ password: credentials.password!);
await wallet.init();
return wallet;
@@ -128,13 +139,18 @@ class MoneroWalletService extends WalletService<
await monero_wallet_manager
.openWalletAsync({'path': path, 'password': password});
final walletInfo = walletInfoSource.values.firstWhere(
- (info) => info.id == WalletBase.idFor(name, getType()));
+ (info) => info.id == WalletBase.idFor(name, getType()));
final wallet = MoneroWallet(
- walletInfo: walletInfo,
- unspentCoinsInfo: unspentCoinsInfoSource,
- password: password);
+ walletInfo: walletInfo,
+ unspentCoinsInfo: unspentCoinsInfoSource,
+ password: password);
final isValid = wallet.walletAddresses.validate();
+ if (wallet.isHardwareWallet) {
+ wallet.setLedgerConnection(gLedger!);
+ gLedger = null;
+ }
+
if (!isValid) {
await restoreOrResetWalletFiles(name);
wallet.close(shouldCleanup: false);
@@ -185,10 +201,9 @@ class MoneroWalletService extends WalletService<
}
@override
- Future rename(
- String currentName, String password, String newName) async {
+ Future rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhere(
- (info) => info.id == WalletBase.idFor(currentName, getType()));
+ (info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = MoneroWallet(
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
@@ -218,9 +233,9 @@ class MoneroWalletService extends WalletService<
viewKey: credentials.viewKey,
spendKey: credentials.spendKey);
final wallet = MoneroWallet(
- walletInfo: credentials.walletInfo!,
- unspentCoinsInfo: unspentCoinsInfoSource,
- password: credentials.password!);
+ walletInfo: credentials.walletInfo!,
+ unspentCoinsInfo: unspentCoinsInfoSource,
+ password: credentials.password!);
await wallet.init();
return wallet;
@@ -232,9 +247,34 @@ class MoneroWalletService extends WalletService<
}
@override
- Future restoreFromHardwareWallet(MoneroNewWalletCredentials credentials) {
- throw UnimplementedError(
- "Restoring a Monero wallet from a hardware wallet is not yet supported!");
+ Future restoreFromHardwareWallet(
+ MoneroRestoreWalletFromHardwareCredentials credentials) async {
+ try {
+ final path = await pathForWallet(name: credentials.name, type: getType());
+ final password = credentials.password;
+ final height = credentials.height;
+
+ if (wptr == null ) monero_wallet_manager.createWalletPointer();
+
+ enableLedgerExchange(wptr!, credentials.ledgerConnection);
+ await monero_wallet_manager.restoreWalletFromHardwareWallet(
+ path: path,
+ password: password!,
+ restoreHeight: height!,
+ deviceName: 'Ledger');
+
+ final wallet = MoneroWallet(
+ walletInfo: credentials.walletInfo!,
+ unspentCoinsInfo: unspentCoinsInfoSource,
+ password: credentials.password!);
+ await wallet.init();
+
+ return wallet;
+ } catch (e) {
+ // TODO: Implement Exception for wallet list service.
+ print('MoneroWalletsManager Error: $e');
+ rethrow;
+ }
}
@override
@@ -253,9 +293,9 @@ class MoneroWalletService extends WalletService<
seed: credentials.mnemonic,
restoreHeight: credentials.height!);
final wallet = MoneroWallet(
- walletInfo: credentials.walletInfo!,
- unspentCoinsInfo: unspentCoinsInfoSource,
- password: credentials.password!);
+ walletInfo: credentials.walletInfo!,
+ unspentCoinsInfo: unspentCoinsInfoSource,
+ password: credentials.password!);
await wallet.init();
return wallet;
@@ -283,8 +323,8 @@ class MoneroWalletService extends WalletService<
}
}
- Future _restoreFromPolyseed(
- String path, String password, Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
+ Future _restoreFromPolyseed(String path, String password, Polyseed polyseed,
+ WalletInfo walletInfo, PolyseedLang lang,
{PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async {
final height = overrideHeight ??
getMoneroHeigthByDate(date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
@@ -329,7 +369,9 @@ class MoneroWalletService extends WalletService<
dir.listSync().forEach((f) {
final file = File(f.path);
- final name = f.path.split('/').last;
+ final name = f.path
+ .split('/')
+ .last;
final newPath = newWalletDirPath + '/$name';
final newFile = File(newPath);
@@ -366,4 +408,11 @@ class MoneroWalletService extends WalletService<
return '';
}
}
+
+ @override
+ bool requireHardwareWalletConnection(String name) {
+ final walletInfo = walletInfoSource.values
+ .firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
+ return walletInfo.isHardwareWallet;
+ }
}
diff --git a/cw_monero/lib/pending_monero_transaction.dart b/cw_monero/lib/pending_monero_transaction.dart
index 2a75fc26b..eb714eeb3 100644
--- a/cw_monero/lib/pending_monero_transaction.dart
+++ b/cw_monero/lib/pending_monero_transaction.dart
@@ -1,3 +1,4 @@
+import 'package:cw_monero/api/account_list.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/transaction_history.dart'
as monero_transaction_history;
@@ -35,11 +36,32 @@ class PendingMoneroTransaction with PendingTransaction {
String get feeFormatted => AmountConverter.amountIntToString(
CryptoCurrency.xmr, pendingTransactionDescription.fee);
+ bool shouldCommitUR() => isViewOnly;
+
@override
Future commit() async {
try {
monero_transaction_history.commitTransactionFromPointerAddress(
- address: pendingTransactionDescription.pointerAddress);
+ address: pendingTransactionDescription.pointerAddress,
+ useUR: false);
+ } catch (e) {
+ final message = e.toString();
+
+ if (message.contains('Reason: double spend')) {
+ throw DoubleSpendException();
+ }
+
+ rethrow;
+ }
+ }
+
+ @override
+ Future commitUR() async {
+ try {
+ final ret = monero_transaction_history.commitTransactionFromPointerAddress(
+ address: pendingTransactionDescription.pointerAddress,
+ useUR: true);
+ return ret;
} catch (e) {
final message = e.toString();
diff --git a/cw_monero/pubspec.lock b/cw_monero/pubspec.lock
index ee1d48df1..31d44ac63 100644
--- a/cw_monero/pubspec.lock
+++ b/cw_monero/pubspec.lock
@@ -29,10 +29,10 @@ packages:
dependency: transitive
description:
name: asn1lib
- sha256: "58082b3f0dca697204dbab0ef9ff208bfaea7767ea771076af9a343488428dda"
+ sha256: "6b151826fcc95ff246cd219a0bf4c753ea14f4081ad71c61939becf3aba27f70"
url: "https://pub.dev"
source: hosted
- version: "1.5.3"
+ version: "1.5.5"
async:
dependency: transitive
description:
@@ -41,6 +41,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.11.0"
+ bluez:
+ dependency: transitive
+ description:
+ name: bluez
+ sha256: "203a1924e818a9dd74af2b2c7a8f375ab8e5edf0e486bba8f90a0d8a17ed9fce"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.8.2"
boolean_selector:
dependency: transitive
description:
@@ -209,6 +217,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.4"
+ dbus:
+ dependency: transitive
+ description:
+ name: dbus
+ sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.10"
encrypt:
dependency: "direct main"
description:
@@ -229,10 +245,10 @@ packages:
dependency: "direct main"
description:
name: ffi
- sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
+ sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
url: "https://pub.dev"
source: hosted
- version: "2.1.2"
+ version: "2.1.3"
file:
dependency: transitive
description:
@@ -267,6 +283,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ flutter_web_bluetooth:
+ dependency: transitive
+ description:
+ name: flutter_web_bluetooth
+ sha256: "52ce64f65d7321c4bf6abfe9dac02fb888731339a5e0ad6de59fb916c20c9f02"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.3"
frontend_server_client:
dependency: transitive
description:
@@ -295,18 +319,18 @@ packages:
dependency: transitive
description:
name: hashlib
- sha256: d41795742c10947930630118c6836608deeb9047cd05aee32d2baeb697afd66a
+ sha256: f572f2abce09fc7aee53f15927052b9732ea1053e540af8cae211111ee0b99b1
url: "https://pub.dev"
source: hosted
- version: "1.19.2"
+ version: "1.21.0"
hashlib_codecs:
dependency: transitive
description:
name: hashlib_codecs
- sha256: "2b570061f5a4b378425be28a576c1e11783450355ad4345a19f606ff3d96db0f"
+ sha256: "8cea9ccafcfeaa7324d2ae52c61c69f7ff71f4237507a018caab31b9e416e3b1"
url: "https://pub.dev"
source: hosted
- version: "2.5.0"
+ version: "2.6.0"
hive:
dependency: transitive
description:
@@ -327,10 +351,10 @@ packages:
dependency: "direct main"
description:
name: http
- sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
+ sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
url: "https://pub.dev"
source: hosted
- version: "1.2.1"
+ version: "1.2.2"
http_multi_server:
dependency: transitive
description:
@@ -403,6 +427,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.1"
+ ledger_flutter_plus:
+ dependency: "direct main"
+ description:
+ name: ledger_flutter_plus
+ sha256: c7b04008553193dbca7e17b430768eecc372a72b0ff3625b5e7fc5e5c8d3231b
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.1"
+ ledger_usb_plus:
+ dependency: transitive
+ description:
+ name: ledger_usb_plus
+ sha256: "21cc5d976cf7edb3518bd2a0c4164139cbb0817d2e4f2054707fc4edfdf9ce87"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.4"
logging:
dependency: transitive
description:
@@ -439,10 +479,10 @@ packages:
dependency: transitive
description:
name: mime
- sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
+ sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
url: "https://pub.dev"
source: hosted
- version: "1.0.5"
+ version: "1.0.6"
mobx:
dependency: "direct main"
description:
@@ -463,8 +503,8 @@ packages:
dependency: "direct main"
description:
path: "impls/monero.dart"
- ref: "6eb571ea498ed7b854934785f00fabfd0dadf75b"
- resolved-ref: "6eb571ea498ed7b854934785f00fabfd0dadf75b"
+ ref: d72c15f4339791a7bbdf17e9d827b7b56ca144e4
+ resolved-ref: d72c15f4339791a7bbdf17e9d827b7b56ca144e4
url: "https://github.com/mrcyjanek/monero_c"
source: git
version: "0.0.0"
@@ -504,10 +544,10 @@ packages:
dependency: "direct main"
description:
name: path_provider
- sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
+ sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
url: "https://pub.dev"
source: hosted
- version: "2.1.3"
+ version: "2.1.4"
path_provider_android:
dependency: transitive
description:
@@ -544,10 +584,18 @@ packages:
dependency: transitive
description:
name: path_provider_windows
- sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
+ sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.dev"
source: hosted
- version: "2.2.1"
+ version: "2.3.0"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.2"
platform:
dependency: transitive
description:
@@ -612,6 +660,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
+ rxdart:
+ dependency: transitive
+ description:
+ name: rxdart
+ sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.28.0"
shelf:
dependency: transitive
description:
@@ -737,6 +793,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.2"
+ universal_ble:
+ dependency: transitive
+ description:
+ name: universal_ble
+ sha256: "0dfbd6b64bff3ad61ed7a895c232530d9614e9b01ab261a74433a43267edb7f3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.12.0"
+ universal_platform:
+ dependency: transitive
+ description:
+ name: universal_platform
+ sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
unorm_dart:
dependency: transitive
description:
@@ -785,22 +857,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.4.5"
- win32:
- dependency: transitive
- description:
- name: win32
- sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
- url: "https://pub.dev"
- source: hosted
- version: "5.5.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
- sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
+ sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
url: "https://pub.dev"
source: hosted
- version: "1.0.4"
+ version: "1.1.0"
+ xml:
+ dependency: transitive
+ description:
+ name: xml
+ sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.5.0"
yaml:
dependency: transitive
description:
@@ -811,4 +883,4 @@ packages:
version: "3.1.2"
sdks:
dart: ">=3.3.0 <4.0.0"
- flutter: ">=3.16.6"
+ flutter: ">=3.19.0"
diff --git a/cw_monero/pubspec.yaml b/cw_monero/pubspec.yaml
index cb1f5519f..71cbfa243 100644
--- a/cw_monero/pubspec.yaml
+++ b/cw_monero/pubspec.yaml
@@ -25,9 +25,11 @@ dependencies:
monero:
git:
url: https://github.com/mrcyjanek/monero_c
- ref: 6eb571ea498ed7b854934785f00fabfd0dadf75b # monero_c hash
+ ref: d72c15f4339791a7bbdf17e9d827b7b56ca144e4
+# ref: 6eb571ea498ed7b854934785f00fabfd0dadf75b # monero_c hash
path: impls/monero.dart
mutex: ^3.1.0
+ ledger_flutter_plus: ^1.4.1
dev_dependencies:
flutter_test:
diff --git a/cw_nano/lib/pending_nano_transaction.dart b/cw_nano/lib/pending_nano_transaction.dart
index a027100fd..51a4ef6c1 100644
--- a/cw_nano/lib/pending_nano_transaction.dart
+++ b/cw_nano/lib/pending_nano_transaction.dart
@@ -37,4 +37,9 @@ class PendingNanoTransaction with PendingTransaction {
await nanoClient.processBlock(block, "send");
}
}
+
+ @override
+ Future commitUR() {
+ throw UnimplementedError();
+ }
}
diff --git a/cw_shared_external/pubspec.lock b/cw_shared_external/pubspec.lock
new file mode 100644
index 000000000..203ecd5c3
--- /dev/null
+++ b/cw_shared_external/pubspec.lock
@@ -0,0 +1,189 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ async:
+ dependency: transitive
+ description:
+ name: async
+ sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.11.0"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.18.0"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.1"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ leak_tracker:
+ dependency: transitive
+ description:
+ name: leak_tracker
+ sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "10.0.4"
+ leak_tracker_flutter_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_flutter_testing
+ sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.3"
+ leak_tracker_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_testing
+ sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.1"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.12.16+1"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.8.0"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.12.0"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.9.0"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.99"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.10.0"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.11.1"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.0"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.1"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.0"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
+ url: "https://pub.dev"
+ source: hosted
+ version: "14.2.1"
+sdks:
+ dart: ">=3.3.0 <4.0.0"
+ flutter: ">=3.18.0-18.0.pre.54"
diff --git a/cw_solana/lib/pending_solana_transaction.dart b/cw_solana/lib/pending_solana_transaction.dart
index 38347ed13..e01446000 100644
--- a/cw_solana/lib/pending_solana_transaction.dart
+++ b/cw_solana/lib/pending_solana_transaction.dart
@@ -40,4 +40,9 @@ class PendingSolanaTransaction with PendingTransaction {
@override
String get id => '';
+
+ @override
+ Future commitUR() {
+ throw UnimplementedError();
+ }
}
diff --git a/cw_tron/lib/pending_tron_transaction.dart b/cw_tron/lib/pending_tron_transaction.dart
index b6d064b31..2420f083b 100644
--- a/cw_tron/lib/pending_tron_transaction.dart
+++ b/cw_tron/lib/pending_tron_transaction.dart
@@ -30,4 +30,9 @@ class PendingTronTransaction with PendingTransaction {
@override
String get id => '';
+
+ @override
+ Future commitUR() {
+ throw UnimplementedError();
+ }
}
diff --git a/cw_wownero/lib/api/exceptions/connection_to_node_exception.dart b/cw_wownero/lib/api/exceptions/connection_to_node_exception.dart
new file mode 100644
index 000000000..483b0a174
--- /dev/null
+++ b/cw_wownero/lib/api/exceptions/connection_to_node_exception.dart
@@ -0,0 +1,5 @@
+class ConnectionToNodeException implements Exception {
+ ConnectionToNodeException({required this.message});
+
+ final String message;
+}
\ No newline at end of file
diff --git a/cw_wownero/lib/api/exceptions/wallet_restore_from_keys_exception.dart b/cw_wownero/lib/api/exceptions/wallet_restore_from_keys_exception.dart
index ad576faa2..6c461ee4c 100644
--- a/cw_wownero/lib/api/exceptions/wallet_restore_from_keys_exception.dart
+++ b/cw_wownero/lib/api/exceptions/wallet_restore_from_keys_exception.dart
@@ -3,5 +3,6 @@ class WalletRestoreFromKeysException implements Exception {
final String message;
+ @override
String toString() => message;
}
\ No newline at end of file
diff --git a/cw_wownero/lib/api/structs/account_row.dart b/cw_wownero/lib/api/structs/account_row.dart
new file mode 100644
index 000000000..aa492ee0f
--- /dev/null
+++ b/cw_wownero/lib/api/structs/account_row.dart
@@ -0,0 +1,12 @@
+import 'dart:ffi';
+import 'package:ffi/ffi.dart';
+
+class AccountRow extends Struct {
+ @Int64()
+ external int id;
+
+ external Pointer label;
+
+ String getLabel() => label.toDartString();
+ int getId() => id;
+}
diff --git a/cw_wownero/lib/api/structs/coins_info_row.dart b/cw_wownero/lib/api/structs/coins_info_row.dart
new file mode 100644
index 000000000..ff6f6ce73
--- /dev/null
+++ b/cw_wownero/lib/api/structs/coins_info_row.dart
@@ -0,0 +1,73 @@
+import 'dart:ffi';
+import 'package:ffi/ffi.dart';
+
+class CoinsInfoRow extends Struct {
+ @Int64()
+ external int blockHeight;
+
+ external Pointer hash;
+
+ @Uint64()
+ external int internalOutputIndex;
+
+ @Uint64()
+ external int globalOutputIndex;
+
+ @Int8()
+ external int spent;
+
+ @Int8()
+ external int frozen;
+
+ @Uint64()
+ external int spentHeight;
+
+ @Uint64()
+ external int amount;
+
+ @Int8()
+ external int rct;
+
+ @Int8()
+ external int keyImageKnown;
+
+ @Uint64()
+ external int pkIndex;
+
+ @Uint32()
+ external int subaddrIndex;
+
+ @Uint32()
+ external int subaddrAccount;
+
+ external Pointer address;
+
+ external Pointer addressLabel;
+
+ external Pointer keyImage;
+
+ @Uint64()
+ external int unlockTime;
+
+ @Int8()
+ external int unlocked;
+
+ external Pointer pubKey;
+
+ @Int8()
+ external int coinbase;
+
+ external Pointer description;
+
+ String getHash() => hash.toDartString();
+
+ String getAddress() => address.toDartString();
+
+ String getAddressLabel() => addressLabel.toDartString();
+
+ String getKeyImage() => keyImage.toDartString();
+
+ String getPubKey() => pubKey.toDartString();
+
+ String getDescription() => description.toDartString();
+}
diff --git a/cw_wownero/lib/api/structs/subaddress_row.dart b/cw_wownero/lib/api/structs/subaddress_row.dart
new file mode 100644
index 000000000..d593a793d
--- /dev/null
+++ b/cw_wownero/lib/api/structs/subaddress_row.dart
@@ -0,0 +1,15 @@
+import 'dart:ffi';
+import 'package:ffi/ffi.dart';
+
+class SubaddressRow extends Struct {
+ @Int64()
+ external int id;
+
+ external Pointer address;
+
+ external Pointer label;
+
+ String getLabel() => label.toDartString();
+ String getAddress() => address.toDartString();
+ int getId() => id;
+}
\ No newline at end of file
diff --git a/cw_wownero/lib/api/structs/transaction_info_row.dart b/cw_wownero/lib/api/structs/transaction_info_row.dart
new file mode 100644
index 000000000..bdcc64d3f
--- /dev/null
+++ b/cw_wownero/lib/api/structs/transaction_info_row.dart
@@ -0,0 +1,41 @@
+import 'dart:ffi';
+import 'package:ffi/ffi.dart';
+
+class TransactionInfoRow extends Struct {
+ @Uint64()
+ external int amount;
+
+ @Uint64()
+ external int fee;
+
+ @Uint64()
+ external int blockHeight;
+
+ @Uint64()
+ external int confirmations;
+
+ @Uint32()
+ external int subaddrAccount;
+
+ @Int8()
+ external int direction;
+
+ @Int8()
+ external int isPending;
+
+ @Uint32()
+ external int subaddrIndex;
+
+ external Pointer hash;
+
+ external Pointer paymentId;
+
+ @Int64()
+ external int datetime;
+
+ int getDatetime() => datetime;
+ int getAmount() => amount >= 0 ? amount : amount * -1;
+ bool getIsPending() => isPending != 0;
+ String getHash() => hash.toDartString();
+ String getPaymentId() => paymentId.toDartString();
+}
diff --git a/cw_wownero/lib/api/structs/ut8_box.dart b/cw_wownero/lib/api/structs/ut8_box.dart
new file mode 100644
index 000000000..53e678c88
--- /dev/null
+++ b/cw_wownero/lib/api/structs/ut8_box.dart
@@ -0,0 +1,8 @@
+import 'dart:ffi';
+import 'package:ffi/ffi.dart';
+
+class Utf8Box extends Struct {
+ external Pointer value;
+
+ String getValue() => value.toDartString();
+}
diff --git a/cw_wownero/lib/cw_wownero.dart b/cw_wownero/lib/cw_wownero.dart
new file mode 100644
index 000000000..33a55e305
--- /dev/null
+++ b/cw_wownero/lib/cw_wownero.dart
@@ -0,0 +1,8 @@
+
+import 'cw_wownero_platform_interface.dart';
+
+class CwWownero {
+ Future getPlatformVersion() {
+ return CwWowneroPlatform.instance.getPlatformVersion();
+ }
+}
diff --git a/cw_wownero/lib/cw_wownero_method_channel.dart b/cw_wownero/lib/cw_wownero_method_channel.dart
new file mode 100644
index 000000000..d797f5f81
--- /dev/null
+++ b/cw_wownero/lib/cw_wownero_method_channel.dart
@@ -0,0 +1,17 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+
+import 'cw_wownero_platform_interface.dart';
+
+/// An implementation of [CwWowneroPlatform] that uses method channels.
+class MethodChannelCwWownero extends CwWowneroPlatform {
+ /// The method channel used to interact with the native platform.
+ @visibleForTesting
+ final methodChannel = const MethodChannel('cw_wownero');
+
+ @override
+ Future getPlatformVersion() async {
+ final version = await methodChannel.invokeMethod('getPlatformVersion');
+ return version;
+ }
+}
diff --git a/cw_wownero/lib/cw_wownero_platform_interface.dart b/cw_wownero/lib/cw_wownero_platform_interface.dart
new file mode 100644
index 000000000..78b21592c
--- /dev/null
+++ b/cw_wownero/lib/cw_wownero_platform_interface.dart
@@ -0,0 +1,29 @@
+import 'package:plugin_platform_interface/plugin_platform_interface.dart';
+
+import 'cw_wownero_method_channel.dart';
+
+abstract class CwWowneroPlatform extends PlatformInterface {
+ /// Constructs a CwWowneroPlatform.
+ CwWowneroPlatform() : super(token: _token);
+
+ static final Object _token = Object();
+
+ static CwWowneroPlatform _instance = MethodChannelCwWownero();
+
+ /// The default instance of [CwWowneroPlatform] to use.
+ ///
+ /// Defaults to [MethodChannelCwWownero].
+ static CwWowneroPlatform get instance => _instance;
+
+ /// Platform-specific implementations should set this with their own
+ /// platform-specific class that extends [CwWowneroPlatform] when
+ /// they register themselves.
+ static set instance(CwWowneroPlatform instance) {
+ PlatformInterface.verifyToken(instance, _token);
+ _instance = instance;
+ }
+
+ Future getPlatformVersion() {
+ throw UnimplementedError('platformVersion() has not been implemented.');
+ }
+}
diff --git a/cw_wownero/lib/mywownero.dart b/cw_wownero/lib/mywownero.dart
new file mode 100644
index 000000000..d50e48b64
--- /dev/null
+++ b/cw_wownero/lib/mywownero.dart
@@ -0,0 +1,1689 @@
+const prefixLength = 3;
+
+String swapEndianBytes(String original) {
+ if (original.length != 8) {
+ return '';
+ }
+
+ return original[6] +
+ original[7] +
+ original[4] +
+ original[5] +
+ original[2] +
+ original[3] +
+ original[0] +
+ original[1];
+}
+
+List tructWords(List wordSet) {
+ final start = 0;
+ final end = prefixLength;
+
+ return wordSet.map((word) => word.substring(start, end)).toList();
+}
+
+String mnemonicDecode(String seed) {
+ final n = englistWordSet.length;
+ var out = '';
+ var wlist = seed.split(' ');
+ wlist.removeLast();
+
+ for (var i = 0; i < wlist.length; i += 3) {
+ final w1 =
+ tructWords(englistWordSet).indexOf(wlist[i].substring(0, prefixLength));
+ final w2 = tructWords(englistWordSet)
+ .indexOf(wlist[i + 1].substring(0, prefixLength));
+ final w3 = tructWords(englistWordSet)
+ .indexOf(wlist[i + 2].substring(0, prefixLength));
+
+ if (w1 == -1 || w2 == -1 || w3 == -1) {
+ print("invalid word in mnemonic");
+ return '';
+ }
+
+ final x = w1 + n * (((n - w1) + w2) % n) + n * n * (((n - w2) + w3) % n);
+
+ if (x % n != w1) {
+ print("Something went wrong when decoding your private key, please try again");
+ return '';
+ }
+
+ final _res = '0000000' + x.toRadixString(16);
+ final start = _res.length - 8;
+ final end = _res.length;
+ final res = _res.substring(start, end);
+
+ out += swapEndianBytes(res);
+ }
+
+ return out;
+}
+
+final englistWordSet = [
+ "abbey",
+ "abducts",
+ "ability",
+ "ablaze",
+ "abnormal",
+ "abort",
+ "abrasive",
+ "absorb",
+ "abyss",
+ "academy",
+ "aces",
+ "aching",
+ "acidic",
+ "acoustic",
+ "acquire",
+ "across",
+ "actress",
+ "acumen",
+ "adapt",
+ "addicted",
+ "adept",
+ "adhesive",
+ "adjust",
+ "adopt",
+ "adrenalin",
+ "adult",
+ "adventure",
+ "aerial",
+ "afar",
+ "affair",
+ "afield",
+ "afloat",
+ "afoot",
+ "afraid",
+ "after",
+ "against",
+ "agenda",
+ "aggravate",
+ "agile",
+ "aglow",
+ "agnostic",
+ "agony",
+ "agreed",
+ "ahead",
+ "aided",
+ "ailments",
+ "aimless",
+ "airport",
+ "aisle",
+ "ajar",
+ "akin",
+ "alarms",
+ "album",
+ "alchemy",
+ "alerts",
+ "algebra",
+ "alkaline",
+ "alley",
+ "almost",
+ "aloof",
+ "alpine",
+ "already",
+ "also",
+ "altitude",
+ "alumni",
+ "always",
+ "amaze",
+ "ambush",
+ "amended",
+ "amidst",
+ "ammo",
+ "amnesty",
+ "among",
+ "amply",
+ "amused",
+ "anchor",
+ "android",
+ "anecdote",
+ "angled",
+ "ankle",
+ "annoyed",
+ "answers",
+ "antics",
+ "anvil",
+ "anxiety",
+ "anybody",
+ "apart",
+ "apex",
+ "aphid",
+ "aplomb",
+ "apology",
+ "apply",
+ "apricot",
+ "aptitude",
+ "aquarium",
+ "arbitrary",
+ "archer",
+ "ardent",
+ "arena",
+ "argue",
+ "arises",
+ "army",
+ "around",
+ "arrow",
+ "arsenic",
+ "artistic",
+ "ascend",
+ "ashtray",
+ "aside",
+ "asked",
+ "asleep",
+ "aspire",
+ "assorted",
+ "asylum",
+ "athlete",
+ "atlas",
+ "atom",
+ "atrium",
+ "attire",
+ "auburn",
+ "auctions",
+ "audio",
+ "august",
+ "aunt",
+ "austere",
+ "autumn",
+ "avatar",
+ "avidly",
+ "avoid",
+ "awakened",
+ "awesome",
+ "awful",
+ "awkward",
+ "awning",
+ "awoken",
+ "axes",
+ "axis",
+ "axle",
+ "aztec",
+ "azure",
+ "baby",
+ "bacon",
+ "badge",
+ "baffles",
+ "bagpipe",
+ "bailed",
+ "bakery",
+ "balding",
+ "bamboo",
+ "banjo",
+ "baptism",
+ "basin",
+ "batch",
+ "bawled",
+ "bays",
+ "because",
+ "beer",
+ "befit",
+ "begun",
+ "behind",
+ "being",
+ "below",
+ "bemused",
+ "benches",
+ "berries",
+ "bested",
+ "betting",
+ "bevel",
+ "beware",
+ "beyond",
+ "bias",
+ "bicycle",
+ "bids",
+ "bifocals",
+ "biggest",
+ "bikini",
+ "bimonthly",
+ "binocular",
+ "biology",
+ "biplane",
+ "birth",
+ "biscuit",
+ "bite",
+ "biweekly",
+ "blender",
+ "blip",
+ "bluntly",
+ "boat",
+ "bobsled",
+ "bodies",
+ "bogeys",
+ "boil",
+ "boldly",
+ "bomb",
+ "border",
+ "boss",
+ "both",
+ "bounced",
+ "bovine",
+ "bowling",
+ "boxes",
+ "boyfriend",
+ "broken",
+ "brunt",
+ "bubble",
+ "buckets",
+ "budget",
+ "buffet",
+ "bugs",
+ "building",
+ "bulb",
+ "bumper",
+ "bunch",
+ "business",
+ "butter",
+ "buying",
+ "buzzer",
+ "bygones",
+ "byline",
+ "bypass",
+ "cabin",
+ "cactus",
+ "cadets",
+ "cafe",
+ "cage",
+ "cajun",
+ "cake",
+ "calamity",
+ "camp",
+ "candy",
+ "casket",
+ "catch",
+ "cause",
+ "cavernous",
+ "cease",
+ "cedar",
+ "ceiling",
+ "cell",
+ "cement",
+ "cent",
+ "certain",
+ "chlorine",
+ "chrome",
+ "cider",
+ "cigar",
+ "cinema",
+ "circle",
+ "cistern",
+ "citadel",
+ "civilian",
+ "claim",
+ "click",
+ "clue",
+ "coal",
+ "cobra",
+ "cocoa",
+ "code",
+ "coexist",
+ "coffee",
+ "cogs",
+ "cohesive",
+ "coils",
+ "colony",
+ "comb",
+ "cool",
+ "copy",
+ "corrode",
+ "costume",
+ "cottage",
+ "cousin",
+ "cowl",
+ "criminal",
+ "cube",
+ "cucumber",
+ "cuddled",
+ "cuffs",
+ "cuisine",
+ "cunning",
+ "cupcake",
+ "custom",
+ "cycling",
+ "cylinder",
+ "cynical",
+ "dabbing",
+ "dads",
+ "daft",
+ "dagger",
+ "daily",
+ "damp",
+ "dangerous",
+ "dapper",
+ "darted",
+ "dash",
+ "dating",
+ "dauntless",
+ "dawn",
+ "daytime",
+ "dazed",
+ "debut",
+ "decay",
+ "dedicated",
+ "deepest",
+ "deftly",
+ "degrees",
+ "dehydrate",
+ "deity",
+ "dejected",
+ "delayed",
+ "demonstrate",
+ "dented",
+ "deodorant",
+ "depth",
+ "desk",
+ "devoid",
+ "dewdrop",
+ "dexterity",
+ "dialect",
+ "dice",
+ "diet",
+ "different",
+ "digit",
+ "dilute",
+ "dime",
+ "dinner",
+ "diode",
+ "diplomat",
+ "directed",
+ "distance",
+ "ditch",
+ "divers",
+ "dizzy",
+ "doctor",
+ "dodge",
+ "does",
+ "dogs",
+ "doing",
+ "dolphin",
+ "domestic",
+ "donuts",
+ "doorway",
+ "dormant",
+ "dosage",
+ "dotted",
+ "double",
+ "dove",
+ "down",
+ "dozen",
+ "dreams",
+ "drinks",
+ "drowning",
+ "drunk",
+ "drying",
+ "dual",
+ "dubbed",
+ "duckling",
+ "dude",
+ "duets",
+ "duke",
+ "dullness",
+ "dummy",
+ "dunes",
+ "duplex",
+ "duration",
+ "dusted",
+ "duties",
+ "dwarf",
+ "dwelt",
+ "dwindling",
+ "dying",
+ "dynamite",
+ "dyslexic",
+ "each",
+ "eagle",
+ "earth",
+ "easy",
+ "eating",
+ "eavesdrop",
+ "eccentric",
+ "echo",
+ "eclipse",
+ "economics",
+ "ecstatic",
+ "eden",
+ "edgy",
+ "edited",
+ "educated",
+ "eels",
+ "efficient",
+ "eggs",
+ "egotistic",
+ "eight",
+ "either",
+ "eject",
+ "elapse",
+ "elbow",
+ "eldest",
+ "eleven",
+ "elite",
+ "elope",
+ "else",
+ "eluded",
+ "emails",
+ "ember",
+ "emerge",
+ "emit",
+ "emotion",
+ "empty",
+ "emulate",
+ "energy",
+ "enforce",
+ "enhanced",
+ "enigma",
+ "enjoy",
+ "enlist",
+ "enmity",
+ "enough",
+ "enraged",
+ "ensign",
+ "entrance",
+ "envy",
+ "epoxy",
+ "equip",
+ "erase",
+ "erected",
+ "erosion",
+ "error",
+ "eskimos",
+ "espionage",
+ "essential",
+ "estate",
+ "etched",
+ "eternal",
+ "ethics",
+ "etiquette",
+ "evaluate",
+ "evenings",
+ "evicted",
+ "evolved",
+ "examine",
+ "excess",
+ "exhale",
+ "exit",
+ "exotic",
+ "exquisite",
+ "extra",
+ "exult",
+ "fabrics",
+ "factual",
+ "fading",
+ "fainted",
+ "faked",
+ "fall",
+ "family",
+ "fancy",
+ "farming",
+ "fatal",
+ "faulty",
+ "fawns",
+ "faxed",
+ "fazed",
+ "feast",
+ "february",
+ "federal",
+ "feel",
+ "feline",
+ "females",
+ "fences",
+ "ferry",
+ "festival",
+ "fetches",
+ "fever",
+ "fewest",
+ "fiat",
+ "fibula",
+ "fictional",
+ "fidget",
+ "fierce",
+ "fifteen",
+ "fight",
+ "films",
+ "firm",
+ "fishing",
+ "fitting",
+ "five",
+ "fixate",
+ "fizzle",
+ "fleet",
+ "flippant",
+ "flying",
+ "foamy",
+ "focus",
+ "foes",
+ "foggy",
+ "foiled",
+ "folding",
+ "fonts",
+ "foolish",
+ "fossil",
+ "fountain",
+ "fowls",
+ "foxes",
+ "foyer",
+ "framed",
+ "friendly",
+ "frown",
+ "fruit",
+ "frying",
+ "fudge",
+ "fuel",
+ "fugitive",
+ "fully",
+ "fuming",
+ "fungal",
+ "furnished",
+ "fuselage",
+ "future",
+ "fuzzy",
+ "gables",
+ "gadget",
+ "gags",
+ "gained",
+ "galaxy",
+ "gambit",
+ "gang",
+ "gasp",
+ "gather",
+ "gauze",
+ "gave",
+ "gawk",
+ "gaze",
+ "gearbox",
+ "gecko",
+ "geek",
+ "gels",
+ "gemstone",
+ "general",
+ "geometry",
+ "germs",
+ "gesture",
+ "getting",
+ "geyser",
+ "ghetto",
+ "ghost",
+ "giant",
+ "giddy",
+ "gifts",
+ "gigantic",
+ "gills",
+ "gimmick",
+ "ginger",
+ "girth",
+ "giving",
+ "glass",
+ "gleeful",
+ "glide",
+ "gnaw",
+ "gnome",
+ "goat",
+ "goblet",
+ "godfather",
+ "goes",
+ "goggles",
+ "going",
+ "goldfish",
+ "gone",
+ "goodbye",
+ "gopher",
+ "gorilla",
+ "gossip",
+ "gotten",
+ "gourmet",
+ "governing",
+ "gown",
+ "greater",
+ "grunt",
+ "guarded",
+ "guest",
+ "guide",
+ "gulp",
+ "gumball",
+ "guru",
+ "gusts",
+ "gutter",
+ "guys",
+ "gymnast",
+ "gypsy",
+ "gyrate",
+ "habitat",
+ "hacksaw",
+ "haggled",
+ "hairy",
+ "hamburger",
+ "happens",
+ "hashing",
+ "hatchet",
+ "haunted",
+ "having",
+ "hawk",
+ "haystack",
+ "hazard",
+ "hectare",
+ "hedgehog",
+ "heels",
+ "hefty",
+ "height",
+ "hemlock",
+ "hence",
+ "heron",
+ "hesitate",
+ "hexagon",
+ "hickory",
+ "hiding",
+ "highway",
+ "hijack",
+ "hiker",
+ "hills",
+ "himself",
+ "hinder",
+ "hippo",
+ "hire",
+ "history",
+ "hitched",
+ "hive",
+ "hoax",
+ "hobby",
+ "hockey",
+ "hoisting",
+ "hold",
+ "honked",
+ "hookup",
+ "hope",
+ "hornet",
+ "hospital",
+ "hotel",
+ "hounded",
+ "hover",
+ "howls",
+ "hubcaps",
+ "huddle",
+ "huge",
+ "hull",
+ "humid",
+ "hunter",
+ "hurried",
+ "husband",
+ "huts",
+ "hybrid",
+ "hydrogen",
+ "hyper",
+ "iceberg",
+ "icing",
+ "icon",
+ "identity",
+ "idiom",
+ "idled",
+ "idols",
+ "igloo",
+ "ignore",
+ "iguana",
+ "illness",
+ "imagine",
+ "imbalance",
+ "imitate",
+ "impel",
+ "inactive",
+ "inbound",
+ "incur",
+ "industrial",
+ "inexact",
+ "inflamed",
+ "ingested",
+ "initiate",
+ "injury",
+ "inkling",
+ "inline",
+ "inmate",
+ "innocent",
+ "inorganic",
+ "input",
+ "inquest",
+ "inroads",
+ "insult",
+ "intended",
+ "inundate",
+ "invoke",
+ "inwardly",
+ "ionic",
+ "irate",
+ "iris",
+ "irony",
+ "irritate",
+ "island",
+ "isolated",
+ "issued",
+ "italics",
+ "itches",
+ "items",
+ "itinerary",
+ "itself",
+ "ivory",
+ "jabbed",
+ "jackets",
+ "jaded",
+ "jagged",
+ "jailed",
+ "jamming",
+ "january",
+ "jargon",
+ "jaunt",
+ "javelin",
+ "jaws",
+ "jazz",
+ "jeans",
+ "jeers",
+ "jellyfish",
+ "jeopardy",
+ "jerseys",
+ "jester",
+ "jetting",
+ "jewels",
+ "jigsaw",
+ "jingle",
+ "jittery",
+ "jive",
+ "jobs",
+ "jockey",
+ "jogger",
+ "joining",
+ "joking",
+ "jolted",
+ "jostle",
+ "journal",
+ "joyous",
+ "jubilee",
+ "judge",
+ "juggled",
+ "juicy",
+ "jukebox",
+ "july",
+ "jump",
+ "junk",
+ "jury",
+ "justice",
+ "juvenile",
+ "kangaroo",
+ "karate",
+ "keep",
+ "kennel",
+ "kept",
+ "kernels",
+ "kettle",
+ "keyboard",
+ "kickoff",
+ "kidneys",
+ "king",
+ "kiosk",
+ "kisses",
+ "kitchens",
+ "kiwi",
+ "knapsack",
+ "knee",
+ "knife",
+ "knowledge",
+ "knuckle",
+ "koala",
+ "laboratory",
+ "ladder",
+ "lagoon",
+ "lair",
+ "lakes",
+ "lamb",
+ "language",
+ "laptop",
+ "large",
+ "last",
+ "later",
+ "launching",
+ "lava",
+ "lawsuit",
+ "layout",
+ "lazy",
+ "lectures",
+ "ledge",
+ "leech",
+ "left",
+ "legion",
+ "leisure",
+ "lemon",
+ "lending",
+ "leopard",
+ "lesson",
+ "lettuce",
+ "lexicon",
+ "liar",
+ "library",
+ "licks",
+ "lids",
+ "lied",
+ "lifestyle",
+ "light",
+ "likewise",
+ "lilac",
+ "limits",
+ "linen",
+ "lion",
+ "lipstick",
+ "liquid",
+ "listen",
+ "lively",
+ "loaded",
+ "lobster",
+ "locker",
+ "lodge",
+ "lofty",
+ "logic",
+ "loincloth",
+ "long",
+ "looking",
+ "lopped",
+ "lordship",
+ "losing",
+ "lottery",
+ "loudly",
+ "love",
+ "lower",
+ "loyal",
+ "lucky",
+ "luggage",
+ "lukewarm",
+ "lullaby",
+ "lumber",
+ "lunar",
+ "lurk",
+ "lush",
+ "luxury",
+ "lymph",
+ "lynx",
+ "lyrics",
+ "macro",
+ "madness",
+ "magically",
+ "mailed",
+ "major",
+ "makeup",
+ "malady",
+ "mammal",
+ "maps",
+ "masterful",
+ "match",
+ "maul",
+ "maverick",
+ "maximum",
+ "mayor",
+ "maze",
+ "meant",
+ "mechanic",
+ "medicate",
+ "meeting",
+ "megabyte",
+ "melting",
+ "memoir",
+ "menu",
+ "merger",
+ "mesh",
+ "metro",
+ "mews",
+ "mice",
+ "midst",
+ "mighty",
+ "mime",
+ "mirror",
+ "misery",
+ "mittens",
+ "mixture",
+ "moat",
+ "mobile",
+ "mocked",
+ "mohawk",
+ "moisture",
+ "molten",
+ "moment",
+ "money",
+ "moon",
+ "mops",
+ "morsel",
+ "mostly",
+ "motherly",
+ "mouth",
+ "movement",
+ "mowing",
+ "much",
+ "muddy",
+ "muffin",
+ "mugged",
+ "mullet",
+ "mumble",
+ "mundane",
+ "muppet",
+ "mural",
+ "musical",
+ "muzzle",
+ "myriad",
+ "mystery",
+ "myth",
+ "nabbing",
+ "nagged",
+ "nail",
+ "names",
+ "nanny",
+ "napkin",
+ "narrate",
+ "nasty",
+ "natural",
+ "nautical",
+ "navy",
+ "nearby",
+ "necklace",
+ "needed",
+ "negative",
+ "neither",
+ "neon",
+ "nephew",
+ "nerves",
+ "nestle",
+ "network",
+ "neutral",
+ "never",
+ "newt",
+ "nexus",
+ "nibs",
+ "niche",
+ "niece",
+ "nifty",
+ "nightly",
+ "nimbly",
+ "nineteen",
+ "nirvana",
+ "nitrogen",
+ "nobody",
+ "nocturnal",
+ "nodes",
+ "noises",
+ "nomad",
+ "noodles",
+ "northern",
+ "nostril",
+ "noted",
+ "nouns",
+ "novelty",
+ "nowhere",
+ "nozzle",
+ "nuance",
+ "nucleus",
+ "nudged",
+ "nugget",
+ "nuisance",
+ "null",
+ "number",
+ "nuns",
+ "nurse",
+ "nutshell",
+ "nylon",
+ "oaks",
+ "oars",
+ "oasis",
+ "oatmeal",
+ "obedient",
+ "object",
+ "obliged",
+ "obnoxious",
+ "observant",
+ "obtains",
+ "obvious",
+ "occur",
+ "ocean",
+ "october",
+ "odds",
+ "odometer",
+ "offend",
+ "often",
+ "oilfield",
+ "ointment",
+ "okay",
+ "older",
+ "olive",
+ "olympics",
+ "omega",
+ "omission",
+ "omnibus",
+ "onboard",
+ "oncoming",
+ "oneself",
+ "ongoing",
+ "onion",
+ "online",
+ "onslaught",
+ "onto",
+ "onward",
+ "oozed",
+ "opacity",
+ "opened",
+ "opposite",
+ "optical",
+ "opus",
+ "orange",
+ "orbit",
+ "orchid",
+ "orders",
+ "organs",
+ "origin",
+ "ornament",
+ "orphans",
+ "oscar",
+ "ostrich",
+ "otherwise",
+ "otter",
+ "ouch",
+ "ought",
+ "ounce",
+ "ourselves",
+ "oust",
+ "outbreak",
+ "oval",
+ "oven",
+ "owed",
+ "owls",
+ "owner",
+ "oxidant",
+ "oxygen",
+ "oyster",
+ "ozone",
+ "pact",
+ "paddles",
+ "pager",
+ "pairing",
+ "palace",
+ "pamphlet",
+ "pancakes",
+ "paper",
+ "paradise",
+ "pastry",
+ "patio",
+ "pause",
+ "pavements",
+ "pawnshop",
+ "payment",
+ "peaches",
+ "pebbles",
+ "peculiar",
+ "pedantic",
+ "peeled",
+ "pegs",
+ "pelican",
+ "pencil",
+ "people",
+ "pepper",
+ "perfect",
+ "pests",
+ "petals",
+ "phase",
+ "pheasants",
+ "phone",
+ "phrases",
+ "physics",
+ "piano",
+ "picked",
+ "pierce",
+ "pigment",
+ "piloted",
+ "pimple",
+ "pinched",
+ "pioneer",
+ "pipeline",
+ "pirate",
+ "pistons",
+ "pitched",
+ "pivot",
+ "pixels",
+ "pizza",
+ "playful",
+ "pledge",
+ "pliers",
+ "plotting",
+ "plus",
+ "plywood",
+ "poaching",
+ "pockets",
+ "podcast",
+ "poetry",
+ "point",
+ "poker",
+ "polar",
+ "ponies",
+ "pool",
+ "popular",
+ "portents",
+ "possible",
+ "potato",
+ "pouch",
+ "poverty",
+ "powder",
+ "pram",
+ "present",
+ "pride",
+ "problems",
+ "pruned",
+ "prying",
+ "psychic",
+ "public",
+ "puck",
+ "puddle",
+ "puffin",
+ "pulp",
+ "pumpkins",
+ "punch",
+ "puppy",
+ "purged",
+ "push",
+ "putty",
+ "puzzled",
+ "pylons",
+ "pyramid",
+ "python",
+ "queen",
+ "quick",
+ "quote",
+ "rabbits",
+ "racetrack",
+ "radar",
+ "rafts",
+ "rage",
+ "railway",
+ "raking",
+ "rally",
+ "ramped",
+ "randomly",
+ "rapid",
+ "rarest",
+ "rash",
+ "rated",
+ "ravine",
+ "rays",
+ "razor",
+ "react",
+ "rebel",
+ "recipe",
+ "reduce",
+ "reef",
+ "refer",
+ "regular",
+ "reheat",
+ "reinvest",
+ "rejoices",
+ "rekindle",
+ "relic",
+ "remedy",
+ "renting",
+ "reorder",
+ "repent",
+ "request",
+ "reruns",
+ "rest",
+ "return",
+ "reunion",
+ "revamp",
+ "rewind",
+ "rhino",
+ "rhythm",
+ "ribbon",
+ "richly",
+ "ridges",
+ "rift",
+ "rigid",
+ "rims",
+ "ringing",
+ "riots",
+ "ripped",
+ "rising",
+ "ritual",
+ "river",
+ "roared",
+ "robot",
+ "rockets",
+ "rodent",
+ "rogue",
+ "roles",
+ "romance",
+ "roomy",
+ "roped",
+ "roster",
+ "rotate",
+ "rounded",
+ "rover",
+ "rowboat",
+ "royal",
+ "ruby",
+ "rudely",
+ "ruffled",
+ "rugged",
+ "ruined",
+ "ruling",
+ "rumble",
+ "runway",
+ "rural",
+ "rustled",
+ "ruthless",
+ "sabotage",
+ "sack",
+ "sadness",
+ "safety",
+ "saga",
+ "sailor",
+ "sake",
+ "salads",
+ "sample",
+ "sanity",
+ "sapling",
+ "sarcasm",
+ "sash",
+ "satin",
+ "saucepan",
+ "saved",
+ "sawmill",
+ "saxophone",
+ "sayings",
+ "scamper",
+ "scenic",
+ "school",
+ "science",
+ "scoop",
+ "scrub",
+ "scuba",
+ "seasons",
+ "second",
+ "sedan",
+ "seeded",
+ "segments",
+ "seismic",
+ "selfish",
+ "semifinal",
+ "sensible",
+ "september",
+ "sequence",
+ "serving",
+ "session",
+ "setup",
+ "seventh",
+ "sewage",
+ "shackles",
+ "shelter",
+ "shipped",
+ "shocking",
+ "shrugged",
+ "shuffled",
+ "shyness",
+ "siblings",
+ "sickness",
+ "sidekick",
+ "sieve",
+ "sifting",
+ "sighting",
+ "silk",
+ "simplest",
+ "sincerely",
+ "sipped",
+ "siren",
+ "situated",
+ "sixteen",
+ "sizes",
+ "skater",
+ "skew",
+ "skirting",
+ "skulls",
+ "skydive",
+ "slackens",
+ "sleepless",
+ "slid",
+ "slower",
+ "slug",
+ "smash",
+ "smelting",
+ "smidgen",
+ "smog",
+ "smuggled",
+ "snake",
+ "sneeze",
+ "sniff",
+ "snout",
+ "snug",
+ "soapy",
+ "sober",
+ "soccer",
+ "soda",
+ "software",
+ "soggy",
+ "soil",
+ "solved",
+ "somewhere",
+ "sonic",
+ "soothe",
+ "soprano",
+ "sorry",
+ "southern",
+ "sovereign",
+ "sowed",
+ "soya",
+ "space",
+ "speedy",
+ "sphere",
+ "spiders",
+ "splendid",
+ "spout",
+ "sprig",
+ "spud",
+ "spying",
+ "square",
+ "stacking",
+ "stellar",
+ "stick",
+ "stockpile",
+ "strained",
+ "stunning",
+ "stylishly",
+ "subtly",
+ "succeed",
+ "suddenly",
+ "suede",
+ "suffice",
+ "sugar",
+ "suitcase",
+ "sulking",
+ "summon",
+ "sunken",
+ "superior",
+ "surfer",
+ "sushi",
+ "suture",
+ "swagger",
+ "swept",
+ "swiftly",
+ "sword",
+ "swung",
+ "syllabus",
+ "symptoms",
+ "syndrome",
+ "syringe",
+ "system",
+ "taboo",
+ "tacit",
+ "tadpoles",
+ "tagged",
+ "tail",
+ "taken",
+ "talent",
+ "tamper",
+ "tanks",
+ "tapestry",
+ "tarnished",
+ "tasked",
+ "tattoo",
+ "taunts",
+ "tavern",
+ "tawny",
+ "taxi",
+ "teardrop",
+ "technical",
+ "tedious",
+ "teeming",
+ "tell",
+ "template",
+ "tender",
+ "tepid",
+ "tequila",
+ "terminal",
+ "testing",
+ "tether",
+ "textbook",
+ "thaw",
+ "theatrics",
+ "thirsty",
+ "thorn",
+ "threaten",
+ "thumbs",
+ "thwart",
+ "ticket",
+ "tidy",
+ "tiers",
+ "tiger",
+ "tilt",
+ "timber",
+ "tinted",
+ "tipsy",
+ "tirade",
+ "tissue",
+ "titans",
+ "toaster",
+ "tobacco",
+ "today",
+ "toenail",
+ "toffee",
+ "together",
+ "toilet",
+ "token",
+ "tolerant",
+ "tomorrow",
+ "tonic",
+ "toolbox",
+ "topic",
+ "torch",
+ "tossed",
+ "total",
+ "touchy",
+ "towel",
+ "toxic",
+ "toyed",
+ "trash",
+ "trendy",
+ "tribal",
+ "trolling",
+ "truth",
+ "trying",
+ "tsunami",
+ "tubes",
+ "tucks",
+ "tudor",
+ "tuesday",
+ "tufts",
+ "tugs",
+ "tuition",
+ "tulips",
+ "tumbling",
+ "tunnel",
+ "turnip",
+ "tusks",
+ "tutor",
+ "tuxedo",
+ "twang",
+ "tweezers",
+ "twice",
+ "twofold",
+ "tycoon",
+ "typist",
+ "tyrant",
+ "ugly",
+ "ulcers",
+ "ultimate",
+ "umbrella",
+ "umpire",
+ "unafraid",
+ "unbending",
+ "uncle",
+ "under",
+ "uneven",
+ "unfit",
+ "ungainly",
+ "unhappy",
+ "union",
+ "unjustly",
+ "unknown",
+ "unlikely",
+ "unmask",
+ "unnoticed",
+ "unopened",
+ "unplugs",
+ "unquoted",
+ "unrest",
+ "unsafe",
+ "until",
+ "unusual",
+ "unveil",
+ "unwind",
+ "unzip",
+ "upbeat",
+ "upcoming",
+ "update",
+ "upgrade",
+ "uphill",
+ "upkeep",
+ "upload",
+ "upon",
+ "upper",
+ "upright",
+ "upstairs",
+ "uptight",
+ "upwards",
+ "urban",
+ "urchins",
+ "urgent",
+ "usage",
+ "useful",
+ "usher",
+ "using",
+ "usual",
+ "utensils",
+ "utility",
+ "utmost",
+ "utopia",
+ "uttered",
+ "vacation",
+ "vague",
+ "vain",
+ "value",
+ "vampire",
+ "vane",
+ "vapidly",
+ "vary",
+ "vastness",
+ "vats",
+ "vaults",
+ "vector",
+ "veered",
+ "vegan",
+ "vehicle",
+ "vein",
+ "velvet",
+ "venomous",
+ "verification",
+ "vessel",
+ "veteran",
+ "vexed",
+ "vials",
+ "vibrate",
+ "victim",
+ "video",
+ "viewpoint",
+ "vigilant",
+ "viking",
+ "village",
+ "vinegar",
+ "violin",
+ "vipers",
+ "virtual",
+ "visited",
+ "vitals",
+ "vivid",
+ "vixen",
+ "vocal",
+ "vogue",
+ "voice",
+ "volcano",
+ "vortex",
+ "voted",
+ "voucher",
+ "vowels",
+ "voyage",
+ "vulture",
+ "wade",
+ "waffle",
+ "wagtail",
+ "waist",
+ "waking",
+ "wallets",
+ "wanted",
+ "warped",
+ "washing",
+ "water",
+ "waveform",
+ "waxing",
+ "wayside",
+ "weavers",
+ "website",
+ "wedge",
+ "weekday",
+ "weird",
+ "welders",
+ "went",
+ "wept",
+ "were",
+ "western",
+ "wetsuit",
+ "whale",
+ "when",
+ "whipped",
+ "whole",
+ "wickets",
+ "width",
+ "wield",
+ "wife",
+ "wiggle",
+ "wildly",
+ "winter",
+ "wipeout",
+ "wiring",
+ "wise",
+ "withdrawn",
+ "wives",
+ "wizard",
+ "wobbly",
+ "woes",
+ "woken",
+ "wolf",
+ "womanly",
+ "wonders",
+ "woozy",
+ "worry",
+ "wounded",
+ "woven",
+ "wrap",
+ "wrist",
+ "wrong",
+ "yacht",
+ "yahoo",
+ "yanks",
+ "yard",
+ "yawning",
+ "yearbook",
+ "yellow",
+ "yesterday",
+ "yeti",
+ "yields",
+ "yodel",
+ "yoga",
+ "younger",
+ "yoyo",
+ "zapped",
+ "zeal",
+ "zebra",
+ "zero",
+ "zesty",
+ "zigzags",
+ "zinger",
+ "zippers",
+ "zodiac",
+ "zombie",
+ "zones",
+ "zoom"
+];
diff --git a/cw_wownero/lib/pending_wownero_transaction.dart b/cw_wownero/lib/pending_wownero_transaction.dart
index 1fc1805eb..967f63756 100644
--- a/cw_wownero/lib/pending_wownero_transaction.dart
+++ b/cw_wownero/lib/pending_wownero_transaction.dart
@@ -50,4 +50,9 @@ class PendingWowneroTransaction with PendingTransaction {
rethrow;
}
}
+
+ @override
+ Future commitUR() {
+ throw UnimplementedError();
+ }
}
diff --git a/cw_wownero/lib/wownero_wallet.dart b/cw_wownero/lib/wownero_wallet.dart
index 331957d67..5927d6434 100644
--- a/cw_wownero/lib/wownero_wallet.dart
+++ b/cw_wownero/lib/wownero_wallet.dart
@@ -120,6 +120,7 @@ abstract class WowneroWalletBase
@override
MoneroWalletKeys get keys => MoneroWalletKeys(
+ primaryAddress: wownero_wallet.getAddress(accountIndex: 0, addressIndex: 0),
privateSpendKey: wownero_wallet.getSecretSpendKey(),
privateViewKey: wownero_wallet.getSecretViewKey(),
publicSpendKey: wownero_wallet.getPublicSpendKey(),
diff --git a/cw_wownero/pubspec.lock b/cw_wownero/pubspec.lock
index c90340800..a861c6bac 100644
--- a/cw_wownero/pubspec.lock
+++ b/cw_wownero/pubspec.lock
@@ -463,8 +463,8 @@ packages:
dependency: "direct main"
description:
path: "impls/monero.dart"
- ref: "6eb571ea498ed7b854934785f00fabfd0dadf75b"
- resolved-ref: "6eb571ea498ed7b854934785f00fabfd0dadf75b"
+ ref: d72c15f4339791a7bbdf17e9d827b7b56ca144e4
+ resolved-ref: d72c15f4339791a7bbdf17e9d827b7b56ca144e4
url: "https://github.com/mrcyjanek/monero_c"
source: git
version: "0.0.0"
@@ -552,10 +552,10 @@ packages:
dependency: transitive
description:
name: plugin_platform_interface
- sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
- version: "2.1.3"
+ version: "2.1.8"
pointycastle:
dependency: transitive
description:
diff --git a/cw_wownero/pubspec.yaml b/cw_wownero/pubspec.yaml
index 6943e60c3..28ece82ef 100644
--- a/cw_wownero/pubspec.yaml
+++ b/cw_wownero/pubspec.yaml
@@ -25,7 +25,8 @@ dependencies:
monero:
git:
url: https://github.com/mrcyjanek/monero_c
- ref: 6eb571ea498ed7b854934785f00fabfd0dadf75b # monero_c hash
+ ref: d72c15f4339791a7bbdf17e9d827b7b56ca144e4
+# ref: 6eb571ea498ed7b854934785f00fabfd0dadf75b # monero_c hash
path: impls/monero.dart
mutex: ^3.1.0
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 470bdf4e7..abcac01fb 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1,8 +1,4 @@
PODS:
- - barcode_scan2 (0.0.1):
- - Flutter
- - MTBBarcodeScanner
- - SwiftProtobuf
- connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
@@ -76,6 +72,8 @@ PODS:
- DKPhotoGallery/Resource (0.0.19):
- SDWebImage
- SwiftyGif
+ - fast_scanner (5.1.1):
+ - Flutter
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
@@ -100,7 +98,6 @@ PODS:
- Flutter
- integration_test (0.0.1):
- Flutter
- - MTBBarcodeScanner (5.0.11)
- OrderedSet (5.0.0)
- package_info_plus (0.4.5):
- Flutter
@@ -109,12 +106,7 @@ PODS:
- FlutterMacOS
- permission_handler_apple (9.1.1):
- Flutter
- - Protobuf (3.28.2)
- ReachabilitySwift (5.2.3)
- - reactive_ble_mobile (0.0.1):
- - Flutter
- - Protobuf (~> 3.5)
- - SwiftProtobuf (~> 1.0)
- SDWebImage (5.19.7):
- SDWebImage/Core (= 5.19.7)
- SDWebImage/Core (5.19.7)
@@ -127,11 +119,13 @@ PODS:
- FlutterMacOS
- sp_scanner (0.0.1):
- Flutter
- - SwiftProtobuf (1.27.1)
- SwiftyGif (5.4.5)
- Toast (4.1.1)
- uni_links (0.0.1):
- Flutter
+ - universal_ble (0.0.1):
+ - Flutter
+ - FlutterMacOS
- url_launcher_ios (0.0.1):
- Flutter
- wakelock_plus (0.0.1):
@@ -140,7 +134,6 @@ PODS:
- Flutter
DEPENDENCIES:
- - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- CryptoSwift
- cw_haven (from `.symlinks/plugins/cw_haven/ios`)
@@ -149,6 +142,7 @@ DEPENDENCIES:
- device_display_brightness (from `.symlinks/plugins/device_display_brightness/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
+ - fast_scanner (from `.symlinks/plugins/fast_scanner/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
@@ -161,12 +155,12 @@ DEPENDENCIES:
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- - reactive_ble_mobile (from `.symlinks/plugins/reactive_ble_mobile/ios`)
- sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sp_scanner (from `.symlinks/plugins/sp_scanner/ios`)
- uni_links (from `.symlinks/plugins/uni_links/ios`)
+ - universal_ble (from `.symlinks/plugins/universal_ble/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
- workmanager (from `.symlinks/plugins/workmanager/ios`)
@@ -176,18 +170,13 @@ SPEC REPOS:
- CryptoSwift
- DKImagePickerController
- DKPhotoGallery
- - MTBBarcodeScanner
- OrderedSet
- - Protobuf
- ReachabilitySwift
- SDWebImage
- - SwiftProtobuf
- SwiftyGif
- Toast
EXTERNAL SOURCES:
- barcode_scan2:
- :path: ".symlinks/plugins/barcode_scan2/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
cw_haven:
@@ -202,6 +191,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/device_info_plus/ios"
devicelocale:
:path: ".symlinks/plugins/devicelocale/ios"
+ fast_scanner:
+ :path: ".symlinks/plugins/fast_scanner/ios"
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
Flutter:
@@ -226,8 +217,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
- reactive_ble_mobile:
- :path: ".symlinks/plugins/reactive_ble_mobile/ios"
sensitive_clipboard:
:path: ".symlinks/plugins/sensitive_clipboard/ios"
share_plus:
@@ -238,6 +227,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/sp_scanner/ios"
uni_links:
:path: ".symlinks/plugins/uni_links/ios"
+ universal_ble:
+ :path: ".symlinks/plugins/universal_ble/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
wakelock_plus:
@@ -246,7 +237,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/workmanager/ios"
SPEC CHECKSUMS:
- barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
CryptoSwift: c63a805d8bb5e5538e88af4e44bb537776af11ea
cw_haven: b3e54e1fbe7b8e6fda57a93206bc38f8e89b898a
@@ -257,6 +247,7 @@ SPEC CHECKSUMS:
devicelocale: b22617f40038496deffba44747101255cee005b0
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
+ fast_scanner: 44c00940355a51258cd6c2085734193cd23d95bc
file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0
@@ -266,23 +257,20 @@ SPEC CHECKSUMS:
fluttertoast: 48c57db1b71b0ce9e6bba9f31c940ff4b001293c
in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d
integration_test: 13825b8a9334a850581300559b8839134b124670
- MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
- Protobuf: 28c89b24435762f60244e691544ed80f50d82701
ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979
- reactive_ble_mobile: 9ce6723d37ccf701dbffd202d487f23f5de03b4c
SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3
sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12
- SwiftProtobuf: b109bd17979d7993a84da14b1e1fdd8b0ded934a
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
+ universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 09c75feee..85d39167a 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -230,6 +230,7 @@
97C146EC1CF9000F007C117D /* Resources */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
F6F67323547956BC4F7B67F1 /* [CP] Embed Pods Frameworks */,
+ 777FE2B16F25A3E820834145 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -343,6 +344,23 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n";
};
+ 777FE2B16F25A3E820834145 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
diff --git a/lib/buy/buy_provider.dart b/lib/buy/buy_provider.dart
index 1a37e09b3..8e79e16b8 100644
--- a/lib/buy/buy_provider.dart
+++ b/lib/buy/buy_provider.dart
@@ -1,6 +1,10 @@
import 'package:cake_wallet/buy/buy_amount.dart';
+import 'package:cake_wallet/buy/buy_quote.dart';
import 'package:cake_wallet/buy/order.dart';
+import 'package:cake_wallet/buy/payment_method.dart';
+import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
+import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
@@ -23,14 +27,38 @@ abstract class BuyProvider {
String get darkIcon;
+ bool get isAggregator;
+
@override
String toString() => title;
- Future launchProvider(BuildContext context, bool? isBuyAction);
+ Future? launchProvider(
+ {required BuildContext context,
+ required Quote quote,
+ required double amount,
+ required bool isBuyAction,
+ required String cryptoCurrencyAddress,
+ String? countryCode}) =>
+ null;
Future requestUrl(String amount, String sourceCurrency) => throw UnimplementedError();
Future findOrderById(String id) => throw UnimplementedError();
- Future calculateAmount(String amount, String sourceCurrency) => throw UnimplementedError();
+ Future calculateAmount(String amount, String sourceCurrency) =>
+ throw UnimplementedError();
+
+ Future> getAvailablePaymentTypes(
+ String fiatCurrency, String cryptoCurrency, bool isBuyAction) async =>
+ [];
+
+ Future?> fetchQuote(
+ {required CryptoCurrency cryptoCurrency,
+ required FiatCurrency fiatCurrency,
+ required double amount,
+ required bool isBuyAction,
+ required String walletAddress,
+ PaymentType? paymentType,
+ String? countryCode}) async =>
+ null;
}
diff --git a/lib/buy/buy_quote.dart b/lib/buy/buy_quote.dart
new file mode 100644
index 000000000..72ab7bd7d
--- /dev/null
+++ b/lib/buy/buy_quote.dart
@@ -0,0 +1,302 @@
+import 'package:cake_wallet/buy/buy_provider.dart';
+import 'package:cake_wallet/buy/payment_method.dart';
+import 'package:cake_wallet/core/selectable_option.dart';
+import 'package:cake_wallet/entities/fiat_currency.dart';
+import 'package:cake_wallet/entities/provider_types.dart';
+import 'package:cake_wallet/exchange/limits.dart';
+import 'package:cw_core/crypto_currency.dart';
+
+enum ProviderRecommendation { bestRate, lowKyc, successRate }
+
+extension RecommendationTitle on ProviderRecommendation {
+ String get title {
+ switch (this) {
+ case ProviderRecommendation.bestRate:
+ return 'BEST RATE';
+ case ProviderRecommendation.lowKyc:
+ return 'LOW KYC';
+ case ProviderRecommendation.successRate:
+ return 'SUCCESS RATE';
+ }
+ }
+}
+
+ProviderRecommendation? getRecommendationFromString(String title) {
+ switch (title) {
+ case 'BEST RATE':
+ return ProviderRecommendation.bestRate;
+ case 'LowKyc':
+ return ProviderRecommendation.lowKyc;
+ case 'SuccessRate':
+ return ProviderRecommendation.successRate;
+ default:
+ return null;
+ }
+}
+
+class Quote extends SelectableOption {
+ Quote({
+ required this.rate,
+ required this.feeAmount,
+ required this.networkFee,
+ required this.transactionFee,
+ required this.payout,
+ required this.provider,
+ required this.paymentType,
+ required this.recommendations,
+ this.isBuyAction = true,
+ this.quoteId,
+ this.rampId,
+ this.rampName,
+ this.rampIconPath,
+ this.limits,
+ }) : super(title: provider.isAggregator ? rampName ?? '' : provider.title);
+
+ final double rate;
+ final double feeAmount;
+ final double networkFee;
+ final double transactionFee;
+ final double payout;
+ final PaymentType paymentType;
+ final BuyProvider provider;
+ final String? quoteId;
+ final List recommendations;
+ String? rampId;
+ String? rampName;
+ String? rampIconPath;
+ bool _isSelected = false;
+ bool _isBestRate = false;
+ bool isBuyAction;
+ Limits? limits;
+
+ late FiatCurrency _fiatCurrency;
+ late CryptoCurrency _cryptoCurrency;
+
+
+ bool get isSelected => _isSelected;
+ bool get isBestRate => _isBestRate;
+ FiatCurrency get fiatCurrency => _fiatCurrency;
+ CryptoCurrency get cryptoCurrency => _cryptoCurrency;
+
+ @override
+ bool get isOptionSelected => this._isSelected;
+
+ @override
+ String get lightIconPath =>
+ provider.isAggregator ? rampIconPath ?? provider.lightIcon : provider.lightIcon;
+
+ @override
+ String get darkIconPath =>
+ provider.isAggregator ? rampIconPath ?? provider.darkIcon : provider.darkIcon;
+
+ @override
+ List get badges => recommendations.map((e) => e.title).toList();
+
+ @override
+ String get topLeftSubTitle =>
+ this.rate > 0 ? '1 $cryptoName = ${rate.toStringAsFixed(2)} $fiatName' : '';
+
+ @override
+ String get bottomLeftSubTitle {
+ if (limits != null) {
+ final min = limits!.min;
+ final max = limits!.max;
+ return 'min: ${min} ${fiatCurrency.toString()} | max: ${max == double.infinity ? '' : '${max} ${fiatCurrency.toString()}'}';
+ }
+ return '';
+ }
+
+ String get fiatName => isBuyAction ? fiatCurrency.toString() : cryptoCurrency.toString();
+
+ String get cryptoName => isBuyAction ? cryptoCurrency.toString() : fiatCurrency.toString();
+
+ @override
+ String? get topRightSubTitle => '';
+
+ @override
+ String get topRightSubTitleLightIconPath => provider.isAggregator ? provider.lightIcon : '';
+
+ @override
+ String get topRightSubTitleDarkIconPath => provider.isAggregator ? provider.darkIcon : '';
+
+ String get quoteTitle => '${provider.title} - ${paymentType.name}';
+
+ String get formatedFee => '$feeAmount ${isBuyAction ? fiatCurrency : cryptoCurrency}';
+
+ set setIsSelected(bool isSelected) => _isSelected = isSelected;
+ set setIsBestRate(bool isBestRate) => _isBestRate = isBestRate;
+ set setFiatCurrency(FiatCurrency fiatCurrency) => _fiatCurrency = fiatCurrency;
+ set setCryptoCurrency(CryptoCurrency cryptoCurrency) => _cryptoCurrency = cryptoCurrency;
+ set setLimits(Limits limits) => this.limits = limits;
+
+ factory Quote.fromOnramperJson(Map json, bool isBuyAction,
+ Map metaData, PaymentType paymentType) {
+ final rate = _toDouble(json['rate']) ?? 0.0;
+ final networkFee = _toDouble(json['networkFee']) ?? 0.0;
+ final transactionFee = _toDouble(json['transactionFee']) ?? 0.0;
+ final feeAmount = double.parse((networkFee + transactionFee).toStringAsFixed(2));
+
+ final rampId = json['ramp'] as String? ?? '';
+ final rampData = metaData[rampId] ?? {};
+ final rampName = rampData['displayName'] as String? ?? '';
+ final rampIconPath = rampData['svg'] as String? ?? '';
+
+ final recommendations = json['recommendations'] != null
+ ? List.from(json['recommendations'] as List)
+ : [];
+
+ final enumRecommendations = recommendations
+ .map((e) => getRecommendationFromString(e))
+ .whereType()
+ .toList();
+
+ final availablePaymentMethods = json['availablePaymentMethods'] as List? ?? [];
+ double minLimit = 0.0;
+ double maxLimit = double.infinity;
+
+ for (var paymentMethod in availablePaymentMethods) {
+ if (paymentMethod is Map) {
+ final details = paymentMethod['details'] as Map?;
+
+ if (details != null) {
+ final limits = details['limits'] as Map?;
+
+ if (limits != null && limits.isNotEmpty) {
+ final firstLimitEntry = limits.values.first as Map?;
+ if (firstLimitEntry != null) {
+ minLimit = _toDouble(firstLimitEntry['min'])?.roundToDouble() ?? 0.0;
+ maxLimit = _toDouble(firstLimitEntry['max'])?.roundToDouble() ?? double.infinity;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return Quote(
+ rate: rate,
+ feeAmount: feeAmount,
+ networkFee: networkFee,
+ transactionFee: transactionFee,
+ payout: json['payout'] as double? ?? 0.0,
+ rampId: rampId,
+ rampName: rampName,
+ rampIconPath: rampIconPath,
+ paymentType: paymentType,
+ quoteId: json['quoteId'] as String? ?? '',
+ recommendations: enumRecommendations,
+ provider: ProvidersHelper.getProviderByType(ProviderType.onramper)!,
+ isBuyAction: isBuyAction,
+ limits: Limits(min: minLimit, max: maxLimit),
+ );
+ }
+
+ factory Quote.fromMoonPayJson(
+ Map json, bool isBuyAction, PaymentType paymentType) {
+ final rate = isBuyAction
+ ? json['quoteCurrencyPrice'] as double? ?? 0.0
+ : json['baseCurrencyPrice'] as double? ?? 0.0;
+ final fee = _toDouble(json['feeAmount']) ?? 0.0;
+ final networkFee = _toDouble(json['networkFeeAmount']) ?? 0.0;
+ final transactionFee = _toDouble(json['extraFeeAmount']) ?? 0.0;
+ final feeAmount = double.parse((fee + networkFee + transactionFee).toStringAsFixed(2));
+
+ final baseCurrency = json['baseCurrency'] as Map?;
+
+ double minLimit = 0.0;
+ double maxLimit = double.infinity;
+
+ if (baseCurrency != null) {
+ minLimit = _toDouble(baseCurrency['minAmount']) ?? minLimit;
+ maxLimit = _toDouble(baseCurrency['maxAmount']) ?? maxLimit;
+ }
+
+ return Quote(
+ rate: rate,
+ feeAmount: feeAmount,
+ networkFee: networkFee,
+ transactionFee: transactionFee,
+ payout: _toDouble(json['quoteCurrencyAmount']) ?? 0.0,
+ paymentType: paymentType,
+ recommendations: [],
+ quoteId: json['signature'] as String? ?? '',
+ provider: ProvidersHelper.getProviderByType(ProviderType.moonpay)!,
+ isBuyAction: isBuyAction,
+ limits: Limits(min: minLimit, max: maxLimit),
+ );
+ }
+
+ factory Quote.fromDFXJson(
+ Map json,
+ bool isBuyAction,
+ PaymentType paymentType,
+ ) {
+ final rate = _toDouble(json['exchangeRate']) ?? 0.0;
+ final fees = json['fees'] as Map;
+
+ final minVolume = _toDouble(json['minVolume']) ?? 0.0;
+ final maxVolume = _toDouble(json['maxVolume']) ?? double.infinity;
+
+ return Quote(
+ rate: isBuyAction ? rate : 1 / rate,
+ feeAmount: _toDouble(json['feeAmount']) ?? 0.0,
+ networkFee: _toDouble(fees['network']) ?? 0.0,
+ transactionFee: _toDouble(fees['rate']) ?? 0.0,
+ payout: _toDouble(json['payout']) ?? 0.0,
+ paymentType: paymentType,
+ recommendations: [ProviderRecommendation.lowKyc],
+ provider: ProvidersHelper.getProviderByType(ProviderType.dfx)!,
+ isBuyAction: isBuyAction,
+ limits: Limits(min: minVolume, max: maxVolume),
+ );
+ }
+
+ factory Quote.fromRobinhoodJson(
+ Map json, bool isBuyAction, PaymentType paymentType) {
+ final networkFee = json['networkFee'] as Map;
+ final processingFee = json['processingFee'] as Map;
+ final networkFeeAmount = _toDouble(networkFee['fiatAmount']) ?? 0.0;
+ final transactionFeeAmount = _toDouble(processingFee['fiatAmount']) ?? 0.0;
+ final feeAmount = double.parse((networkFeeAmount + transactionFeeAmount).toStringAsFixed(2));
+
+ return Quote(
+ rate: _toDouble(json['price']) ?? 0.0,
+ feeAmount: feeAmount,
+ networkFee: _toDouble(networkFee['fiatAmount']) ?? 0.0,
+ transactionFee: _toDouble(processingFee['fiatAmount']) ?? 0.0,
+ payout: _toDouble(json['cryptoAmount']) ?? 0.0,
+ paymentType: paymentType,
+ recommendations: [],
+ provider: ProvidersHelper.getProviderByType(ProviderType.robinhood)!,
+ isBuyAction: isBuyAction,
+ limits: Limits(min: 0.0, max: double.infinity),
+ );
+ }
+
+ factory Quote.fromMeldJson(Map json, bool isBuyAction, PaymentType paymentType) {
+ final quotes = json['quotes'][0] as Map;
+ return Quote(
+ rate: quotes['exchangeRate'] as double? ?? 0.0,
+ feeAmount: quotes['totalFee'] as double? ?? 0.0,
+ networkFee: quotes['networkFee'] as double? ?? 0.0,
+ transactionFee: quotes['transactionFee'] as double? ?? 0.0,
+ payout: quotes['payout'] as double? ?? 0.0,
+ paymentType: paymentType,
+ recommendations: [],
+ provider: ProvidersHelper.getProviderByType(ProviderType.meld)!,
+ isBuyAction: isBuyAction,
+ limits: Limits(min: 0.0, max: double.infinity),
+ );
+ }
+
+ static double? _toDouble(dynamic value) {
+ if (value is int) {
+ return value.toDouble();
+ } else if (value is double) {
+ return value;
+ } else if (value is String) {
+ return double.tryParse(value);
+ }
+ return null;
+ }
+}
diff --git a/lib/buy/dfx/dfx_buy_provider.dart b/lib/buy/dfx/dfx_buy_provider.dart
index b3ed72498..c1ed762b1 100644
--- a/lib/buy/dfx/dfx_buy_provider.dart
+++ b/lib/buy/dfx/dfx_buy_provider.dart
@@ -1,13 +1,17 @@
import 'dart:convert';
+import 'dart:developer';
import 'package:cake_wallet/buy/buy_provider.dart';
+import 'package:cake_wallet/buy/buy_quote.dart';
+import 'package:cake_wallet/buy/payment_method.dart';
+import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
-import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
+import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
@@ -15,10 +19,12 @@ import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class DFXBuyProvider extends BuyProvider {
- DFXBuyProvider({required WalletBase wallet, bool isTestEnvironment = false, LedgerViewModel? ledgerVM})
+ DFXBuyProvider(
+ {required WalletBase wallet, bool isTestEnvironment = false, LedgerViewModel? ledgerVM})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: ledgerVM);
static const _baseUrl = 'api.dfx.swiss';
+
// static const _signMessagePath = '/v1/auth/signMessage';
static const _authPath = '/v1/auth';
static const walletName = 'CakeWallet';
@@ -35,24 +41,8 @@ class DFXBuyProvider extends BuyProvider {
@override
String get darkIcon => 'assets/images/dfx_dark.png';
- String get assetOut {
- switch (wallet.type) {
- case WalletType.bitcoin:
- return 'BTC';
- case WalletType.bitcoinCash:
- return 'BCH';
- case WalletType.litecoin:
- return 'LTC';
- case WalletType.monero:
- return 'XMR';
- case WalletType.ethereum:
- return 'ETH';
- case WalletType.polygon:
- return 'MATIC';
- default:
- throw Exception("WalletType is not available for DFX ${wallet.type}");
- }
- }
+ @override
+ bool get isAggregator => false;
String get blockchain {
switch (wallet.type) {
@@ -60,21 +50,13 @@ class DFXBuyProvider extends BuyProvider {
case WalletType.bitcoinCash:
case WalletType.litecoin:
return 'Bitcoin';
- case WalletType.monero:
- return 'Monero';
- case WalletType.ethereum:
- return 'Ethereum';
- case WalletType.polygon:
- return 'Polygon';
default:
- throw Exception("WalletType is not available for DFX ${wallet.type}");
+ return walletTypeToString(wallet.type);
}
}
- String get walletAddress =>
- wallet.walletAddresses.primaryAddress ?? wallet.walletAddresses.address;
- Future getSignMessage() async =>
+ Future getSignMessage(String walletAddress) async =>
"By_signing_this_message,_you_confirm_that_you_are_the_sole_owner_of_the_provided_Blockchain_address._Your_ID:_$walletAddress";
// // Lets keep this just in case, but we can avoid this API Call
@@ -92,8 +74,9 @@ class DFXBuyProvider extends BuyProvider {
// }
// }
- Future auth() async {
- final signMessage = await getSignature(await getSignMessage());
+ Future auth(String walletAddress) async {
+ final signMessage = await getSignature(
+ await getSignMessage(walletAddress), walletAddress);
final requestBody = jsonEncode({
'wallet': walletName,
@@ -120,7 +103,7 @@ class DFXBuyProvider extends BuyProvider {
}
}
- Future getSignature(String message) async {
+ Future getSignature(String message, String walletAddress) async {
switch (wallet.type) {
case WalletType.ethereum:
case WalletType.polygon:
@@ -135,8 +118,178 @@ class DFXBuyProvider extends BuyProvider {
}
}
+ Future