Merge branch 'flutter-upgrade' of https://github.com/cake-tech/cake_wallet into cake-phone
BIN
assets/images/dcr_icon.png
Normal file
After Width: | Height: | Size: 115 KiB |
BIN
assets/images/husd_icon.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
assets/images/kmd_icon.png
Normal file
After Width: | Height: | Size: 129 KiB |
BIN
assets/images/mana_icon.png
Normal file
After Width: | Height: | Size: 125 KiB |
BIN
assets/images/matic_icon.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
assets/images/mkr_icon.png
Normal file
After Width: | Height: | Size: 69 KiB |
BIN
assets/images/near_icon.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
assets/images/oxt_icon.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
assets/images/paxg_icon.png
Normal file
After Width: | Height: | Size: 127 KiB |
BIN
assets/images/pivx_icon.png
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
assets/images/rune_icon.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
assets/images/rvn_icon.png
Normal file
After Width: | Height: | Size: 242 KiB |
BIN
assets/images/scrt_icon.png
Normal file
After Width: | Height: | Size: 164 KiB |
BIN
assets/images/stx_icon.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
assets/images/uni_icon.png
Normal file
After Width: | Height: | Size: 117 KiB |
|
@ -51,6 +51,23 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
|
|||
CryptoCurrency.zec,
|
||||
CryptoCurrency.zen,
|
||||
CryptoCurrency.xvg,
|
||||
CryptoCurrency.usdcpoly,
|
||||
CryptoCurrency.dcr,
|
||||
CryptoCurrency.husd,
|
||||
CryptoCurrency.kmd,
|
||||
CryptoCurrency.mana,
|
||||
CryptoCurrency.maticpoly,
|
||||
CryptoCurrency.matic,
|
||||
CryptoCurrency.mkr,
|
||||
CryptoCurrency.near,
|
||||
CryptoCurrency.oxt,
|
||||
CryptoCurrency.paxg,
|
||||
CryptoCurrency.pivx,
|
||||
CryptoCurrency.rune,
|
||||
CryptoCurrency.rvn,
|
||||
CryptoCurrency.scrt,
|
||||
CryptoCurrency.uni,
|
||||
CryptoCurrency.stx,
|
||||
];
|
||||
|
||||
static const xmr = CryptoCurrency(title: 'XMR', iconPath: 'assets/images/monero_icon.png', name: 'Monero', raw: 0);
|
||||
|
@ -102,6 +119,26 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
|
|||
static const zen = CryptoCurrency(title: 'ZEN', iconPath: 'assets/images/zen_icon.png', raw: 44);
|
||||
static const xvg = CryptoCurrency(title: 'XVG', name: 'Verge', iconPath: 'assets/images/xvg_icon.png', raw: 45);
|
||||
|
||||
static const usdcpoly = CryptoCurrency(title: 'USDC', iconPath: 'assets/images/usdc_icon.png', tag: 'POLY', raw: 46);
|
||||
static const dcr = CryptoCurrency(title: 'DCR', iconPath: 'assets/images/dcr_icon.png', raw: 47);
|
||||
static const husd = CryptoCurrency(title: 'HUSD', iconPath: 'assets/images/husd_icon.png', tag: 'ETH', raw: 48);
|
||||
static const kmd = CryptoCurrency(title: 'KMD', iconPath: 'assets/images/kmd_icon.png', raw: 49);
|
||||
static const mana = CryptoCurrency(title: 'MANA', iconPath: 'assets/images/mana_icon.png', tag: 'ETH', raw: 50);
|
||||
static const maticpoly = CryptoCurrency(title: 'MATIC', iconPath: 'assets/images/matic_icon.png', tag: 'POLY', raw: 51);
|
||||
static const matic = CryptoCurrency(title: 'MATIC', iconPath: 'assets/images/matic_icon.png', tag: 'ETH', raw: 52);
|
||||
static const mkr = CryptoCurrency(title: 'MKR', iconPath: 'assets/images/mkr_icon.png', tag: 'ETH', raw: 53);
|
||||
static const near = CryptoCurrency(title: 'NEAR', iconPath: 'assets/images/near_icon.png', raw: 54);
|
||||
static const oxt = CryptoCurrency(title: 'OXT', iconPath: 'assets/images/oxt_icon.png', tag: 'ETH', raw: 55);
|
||||
static const paxg = CryptoCurrency(title: 'PAXG', iconPath: 'assets/images/paxg_icon.png', tag: 'ETH', raw: 56);
|
||||
static const pivx = CryptoCurrency(title: 'PIVX', iconPath: 'assets/images/pivx_icon.png', raw: 57);
|
||||
static const rune = CryptoCurrency(title: 'RUNE', iconPath: 'assets/images/rune_icon.png', raw: 58);
|
||||
static const rvn = CryptoCurrency(title: 'RVN', iconPath: 'assets/images/rvn_icon.png', raw: 59);
|
||||
static const scrt = CryptoCurrency(title: 'SCRT', iconPath: 'assets/images/scrt_icon.png', raw: 60);
|
||||
static const uni = CryptoCurrency(title: 'UNI', iconPath: 'assets/images/uni_icon.png', tag: 'ETH', raw: 61);
|
||||
static const stx = CryptoCurrency(title: 'STX', iconPath: 'assets/images/stx_icon.png', raw: 62);
|
||||
|
||||
|
||||
|
||||
static CryptoCurrency deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
|
@ -196,6 +233,40 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
|
|||
return CryptoCurrency.zen;
|
||||
case 45:
|
||||
return CryptoCurrency.xvg;
|
||||
case 46:
|
||||
return CryptoCurrency.usdcpoly;
|
||||
case 47:
|
||||
return CryptoCurrency.dcr;
|
||||
case 48:
|
||||
return CryptoCurrency.husd;
|
||||
case 49:
|
||||
return CryptoCurrency.kmd;
|
||||
case 50:
|
||||
return CryptoCurrency.mana;
|
||||
case 51:
|
||||
return CryptoCurrency.maticpoly;
|
||||
case 52:
|
||||
return CryptoCurrency.matic;
|
||||
case 53:
|
||||
return CryptoCurrency.mkr;
|
||||
case 54:
|
||||
return CryptoCurrency.near;
|
||||
case 55:
|
||||
return CryptoCurrency.oxt;
|
||||
case 56:
|
||||
return CryptoCurrency.paxg;
|
||||
case 57:
|
||||
return CryptoCurrency.pivx;
|
||||
case 58:
|
||||
return CryptoCurrency.rune;
|
||||
case 59:
|
||||
return CryptoCurrency.rvn;
|
||||
case 60:
|
||||
return CryptoCurrency.scrt;
|
||||
case 61:
|
||||
return CryptoCurrency.uni;
|
||||
case 62:
|
||||
return CryptoCurrency.stx;
|
||||
default:
|
||||
throw Exception('Unexpected token: $raw for CryptoCurrency deserialize');
|
||||
}
|
||||
|
@ -295,6 +366,40 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
|
|||
return CryptoCurrency.zen;
|
||||
case 'xvg':
|
||||
return CryptoCurrency.xvg;
|
||||
case 'usdcpoly':
|
||||
return CryptoCurrency.usdcpoly;
|
||||
case 'dcr':
|
||||
return CryptoCurrency.dcr;
|
||||
case 'husd':
|
||||
return CryptoCurrency.husd;
|
||||
case 'kmd':
|
||||
return CryptoCurrency.kmd;
|
||||
case 'mana':
|
||||
return CryptoCurrency.mana;
|
||||
case 'maticpoly':
|
||||
return CryptoCurrency.maticpoly;
|
||||
case 'matic':
|
||||
return CryptoCurrency.matic;
|
||||
case 'mkr':
|
||||
return CryptoCurrency.mkr;
|
||||
case 'near':
|
||||
return CryptoCurrency.near;
|
||||
case 'oxt':
|
||||
return CryptoCurrency.oxt;
|
||||
case 'paxg':
|
||||
return CryptoCurrency.paxg;
|
||||
case 'pivx':
|
||||
return CryptoCurrency.pivx;
|
||||
case 'rune':
|
||||
return CryptoCurrency.rune;
|
||||
case 'rvn':
|
||||
return CryptoCurrency.rvn;
|
||||
case 'scrt':
|
||||
return CryptoCurrency.scrt;
|
||||
case 'uni':
|
||||
return CryptoCurrency.uni;
|
||||
case 'stx':
|
||||
return CryptoCurrency.stx;
|
||||
default:
|
||||
throw Exception('Unexpected token: $raw for CryptoCurrency fromString');
|
||||
}
|
||||
|
|
|
@ -17,39 +17,25 @@ class AddressValidator extends TextValidator {
|
|||
case CryptoCurrency.ada:
|
||||
return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$'
|
||||
'|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$';
|
||||
case CryptoCurrency.ape:
|
||||
return '0x[0-9a-zA-Z]';
|
||||
case CryptoCurrency.avaxc:
|
||||
return '0x[0-9a-zA-Z]';
|
||||
case CryptoCurrency.bch:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.bnb:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.btc:
|
||||
return '^1[0-9a-zA-Z]{32}\$|^1[0-9a-zA-Z]{33}\$|^3[0-9a-zA-Z]{32}\$'
|
||||
'|^3[0-9a-zA-Z]{33}\$|^bc1[0-9a-zA-Z]{39}\$|^bc1[0-9a-zA-Z]{59}\$';
|
||||
case CryptoCurrency.dai:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.dash:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.eos:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.eth:
|
||||
return '0x[0-9a-zA-Z]';
|
||||
case CryptoCurrency.ltc:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.nano:
|
||||
return '[0-9a-zA-Z_]';
|
||||
case CryptoCurrency.trx:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.usdc:
|
||||
case CryptoCurrency.usdcpoly:
|
||||
case CryptoCurrency.husd:
|
||||
case CryptoCurrency.ape:
|
||||
case CryptoCurrency.avaxc:
|
||||
case CryptoCurrency.eth:
|
||||
case CryptoCurrency.mana:
|
||||
case CryptoCurrency.matic:
|
||||
case CryptoCurrency.maticpoly:
|
||||
case CryptoCurrency.mkr:
|
||||
case CryptoCurrency.oxt:
|
||||
case CryptoCurrency.paxg:
|
||||
case CryptoCurrency.uni:
|
||||
return '0x[0-9a-zA-Z]';
|
||||
case CryptoCurrency.usdt:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.usdterc20:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.xlm:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.xrp:
|
||||
return '^[0-9a-zA-Z]{34}\$|^X[0-9a-zA-Z]{46}\$';
|
||||
case CryptoCurrency.xhv:
|
||||
|
@ -67,6 +53,16 @@ class AddressValidator extends TextValidator {
|
|||
case CryptoCurrency.xnok:
|
||||
case CryptoCurrency.xnzd:
|
||||
case CryptoCurrency.xusd:
|
||||
case CryptoCurrency.usdt:
|
||||
case CryptoCurrency.usdterc20:
|
||||
case CryptoCurrency.xlm:
|
||||
case CryptoCurrency.trx:
|
||||
case CryptoCurrency.dai:
|
||||
case CryptoCurrency.dash:
|
||||
case CryptoCurrency.eos:
|
||||
case CryptoCurrency.ltc:
|
||||
case CryptoCurrency.bch:
|
||||
case CryptoCurrency.bnb:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.hbar:
|
||||
return '[0-9a-zA-Z.]';
|
||||
|
@ -74,6 +70,22 @@ class AddressValidator extends TextValidator {
|
|||
return '^zs[0-9a-zA-Z]{75}';
|
||||
case CryptoCurrency.zec:
|
||||
return '^t1[0-9a-zA-Z]{33}\$|^t3[0-9a-zA-Z]{33}\$';
|
||||
case CryptoCurrency.dcr:
|
||||
return 'D[ksecS]([0-9a-zA-Z])+';
|
||||
case CryptoCurrency.rvn:
|
||||
return '[Rr]([1-9a-km-zA-HJ-NP-Z]){33}';
|
||||
case CryptoCurrency.near:
|
||||
return '[0-9a-f]{64}';
|
||||
case CryptoCurrency.rune:
|
||||
return 'thor1[0-9a-z]{38}';
|
||||
case CryptoCurrency.scrt:
|
||||
return 'secret1[0-9a-z]{38}';
|
||||
case CryptoCurrency.stx:
|
||||
return 'S[MP][0-9a-zA-Z]+';
|
||||
case CryptoCurrency.kmd:
|
||||
return 'R[0-9a-zA-Z]{33}';
|
||||
case CryptoCurrency.pivx:
|
||||
return 'D([1-9a-km-zA-HJ-NP-Z]){33}';
|
||||
default:
|
||||
return '[0-9a-zA-Z]';
|
||||
}
|
||||
|
@ -160,6 +172,30 @@ class AddressValidator extends TextValidator {
|
|||
return null;
|
||||
case CryptoCurrency.zec:
|
||||
return null;
|
||||
case CryptoCurrency.kmd:
|
||||
case CryptoCurrency.pivx:
|
||||
case CryptoCurrency.rvn:
|
||||
return [34];
|
||||
case CryptoCurrency.dcr:
|
||||
return [35];
|
||||
case CryptoCurrency.stx:
|
||||
return [40, 41, 42];
|
||||
case CryptoCurrency.usdcpoly:
|
||||
case CryptoCurrency.husd:
|
||||
case CryptoCurrency.mana:
|
||||
case CryptoCurrency.matic:
|
||||
case CryptoCurrency.maticpoly:
|
||||
case CryptoCurrency.mkr:
|
||||
case CryptoCurrency.oxt:
|
||||
case CryptoCurrency.paxg:
|
||||
case CryptoCurrency.uni:
|
||||
return [42];
|
||||
case CryptoCurrency.rune:
|
||||
return [43];
|
||||
case CryptoCurrency.scrt:
|
||||
return [45];
|
||||
case CryptoCurrency.near:
|
||||
return [64];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -16,16 +16,7 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:http/http.dart';
|
||||
|
||||
class SideShiftExchangeProvider extends ExchangeProvider {
|
||||
SideShiftExchangeProvider()
|
||||
: super(
|
||||
pairList: CryptoCurrency.all
|
||||
.where((i) => i != CryptoCurrency.xhv)
|
||||
.map((i) => CryptoCurrency.all
|
||||
.where((i) => i != CryptoCurrency.xhv)
|
||||
.map((k) => ExchangePair(from: i, to: k, reverse: true))
|
||||
.where((c) => c != null))
|
||||
.expand((i) => i)
|
||||
.toList());
|
||||
SideShiftExchangeProvider() : super(pairList: _supportedPairs());
|
||||
|
||||
static const affiliateId = secrets.sideShiftAffiliateId;
|
||||
static const apiBaseUrl = 'https://sideshift.ai/api';
|
||||
|
@ -34,6 +25,35 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
|||
static const quotePath = '/v1/quotes';
|
||||
static const permissionPath = '/v1/permissions';
|
||||
|
||||
static const List<CryptoCurrency> _notSupported = [
|
||||
CryptoCurrency.xhv,
|
||||
CryptoCurrency.dcr,
|
||||
CryptoCurrency.husd,
|
||||
CryptoCurrency.kmd,
|
||||
CryptoCurrency.mkr,
|
||||
CryptoCurrency.near,
|
||||
CryptoCurrency.oxt,
|
||||
CryptoCurrency.paxg,
|
||||
CryptoCurrency.pivx,
|
||||
CryptoCurrency.rune,
|
||||
CryptoCurrency.rvn,
|
||||
CryptoCurrency.scrt,
|
||||
CryptoCurrency.stx,
|
||||
];
|
||||
|
||||
static List<ExchangePair> _supportedPairs() {
|
||||
final supportedCurrencies = CryptoCurrency.all
|
||||
.where((element) => !_notSupported.contains(element))
|
||||
.toList();
|
||||
|
||||
return supportedCurrencies
|
||||
.map((i) => supportedCurrencies
|
||||
.map((k) => ExchangePair(from: i, to: k, reverse: true))
|
||||
.where((c) => c != null))
|
||||
.expand((i) => i)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description =>
|
||||
ExchangeProviderDescription.sideShift;
|
||||
|
@ -270,6 +290,14 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
|||
return currency.tag!.toLowerCase();
|
||||
case CryptoCurrency.usdterc20:
|
||||
return 'usdtErc20';
|
||||
case CryptoCurrency.usdttrc20:
|
||||
return 'usdtTrc20';
|
||||
case CryptoCurrency.usdcpoly:
|
||||
return 'usdcpolygon';
|
||||
case CryptoCurrency.usdcsol:
|
||||
return 'usdcsol';
|
||||
case CryptoCurrency.maticpoly:
|
||||
return 'polygon';
|
||||
default:
|
||||
return currency.title.toLowerCase();
|
||||
}
|
||||
|
|
|
@ -17,8 +17,11 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
|||
SimpleSwapExchangeProvider()
|
||||
: super(
|
||||
pairList: CryptoCurrency.all
|
||||
.map((i) =>
|
||||
CryptoCurrency.all.map((k) => ExchangePair(from: i, to: k, reverse: true)).where((c) => c != null))
|
||||
.where((i) => i != CryptoCurrency.zaddr)
|
||||
.map((i) => CryptoCurrency.all
|
||||
.where((i) => i != CryptoCurrency.zaddr)
|
||||
.map((k) => ExchangePair(from: i, to: k, reverse: true))
|
||||
.where((c) => c != null))
|
||||
.expand((i) => i)
|
||||
.toList());
|
||||
|
||||
|
@ -56,7 +59,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
|||
final uri = Uri.https(apiAuthority, getEstimatePath, params);
|
||||
final response = await get(uri);
|
||||
|
||||
if (response.body == null) return 0.00;
|
||||
if (response.body == null || response.body == "null") return 0.00;
|
||||
final data = json.decode(response.body) as String;
|
||||
|
||||
return double.parse(data);
|
||||
|
@ -151,8 +154,8 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
|||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final min = responseJSON['min'] != null ? double.tryParse(responseJSON['min'] as String) : null;
|
||||
final max = responseJSON['max'] != null ? double.parse(responseJSON['max'] as String) : null;
|
||||
final min = double.tryParse(responseJSON['min'] as String? ?? '');
|
||||
final max = double.tryParse(responseJSON['max'] as String? ?? '');
|
||||
|
||||
return Limits(min: min, max: max);
|
||||
}
|
||||
|
@ -217,7 +220,13 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
|||
case CryptoCurrency.bnb:
|
||||
return currency.tag!.toLowerCase();
|
||||
case CryptoCurrency.usdterc20:
|
||||
return 'usdterc';
|
||||
return 'usdterc20';
|
||||
case CryptoCurrency.usdttrc20:
|
||||
return 'usdttrc20';
|
||||
case CryptoCurrency.usdcpoly:
|
||||
return 'usdcpoly';
|
||||
case CryptoCurrency.usdcsol:
|
||||
return 'usdcspl';
|
||||
default:
|
||||
return currency.title.toLowerCase();
|
||||
}
|
||||
|
|
|
@ -179,6 +179,7 @@ class ExchangePage extends BasePage {
|
|||
padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
|
||||
child: Observer(
|
||||
builder: (_) => ExchangeCard(
|
||||
onDispose: disposeBestRateSync,
|
||||
hasAllAmount: exchangeViewModel.hasAllAmount,
|
||||
allAmount: exchangeViewModel.hasAllAmount
|
||||
? () => exchangeViewModel
|
||||
|
@ -265,6 +266,7 @@ class ExchangePage extends BasePage {
|
|||
EdgeInsets.only(top: 29, left: 24, right: 24),
|
||||
child: Observer(
|
||||
builder: (_) => ExchangeCard(
|
||||
onDispose: disposeBestRateSync,
|
||||
amountFocusNode: _receiveAmountFocus,
|
||||
addressFocusNode: _receiveAddressFocus,
|
||||
key: receiveKey,
|
||||
|
@ -743,13 +745,13 @@ class ExchangePage extends BasePage {
|
|||
if (_receiveAmountFocus.hasFocus) {
|
||||
exchangeViewModel.isFixedRateMode = true;
|
||||
}
|
||||
exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text);
|
||||
// exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text);
|
||||
});
|
||||
|
||||
_depositAmountFocus.addListener(() {
|
||||
exchangeViewModel.isFixedRateMode = false;
|
||||
exchangeViewModel.changeDepositAmount(
|
||||
amount: depositAmountController.text);
|
||||
// exchangeViewModel.changeDepositAmount(
|
||||
// amount: depositAmountController.text);
|
||||
});
|
||||
|
||||
_isReactionsSet = true;
|
||||
|
@ -791,4 +793,6 @@ class ExchangePage extends BasePage {
|
|||
final address = await extractAddressFromParsed(context, parsedAddress);
|
||||
return address;
|
||||
}
|
||||
|
||||
void disposeBestRateSync() => exchangeViewModel.bestRateSync?.cancel();
|
||||
}
|
||||
|
|
|
@ -37,7 +37,8 @@ class ExchangeCard extends StatefulWidget {
|
|||
this.addressFocusNode,
|
||||
this.allAmount,
|
||||
this.onPushPasteButton,
|
||||
this.onPushAddressBookButton})
|
||||
this.onPushAddressBookButton,
|
||||
this.onDispose})
|
||||
: super(key: key);
|
||||
|
||||
final List<CryptoCurrency> currencies;
|
||||
|
@ -63,6 +64,7 @@ class ExchangeCard extends StatefulWidget {
|
|||
final VoidCallback? allAmount;
|
||||
final void Function(BuildContext context)? onPushPasteButton;
|
||||
final void Function(BuildContext context)? onPushAddressBookButton;
|
||||
final Function()? onDispose;
|
||||
|
||||
@override
|
||||
ExchangeCardState createState() => ExchangeCardState();
|
||||
|
@ -106,6 +108,13 @@ class ExchangeCardState extends State<ExchangeCard> {
|
|||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.onDispose?.call();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void changeLimits({String? min, String? max}) {
|
||||
setState(() {
|
||||
_min = min;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
|
||||
|
@ -59,12 +60,12 @@ abstract class ExchangeViewModelBase with Store {
|
|||
receiveCurrency = wallet.currency,
|
||||
depositCurrency = wallet.currency,
|
||||
providerList = [ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider()],
|
||||
selectedProviders = ObservableList<ExchangeProvider>(),
|
||||
currentTradeAvailableProviders = SplayTreeMap<double, ExchangeProvider>() {
|
||||
selectedProviders = ObservableList<ExchangeProvider>() {
|
||||
const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano];
|
||||
const excludeReceiveCurrencies = [CryptoCurrency.xlm, CryptoCurrency.xrp,
|
||||
CryptoCurrency.bnb, CryptoCurrency.btt, CryptoCurrency.nano];
|
||||
_initialPairBasedOnWallet();
|
||||
|
||||
final Map<String, dynamic> exchangeProvidersSelection = json
|
||||
.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") as Map<String, dynamic>;
|
||||
|
||||
|
@ -76,6 +77,11 @@ abstract class ExchangeViewModelBase with Store {
|
|||
: (exchangeProvidersSelection[element.title] as bool))
|
||||
.toList());
|
||||
|
||||
_setAvailableProviders();
|
||||
_calculateBestRate();
|
||||
|
||||
bestRateSync = Timer.periodic(Duration(seconds: 10), (timer) => _calculateBestRate());
|
||||
|
||||
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
|
||||
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
|
||||
depositAmount = '';
|
||||
|
@ -119,8 +125,15 @@ abstract class ExchangeViewModelBase with Store {
|
|||
/// Maps in dart are not sorted by default
|
||||
/// SplayTreeMap is a map sorted by keys
|
||||
/// will use it to sort available providers
|
||||
/// depending on the amount they yield for the current trade
|
||||
SplayTreeMap<double, ExchangeProvider> currentTradeAvailableProviders;
|
||||
/// based on the rate they yield for the current trade
|
||||
///
|
||||
///
|
||||
/// initialize with descending comparator
|
||||
/// since we want largest rate first
|
||||
final SplayTreeMap<double, ExchangeProvider> _sortedAvailableProviders =
|
||||
SplayTreeMap<double, ExchangeProvider>((double a, double b) => b.compareTo(a));
|
||||
|
||||
final List<ExchangeProvider> _tradeAvailableProviders = [];
|
||||
|
||||
@observable
|
||||
ObservableList<ExchangeProvider> selectedProviders;
|
||||
|
@ -191,6 +204,10 @@ abstract class ExchangeViewModelBase with Store {
|
|||
|
||||
final SettingsStore _settingsStore;
|
||||
|
||||
double _bestRate = 0.0;
|
||||
|
||||
late Timer bestRateSync;
|
||||
|
||||
@action
|
||||
void changeDepositCurrency({required CryptoCurrency currency}) {
|
||||
depositCurrency = currency;
|
||||
|
@ -210,68 +227,11 @@ abstract class ExchangeViewModelBase with Store {
|
|||
}
|
||||
|
||||
@action
|
||||
void changeReceiveAmount({required String amount}) {
|
||||
Future<void> changeReceiveAmount({required String amount}) async {
|
||||
receiveAmount = amount;
|
||||
isReverse = true;
|
||||
|
||||
if (amount == null || amount.isEmpty) {
|
||||
depositAmount = '';
|
||||
receiveAmount = '';
|
||||
return;
|
||||
}
|
||||
|
||||
final _enteredAmount = double.parse(amount.replaceAll(',', '.')) ?? 0;
|
||||
|
||||
currentTradeAvailableProviders.clear();
|
||||
for (var provider in selectedProviders) {
|
||||
/// if this provider is not valid for the current pair, skip it
|
||||
if (!providersForCurrentPair().contains(provider)) {
|
||||
continue;
|
||||
}
|
||||
provider
|
||||
.calculateAmount(
|
||||
from: receiveCurrency,
|
||||
to: depositCurrency,
|
||||
amount: _enteredAmount,
|
||||
isFixedRateMode: isFixedRateMode,
|
||||
isReceiveAmount: true)
|
||||
.then((amount) {
|
||||
|
||||
final from = isFixedRateMode
|
||||
? receiveCurrency
|
||||
: depositCurrency;
|
||||
final to = isFixedRateMode
|
||||
? depositCurrency
|
||||
: receiveCurrency;
|
||||
|
||||
provider.fetchLimits(
|
||||
from: from,
|
||||
to: to,
|
||||
isFixedRateMode: isFixedRateMode,
|
||||
).then((limits) {
|
||||
/// if the entered amount doesn't exceed the limits of this provider
|
||||
if ((limits?.max ?? double.maxFinite) >= _enteredAmount
|
||||
&& (limits?.min ?? 0) <= _enteredAmount) {
|
||||
/// add this provider as its valid for this trade
|
||||
/// will be sorted ascending already since
|
||||
/// we seek the least deposit amount
|
||||
currentTradeAvailableProviders[amount] = provider;
|
||||
}
|
||||
return amount;
|
||||
}).then((amount) => depositAmount = _cryptoNumberFormat
|
||||
.format(amount)
|
||||
.toString()
|
||||
.replaceAll(RegExp('\\,'), ''));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void changeDepositAmount({required String amount}) {
|
||||
depositAmount = amount;
|
||||
isReverse = false;
|
||||
|
||||
if (amount == null || amount.isEmpty) {
|
||||
if (amount.isEmpty) {
|
||||
depositAmount = '';
|
||||
receiveAmount = '';
|
||||
return;
|
||||
|
@ -279,93 +239,115 @@ abstract class ExchangeViewModelBase with Store {
|
|||
|
||||
final _enteredAmount = double.tryParse(amount.replaceAll(',', '.')) ?? 0;
|
||||
|
||||
currentTradeAvailableProviders.clear();
|
||||
for (var provider in selectedProviders) {
|
||||
/// if this provider is not valid for the current pair, skip it
|
||||
if (!providersForCurrentPair().contains(provider)) {
|
||||
continue;
|
||||
if (_bestRate == 0) {
|
||||
depositAmount = S.current.fetching;
|
||||
|
||||
await _calculateBestRate();
|
||||
}
|
||||
|
||||
depositAmount = _cryptoNumberFormat
|
||||
.format(_enteredAmount / _bestRate)
|
||||
.toString()
|
||||
.replaceAll(RegExp('\\,'), '');
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> changeDepositAmount({required String amount}) async {
|
||||
depositAmount = amount;
|
||||
isReverse = false;
|
||||
|
||||
if (amount.isEmpty) {
|
||||
depositAmount = '';
|
||||
receiveAmount = '';
|
||||
return;
|
||||
}
|
||||
|
||||
final _enteredAmount = double.tryParse(amount.replaceAll(',', '.')) ?? 0;
|
||||
|
||||
/// in case the best rate was not calculated yet
|
||||
if (_bestRate == 0) {
|
||||
receiveAmount = S.current.fetching;
|
||||
|
||||
await _calculateBestRate();
|
||||
}
|
||||
|
||||
receiveAmount = _cryptoNumberFormat
|
||||
.format(_bestRate * _enteredAmount)
|
||||
.toString()
|
||||
.replaceAll(RegExp('\\,'), '');
|
||||
}
|
||||
|
||||
Future<void> _calculateBestRate() async {
|
||||
final result = await Future.wait<double>(
|
||||
_tradeAvailableProviders
|
||||
.map((element) => element.calculateAmount(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
amount: 1,
|
||||
isFixedRateMode: isFixedRateMode,
|
||||
isReceiveAmount: false))
|
||||
);
|
||||
|
||||
_sortedAvailableProviders.clear();
|
||||
|
||||
for (int i=0;i<result.length;i++) {
|
||||
if (result[i] != 0) {
|
||||
/// add this provider as its valid for this trade
|
||||
_sortedAvailableProviders[result[i]] = _tradeAvailableProviders[i];
|
||||
}
|
||||
provider
|
||||
.calculateAmount(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
amount: _enteredAmount,
|
||||
isFixedRateMode: isFixedRateMode,
|
||||
isReceiveAmount: false)
|
||||
.then((amount) {
|
||||
|
||||
final from = isFixedRateMode
|
||||
? receiveCurrency
|
||||
: depositCurrency;
|
||||
final to = isFixedRateMode
|
||||
? depositCurrency
|
||||
: receiveCurrency;
|
||||
|
||||
provider.fetchLimits(
|
||||
from: from,
|
||||
to: to,
|
||||
isFixedRateMode: isFixedRateMode,
|
||||
).then((limits) {
|
||||
|
||||
/// if the entered amount doesn't exceed the limits of this provider
|
||||
if ((limits?.max ?? double.maxFinite) >= _enteredAmount
|
||||
&& (limits?.min ?? 0) <= _enteredAmount) {
|
||||
/// add this provider as its valid for this trade
|
||||
/// subtract from maxFinite so the provider
|
||||
/// with the largest amount would be sorted ascending
|
||||
currentTradeAvailableProviders[double.maxFinite - amount] = provider;
|
||||
}
|
||||
return amount;
|
||||
}).then((amount) => receiveAmount =
|
||||
receiveAmount = _cryptoNumberFormat
|
||||
.format(amount)
|
||||
.toString()
|
||||
.replaceAll(RegExp('\\,'), ''));
|
||||
});
|
||||
}
|
||||
if (_sortedAvailableProviders.isNotEmpty) {
|
||||
_bestRate = _sortedAvailableProviders.keys.first;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
Future loadLimits() async {
|
||||
Future<void> loadLimits() async {
|
||||
if (selectedProviders.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
limitsState = LimitsIsLoading();
|
||||
|
||||
try {
|
||||
final from = isFixedRateMode
|
||||
final from = isFixedRateMode
|
||||
? receiveCurrency
|
||||
: depositCurrency;
|
||||
final to = isFixedRateMode
|
||||
final to = isFixedRateMode
|
||||
? depositCurrency
|
||||
: receiveCurrency;
|
||||
|
||||
limits = await selectedProviders.first.fetchLimits(
|
||||
from: from,
|
||||
to: to,
|
||||
isFixedRateMode: isFixedRateMode);
|
||||
double lowestMin = double.maxFinite;
|
||||
double? highestMax = 0.0;
|
||||
|
||||
/// if the first provider limits is bounded then check with other providers
|
||||
/// for the highest maximum limit
|
||||
if (limits.max != null) {
|
||||
for (int i = 1;i < selectedProviders.length;i++) {
|
||||
final Limits tempLimits = await selectedProviders[i].fetchLimits(
|
||||
from: from,
|
||||
to: to,
|
||||
isFixedRateMode: isFixedRateMode);
|
||||
|
||||
/// set the limits with the maximum provider limit
|
||||
/// if there is a provider with null max then it's the maximum limit
|
||||
if ((tempLimits.max ?? double.maxFinite) > limits.max!) {
|
||||
limits = tempLimits;
|
||||
}
|
||||
}
|
||||
for (var provider in selectedProviders) {
|
||||
/// if this provider is not valid for the current pair, skip it
|
||||
if (!providersForCurrentPair().contains(provider)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
final tempLimits = await provider.fetchLimits(
|
||||
from: from,
|
||||
to: to,
|
||||
isFixedRateMode: isFixedRateMode);
|
||||
|
||||
if (tempLimits.min != null && tempLimits.min! < lowestMin) {
|
||||
lowestMin = tempLimits.min!;
|
||||
}
|
||||
if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) {
|
||||
highestMax = tempLimits.max;
|
||||
}
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (lowestMin < double.maxFinite) {
|
||||
limits = Limits(min: lowestMin, max: highestMax);
|
||||
|
||||
limitsState = LimitsLoadedSuccessfully(limits: limits);
|
||||
} catch (e) {
|
||||
limitsState = LimitsLoadedFailure(error: e.toString());
|
||||
} else {
|
||||
limitsState = LimitsLoadedFailure(error: 'Limits loading failed');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,7 +356,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
TradeRequest? request;
|
||||
String amount = '';
|
||||
|
||||
for (var provider in currentTradeAvailableProviders.values) {
|
||||
for (var provider in _sortedAvailableProviders.values) {
|
||||
if (!(await provider.checkIsAvailable())) {
|
||||
continue;
|
||||
}
|
||||
|
@ -383,7 +365,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
request = SideShiftRequest(
|
||||
depositMethod: depositCurrency,
|
||||
settleMethod: receiveCurrency,
|
||||
depositAmount: depositAmount?.replaceAll(',', '.') ?? '',
|
||||
depositAmount: depositAmount.replaceAll(',', '.'),
|
||||
settleAddress: receiveAddress,
|
||||
refundAddress: depositAddress,
|
||||
);
|
||||
|
@ -394,7 +376,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
request = SimpleSwapRequest(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
amount: depositAmount?.replaceAll(',', '.') ?? '',
|
||||
amount: depositAmount.replaceAll(',', '.'),
|
||||
address: receiveAddress,
|
||||
refundAddress: depositAddress,
|
||||
);
|
||||
|
@ -405,8 +387,8 @@ abstract class ExchangeViewModelBase with Store {
|
|||
request = XMRTOTradeRequest(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
amount: depositAmount?.replaceAll(',', '.') ?? '',
|
||||
receiveAmount: receiveAmount?.replaceAll(',', '.') ?? '',
|
||||
amount: depositAmount.replaceAll(',', '.'),
|
||||
receiveAmount: receiveAmount.replaceAll(',', '.'),
|
||||
address: receiveAddress,
|
||||
refundAddress: depositAddress,
|
||||
isBTCRequest: isReceiveAmountEntered);
|
||||
|
@ -417,8 +399,8 @@ abstract class ExchangeViewModelBase with Store {
|
|||
request = ChangeNowRequest(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
fromAmount: depositAmount?.replaceAll(',', '.') ?? '',
|
||||
toAmount: receiveAmount?.replaceAll(',', '.') ?? '',
|
||||
fromAmount: depositAmount.replaceAll(',', '.'),
|
||||
toAmount: receiveAmount.replaceAll(',', '.'),
|
||||
refundAddress: depositAddress,
|
||||
address: receiveAddress,
|
||||
isReverse: isReverse);
|
||||
|
@ -429,7 +411,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
request = MorphTokenRequest(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
amount: depositAmount?.replaceAll(',', '.') ?? '',
|
||||
amount: depositAmount.replaceAll(',', '.'),
|
||||
refundAddress: depositAddress,
|
||||
address: receiveAddress);
|
||||
amount = depositAmount;
|
||||
|
@ -437,7 +419,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
|
||||
amount = amount.replaceAll(',', '.');
|
||||
|
||||
if (limitsState is LimitsLoadedSuccessfully && amount != null) {
|
||||
if (limitsState is LimitsLoadedSuccessfully) {
|
||||
if (double.parse(amount) < limits.min!) {
|
||||
continue;
|
||||
} else if (limits.max != null && double.parse(amount) > limits.max!) {
|
||||
|
@ -527,7 +509,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
final providers = providerList
|
||||
.where((provider) => provider.pairList
|
||||
.where((pair) =>
|
||||
pair.from == (from ?? depositCurrency) && pair.to == (to ?? receiveCurrency))
|
||||
pair.from == from && pair.to == to)
|
||||
.isNotEmpty)
|
||||
.toList();
|
||||
|
||||
|
@ -537,6 +519,10 @@ abstract class ExchangeViewModelBase with Store {
|
|||
void _onPairChange() {
|
||||
depositAmount = '';
|
||||
receiveAmount = '';
|
||||
loadLimits();
|
||||
_setAvailableProviders();
|
||||
_bestRate = 0;
|
||||
_calculateBestRate();
|
||||
}
|
||||
|
||||
void _initialPairBasedOnWallet() {
|
||||
|
@ -579,11 +565,15 @@ abstract class ExchangeViewModelBase with Store {
|
|||
@action
|
||||
void addExchangeProvider(ExchangeProvider provider) {
|
||||
selectedProviders.add(provider);
|
||||
if (providersForCurrentPair().contains(provider)) {
|
||||
_tradeAvailableProviders.add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void removeExchangeProvider(ExchangeProvider provider) {
|
||||
selectedProviders.remove(provider);
|
||||
_tradeAvailableProviders.remove(provider);
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -593,13 +583,14 @@ abstract class ExchangeViewModelBase with Store {
|
|||
isFixedRateMode = false;
|
||||
_defineIsReceiveAmountEditable();
|
||||
loadLimits();
|
||||
_bestRate = 0;
|
||||
_calculateBestRate();
|
||||
|
||||
final Map<String, dynamic> exchangeProvidersSelection = json
|
||||
.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") as Map<String, dynamic>;
|
||||
|
||||
exchangeProvidersSelection.updateAll((key, dynamic value) => false);
|
||||
for (var provider in selectedProviders) {
|
||||
exchangeProvidersSelection[provider.title] = true;
|
||||
for (var provider in providerList) {
|
||||
exchangeProvidersSelection[provider.title] = selectedProviders.contains(provider);
|
||||
}
|
||||
|
||||
sharedPreferences.setString(
|
||||
|
@ -612,4 +603,12 @@ abstract class ExchangeViewModelBase with Store {
|
|||
final providersForPair = providersForCurrentPair();
|
||||
return selectedProviders.any((element) => element.isAvailable && providersForPair.contains(element));
|
||||
}
|
||||
|
||||
void _setAvailableProviders() {
|
||||
_tradeAvailableProviders.clear();
|
||||
|
||||
_tradeAvailableProviders.addAll(
|
||||
selectedProviders
|
||||
.where((provider) => providersForCurrentPair().contains(provider)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ abstract class RestoreFromBackupViewModelBase with Store {
|
|||
} catch (e) {
|
||||
var msg = e.toString();
|
||||
|
||||
if (msg == 'Message authentication code (MAC) is invalid') {
|
||||
if (msg.toLowerCase().contains("message authentication code (mac)")) {
|
||||
msg = 'Incorrect backup password';
|
||||
}
|
||||
|
||||
|
|