Merge branch 'main' of https://github.com/cake-tech/cake_wallet into cw_linux_direct_input_password
Conflicts: .gitignore cw_haven/pubspec.lock cw_nano/lib/nano_wallet.dart cw_nano/pubspec.lock lib/buy/moonpay/moonpay_provider.dart lib/di.dart lib/entities/load_current_wallet.dart
33
.github/ISSUE_TEMPLATE/bug-report-🪲-.md
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
name: "Bug Report \U0001FAB2 "
|
||||
about: 'Report a bug '
|
||||
title: ''
|
||||
labels: Bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Platform:**
|
||||
- OS: [e.g. iOS 15.1, Android 14]
|
||||
- Device: [e.g. iPhone 14, Galaxy S21]
|
||||
- Cake Wallet Version: [e.g. 4.12.1]
|
||||
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Not sure where to start?
|
||||
url: https://guides.cakewallet.com
|
||||
about: Start by reading checking out the guides!
|
||||
- name: Need help?
|
||||
url: https://cakewallet.com/#contact
|
||||
about: Use our live chat or send a support email!
|
20
.github/ISSUE_TEMPLATE/feature-or-enhancement-request-✨.md
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
name: Feature or Enhancement Request ✨
|
||||
about: Suggest an idea for Cake Wallet
|
||||
title: ''
|
||||
labels: Enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
9
.gitignore
vendored
|
@ -149,3 +149,12 @@ macos/Runner/DebugProfile.entitlements
|
|||
macos/Runner/Release.entitlements
|
||||
|
||||
lib/core/secure_storage.dart
|
||||
|
||||
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
|
||||
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
|
||||
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
|
||||
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
|
||||
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
|
||||
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
|
||||
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
|
||||
macos/Runner/Configs/AppInfo.xcconfig
|
||||
|
|
|
@ -37,7 +37,7 @@ if (appPropertiesFile.exists()) {
|
|||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
compileSdkVersion 34
|
||||
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
|
|
|
@ -71,8 +71,8 @@
|
|||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
<provider
|
||||
android:name="com.pichillilorenzo.flutter_inappwebview.InAppWebViewFileProvider"
|
||||
android:authorities="${applicationId}.flutter_inappwebview.fileprovider"
|
||||
android:name="com.pichillilorenzo.flutter_inappwebview_android.InAppWebViewFileProvider"
|
||||
android:authorities="${applicationId}.flutter_inappwebview_android.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
|
|
BIN
assets/images/kaspa_icon.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 678 B After Width: | Height: | Size: 678 B |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 117 KiB |
After Width: | Height: | Size: 5.7 KiB |
BIN
assets/images/macos_icons/monero_macos_icons/monero_macos_16.png
Normal file
After Width: | Height: | Size: 548 B |
After Width: | Height: | Size: 14 KiB |
BIN
assets/images/macos_icons/monero_macos_icons/monero_macos_32.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 40 KiB |
BIN
assets/images/macos_icons/monero_macos_icons/monero_macos_64.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
assets/images/moonpay_dark.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
assets/images/moonpay_light.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assets/images/setup_2fa_img.png
Normal file
After Width: | Height: | Size: 57 KiB |
|
@ -1,4 +1,3 @@
|
|||
Polyseed enhancements
|
||||
New on-ramp provider DFX
|
||||
Usability enhancements
|
||||
Bug fixes
|
||||
On-ramp flow fixes and enhancements
|
||||
UI enhancements
|
||||
Generic enhancements and bug fixes
|
|
@ -1,2 +1,6 @@
|
|||
Support multiple address types for Bitcoin Cash
|
||||
Bug fixes
|
||||
Add new Off-ramp providers (DFX, OnRamper)
|
||||
On-ramp flow fixes and enhancements
|
||||
Ethereum and WalletConnect fixes and improvements
|
||||
Nano enhancements
|
||||
UI enhancements
|
||||
Generic enhancements and bug fixes
|
|
@ -1,4 +1,24 @@
|
|||
cd scripts/android
|
||||
IOS="ios"
|
||||
ANDROID="android"
|
||||
|
||||
PLATFORMS=($IOS $ANDROID)
|
||||
PLATFORM=$1
|
||||
|
||||
if ! [[ " ${PLATFORMS[*]} " =~ " ${PLATFORM} " ]]; then
|
||||
echo "specify platform: ./configure_cake_wallet.sh ios|android"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$PLATFORM" == "$IOS" ]; then
|
||||
echo "Configuring for iOS"
|
||||
cd scripts/ios
|
||||
fi
|
||||
|
||||
if [ "$PLATFORM" == "$ANDROID" ]; then
|
||||
echo "Configuring for Android"
|
||||
cd scripts/android
|
||||
fi
|
||||
|
||||
source ./app_env.sh cakewallet
|
||||
./app_config.sh
|
||||
cd ../.. && flutter pub get
|
|
@ -1,13 +1,9 @@
|
|||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
|
||||
class ElectrumBalance extends Balance {
|
||||
const ElectrumBalance(
|
||||
{required this.confirmed,
|
||||
required this.unconfirmed,
|
||||
required this.frozen})
|
||||
const ElectrumBalance({required this.confirmed, required this.unconfirmed, required this.frozen})
|
||||
: super(confirmed, unconfirmed);
|
||||
|
||||
static ElectrumBalance? fromJSON(String? jsonSource) {
|
||||
|
@ -28,12 +24,10 @@ class ElectrumBalance extends Balance {
|
|||
final int frozen;
|
||||
|
||||
@override
|
||||
String get formattedAvailableBalance =>
|
||||
bitcoinAmountToString(amount: confirmed - unconfirmed.abs() - frozen);
|
||||
String get formattedAvailableBalance => bitcoinAmountToString(amount: confirmed - frozen);
|
||||
|
||||
@override
|
||||
String get formattedAdditionalBalance =>
|
||||
bitcoinAmountToString(amount: unconfirmed);
|
||||
String get formattedAdditionalBalance => bitcoinAmountToString(amount: unconfirmed);
|
||||
|
||||
@override
|
||||
String get formattedUnAvailableBalance {
|
||||
|
@ -41,6 +35,6 @@ class ElectrumBalance extends Balance {
|
|||
return frozenFormatted == '0.0' ? '' : frozenFormatted;
|
||||
}
|
||||
|
||||
String toJSON() => json.encode(
|
||||
{'confirmed': confirmed, 'unconfirmed': unconfirmed, 'frozen': frozen});
|
||||
String toJSON() =>
|
||||
json.encode({'confirmed': confirmed, 'unconfirmed': unconfirmed, 'frozen': frozen});
|
||||
}
|
||||
|
|
|
@ -62,6 +62,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(receiveAddress) : receiveAddress;
|
||||
}
|
||||
|
||||
@override
|
||||
String get primaryAddress => getAddress(index: 0, hd: mainHd);
|
||||
|
||||
@override
|
||||
set address(String addr) => null;
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
|||
CryptoCurrency.banano,
|
||||
CryptoCurrency.usdtPoly,
|
||||
CryptoCurrency.usdcEPoly,
|
||||
CryptoCurrency.kaspa,
|
||||
];
|
||||
|
||||
static const havenCurrencies = [
|
||||
|
@ -206,6 +207,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
|||
static const banano = CryptoCurrency(title: 'BAN', fullName: 'Banano', raw: 86, name: 'banano', iconPath: 'assets/images/nano_icon.png', decimals: 29);
|
||||
static const usdtPoly = CryptoCurrency(title: 'USDT', tag: 'POLY', fullName: 'Tether USD (PoS)', raw: 87, name: 'usdtpoly', iconPath: 'assets/images/usdt_icon.png', decimals: 6);
|
||||
static const usdcEPoly = CryptoCurrency(title: 'USDC.E', tag: 'POLY', fullName: 'USD Coin (PoS)', raw: 88, name: 'usdcepoly', iconPath: 'assets/images/usdc_icon.png', decimals: 6);
|
||||
static const kaspa = CryptoCurrency(title: 'KAS', fullName: 'Kaspa', raw: 89, name: 'kaspa', iconPath: 'assets/images/kaspa_icon.png', decimals: 8);
|
||||
|
||||
|
||||
static final Map<int, CryptoCurrency> _rawCurrencyMap =
|
||||
|
|
|
@ -61,7 +61,8 @@ class Erc20Token extends CryptoCurrency with HiveObjectMixin {
|
|||
|
||||
static const typeId = ERC20_TOKEN_TYPE_ID;
|
||||
static const boxName = 'Erc20Tokens';
|
||||
static const polygonBoxName = ' PolygonErc20Tokens';
|
||||
static const ethereumBoxName = 'EthereumErc20Tokens';
|
||||
static const polygonBoxName = 'PolygonErc20Tokens';
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
|
|
|
@ -15,6 +15,7 @@ abstract class TransactionInfo extends Object with Keyable {
|
|||
String? feeFormatted();
|
||||
void changeFiatAmount(String amount);
|
||||
String? to;
|
||||
String? from;
|
||||
|
||||
@override
|
||||
dynamic get keyIndex => id;
|
||||
|
|
|
@ -10,6 +10,8 @@ abstract class WalletAddresses {
|
|||
|
||||
String get address;
|
||||
|
||||
String? get primaryAddress => null;
|
||||
|
||||
set address(String address);
|
||||
|
||||
Map<String, String> addressesMap;
|
||||
|
|
|
@ -83,6 +83,8 @@ abstract class EthereumWalletBase
|
|||
|
||||
late final Box<Erc20Token> erc20TokensBox;
|
||||
|
||||
late final Box<Erc20Token> ethereumErc20TokensBox;
|
||||
|
||||
late final EthPrivateKey _ethPrivateKey;
|
||||
|
||||
EthPrivateKey get ethPrivateKey => _ethPrivateKey;
|
||||
|
@ -110,7 +112,8 @@ abstract class EthereumWalletBase
|
|||
Completer<SharedPreferences> _sharedPrefs = Completer();
|
||||
|
||||
Future<void> init() async {
|
||||
erc20TokensBox = await CakeHive.openBox<Erc20Token>(Erc20Token.boxName);
|
||||
await movePreviousErc20BoxConfigsToNewBox();
|
||||
|
||||
await walletAddresses.init();
|
||||
await transactionHistory.init();
|
||||
_ethPrivateKey = await getPrivateKey(
|
||||
|
@ -122,6 +125,33 @@ abstract class EthereumWalletBase
|
|||
await save();
|
||||
}
|
||||
|
||||
/// Majorly for backward compatibility for previous configs that have been set.
|
||||
Future<void> movePreviousErc20BoxConfigsToNewBox() async {
|
||||
// Opens a box specific to this wallet
|
||||
ethereumErc20TokensBox = await CakeHive.openBox<Erc20Token>(
|
||||
"${walletInfo.name.replaceAll(" ", "_")}_${Erc20Token.ethereumBoxName}");
|
||||
|
||||
//Open the previous token configs box
|
||||
erc20TokensBox = await CakeHive.openBox<Erc20Token>(Erc20Token.boxName);
|
||||
|
||||
// Check if it's empty, if it is, we stop the flow and return.
|
||||
if (erc20TokensBox.isEmpty) {
|
||||
// If it's empty, but the new wallet specific box is also empty,
|
||||
// we load the initial tokens to the new box.
|
||||
if (ethereumErc20TokensBox.isEmpty) addInitialTokens();
|
||||
return;
|
||||
}
|
||||
|
||||
final allValues = erc20TokensBox.values.toList();
|
||||
|
||||
// Clear and delete the old token box
|
||||
await erc20TokensBox.clear();
|
||||
await erc20TokensBox.deleteFromDisk();
|
||||
|
||||
// Add all the previous tokens with configs to the new box
|
||||
ethereumErc20TokensBox.addAll(allValues);
|
||||
}
|
||||
|
||||
@override
|
||||
int calculateEstimatedFee(TransactionPriority priority, int? amount) {
|
||||
try {
|
||||
|
@ -388,7 +418,7 @@ abstract class EthereumWalletBase
|
|||
}
|
||||
|
||||
Future<void> _fetchErc20Balances() async {
|
||||
for (var token in erc20TokensBox.values) {
|
||||
for (var token in ethereumErc20TokensBox.values) {
|
||||
try {
|
||||
if (token.enabled) {
|
||||
balance[token] = await _client.fetchERC20Balances(
|
||||
|
@ -423,7 +453,7 @@ abstract class EthereumWalletBase
|
|||
|
||||
Future<void>? updateBalance() async => await _updateBalance();
|
||||
|
||||
List<Erc20Token> get erc20Currencies => erc20TokensBox.values.toList();
|
||||
List<Erc20Token> get erc20Currencies => ethereumErc20TokensBox.values.toList();
|
||||
|
||||
Future<void> addErc20Token(Erc20Token token) async {
|
||||
String? iconPath;
|
||||
|
@ -443,7 +473,7 @@ abstract class EthereumWalletBase
|
|||
iconPath: iconPath,
|
||||
);
|
||||
|
||||
await erc20TokensBox.put(_token.contractAddress, _token);
|
||||
await ethereumErc20TokensBox.put(_token.contractAddress, _token);
|
||||
|
||||
if (_token.enabled) {
|
||||
balance[_token] = await _client.fetchERC20Balances(
|
||||
|
@ -473,7 +503,7 @@ abstract class EthereumWalletBase
|
|||
void addInitialTokens() {
|
||||
final initialErc20Tokens = DefaultErc20Tokens().initialErc20Tokens;
|
||||
|
||||
initialErc20Tokens.forEach((token) => erc20TokensBox.put(token.contractAddress, token));
|
||||
initialErc20Tokens.forEach((token) => ethereumErc20TokensBox.put(token.contractAddress, token));
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -22,12 +22,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
|
|||
|
||||
@override
|
||||
Future<EthereumWallet> create(EthereumNewWalletCredentials credentials) async {
|
||||
|
||||
final strength = (credentials.seedPhraseLength == 12)
|
||||
? 128
|
||||
: (credentials.seedPhraseLength == 24)
|
||||
? 256
|
||||
: 128;
|
||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
|
||||
final mnemonic = bip39.generateMnemonic(strength: strength);
|
||||
final wallet = EthereumWallet(
|
||||
|
|
|
@ -113,15 +113,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.4.3"
|
||||
cake_backup:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: "."
|
||||
ref: main
|
||||
resolved-ref: "3aba867dcab6737f6707782f5db15d71f303db38"
|
||||
url: "https://github.com/cake-tech/cake_backup.git"
|
||||
source: git
|
||||
version: "1.0.0+1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -178,22 +169,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
cryptography:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cryptography
|
||||
sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
cupertino_icons:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
cw_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -648,14 +623,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
tuple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: tuple
|
||||
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_nano/nano_util.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
class BananoBalance extends Balance {
|
||||
final BigInt currentBalance;
|
||||
final BigInt receivableBalance;
|
||||
|
||||
BananoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0) {
|
||||
}
|
||||
BananoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0);
|
||||
|
||||
@override
|
||||
String get formattedAvailableBalance {
|
||||
return NanoUtil.getRawAsUsableString(currentBalance.toString(), NanoUtil.rawPerBanano);
|
||||
return NanoAmounts.getRawAsUsableString(currentBalance.toString(), NanoAmounts.rawPerBanano);
|
||||
}
|
||||
|
||||
@override
|
||||
String get formattedAdditionalBalance {
|
||||
return NanoUtil.getRawAsUsableString(receivableBalance.toString(), NanoUtil.rawPerBanano);
|
||||
return NanoAmounts.getRawAsUsableString(receivableBalance.toString(), NanoAmounts.rawPerBanano);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,35 @@
|
|||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_nano/nano_util.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
BigInt stringAmountToBigInt(String amount) {
|
||||
return BigInt.parse(NanoUtil.getAmountAsRaw(amount, NanoUtil.rawPerNano));
|
||||
return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerNano));
|
||||
}
|
||||
|
||||
class NanoBalance extends Balance {
|
||||
final BigInt currentBalance;
|
||||
final BigInt receivableBalance;
|
||||
late String formattedCurrentBalance;
|
||||
late String formattedReceivableBalance;
|
||||
|
||||
NanoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0) {
|
||||
this.formattedCurrentBalance = "";
|
||||
this.formattedReceivableBalance = "";
|
||||
}
|
||||
NanoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0);
|
||||
|
||||
NanoBalance.fromString(
|
||||
{required this.formattedCurrentBalance, required this.formattedReceivableBalance})
|
||||
NanoBalance.fromFormattedString(
|
||||
{required String formattedCurrentBalance, required String formattedReceivableBalance})
|
||||
: currentBalance = stringAmountToBigInt(formattedCurrentBalance),
|
||||
receivableBalance = stringAmountToBigInt(formattedReceivableBalance),
|
||||
super(0, 0);
|
||||
|
||||
NanoBalance.fromRawString(
|
||||
{required String currentBalance, required String receivableBalance})
|
||||
: currentBalance = BigInt.parse(currentBalance),
|
||||
receivableBalance = BigInt.parse(receivableBalance),
|
||||
super(0, 0);
|
||||
|
||||
@override
|
||||
String get formattedAvailableBalance {
|
||||
return NanoUtil.getRawAsUsableString(currentBalance.toString(), NanoUtil.rawPerNano);
|
||||
return NanoAmounts.getRawAsUsableString(currentBalance.toString(), NanoAmounts.rawPerNano);
|
||||
}
|
||||
|
||||
@override
|
||||
String get formattedAdditionalBalance {
|
||||
return NanoUtil.getRawAsUsableString(receivableBalance.toString(), NanoUtil.rawPerNano);
|
||||
return NanoAmounts.getRawAsUsableString(receivableBalance.toString(), NanoAmounts.rawPerNano);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@ import 'dart:convert';
|
|||
import 'package:cw_core/nano_account_info_response.dart';
|
||||
import 'package:cw_nano/nano_balance.dart';
|
||||
import 'package:cw_nano/nano_transaction_model.dart';
|
||||
import 'package:cw_nano/nano_util.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:nanodart/nanodart.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class NanoClient {
|
||||
|
@ -61,6 +61,13 @@ class NanoClient {
|
|||
),
|
||||
);
|
||||
final data = await jsonDecode(response.body);
|
||||
if (response.statusCode != 200 ||
|
||||
data["error"] != null ||
|
||||
data["balance"] == null ||
|
||||
data["receivable"] == null) {
|
||||
throw Exception(
|
||||
"Error while trying to get balance! ${data["error"] != null ? data["error"] : ""}");
|
||||
}
|
||||
final String currentBalance = data["balance"] as String;
|
||||
final String receivableBalance = data["receivable"] as String;
|
||||
final BigInt cur = BigInt.parse(currentBalance);
|
||||
|
@ -203,7 +210,7 @@ class NanoClient {
|
|||
String? previousHash,
|
||||
}) async {
|
||||
// our address:
|
||||
final String publicAddress = NanoUtil.privateKeyToAddress(privateKey);
|
||||
final String publicAddress = NanoDerivations.privateKeyToAddress(privateKey);
|
||||
|
||||
// first get the current account balance:
|
||||
if (balanceAfterTx == null) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:cw_core/format_amount.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_nano/nano_util.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
class NanoTransactionInfo extends TransactionInfo {
|
||||
NanoTransactionInfo({
|
||||
|
@ -13,6 +13,8 @@ class NanoTransactionInfo extends TransactionInfo {
|
|||
required this.confirmed,
|
||||
required this.date,
|
||||
required this.confirmations,
|
||||
required this.to,
|
||||
required this.from,
|
||||
}) : this.amount = amountRaw.toInt();
|
||||
|
||||
final String id;
|
||||
|
@ -24,14 +26,17 @@ class NanoTransactionInfo extends TransactionInfo {
|
|||
final bool confirmed;
|
||||
final int confirmations;
|
||||
final String tokenSymbol;
|
||||
final String? to;
|
||||
final String? from;
|
||||
String? _fiatAmount;
|
||||
|
||||
bool get isPending => !this.confirmed;
|
||||
|
||||
@override
|
||||
String amountFormatted() {
|
||||
final String amt = NanoUtil.getRawAsUsableString(amountRaw.toString(), NanoUtil.rawPerNano);
|
||||
final String acc = NanoUtil.getRawAccuracy(amountRaw.toString(), NanoUtil.rawPerNano);
|
||||
final String amt =
|
||||
NanoAmounts.getRawAsUsableString(amountRaw.toString(), NanoAmounts.rawPerNano);
|
||||
final String acc = NanoAmounts.getRawAccuracy(amountRaw.toString(), NanoAmounts.rawPerNano);
|
||||
return "$acc$amt $tokenSymbol";
|
||||
}
|
||||
|
||||
|
@ -54,6 +59,8 @@ class NanoTransactionInfo extends TransactionInfo {
|
|||
confirmed: data['confirmed'] as bool,
|
||||
confirmations: data['confirmations'] as int,
|
||||
tokenSymbol: data['tokenSymbol'] as String,
|
||||
to: data['to'] as String,
|
||||
from: data['from'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -66,5 +73,7 @@ class NanoTransactionInfo extends TransactionInfo {
|
|||
'confirmed': confirmed,
|
||||
'confirmations': confirmations,
|
||||
'tokenSymbol': tokenSymbol,
|
||||
'to': to,
|
||||
'from': from,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,193 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:convert/convert.dart';
|
||||
import "package:ed25519_hd_key/ed25519_hd_key.dart";
|
||||
import 'package:libcrypto/libcrypto.dart';
|
||||
import 'package:nanodart/nanodart.dart';
|
||||
import 'package:decimal/decimal.dart';
|
||||
|
||||
class NanoUtil {
|
||||
// standard:
|
||||
static String seedToPrivate(String seed, int index) {
|
||||
return NanoKeys.seedToPrivate(seed, index);
|
||||
}
|
||||
|
||||
static String seedToAddress(String seed, int index) {
|
||||
return NanoAccounts.createAccount(
|
||||
NanoAccountType.NANO, privateKeyToPublic(seedToPrivate(seed, index)));
|
||||
}
|
||||
|
||||
static String seedToMnemonic(String seed) {
|
||||
return NanoMnemomics.seedToMnemonic(seed).join(" ");
|
||||
}
|
||||
|
||||
static Future<String> mnemonicToSeed(String mnemonic) async {
|
||||
return NanoMnemomics.mnemonicListToSeed(mnemonic.split(' '));
|
||||
}
|
||||
|
||||
static String privateKeyToPublic(String privateKey) {
|
||||
// return NanoHelpers.byteToHex(Ed25519Blake2b.getPubkey(NanoHelpers.hexToBytes(privateKey))!);
|
||||
return NanoKeys.createPublicKey(privateKey);
|
||||
}
|
||||
|
||||
static String addressToPublicKey(String publicAddress) {
|
||||
return NanoAccounts.extractPublicKey(publicAddress);
|
||||
}
|
||||
|
||||
// universal:
|
||||
static String privateKeyToAddress(String privateKey) {
|
||||
return NanoAccounts.createAccount(NanoAccountType.NANO, privateKeyToPublic(privateKey));
|
||||
}
|
||||
|
||||
static String publicKeyToAddress(String publicKey) {
|
||||
return NanoAccounts.createAccount(NanoAccountType.NANO, publicKey);
|
||||
}
|
||||
|
||||
// standard + hd:
|
||||
static bool isValidSeed(String seed) {
|
||||
// Ensure seed is 64 or 128 characters long
|
||||
if (seed == null || (seed.length != 64 && seed.length != 128)) {
|
||||
return false;
|
||||
}
|
||||
// Ensure seed only contains hex characters, 0-9;A-F
|
||||
return NanoHelpers.isHexString(seed);
|
||||
}
|
||||
|
||||
// // hd:
|
||||
static Future<String> hdMnemonicListToSeed(List<String> words) async {
|
||||
// if (words.length != 24) {
|
||||
// throw Exception('Expected a 24-word list, got a ${words.length} list');
|
||||
// }
|
||||
final Uint8List salt = Uint8List.fromList(utf8.encode('mnemonic'));
|
||||
final Pbkdf2 hasher = Pbkdf2(iterations: 2048);
|
||||
final String seed = await hasher.sha512(words.join(' '), salt);
|
||||
return seed;
|
||||
}
|
||||
|
||||
static Future<String> hdSeedToPrivate(String seed, int index) async {
|
||||
List<int> seedBytes = hex.decode(seed);
|
||||
KeyData data = await ED25519_HD_KEY.derivePath("m/44'/165'/$index'", seedBytes);
|
||||
return hex.encode(data.key);
|
||||
}
|
||||
|
||||
static Future<String> hdSeedToAddress(String seed, int index) async {
|
||||
return NanoAccounts.createAccount(
|
||||
NanoAccountType.NANO, privateKeyToPublic(await hdSeedToPrivate(seed, index)));
|
||||
}
|
||||
|
||||
static Future<String> uniSeedToAddress(String seed, int index, String type) {
|
||||
if (type == "standard") {
|
||||
return Future<String>.value(seedToAddress(seed, index));
|
||||
} else if (type == "hd") {
|
||||
return hdSeedToAddress(seed, index);
|
||||
} else {
|
||||
throw Exception('Unknown seed type');
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String> uniSeedToPrivate(String seed, int index, String type) {
|
||||
if (type == "standard") {
|
||||
return Future<String>.value(seedToPrivate(seed, index));
|
||||
} else if (type == "hd") {
|
||||
return hdSeedToPrivate(seed, index);
|
||||
} else {
|
||||
throw Exception('Unknown seed type');
|
||||
}
|
||||
}
|
||||
|
||||
static bool isValidBip39Seed(String seed) {
|
||||
// Ensure seed is 128 characters long
|
||||
if (seed.length != 128) {
|
||||
return false;
|
||||
}
|
||||
// Ensure seed only contains hex characters, 0-9;A-F
|
||||
return NanoHelpers.isHexString(seed);
|
||||
}
|
||||
|
||||
// number util:
|
||||
|
||||
static const int maxDecimalDigits = 6; // Max digits after decimal
|
||||
static BigInt rawPerNano = BigInt.parse("1000000000000000000000000000000");
|
||||
static BigInt rawPerNyano = BigInt.parse("1000000000000000000000000");
|
||||
static BigInt rawPerBanano = BigInt.parse("100000000000000000000000000000");
|
||||
static BigInt rawPerXMR = BigInt.parse("1000000000000");
|
||||
static BigInt convertXMRtoNano = BigInt.parse("1000000000000000000");
|
||||
// static BigInt convertXMRtoNano = BigInt.parse("1000000000000000000000000000");
|
||||
|
||||
/// Convert raw to ban and return as BigDecimal
|
||||
///
|
||||
/// @param raw 100000000000000000000000000000
|
||||
/// @return Decimal value 1.000000000000000000000000000000
|
||||
///
|
||||
static Decimal getRawAsDecimal(String? raw, BigInt? rawPerCur) {
|
||||
rawPerCur ??= rawPerNano;
|
||||
final Decimal amount = Decimal.parse(raw.toString());
|
||||
final Decimal result = (amount / Decimal.parse(rawPerCur.toString())).toDecimal();
|
||||
return result;
|
||||
}
|
||||
|
||||
static String truncateDecimal(Decimal input, {int digits = maxDecimalDigits}) {
|
||||
Decimal bigger = input.shift(digits);
|
||||
bigger = bigger.floor(); // chop off the decimal: 1.059 -> 1.05
|
||||
bigger = bigger.shift(-digits);
|
||||
return bigger.toString();
|
||||
}
|
||||
|
||||
/// Return raw as a NANO amount.
|
||||
///
|
||||
/// @param raw 100000000000000000000000000000
|
||||
/// @returns 1
|
||||
///
|
||||
static String getRawAsUsableString(String? raw, BigInt rawPerCur) {
|
||||
final String res =
|
||||
truncateDecimal(getRawAsDecimal(raw, rawPerCur), digits: maxDecimalDigits + 9);
|
||||
|
||||
if (raw == null || raw == "0" || raw == "00000000000000000000000000000000") {
|
||||
return "0";
|
||||
}
|
||||
|
||||
if (!res.contains(".")) {
|
||||
return res;
|
||||
}
|
||||
|
||||
final String numAmount = res.split(".")[0];
|
||||
String decAmount = res.split(".")[1];
|
||||
|
||||
// truncate:
|
||||
if (decAmount.length > maxDecimalDigits) {
|
||||
decAmount = decAmount.substring(0, maxDecimalDigits);
|
||||
// remove trailing zeros:
|
||||
decAmount = decAmount.replaceAllMapped(RegExp(r'0+$'), (Match match) => '');
|
||||
if (decAmount.isEmpty) {
|
||||
return numAmount;
|
||||
}
|
||||
}
|
||||
|
||||
return "$numAmount.$decAmount";
|
||||
}
|
||||
|
||||
static String getRawAccuracy(String? raw, BigInt rawPerCur) {
|
||||
final String rawString = getRawAsUsableString(raw, rawPerCur);
|
||||
final String rawDecimalString = getRawAsDecimal(raw, rawPerCur).toString();
|
||||
|
||||
if (raw == null || raw.isEmpty || raw == "0") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (rawString != rawDecimalString) {
|
||||
return "~";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/// Return readable string amount as raw string
|
||||
/// @param amount 1.01
|
||||
/// @returns 101000000000000000000000000000
|
||||
///
|
||||
static String getAmountAsRaw(String amount, BigInt rawPerCur) {
|
||||
final Decimal asDecimal = Decimal.parse(amount);
|
||||
final Decimal rawDecimal = Decimal.parse(rawPerCur.toString());
|
||||
return (asDecimal * rawDecimal).toString();
|
||||
}
|
||||
}
|
|
@ -18,7 +18,6 @@ import 'package:cw_nano/nano_client.dart';
|
|||
import 'package:cw_nano/nano_transaction_credentials.dart';
|
||||
import 'package:cw_nano/nano_transaction_history.dart';
|
||||
import 'package:cw_nano/nano_transaction_info.dart';
|
||||
import 'package:cw_nano/nano_util.dart';
|
||||
import 'package:cw_nano/nano_wallet_keys.dart';
|
||||
import 'package:cw_nano/pending_nano_transaction.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -27,6 +26,7 @@ import 'package:cw_nano/nano_wallet_addresses.dart';
|
|||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:nanodart/nanodart.dart';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
part 'nano_wallet.g.dart';
|
||||
|
||||
|
@ -94,6 +94,8 @@ abstract class NanoWalletBase
|
|||
@override
|
||||
String get password => _password;
|
||||
|
||||
static const int POLL_INTERVAL_SECONDS = 10;
|
||||
|
||||
// initialize the different forms of private / public key we'll need:
|
||||
Future<void> init() async {
|
||||
if (_derivationType == DerivationType.unknown) {
|
||||
|
@ -111,11 +113,21 @@ abstract class NanoWalletBase
|
|||
if (_derivationType == DerivationType.nano) {
|
||||
_hexSeed = bip39.mnemonicToEntropy(_mnemonic).toUpperCase();
|
||||
} else {
|
||||
_hexSeed = await NanoUtil.hdMnemonicListToSeed(_mnemonic.split(' '));
|
||||
_hexSeed = await NanoDerivations.hdMnemonicListToSeed(_mnemonic.split(' '));
|
||||
}
|
||||
}
|
||||
_privateKey = await NanoUtil.uniSeedToPrivate(_hexSeed!, 0, type);
|
||||
_publicAddress = await NanoUtil.uniSeedToAddress(_hexSeed!, 0, type);
|
||||
NanoDerivationType derivationType =
|
||||
type == "standard" ? NanoDerivationType.STANDARD : NanoDerivationType.HD;
|
||||
_privateKey = await NanoDerivations.universalSeedToPrivate(
|
||||
_hexSeed!,
|
||||
index: 0,
|
||||
type: derivationType,
|
||||
);
|
||||
_publicAddress = await NanoDerivations.universalSeedToAddress(
|
||||
_hexSeed!,
|
||||
index: 0,
|
||||
type: derivationType,
|
||||
);
|
||||
this.walletInfo.address = _publicAddress!;
|
||||
|
||||
await walletAddresses.init();
|
||||
|
@ -136,6 +148,7 @@ abstract class NanoWalletBase
|
|||
@override
|
||||
void close() {
|
||||
_client.stop();
|
||||
_receiveTimer?.cancel();
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -150,6 +163,7 @@ abstract class NanoWalletBase
|
|||
|
||||
try {
|
||||
await _updateBalance();
|
||||
await updateTransactions();
|
||||
await _updateRep();
|
||||
await _receiveAll();
|
||||
} catch (e) {
|
||||
|
@ -184,8 +198,8 @@ abstract class NanoWalletBase
|
|||
if (txOut.sendAll) {
|
||||
amt = balance[currency]?.currentBalance ?? BigInt.zero;
|
||||
} else {
|
||||
amt = BigInt.tryParse(NanoUtil.getAmountAsRaw(
|
||||
txOut.cryptoAmount?.replaceAll(',', '.') ?? "0", NanoUtil.rawPerNano)) ??
|
||||
amt = BigInt.tryParse(NanoAmounts.getAmountAsRaw(
|
||||
txOut.cryptoAmount?.replaceAll(',', '.') ?? "0", NanoAmounts.rawPerNano)) ??
|
||||
BigInt.zero;
|
||||
}
|
||||
|
||||
|
@ -197,9 +211,7 @@ abstract class NanoWalletBase
|
|||
|
||||
final block = await _client.constructSendBlock(
|
||||
amountRaw: amt.toString(),
|
||||
destinationAddress: txOut.isParsedAddress
|
||||
? txOut.extractedAddress!
|
||||
: txOut.address,
|
||||
destinationAddress: txOut.isParsedAddress ? txOut.extractedAddress! : txOut.address,
|
||||
privateKey: _privateKey!,
|
||||
balanceAfterTx: runningBalance,
|
||||
previousHash: previousHash,
|
||||
|
@ -247,10 +259,10 @@ abstract class NanoWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> updateTransactions() async {
|
||||
Future<bool> updateTransactions() async {
|
||||
try {
|
||||
if (_isTransactionUpdating) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
_isTransactionUpdating = true;
|
||||
|
@ -258,8 +270,10 @@ abstract class NanoWalletBase
|
|||
transactionHistory.addMany(transactions);
|
||||
await transactionHistory.save();
|
||||
_isTransactionUpdating = false;
|
||||
return true;
|
||||
} catch (_) {
|
||||
_isTransactionUpdating = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,16 +286,17 @@ abstract class NanoWalletBase
|
|||
final Map<String, NanoTransactionInfo> result = {};
|
||||
|
||||
for (var transactionModel in transactions) {
|
||||
final bool isSend = transactionModel.type == "send";
|
||||
result[transactionModel.hash] = NanoTransactionInfo(
|
||||
id: transactionModel.hash,
|
||||
amountRaw: transactionModel.amount,
|
||||
height: transactionModel.height,
|
||||
direction: transactionModel.type == "send"
|
||||
? TransactionDirection.outgoing
|
||||
: TransactionDirection.incoming,
|
||||
direction: isSend ? TransactionDirection.outgoing : TransactionDirection.incoming,
|
||||
confirmed: transactionModel.confirmed,
|
||||
date: transactionModel.date ?? DateTime.now(),
|
||||
confirmations: transactionModel.confirmed ? 1 : 0,
|
||||
to: isSend ? transactionModel.account : address,
|
||||
from: isSend ? address : transactionModel.account,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -323,11 +338,10 @@ abstract class NanoWalletBase
|
|||
Future<void> startSync() async {
|
||||
try {
|
||||
syncStatus = AttemptingSyncStatus();
|
||||
await _updateBalance();
|
||||
await updateTransactions();
|
||||
|
||||
// setup a timer to receive transactions periodically:
|
||||
_receiveTimer?.cancel();
|
||||
_receiveTimer = Timer.periodic(const Duration(seconds: 15), (timer) async {
|
||||
_receiveTimer = Timer.periodic(const Duration(seconds: POLL_INTERVAL_SECONDS), (timer) async {
|
||||
// get our balance:
|
||||
await _updateBalance();
|
||||
// if we have anything to receive, process it:
|
||||
|
@ -336,6 +350,14 @@ abstract class NanoWalletBase
|
|||
}
|
||||
});
|
||||
|
||||
// also run once, immediately:
|
||||
await _updateBalance();
|
||||
bool updateSuccess = await updateTransactions();
|
||||
if (!updateSuccess) {
|
||||
syncStatus = FailedSyncStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
syncStatus = SyncedSyncStatus();
|
||||
} catch (e) {
|
||||
print(e);
|
||||
|
@ -365,9 +387,11 @@ abstract class NanoWalletBase
|
|||
|
||||
final data = json.decode(jsonSource) as Map;
|
||||
final mnemonic = data['mnemonic'] as String;
|
||||
final balance = NanoBalance.fromString(
|
||||
formattedCurrentBalance: data['currentBalance'] as String? ?? "0",
|
||||
formattedReceivableBalance: data['receivableBalance'] as String? ?? "0");
|
||||
|
||||
final balance = NanoBalance.fromRawString(
|
||||
currentBalance: data['currentBalance'] as String? ?? "0",
|
||||
receivableBalance: data['receivableBalance'] as String? ?? "0",
|
||||
);
|
||||
|
||||
DerivationType derivationType = DerivationType.nano;
|
||||
if (data['derivationType'] == "DerivationType.bip39") {
|
||||
|
@ -387,12 +411,26 @@ abstract class NanoWalletBase
|
|||
}
|
||||
|
||||
Future<void> _updateBalance() async {
|
||||
var oldBalance = balance[currency];
|
||||
try {
|
||||
balance[currency] = await _client.getBalance(_publicAddress!);
|
||||
} catch (e) {
|
||||
print("Failed to get balance $e");
|
||||
// if we don't have a balance, we should at least create one, since it's a late binding
|
||||
// otherwise, it's better to just leave it as whatever it was before:
|
||||
if (balance[currency] == null) {
|
||||
balance[currency] =
|
||||
NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero);
|
||||
}
|
||||
}
|
||||
// don't save unnecessarily:
|
||||
// trying to save too frequently can cause problems with the file system
|
||||
// since nano is updated frequently this can be a problem, so we only save if there is a change:
|
||||
if (oldBalance == null ||
|
||||
balance[currency]!.currentBalance != oldBalance.currentBalance ||
|
||||
balance[currency]!.receivableBalance != oldBalance.receivableBalance) {
|
||||
await save();
|
||||
}
|
||||
await save();
|
||||
}
|
||||
|
||||
Future<void> _updateRep() async {
|
||||
|
@ -407,11 +445,19 @@ abstract class NanoWalletBase
|
|||
}
|
||||
|
||||
Future<void> regenerateAddress() async {
|
||||
final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd";
|
||||
_privateKey =
|
||||
await NanoUtil.uniSeedToPrivate(_hexSeed!, this.walletAddresses.account!.id, type);
|
||||
_publicAddress =
|
||||
await NanoUtil.uniSeedToAddress(_hexSeed!, this.walletAddresses.account!.id, type);
|
||||
final NanoDerivationType type = (_derivationType == DerivationType.nano)
|
||||
? NanoDerivationType.STANDARD
|
||||
: NanoDerivationType.HD;
|
||||
_privateKey = await NanoDerivations.universalSeedToPrivate(
|
||||
_hexSeed!,
|
||||
index: this.walletAddresses.account!.id,
|
||||
type: type,
|
||||
);
|
||||
_publicAddress = await NanoDerivations.universalSeedToAddress(
|
||||
_hexSeed!,
|
||||
index: this.walletAddresses.account!.id,
|
||||
type: type,
|
||||
);
|
||||
|
||||
this.walletInfo.address = _publicAddress!;
|
||||
this.walletAddresses.address = _publicAddress!;
|
||||
|
|
|
@ -7,12 +7,12 @@ import 'package:cw_core/wallet_info.dart';
|
|||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_nano/nano_mnemonic.dart' as nm;
|
||||
import 'package:cw_nano/nano_util.dart';
|
||||
import 'package:cw_nano/nano_wallet.dart';
|
||||
import 'package:cw_nano/nano_wallet_creation_credentials.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:nanodart/nanodart.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials> {
|
||||
|
@ -32,7 +32,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
// nano standard:
|
||||
DerivationType derivationType = DerivationType.nano;
|
||||
String seedKey = NanoSeeds.generateSeed();
|
||||
String mnemonic = NanoUtil.seedToMnemonic(seedKey);
|
||||
String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
|
||||
|
||||
credentials.walletInfo!.derivationType = derivationType;
|
||||
|
||||
|
@ -102,7 +102,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
// we can't derive the mnemonic from the key in all cases, only if it's a "nano" seed
|
||||
if (credentials.seedKey.length == 64) {
|
||||
try {
|
||||
mnemonic = NanoUtil.seedToMnemonic(credentials.seedKey);
|
||||
mnemonic = NanoDerivations.standardSeedToMnemonic(credentials.seedKey);
|
||||
} catch (e) {
|
||||
throw Exception("Wasn't a valid nano style seed!");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_nano/nano_client.dart';
|
||||
import 'package:cw_nano/nano_util.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
class PendingNanoTransaction with PendingTransaction {
|
||||
PendingNanoTransaction({
|
||||
|
@ -18,13 +18,13 @@ class PendingNanoTransaction with PendingTransaction {
|
|||
|
||||
@override
|
||||
String get amountFormatted {
|
||||
final String amt = NanoUtil.getRawAsUsableString(amount.toString(), NanoUtil.rawPerNano);
|
||||
final String amt = NanoAmounts.getRawAsUsableString(amount.toString(), NanoAmounts.rawPerNano);
|
||||
return amt;
|
||||
}
|
||||
|
||||
String get accurateAmountFormatted {
|
||||
final String amt = NanoUtil.getRawAsUsableString(amount.toString(), NanoUtil.rawPerNano);
|
||||
final String acc = NanoUtil.getRawAccuracy(amount.toString(), NanoUtil.rawPerNano);
|
||||
final String amt = NanoAmounts.getRawAsUsableString(amount.toString(), NanoAmounts.rawPerNano);
|
||||
final String acc = NanoAmounts.getRawAccuracy(amount.toString(), NanoAmounts.rawPerNano);
|
||||
return "$acc$amt";
|
||||
}
|
||||
|
||||
|
|
|
@ -137,15 +137,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.6.1"
|
||||
cake_backup:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: "."
|
||||
ref: main
|
||||
resolved-ref: "3aba867dcab6737f6707782f5db15d71f303db38"
|
||||
url: "https://github.com/cake-tech/cake_backup.git"
|
||||
source: git
|
||||
version: "1.0.0+1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -202,22 +193,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
cryptography:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cryptography
|
||||
sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
cupertino_icons:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
cw_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -496,6 +471,15 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
nanoutil:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: c37e72817cf0a28162f43124f79661d6c8e0098f
|
||||
resolved-ref: c37e72817cf0a28162f43124f79661d6c8e0098f
|
||||
url: "https://github.com/perishllc/nanoutil.git"
|
||||
source: git
|
||||
version: "1.0.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -789,14 +773,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
tuple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: tuple
|
||||
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -22,6 +22,10 @@ dependencies:
|
|||
hex: ^0.2.0
|
||||
http: ^1.1.0
|
||||
shared_preferences: ^2.0.15
|
||||
nanoutil:
|
||||
git:
|
||||
url: https://github.com/perishllc/nanoutil.git
|
||||
ref: c37e72817cf0a28162f43124f79661d6c8e0098f
|
||||
cw_core:
|
||||
path: ../cw_core
|
||||
|
||||
|
|
|
@ -102,8 +102,12 @@ abstract class PolygonWalletBase
|
|||
final Completer<SharedPreferences> _sharedPrefs = Completer();
|
||||
|
||||
Future<void> init() async {
|
||||
polygonErc20TokensBox = await CakeHive.openBox<Erc20Token>(
|
||||
"${walletInfo.name.replaceAll(" ", "_")}_${Erc20Token.polygonBoxName}");
|
||||
final boxName = "${walletInfo.name.replaceAll(" ", "_")}_ ${Erc20Token.polygonBoxName}";
|
||||
if (await CakeHive.boxExists(boxName)) {
|
||||
polygonErc20TokensBox = await CakeHive.openBox<Erc20Token>(boxName);
|
||||
} else {
|
||||
polygonErc20TokensBox = await CakeHive.openBox<Erc20Token>(boxName.replaceAll(" ", ""));
|
||||
}
|
||||
await walletAddresses.init();
|
||||
await transactionHistory.init();
|
||||
_polygonPrivateKey = await getPrivateKey(
|
||||
|
|
|
@ -20,11 +20,7 @@ class PolygonWalletService extends WalletService<PolygonNewWalletCredentials,
|
|||
|
||||
@override
|
||||
Future<PolygonWallet> create(PolygonNewWalletCredentials credentials) async {
|
||||
final strength = (credentials.seedPhraseLength == 12)
|
||||
? 128
|
||||
: (credentials.seedPhraseLength == 24)
|
||||
? 256
|
||||
: 128;
|
||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
|
||||
final mnemonic = bip39.generateMnemonic(strength: strength);
|
||||
final wallet = PolygonWallet(
|
||||
|
|
|
@ -102,11 +102,11 @@ PODS:
|
|||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_inappwebview (0.0.1):
|
||||
- flutter_inappwebview_ios (0.0.1):
|
||||
- Flutter
|
||||
- flutter_inappwebview/Core (= 0.0.1)
|
||||
- flutter_inappwebview_ios/Core (= 0.0.1)
|
||||
- OrderedSet (~> 5.0)
|
||||
- flutter_inappwebview/Core (0.0.1):
|
||||
- flutter_inappwebview_ios/Core (0.0.1):
|
||||
- Flutter
|
||||
- OrderedSet (~> 5.0)
|
||||
- flutter_mailer (0.0.1):
|
||||
|
@ -169,7 +169,7 @@ DEPENDENCIES:
|
|||
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
|
||||
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
|
||||
- flutter_mailer (from `.symlinks/plugins/flutter_mailer/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||
|
@ -224,8 +224,8 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_inappwebview:
|
||||
:path: ".symlinks/plugins/flutter_inappwebview/ios"
|
||||
flutter_inappwebview_ios:
|
||||
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
|
||||
flutter_mailer:
|
||||
:path: ".symlinks/plugins/flutter_mailer/ios"
|
||||
flutter_secure_storage:
|
||||
|
@ -274,29 +274,29 @@ SPEC CHECKSUMS:
|
|||
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
||||
file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
flutter_inappwebview: 3d32228f1304635e7c028b0d4252937730bbc6cf
|
||||
flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0
|
||||
flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
|
||||
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
||||
fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
|
||||
in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d
|
||||
local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605
|
||||
local_auth_ios: 1ba1475238daa33a6ffa2a29242558437be435ac
|
||||
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
||||
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
||||
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
|
||||
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
SDWebImage: 2aea163b50bfcb569a2726b6a754c54a4506fcf6
|
||||
sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
|
||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
|
||||
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
|
||||
SwiftProtobuf: 40bd808372cb8706108f22d28f8ab4a6b9bc6989
|
||||
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
|
||||
UnstoppableDomainsResolution: c3c67f4d0a5e2437cb00d4bd50c2e00d6e743841
|
||||
url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b
|
||||
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
|
||||
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
|
||||
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
|
||||
|
||||
|
|
|
@ -2,11 +2,11 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
|
||||
class BuyException implements Exception {
|
||||
BuyException({required this.description, required this.text});
|
||||
BuyException({required this.title, required this.content});
|
||||
|
||||
final BuyProviderDescription description;
|
||||
final String text;
|
||||
final String title;
|
||||
final String content;
|
||||
|
||||
@override
|
||||
String toString() => '${description.title}: $text';
|
||||
String toString() => '$title: $content';
|
||||
}
|
|
@ -1,27 +1,33 @@
|
|||
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class BuyProvider {
|
||||
BuyProvider({required this.wallet, required this.isTestEnvironment});
|
||||
BuyProvider({
|
||||
required this.wallet,
|
||||
required this.isTestEnvironment,
|
||||
});
|
||||
|
||||
final WalletBase wallet;
|
||||
final bool isTestEnvironment;
|
||||
|
||||
String get title;
|
||||
BuyProviderDescription get description;
|
||||
String get trackUrl;
|
||||
|
||||
WalletType get walletType => wallet.type;
|
||||
String get walletAddress => wallet.walletAddresses.address;
|
||||
String get walletId => wallet.id;
|
||||
String get providerDescription;
|
||||
|
||||
String get lightIcon;
|
||||
|
||||
String get darkIcon;
|
||||
|
||||
@override
|
||||
String toString() => title;
|
||||
|
||||
Future<String> requestUrl(String amount, String sourceCurrency);
|
||||
Future<Order> findOrderById(String id);
|
||||
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency);
|
||||
}
|
||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction);
|
||||
|
||||
Future<String> requestUrl(String amount, String sourceCurrency) => throw UnimplementedError();
|
||||
|
||||
Future<Order> findOrderById(String id) => throw UnimplementedError();
|
||||
|
||||
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) => throw UnimplementedError();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
|
@ -11,10 +12,9 @@ import 'package:flutter/material.dart';
|
|||
import 'package:http/http.dart' as http;
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class DFXBuyProvider {
|
||||
DFXBuyProvider({required WalletBase wallet}) : this._wallet = wallet;
|
||||
|
||||
final WalletBase _wallet;
|
||||
class DFXBuyProvider extends BuyProvider {
|
||||
DFXBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
|
||||
: super(wallet: wallet, isTestEnvironment: isTestEnvironment);
|
||||
|
||||
static const _baseUrl = 'api.dfx.swiss';
|
||||
static const _authPath = '/v1/auth/signMessage';
|
||||
|
@ -22,8 +22,20 @@ class DFXBuyProvider {
|
|||
static const _signInPath = '/v1/auth/signIn';
|
||||
static const walletName = 'CakeWallet';
|
||||
|
||||
@override
|
||||
String get title => 'DFX Connect';
|
||||
|
||||
@override
|
||||
String get providerDescription => S.current.dfx_option_description;
|
||||
|
||||
@override
|
||||
String get lightIcon => 'assets/images/dfx_light.png';
|
||||
|
||||
@override
|
||||
String get darkIcon => 'assets/images/dfx_dark.png';
|
||||
|
||||
String get assetOut {
|
||||
switch (_wallet.type) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.bitcoin:
|
||||
return 'BTC';
|
||||
case WalletType.bitcoinCash:
|
||||
|
@ -35,12 +47,12 @@ class DFXBuyProvider {
|
|||
case WalletType.ethereum:
|
||||
return 'ETH';
|
||||
default:
|
||||
throw Exception("WalletType is not available for DFX ${_wallet.type}");
|
||||
throw Exception("WalletType is not available for DFX ${wallet.type}");
|
||||
}
|
||||
}
|
||||
|
||||
String get blockchain {
|
||||
switch (_wallet.type) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.litecoin:
|
||||
|
@ -50,12 +62,14 @@ class DFXBuyProvider {
|
|||
case WalletType.ethereum:
|
||||
return 'Ethereum';
|
||||
default:
|
||||
throw Exception("WalletType is not available for DFX ${_wallet.type}");
|
||||
throw Exception("WalletType is not available for DFX ${wallet.type}");
|
||||
}
|
||||
}
|
||||
|
||||
String get walletAddress =>
|
||||
wallet.walletAddresses.primaryAddress ?? wallet.walletAddresses.address;
|
||||
|
||||
Future<String> getSignMessage() async {
|
||||
final walletAddress = _wallet.walletAddresses.address;
|
||||
final uri = Uri.https(_baseUrl, _authPath, {'address': walletAddress});
|
||||
|
||||
var response = await http.get(uri, headers: {'accept': 'application/json'});
|
||||
|
@ -71,7 +85,6 @@ class DFXBuyProvider {
|
|||
|
||||
Future<String> signUp() async {
|
||||
final signMessage = getSignature(await getSignMessage());
|
||||
final walletAddress = _wallet.walletAddresses.address;
|
||||
|
||||
final requestBody = jsonEncode({
|
||||
'wallet': walletName,
|
||||
|
@ -80,21 +93,26 @@ class DFXBuyProvider {
|
|||
});
|
||||
|
||||
final uri = Uri.https(_baseUrl, _signUpPath);
|
||||
var response = await http.post(uri,
|
||||
headers: {'Content-Type': 'application/json'}, body: requestBody);
|
||||
var response = await http.post(
|
||||
uri,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: requestBody,
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
final responseBody = jsonDecode(response.body);
|
||||
return responseBody['accessToken'] as String;
|
||||
} else if (response.statusCode == 403) {
|
||||
final responseBody = jsonDecode(response.body);
|
||||
final message = responseBody['message'] ?? 'Service unavailable in your country';
|
||||
throw Exception(message);
|
||||
} else {
|
||||
throw Exception(
|
||||
'Failed to sign up. Status: ${response.statusCode} ${response.body}');
|
||||
throw Exception('Failed to sign up. Status: ${response.statusCode} ${response.body}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> signIn() async {
|
||||
final signMessage = getSignature(await getSignMessage());
|
||||
final walletAddress = _wallet.walletAddresses.address;
|
||||
|
||||
final requestBody = jsonEncode({
|
||||
'address': walletAddress,
|
||||
|
@ -102,37 +120,44 @@ class DFXBuyProvider {
|
|||
});
|
||||
|
||||
final uri = Uri.https(_baseUrl, _signInPath);
|
||||
var response = await http.post(uri,
|
||||
headers: {'Content-Type': 'application/json'}, body: requestBody);
|
||||
var response = await http.post(
|
||||
uri,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: requestBody,
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
final responseBody = jsonDecode(response.body);
|
||||
return responseBody['accessToken'] as String;
|
||||
} else if (response.statusCode == 403) {
|
||||
final responseBody = jsonDecode(response.body);
|
||||
final message = responseBody['message'] ?? 'Service unavailable in your country';
|
||||
throw Exception(message);
|
||||
} else {
|
||||
throw Exception(
|
||||
'Failed to sign in. Status: ${response.statusCode} ${response.body}');
|
||||
throw Exception('Failed to sign in. Status: ${response.statusCode} ${response.body}');
|
||||
}
|
||||
}
|
||||
|
||||
String getSignature(String message) {
|
||||
switch (_wallet.type) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.ethereum:
|
||||
return _wallet.signMessage(message);
|
||||
return wallet.signMessage(message);
|
||||
case WalletType.monero:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.bitcoinCash:
|
||||
return _wallet.signMessage(message,
|
||||
address: _wallet.walletAddresses.address);
|
||||
return wallet.signMessage(message, address: walletAddress);
|
||||
default:
|
||||
throw Exception("WalletType is not available for DFX ${_wallet.type}");
|
||||
throw Exception("WalletType is not available for DFX ${wallet.type}");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> launchProvider(BuildContext context) async {
|
||||
@override
|
||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
|
||||
try {
|
||||
final assetOut = this.assetOut;
|
||||
final blockchain = this.blockchain;
|
||||
final actionType = isBuyAction == true ? '/buy' : '/sell';
|
||||
|
||||
String accessToken;
|
||||
|
||||
|
@ -146,7 +171,7 @@ class DFXBuyProvider {
|
|||
}
|
||||
}
|
||||
|
||||
final uri = Uri.https('services.dfx.swiss', '/buy', {
|
||||
final uri = Uri.https('services.dfx.swiss', actionType, {
|
||||
'session': accessToken,
|
||||
'lang': 'en',
|
||||
'asset-out': assetOut,
|
||||
|
@ -156,8 +181,7 @@ class DFXBuyProvider {
|
|||
|
||||
if (await canLaunchUrl(uri)) {
|
||||
if (DeviceInfo.instance.isMobile) {
|
||||
Navigator.of(context).pushNamed(Routes.webViewPage,
|
||||
arguments: [S.of(context).buy, uri]);
|
||||
Navigator.of(context).pushNamed(Routes.webViewPage, arguments: [title, uri]);
|
||||
} else {
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
import 'dart:convert';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:cake_wallet/buy/buy_exception.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
|
@ -14,17 +19,38 @@ import 'package:cw_core/wallet_type.dart';
|
|||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class MoonPaySellProvider {
|
||||
MoonPaySellProvider({this.isTest = false}) : baseUrl = isTest ? _baseTestUrl : _baseProductUrl;
|
||||
class MoonPaySellProvider extends BuyProvider {
|
||||
MoonPaySellProvider({
|
||||
required SettingsStore settingsStore,
|
||||
required WalletBase wallet,
|
||||
bool isTestEnvironment = false,
|
||||
}) : baseUrl = isTestEnvironment ? _baseTestUrl : _baseProductUrl,
|
||||
this._settingsStore = settingsStore,
|
||||
super(wallet: wallet, isTestEnvironment: isTestEnvironment);
|
||||
|
||||
final SettingsStore _settingsStore;
|
||||
|
||||
static const _baseTestUrl = 'sell-sandbox.moonpay.com';
|
||||
static const _baseProductUrl = 'sell.moonpay.com';
|
||||
|
||||
@override
|
||||
String get providerDescription =>
|
||||
'MoonPay offers a fast and simple way to buy and sell cryptocurrencies';
|
||||
|
||||
@override
|
||||
String get title => 'MoonPay';
|
||||
|
||||
@override
|
||||
String get lightIcon => 'assets/images/moonpay_light.png';
|
||||
|
||||
@override
|
||||
String get darkIcon => 'assets/images/moonpay_dark.png';
|
||||
|
||||
static String themeToMoonPayTheme(ThemeBase theme) {
|
||||
switch (theme.type) {
|
||||
case ThemeType.bright:
|
||||
return 'light';
|
||||
case ThemeType.light:
|
||||
return 'light';
|
||||
case ThemeType.dark:
|
||||
|
@ -35,13 +61,13 @@ class MoonPaySellProvider {
|
|||
static String get _apiKey => secrets.moonPayApiKey;
|
||||
|
||||
static String get _secretKey => secrets.moonPaySecretKey;
|
||||
final bool isTest;
|
||||
final String baseUrl;
|
||||
|
||||
Future<Uri> requestUrl(
|
||||
{required CryptoCurrency currency,
|
||||
required String refundWalletAddress,
|
||||
required SettingsStore settingsStore}) async {
|
||||
Future<Uri> requestMoonPayUrl({
|
||||
required CryptoCurrency currency,
|
||||
required String refundWalletAddress,
|
||||
required SettingsStore settingsStore,
|
||||
}) async {
|
||||
final customParams = {
|
||||
'theme': themeToMoonPayTheme(settingsStore.currentTheme),
|
||||
'language': settingsStore.languageCode,
|
||||
|
@ -51,20 +77,22 @@ class MoonPaySellProvider {
|
|||
};
|
||||
|
||||
final originalUri = Uri.https(
|
||||
baseUrl,
|
||||
'',
|
||||
<String, dynamic>{
|
||||
'apiKey': _apiKey,
|
||||
'defaultBaseCurrencyCode': currency.toString().toLowerCase(),
|
||||
'refundWalletAddress': refundWalletAddress
|
||||
}..addAll(customParams));
|
||||
baseUrl,
|
||||
'',
|
||||
<String, dynamic>{
|
||||
'apiKey': _apiKey,
|
||||
'defaultBaseCurrencyCode': currency.toString().toLowerCase(),
|
||||
'refundWalletAddress': refundWalletAddress,
|
||||
}..addAll(customParams),
|
||||
);
|
||||
|
||||
final messageBytes = utf8.encode('?${originalUri.query}');
|
||||
final key = utf8.encode(_secretKey);
|
||||
final hmac = Hmac(sha256, key);
|
||||
final digest = hmac.convert(messageBytes);
|
||||
final signature = base64.encode(digest.bytes);
|
||||
|
||||
if (isTest) {
|
||||
if (isTestEnvironment) {
|
||||
return originalUri;
|
||||
}
|
||||
|
||||
|
@ -73,6 +101,39 @@ class MoonPaySellProvider {
|
|||
final signedUri = originalUri.replace(queryParameters: query);
|
||||
return signedUri;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
|
||||
try {
|
||||
final uri = await requestMoonPayUrl(
|
||||
currency: wallet.currency,
|
||||
refundWalletAddress: wallet.walletAddresses.address,
|
||||
settingsStore: _settingsStore,
|
||||
);
|
||||
|
||||
if (await canLaunchUrl(uri)) {
|
||||
if (DeviceInfo.instance.isMobile) {
|
||||
Navigator.of(context).pushNamed(Routes.webViewPage, arguments: ['MoonPay', uri]);
|
||||
} else {
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
}
|
||||
} else {
|
||||
throw Exception('Could not launch URL');
|
||||
}
|
||||
} catch (e) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: 'MoonPay',
|
||||
alertContent: 'The MoonPay service is currently unavailable: $e',
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MoonPayBuyProvider extends BuyProvider {
|
||||
|
@ -94,16 +155,21 @@ class MoonPayBuyProvider extends BuyProvider {
|
|||
String get title => 'MoonPay';
|
||||
|
||||
@override
|
||||
BuyProviderDescription get description => BuyProviderDescription.moonPay;
|
||||
|
||||
String get currencyCode => walletTypeToCryptoCurrency(walletType).title.toLowerCase();
|
||||
String get providerDescription =>
|
||||
'MoonPay offers a fast and simple way to buy and sell cryptocurrencies';
|
||||
|
||||
@override
|
||||
String get lightIcon => 'assets/images/moonpay_light.png';
|
||||
|
||||
@override
|
||||
String get darkIcon => 'assets/images/moonpay_dark.png';
|
||||
|
||||
String get currencyCode => walletTypeToCryptoCurrency(wallet.type).title.toLowerCase();
|
||||
|
||||
String get trackUrl => baseUrl + '/transaction_receipt?transactionId=';
|
||||
|
||||
String baseUrl;
|
||||
|
||||
@override
|
||||
Future<String> requestUrl(String amount, String sourceCurrency) async {
|
||||
final enabledPaymentMethods = 'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay'
|
||||
'%2Csepa_bank_transfer%2Cgbp_bank_transfer%2Cgbp_open_banking_payment';
|
||||
|
@ -115,7 +181,7 @@ class MoonPayBuyProvider extends BuyProvider {
|
|||
'&enabledPaymentMethods=' +
|
||||
enabledPaymentMethods +
|
||||
'&walletAddress=' +
|
||||
walletAddress +
|
||||
wallet.walletAddresses.address +
|
||||
'&baseCurrencyCode=' +
|
||||
sourceCurrency.toLowerCase() +
|
||||
'&baseCurrencyAmount=' +
|
||||
|
@ -136,7 +202,6 @@ class MoonPayBuyProvider extends BuyProvider {
|
|||
return isTestEnvironment ? originalUrl : urlWithSignature;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) async {
|
||||
final url = _apiUrl +
|
||||
_currenciesSuffix +
|
||||
|
@ -152,7 +217,7 @@ class MoonPayBuyProvider extends BuyProvider {
|
|||
final response = await get(uri);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw BuyException(description: description, text: 'Quote is not found!');
|
||||
throw BuyException(title: providerDescription, content: 'Quote is not found!');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
@ -164,14 +229,13 @@ class MoonPayBuyProvider extends BuyProvider {
|
|||
sourceAmount: sourceAmount, destAmount: destAmount, minAmount: minSourceAmount);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Order> findOrderById(String id) async {
|
||||
final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey;
|
||||
final uri = Uri.parse(url);
|
||||
final response = await get(uri);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw BuyException(description: description, text: 'Transaction $id is not found!');
|
||||
throw BuyException(title: providerDescription, content: 'Transaction $id is not found!');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
@ -183,13 +247,13 @@ class MoonPayBuyProvider extends BuyProvider {
|
|||
|
||||
return Order(
|
||||
id: id,
|
||||
provider: description,
|
||||
provider: BuyProviderDescription.moonPay,
|
||||
transferId: id,
|
||||
state: state,
|
||||
createdAt: createdAt,
|
||||
amount: amount.toString(),
|
||||
receiveAddress: walletAddress,
|
||||
walletId: walletId);
|
||||
receiveAddress: wallet.walletAddresses.address,
|
||||
walletId: wallet.id);
|
||||
}
|
||||
|
||||
static Future<bool> onEnabled() async {
|
||||
|
@ -208,4 +272,8 @@ class MoonPayBuyProvider extends BuyProvider {
|
|||
|
||||
return isBuyEnable;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) =>
|
||||
throw UnimplementedError();
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
|
@ -9,20 +10,31 @@ import 'package:cw_core/wallet_base.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class OnRamperBuyProvider {
|
||||
OnRamperBuyProvider({required SettingsStore settingsStore, required WalletBase wallet})
|
||||
: this._settingsStore = settingsStore,
|
||||
this._wallet = wallet;
|
||||
|
||||
final SettingsStore _settingsStore;
|
||||
final WalletBase _wallet;
|
||||
class OnRamperBuyProvider extends BuyProvider {
|
||||
OnRamperBuyProvider(this._settingsStore,
|
||||
{required WalletBase wallet, bool isTestEnvironment = false})
|
||||
: super(wallet: wallet, isTestEnvironment: isTestEnvironment);
|
||||
|
||||
static const _baseUrl = 'buy.onramper.com';
|
||||
|
||||
final SettingsStore _settingsStore;
|
||||
|
||||
@override
|
||||
String get title => 'Onramper';
|
||||
|
||||
@override
|
||||
String get providerDescription => S.current.onramper_option_description;
|
||||
|
||||
@override
|
||||
String get lightIcon => 'assets/images/onramper_light.png';
|
||||
|
||||
@override
|
||||
String get darkIcon => 'assets/images/onramper_dark.png';
|
||||
|
||||
String get _apiKey => secrets.onramperApiKey;
|
||||
|
||||
String get _normalizeCryptoCurrency {
|
||||
switch (_wallet.currency) {
|
||||
switch (wallet.currency) {
|
||||
case CryptoCurrency.ltc:
|
||||
return "LTC_LITECOIN";
|
||||
case CryptoCurrency.xmr:
|
||||
|
@ -32,7 +44,7 @@ class OnRamperBuyProvider {
|
|||
case CryptoCurrency.nano:
|
||||
return "XNO_NANO";
|
||||
default:
|
||||
return _wallet.currency.title;
|
||||
return wallet.currency.title;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +52,7 @@ class OnRamperBuyProvider {
|
|||
return color.value.toRadixString(16).replaceAll(RegExp(r'^ff'), "");
|
||||
}
|
||||
|
||||
Uri requestUrl(BuildContext context) {
|
||||
Uri requestOnramperUrl(BuildContext context, bool? isBuyAction) {
|
||||
String primaryColor,
|
||||
secondaryColor,
|
||||
primaryTextColor,
|
||||
|
@ -50,9 +62,10 @@ class OnRamperBuyProvider {
|
|||
|
||||
primaryColor = getColorStr(Theme.of(context).primaryColor);
|
||||
secondaryColor = getColorStr(Theme.of(context).colorScheme.background);
|
||||
primaryTextColor = getColorStr(Theme.of(context).extension<CakeTextTheme>()!.titleColor);
|
||||
secondaryTextColor =
|
||||
getColorStr(Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor);
|
||||
primaryTextColor =
|
||||
getColorStr(Theme.of(context).extension<CakeTextTheme>()!.titleColor);
|
||||
secondaryTextColor = getColorStr(
|
||||
Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor);
|
||||
containerColor = getColorStr(Theme.of(context).colorScheme.background);
|
||||
cardColor = getColorStr(Theme.of(context).cardColor);
|
||||
|
||||
|
@ -60,27 +73,30 @@ class OnRamperBuyProvider {
|
|||
cardColor = getColorStr(Colors.white);
|
||||
}
|
||||
|
||||
final networkName = _wallet.currency.fullName?.toUpperCase().replaceAll(" ", "");
|
||||
final networkName =
|
||||
wallet.currency.fullName?.toUpperCase().replaceAll(" ", "");
|
||||
|
||||
return Uri.https(_baseUrl, '', <String, dynamic>{
|
||||
'apiKey': _apiKey,
|
||||
'defaultCrypto': _normalizeCryptoCurrency,
|
||||
'networkWallets': '${networkName}:${_wallet.walletAddresses.address}',
|
||||
'supportSell': "false",
|
||||
'sell_defaultCrypto': _normalizeCryptoCurrency,
|
||||
'networkWallets': '${networkName}:${wallet.walletAddresses.address}',
|
||||
'supportSwap': "false",
|
||||
'primaryColor': primaryColor,
|
||||
'secondaryColor': secondaryColor,
|
||||
'primaryTextColor': primaryTextColor,
|
||||
'secondaryTextColor': secondaryTextColor,
|
||||
'containerColor': containerColor,
|
||||
'cardColor': cardColor
|
||||
'cardColor': cardColor,
|
||||
'mode': isBuyAction == true ? 'buy' : 'sell',
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> launchProvider(BuildContext context) async {
|
||||
final uri = requestUrl(context);
|
||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
|
||||
final uri = requestOnramperUrl(context, isBuyAction);
|
||||
if (DeviceInfo.instance.isMobile) {
|
||||
Navigator.of(context).pushNamed(Routes.webViewPage, arguments: [S.of(context).buy, uri]);
|
||||
Navigator.of(context)
|
||||
.pushNamed(Routes.webViewPage, arguments: [title, uri]);
|
||||
} else {
|
||||
await launchUrl(uri);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
|
@ -10,40 +11,44 @@ import 'package:flutter/material.dart';
|
|||
import 'package:http/http.dart' as http;
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class RobinhoodBuyProvider {
|
||||
RobinhoodBuyProvider({required WalletBase wallet}) : this._wallet = wallet;
|
||||
|
||||
final WalletBase _wallet;
|
||||
class RobinhoodBuyProvider extends BuyProvider {
|
||||
RobinhoodBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
|
||||
: super(wallet: wallet, isTestEnvironment: isTestEnvironment);
|
||||
|
||||
static const _baseUrl = 'applink.robinhood.com';
|
||||
static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
|
||||
|
||||
@override
|
||||
String get title => 'Robinhood Connect';
|
||||
|
||||
@override
|
||||
String get providerDescription => S.current.robinhood_option_description;
|
||||
|
||||
@override
|
||||
String get lightIcon => 'assets/images/robinhood_light.png';
|
||||
|
||||
@override
|
||||
String get darkIcon => 'assets/images/robinhood_dark.png';
|
||||
|
||||
String get _applicationId => secrets.robinhoodApplicationId;
|
||||
|
||||
String get _apiSecret => secrets.robinhoodCIdApiSecret;
|
||||
|
||||
bool get isAvailable => [
|
||||
WalletType.bitcoin,
|
||||
WalletType.bitcoinCash,
|
||||
WalletType.litecoin,
|
||||
WalletType.ethereum
|
||||
].contains(_wallet.type);
|
||||
|
||||
String getSignature(String message) {
|
||||
switch (_wallet.type) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.ethereum:
|
||||
return _wallet.signMessage(message);
|
||||
return wallet.signMessage(message);
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.bitcoinCash:
|
||||
return _wallet.signMessage(message, address: _wallet.walletAddresses.address);
|
||||
return wallet.signMessage(message, address: wallet.walletAddresses.address);
|
||||
default:
|
||||
throw Exception("WalletType is not available for Robinhood ${_wallet.type}");
|
||||
throw Exception("WalletType is not available for Robinhood ${wallet.type}");
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> getConnectId() async {
|
||||
final walletAddress = _wallet.walletAddresses.address;
|
||||
final walletAddress = wallet.walletAddresses.address;
|
||||
final valid_until = (DateTime.now().millisecondsSinceEpoch / 1000).round() + 10;
|
||||
final message = "$_apiSecret:${valid_until}";
|
||||
|
||||
|
@ -64,22 +69,22 @@ class RobinhoodBuyProvider {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Uri> requestUrl() async {
|
||||
Future<Uri> requestProviderUrl() async {
|
||||
final connectId = await getConnectId();
|
||||
final networkName = _wallet.currency.fullName?.toUpperCase().replaceAll(" ", "_");
|
||||
final networkName = wallet.currency.fullName?.toUpperCase().replaceAll(" ", "_");
|
||||
|
||||
return Uri.https(_baseUrl, '/u/connect', <String, dynamic>{
|
||||
'applicationId': _applicationId,
|
||||
'connectId': connectId,
|
||||
'walletAddress': _wallet.walletAddresses.address,
|
||||
'userIdentifier': _wallet.walletAddresses.address,
|
||||
'walletAddress': wallet.walletAddresses.address,
|
||||
'userIdentifier': wallet.walletAddresses.address,
|
||||
'supportedNetworks': networkName
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> launchProvider(BuildContext context) async {
|
||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
|
||||
try {
|
||||
final uri = await requestUrl();
|
||||
final uri = await requestProviderUrl();
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
} catch (_) {
|
||||
await showPopUp<void>(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:convert';
|
||||
import 'package:cake_wallet/buy/buy_exception.dart';
|
||||
import 'package:flutter/src/widgets/framework.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
|
@ -12,10 +13,8 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
|||
|
||||
class WyreBuyProvider extends BuyProvider {
|
||||
WyreBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
|
||||
: baseApiUrl = isTestEnvironment
|
||||
? _baseTestApiUrl
|
||||
: _baseProductApiUrl,
|
||||
super(wallet: wallet, isTestEnvironment: isTestEnvironment);
|
||||
: baseApiUrl = isTestEnvironment ? _baseTestApiUrl : _baseProductApiUrl,
|
||||
super(wallet: wallet, isTestEnvironment: isTestEnvironment);
|
||||
|
||||
static const _baseTestApiUrl = 'https://api.testwyre.com';
|
||||
static const _baseProductApiUrl = 'https://api.sendwyre.com';
|
||||
|
@ -35,26 +34,27 @@ class WyreBuyProvider extends BuyProvider {
|
|||
String get title => 'Wyre';
|
||||
|
||||
@override
|
||||
BuyProviderDescription get description => BuyProviderDescription.wyre;
|
||||
String get providerDescription => '';
|
||||
|
||||
@override
|
||||
String get trackUrl => isTestEnvironment
|
||||
? _trackTestUrl
|
||||
: _trackProductUrl;
|
||||
String get lightIcon => 'assets/images/robinhood_light.png';
|
||||
|
||||
@override
|
||||
String get darkIcon => 'assets/images/robinhood_dark.png';
|
||||
|
||||
String get trackUrl => isTestEnvironment ? _trackTestUrl : _trackProductUrl;
|
||||
|
||||
String baseApiUrl;
|
||||
|
||||
@override
|
||||
Future<String> requestUrl(String amount, String sourceCurrency) async {
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
final url = baseApiUrl + _ordersSuffix + _reserveSuffix +
|
||||
_timeStampSuffix + timestamp;
|
||||
final url = baseApiUrl + _ordersSuffix + _reserveSuffix + _timeStampSuffix + timestamp;
|
||||
final uri = Uri.parse(url);
|
||||
final body = {
|
||||
'amount': amount,
|
||||
'sourceCurrency': sourceCurrency,
|
||||
'destCurrency': walletTypeToCryptoCurrency(walletType).title,
|
||||
'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress,
|
||||
'destCurrency': walletTypeToCryptoCurrency(wallet.type).title,
|
||||
'dest': walletTypeToString(wallet.type).toLowerCase() + ':' + wallet.walletAddresses.address,
|
||||
'referrerAccountId': _accountId,
|
||||
'lockFields': ['amount', 'sourceCurrency', 'destCurrency', 'dest']
|
||||
};
|
||||
|
@ -67,9 +67,7 @@ class WyreBuyProvider extends BuyProvider {
|
|||
body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw BuyException(
|
||||
description: description,
|
||||
text: 'Url $url is not found!');
|
||||
throw BuyException(title: providerDescription, content: 'Url $url is not found!');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
@ -77,14 +75,13 @@ class WyreBuyProvider extends BuyProvider {
|
|||
return urlFromResponse;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) async {
|
||||
final quoteUrl = _baseProductApiUrl + _ordersSuffix + _quoteSuffix;
|
||||
final body = {
|
||||
'amount': amount,
|
||||
'sourceCurrency': sourceCurrency,
|
||||
'destCurrency': walletTypeToCryptoCurrency(walletType).title,
|
||||
'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress,
|
||||
'destCurrency': walletTypeToCryptoCurrency(wallet.type).title,
|
||||
'dest': walletTypeToString(wallet.type).toLowerCase() + ':' + wallet.walletAddresses.address,
|
||||
'accountId': _accountId,
|
||||
'country': _countryCode
|
||||
};
|
||||
|
@ -98,9 +95,7 @@ class WyreBuyProvider extends BuyProvider {
|
|||
body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw BuyException(
|
||||
description: description,
|
||||
text: 'Quote is not found!');
|
||||
throw BuyException(title: providerDescription, content: 'Quote is not found!');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
@ -108,58 +103,55 @@ class WyreBuyProvider extends BuyProvider {
|
|||
final destAmount = responseJSON['destAmount'] as double;
|
||||
final achAmount = responseJSON['sourceAmountWithoutFees'] as double;
|
||||
|
||||
return BuyAmount(sourceAmount: sourceAmount, destAmount: destAmount, achSourceAmount: achAmount);
|
||||
return BuyAmount(
|
||||
sourceAmount: sourceAmount, destAmount: destAmount, achSourceAmount: achAmount);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Order> findOrderById(String id) async {
|
||||
final orderUrl = baseApiUrl + _ordersSuffix + '/$id';
|
||||
final orderUri = Uri.parse(orderUrl);
|
||||
final orderResponse = await get(orderUri);
|
||||
|
||||
if (orderResponse.statusCode != 200) {
|
||||
throw BuyException(
|
||||
description: description,
|
||||
text: 'Order $id is not found!');
|
||||
throw BuyException(title: providerDescription, content: 'Order $id is not found!');
|
||||
}
|
||||
|
||||
final orderResponseJSON =
|
||||
json.decode(orderResponse.body) as Map<String, dynamic>;
|
||||
final orderResponseJSON = json.decode(orderResponse.body) as Map<String, dynamic>;
|
||||
final transferId = orderResponseJSON['transferId'] as String;
|
||||
final from = orderResponseJSON['sourceCurrency'] as String;
|
||||
final to = orderResponseJSON['destCurrency'] as String;
|
||||
final status = orderResponseJSON['status'] as String;
|
||||
final state = TradeState.deserialize(raw: status.toLowerCase());
|
||||
final createdAtRaw = orderResponseJSON['createdAt'] as int;
|
||||
final createdAt =
|
||||
DateTime.fromMillisecondsSinceEpoch(createdAtRaw).toLocal();
|
||||
final createdAt = DateTime.fromMillisecondsSinceEpoch(createdAtRaw).toLocal();
|
||||
|
||||
final transferUrl =
|
||||
baseApiUrl + _transferSuffix + transferId + _trackSuffix;
|
||||
final transferUrl = baseApiUrl + _transferSuffix + transferId + _trackSuffix;
|
||||
final transferUri = Uri.parse(transferUrl);
|
||||
final transferResponse = await get(transferUri);
|
||||
|
||||
if (transferResponse.statusCode != 200) {
|
||||
throw BuyException(
|
||||
description: description,
|
||||
text: 'Transfer $transferId is not found!');
|
||||
throw BuyException(title: providerDescription, content: 'Transfer $transferId is not found!');
|
||||
}
|
||||
|
||||
final transferResponseJSON =
|
||||
json.decode(transferResponse.body) as Map<String, dynamic>;
|
||||
final transferResponseJSON = json.decode(transferResponse.body) as Map<String, dynamic>;
|
||||
final amount = transferResponseJSON['destAmount'] as double;
|
||||
|
||||
return Order(
|
||||
id: id,
|
||||
provider: description,
|
||||
provider: BuyProviderDescription.wyre,
|
||||
transferId: transferId,
|
||||
from: from,
|
||||
to: to,
|
||||
state: state,
|
||||
createdAt: createdAt,
|
||||
amount: amount.toString(),
|
||||
receiveAddress: walletAddress,
|
||||
walletId: walletId
|
||||
);
|
||||
receiveAddress: wallet.walletAddresses.address,
|
||||
walletId: wallet.id);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) {
|
||||
// TODO: implement launchProvider
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ class EvmChainServiceImpl implements ChainService {
|
|||
try {
|
||||
// Load the private key
|
||||
final List<ChainKeyModel> keys = wcKeyService
|
||||
.getKeysForChain(getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type));
|
||||
.getKeysForChain(appStore.wallet!);
|
||||
|
||||
final Credentials credentials = EthPrivateKey.fromHex(keys[0].privateKey);
|
||||
|
||||
|
@ -177,7 +177,7 @@ class EvmChainServiceImpl implements ChainService {
|
|||
try {
|
||||
// Load the private key
|
||||
final List<ChainKeyModel> keys = wcKeyService
|
||||
.getKeysForChain(getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type));
|
||||
.getKeysForChain(appStore.wallet!);
|
||||
|
||||
final EthPrivateKey credentials = EthPrivateKey.fromHex(keys[0].privateKey);
|
||||
|
||||
|
@ -215,7 +215,7 @@ class EvmChainServiceImpl implements ChainService {
|
|||
|
||||
// Load the private key
|
||||
final List<ChainKeyModel> keys = wcKeyService
|
||||
.getKeysForChain(getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type));
|
||||
.getKeysForChain(appStore.wallet!);
|
||||
|
||||
final Credentials credentials = EthPrivateKey.fromHex(keys[0].privateKey);
|
||||
|
||||
|
@ -275,7 +275,7 @@ class EvmChainServiceImpl implements ChainService {
|
|||
}
|
||||
|
||||
final List<ChainKeyModel> keys = wcKeyService
|
||||
.getKeysForChain(getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type));
|
||||
.getKeysForChain(appStore.wallet!);
|
||||
|
||||
return EthSigUtil.signTypedData(
|
||||
privateKey: keys[0].privateKey,
|
||||
|
|
|
@ -1,48 +1,22 @@
|
|||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
abstract class WalletConnectKeyService {
|
||||
/// Returns a list of all the keys.
|
||||
List<ChainKeyModel> getKeys();
|
||||
|
||||
/// Returns a list of all the chain ids.
|
||||
List<String> getChains();
|
||||
List<ChainKeyModel> getKeys(WalletBase wallet);
|
||||
|
||||
/// Returns a list of all the keys for a given chain id.
|
||||
/// If the chain is not found, returns an empty list.
|
||||
/// - [chain]: The chain to get the keys for.
|
||||
List<ChainKeyModel> getKeysForChain(String chain);
|
||||
List<ChainKeyModel> getKeysForChain(WalletBase wallet);
|
||||
|
||||
/// Returns a list of all the accounts in namespace:chainId:address format.
|
||||
List<String> getAllAccounts();
|
||||
}
|
||||
|
||||
class KeyServiceImpl implements WalletConnectKeyService {
|
||||
KeyServiceImpl(this.wallet)
|
||||
: _keys = [
|
||||
ChainKeyModel(
|
||||
chains: [
|
||||
'eip155:1',
|
||||
'eip155:5',
|
||||
'eip155:137',
|
||||
'eip155:42161',
|
||||
'eip155:80001',
|
||||
],
|
||||
privateKey: _getPrivateKeyForWallet(wallet),
|
||||
publicKey: _getPublicKeyForWallet(wallet),
|
||||
),
|
||||
];
|
||||
|
||||
late final WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
|
||||
|
||||
late final List<ChainKeyModel> _keys;
|
||||
|
||||
static String _getPrivateKeyForWallet(WalletBase wallet) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.ethereum:
|
||||
|
@ -64,31 +38,31 @@ class KeyServiceImpl implements WalletConnectKeyService {
|
|||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> getChains() {
|
||||
final List<String> chainIds = [];
|
||||
for (final ChainKeyModel key in _keys) {
|
||||
chainIds.addAll(key.chains);
|
||||
}
|
||||
return chainIds;
|
||||
List<ChainKeyModel> getKeys(WalletBase wallet) {
|
||||
final keys = [
|
||||
ChainKeyModel(
|
||||
chains: [
|
||||
'eip155:1',
|
||||
'eip155:5',
|
||||
'eip155:137',
|
||||
'eip155:42161',
|
||||
'eip155:80001',
|
||||
],
|
||||
privateKey: _getPrivateKeyForWallet(wallet),
|
||||
publicKey: _getPublicKeyForWallet(wallet),
|
||||
),
|
||||
];
|
||||
return keys;
|
||||
}
|
||||
|
||||
@override
|
||||
List<ChainKeyModel> getKeys() => _keys;
|
||||
List<ChainKeyModel> getKeysForChain(WalletBase wallet) {
|
||||
final chain = getChainNameSpaceAndIdBasedOnWalletType(wallet.type);
|
||||
|
||||
@override
|
||||
List<ChainKeyModel> getKeysForChain(String chain) {
|
||||
return _keys.where((e) => e.chains.contains(chain)).toList();
|
||||
}
|
||||
final keys = getKeys(wallet);
|
||||
|
||||
@override
|
||||
List<String> getAllAccounts() {
|
||||
final List<String> accounts = [];
|
||||
for (final ChainKeyModel key in _keys) {
|
||||
for (final String chain in key.chains) {
|
||||
accounts.add('$chain:${key.publicKey}');
|
||||
}
|
||||
}
|
||||
return accounts;
|
||||
return keys.where((e) => e.chains.contains(chain)).toList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ abstract class Web3WalletServiceBase with Store {
|
|||
);
|
||||
|
||||
// Setup our accounts
|
||||
List<ChainKeyModel> chainKeys = walletKeyService.getKeys();
|
||||
List<ChainKeyModel> chainKeys = walletKeyService.getKeys(appStore.wallet!);
|
||||
for (final chainKey in chainKeys) {
|
||||
for (final chainId in chainKey.chains) {
|
||||
_web3Wallet.registerAccount(
|
||||
|
@ -136,6 +136,7 @@ abstract class Web3WalletServiceBase with Store {
|
|||
_web3Wallet.onAuthRequest.unsubscribe(_onAuthRequest);
|
||||
_web3Wallet.core.pairing.onPairingDelete.unsubscribe(_onPairingDelete);
|
||||
_web3Wallet.core.pairing.onPairingExpire.unsubscribe(_onPairingDelete);
|
||||
isInitialized = false;
|
||||
}
|
||||
|
||||
Web3Wallet getWeb3Wallet() {
|
||||
|
@ -236,7 +237,7 @@ abstract class Web3WalletServiceBase with Store {
|
|||
Future<void> _onAuthRequest(AuthRequest? args) async {
|
||||
if (args != null) {
|
||||
final chaindIdNamespace = getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type);
|
||||
List<ChainKeyModel> chainKeys = walletKeyService.getKeysForChain(chaindIdNamespace);
|
||||
List<ChainKeyModel> chainKeys = walletKeyService.getKeysForChain(appStore.wallet!);
|
||||
// Create the message to be signed
|
||||
final String iss = 'did:pkh:$chaindIdNamespace:${chainKeys.first.publicKey}';
|
||||
final Widget modalWidget = Web3RequestModal(
|
||||
|
|
34
lib/di.dart
|
@ -5,6 +5,7 @@ import 'package:cake_wallet/entities/preferences_key.dart';
|
|||
import 'package:cake_wallet/anonpay/anonpay_api.dart';
|
||||
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
|
||||
|
@ -58,6 +59,7 @@ import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart
|
|||
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_info_page.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
|
||||
|
@ -354,14 +356,18 @@ Future<void> setup({
|
|||
settingsStore: getIt.get<SettingsStore>(),
|
||||
walletInfoSource: _walletInfoSource));
|
||||
|
||||
getIt.registerFactoryParam<AdvancedPrivacySettingsViewModel, WalletType, void>(
|
||||
(type, _) => AdvancedPrivacySettingsViewModel(type, getIt.get<SettingsStore>()));
|
||||
|
||||
getIt.registerFactory<WalletLoadingService>(() => WalletLoadingService(
|
||||
getIt.get<SharedPreferences>(),
|
||||
getIt.get<KeyService>(),
|
||||
(WalletType type) => getIt.get<WalletService>(param1: type)));
|
||||
|
||||
getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) => WalletNewVM(
|
||||
getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
|
||||
type: type));
|
||||
getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) =>
|
||||
WalletNewVM(getIt.get<AppStore>(),
|
||||
getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
|
||||
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),type: type));
|
||||
|
||||
getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) {
|
||||
return WalletRestorationFromQRVM(getIt.get<AppStore>(),
|
||||
|
@ -487,7 +493,7 @@ Future<void> setup({
|
|||
|
||||
final appStore = getIt.get<AppStore>();
|
||||
|
||||
getIt.registerLazySingleton<WalletConnectKeyService>(() => KeyServiceImpl(appStore.wallet!));
|
||||
getIt.registerLazySingleton<WalletConnectKeyService>(() => KeyServiceImpl());
|
||||
|
||||
getIt.registerLazySingleton<Web3WalletService>(() {
|
||||
final Web3WalletService web3WalletService = Web3WalletService(
|
||||
|
@ -532,6 +538,8 @@ Future<void> setup({
|
|||
getIt.registerFactory<TransactionsPage>(
|
||||
() => TransactionsPage(dashboardViewModel: getIt.get<DashboardViewModel>()));
|
||||
|
||||
getIt.registerFactory<Setup2FAInfoPage>(() => Setup2FAInfoPage());
|
||||
|
||||
getIt.registerFactory<Setup2FAPage>(
|
||||
() => Setup2FAPage(setup2FAViewModel: getIt.get<Setup2FAViewModel>()));
|
||||
|
||||
|
@ -809,8 +817,12 @@ Future<void> setup({
|
|||
getIt
|
||||
.registerFactory<DFXBuyProvider>(() => DFXBuyProvider(wallet: getIt.get<AppStore>().wallet!));
|
||||
|
||||
getIt.registerFactory<MoonPaySellProvider>(() => MoonPaySellProvider(
|
||||
settingsStore: getIt.get<AppStore>().settingsStore,
|
||||
wallet: getIt.get<AppStore>().wallet!));
|
||||
|
||||
getIt.registerFactory<OnRamperBuyProvider>(() => OnRamperBuyProvider(
|
||||
settingsStore: getIt.get<AppStore>().settingsStore,
|
||||
getIt.get<AppStore>().settingsStore,
|
||||
wallet: getIt.get<AppStore>().wallet!,
|
||||
));
|
||||
|
||||
|
@ -924,9 +936,9 @@ Future<void> setup({
|
|||
getIt.registerFactoryParam<NewWalletTypePage, void Function(BuildContext, WalletType), bool?>(
|
||||
(param1, isCreate) => NewWalletTypePage(onTypeSelected: param1, isCreate: isCreate ?? true));
|
||||
|
||||
getIt.registerFactoryParam<PreSeedPage, WalletType, AdvancedPrivacySettingsViewModel>(
|
||||
(WalletType type, AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel) =>
|
||||
PreSeedPage(type, advancedPrivacySettingsViewModel, getIt.get<SeedTypeViewModel>()));
|
||||
getIt.registerFactoryParam<PreSeedPage, int, void>(
|
||||
(seedPhraseLength, _)
|
||||
=> PreSeedPage(seedPhraseLength));
|
||||
|
||||
getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) =>
|
||||
TradeDetailsViewModel(
|
||||
|
@ -959,7 +971,8 @@ Future<void> setup({
|
|||
|
||||
getIt.registerFactory(() => BuyAmountViewModel());
|
||||
|
||||
getIt.registerFactory(() => BuyOptionsPage(getIt.get<DashboardViewModel>()));
|
||||
getIt.registerFactoryParam<BuySellOptionsPage, bool, void>(
|
||||
(isBuyOption, _) => BuySellOptionsPage(getIt.get<DashboardViewModel>(), isBuyOption));
|
||||
|
||||
getIt.registerFactory(() {
|
||||
final wallet = getIt.get<AppStore>().wallet;
|
||||
|
@ -1172,9 +1185,6 @@ Future<void> setup({
|
|||
IoniaPaymentStatusPage(
|
||||
getIt.get<IoniaPaymentStatusViewModel>(param1: paymentInfo, param2: committedInfo)));
|
||||
|
||||
getIt.registerFactoryParam<AdvancedPrivacySettingsViewModel, WalletType, void>(
|
||||
(type, _) => AdvancedPrivacySettingsViewModel(type, getIt.get<SettingsStore>()));
|
||||
|
||||
getIt.registerFactoryParam<WalletUnlockLoadableViewModel, WalletUnlockArguments, void>((args, _) {
|
||||
final currentWalletName =
|
||||
getIt.get<SharedPreferences>().getString(PreferencesKey.currentWalletName) ?? '';
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
enum BuyProviderType {
|
||||
AskEachTime,
|
||||
Robinhood,
|
||||
Onramper,
|
||||
DFX;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
switch (this) {
|
||||
case BuyProviderType.AskEachTime:
|
||||
return S.current.ask_each_time;
|
||||
case BuyProviderType.Robinhood:
|
||||
return "Robinhood";
|
||||
case BuyProviderType.Onramper:
|
||||
return "Onramper";
|
||||
case BuyProviderType.DFX:
|
||||
return "DFX";
|
||||
}
|
||||
}
|
||||
|
||||
static List<BuyProviderType> getAvailableProviders(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return [
|
||||
BuyProviderType.AskEachTime,
|
||||
BuyProviderType.Onramper
|
||||
];
|
||||
case WalletType.monero:
|
||||
return [
|
||||
BuyProviderType.AskEachTime,
|
||||
BuyProviderType.Onramper,
|
||||
BuyProviderType.DFX
|
||||
];
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.ethereum:
|
||||
return [
|
||||
BuyProviderType.AskEachTime,
|
||||
BuyProviderType.Onramper,
|
||||
BuyProviderType.DFX,
|
||||
BuyProviderType.Robinhood
|
||||
];
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
return [
|
||||
BuyProviderType.AskEachTime,
|
||||
BuyProviderType.Onramper,
|
||||
BuyProviderType.Robinhood
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
|||
static List<FiatCurrency> get all => _all.values.toList();
|
||||
|
||||
static List<FiatCurrency> get currenciesAvailableToBuyWith =>
|
||||
[aud, bgn, brl, cad, chf, clp, cop, czk, dkk, egp, eur, gbp, gtq, hkd, hrk, huf, idr, ils, inr, isk, jpy, krw, mad, mxn, myr, ngn, nok, nzd, php, pkr, pln, ron, sek, sgd, thb, twd, usd, vnd, zar];
|
||||
[aud, bgn, brl, cad, chf, clp, cop, czk, dkk, egp, eur, gbp, gtq, hkd, hrk, huf, idr, ils, inr, isk, jpy, krw, mad, mxn, myr, ngn, nok, nzd, php, pkr, pln, ron, sek, sgd, thb, twd, usd, vnd, zar, tur];
|
||||
|
||||
static const ars = FiatCurrency(symbol: 'ARS', countryCode: "arg", fullName: "Argentine Peso");
|
||||
static const aud = FiatCurrency(symbol: 'AUD', countryCode: "aus", fullName: "Australian Dollar");
|
||||
|
@ -60,6 +60,7 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
|||
static const vef = FiatCurrency(symbol: 'VEF', countryCode: "ven", fullName: "Venezuelan Bolivar Bolívar");
|
||||
static const vnd = FiatCurrency(symbol: 'VND', countryCode: "vnm", fullName: "Vietnamese Dong đồng");
|
||||
static const zar = FiatCurrency(symbol: 'ZAR', countryCode: "saf", fullName: "South African Rand");
|
||||
static const tur = FiatCurrency(symbol: 'TRY', countryCode: "tur", fullName: "Turkish Lira");
|
||||
|
||||
static final _all = {
|
||||
FiatCurrency.ars.raw: FiatCurrency.ars,
|
||||
|
@ -109,7 +110,8 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
|||
FiatCurrency.usd.raw: FiatCurrency.usd,
|
||||
FiatCurrency.vef.raw: FiatCurrency.vef,
|
||||
FiatCurrency.vnd.raw: FiatCurrency.vnd,
|
||||
FiatCurrency.zar.raw: FiatCurrency.zar
|
||||
FiatCurrency.zar.raw: FiatCurrency.zar,
|
||||
FiatCurrency.tur.raw: FiatCurrency.tur,
|
||||
};
|
||||
|
||||
static FiatCurrency deserialize({required String raw}) => _all[raw]!;
|
||||
|
|
|
@ -25,7 +25,7 @@ Future<void> loadCurrentWallet({String? password}) async {
|
|||
type,
|
||||
name,
|
||||
password: password);
|
||||
appStore.changeCurrentWallet(wallet);
|
||||
await appStore.changeCurrentWallet(wallet);
|
||||
|
||||
getIt.get<BackgroundTasks>().registerSyncTask();
|
||||
}
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/buy_provider_types.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.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/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class MainActions {
|
||||
final String Function(BuildContext context) name;
|
||||
|
@ -46,53 +37,20 @@ class MainActions {
|
|||
canShow: (viewModel) => viewModel.hasBuyAction,
|
||||
onTap: (BuildContext context, DashboardViewModel viewModel) async {
|
||||
if (!viewModel.isEnabledBuyAction) {
|
||||
await _showErrorDialog(context, S.of(context).unsupported_asset);
|
||||
return;
|
||||
}
|
||||
|
||||
final defaultBuyProvider = viewModel.defaultBuyProvider;
|
||||
try {
|
||||
await _launchProviderByType(context, defaultBuyProvider);
|
||||
defaultBuyProvider != null
|
||||
? await defaultBuyProvider.launchProvider(context, true)
|
||||
: await Navigator.of(context).pushNamed(Routes.buySellPage, arguments: true);
|
||||
} catch (e) {
|
||||
await _showErrorDialog(context, e.toString());
|
||||
await _showErrorDialog(context, defaultBuyProvider.toString(), e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
static Future<void> _launchProviderByType(BuildContext context, BuyProviderType providerType) async {
|
||||
switch (providerType) {
|
||||
case BuyProviderType.AskEachTime:
|
||||
Navigator.pushNamed(context, Routes.buy);
|
||||
break;
|
||||
case BuyProviderType.Onramper:
|
||||
await getIt.get<OnRamperBuyProvider>().launchProvider(context);
|
||||
break;
|
||||
case BuyProviderType.Robinhood:
|
||||
await getIt.get<RobinhoodBuyProvider>().launchProvider(context);
|
||||
break;
|
||||
case BuyProviderType.DFX:
|
||||
await getIt.get<DFXBuyProvider>().launchProvider(context);
|
||||
break;
|
||||
default:
|
||||
throw UnsupportedError('Unsupported buy provider type');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Future<void> _showErrorDialog(BuildContext context, String errorMessage) async {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).buy,
|
||||
alertContent: errorMessage,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static MainActions receiveAction = MainActions._(
|
||||
name: (context) => S.of(context).receive,
|
||||
image: 'assets/images/received.png',
|
||||
|
@ -127,42 +85,33 @@ class MainActions {
|
|||
isEnabled: (viewModel) => viewModel.isEnabledSellAction,
|
||||
canShow: (viewModel) => viewModel.hasSellAction,
|
||||
onTap: (BuildContext context, DashboardViewModel viewModel) async {
|
||||
final walletType = viewModel.type;
|
||||
if (!viewModel.isEnabledSellAction) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (walletType) {
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.bitcoinCash:
|
||||
if (viewModel.isEnabledSellAction) {
|
||||
final moonPaySellProvider = MoonPaySellProvider();
|
||||
final uri = await moonPaySellProvider.requestUrl(
|
||||
currency: viewModel.wallet.currency,
|
||||
refundWalletAddress: viewModel.wallet.walletAddresses.address,
|
||||
settingsStore: viewModel.settingsStore,
|
||||
);
|
||||
if (DeviceInfo.instance.isMobile) {
|
||||
Navigator.of(context).pushNamed(Routes.webViewPage,
|
||||
arguments: [S.of(context).sell, uri]);
|
||||
} else {
|
||||
await launchUrl(uri);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).sell,
|
||||
alertContent: S.of(context).unsupported_asset,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
},
|
||||
);
|
||||
final defaultSellProvider = viewModel.defaultSellProvider;
|
||||
try {
|
||||
defaultSellProvider != null
|
||||
? await defaultSellProvider.launchProvider(context, false)
|
||||
: await Navigator.of(context).pushNamed(Routes.buySellPage, arguments: false);
|
||||
} catch (e) {
|
||||
await _showErrorDialog(context, defaultSellProvider.toString(), e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> _showErrorDialog(
|
||||
BuildContext context, String title, String errorMessage) async {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: title,
|
||||
alertContent: errorMessage,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
108
lib/entities/provider_types.dart
Normal file
|
@ -0,0 +1,108 @@
|
|||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
enum ProviderType {
|
||||
askEachTime,
|
||||
robinhood,
|
||||
dfx,
|
||||
onramper,
|
||||
moonpaySell,
|
||||
}
|
||||
|
||||
extension ProviderTypeName on ProviderType {
|
||||
String get title {
|
||||
switch (this) {
|
||||
case ProviderType.askEachTime:
|
||||
return 'Ask each time';
|
||||
case ProviderType.robinhood:
|
||||
return 'Robinhood Connect';
|
||||
case ProviderType.dfx:
|
||||
return 'DFX Connect';
|
||||
case ProviderType.onramper:
|
||||
return 'Onramper';
|
||||
case ProviderType.moonpaySell:
|
||||
return 'MoonPay';
|
||||
}
|
||||
}
|
||||
|
||||
String get id {
|
||||
switch (this) {
|
||||
case ProviderType.askEachTime:
|
||||
return 'ask_each_time_provider';
|
||||
case ProviderType.robinhood:
|
||||
return 'robinhood_connect_provider';
|
||||
case ProviderType.dfx:
|
||||
return 'dfx_connect_provider';
|
||||
case ProviderType.onramper:
|
||||
return 'onramper_provider';
|
||||
case ProviderType.moonpaySell:
|
||||
return 'moonpay_provider';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ProvidersHelper {
|
||||
static List<ProviderType> getAvailableBuyProviderTypes(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return [ProviderType.askEachTime, ProviderType.onramper];
|
||||
case WalletType.monero:
|
||||
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.dfx];
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.ethereum:
|
||||
return [
|
||||
ProviderType.askEachTime,
|
||||
ProviderType.onramper,
|
||||
ProviderType.dfx,
|
||||
ProviderType.robinhood,
|
||||
];
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood];
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
case WalletType.polygon:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static List<ProviderType> getAvailableSellProviderTypes(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.ethereum:
|
||||
return [ProviderType.askEachTime, ProviderType.onramper,
|
||||
ProviderType.moonpaySell, ProviderType.dfx];
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
return [ProviderType.askEachTime, ProviderType.moonpaySell];
|
||||
case WalletType.monero:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
case WalletType.polygon:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static BuyProvider? getProviderByType(ProviderType type) {
|
||||
switch (type) {
|
||||
case ProviderType.robinhood:
|
||||
return getIt.get<RobinhoodBuyProvider>();
|
||||
case ProviderType.dfx:
|
||||
return getIt.get<DFXBuyProvider>();
|
||||
case ProviderType.onramper:
|
||||
return getIt.get<OnRamperBuyProvider>();
|
||||
case ProviderType.askEachTime:
|
||||
return null;
|
||||
case ProviderType.moonpaySell:
|
||||
return getIt.get<MoonPaySellProvider>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -265,6 +265,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
}
|
||||
|
||||
String _normalizeCurrency(CryptoCurrency currency) {
|
||||
if (currency.title == "USDC" && currency.tag == "POLY") {
|
||||
throw "Only Bridged USDC (USDC.e) is allowed in ChangeNow";
|
||||
}
|
||||
switch (currency) {
|
||||
case CryptoCurrency.zec:
|
||||
return 'zec';
|
||||
|
|
|
@ -173,7 +173,7 @@ class CWNano extends Nano {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> updateTransactions(Object wallet) async {
|
||||
Future<bool> updateTransactions(Object wallet) async {
|
||||
return (wallet as NanoWallet).updateTransactions();
|
||||
}
|
||||
|
||||
|
@ -189,116 +189,10 @@ class CWNano extends Nano {
|
|||
}
|
||||
|
||||
class CWNanoUtil extends NanoUtil {
|
||||
// standard:
|
||||
@override
|
||||
String seedToPrivate(String seed, int index) {
|
||||
return ND.NanoKeys.seedToPrivate(seed, index);
|
||||
}
|
||||
|
||||
@override
|
||||
String seedToAddress(String seed, int index) {
|
||||
return ND.NanoAccounts.createAccount(
|
||||
ND.NanoAccountType.NANO, privateKeyToPublic(seedToPrivate(seed, index)));
|
||||
}
|
||||
|
||||
@override
|
||||
String seedToMnemonic(String seed) {
|
||||
return NanoMnemomics.seedToMnemonic(seed).join(" ");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> mnemonicToSeed(String mnemonic) async {
|
||||
return NanoMnemomics.mnemonicListToSeed(mnemonic.split(' '));
|
||||
}
|
||||
|
||||
@override
|
||||
String privateKeyToPublic(String privateKey) {
|
||||
// return NanoHelpers.byteToHex(Ed25519Blake2b.getPubkey(NanoHelpers.hexToBytes(privateKey))!);
|
||||
return ND.NanoKeys.createPublicKey(privateKey);
|
||||
}
|
||||
|
||||
@override
|
||||
String addressToPublicKey(String publicAddress) {
|
||||
return ND.NanoAccounts.extractPublicKey(publicAddress);
|
||||
}
|
||||
|
||||
// universal:
|
||||
@override
|
||||
String privateKeyToAddress(String privateKey) {
|
||||
return ND.NanoAccounts.createAccount(ND.NanoAccountType.NANO, privateKeyToPublic(privateKey));
|
||||
}
|
||||
|
||||
@override
|
||||
String publicKeyToAddress(String publicKey) {
|
||||
return ND.NanoAccounts.createAccount(ND.NanoAccountType.NANO, publicKey);
|
||||
}
|
||||
|
||||
// standard + hd:
|
||||
@override
|
||||
bool isValidSeed(String seed) {
|
||||
// Ensure seed is 64 or 128 characters long
|
||||
if (seed.length != 64 && seed.length != 128) {
|
||||
return false;
|
||||
}
|
||||
// Ensure seed only contains hex characters, 0-9;A-F
|
||||
return ND.NanoHelpers.isHexString(seed);
|
||||
}
|
||||
|
||||
// hd:
|
||||
@override
|
||||
Future<String> hdMnemonicListToSeed(List<String> words) async {
|
||||
// if (words.length != 24) {
|
||||
// throw Exception('Expected a 24-word list, got a ${words.length} list');
|
||||
// }
|
||||
final Uint8List salt = Uint8List.fromList(utf8.encode('mnemonic'));
|
||||
final Pbkdf2 hasher = Pbkdf2(iterations: 2048);
|
||||
final String seed = await hasher.sha512(words.join(' '), salt);
|
||||
return seed;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> hdSeedToPrivate(String seed, int index) async {
|
||||
List<int> seedBytes = hex.decode(seed);
|
||||
KeyData data = await ED25519_HD_KEY.derivePath("m/44'/165'/$index'", seedBytes);
|
||||
return hex.encode(data.key);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> hdSeedToAddress(String seed, int index) async {
|
||||
return ND.NanoAccounts.createAccount(
|
||||
ND.NanoAccountType.NANO, privateKeyToPublic(await hdSeedToPrivate(seed, index)));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> uniSeedToAddress(String seed, int index, String type) {
|
||||
if (type == "standard") {
|
||||
return Future<String>.value(seedToAddress(seed, index));
|
||||
} else if (type == "hd") {
|
||||
return hdSeedToAddress(seed, index);
|
||||
} else {
|
||||
throw Exception('Unknown seed type');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> uniSeedToPrivate(String seed, int index, String type) {
|
||||
if (type == "standard") {
|
||||
return Future<String>.value(seedToPrivate(seed, index));
|
||||
} else if (type == "hd") {
|
||||
return hdSeedToPrivate(seed, index);
|
||||
} else {
|
||||
throw Exception('Unknown seed type');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool isValidBip39Seed(String seed) {
|
||||
// Ensure seed is 128 characters long
|
||||
if (seed.length != 128) {
|
||||
return false;
|
||||
}
|
||||
// Ensure seed only contains hex characters, 0-9;A-F
|
||||
return ND.NanoHelpers.isHexString(seed);
|
||||
return NanoDerivations.isValidBip39Seed(seed);
|
||||
}
|
||||
|
||||
// number util:
|
||||
|
@ -309,92 +203,20 @@ class CWNanoUtil extends NanoUtil {
|
|||
BigInt rawPerBanano = BigInt.parse("100000000000000000000000000000");
|
||||
BigInt rawPerXMR = BigInt.parse("1000000000000");
|
||||
BigInt convertXMRtoNano = BigInt.parse("1000000000000000000");
|
||||
// static BigInt convertXMRtoNano = BigInt.parse("1000000000000000000000000000");
|
||||
|
||||
/// Convert raw to ban and return as BigDecimal
|
||||
///
|
||||
/// @param raw 100000000000000000000000000000
|
||||
/// @return Decimal value 1.000000000000000000000000000000
|
||||
///
|
||||
Decimal _getRawAsDecimal(String? raw, BigInt? rawPerCur) {
|
||||
rawPerCur ??= rawPerNano;
|
||||
final Decimal amount = Decimal.parse(raw.toString());
|
||||
final Decimal result = (amount / Decimal.parse(rawPerCur.toString())).toDecimal();
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
String getRawAsDecimalString(String? raw, BigInt? rawPerCur) {
|
||||
final Decimal result = _getRawAsDecimal(raw, rawPerCur);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
String truncateDecimal(Decimal input, {int digits = maxDecimalDigits}) {
|
||||
Decimal bigger = input.shift(digits);
|
||||
bigger = bigger.floor(); // chop off the decimal: 1.059 -> 1.05
|
||||
bigger = bigger.shift(-digits);
|
||||
return bigger.toString();
|
||||
}
|
||||
|
||||
/// Return raw as a NANO amount.
|
||||
///
|
||||
/// @param raw 100000000000000000000000000000
|
||||
/// @returns 1
|
||||
///
|
||||
@override
|
||||
String getRawAsUsableString(String? raw, BigInt rawPerCur) {
|
||||
final String res =
|
||||
truncateDecimal(_getRawAsDecimal(raw, rawPerCur), digits: maxDecimalDigits + 9);
|
||||
|
||||
if (raw == null || raw == "0" || raw == "00000000000000000000000000000000") {
|
||||
return "0";
|
||||
}
|
||||
|
||||
if (!res.contains(".")) {
|
||||
return res;
|
||||
}
|
||||
|
||||
final String numAmount = res.split(".")[0];
|
||||
String decAmount = res.split(".")[1];
|
||||
|
||||
// truncate:
|
||||
if (decAmount.length > maxDecimalDigits) {
|
||||
decAmount = decAmount.substring(0, maxDecimalDigits);
|
||||
// remove trailing zeros:
|
||||
decAmount = decAmount.replaceAllMapped(RegExp(r'0+$'), (Match match) => '');
|
||||
if (decAmount.isEmpty) {
|
||||
return numAmount;
|
||||
}
|
||||
}
|
||||
|
||||
return "$numAmount.$decAmount";
|
||||
return NanoAmounts.getRawAsUsableString(raw, rawPerCur);
|
||||
}
|
||||
|
||||
@override
|
||||
String getRawAccuracy(String? raw, BigInt rawPerCur) {
|
||||
final String rawString = getRawAsUsableString(raw, rawPerCur);
|
||||
final String rawDecimalString = _getRawAsDecimal(raw, rawPerCur).toString();
|
||||
|
||||
if (raw == null || raw.isEmpty || raw == "0") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (rawString != rawDecimalString) {
|
||||
return "~";
|
||||
}
|
||||
return "";
|
||||
return NanoAmounts.getRawAccuracy(raw, rawPerCur);
|
||||
}
|
||||
|
||||
/// Return readable string amount as raw string
|
||||
/// @param amount 1.01
|
||||
/// @returns 101000000000000000000000000000
|
||||
///
|
||||
@override
|
||||
String getAmountAsRaw(String amount, BigInt rawPerCur) {
|
||||
final Decimal asDecimal = Decimal.parse(amount);
|
||||
final Decimal rawDecimal = Decimal.parse(rawPerCur.toString());
|
||||
return (asDecimal * rawDecimal).toString();
|
||||
return NanoAmounts.getAmountAsRaw(amount, rawPerCur);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -411,29 +233,29 @@ class CWNanoUtil extends NanoUtil {
|
|||
if (seedKey != null) {
|
||||
if (seedKey.length == 64) {
|
||||
try {
|
||||
mnemonic = nanoUtil!.seedToMnemonic(seedKey);
|
||||
mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
|
||||
} catch (e) {
|
||||
print("not a valid 'nano' seed key");
|
||||
}
|
||||
}
|
||||
if (derivationType == DerivationType.bip39) {
|
||||
publicAddress = await hdSeedToAddress(seedKey, 0);
|
||||
publicAddress = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
|
||||
} else if (derivationType == DerivationType.nano) {
|
||||
publicAddress = await seedToAddress(seedKey, 0);
|
||||
publicAddress = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (derivationType == DerivationType.bip39) {
|
||||
if (mnemonic != null) {
|
||||
seedKey = await hdMnemonicListToSeed(mnemonic.split(' '));
|
||||
publicAddress = await hdSeedToAddress(seedKey, 0);
|
||||
seedKey = await NanoDerivations.hdMnemonicListToSeed(mnemonic.split(' '));
|
||||
publicAddress = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (derivationType == DerivationType.nano) {
|
||||
if (mnemonic != null) {
|
||||
seedKey = await mnemonicToSeed(mnemonic);
|
||||
publicAddress = await seedToAddress(seedKey, 0);
|
||||
seedKey = await NanoDerivations.standardMnemonicToSeed(mnemonic);
|
||||
publicAddress = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -461,7 +283,7 @@ class CWNanoUtil extends NanoUtil {
|
|||
return [DerivationType.bip39];
|
||||
} else if (seedKey?.length == 64) {
|
||||
try {
|
||||
mnemonic = nanoUtil!.seedToMnemonic(seedKey!);
|
||||
mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey!);
|
||||
} catch (e) {
|
||||
print("not a valid 'nano' seed key");
|
||||
}
|
||||
|
@ -475,19 +297,19 @@ class CWNanoUtil extends NanoUtil {
|
|||
nanoClient.connect(node);
|
||||
|
||||
if (mnemonic != null) {
|
||||
seedKey = await hdMnemonicListToSeed(mnemonic.split(' '));
|
||||
publicAddressBip39 = await hdSeedToAddress(seedKey, 0);
|
||||
seedKey = await NanoDerivations.hdMnemonicListToSeed(mnemonic.split(' '));
|
||||
publicAddressBip39 = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
|
||||
|
||||
seedKey = await mnemonicToSeed(mnemonic);
|
||||
publicAddressStandard = await seedToAddress(seedKey, 0);
|
||||
seedKey = await NanoDerivations.standardMnemonicToSeed(mnemonic);
|
||||
publicAddressStandard = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
|
||||
} else if (seedKey != null) {
|
||||
try {
|
||||
publicAddressBip39 = await hdSeedToAddress(seedKey, 0);
|
||||
publicAddressBip39 = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
|
||||
} catch (e) {
|
||||
return [DerivationType.nano];
|
||||
}
|
||||
try {
|
||||
publicAddressStandard = await seedToAddress(seedKey, 0);
|
||||
publicAddressStandard = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
|
||||
} catch (e) {
|
||||
return [DerivationType.bip39];
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
|
|||
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_info_page.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
|
||||
|
@ -433,8 +434,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
return MaterialPageRoute<void>(
|
||||
builder: (_) => getIt.get<OrderDetailsPage>(param1: settings.arguments as Order));
|
||||
|
||||
case Routes.buy:
|
||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<BuyOptionsPage>());
|
||||
case Routes.buySellPage:
|
||||
final args = settings.arguments as bool;
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => getIt.get<BuySellOptionsPage>(param1: args));
|
||||
|
||||
case Routes.buyWebView:
|
||||
final args = settings.arguments as List;
|
||||
|
@ -455,12 +458,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
case Routes.faq:
|
||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<FaqPage>());
|
||||
|
||||
case Routes.preSeed:
|
||||
case Routes.preSeedPage:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => getIt.get<PreSeedPage>(
|
||||
param1: settings.arguments as WalletType,
|
||||
param2: getIt.get<AdvancedPrivacySettingsViewModel>(
|
||||
param1: settings.arguments as WalletType)));
|
||||
param1: settings.arguments as int));
|
||||
|
||||
case Routes.backup:
|
||||
return CupertinoPageRoute<void>(
|
||||
|
@ -625,6 +626,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
case Routes.modify2FAPage:
|
||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<Modify2FAPage>());
|
||||
|
||||
case Routes.setup2faInfoPage:
|
||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<Setup2FAInfoPage>());
|
||||
|
||||
case Routes.homeSettings:
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<HomeSettingsPage>(param1: settings.arguments),
|
||||
|
|
|
@ -47,7 +47,7 @@ class Routes {
|
|||
static const exchangeTemplate = '/exchange_template';
|
||||
static const restoreWalletType = '/restore_wallet_type';
|
||||
static const restoreWallet = '/restore_wallet';
|
||||
static const preSeed = '/pre_seed';
|
||||
static const preSeedPage = '/pre_seed_page';
|
||||
static const backup = '/backup';
|
||||
static const editBackupPassword = '/edit_backup_passowrd';
|
||||
static const restoreFromBackup = '/restore_from_backup';
|
||||
|
@ -55,7 +55,7 @@ class Routes {
|
|||
static const supportLiveChat = '/support/live_chat';
|
||||
static const supportOtherLinks = '/support/other';
|
||||
static const orderDetails = '/order_details';
|
||||
static const buy = '/buy';
|
||||
static const buySellPage = '/buy_sell_page';
|
||||
static const buyWebView = '/buy_web_view';
|
||||
static const unspentCoinsList = '/unspent_coins_list';
|
||||
static const unspentCoinsDetails = '/unspent_coins_details';
|
||||
|
@ -98,6 +98,7 @@ class Routes {
|
|||
static const setup_2faQRPage = '/setup_2fa_qr_page';
|
||||
static const totpAuthCodePage = '/totp_auth_code_page';
|
||||
static const modify2FAPage = '/modify_2fa_page';
|
||||
static const setup2faInfoPage = '/setup_2fa_info_page';
|
||||
static const homeSettings = '/home_settings';
|
||||
static const editToken = '/edit_token';
|
||||
static const manageNodes = '/manage_nodes';
|
||||
|
|
83
lib/src/screens/InfoPage.dart
Normal file
|
@ -0,0 +1,83 @@
|
|||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class InfoPage extends BasePage {
|
||||
InfoPage({
|
||||
this.imageLightPath = 'assets/images/pre_seed_light.png',
|
||||
this.imageDarkPath = 'assets/images/pre_seed_dark.png',
|
||||
});
|
||||
|
||||
final String imageLightPath;
|
||||
final String imageDarkPath;
|
||||
|
||||
Image get imageLight => Image.asset(imageLightPath);
|
||||
Image get imageDark => Image.asset(imageDarkPath);
|
||||
|
||||
bool get onWillPop => true;
|
||||
String get pageTitle;
|
||||
String get pageDescription;
|
||||
String get buttonText;
|
||||
void Function(BuildContext) get onPressed;
|
||||
|
||||
@override
|
||||
Widget? leading(BuildContext context) => null;
|
||||
|
||||
@override
|
||||
String get title => pageTitle;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final image = currentTheme.type == ThemeType.dark ? imageDark : imageLight;
|
||||
|
||||
return WillPopScope(
|
||||
onWillPop: () async => onWillPop,
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.all(24),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: MediaQuery.of(context).size.height * 0.3),
|
||||
child: AspectRatio(aspectRatio: 1, child: image),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
pageDescription,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
height: 1.7,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.secondaryTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
PrimaryButton(
|
||||
onPressed: () => onPressed(context),
|
||||
text: buttonText,
|
||||
color: Theme.of(context).primaryColor,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,3 @@
|
|||
import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/buy_provider_types.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/option_tile.dart';
|
||||
|
@ -11,38 +6,24 @@ import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
|
|||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BuyOptionsPage extends BasePage {
|
||||
BuyOptionsPage(this.dashboardViewModel);
|
||||
class BuySellOptionsPage extends BasePage {
|
||||
BuySellOptionsPage(this.dashboardViewModel, this.isBuyAction);
|
||||
|
||||
final DashboardViewModel dashboardViewModel;
|
||||
final iconDarkRobinhood = 'assets/images/robinhood_dark.png';
|
||||
final iconLightRobinhood = 'assets/images/robinhood_light.png';
|
||||
final iconDarkOnramper = 'assets/images/onramper_dark.png';
|
||||
final iconLightOnramper = 'assets/images/onramper_light.png';
|
||||
final iconDarkDFX = 'assets/images/dfx_dark.png';
|
||||
final iconLightDFX = 'assets/images/dfx_light.png';
|
||||
final bool isBuyAction;
|
||||
|
||||
@override
|
||||
String get title => S.current.buy;
|
||||
String get title => isBuyAction ? S.current.buy : S.current.sell;
|
||||
|
||||
@override
|
||||
AppBarStyle get appBarStyle => AppBarStyle.regular;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final isLightMode =
|
||||
Theme.of(context).extension<OptionTileTheme>()?.useDarkImage ?? false;
|
||||
final iconRobinhood = Image.asset(
|
||||
isLightMode ? iconLightRobinhood : iconDarkRobinhood,
|
||||
height: 40,
|
||||
width: 40);
|
||||
final iconOnramper = Image.asset(
|
||||
isLightMode ? iconLightOnramper : iconDarkOnramper,
|
||||
height: 40,
|
||||
width: 40);
|
||||
final iconDFX = Image.asset(isLightMode ? iconLightDFX : iconDarkDFX,
|
||||
height: 40, width: 40);
|
||||
final availableProviders = dashboardViewModel.availableProviders;
|
||||
final isLightMode = Theme.of(context).extension<OptionTileTheme>()?.useDarkImage ?? false;
|
||||
final availableProviders = isBuyAction
|
||||
? dashboardViewModel.availableBuyProviders
|
||||
: dashboardViewModel.availableSellProviders;
|
||||
|
||||
return Container(
|
||||
child: Center(
|
||||
|
@ -50,54 +31,35 @@ class BuyOptionsPage extends BasePage {
|
|||
constraints: BoxConstraints(maxWidth: 330),
|
||||
child: Column(
|
||||
children: [
|
||||
if (availableProviders.contains(BuyProviderType.Onramper))
|
||||
Padding(
|
||||
...availableProviders.map((provider) {
|
||||
final icon = Image.asset(
|
||||
isLightMode ? provider.lightIcon : provider.darkIcon,
|
||||
height: 40,
|
||||
width: 40,
|
||||
);
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: OptionTile(
|
||||
image: iconOnramper,
|
||||
title: "Onramper",
|
||||
description: S.of(context).onramper_option_description,
|
||||
onPressed: () async => await getIt
|
||||
.get<OnRamperBuyProvider>()
|
||||
.launchProvider(context),
|
||||
image: icon,
|
||||
title: provider.toString(),
|
||||
description: provider.providerDescription,
|
||||
onPressed: () => provider.launchProvider(context, isBuyAction),
|
||||
),
|
||||
),
|
||||
if (availableProviders.contains(BuyProviderType.Robinhood))
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: OptionTile(
|
||||
image: iconRobinhood,
|
||||
title: "Robinhood Connect",
|
||||
description: S.of(context).robinhood_option_description,
|
||||
onPressed: () async => await getIt
|
||||
.get<RobinhoodBuyProvider>()
|
||||
.launchProvider(context),
|
||||
),
|
||||
),
|
||||
if (availableProviders.contains(BuyProviderType.DFX))
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: OptionTile(
|
||||
image: iconDFX,
|
||||
title: "DFX Connect",
|
||||
description: S.of(context).dfx_option_description,
|
||||
onPressed: () async => await getIt
|
||||
.get<DFXBuyProvider>()
|
||||
.launchProvider(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
Spacer(),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
|
||||
child: Text(
|
||||
S.of(context).select_buy_provider_notice,
|
||||
isBuyAction
|
||||
? S.of(context).select_buy_provider_notice
|
||||
: S.of(context).select_sell_provider_notice,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Theme.of(context)
|
||||
.extension<TransactionTradeTheme>()!
|
||||
.detailsTitlesColor,
|
||||
color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||
import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/store/dashboard/orders_store.dart';
|
||||
import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
|
||||
|
@ -72,10 +69,10 @@ class BuyWebViewPageBodyState extends State<BuyWebViewPageBody> {
|
|||
Widget build(BuildContext context) {
|
||||
return InAppWebView(
|
||||
key: _webViewkey,
|
||||
initialOptions: InAppWebViewGroupOptions(
|
||||
crossPlatform: InAppWebViewOptions(transparentBackground: true),
|
||||
initialSettings: InAppWebViewSettings(
|
||||
transparentBackground: true,
|
||||
),
|
||||
initialUrlRequest: URLRequest(url: Uri.tryParse(widget.url ?? '')),
|
||||
initialUrlRequest: URLRequest(url: WebUri(widget.url ?? '')),
|
||||
onWebViewCreated: (InAppWebViewController controller) =>
|
||||
setState(() => _webViewController = controller));
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
|
@ -35,21 +33,21 @@ class WebViewPageBodyState extends State<WebViewPageBody> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InAppWebView(
|
||||
initialOptions: InAppWebViewGroupOptions(
|
||||
crossPlatform: InAppWebViewOptions(transparentBackground: true),
|
||||
initialSettings: InAppWebViewSettings(
|
||||
transparentBackground: true,
|
||||
),
|
||||
initialUrlRequest: URLRequest(url: widget.uri),
|
||||
androidOnPermissionRequest: (_, __, resources) async {
|
||||
initialUrlRequest: URLRequest(url: WebUri.uri(widget.uri)),
|
||||
onPermissionRequest: (controller, request) async {
|
||||
bool permissionGranted = await Permission.camera.status == PermissionStatus.granted;
|
||||
if (!permissionGranted) {
|
||||
permissionGranted = await Permission.camera.request().isGranted;
|
||||
}
|
||||
|
||||
return PermissionRequestResponse(
|
||||
resources: resources,
|
||||
return PermissionResponse(
|
||||
resources: request.resources,
|
||||
action: permissionGranted
|
||||
? PermissionRequestResponseAction.GRANT
|
||||
: PermissionRequestResponseAction.DENY,
|
||||
? PermissionResponseAction.GRANT
|
||||
: PermissionResponseAction.DENY,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -29,11 +29,10 @@ class BuyListItem extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isSelected = selectedProvider?.description == provider.description;
|
||||
final isSelected = selectedProvider?.providerDescription == provider.providerDescription;
|
||||
final iconColor = isSelected ? Colors.white : Colors.black;
|
||||
|
||||
final providerIcon = getBuyProviderIcon(provider.description,
|
||||
iconColor: iconColor)!;
|
||||
final providerIcon = Image.asset('assets/images/wyre-icon.png', width: 36, height: 36);
|
||||
|
||||
final backgroundColor = isSelected
|
||||
? Palette.greyBlueCraiola
|
||||
|
@ -76,7 +75,7 @@ class BuyListItem extends StatelessWidget {
|
|||
padding: EdgeInsets.only(right: 10),
|
||||
child: providerIcon),
|
||||
Text(
|
||||
provider.description.title,
|
||||
provider.title,
|
||||
style: TextStyle(
|
||||
color: secondaryTextColor,
|
||||
fontSize: 20,
|
||||
|
|
|
@ -126,8 +126,8 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
|||
alertContent: S.of(context).change_wallet_alert_content(selectedWallet.name),
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).change,
|
||||
actionLeftButton: () => Navigator.of(context).pop(false),
|
||||
actionRightButton: () => Navigator.of(context).pop(true));
|
||||
actionLeftButton: () => Navigator.of(dialogContext).pop(false),
|
||||
actionRightButton: () => Navigator.of(dialogContext).pop(true));
|
||||
}) ??
|
||||
false;
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
|
@ -14,6 +16,7 @@ import 'package:cake_wallet/utils/feature_flag.dart';
|
|||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
|
@ -200,7 +203,7 @@ class CryptoBalanceWidget extends StatelessWidget {
|
|||
additionalFiatBalance: balance.fiatAdditionalBalance,
|
||||
frozenBalance: balance.frozenBalance,
|
||||
frozenFiatBalance: balance.fiatFrozenBalance,
|
||||
currency: balance.formattedAssetTitle,
|
||||
currency: balance.asset,
|
||||
hasAdditionalBalance:
|
||||
dashboardViewModel.balanceViewModel.hasAdditionalBalance,
|
||||
);
|
||||
|
@ -216,7 +219,7 @@ class CryptoBalanceWidget extends StatelessWidget {
|
|||
}
|
||||
|
||||
class BalanceRowWidget extends StatelessWidget {
|
||||
const BalanceRowWidget({
|
||||
BalanceRowWidget({
|
||||
required this.availableBalanceLabel,
|
||||
required this.availableBalance,
|
||||
required this.availableFiatBalance,
|
||||
|
@ -238,7 +241,7 @@ class BalanceRowWidget extends StatelessWidget {
|
|||
final String additionalFiatBalance;
|
||||
final String frozenBalance;
|
||||
final String frozenFiatBalance;
|
||||
final String currency;
|
||||
final CryptoCurrency currency;
|
||||
final bool hasAdditionalBalance;
|
||||
|
||||
// void _showBalanceDescription(BuildContext context) {
|
||||
|
@ -262,13 +265,13 @@ class BalanceRowWidget extends StatelessWidget {
|
|||
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
||||
),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 16, left: 24, right: 24, bottom: 24),
|
||||
margin: const EdgeInsets.only(top: 16, left: 24, right: 8, bottom: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
|
@ -325,13 +328,48 @@ class BalanceRowWidget extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
Text(currency,
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
fontFamily: 'Lato',
|
||||
fontWeight: FontWeight.w800,
|
||||
color: Theme.of(context).extension<BalancePageTheme>()!.assetTitleColor,
|
||||
height: 1)),
|
||||
SizedBox(
|
||||
width: min(MediaQuery.of(context).size.width * 0.2, 100),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
currency.iconPath != null
|
||||
? Container(
|
||||
child: Image.asset(
|
||||
currency.iconPath!,
|
||||
height: 40.0,
|
||||
width: 40.0,
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
height: 30.0,
|
||||
width: 30.0,
|
||||
child: Center(
|
||||
child: Text(
|
||||
currency.title.substring(0, min(currency.title.length, 2)),
|
||||
style: TextStyle(fontSize: 11),
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
currency.title,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontFamily: 'Lato',
|
||||
fontWeight: FontWeight.w800,
|
||||
color: Theme.of(context).extension<BalancePageTheme>()!.assetTitleColor,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (frozenBalance.isNotEmpty)
|
||||
|
@ -374,7 +412,9 @@ class BalanceRowWidget extends StatelessWidget {
|
|||
fontSize: 20,
|
||||
fontFamily: 'Lato',
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
|
||||
color: Theme.of(context)
|
||||
.extension<BalancePageTheme>()!
|
||||
.balanceAmountColor,
|
||||
height: 1,
|
||||
),
|
||||
maxLines: 1,
|
||||
|
@ -388,7 +428,7 @@ class BalanceRowWidget extends StatelessWidget {
|
|||
fontSize: 12,
|
||||
fontFamily: 'Lato',
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
|
||||
color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -49,7 +49,7 @@ class TransactionsPage extends StatelessWidget {
|
|||
onTap: () => Navigator.of(context).pushNamed(Routes.webViewPage, arguments: [
|
||||
'',
|
||||
Uri.parse(
|
||||
'https://guides.cakewallet.com/docs/bugs-service-status/why_are_my_funds_not_appearing/')
|
||||
'https://guides.cakewallet.com/docs/FAQ/why_are_my_funds_not_appearing/')
|
||||
]),
|
||||
title: S.of(context).syncing_wallet_alert_title,
|
||||
subTitle: S.of(context).syncing_wallet_alert_content,
|
||||
|
|
|
@ -261,7 +261,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
|||
fee: S.of(popupContext).send_fee,
|
||||
feeValue: widget.exchangeTradeViewModel.sendViewModel
|
||||
.pendingTransaction!.feeFormatted,
|
||||
rightButtonText: S.of(popupContext).ok,
|
||||
rightButtonText: S.of(popupContext).send,
|
||||
leftButtonText: S.of(popupContext).cancel,
|
||||
actionRightButton: () async {
|
||||
Navigator.of(popupContext).pop();
|
||||
|
@ -299,7 +299,8 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
|||
}
|
||||
|
||||
void transactionStatePopup() {
|
||||
showPopUp<void>(
|
||||
if (this.mounted) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext popupContext) {
|
||||
return Observer(builder: (_) {
|
||||
|
@ -344,7 +345,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
|||
onPressed: () {
|
||||
Navigator.of(popupContext).pop();
|
||||
RequestReviewHandler.requestReview();
|
||||
},
|
||||
},
|
||||
text: S.of(popupContext).got_it,
|
||||
color: Theme.of(popupContext).primaryColor,
|
||||
textColor: Colors.white))
|
||||
|
@ -391,5 +392,6 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
|||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
|||
_stateReaction ??= reaction((_) => _walletNewVM.state, (ExecutionState state) async {
|
||||
if (state is ExecutedSuccessfullyState) {
|
||||
Navigator.of(navigatorKey.currentContext!)
|
||||
.pushNamed(Routes.preSeed, arguments: _walletNewVM.type);
|
||||
.pushNamed(Routes.preSeedPage, arguments: _walletNewVM.seedPhraseWordsLength);
|
||||
}
|
||||
|
||||
if (state is FailureState) {
|
||||
|
|
|
@ -4,12 +4,16 @@ import 'package:qr_flutter/qr_flutter.dart' as qr;
|
|||
class QrImage extends StatelessWidget {
|
||||
QrImage({
|
||||
required this.data,
|
||||
this.foregroundColor = Colors.black,
|
||||
this.backgroundColor = Colors.white,
|
||||
this.size = 100.0,
|
||||
this.version,
|
||||
this.errorCorrectionLevel = qr.QrErrorCorrectLevel.L,
|
||||
});
|
||||
|
||||
final double size;
|
||||
final Color foregroundColor;
|
||||
final Color backgroundColor;
|
||||
final String data;
|
||||
final int? version;
|
||||
final int errorCorrectionLevel;
|
||||
|
@ -21,8 +25,8 @@ class QrImage extends StatelessWidget {
|
|||
errorCorrectionLevel: errorCorrectionLevel,
|
||||
version: version ?? 9, // Previous value: 7 something happened after flutter upgrade monero wallets addresses are longer than ver. 7 ???
|
||||
size: size,
|
||||
foregroundColor: Colors.black,
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: foregroundColor,
|
||||
backgroundColor: backgroundColor,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,97 +1,27 @@
|
|||
import 'package:cake_wallet/entities/seed_type.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/screens/InfoPage.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class PreSeedPage extends BasePage {
|
||||
PreSeedPage(this.type, this.advancedPrivacySettingsViewModel, this.seedTypeViewModel)
|
||||
: imageLight = Image.asset('assets/images/pre_seed_light.png'),
|
||||
imageDark = Image.asset('assets/images/pre_seed_dark.png'),
|
||||
seedPhraseLength = advancedPrivacySettingsViewModel.seedPhraseLength.value,
|
||||
moneroSeedType = seedTypeViewModel.moneroSeedType {
|
||||
wordsCount = _wordsCount(type, seedPhraseLength, moneroSeedType);
|
||||
}
|
||||
class PreSeedPage extends InfoPage {
|
||||
PreSeedPage(this.seedPhraseLength);
|
||||
|
||||
final Image imageDark;
|
||||
final Image imageLight;
|
||||
final WalletType type;
|
||||
final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel;
|
||||
final SeedTypeViewModel seedTypeViewModel;
|
||||
final int seedPhraseLength;
|
||||
final SeedType moneroSeedType;
|
||||
late final int wordsCount;
|
||||
|
||||
@override
|
||||
Widget? leading(BuildContext context) => null;
|
||||
bool get onWillPop => false;
|
||||
|
||||
@override
|
||||
String? get title => S.current.pre_seed_title;
|
||||
String get pageTitle => S.current.pre_seed_title;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final image = currentTheme.type == ThemeType.dark ? imageDark : imageLight;
|
||||
String get pageDescription =>
|
||||
S.current.pre_seed_description(seedPhraseLength.toString());
|
||||
|
||||
return WillPopScope(
|
||||
onWillPop: () async => false,
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.all(24),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: MediaQuery.of(context).size.height * 0.3),
|
||||
child: AspectRatio(aspectRatio: 1, child: image),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
S.of(context).pre_seed_description(wordsCount.toString()),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.secondaryTextColor),
|
||||
),
|
||||
),
|
||||
PrimaryButton(
|
||||
onPressed: () => Navigator.of(context)
|
||||
.popAndPushNamed(Routes.seed, arguments: true),
|
||||
text: S.of(context).pre_seed_button_text,
|
||||
color: Theme.of(context).primaryColor,
|
||||
textColor: Colors.white)
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
@override
|
||||
String get buttonText => S.current.pre_seed_button_text;
|
||||
|
||||
static int _wordsCount(WalletType type, int seedPhraseLength, SeedType moneroSeedType) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
if (moneroSeedType == SeedType.polyseed)
|
||||
return 16;
|
||||
return 25;
|
||||
case WalletType.ethereum:
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.polygon:
|
||||
return seedPhraseLength;
|
||||
default:
|
||||
return 24;
|
||||
}
|
||||
}
|
||||
@override
|
||||
void Function(BuildContext) get onPressed => (BuildContext context) =>
|
||||
Navigator.of(context).popAndPushNamed(Routes.seed, arguments: true);
|
||||
}
|
||||
|
|
|
@ -423,7 +423,7 @@ class SendPage extends BasePage {
|
|||
feeValue: sendViewModel.pendingTransaction!.feeFormatted,
|
||||
feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmountFormatted,
|
||||
outputs: sendViewModel.outputs,
|
||||
rightButtonText: S.of(_dialogContext).ok,
|
||||
rightButtonText: S.of(_dialogContext).send,
|
||||
leftButtonText: S.of(_dialogContext).cancel,
|
||||
actionRightButton: () {
|
||||
Navigator.of(_dialogContext).pop();
|
||||
|
|
|
@ -43,10 +43,18 @@ class OtherSettingsPage extends BasePage {
|
|||
if(_otherSettingsViewModel.isEnabledBuyAction)
|
||||
SettingsPickerCell(
|
||||
title: S.current.default_buy_provider,
|
||||
items: _otherSettingsViewModel.availableBuyProviders,
|
||||
items: _otherSettingsViewModel.availableBuyProvidersTypes,
|
||||
displayItem: _otherSettingsViewModel.getBuyProviderType,
|
||||
selectedItem: _otherSettingsViewModel.buyProviderType,
|
||||
onItemSelected: _otherSettingsViewModel.onBuyProviderTypeSelected,
|
||||
onItemSelected: _otherSettingsViewModel.onBuyProviderTypeSelected
|
||||
),
|
||||
if(_otherSettingsViewModel.isEnabledSellAction)
|
||||
SettingsPickerCell(
|
||||
title: S.current.default_sell_provider,
|
||||
items: _otherSettingsViewModel.availableSellProvidersTypes,
|
||||
displayItem: _otherSettingsViewModel.getSellProviderType,
|
||||
selectedItem: _otherSettingsViewModel.sellProviderType,
|
||||
onItemSelected: _otherSettingsViewModel.onSellProviderTypeSelected,
|
||||
),
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.settings_terms_and_conditions,
|
||||
|
|
|
@ -111,7 +111,7 @@ class SecurityBackupPage extends BasePage {
|
|||
context,
|
||||
route: _securitySettingsViewModel.useTotp2FA
|
||||
? Routes.modify2FAPage
|
||||
: Routes.setup_2faPage,
|
||||
: Routes.setup2faInfoPage,
|
||||
conditionToDetermineIfToUse2FA: _securitySettingsViewModel
|
||||
.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
),
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
|
||||
|
||||
import '../../widgets/standard_list.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class Setup2FAPage extends BasePage {
|
||||
Setup2FAPage({required this.setup2FAViewModel});
|
||||
|
@ -15,52 +14,64 @@ class Setup2FAPage extends BasePage {
|
|||
final Setup2FAViewModel setup2FAViewModel;
|
||||
|
||||
@override
|
||||
String get title => S.current.setup_2fa;
|
||||
String get title => 'Cake 2FA';
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
final cake2FAGuideTitle = 'Cake 2FA Guide';
|
||||
final cake2FAGuideUri =
|
||||
Uri.parse('https://guides.cakewallet.com/docs/advanced-features/authentication');
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.3),
|
||||
child:
|
||||
AspectRatio(aspectRatio: 0.6, child: Image.asset('assets/images/setup_2fa_img.png')),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
S.current.important_note,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 14,
|
||||
height: 1.571,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
S.current.setup_2fa_text,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
height: 1.571,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: Text(
|
||||
S.current.setup_2fa_text,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
height: 1.571,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 86),
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.setup_totp_recommended,
|
||||
handler: (_) {
|
||||
setup2FAViewModel.generateSecretKey();
|
||||
return Navigator.of(context).pushReplacementNamed(Routes.setup_2faQRPage);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.setup_totp_recommended,
|
||||
handler: (_) {
|
||||
setup2FAViewModel.generateSecretKey();
|
||||
return Navigator.of(context).pushReplacementNamed(Routes.setup_2faQRPage);
|
||||
},
|
||||
),
|
||||
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||
SettingsCellWithArrow(
|
||||
title: cake2FAGuideTitle, handler: (_) => _launchUrl(cake2FAGuideUri)),
|
||||
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||
],
|
||||
),
|
||||
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static void _launchUrl(Uri url) async {
|
||||
try {
|
||||
await launchUrl(url, mode: LaunchMode.externalApplication);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
|
20
lib/src/screens/setup_2fa/setup_2fa_info_page.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/InfoPage.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class Setup2FAInfoPage extends InfoPage {
|
||||
|
||||
@override
|
||||
String get pageTitle => S.current.pre_seed_title;
|
||||
|
||||
@override
|
||||
String get pageDescription => S.current.setup_warning_2fa_text;
|
||||
|
||||
@override
|
||||
String get buttonText => S.current.understand;
|
||||
|
||||
@override
|
||||
void Function(BuildContext) get onPressed => (BuildContext context) =>
|
||||
Navigator.of(context).popAndPushNamed(Routes.setup_2faPage);
|
||||
}
|
|
@ -4,8 +4,8 @@ import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
|||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
|
||||
import 'package:cake_wallet/utils/clipboard_util.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
|
@ -13,6 +13,7 @@ import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart' as qr;
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class Setup2FAQRPage extends BasePage {
|
||||
Setup2FAQRPage({required this.setup2FAViewModel});
|
||||
|
@ -24,51 +25,63 @@ class Setup2FAQRPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final copyImage = Image.asset(
|
||||
'assets/images/copy_content.png',
|
||||
height: 12,
|
||||
width: 12,
|
||||
color: Color(0xFF355688),
|
||||
);
|
||||
final copyImage = Image.asset('assets/images/copy_content.png',
|
||||
height: 16,
|
||||
width: 16,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor);
|
||||
final cake2FAHowToUseUrl = Uri.parse(
|
||||
'https://guides.cakewallet.com/docs/advanced-features/authentication/#enabling-cake-2fa');
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 58),
|
||||
Spacer(),
|
||||
Text(
|
||||
S.current.scan_qr_on_device,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
height: 1.5714,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: MediaQuery.of(context).size.height * 0.4),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 3,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.titleColor,
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
child: QrImage(
|
||||
data: setup2FAViewModel.totpVersionOneLink,
|
||||
version: qr.QrVersions.auto,
|
||||
foregroundColor:
|
||||
Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
backgroundColor: Colors.transparent,
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 26),
|
||||
Text(
|
||||
S.current.add_secret_code,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
height: 1.5714,
|
||||
color: Palette.darkBlueCraiola,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 3,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 3,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
child: QrImage(
|
||||
data: setup2FAViewModel.totpVersionOneLink,
|
||||
version: qr.QrVersions.auto,
|
||||
)),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 13),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
@ -83,7 +96,9 @@ class Setup2FAQRPage extends BasePage {
|
|||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Palette.darkGray,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.secondaryTextColor,
|
||||
height: 1.8333,
|
||||
),
|
||||
),
|
||||
|
@ -91,10 +106,10 @@ class Setup2FAQRPage extends BasePage {
|
|||
Text(
|
||||
'${setup2FAViewModel.totpSecretKey}',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
height: 1.375,
|
||||
),
|
||||
fontSize: 18,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.titleColor),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
@ -107,15 +122,11 @@ class Setup2FAQRPage extends BasePage {
|
|||
height: 32,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
ClipboardUtil.setSensitiveDataToClipboard(
|
||||
ClipboardData(text: '${setup2FAViewModel.totpSecretKey}'));
|
||||
ClipboardUtil.setSensitiveDataToClipboard(ClipboardData(
|
||||
text: '${setup2FAViewModel.totpSecretKey}'));
|
||||
showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
color: Color(0xFFF2F0FA),
|
||||
),
|
||||
child: copyImage,
|
||||
),
|
||||
),
|
||||
|
@ -139,7 +150,9 @@ class Setup2FAQRPage extends BasePage {
|
|||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Palette.darkGray,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.secondaryTextColor,
|
||||
height: 1.8333,
|
||||
),
|
||||
),
|
||||
|
@ -147,10 +160,10 @@ class Setup2FAQRPage extends BasePage {
|
|||
Text(
|
||||
'${setup2FAViewModel.totpVersionOneLink}',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
height: 1.375,
|
||||
),
|
||||
fontSize: 18,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.titleColor),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
@ -163,15 +176,11 @@ class Setup2FAQRPage extends BasePage {
|
|||
height: 32,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
ClipboardUtil.setSensitiveDataToClipboard(
|
||||
ClipboardData(text: '${setup2FAViewModel.totpVersionOneLink}'));
|
||||
ClipboardUtil.setSensitiveDataToClipboard(ClipboardData(
|
||||
text: '${setup2FAViewModel.totpVersionOneLink}'));
|
||||
showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
color: Color(0xFFF2F0FA),
|
||||
),
|
||||
child: copyImage,
|
||||
),
|
||||
),
|
||||
|
@ -180,13 +189,36 @@ class Setup2FAQRPage extends BasePage {
|
|||
),
|
||||
SizedBox(height: 8),
|
||||
StandardListSeparator(),
|
||||
Spacer(),
|
||||
SizedBox(height: 16),
|
||||
GestureDetector(
|
||||
onTap: () => _launchUrl(cake2FAHowToUseUrl),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(S.current.how_to_use,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.titleColor)),
|
||||
Icon(Icons.info_outline,
|
||||
size: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.titleColor)
|
||||
],
|
||||
)),
|
||||
Spacer(flex: 5),
|
||||
PrimaryButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushReplacementNamed(Routes.totpAuthCodePage,
|
||||
arguments: TotpAuthArgumentsModel(
|
||||
isForSetup: true,
|
||||
));
|
||||
Navigator.of(context)
|
||||
.pushReplacementNamed(Routes.totpAuthCodePage,
|
||||
arguments: TotpAuthArgumentsModel(
|
||||
isForSetup: true,
|
||||
));
|
||||
},
|
||||
text: S.current.continue_text,
|
||||
color: Theme.of(context).primaryColor,
|
||||
|
@ -197,4 +229,10 @@ class Setup2FAQRPage extends BasePage {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
static void _launchUrl(Uri url) async {
|
||||
try {
|
||||
await launchUrl(url, mode: LaunchMode.externalApplication);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,17 +22,17 @@ class ChatwootWidgetState extends State<ChatwootWidget> {
|
|||
@override
|
||||
Widget build(BuildContext context) => InAppWebView(
|
||||
key: _webViewkey,
|
||||
initialOptions: InAppWebViewGroupOptions(
|
||||
crossPlatform: InAppWebViewOptions(transparentBackground: true),
|
||||
),
|
||||
initialUrlRequest: URLRequest(url: Uri.tryParse(widget.supportUrl)),
|
||||
initialSettings: InAppWebViewSettings(
|
||||
transparentBackground: true,
|
||||
),
|
||||
initialUrlRequest: URLRequest(url: WebUri(widget.supportUrl)),
|
||||
onWebViewCreated: (InAppWebViewController controller) {
|
||||
controller.addWebMessageListener(
|
||||
WebMessageListener(
|
||||
jsObjectName: 'ReactNativeWebView',
|
||||
onPostMessage: (String? message, Uri? sourceOrigin, bool isMainFrame,
|
||||
JavaScriptReplyProxy replyProxy) {
|
||||
final shortenedMessage = message?.substring(16);
|
||||
onPostMessage: (WebMessage? message, WebUri? sourceOrigin, bool isMainFrame,
|
||||
PlatformJavaScriptReplyProxy replyProxy) {
|
||||
final shortenedMessage = message?.data.toString().substring(16);
|
||||
if (shortenedMessage != null && isJsonString(shortenedMessage)) {
|
||||
final parsedMessage = jsonDecode(shortenedMessage);
|
||||
final eventType = parsedMessage["event"];
|
||||
|
|
|
@ -351,7 +351,9 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
// in desktop platforms the navigation tree is different
|
||||
if (responsiveLayoutUtil.shouldRenderMobileUI) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Navigator.of(context).pop();
|
||||
if (this.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
@ -26,8 +26,7 @@ abstract class AppStoreBase with Store {
|
|||
AuthenticationStore authenticationStore;
|
||||
|
||||
@observable
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
|
||||
wallet;
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>? wallet;
|
||||
|
||||
WalletListStore walletList;
|
||||
|
||||
|
@ -36,16 +35,16 @@ abstract class AppStoreBase with Store {
|
|||
NodeListStore nodeListStore;
|
||||
|
||||
@action
|
||||
void changeCurrentWallet(
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
|
||||
TransactionInfo>
|
||||
wallet) {
|
||||
Future<void> changeCurrentWallet(
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet) async {
|
||||
this.wallet?.close();
|
||||
this.wallet = wallet;
|
||||
this.wallet!.setExceptionHandler(ExceptionHandler.onError);
|
||||
|
||||
if (isEVMCompatibleChain(wallet.type)) {
|
||||
getIt.get<Web3WalletService>().init();
|
||||
await getIt.get<Web3WalletService>().onDispose();
|
||||
getIt.get<Web3WalletService>().create();
|
||||
await getIt.get<Web3WalletService>().init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,9 @@ import 'dart:io';
|
|||
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/entities/buy_provider_types.dart';
|
||||
import 'package:cake_wallet/entities/provider_types.dart';
|
||||
import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
|
||||
import 'package:cake_wallet/entities/background_tasks.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
|
@ -147,7 +148,9 @@ abstract class SettingsStoreBase with Store {
|
|||
initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
currentSyncMode = initialSyncMode,
|
||||
currentSyncAll = initialSyncAll,
|
||||
priority = ObservableMap<WalletType, TransactionPriority>() {
|
||||
priority = ObservableMap<WalletType, TransactionPriority>(),
|
||||
defaultBuyProviders = ObservableMap<WalletType, ProviderType>(),
|
||||
defaultSellProviders = ObservableMap<WalletType, ProviderType>() {
|
||||
//this.nodes = ObservableMap<WalletType, Node>.of(nodes);
|
||||
|
||||
if (initialMoneroTransactionPriority != null) {
|
||||
|
@ -181,9 +184,25 @@ abstract class SettingsStoreBase with Store {
|
|||
initializeTrocadorProviderStates();
|
||||
|
||||
WalletType.values.forEach((walletType) {
|
||||
final key = 'defaultBuyProvider_${walletType.toString()}';
|
||||
final providerIndex = sharedPreferences.getInt(key);
|
||||
defaultBuyProviders[walletType] = providerIndex != null ? BuyProviderType.values[providerIndex] : BuyProviderType.AskEachTime;
|
||||
final key = 'buyProvider_${walletType.toString()}';
|
||||
final providerId = sharedPreferences.getString(key);
|
||||
if (providerId != null) {
|
||||
defaultBuyProviders[walletType] = ProviderType.values
|
||||
.firstWhere((provider) => provider.id == providerId, orElse: () => ProviderType.askEachTime);
|
||||
} else {
|
||||
defaultBuyProviders[walletType] = ProviderType.askEachTime;
|
||||
}
|
||||
});
|
||||
|
||||
WalletType.values.forEach((walletType) {
|
||||
final key = 'sellProvider_${walletType.toString()}';
|
||||
final providerId = sharedPreferences.getString(key);
|
||||
if (providerId != null) {
|
||||
defaultSellProviders[walletType] = ProviderType.values
|
||||
.firstWhere((provider) => provider.id == providerId, orElse: () => ProviderType.askEachTime);
|
||||
} else {
|
||||
defaultSellProviders[walletType] = ProviderType.askEachTime;
|
||||
}
|
||||
});
|
||||
|
||||
reaction(
|
||||
|
@ -196,6 +215,20 @@ abstract class SettingsStoreBase with Store {
|
|||
(bool shouldShowYatPopup) =>
|
||||
sharedPreferences.setBool(PreferencesKey.shouldShowYatPopup, shouldShowYatPopup));
|
||||
|
||||
defaultBuyProviders.observe((change) {
|
||||
final String key = 'buyProvider_${change.key.toString()}';
|
||||
if (change.newValue != null) {
|
||||
sharedPreferences.setString(key, change.newValue!.id);
|
||||
}
|
||||
});
|
||||
|
||||
defaultSellProviders.observe((change) {
|
||||
final String key = 'sellProvider_${change.key.toString()}';
|
||||
if (change.newValue != null) {
|
||||
sharedPreferences.setString(key, change.newValue!.id);
|
||||
}
|
||||
});
|
||||
|
||||
priority.observe((change) {
|
||||
final String? key;
|
||||
switch (change.key) {
|
||||
|
@ -251,16 +284,6 @@ abstract class SettingsStoreBase with Store {
|
|||
(bool disableSell) =>
|
||||
sharedPreferences.setBool(PreferencesKey.disableSellKey, disableSell));
|
||||
|
||||
reaction(
|
||||
(_) => defaultBuyProviders.asObservable(),
|
||||
(ObservableMap<WalletType, BuyProviderType> providers) {
|
||||
providers.forEach((walletType, provider) {
|
||||
final key = 'defaultBuyProvider_${walletType.toString()}';
|
||||
sharedPreferences.setInt(key, provider.index);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
reaction(
|
||||
(_) => walletListOrder,
|
||||
(WalletListOrderType walletListOrder) =>
|
||||
|
@ -595,7 +618,10 @@ abstract class SettingsStoreBase with Store {
|
|||
ObservableMap<String, bool> trocadorProviderStates = ObservableMap<String, bool>();
|
||||
|
||||
@observable
|
||||
ObservableMap<WalletType, BuyProviderType> defaultBuyProviders = ObservableMap<WalletType, BuyProviderType>();
|
||||
ObservableMap<WalletType, ProviderType> defaultBuyProviders;
|
||||
|
||||
@observable
|
||||
ObservableMap<WalletType, ProviderType> defaultSellProviders;
|
||||
|
||||
@observable
|
||||
SortBalanceBy sortBalanceBy;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/seed_phrase_length.dart';
|
||||
import 'package:cake_wallet/entities/seed_type.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -37,6 +38,9 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
|||
@computed
|
||||
SeedPhraseLength get seedPhraseLength => _settingsStore.seedPhraseLength;
|
||||
|
||||
@computed
|
||||
bool get isPolySeed => _settingsStore.moneroSeedType == SeedType.polyseed;
|
||||
|
||||
@action
|
||||
void setFiatApiMode(FiatApiMode fiatApiMode) => _settingsStore.fiatApiMode = fiatApiMode;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||
import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
|
@ -61,8 +61,7 @@ abstract class BuyViewModelBase with Store {
|
|||
String _url = '';
|
||||
|
||||
try {
|
||||
_url = await selectedProvider
|
||||
!.requestUrl(doubleAmount.toString(), fiatCurrency.title);
|
||||
_url = await selectedProvider!.requestUrl(doubleAmount.toString(), fiatCurrency.title);
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
|
|
|
@ -125,6 +125,8 @@ abstract class BalanceViewModelBase with Store {
|
|||
case WalletType.haven:
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return S.current.xmr_available_balance;
|
||||
default:
|
||||
return S.current.confirmed;
|
||||
|
@ -139,6 +141,9 @@ abstract class BalanceViewModelBase with Store {
|
|||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
return S.current.xmr_full_balance;
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return S.current.receivable_balance;
|
||||
default:
|
||||
return S.current.unconfirmed;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/entities/buy_provider_types.dart';
|
||||
import 'package:cake_wallet/entities/provider_types.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
@ -41,6 +42,7 @@ import 'package:cw_core/wallet_type.dart';
|
|||
import 'package:eth_sig_util/util/utils.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/entities/provider_types.dart';
|
||||
|
||||
part 'dashboard_view_model.g.dart';
|
||||
|
||||
|
@ -295,13 +297,31 @@ abstract class DashboardViewModelBase with Store {
|
|||
|
||||
Map<String, List<FilterItem>> filterItems;
|
||||
|
||||
BuyProviderType get defaultBuyProvider =>
|
||||
settingsStore.defaultBuyProviders[wallet.type] ?? BuyProviderType.AskEachTime;
|
||||
BuyProvider? get defaultBuyProvider => ProvidersHelper.getProviderByType(
|
||||
settingsStore.defaultBuyProviders[wallet.type] ?? ProviderType.askEachTime);
|
||||
|
||||
BuyProvider? get defaultSellProvider => ProvidersHelper.getProviderByType(
|
||||
settingsStore.defaultSellProviders[wallet.type] ?? ProviderType.askEachTime);
|
||||
|
||||
bool get isBuyEnabled => settingsStore.isBitcoinBuyEnabled;
|
||||
|
||||
List<BuyProviderType> get availableProviders =>
|
||||
BuyProviderType.getAvailableProviders(wallet.type);
|
||||
List<BuyProvider> get availableBuyProviders {
|
||||
final providerTypes = ProvidersHelper.getAvailableBuyProviderTypes(wallet.type);
|
||||
return providerTypes
|
||||
.map((type) => ProvidersHelper.getProviderByType(type))
|
||||
.where((provider) => provider != null)
|
||||
.cast<BuyProvider>()
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<BuyProvider> get availableSellProviders {
|
||||
final providerTypes = ProvidersHelper.getAvailableSellProviderTypes(wallet.type);
|
||||
return providerTypes
|
||||
.map((type) => ProvidersHelper.getProviderByType(type))
|
||||
.where((provider) => provider != null)
|
||||
.cast<BuyProvider>()
|
||||
.toList();
|
||||
}
|
||||
|
||||
bool get shouldShowYatPopup => settingsStore.shouldShowYatPopup;
|
||||
|
||||
|
@ -315,16 +335,15 @@ abstract class DashboardViewModelBase with Store {
|
|||
bool hasExchangeAction;
|
||||
|
||||
@computed
|
||||
bool get isEnabledBuyAction => !settingsStore.disableBuy && wallet.type != WalletType.haven;
|
||||
bool get isEnabledBuyAction =>
|
||||
!settingsStore.disableBuy && availableBuyProviders.isNotEmpty;
|
||||
|
||||
@observable
|
||||
bool hasBuyAction;
|
||||
|
||||
@computed
|
||||
bool get isEnabledSellAction =>
|
||||
!settingsStore.disableSell &&
|
||||
wallet.type != WalletType.haven &&
|
||||
wallet.type != WalletType.monero;
|
||||
!settingsStore.disableSell && availableSellProviders.isNotEmpty;
|
||||
|
||||
@observable
|
||||
bool hasSellAction;
|
||||
|
|
|
@ -101,7 +101,7 @@ class TransactionListItem extends ActionListItem with Keyable {
|
|||
break;
|
||||
case WalletType.nano:
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: double.parse(nanoUtil!.getRawAsDecimalString(
|
||||
cryptoAmount: double.parse(nanoUtil!.getRawAsUsableString(
|
||||
nano!.getTransactionAmountRaw(transaction).toString(), nanoUtil!.rawPerNano)),
|
||||
price: price);
|
||||
break;
|
||||
|
|
|
@ -9,7 +9,7 @@ import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.d
|
|||
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||
import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
|
||||
|
||||
part 'order_details_view_model.g.dart';
|
||||
|
@ -50,8 +50,10 @@ abstract class OrderDetailsViewModelBase with Store {
|
|||
@action
|
||||
Future<void> _updateOrder() async {
|
||||
try {
|
||||
if (_provider != null) {
|
||||
final updatedOrder = await _provider!.findOrderById(order.id);
|
||||
if (_provider != null && (_provider is MoonPayBuyProvider || _provider is WyreBuyProvider)) {
|
||||
final updatedOrder = _provider is MoonPayBuyProvider
|
||||
? await (_provider as MoonPayBuyProvider).findOrderById(order.id)
|
||||
: await (_provider as WyreBuyProvider).findOrderById(order.id);
|
||||
updatedOrder.from = order.from;
|
||||
updatedOrder.to = order.to;
|
||||
updatedOrder.receiveAddress = order.receiveAddress;
|
||||
|
@ -87,19 +89,26 @@ abstract class OrderDetailsViewModelBase with Store {
|
|||
value: order.provider.title)
|
||||
);
|
||||
|
||||
if (_provider?.trackUrl.isNotEmpty ?? false) {
|
||||
final buildURL = _provider!.trackUrl + '${order.transferId}';
|
||||
items.add(
|
||||
TrackTradeListItem(
|
||||
title: 'Track',
|
||||
value: buildURL,
|
||||
onTap: () {
|
||||
try {
|
||||
launch(buildURL);
|
||||
} catch (e) {}
|
||||
}
|
||||
)
|
||||
);
|
||||
if (_provider != null && (_provider is MoonPayBuyProvider || _provider is WyreBuyProvider)) {
|
||||
|
||||
final trackUrl = _provider is MoonPayBuyProvider
|
||||
? (_provider as MoonPayBuyProvider).trackUrl
|
||||
: (_provider as WyreBuyProvider).trackUrl;
|
||||
|
||||
if (trackUrl.isNotEmpty ?? false) {
|
||||
final buildURL = trackUrl + '${order.transferId}';
|
||||
items.add(
|
||||
TrackTradeListItem(
|
||||
title: 'Track',
|
||||
value: buildURL,
|
||||
onTap: () {
|
||||
try {
|
||||
launch(buildURL);
|
||||
} catch (e) {}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
items.add(
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import 'dart:io';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/main.dart';
|
||||
|
@ -60,11 +62,17 @@ abstract class RestoreFromBackupViewModelBase with Store {
|
|||
});
|
||||
|
||||
state = ExecutedSuccessfullyState();
|
||||
} catch (e) {
|
||||
} catch (e, s) {
|
||||
var msg = e.toString();
|
||||
|
||||
if (msg.toLowerCase().contains("message authentication code (mac)")) {
|
||||
msg = 'Incorrect backup password';
|
||||
} else {
|
||||
ExceptionHandler.onError(FlutterErrorDetails(
|
||||
exception: e,
|
||||
stack: s,
|
||||
library: this.toString(),
|
||||
));
|
||||
}
|
||||
|
||||
state = FailureState(msg);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/buy_provider_types.dart';
|
||||
import 'package:cake_wallet/entities/provider_types.dart';
|
||||
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
|
@ -59,12 +60,24 @@ abstract class OtherSettingsViewModelBase with Store {
|
|||
bool get isEnabledBuyAction =>
|
||||
!_settingsStore.disableBuy && _wallet.type != WalletType.haven;
|
||||
|
||||
List<BuyProviderType> get availableBuyProviders =>
|
||||
BuyProviderType.getAvailableProviders(walletType);
|
||||
@computed
|
||||
bool get isEnabledSellAction =>
|
||||
!_settingsStore.disableSell && _wallet.type != WalletType.haven;
|
||||
|
||||
BuyProviderType get buyProviderType =>
|
||||
List<ProviderType> get availableBuyProvidersTypes {
|
||||
return ProvidersHelper.getAvailableBuyProviderTypes(walletType);
|
||||
}
|
||||
|
||||
List<ProviderType> get availableSellProvidersTypes =>
|
||||
ProvidersHelper.getAvailableSellProviderTypes(walletType);
|
||||
|
||||
ProviderType get buyProviderType =>
|
||||
_settingsStore.defaultBuyProviders[walletType] ??
|
||||
BuyProviderType.AskEachTime;
|
||||
ProviderType.askEachTime;
|
||||
|
||||
ProviderType get sellProviderType =>
|
||||
_settingsStore.defaultSellProviders[walletType] ??
|
||||
ProviderType.askEachTime;
|
||||
|
||||
String getDisplayPriority(dynamic priority) {
|
||||
final _priority = priority as TransactionPriority;
|
||||
|
@ -80,14 +93,28 @@ abstract class OtherSettingsViewModelBase with Store {
|
|||
}
|
||||
|
||||
String getBuyProviderType(dynamic buyProviderType) {
|
||||
final _buyProviderType = buyProviderType as BuyProviderType;
|
||||
final _buyProviderType = buyProviderType as ProviderType;
|
||||
return _buyProviderType == ProviderType.askEachTime
|
||||
? S.current.ask_each_time
|
||||
: _buyProviderType.title;
|
||||
}
|
||||
|
||||
return _buyProviderType.toString();
|
||||
String getSellProviderType(dynamic sellProviderType) {
|
||||
final _sellProviderType = sellProviderType as ProviderType;
|
||||
return _sellProviderType == ProviderType.askEachTime
|
||||
? S.current.ask_each_time
|
||||
: _sellProviderType.title;
|
||||
}
|
||||
|
||||
void onDisplayPrioritySelected(TransactionPriority priority) =>
|
||||
_settingsStore.priority[_wallet.type] = priority;
|
||||
|
||||
void onBuyProviderTypeSelected(BuyProviderType buyProviderType) =>
|
||||
@action
|
||||
ProviderType onBuyProviderTypeSelected(ProviderType buyProviderType) =>
|
||||
_settingsStore.defaultBuyProviders[walletType] = buyProviderType;
|
||||
|
||||
@action
|
||||
ProviderType onSellProviderTypeSelected(
|
||||
ProviderType sellProviderType) =>
|
||||
_settingsStore.defaultSellProviders[walletType] = sellProviderType;
|
||||
}
|
||||
|
|