diff --git a/assets/images/2.0x/morph_icon.png b/assets/images/2.0x/morph_icon.png new file mode 100644 index 000000000..a897a73b1 Binary files /dev/null and b/assets/images/2.0x/morph_icon.png differ diff --git a/assets/images/3.0x/morph_icon.png b/assets/images/3.0x/morph_icon.png new file mode 100644 index 000000000..db98d49da Binary files /dev/null and b/assets/images/3.0x/morph_icon.png differ diff --git a/assets/images/morph_icon.png b/assets/images/morph_icon.png new file mode 100644 index 000000000..e4b019b86 Binary files /dev/null and b/assets/images/morph_icon.png differ diff --git a/lib/generated/i18n.dart b/lib/generated/i18n.dart index 8167cc04a..751fb13af 100644 --- a/lib/generated/i18n.dart +++ b/lib/generated/i18n.dart @@ -272,6 +272,9 @@ class S implements WidgetsLocalizations { String change_language_to(String language) => "Change language to ${language}?"; String commit_transaction_amount_fee(String amount, String fee) => "Commit transaction\nAmount: ${amount}\nFee: ${fee}"; String copied_key_to_clipboard(String key) => "Copied ${key} to Clipboard"; + String error_text_limits_loading_failed(String provider) => "Trade for ${provider} is not created. Limits loading failed"; + String error_text_maximum_limit(String provider, String max, String currency) => "Trade for ${provider} is not created. Amount is more then maximum: ${max} ${currency}"; + String error_text_minimal_limit(String provider, String min, String currency) => "Trade for ${provider} is not created. Amount is less then minimal: ${min} ${currency}"; String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "By pressing confirm, you will be sending ${fetchingLabel} ${from} from your wallet called ${walletName} to the address shown above. Or you can send from your external wallet to the above address/QR code.\n\nPlease press confirm to continue or go back to change the amounts.\n\n"; String exchange_result_description(String fetchingLabel, String from) => "Please send ${fetchingLabel} ${from} to the address shown above.\n\n"; String failed_authentication(String state_error) => "Failed authentication. ${state_error}"; @@ -805,6 +808,8 @@ class $de extends S { @override String router_no_route(String name) => "Keine Route definiert für ${name}"; @override + String error_text_minimal_limit(String provider, String min, String currency) => "Handel für ${provider} wird nicht erstellt. Menge ist weniger als minimal: ${min} ${currency}"; + @override String trade_id_not_found(String tradeId, String title) => "Handel ${tradeId} von ${title} nicht gefunden."; @override String transaction_details_copied(String title) => "${title} in die Zwischenablage kopiert"; @@ -823,6 +828,8 @@ class $de extends S { @override String max_value(String value, String currency) => "Max: ${value} ${currency}"; @override + String error_text_maximum_limit(String provider, String max, String currency) => "Handel für ${provider} wird nicht erstellt. Menge ist mehr als maximal: ${max} ${currency}"; + @override String min_value(String value, String currency) => "Mindest: ${value} ${currency}"; @override String failed_authentication(String state_error) => "Authentifizierung fehlgeschlagen. ${state_error}"; @@ -831,6 +838,8 @@ class $de extends S { @override String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Durch Drücken von Bestätigen wird gesendet ${fetchingLabel} ${from} von Ihrer Brieftasche aus angerufen ${walletName} an die oben angegebene Adresse. Oder Sie können von Ihrem externen Portemonnaie an die oben angegebene Adresse / QR-Code senden.\n\nBitte bestätigen Sie, um fortzufahren, oder gehen Sie zurück, um die Beträge zu änderns.\n\n"; @override + String error_text_limits_loading_failed(String provider) => "Handel für ${provider} wird nicht erstellt. Das Laden der Limits ist fehlgeschlagen"; + @override String exchange_result_description(String fetchingLabel, String from) => "Bitte senden ${fetchingLabel} ${from} an die oben angegebene Adresse.\n\n'"; @override String commit_transaction_amount_fee(String amount, String fee) => "Transaktion festschreiben\nMenge: ${amount}\nGebühr: ${fee}"; @@ -1355,6 +1364,8 @@ class $hi extends S { @override String router_no_route(String name) => "के लिए कोई मार्ग निर्धारित नहीं है ${name}"; @override + String error_text_minimal_limit(String provider, String min, String currency) => "व्यापार ${provider} के लिए नहीं बनाया गया है। राशि कम है तो न्यूनतम: ${min} ${currency}"; + @override String trade_id_not_found(String tradeId, String title) => "व्यापार ${tradeId} of ${title} नहीं मिला."; @override String transaction_details_copied(String title) => "${title} क्लिपबोर्ड पर नकल"; @@ -1373,6 +1384,8 @@ class $hi extends S { @override String max_value(String value, String currency) => "मैक्स: ${value} ${currency}"; @override + String error_text_maximum_limit(String provider, String max, String currency) => "व्यापार ${provider} के लिए नहीं बनाया गया है। राशि अधिक है तो अधिकतम: ${max} ${currency}"; + @override String min_value(String value, String currency) => "मिन: ${value} ${currency}"; @override String failed_authentication(String state_error) => "प्रमाणीकरण विफल. ${state_error}"; @@ -1381,6 +1394,8 @@ class $hi extends S { @override String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "पुष्टि दबाकर, आप भेज रहे होंगे ${fetchingLabel} ${from} अपने बटुए से ${walletName} ऊपर दिखाए गए पते पर। या आप अपने बाहरी वॉलेट से उपरोक्त पते / क्यूआर कोड पर भेज सकते हैं।\n\nकृपया जारी रखने या राशि बदलने के लिए वापस जाने के लिए पुष्टि करें दबाएं.\n\n"; @override + String error_text_limits_loading_failed(String provider) => "व्यापार ${provider} के लिए नहीं बनाया गया है। लोडिंग की सीमाएं विफल रहीं"; + @override String exchange_result_description(String fetchingLabel, String from) => "कृपया भेजें ${fetchingLabel} ${from} ऊपर दिखाए गए पते पर\n\n'"; @override String commit_transaction_amount_fee(String amount, String fee) => "लेन-देन करें\nरकम: ${amount}\nशुल्क: ${fee}"; @@ -1905,6 +1920,8 @@ class $ru extends S { @override String router_no_route(String name) => "Экран не найден ${name}"; @override + String error_text_minimal_limit(String provider, String min, String currency) => "Сделка для ${provider} не создана. Сумма меньше минимальной: ${min} ${currency}"; + @override String trade_id_not_found(String tradeId, String title) => "Сделка ${tradeId} ${title} не найдена."; @override String transaction_details_copied(String title) => "${title} скопировано в буфер обмена"; @@ -1923,6 +1940,8 @@ class $ru extends S { @override String max_value(String value, String currency) => "Макс: ${value} ${currency}"; @override + String error_text_maximum_limit(String provider, String max, String currency) => "Сделка для ${provider} не создана. Сумма больше максимальной: ${max} ${currency}"; + @override String min_value(String value, String currency) => "Мин: ${value} ${currency}"; @override String failed_authentication(String state_error) => "Ошибка аутентификации. ${state_error}"; @@ -1931,6 +1950,8 @@ class $ru extends S { @override String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Нажимая подтвердить, Вы отправите ${fetchingLabel} ${from} с Вашего кошелька ${walletName} на адрес указанный выше. Или Вы можете отправить со своего внешнего кошелька на вышеуказанный адрес / QR-код.\n\nПожалуйста, нажмите подтвердить для продолжения или вернитесь назад для изменения суммы.\n\n"; @override + String error_text_limits_loading_failed(String provider) => "Сделка для ${provider} не создана. Ошибка загрузки лимитов"; + @override String exchange_result_description(String fetchingLabel, String from) => "Пожалуйста отправьте ${fetchingLabel} ${from} на адрес, указанный выше.\n\n'"; @override String commit_transaction_amount_fee(String amount, String fee) => "Подтвердить транзакцию \nСумма: ${amount}\nСбор: ${fee}"; @@ -2455,6 +2476,8 @@ class $ko extends S { @override String router_no_route(String name) => "에 정의 된 경로가 없습니다 ${name}"; @override + String error_text_minimal_limit(String provider, String min, String currency) => "거래 ${provider} 가 생성되지 않습니다. 금액이 최소보다 적습니다. ${min} ${currency}"; + @override String trade_id_not_found(String tradeId, String title) => "무역 ${tradeId} 의 ${title} 찾을 수 없습니다."; @override String transaction_details_copied(String title) => "${title} 클립 보드에 복사"; @@ -2473,6 +2496,8 @@ class $ko extends S { @override String max_value(String value, String currency) => "맥스: ${value} ${currency}"; @override + String error_text_maximum_limit(String provider, String max, String currency) => "거래 ${provider} 가 생성되지 않습니다. 금액이 최대 값보다 많습니다. ${max} ${currency}"; + @override String min_value(String value, String currency) => "최소: ${value} ${currency}"; @override String failed_authentication(String state_error) => "인증 실패. ${state_error}"; @@ -2481,6 +2506,8 @@ class $ko extends S { @override String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "확인을 누르면 전송됩니다 ${fetchingLabel} ${from} 지갑에서 ${walletName} 위에 표시된 주소로. 또는 외부 지갑에서 위의 주소 / QR 코드로 보낼 수 있습니다.\n\n확인을 눌러 계속하거나 금액을 변경하려면 돌아가십시오.\n\n"; @override + String error_text_limits_loading_failed(String provider) => "거래 ${provider} 가 생성되지 않습니다. 로딩 실패"; + @override String exchange_result_description(String fetchingLabel, String from) => "보내주세요 ${fetchingLabel} ${from} 위에 표시된 주소로.\n\n'"; @override String commit_transaction_amount_fee(String amount, String fee) => "커밋 거래\n양: ${amount}\n보수: ${fee}"; @@ -3005,6 +3032,8 @@ class $pt extends S { @override String router_no_route(String name) => "Nenhuma rota definida para ${name}"; @override + String error_text_minimal_limit(String provider, String min, String currency) => "A troca por ${provider} não é criada. O valor é menor que o mínimo: ${min} ${currency}"; + @override String trade_id_not_found(String tradeId, String title) => "A troca ${tradeId} de ${title} não foi encontrada."; @override String transaction_details_copied(String title) => "${title} copiados para a área de transferência"; @@ -3023,6 +3052,8 @@ class $pt extends S { @override String max_value(String value, String currency) => "Máx: ${value} ${currency}"; @override + String error_text_maximum_limit(String provider, String max, String currency) => "A troca por ${provider} não é criada. O valor é superior ao máximo: ${max} ${currency}"; + @override String min_value(String value, String currency) => "Mín: ${value} ${currency}"; @override String failed_authentication(String state_error) => "Falha na autenticação. ${state_error}"; @@ -3031,6 +3062,8 @@ class $pt extends S { @override String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Ao confirmar, você enviará ${fetchingLabel} ${from} da sua carteira ${walletName} para o endereço exibido acima. Você também pode enviar com uma carteira externa para o endereço/código QR acima.\n\nPressione Confirmar para continuar ou volte para alterar os valores.\n\n"; @override + String error_text_limits_loading_failed(String provider) => "A troca por ${provider} não é criada. Falha no carregamento dos limites"; + @override String exchange_result_description(String fetchingLabel, String from) => "Por favor, envie ${fetchingLabel} ${from} para o endereço mostrado acima.\n\n'"; @override String commit_transaction_amount_fee(String amount, String fee) => "Confirmar transação\nQuantia: ${amount}\nTaxa: ${fee}"; @@ -3555,6 +3588,8 @@ class $ja extends S { @override String router_no_route(String name) => "ルートが定義されていません ${name}"; @override + String error_text_minimal_limit(String provider, String min, String currency) => "${provider} の取引は作成されません。 金額は最小額より少ない: ${min} ${currency}"; + @override String trade_id_not_found(String tradeId, String title) => "トレード ${tradeId} of ${title} 見つかりません"; @override String transaction_details_copied(String title) => "${title} クリップボードにコピーしました"; @@ -3573,6 +3608,8 @@ class $ja extends S { @override String max_value(String value, String currency) => "マックス: ${value} ${currency}"; @override + String error_text_maximum_limit(String provider, String max, String currency) => "${provider} の取引は作成されません。 金額は最大値を超えています: ${max} ${currency}"; + @override String min_value(String value, String currency) => "分: ${value} ${currency}"; @override String failed_authentication(String state_error) => "認証失敗. ${state_error}"; @@ -3581,6 +3618,8 @@ class $ja extends S { @override String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "確認を押すと、送信されます ${fetchingLabel} ${from} と呼ばれるあなたの財布から ${walletName} 上記のアドレスへ. または、外部ウォレットから上記のアドレス/ QRコードに送信できます.\n\n確認を押して続行するか、戻って金額を変更してください.\n\n"; @override + String error_text_limits_loading_failed(String provider) => "${provider} の取引は作成されません。 制限の読み込みに失敗しました"; + @override String exchange_result_description(String fetchingLabel, String from) => "送信してください ${fetchingLabel} ${from} 上記のアドレスへ.\n\n'"; @override String commit_transaction_amount_fee(String amount, String fee) => "トランザクションをコミット\n量: ${amount}\n費用: ${fee}"; @@ -4109,6 +4148,8 @@ class $pl extends S { @override String router_no_route(String name) => "Brak zdefiniowanej trasy dla ${name}"; @override + String error_text_minimal_limit(String provider, String min, String currency) => "Wymiana dla ${provider} nie została utworzona. Kwota jest mniejsza niż minimalna: ${min} ${currency}"; + @override String trade_id_not_found(String tradeId, String title) => "Handel ${tradeId} of ${title} nie znaleziono."; @override String transaction_details_copied(String title) => "${title} skopiowane do schowka"; @@ -4127,6 +4168,8 @@ class $pl extends S { @override String max_value(String value, String currency) => "Max: ${value} ${currency}"; @override + String error_text_maximum_limit(String provider, String max, String currency) => "Wymiana dla ${provider} nie została utworzona. Kwota jest większa niż maksymalna: ${max} ${currency}"; + @override String min_value(String value, String currency) => "Min: ${value} ${currency}"; @override String failed_authentication(String state_error) => "Nieudane uwierzytelnienie. ${state_error}"; @@ -4135,6 +4178,8 @@ class $pl extends S { @override String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Naciskając Potwierdź, wyślesz ${fetchingLabel} ${from} z twojego portfela ${walletName} z twojego portfela. Lub możesz wysłać z zewnętrznego portfela na powyższy adres / kod QR.\n\nNaciśnij Potwierdź, aby kontynuować lub wróć, aby zmienić kwoty.\n\n"; @override + String error_text_limits_loading_failed(String provider) => "Wymiana dla ${provider} nie została utworzona. Ładowanie limitów nie powiodło się"; + @override String exchange_result_description(String fetchingLabel, String from) => "Proszę wyślij ${fetchingLabel} ${from} na adres podany powyżej.\n\n'"; @override String commit_transaction_amount_fee(String amount, String fee) => "Zatwierdź transakcję\nIlość: ${amount}\nOpłata: ${fee}"; @@ -4659,6 +4704,8 @@ class $es extends S { @override String router_no_route(String name) => "No hay ruta definida para ${name}"; @override + String error_text_minimal_limit(String provider, String min, String currency) => "El comercio por ${provider} no se crea. La cantidad es menos que mínima: ${min} ${currency}"; + @override String trade_id_not_found(String tradeId, String title) => "Comercio ${tradeId} de ${title} no encontrado."; @override String transaction_details_copied(String title) => "${title} Copiado al portapapeles"; @@ -4677,6 +4724,8 @@ class $es extends S { @override String max_value(String value, String currency) => "Max: ${value} ${currency}"; @override + String error_text_maximum_limit(String provider, String max, String currency) => "El comercio por ${provider} no se crea. La cantidad es más que el máximo: ${max} ${currency}"; + @override String min_value(String value, String currency) => "Min: ${value} ${currency}"; @override String failed_authentication(String state_error) => "Autenticación fallida. ${state_error}"; @@ -4685,6 +4734,8 @@ class $es extends S { @override String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Al presionar confirmar, enviará ${fetchingLabel} ${from} desde su billetera llamada ${walletName} a la dirección que se muestra arriba. O puede enviar desde su billetera externa a la dirección / código QR anterior.\n\nPresione confirmar para continuar o regrese para cambiar los montos.\n\n"; @override + String error_text_limits_loading_failed(String provider) => "El comercio por ${provider} no se crea. Límites de carga fallidos"; + @override String exchange_result_description(String fetchingLabel, String from) => "Envíe ${fetchingLabel} ${from} a la dirección que se muestra arriba.\n\n'"; @override String commit_transaction_amount_fee(String amount, String fee) => "Confirmar transacción\nCantidad: ${amount}\nCuota: ${fee}"; @@ -5209,6 +5260,8 @@ class $nl extends S { @override String router_no_route(String name) => "Geen route gedefinieerd voor ${name}"; @override + String error_text_minimal_limit(String provider, String min, String currency) => "Ruil voor ${provider} is niet gemaakt. Bedrag is minder dan minimaal: ${min} ${currency}"; + @override String trade_id_not_found(String tradeId, String title) => "Handel ${tradeId} van ${title} niet gevonden."; @override String transaction_details_copied(String title) => "${title} gekopieerd naar het klembord"; @@ -5227,6 +5280,8 @@ class $nl extends S { @override String max_value(String value, String currency) => "Max: ${value} ${currency}"; @override + String error_text_maximum_limit(String provider, String max, String currency) => "Ruil voor ${provider} is niet gemaakt. Bedrag is meer dan maximaal: ${max} ${currency}"; + @override String min_value(String value, String currency) => "Min: ${value} ${currency}"; @override String failed_authentication(String state_error) => "Mislukte authenticatie. ${state_error}"; @@ -5235,6 +5290,8 @@ class $nl extends S { @override String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Door op bevestigen te drukken, wordt u verzonden ${fetchingLabel} ${from} uit je portemonnee genoemd ${walletName} naar bovenstaand adres. Of u kunt uw externe portemonnee naar bovenstaand adres / QR-code sturen.\n\nDruk op bevestigen om door te gaan of terug te gaan om de bedragen te wijzigen.\n\n"; @override + String error_text_limits_loading_failed(String provider) => "Ruil voor ${provider} is niet gemaakt. Beperkingen laden mislukt"; + @override String exchange_result_description(String fetchingLabel, String from) => "Zend alstublieft ${fetchingLabel} ${from} naar bovenstaand adres.\n\n'"; @override String commit_transaction_amount_fee(String amount, String fee) => "Verricht transactie\nBedrag: ${amount}\nhonorarium: ${fee}"; @@ -5759,6 +5816,8 @@ class $zh extends S { @override String router_no_route(String name) => "未定义路线 ${name}"; @override + String error_text_minimal_limit(String provider, String min, String currency) => "未創建 ${provider} 交易。 金額少於最小值:${min} ${currency}"; + @override String trade_id_not_found(String tradeId, String title) => "贸易方式 ${tradeId} 的 ${title} 未找到."; @override String transaction_details_copied(String title) => "${title} 复制到剪贴板"; @@ -5777,6 +5836,8 @@ class $zh extends S { @override String max_value(String value, String currency) => "最高: ${value} ${currency}"; @override + String error_text_maximum_limit(String provider, String max, String currency) => "未創建 ${provider} 交易。 金額大於最大值:${max} ${currency}"; + @override String min_value(String value, String currency) => "敏: ${value} ${currency}"; @override String failed_authentication(String state_error) => "身份验证失败. ${state_error}"; @@ -5785,6 +5846,8 @@ class $zh extends S { @override String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "点击确认 您将发送 ${fetchingLabel} ${from} 从你的钱包里 ${walletName} 到上面显示的地址. 或者,您也可以从外部钱包发送上述地址/ QR码。\n\n请按确认继续或返回以更改金额\n\n"; @override + String error_text_limits_loading_failed(String provider) => "未創建 ${provider} 交易。 限制加載失敗"; + @override String exchange_result_description(String fetchingLabel, String from) => "请发送 ${fetchingLabel} ${from} 到上面显示的地址.\n\n'"; @override String commit_transaction_amount_fee(String amount, String fee) => "提交交易\n量: ${amount}\nFee: ${fee}"; diff --git a/lib/router.dart b/lib/router.dart index c10ebea12..bca3bebce 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -16,6 +16,7 @@ import 'package:cake_wallet/src/domain/services/wallet_service.dart'; import 'package:cake_wallet/src/domain/common/crypto_currency.dart'; import 'package:cake_wallet/src/domain/exchange/changenow/changenow_exchange_provider.dart'; import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart'; +import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_exchange_provider.dart'; import 'package:cake_wallet/src/domain/common/node.dart'; import 'package:cake_wallet/src/domain/monero/transaction_description.dart'; import 'package:cake_wallet/src/domain/exchange/trade.dart'; @@ -394,7 +395,8 @@ class Router { ProxyProvider( update: (_, settingsStore, __) => ExchangeTradeStore( trade: settings.arguments as Trade, - walletStore: walletStore), + walletStore: walletStore, + trades: trades), ), ProxyProvider( update: (_, settingsStore, __) => SendStore( @@ -416,7 +418,9 @@ class Router { return MultiProvider(providers: [ ProxyProvider( update: (_, settingsStore, __) => ExchangeTradeStore( - trade: settings.arguments as Trade, walletStore: walletStore), + trade: settings.arguments as Trade, + walletStore: walletStore, + trades: trades), ) ], child: TradeDetailsPage()); }); @@ -453,7 +457,8 @@ class Router { trades: trades, providerList: [ xmrtoprovider, - ChangeNowExchangeProvider() + ChangeNowExchangeProvider(), + MorphTokenExchangeProvider(trades: trades) ], walletStore: walletStore); }), diff --git a/lib/src/domain/bitcoin/bitcoin_amount_format.dart b/lib/src/domain/bitcoin/bitcoin_amount_format.dart new file mode 100644 index 000000000..c816dedc2 --- /dev/null +++ b/lib/src/domain/bitcoin/bitcoin_amount_format.dart @@ -0,0 +1,6 @@ +import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart'; + +const bitcoinAmountDivider = 100000000; + +double bitcoinAmountToDouble({int amount}) => + cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider); \ No newline at end of file diff --git a/lib/src/domain/bitcoin_cash/bitcoin_cash_amount_format.dart b/lib/src/domain/bitcoin_cash/bitcoin_cash_amount_format.dart new file mode 100644 index 000000000..bbb0192c8 --- /dev/null +++ b/lib/src/domain/bitcoin_cash/bitcoin_cash_amount_format.dart @@ -0,0 +1,6 @@ +import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart'; + +const bitcoinCashAmountDivider = 100000000; + +double bitcoinCashAmountToDouble({int amount}) => + cryptoAmountToDouble(amount: amount, divider: bitcoinCashAmountDivider); \ No newline at end of file diff --git a/lib/src/domain/common/crypto_amount_format.dart b/lib/src/domain/common/crypto_amount_format.dart new file mode 100644 index 000000000..649ac45f5 --- /dev/null +++ b/lib/src/domain/common/crypto_amount_format.dart @@ -0,0 +1 @@ +double cryptoAmountToDouble({num amount, num divider}) => amount / divider; \ No newline at end of file diff --git a/lib/src/domain/common/format_amount.dart b/lib/src/domain/common/format_amount.dart new file mode 100644 index 000000000..88e8c1651 --- /dev/null +++ b/lib/src/domain/common/format_amount.dart @@ -0,0 +1,8 @@ +String formatAmount(String amount) { + if (!amount.contains('.')) { + return amount + '.00'; + } else if (amount.endsWith('.')) { + return amount + '00'; + } + return amount; +} \ No newline at end of file diff --git a/lib/src/domain/common/transaction_info.dart b/lib/src/domain/common/transaction_info.dart index 08fcd2372..e2daaec7a 100644 --- a/lib/src/domain/common/transaction_info.dart +++ b/lib/src/domain/common/transaction_info.dart @@ -2,6 +2,7 @@ import 'package:cake_wallet/src/domain/monero/monero_amount_format.dart'; import 'package:cw_monero/structs/transaction_info_row.dart'; import 'package:cake_wallet/src/domain/common/parseBoolFromString.dart'; import 'package:cake_wallet/src/domain/common/transaction_direction.dart'; +import 'package:cake_wallet/src/domain/common/format_amount.dart'; class TransactionInfo { TransactionInfo(this.id, this.height, this.direction, this.date, @@ -40,9 +41,9 @@ class TransactionInfo { String _fiatAmount; - String amountFormatted() => '${moneroAmountToString(amount: amount)} XMR'; + String amountFormatted() => '${formatAmount(moneroAmountToString(amount: amount))} XMR'; String fiatAmount() => _fiatAmount ?? ''; - void changeFiatAmount(String amount) => _fiatAmount = amount; + void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount); } diff --git a/lib/src/domain/dash/dash_amount_format.dart b/lib/src/domain/dash/dash_amount_format.dart new file mode 100644 index 000000000..3f1632cd6 --- /dev/null +++ b/lib/src/domain/dash/dash_amount_format.dart @@ -0,0 +1,6 @@ +import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart'; + +const dashAmountDivider = 100000000; + +double dashAmountToDouble({int amount}) => + cryptoAmountToDouble(amount: amount, divider: dashAmountDivider); \ No newline at end of file diff --git a/lib/src/domain/ethereum/ethereum_amount_format.dart b/lib/src/domain/ethereum/ethereum_amount_format.dart new file mode 100644 index 000000000..644153a22 --- /dev/null +++ b/lib/src/domain/ethereum/ethereum_amount_format.dart @@ -0,0 +1,6 @@ +import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart'; + +const ethereumAmountDivider = 1000000000000000000; + +double ethereumAmountToDouble({num amount}) => + cryptoAmountToDouble(amount: amount, divider: ethereumAmountDivider); \ No newline at end of file diff --git a/lib/src/domain/exchange/exchange_provider_description.dart b/lib/src/domain/exchange/exchange_provider_description.dart index 0ae388848..67a2565d8 100644 --- a/lib/src/domain/exchange/exchange_provider_description.dart +++ b/lib/src/domain/exchange/exchange_provider_description.dart @@ -8,6 +8,8 @@ class ExchangeProviderDescription extends EnumerableItem static const xmrto = ExchangeProviderDescription(title: 'XMR.TO', raw: 0); static const changeNow = ExchangeProviderDescription(title: 'ChangeNOW', raw: 1); + static const morphToken = + ExchangeProviderDescription(title: 'MorphToken', raw: 2); static ExchangeProviderDescription deserialize({int raw}) { switch (raw) { @@ -15,6 +17,8 @@ class ExchangeProviderDescription extends EnumerableItem return xmrto; case 1: return changeNow; + case 2: + return morphToken; default: return null; } diff --git a/lib/src/domain/exchange/morphtoken/morphtoken_exchange_provider.dart b/lib/src/domain/exchange/morphtoken/morphtoken_exchange_provider.dart new file mode 100644 index 000000000..b150ebce1 --- /dev/null +++ b/lib/src/domain/exchange/morphtoken/morphtoken_exchange_provider.dart @@ -0,0 +1,241 @@ +import 'dart:convert'; +import 'package:hive/hive.dart'; +import 'package:cake_wallet/src/domain/exchange/trade_not_found_exeption.dart'; +import 'package:flutter/foundation.dart'; +import 'package:http/http.dart'; +import 'package:cake_wallet/src/domain/common/crypto_currency.dart'; +import 'package:cake_wallet/src/domain/exchange/exchange_pair.dart'; +import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart'; +import 'package:cake_wallet/src/domain/exchange/limits.dart'; +import 'package:cake_wallet/src/domain/exchange/trade.dart'; +import 'package:cake_wallet/src/domain/exchange/trade_request.dart'; +import 'package:cake_wallet/src/domain/exchange/trade_state.dart'; +import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_request.dart'; +import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart'; +import 'package:cake_wallet/src/domain/exchange/trade_not_created_exeption.dart'; +import 'package:cake_wallet/src/domain/monero/monero_amount_format.dart'; +import 'package:cake_wallet/src/domain/bitcoin/bitcoin_amount_format.dart'; +import 'package:cake_wallet/src/domain/bitcoin_cash/bitcoin_cash_amount_format.dart'; +import 'package:cake_wallet/src/domain/dash/dash_amount_format.dart'; +import 'package:cake_wallet/src/domain/ethereum/ethereum_amount_format.dart'; +import 'package:cake_wallet/src/domain/litecoin/litecoin_amount_format.dart'; + +class MorphTokenExchangeProvider extends ExchangeProvider { + MorphTokenExchangeProvider({@required this.trades}) + : super( + pairList: [ + ExchangePair(from: CryptoCurrency.xmr, to: CryptoCurrency.eth), + ExchangePair(from: CryptoCurrency.xmr, to: CryptoCurrency.bch), + ExchangePair(from: CryptoCurrency.xmr, to: CryptoCurrency.ltc), + ExchangePair(from: CryptoCurrency.xmr, to: CryptoCurrency.dash), + + ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.btc), + ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.eth), + ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.bch), + ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.ltc), + ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.xmr), + + ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.btc), + ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.eth), + ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.bch), + ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.dash), + ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.xmr), + + ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.btc), + ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.eth), + ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.ltc), + ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.dash), + ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.xmr), + + ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.btc), + ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.bch), + ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.ltc), + ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.dash), + ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.xmr), + + ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.eth), + ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.bch), + ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.ltc), + ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.dash), + ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.xmr) + ]); + + Box trades; + + static const apiUri = 'https://api.morphtoken.com'; + static const _morphURISuffix = '/morph'; + static const _limitsURISuffix = '/limits'; + static const _ratesURISuffix = '/rates'; + static const weight = 10000; + + @override + String get title => 'MorphToken'; + + @override + ExchangeProviderDescription get description => + ExchangeProviderDescription.morphToken; + + @override + Future fetchLimits({CryptoCurrency from, CryptoCurrency to}) async { + final url = apiUri + _limitsURISuffix; + final headers = {'Content-type': 'application/json'}; + final body = + json.encode({ + "input": { + "asset": from.toString() + }, + "output": [{ + "asset": to.toString(), + "weight": weight + }]}); + final response = + await post(url, headers: headers, body: body); + final responseJSON = json.decode(response.body) as Map; + + final min = responseJSON['input']['limits']['min'] as int; + int max; + double ethMax; + + if (from == CryptoCurrency.eth) { + ethMax = responseJSON['input']['limits']['max'] as double; + } else { + max = responseJSON['input']['limits']['max'] as int; + } + + double minFormatted; + double maxFormatted; + + switch (from) { + case CryptoCurrency.xmr: + minFormatted = moneroAmountToDouble(amount: min); + maxFormatted = moneroAmountToDouble(amount: max); + break; + case CryptoCurrency.btc: + minFormatted = bitcoinAmountToDouble(amount: min); + maxFormatted = bitcoinAmountToDouble(amount: max); + break; + case CryptoCurrency.bch: + minFormatted = bitcoinCashAmountToDouble(amount: min); + maxFormatted = bitcoinCashAmountToDouble(amount: max); + break; + case CryptoCurrency.dash: + minFormatted = dashAmountToDouble(amount: min); + maxFormatted = dashAmountToDouble(amount: max); + break; + case CryptoCurrency.eth: + minFormatted = ethereumAmountToDouble(amount: min); + maxFormatted = ethereumAmountToDouble(amount: ethMax); + break; + case CryptoCurrency.ltc: + minFormatted = litecoinAmountToDouble(amount: min); + maxFormatted = litecoinAmountToDouble(amount: max); + break; + } + + return Limits(min: minFormatted, max: maxFormatted); + } + + @override + Future createTrade({TradeRequest request}) async { + const url = apiUri + _morphURISuffix; + final _request = request as MorphTokenRequest; + final body = { + "input": { + "asset": _request.from.toString(), + "refund": _request.refundAddress + }, + "output": [{ + "asset": _request.to.toString(), + "weight": weight, + "address": _request.address + }], + "tag": "cakewallet" + }; + + final response = await post(url, + headers: {'Content-Type': 'application/json'}, body: json.encode(body)); + + if (response.statusCode != 200) { + if (response.statusCode == 400) { + final responseJSON = json.decode(response.body) as Map; + final error = responseJSON['description'] as String; + + throw TradeNotCreatedException(description, description: error); + } + + throw TradeNotCreatedException(description); + } + + final responseJSON = json.decode(response.body) as Map; + final id = responseJSON['id'] as String; + + return Trade( + id: id, + provider: description, + from: _request.from, + to: _request.to, + state: TradeState.created, + amount: _request.amount, + createdAt: DateTime.now()); + } + + @override + Future findTradeById({@required String id}) async { + final url = apiUri + _morphURISuffix + '/' + id; + final response = await get(url); + + if (response.statusCode != 200) { + if (response.statusCode == 400) { + final responseJSON = json.decode(response.body) as Map; + final error = responseJSON['description'] as String; + + throw TradeNotFoundException(id, + provider: description, description: error); + } + + throw TradeNotFoundException(id, provider: description); + } + + final responseJSON = json.decode(response.body) as Map; + final fromCurrency = responseJSON['input']['asset'] as String; + final from = CryptoCurrency.fromString(fromCurrency.toLowerCase()); + final toCurrency = responseJSON['output'][0]['asset'] as String; + final to = CryptoCurrency.fromString(toCurrency.toLowerCase()); + final inputAddress = responseJSON['input']['deposit_address'] as String; + final status = responseJSON['state'] as String; + final state = TradeState.deserialize(raw: status.toLowerCase()); + + String amount = ""; + for (final trade in trades.values) { + if (trade.id == id) { + amount = trade.amount; + break; + } + } + + return Trade( + id: id, + from: from, + to: to, + provider: description, + inputAddress: inputAddress, + amount: amount, + state: state); + } + + @override + Future calculateAmount( + {CryptoCurrency from, CryptoCurrency to, double amount}) async { + final url = apiUri + _ratesURISuffix; + final response = await get(url); + final responseJSON = json.decode(response.body) as Map; + final rate = responseJSON['data'][from.toString()][to.toString()] as String; + + try { + final estimatedAmount = double.parse(rate) * amount; + return estimatedAmount; + } catch(e) { + return 0.0; + } + } +} diff --git a/lib/src/domain/exchange/morphtoken/morphtoken_request.dart b/lib/src/domain/exchange/morphtoken/morphtoken_request.dart new file mode 100644 index 000000000..f034372d9 --- /dev/null +++ b/lib/src/domain/exchange/morphtoken/morphtoken_request.dart @@ -0,0 +1,18 @@ +import 'package:flutter/foundation.dart'; +import 'package:cake_wallet/src/domain/common/crypto_currency.dart'; +import 'package:cake_wallet/src/domain/exchange/trade_request.dart'; + +class MorphTokenRequest extends TradeRequest { + MorphTokenRequest( + {@required this.from, + @required this.to, + @required this.address, + @required this.amount, + @required this.refundAddress}); + + CryptoCurrency from; + CryptoCurrency to; + String address; + String amount; + String refundAddress; +} diff --git a/lib/src/domain/exchange/trade.dart b/lib/src/domain/exchange/trade.dart index 3554d340a..8aa9f0472 100644 --- a/lib/src/domain/exchange/trade.dart +++ b/lib/src/domain/exchange/trade.dart @@ -2,6 +2,7 @@ import 'package:hive/hive.dart'; import 'package:cake_wallet/src/domain/common/crypto_currency.dart'; import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/src/domain/exchange/trade_state.dart'; +import 'package:cake_wallet/src/domain/common/format_amount.dart'; part 'trade.g.dart'; @@ -101,4 +102,6 @@ class Trade extends HiveObject { 'wallet_id': walletId }; } + + String amountFormatted() => formatAmount(amount); } diff --git a/lib/src/domain/exchange/trade_state.dart b/lib/src/domain/exchange/trade_state.dart index 2487ae89c..0a94cf4c0 100644 --- a/lib/src/domain/exchange/trade_state.dart +++ b/lib/src/domain/exchange/trade_state.dart @@ -26,6 +26,7 @@ class TradeState extends EnumerableItem with Serializable { static const created = TradeState(raw: 'created', title: 'Created'); static const finished = TradeState(raw: 'finished', title: 'Finished'); static const waiting = TradeState(raw: 'waiting', title: 'Waiting'); + static const processing = TradeState(raw: 'processing', title: 'Processing'); static TradeState deserialize({String raw}) { switch (raw) { @@ -59,6 +60,8 @@ class TradeState extends EnumerableItem with Serializable { return finished; case 'waiting': return waiting; + case 'processing': + return processing; default: return null; } diff --git a/lib/src/domain/litecoin/litecoin_amount_format.dart b/lib/src/domain/litecoin/litecoin_amount_format.dart new file mode 100644 index 000000000..f030f5e34 --- /dev/null +++ b/lib/src/domain/litecoin/litecoin_amount_format.dart @@ -0,0 +1,6 @@ +import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart'; + +const litecoinAmountDivider = 100000000; + +double litecoinAmountToDouble({int amount}) => + cryptoAmountToDouble(amount: amount, divider: litecoinAmountDivider); \ No newline at end of file diff --git a/lib/src/domain/monero/monero_amount_format.dart b/lib/src/domain/monero/monero_amount_format.dart index 3eb54d880..7dfb7142b 100644 --- a/lib/src/domain/monero/monero_amount_format.dart +++ b/lib/src/domain/monero/monero_amount_format.dart @@ -1,4 +1,5 @@ import 'package:intl/intl.dart'; +import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart'; const moneroAmountLength = 12; const moneroAmountDivider = 1000000000000; @@ -7,6 +8,6 @@ final moneroAmountFormat = NumberFormat() ..minimumFractionDigits = 1; String moneroAmountToString({int amount}) => - moneroAmountFormat.format(amount / moneroAmountDivider); + moneroAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: moneroAmountDivider)); -double moneroAmountToDouble({int amount}) => amount / moneroAmountDivider; +double moneroAmountToDouble({int amount}) => cryptoAmountToDouble(amount: amount, divider: moneroAmountDivider); diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index 89d767418..b7b3b229e 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -480,7 +480,28 @@ class DashboardPageBodyState extends State { ExchangeProviderDescription .changeNow), ) - ]))) + ]))), + PopupMenuItem( + value: 5, + child: Observer( + builder: (_) => Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Text('MorphToken'), + Checkbox( + value: actionListStore + .tradeFilterStore + .displayMorphToken, + onChanged: (value) => + actionListStore + .tradeFilterStore + .toggleDisplayExchange( + ExchangeProviderDescription + .morphToken), + ) + ]))) ], child: Text(S.of(context).filters, style: TextStyle( @@ -556,7 +577,7 @@ class DashboardPageBodyState extends State { final formattedAmount = trade.amount != null ? savedDisplayMode == BalanceDisplayMode.hiddenBalance ? '---' - : trade.amount + : trade.amountFormatted() : trade.amount; return TradeRow( diff --git a/lib/src/screens/dashboard/trade_row.dart b/lib/src/screens/dashboard/trade_row.dart index c3e88c8e2..75a3371bd 100644 --- a/lib/src/screens/dashboard/trade_row.dart +++ b/lib/src/screens/dashboard/trade_row.dart @@ -82,6 +82,9 @@ class TradeRow extends StatelessWidget { case ExchangeProviderDescription.changeNow: image = Image.asset('assets/images/change_now.png'); break; + case ExchangeProviderDescription.morphToken: + image = Image.asset('assets/images/morph_icon.png'); + break; default: image = null; } diff --git a/lib/src/screens/disclaimer/disclaimer_page.dart b/lib/src/screens/disclaimer/disclaimer_page.dart index 6e916df6f..490118e10 100644 --- a/lib/src/screens/disclaimer/disclaimer_page.dart +++ b/lib/src/screens/disclaimer/disclaimer_page.dart @@ -36,6 +36,7 @@ class DisclaimerBodyState extends State { static const xmrtoUrl = 'https://xmr.to/app_static/html/tos.html'; static const changenowUrl = 'https://changenow.io/terms-of-use'; + static const morphUrl = 'http://morphtoken.com/terms'; final bool _isAccepted; bool _checked = false; @@ -197,6 +198,27 @@ class DisclaimerBodyState extends State { )) ], ), + SizedBox( + height: 16.0, + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: GestureDetector( + onTap: () => launchUrl(morphUrl), + child: Text( + morphUrl, + textAlign: TextAlign.left, + style: TextStyle( + color: Colors.blue, + fontSize: 14.0, + fontWeight: FontWeight.normal, + decoration: TextDecoration.underline), + ), + )) + ], + ), SizedBox( height: 16.0, ) diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index a5cb2b55c..ce533c3cd 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -278,6 +278,9 @@ class ExchangeFormState extends State { case ExchangeProviderDescription.changeNow: imageSrc = 'assets/images/change_now.png'; break; + case ExchangeProviderDescription.morphToken: + imageSrc = 'assets/images/morph_icon.png'; + break; } return Padding( diff --git a/lib/src/screens/settings/settings.dart b/lib/src/screens/settings/settings.dart index 5d8f935c2..f893b0d6f 100644 --- a/lib/src/screens/settings/settings.dart +++ b/lib/src/screens/settings/settings.dart @@ -51,12 +51,14 @@ class SettingsFormState extends State { final _twitterImage = Image.asset('assets/images/Twitter.png'); final _changeNowImage = Image.asset('assets/images/change_now.png'); final _xmrBtcImage = Image.asset('assets/images/xmr_btc.png'); + final _morphImage = Image.asset('assets/images/morph_icon.png'); final _emailUrl = 'mailto:support@cakewallet.com'; final _telegramUrl = 'https:t.me/cakewallet_bot'; final _twitterUrl = 'https:twitter.com/CakewalletXMR'; final _changeNowUrl = 'mailto:support@changenow.io'; final _xmrToUrl = 'mailto:support@xmr.to'; + final _morphUrl = 'mailto:support@morphtoken.com'; final _items = List(); @@ -267,6 +269,12 @@ class SettingsFormState extends State { link: 'support@xmr.to', image: _xmrBtcImage, attribute: Attributes.link), + SettingsItem( + onTaped: () => _launchUrl(_morphUrl), + title: 'MorphToken', + link: 'support@morphtoken.com', + image: _morphImage, + attribute: Attributes.link), SettingsItem( onTaped: () { Navigator.push( diff --git a/lib/src/screens/trade_details/trade_details_page.dart b/lib/src/screens/trade_details/trade_details_page.dart index f60aae071..8aaf1ced4 100644 --- a/lib/src/screens/trade_details/trade_details_page.dart +++ b/lib/src/screens/trade_details/trade_details_page.dart @@ -8,6 +8,7 @@ import 'package:cake_wallet/src/stores/exchange_trade/exchange_trade_store.dart' import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; import 'package:cake_wallet/src/screens/transaction_details/standart_list_row.dart'; +import 'package:intl/intl.dart'; class TradeDetailsPage extends BasePage { @override @@ -16,6 +17,8 @@ class TradeDetailsPage extends BasePage { @override bool get isModalBackButton => true; + final createdAtFormat = DateFormat('yyyy-MM-dd HH:mm:ss'); + @override Widget body(BuildContext context) { final exchangeStore = Provider.of(context); @@ -43,7 +46,7 @@ class TradeDetailsPage extends BasePage { if (trade.createdAt != null) { items.add(StandartListItem( title: S.of(context).trade_details_created_at, - value: trade.createdAt.toString())); + value: createdAtFormat.format(trade.createdAt).toString())); } if (trade.from != null && trade.to != null) { diff --git a/lib/src/stores/action_list/trade_filter_store.dart b/lib/src/stores/action_list/trade_filter_store.dart index 3576e459f..4577038e3 100644 --- a/lib/src/stores/action_list/trade_filter_store.dart +++ b/lib/src/stores/action_list/trade_filter_store.dart @@ -11,6 +11,7 @@ abstract class TradeFilterStoreBase with Store { TradeFilterStoreBase( {this.displayXMRTO = true, this.displayChangeNow = true, + this.displayMorphToken = true, this.walletStore}); @observable @@ -19,6 +20,9 @@ abstract class TradeFilterStoreBase with Store { @observable bool displayChangeNow; + @observable + bool displayMorphToken; + WalletStore walletStore; @action @@ -30,13 +34,16 @@ abstract class TradeFilterStoreBase with Store { case ExchangeProviderDescription.xmrto: displayXMRTO = !displayXMRTO; break; + case ExchangeProviderDescription.morphToken: + displayMorphToken = !displayMorphToken; + break; } } List filtered({List trades}) { final _trades = trades.where((item) => item.trade.walletId == walletStore.id).toList(); - final needToFilter = !displayChangeNow || !displayXMRTO; + final needToFilter = !displayChangeNow || !displayXMRTO || !displayMorphToken; return needToFilter ? trades @@ -45,7 +52,10 @@ abstract class TradeFilterStoreBase with Store { item.trade.provider == ExchangeProviderDescription.xmrto) || (displayChangeNow && item.trade.provider == - ExchangeProviderDescription.changeNow)) + ExchangeProviderDescription.changeNow) || + (displayMorphToken && + item.trade.provider == + ExchangeProviderDescription.morphToken)) .toList() : _trades; } diff --git a/lib/src/stores/exchange/exchange_store.dart b/lib/src/stores/exchange/exchange_store.dart index a0cf932e4..189bbea8a 100644 --- a/lib/src/stores/exchange/exchange_store.dart +++ b/lib/src/stores/exchange/exchange_store.dart @@ -8,11 +8,15 @@ import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart'; import 'package:cake_wallet/src/domain/exchange/trade_request.dart'; import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart'; import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_trade_request.dart'; +import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_exchange_provider.dart'; +import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_request.dart'; import 'package:cake_wallet/src/domain/exchange/trade.dart'; import 'package:cake_wallet/src/stores/wallet/wallet_store.dart'; import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart'; import 'package:cake_wallet/src/stores/exchange/limits_state.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/domain/exchange/limits.dart'; +import 'package:intl/intl.dart'; part 'exchange_store.g.dart'; @@ -31,6 +35,7 @@ abstract class ExchangeStoreBase with Store { receiveCurrency = initialReceiveCurrency; limitsState = LimitsInitialState(); tradeState = ExchangeTradeStateInitial(); + _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12; loadLimits(); } @@ -72,11 +77,16 @@ abstract class ExchangeStoreBase with Store { WalletStore walletStore; + Limits limits; + + NumberFormat _cryptoNumberFormat; + @action void changeProvider({ExchangeProvider provider}) { this.provider = provider; depositAmount = ''; receiveAmount = ''; + loadLimits(); } @action @@ -105,7 +115,7 @@ abstract class ExchangeStoreBase with Store { provider .calculateAmount( from: depositCurrency, to: receiveCurrency, amount: _amount) - .then((amount) => amount.toString()) + .then((amount) => _cryptoNumberFormat.format(amount).toString().replaceAll(RegExp("\\,"), "")) .then((amount) => depositAmount = amount); } @@ -122,7 +132,7 @@ abstract class ExchangeStoreBase with Store { provider .calculateAmount( from: depositCurrency, to: receiveCurrency, amount: _amount) - .then((amount) => amount.toString()) + .then((amount) => _cryptoNumberFormat.format(amount).toString().replaceAll(RegExp("\\,"), "")) .then((amount) => receiveAmount = amount); } @@ -131,7 +141,7 @@ abstract class ExchangeStoreBase with Store { limitsState = LimitsIsLoading(); try { - final limits = await provider.fetchLimits( + limits = await provider.fetchLimits( from: depositCurrency, to: receiveCurrency); limitsState = LimitsLoadedSuccessfully(limits: limits); } catch (e) { @@ -142,6 +152,8 @@ abstract class ExchangeStoreBase with Store { @action Future createTrade() async { TradeRequest request; + String amount; + CryptoCurrency currency; if (provider is XMRTOExchangeProvider) { request = XMRTOTradeRequest( @@ -150,6 +162,8 @@ abstract class ExchangeStoreBase with Store { amount: receiveAmount, address: receiveAddress, refundAddress: depositAddress); + amount = receiveAmount; + currency = receiveCurrency; } if (provider is ChangeNowExchangeProvider) { @@ -159,17 +173,43 @@ abstract class ExchangeStoreBase with Store { amount: depositAmount, refundAddress: depositAddress, address: receiveAddress); + amount = depositAmount; + currency = depositCurrency; } - try { - tradeState = TradeIsCreating(); - final trade = await provider.createTrade(request: request); - trade.walletId = walletStore.id; - await trades.add(trade); - tradeState = TradeIsCreatedSuccessfully(trade: trade); - } catch (e) { - tradeState = TradeIsCreatedFailure(error: e.toString()); + if (provider is MorphTokenExchangeProvider) { + request = MorphTokenRequest( + from: depositCurrency, + to: receiveCurrency, + amount: depositAmount, + refundAddress: depositAddress, + address: receiveAddress); + amount = depositAmount; + currency = depositCurrency; } + + if (limitsState is LimitsLoadedSuccessfully && amount != null) { + if (double.parse(amount) < limits.min) { + tradeState = TradeIsCreatedFailure(error: S.current.error_text_minimal_limit("${provider.description}", + "${limits.min}", currency.toString())); + } else if (limits.max != null && double.parse(amount) > limits.max) { + tradeState = TradeIsCreatedFailure(error: S.current.error_text_maximum_limit("${provider.description}", + "${limits.max}", currency.toString())); + } else { + try { + tradeState = TradeIsCreating(); + final trade = await provider.createTrade(request: request); + trade.walletId = walletStore.id; + await trades.add(trade); + tradeState = TradeIsCreatedSuccessfully(trade: trade); + } catch (e) { + tradeState = TradeIsCreatedFailure(error: e.toString()); + } + } + } else { + tradeState = TradeIsCreatedFailure(error: S.current.error_text_limits_loading_failed("${provider.description}")); + } + } @action @@ -181,6 +221,7 @@ abstract class ExchangeStoreBase with Store { provider = XMRTOExchangeProvider(); depositCurrency = CryptoCurrency.xmr; receiveCurrency = CryptoCurrency.btc; + loadLimits(); } List providersForCurrentPair() { diff --git a/lib/src/stores/exchange_trade/exchange_trade_store.dart b/lib/src/stores/exchange_trade/exchange_trade_store.dart index 6e6cb2536..26e75977a 100644 --- a/lib/src/stores/exchange_trade/exchange_trade_store.dart +++ b/lib/src/stores/exchange_trade/exchange_trade_store.dart @@ -6,7 +6,9 @@ import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart'; import 'package:cake_wallet/src/domain/exchange/changenow/changenow_exchange_provider.dart'; import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart'; +import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_exchange_provider.dart'; import 'package:cake_wallet/src/stores/wallet/wallet_store.dart'; +import 'package:hive/hive.dart'; part 'exchange_trade_store.g.dart'; @@ -14,7 +16,7 @@ class ExchangeTradeStore = ExchangeTradeStoreBase with _$ExchangeTradeStore; abstract class ExchangeTradeStoreBase with Store { ExchangeTradeStoreBase( - {@required this.trade, @required WalletStore walletStore}) { + {@required this.trade, @required WalletStore walletStore, @required this.trades}) { isSendable = trade.from == walletStore.type || trade.provider == ExchangeProviderDescription.xmrto; @@ -25,6 +27,9 @@ abstract class ExchangeTradeStoreBase with Store { case ExchangeProviderDescription.changeNow: _provider = ChangeNowExchangeProvider(); break; + case ExchangeProviderDescription.morphToken: + _provider = MorphTokenExchangeProvider(trades: trades); + break; } _updateTrade(); @@ -37,6 +42,8 @@ abstract class ExchangeTradeStoreBase with Store { @observable bool isSendable; + Box trades; + ExchangeProvider _provider; Timer _timer; diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 05106ffdd..2dc78e7c7 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -278,6 +278,9 @@ "error_text_wallet_name" : "Der Wallet darf nur Buchstaben und Zahlen enthalten\nund muss zwischen 1 und 15 Zeichen lang sein", "error_text_keys" : "Walletschlüssel können nur 64 hexadezimale Zeichen enthalten", "error_text_crypto_currency" : "Die Anzahl der Nachkommastellen\nmuss kleiner oder gleich 12 sein.", + "error_text_minimal_limit" : "Handel für ${provider} wird nicht erstellt. Menge ist weniger als minimal: ${min} ${currency}", + "error_text_maximum_limit" : "Handel für ${provider} wird nicht erstellt. Menge ist mehr als maximal: ${max} ${currency}", + "error_text_limits_loading_failed" : "Handel für ${provider} wird nicht erstellt. Das Laden der Limits ist fehlgeschlagen", "auth_store_ban_timeout" : "Auszeit verbieten", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 70e95b298..5d8b88eda 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -278,6 +278,9 @@ "error_text_wallet_name" : "Wallet name can only contain letters, numbers\nand must be between 1 and 15 characters long", "error_text_keys" : "Wallet keys can only contain 64 chars in hex", "error_text_crypto_currency" : "The number of fraction digits\nmust be less or equal to 12", + "error_text_minimal_limit" : "Trade for ${provider} is not created. Amount is less then minimal: ${min} ${currency}", + "error_text_maximum_limit" : "Trade for ${provider} is not created. Amount is more then maximum: ${max} ${currency}", + "error_text_limits_loading_failed" : "Trade for ${provider} is not created. Limits loading failed", "auth_store_ban_timeout" : "ban_timeout", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index e76cb10f6..308833a1a 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -278,6 +278,9 @@ "error_text_wallet_name" : "El nombre de la billetera solo puede contener letras, números \ny debe tener entre 1 y 15 caracteres de longitud", "error_text_keys" : "Las llaves de billetera solo pueden contener 64 caracteres en hexadecimal", "error_text_crypto_currency" : "El número de dígitos fraccionarios \ndebe ser menor o igual a 12", + "error_text_minimal_limit" : "El comercio por ${provider} no se crea. La cantidad es menos que mínima: ${min} ${currency}", + "error_text_maximum_limit" : "El comercio por ${provider} no se crea. La cantidad es más que el máximo: ${max} ${currency}", + "error_text_limits_loading_failed" : "El comercio por ${provider} no se crea. Límites de carga fallidos", "auth_store_ban_timeout" : "prohibición de tiempo de espera", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 7c5b21b08..aa4a8545e 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -278,6 +278,9 @@ "error_text_wallet_name" : "वॉलेट नाम में केवल अक्षर, संख्याएं हो सकती हैं\nऔर 1 और 15 वर्णों के बीच लंबा होना चाहिए", "error_text_keys" : "वॉलेट कीज़ में हेक्स में केवल 64 वर्ण हो सकते हैं", "error_text_crypto_currency" : "अंश अंकों की संख्या\n12 से कम या इसके बराबर होना चाहिए", + "error_text_minimal_limit" : "व्यापार ${provider} के लिए नहीं बनाया गया है। राशि कम है तो न्यूनतम: ${min} ${currency}", + "error_text_maximum_limit" : "व्यापार ${provider} के लिए नहीं बनाया गया है। राशि अधिक है तो अधिकतम: ${max} ${currency}", + "error_text_limits_loading_failed" : "व्यापार ${provider} के लिए नहीं बनाया गया है। लोडिंग की सीमाएं विफल रहीं", "auth_store_ban_timeout" : "समय की पाबंदी", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index f78d7c8d1..a23b72f4c 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -278,6 +278,9 @@ "error_text_wallet_name" : "ウォレット名には文字のみを含めることができます\n1〜15文字である必要があります", "error_text_keys" : "ウォレットキーには、16進数で64文字しか含めることができません", "error_text_crypto_currency" : "小数桁数\n12以下でなければなりません", + "error_text_minimal_limit" : "${provider} の取引は作成されません。 金額は最小額より少ない: ${min} ${currency}", + "error_text_maximum_limit" : "${provider} の取引は作成されません。 金額は最大値を超えています: ${max} ${currency}", + "error_text_limits_loading_failed" : "${provider} の取引は作成されません。 制限の読み込みに失敗しました", "auth_store_ban_timeout" : "禁止タイムアウト", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 279a81ace..699c6caa7 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -278,6 +278,9 @@ "error_text_wallet_name" : "지갑 이름은 문자, 숫자 만 포함 할 수 있습니다\n1 ~ 15 자 사이 여야합니다", "error_text_keys" : "지갑 키는 16 진수로 64 자만 포함 할 수 있습니다", "error_text_crypto_currency" : "소수 자릿수\n12 이하 여야합니다", + "error_text_minimal_limit" : "거래 ${provider} 가 생성되지 않습니다. 금액이 최소보다 적습니다. ${min} ${currency}", + "error_text_maximum_limit" : "거래 ${provider} 가 생성되지 않습니다. 금액이 최대 값보다 많습니다. ${max} ${currency}", + "error_text_limits_loading_failed" : "거래 ${provider} 가 생성되지 않습니다. 로딩 실패", "auth_store_ban_timeout" : "타임 아웃 금지", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 6550cfbb6..3784383dc 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -278,6 +278,9 @@ "error_text_wallet_name" : "Naam portemonnee kan alleen letters, cijfers bevatten\nen moet tussen de 1 en 15 tekens lang zijn", "error_text_keys" : "Portefeuillesleutels kunnen maximaal 64 tekens bevatten in hexadecimale volgorde", "error_text_crypto_currency" : "Het aantal breukcijfers\nmoet kleiner zijn dan of gelijk zijn aan 12", + "error_text_minimal_limit" : "Ruil voor ${provider} is niet gemaakt. Bedrag is minder dan minimaal: ${min} ${currency}", + "error_text_maximum_limit" : "Ruil voor ${provider} is niet gemaakt. Bedrag is meer dan maximaal: ${max} ${currency}", + "error_text_limits_loading_failed" : "Ruil voor ${provider} is niet gemaakt. Beperkingen laden mislukt", "auth_store_ban_timeout" : "time-out verbieden", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 5f4236eba..966ce647c 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -278,6 +278,9 @@ "error_text_wallet_name" : "Nazwa portfela może zawierać tylko litery i cyfry\ni musi mieć od 1 do 15 znaków", "error_text_keys" : "Klucze portfela mogą zawierać tylko 64 znaki w systemie szesnastkowym", "error_text_crypto_currency" : "Liczba cyfr ułamkowych\nmusi być mniejsza lub równa 12", + "error_text_minimal_limit" : "Wymiana dla ${provider} nie została utworzona. Kwota jest mniejsza niż minimalna: ${min} ${currency}", + "error_text_maximum_limit" : "Wymiana dla ${provider} nie została utworzona. Kwota jest większa niż maksymalna: ${max} ${currency}", + "error_text_limits_loading_failed" : "Wymiana dla ${provider} nie została utworzona. Ładowanie limitów nie powiodło się", "auth_store_ban_timeout" : "przekroczenie limitu czasu", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 73cbe5866..8ef36326c 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -278,6 +278,9 @@ "error_text_wallet_name" : "O nome da carteira só pode conter letras, números\ne deve ter entre 1 e 15 caracteres", "error_text_keys" : "As chaves da carteira podem conter apenas 64 caracteres em hexadecimal", "error_text_crypto_currency" : "O número de dígitos decimais\ndeve ser menor ou igual a 12", + "error_text_minimal_limit" : "A troca por ${provider} não é criada. O valor é menor que o mínimo: ${min} ${currency}", + "error_text_maximum_limit" : "A troca por ${provider} não é criada. O valor é superior ao máximo: ${max} ${currency}", + "error_text_limits_loading_failed" : "A troca por ${provider} não é criada. Falha no carregamento dos limites", "auth_store_ban_timeout" : "ban_timeout", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 62b617b5e..d2d17760f 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -278,6 +278,9 @@ "error_text_wallet_name" : "Имя кошелька может содержать только буквы, цифры\nи может быть от 1 до 15 символов в длину", "error_text_keys" : "Ключи кошелька могут содержать только 64 символа в hex", "error_text_crypto_currency" : "Количество цифр после запятой\nдолжно быть меньше или равно 12", + "error_text_minimal_limit" : "Сделка для ${provider} не создана. Сумма меньше минимальной: ${min} ${currency}", + "error_text_maximum_limit" : "Сделка для ${provider} не создана. Сумма больше максимальной: ${max} ${currency}", + "error_text_limits_loading_failed" : "Сделка для ${provider} не создана. Ошибка загрузки лимитов", "auth_store_ban_timeout" : "ban_timeout", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index b0fbb6bbb..d19056644 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -278,6 +278,9 @@ "error_text_wallet_name" : "钱包名称只能包含字母,数字\n且必须介于1到15个字符之间", "error_text_keys" : "钱包密钥只能包含16个字符的十六进制字符", "error_text_crypto_currency" : "小数位数\n必须小于或等于12", + "error_text_minimal_limit" : "未創建 ${provider} 交易。 金額少於最小值:${min} ${currency}", + "error_text_maximum_limit" : "未創建 ${provider} 交易。 金額大於最大值:${max} ${currency}", + "error_text_limits_loading_failed" : "未創建 ${provider} 交易。 限制加載失敗", "auth_store_ban_timeout" : "禁止超时",