From d6c641242f3c2ead5ec64fe2c87fab7c188650ca Mon Sep 17 00:00:00 2001 From: Serhii Date: Mon, 25 Sep 2023 15:20:11 +0300 Subject: [PATCH 1/6] CW-475-Better-error-message-if-there-isnt-enough-network-fee-asset-available (#1092) * Better error message * minor fixes * localization * Update strings_tl.arb * Update append_translation.sh --- lib/view_model/send/send_view_model.dart | 14 +++++++++++++- res/values/strings_ar.arb | 3 +++ res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 1 + res/values/strings_en.arb | 1 + res/values/strings_es.arb | 1 + res/values/strings_fr.arb | 1 + res/values/strings_ha.arb | 1 + res/values/strings_hi.arb | 1 + res/values/strings_hr.arb | 3 ++- res/values/strings_id.arb | 3 ++- res/values/strings_it.arb | 1 + res/values/strings_ja.arb | 1 + res/values/strings_ko.arb | 1 + res/values/strings_my.arb | 1 + res/values/strings_nl.arb | 1 + res/values/strings_pl.arb | 1 + res/values/strings_pt.arb | 1 + res/values/strings_ru.arb | 1 + res/values/strings_th.arb | 1 + res/values/strings_tl.arb | 4 +++- res/values/strings_tr.arb | 1 + res/values/strings_uk.arb | 1 + res/values/strings_ur.arb | 1 + res/values/strings_yo.arb | 1 + res/values/strings_zh.arb | 1 + scripts/append_translation.sh | 2 +- 28 files changed, 46 insertions(+), 5 deletions(-) diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 329b3c4ad..72ce9fdb2 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -30,6 +30,7 @@ import 'package:cake_wallet/view_model/send/send_view_model_state.dart'; import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/generated/i18n.dart'; part 'send_view_model.g.dart'; @@ -325,7 +326,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor state = TransactionCommitted(); } catch (e) { - state = FailureState(e.toString()); + String translatedError = translateErrorMessage(e.toString(), wallet.type, wallet.currency); + state = FailureState(translatedError); } } @@ -412,4 +414,14 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor selectedCryptoCurrency = wallet.currency; } } + + String translateErrorMessage(String error, WalletType walletType, CryptoCurrency currency,) { + if (walletType == WalletType.ethereum || walletType == WalletType.haven) { + if (error.contains('gas required exceeds allowance (0)') || error.contains('insufficient funds for gas')) { + return S.current.do_not_have_enough_gas_asset(currency.toString()); + } + } + + return error; + } } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index b10dfbe8e..3a90aa308 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -689,5 +689,8 @@ "default_buy_provider": "مزود شراء الافتراضي", "ask_each_time": "اسأل في كل مرة", "buy_provider_unavailable": "مزود حاليا غير متوفر.", + + "do_not_have_enough_gas_asset": "ليس لديك ما يكفي من ${currency} لإجراء معاملة وفقًا لشروط شبكة blockchain الحالية. أنت بحاجة إلى المزيد من ${currency} لدفع رسوم شبكة blockchain، حتى لو كنت ترسل أصلًا مختلفًا.", "totp_auth_url": " TOTP ﺔﻗﺩﺎﺼﻤﻟ URL ﻥﺍﻮﻨﻋ" } + diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 828af2102..39ce6196b 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -685,5 +685,6 @@ "default_buy_provider": "Доставчик по подразбиране купува", "ask_each_time": "Питайте всеки път", "buy_provider_unavailable": "Понастоящем доставчик не е наличен.", + "do_not_have_enough_gas_asset": "Нямате достатъчно ${currency}, за да извършите транзакция с текущите условия на блокчейн мрежата. Имате нужда от повече ${currency}, за да платите таксите за блокчейн мрежа, дори ако изпращате различен актив.", "totp_auth_url": "TOTP AUTH URL" } diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index ea4dc1856..0c063f0c9 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -685,5 +685,6 @@ "default_buy_provider": "Výchozí poskytovatel nákupu", "ask_each_time": "Zeptejte se pokaždé", "buy_provider_unavailable": "Poskytovatel aktuálně nedostupný.", + "do_not_have_enough_gas_asset": "Nemáte dostatek ${currency} k provedení transakce s aktuálními podmínkami blockchainové sítě. K placení poplatků za blockchainovou síť potřebujete více ${currency}, i když posíláte jiné aktivum.", "totp_auth_url": "URL AUTH TOTP" } diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 09538ccc0..4b2cba6d4 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -693,5 +693,6 @@ "default_buy_provider": "Standard-Kaufanbieter", "ask_each_time": "Jedes Mal fragen", "buy_provider_unavailable": "Anbieter derzeit nicht verfügbar.", + "do_not_have_enough_gas_asset": "Sie verfügen nicht über genügend ${currency}, um eine Transaktion unter den aktuellen Bedingungen des Blockchain-Netzwerks durchzuführen. Sie benötigen mehr ${currency}, um die Gebühren für das Blockchain-Netzwerk zu bezahlen, auch wenn Sie einen anderen Vermögenswert senden.", "totp_auth_url": "TOTP-Auth-URL" } diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 99ba22b18..7e3b1b55d 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -694,5 +694,6 @@ "ask_each_time": "Ask each time", "robinhood_option_description": "Buy and transfer instantly using your debit card, bank account, or Robinhood balance. USA only.", "buy_provider_unavailable": "Provider currently unavailable.", + "do_not_have_enough_gas_asset": "You do not have enough ${currency} to make a transaction with the current blockchain network conditions. You need more ${currency} to pay blockchain network fees, even if you are sending a different asset.", "totp_auth_url": "TOTP AUTH URL" } diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index a6175c6c0..f2b0729f3 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -693,5 +693,6 @@ "default_buy_provider": "Proveedor de compra predeterminado", "ask_each_time": "Pregunta cada vez", "buy_provider_unavailable": "Proveedor actualmente no disponible.", + "do_not_have_enough_gas_asset": "No tienes suficiente ${currency} para realizar una transacción con las condiciones actuales de la red blockchain. Necesita más ${currency} para pagar las tarifas de la red blockchain, incluso si envía un activo diferente.", "totp_auth_url": "URL de autenticación TOTP" } diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index f659f0b14..a081727fb 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -693,5 +693,6 @@ "default_buy_provider": "Fournisseur d'achat par défaut", "ask_each_time": "Demandez à chaque fois", "buy_provider_unavailable": "Fournisseur actuellement indisponible.", + "do_not_have_enough_gas_asset": "Vous n'avez pas assez de ${currency} pour effectuer une transaction avec les conditions actuelles du réseau blockchain. Vous avez besoin de plus de ${currency} pour payer les frais du réseau blockchain, même si vous envoyez un actif différent.", "totp_auth_url": "URL D'AUTORISATION TOTP" } diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 2b55ffd2a..68051b5d9 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -671,5 +671,6 @@ "default_buy_provider": "Tsohuwar Siyarwa", "ask_each_time": "Tambaya kowane lokaci", "buy_provider_unavailable": "Mai ba da kyauta a halin yanzu babu.", + "do_not_have_enough_gas_asset": "Ba ku da isassun ${currency} don yin ma'amala tare da yanayin cibiyar sadarwar blockchain na yanzu. Kuna buƙatar ƙarin ${currency} don biyan kuɗaɗen cibiyar sadarwar blockchain, koda kuwa kuna aika wata kadara daban.", "totp_auth_url": "TOTP AUTH URL" } diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 69f37ab83..9f531bb29 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -693,5 +693,6 @@ "default_buy_provider": "डिफ़ॉल्ट खरीद प्रदाता", "ask_each_time": "हर बार पूछें", "buy_provider_unavailable": "वर्तमान में प्रदाता अनुपलब्ध है।", + "do_not_have_enough_gas_asset": "वर्तमान ब्लॉकचेन नेटवर्क स्थितियों में लेनदेन करने के लिए आपके पास पर्याप्त ${currency} नहीं है। ब्लॉकचेन नेटवर्क शुल्क का भुगतान करने के लिए आपको अधिक ${currency} की आवश्यकता है, भले ही आप एक अलग संपत्ति भेज रहे हों।", "totp_auth_url": "TOTP प्रामाणिक यूआरएल" } diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 3c4299092..4b56ef344 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -693,5 +693,6 @@ "default_buy_provider": "Zadani davatelj kupnje", "ask_each_time": "Pitajte svaki put", "buy_provider_unavailable": "Davatelj trenutno nije dostupan.", + "do_not_have_enough_gas_asset": "Nemate dovoljno ${currency} da izvršite transakciju s trenutačnim uvjetima blockchain mreže. Trebate više ${currency} da platite naknade za blockchain mrežu, čak i ako šaljete drugu imovinu.", "totp_auth_url": "TOTP AUTH URL" -} +} \ No newline at end of file diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index a5b2e3ffa..6200559e3 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -681,5 +681,6 @@ "default_buy_provider": "Penyedia beli default", "ask_each_time": "Tanyakan setiap kali", "buy_provider_unavailable": "Penyedia saat ini tidak tersedia.", + "do_not_have_enough_gas_asset": "Anda tidak memiliki cukup ${currency} untuk melakukan transaksi dengan kondisi jaringan blockchain saat ini. Anda memerlukan lebih banyak ${currency} untuk membayar biaya jaringan blockchain, meskipun Anda mengirimkan aset yang berbeda.", "totp_auth_url": "URL Otentikasi TOTP" -} +} \ No newline at end of file diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 98c13aa2d..e3fd1b94b 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -693,5 +693,6 @@ "default_buy_provider": "Provider di acquisto predefinito", "ask_each_time": "Chiedi ogni volta", "buy_provider_unavailable": "Provider attualmente non disponibile.", + "do_not_have_enough_gas_asset": "Non hai abbastanza ${currency} per effettuare una transazione con le attuali condizioni della rete blockchain. Hai bisogno di più ${currency} per pagare le commissioni della rete blockchain, anche se stai inviando una risorsa diversa.", "totp_auth_url": "URL DI AUT. TOTP" } diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index f5bea4b58..8dbf429c5 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -693,5 +693,6 @@ "default_buy_provider": "デフォルトの購入プロバイダー", "ask_each_time": "毎回尋ねてください", "buy_provider_unavailable": "現在、プロバイダーは利用できません。", + "do_not_have_enough_gas_asset": "現在のブロックチェーン ネットワークの状況では、トランザクションを行うのに十分な ${currency} がありません。別のアセットを送信する場合でも、ブロックチェーン ネットワーク料金を支払うにはさらに ${currency} が必要です。", "totp_auth_url": "TOTP認証URL" } diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 79455ff92..da9b99c2b 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -693,5 +693,6 @@ "default_buy_provider": "기본 구매 제공자", "ask_each_time": "매번 물어보십시오", "buy_provider_unavailable": "제공자는 현재 사용할 수 없습니다.", + "do_not_have_enough_gas_asset": "현재 블록체인 네트워크 조건으로 거래를 하기에는 ${currency}이(가) 충분하지 않습니다. 다른 자산을 보내더라도 블록체인 네트워크 수수료를 지불하려면 ${currency}가 더 필요합니다.", "totp_auth_url": "TOTP 인증 URL" } diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 4ea13bc27..043484feb 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -691,5 +691,6 @@ "default_buy_provider": "Default Provider ကိုဝယ်ပါ", "ask_each_time": "တစ်ခုချင်းစီကိုအချိန်မေးပါ", "buy_provider_unavailable": "လက်ရှိတွင်လက်ရှိမရနိုင်ပါ။", + "do_not_have_enough_gas_asset": "လက်ရှိ blockchain ကွန်ရက်အခြေအနေများနှင့် အရောင်းအဝယ်ပြုလုပ်ရန် သင့်တွင် ${currency} လုံလောက်မှုမရှိပါ။ သင်သည် မတူညီသော ပိုင်ဆိုင်မှုတစ်ခုကို ပေးပို့နေသော်လည်း blockchain ကွန်ရက်အခကြေးငွေကို ပေးဆောင်ရန် သင်သည် နောက်ထပ် ${currency} လိုအပ်ပါသည်။", "totp_auth_url": "TOTP AUTH URL" } diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 99a469945..2d9f3655e 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -693,5 +693,6 @@ "default_buy_provider": "Standaard Koopprovider", "ask_each_time": "Vraag het elke keer", "buy_provider_unavailable": "Provider momenteel niet beschikbaar.", + "do_not_have_enough_gas_asset": "U heeft niet genoeg ${currency} om een transactie uit te voeren met de huidige blockchain-netwerkomstandigheden. U heeft meer ${currency} nodig om blockchain-netwerkkosten te betalen, zelfs als u een ander item verzendt.", "totp_auth_url": "TOTP AUTH-URL" } diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 5d32ee12c..6f3a41d0b 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -693,5 +693,6 @@ "default_buy_provider": "Domyślny dostawca zakupu", "ask_each_time": "Zapytaj za każdym razem", "buy_provider_unavailable": "Dostawca obecnie niedostępny.", + "do_not_have_enough_gas_asset": "Nie masz wystarczającej ilości ${currency}, aby dokonać transakcji przy bieżących warunkach sieci blockchain. Potrzebujesz więcej ${currency}, aby uiścić opłaty za sieć blockchain, nawet jeśli wysyłasz inny zasób.", "totp_auth_url": "Adres URL TOTP AUTH" } diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index ca46a26e7..ea442420f 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -692,5 +692,6 @@ "default_buy_provider": "Provedor de compra padrão", "ask_each_time": "Pergunte cada vez", "buy_provider_unavailable": "Provedor atualmente indisponível.", + "do_not_have_enough_gas_asset": "Você não tem ${currency} suficiente para fazer uma transação com as condições atuais da rede blockchain. Você precisa de mais ${currency} para pagar as taxas da rede blockchain, mesmo se estiver enviando um ativo diferente.", "totp_auth_url": "URL de autenticação TOTP" } diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index fb3883338..5e70bc544 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -693,5 +693,6 @@ "default_buy_provider": "По умолчанию поставщик покупки", "ask_each_time": "Спросите каждый раз", "buy_provider_unavailable": "Поставщик в настоящее время недоступен.", + "do_not_have_enough_gas_asset": "У вас недостаточно ${currency} для совершения транзакции при текущих условиях сети блокчейн. Вам нужно больше ${currency} для оплаты комиссий за сеть блокчейна, даже если вы отправляете другой актив.", "totp_auth_url": "URL-адрес TOTP-АВТОРИЗАЦИИ" } diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 0e31518eb..8778802a9 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -691,5 +691,6 @@ "default_buy_provider": "ผู้ให้บริการซื้อเริ่มต้น", "ask_each_time": "ถามทุกครั้ง", "buy_provider_unavailable": "ผู้ให้บริการไม่สามารถใช้งานได้ในปัจจุบัน", + "do_not_have_enough_gas_asset": "คุณมี ${currency} ไม่เพียงพอที่จะทำธุรกรรมกับเงื่อนไขเครือข่ายบล็อคเชนในปัจจุบัน คุณต้องมี ${currency} เพิ่มขึ้นเพื่อชำระค่าธรรมเนียมเครือข่ายบล็อคเชน แม้ว่าคุณจะส่งสินทรัพย์อื่นก็ตาม", "totp_auth_url": "URL การตรวจสอบสิทธิ์ TOTP" } diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index d65e36609..18c52aa83 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -687,5 +687,7 @@ "support_title_other_links": "Iba pang mga link sa suporta", "support_description_other_links": "Sumali sa aming mga komunidad o maabot sa amin ang aming mga kasosyo sa pamamagitan ng iba pang mga pamamaraan", "select_destination": "Mangyaring piliin ang patutunguhan para sa backup file.", - "save_to_downloads": "I -save sa mga pag -download" + "save_to_downloads": "I -save sa mga pag -download", + "do_not_have_enough_gas_asset": "Wala kang sapat na ${currency} para gumawa ng transaksyon sa kasalukuyang kundisyon ng network ng blockchain. Kailangan mo ng higit pang ${currency} upang magbayad ng mga bayarin sa network ng blockchain, kahit na nagpapadala ka ng ibang asset.", + "totp_auth_url": "TOTP AUTH URL" } \ No newline at end of file diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 16ed18b96..fa4568e93 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -691,5 +691,6 @@ "default_buy_provider": "Varsayılan Satın Alma Sağlayıcısı", "ask_each_time": "Her seferinde sor", "buy_provider_unavailable": "Sağlayıcı şu anda kullanılamıyor.", + "do_not_have_enough_gas_asset": "Mevcut blockchain ağ koşullarıyla işlem yapmak için yeterli ${currency} paranız yok. Farklı bir varlık gönderiyor olsanız bile blockchain ağ ücretlerini ödemek için daha fazla ${currency} miktarına ihtiyacınız var.", "totp_auth_url": "TOTP YETKİ URL'si" } diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index c9943dd3a..7b739e201 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -693,5 +693,6 @@ "default_buy_provider": "Постачальник покупки за замовчуванням", "ask_each_time": "Запитайте кожен раз", "buy_provider_unavailable": "В даний час постачальник недоступний.", + "do_not_have_enough_gas_asset": "У вас недостатньо ${currency}, щоб здійснити трансакцію з поточними умовами мережі блокчейн. Вам потрібно більше ${currency}, щоб сплатити комісію мережі блокчейн, навіть якщо ви надсилаєте інший актив.", "totp_auth_url": "TOTP AUTH URL" } diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 47c96d9fb..e53784923 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -685,5 +685,6 @@ "default_buy_provider": "پہلے سے طے شدہ خریدنے والا", "ask_each_time": "ہر بار پوچھیں", "buy_provider_unavailable": "فراہم کنندہ فی الحال دستیاب نہیں ہے۔", + "do_not_have_enough_gas_asset": "آپ کے پاس موجودہ بلاکچین نیٹ ورک کی شرائط کے ساتھ لین دین کرنے کے لیے کافی ${currency} نہیں ہے۔ آپ کو بلاکچین نیٹ ورک کی فیس ادا کرنے کے لیے مزید ${currency} کی ضرورت ہے، چاہے آپ کوئی مختلف اثاثہ بھیج رہے ہوں۔", "totp_auth_url": "TOTP AUTH URL" } diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index ec84589da..0532ae2eb 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -687,5 +687,6 @@ "default_buy_provider": "Aiyipada Ra Olupese", "ask_each_time": "Beere lọwọ kọọkan", "buy_provider_unavailable": "Olupese lọwọlọwọ ko si.", + "do_not_have_enough_gas_asset": "O ko ni to ${currency} lati ṣe idunadura kan pẹlu awọn ipo nẹtiwọki blockchain lọwọlọwọ. O nilo diẹ sii ${currency} lati san awọn owo nẹtiwọọki blockchain, paapaa ti o ba nfi dukia miiran ranṣẹ.", "totp_auth_url": "TOTP AUTH URL" } diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index c606d4135..7c947847e 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -692,5 +692,6 @@ "default_buy_provider": "默认购买提供商", "ask_each_time": "每次问", "buy_provider_unavailable": "提供者目前不可用。", + "do_not_have_enough_gas_asset": "您没有足够的 ${currency} 来在当前的区块链网络条件下进行交易。即使您发送的是不同的资产,您也需要更多的 ${currency} 来支付区块链网络费用。", "totp_auth_url": "TOTP 授权 URL" } diff --git a/scripts/append_translation.sh b/scripts/append_translation.sh index 2dc373e0e..0cc33fc0f 100755 --- a/scripts/append_translation.sh +++ b/scripts/append_translation.sh @@ -7,7 +7,7 @@ # if you get an error `command not found` # give the correct permissions to this file using `chmod 777 append_translation.sh` -langs=("ar" "bg" "cs" "de" "en" "es" "fr" "ha" "hi" "hr" "id" "it" "ja" "ko" "my" "nl" "pl" "pt" "ru" "th" "tr" "uk" "ur" "yo" "zh") +langs=("ar" "bg" "cs" "de" "en" "es" "fr" "ha" "hi" "hr" "id" "it" "ja" "ko" "my" "nl" "pl" "pt" "ru" "th" "tl" "tr" "uk" "ur" "yo" "zh") name=$1 text=$2 From 9eb6867ab98dbb3a5fec64e0c940c716cf1fd43c Mon Sep 17 00:00:00 2001 From: Procyon Lotor <110021993+ProcyonLotor123@users.noreply.github.com> Date: Thu, 28 Sep 2023 19:20:08 +0300 Subject: [PATCH 2/6] Exolix integration (#1080) * Add Exolix exchange integration * update tx payload * remove import * Improve mapping * Additional fixes * fix apiBaseUrl * Update trade_details_view_model.dart * Update exolix_exchange_provider.dart * Fix status URL * Fix fetch rates API error handling update limits API to use a valid amount and validate on success status code --------- Co-authored-by: Justin Ehrenhofer Co-authored-by: Omar Hatem --- .github/workflows/pr_test_build.yml | 1 + assets/images/exolix.png | Bin 0 -> 1203 bytes .../exchange_provider_description.dart | 7 +- .../exolix/exolix_exchange_provider.dart | 294 ++++++++++++++++++ lib/exchange/exolix/exolix_request.dart | 20 ++ lib/exchange/trade_state.dart | 27 ++ .../screens/dashboard/widgets/trade_row.dart | 3 + lib/store/dashboard/trade_filter_store.dart | 18 +- .../dashboard/dashboard_view_model.dart | 5 + .../exchange/exchange_trade_view_model.dart | 4 + .../exchange/exchange_view_model.dart | 14 + lib/view_model/support_view_model.dart | 5 + lib/view_model/trade_details_view_model.dart | 10 + tool/utils/secret_key.dart | 1 + 14 files changed, 404 insertions(+), 5 deletions(-) create mode 100644 assets/images/exolix.png create mode 100644 lib/exchange/exolix/exolix_exchange_provider.dart create mode 100644 lib/exchange/exolix/exolix_request.dart diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 92d2ad8ba..d01d4222e 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -128,6 +128,7 @@ jobs: echo "const payfuraApiKey = '${{ secrets.PAYFURA_API_KEY }}';" >> lib/.secrets.g.dart echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_ethereum/lib/.secrets.g.dart echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart + echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart echo "const robinhoodCIdApiSecret = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart diff --git a/assets/images/exolix.png b/assets/images/exolix.png new file mode 100644 index 0000000000000000000000000000000000000000..29e5f2db1d659dc3ca64858d800287c29b0a49fa GIT binary patch literal 1203 zcmV;k1WfyhP)Px(ZAnByRA@upT3t+3M-={MH7sigx|iVHa%-9*rD;eiDv(r5E2W5NYg6q{jN0l$ z+cY7PkiIm<#56^T51O>5sXnx%8dB;{OlVT-Pg|@HO{iU3kPSPJ+FaGHkbfx&^k z^U0W+*a2}bR6Yd3mjrNH!J)t?D0U3qQ4d+sXF8S$NDGx?AlPViQUkrCey{p204jnj zL?B02KBK@FA`^(p5}?87RU3KrI0+OuB{?t%z{Lw}Q!e25y4*nbs2PqafgF#gl+rnW znE_45_k=jgeEqloPK2G1Ku)f*jXLn%bQs5rNkZ7}8&I2#jWP5vwM_4iM4r#J+(13SXI#i^9@dtI5a2YU(uNmhna7;QxP2mD@jUbK>q zKq}(AsKv4JvG;_IfFvtIx1e2Ptm^x{YQ*lyACj#62;gn2pefa(IPSlrzNO2B?5;lB zx_!mZizPA!tl6s8U+PSlsD*by+X0qU5`u$vSFs=~3#Tf|?ZTk^%@542F2Ng5=2<3jsJjPET~|%@St>s0RWmgLcHJj-wyt&6a5@~OM#KvE zM#oTk>MXY&b0aeOlKO*Ni$icOA-MM18V*UlNPgSTLOL`=%ZVf_tpMH;(&P|cEz9_( zY#E2-gh<8N4%{4Occ{>4C2m>q8NFeJpeW#3RgC_QOA)oUmsX-^ezqx0bI%QYex=J) zE>=K$V<*f1i+62D{-#%Pvgmz;#>cg}C^KCF-w0xpk)nvSnD z%usM#BM}bb?u-mvtgeh}Ad4VR3;KO5kbMG_V#Bo-9d6(03;45kf8?{e!UEJi`^*#) zRll9X^#>0vuRc?>O`x)^3G)jVO)=uvjXN=PxhL}Zh3ZO385xoPTW;J!!@pN-8E>1w z6P3&H!q@fjjUd2@!Y#TgN$=}5TB>m`7({Wqb-$0E(DDuthXZeR9ZtA{!TZB#EBPQI zQzM01@^42+sqOSoJs97cZtz$#+g zV@bn%SiH5GTfV)b4#RzSx#a}}TZL1J%v?9to9bh|=%%LT743IaSe&ytDEO_oI%QkfGIfCwHv4qWNGF>{&^PuspT8b41Ke23l zSdm2~7M<)4-Gj!|jVMLQ-jB81#`d5T7eBTA_=E0AsS%i-n!jzn1^}#=pgjR*Pg#Yr zN&CXqr)3ePQ%{CSH#56U!b+I8_%}Z`CA*$vtl7DczP<+U={jod Rkx~Ev002ovPDHLkV1hCPI;#Kx literal 0 HcmV?d00001 diff --git a/lib/exchange/exchange_provider_description.dart b/lib/exchange/exchange_provider_description.dart index e545f69ce..151d018e0 100644 --- a/lib/exchange/exchange_provider_description.dart +++ b/lib/exchange/exchange_provider_description.dart @@ -24,7 +24,10 @@ class ExchangeProviderDescription extends EnumerableItem with Serializable< static const trocador = ExchangeProviderDescription(title: 'Trocador', raw: 5, image: 'assets/images/trocador.png'); - static const all = ExchangeProviderDescription(title: 'All trades', raw: 6, image: ''); + static const exolix = + ExchangeProviderDescription(title: 'Exolix', raw: 6, image: 'assets/images/exolix.png'); + + static const all = ExchangeProviderDescription(title: 'All trades', raw: 7, image: ''); static ExchangeProviderDescription deserialize({required int raw}) { switch (raw) { @@ -41,6 +44,8 @@ class ExchangeProviderDescription extends EnumerableItem with Serializable< case 5: return trocador; case 6: + return exolix; + case 7: return all; default: throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize'); diff --git a/lib/exchange/exolix/exolix_exchange_provider.dart b/lib/exchange/exolix/exolix_exchange_provider.dart new file mode 100644 index 000000000..0768f1160 --- /dev/null +++ b/lib/exchange/exolix/exolix_exchange_provider.dart @@ -0,0 +1,294 @@ +import 'dart:convert'; +import 'package:cake_wallet/exchange/trade_not_found_exeption.dart'; +import 'package:http/http.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cake_wallet/exchange/exchange_pair.dart'; +import 'package:cake_wallet/exchange/exchange_provider.dart'; +import 'package:cake_wallet/exchange/limits.dart'; +import 'package:cake_wallet/exchange/trade.dart'; +import 'package:cake_wallet/exchange/trade_request.dart'; +import 'package:cake_wallet/exchange/trade_state.dart'; +import 'package:cake_wallet/exchange/exolix/exolix_request.dart'; +import 'package:cake_wallet/exchange/exchange_provider_description.dart'; + +class ExolixExchangeProvider extends ExchangeProvider { + ExolixExchangeProvider() : super(pairList: _supportedPairs()); + + static final apiKey = secrets.exolixApiKey; + static const apiBaseUrl = 'exolix.com'; + static const transactionsPath = '/api/v2/transactions'; + static const ratePath = '/api/v2/rate'; + + static const List _notSupported = [ + CryptoCurrency.usdt, + CryptoCurrency.xhv, + CryptoCurrency.btt, + CryptoCurrency.firo, + CryptoCurrency.zaddr, + CryptoCurrency.xvg, + CryptoCurrency.kmd, + CryptoCurrency.paxg, + CryptoCurrency.rune, + CryptoCurrency.scrt, + CryptoCurrency.btcln, + CryptoCurrency.cro, + CryptoCurrency.ftm, + CryptoCurrency.frax, + CryptoCurrency.gusd, + CryptoCurrency.gtc, + CryptoCurrency.weth, + ]; + + static List _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))) + .expand((i) => i) + .toList(); + } + + @override + String get title => 'Exolix'; + + @override + bool get isAvailable => true; + + @override + bool get isEnabled => true; + + @override + bool get supportsFixedRate => true; + + @override + ExchangeProviderDescription get description => ExchangeProviderDescription.exolix; + + @override + Future checkIsAvailable() async => true; + + static String getRateType(bool isFixedRate) => isFixedRate ? 'fixed' : 'float'; + + @override + Future fetchLimits( + {required CryptoCurrency from, + required CryptoCurrency to, + required bool isFixedRateMode}) async { + final params = { + 'rateType': getRateType(isFixedRateMode), + 'amount': '1', + }; + if (isFixedRateMode) { + params['coinFrom'] = _normalizeCurrency(to); + params['coinTo'] = _normalizeCurrency(from); + params['networkFrom'] = _networkFor(to); + params['networkTo'] = _networkFor(from); + } else { + params['coinFrom'] = _normalizeCurrency(from); + params['coinTo'] = _normalizeCurrency(to); + params['networkFrom'] = _networkFor(from); + params['networkTo'] = _networkFor(to); + } + final uri = Uri.https(apiBaseUrl, ratePath, params); + final response = await get(uri); + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as Map; + return Limits(min: responseJSON['minAmount'] as double?); + } + + @override + Future createTrade({required TradeRequest request, required bool isFixedRateMode}) async { + final _request = request as ExolixRequest; + + final headers = {'Content-Type': 'application/json'}; + final body = { + 'coinFrom': _normalizeCurrency(_request.from), + 'coinTo': _normalizeCurrency(_request.to), + 'networkFrom': _networkFor(_request.from), + 'networkTo': _networkFor(_request.to), + 'withdrawalAddress': _request.address, + 'refundAddress': _request.refundAddress, + 'rateType': getRateType(isFixedRateMode), + 'apiToken': apiKey, + }; + + if (isFixedRateMode) { + body['withdrawalAmount'] = _request.toAmount; + } else { + body['amount'] = _request.fromAmount; + } + + final uri = Uri.https(apiBaseUrl, transactionsPath); + final response = await post(uri, headers: headers, body: json.encode(body)); + + if (response.statusCode == 400) { + final responseJSON = json.decode(response.body) as Map; + final errors = responseJSON['errors'] as Map; + final errorMessage = errors.values.join(', '); + throw Exception(errorMessage); + } + + if (response.statusCode != 200 && response.statusCode != 201) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as Map; + final id = responseJSON['id'] as String; + final inputAddress = responseJSON['depositAddress'] as String; + final refundAddress = responseJSON['refundAddress'] as String?; + final extraId = responseJSON['depositExtraId'] as String?; + final payoutAddress = responseJSON['withdrawalAddress'] as String; + final amount = responseJSON['amount'].toString(); + + return Trade( + id: id, + from: _request.from, + to: _request.to, + provider: description, + inputAddress: inputAddress, + refundAddress: refundAddress, + extraId: extraId, + createdAt: DateTime.now(), + amount: amount, + state: TradeState.created, + payoutAddress: payoutAddress); + } + + @override + Future findTradeById({required String id}) async { + final findTradeByIdPath = transactionsPath + '/$id'; + final uri = Uri.https(apiBaseUrl, findTradeByIdPath); + final response = await get(uri); + + if (response.statusCode == 404) { + throw TradeNotFoundException(id, provider: description); + } + + if (response.statusCode == 400) { + final responseJSON = json.decode(response.body) as Map; + final errors = responseJSON['errors'] as Map; + final errorMessage = errors.values.join(', '); + + throw TradeNotFoundException(id, provider: description, description: errorMessage); + } + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as Map; + final coinFrom = responseJSON['coinFrom']['coinCode'] as String; + final from = CryptoCurrency.fromString(coinFrom); + final coinTo = responseJSON['coinTo']['coinCode'] as String; + final to = CryptoCurrency.fromString(coinTo); + final inputAddress = responseJSON['depositAddress'] as String; + final amount = responseJSON['amount'].toString(); + final status = responseJSON['status'] as String; + final state = TradeState.deserialize(raw: _prepareStatus(status)); + final extraId = responseJSON['depositExtraId'] as String?; + final outputTransaction = responseJSON['hashOut']['hash'] as String?; + final payoutAddress = responseJSON['withdrawalAddress'] as String; + + return Trade( + id: id, + from: from, + to: to, + provider: description, + inputAddress: inputAddress, + amount: amount, + state: state, + extraId: extraId, + outputTransaction: outputTransaction, + payoutAddress: payoutAddress); + } + + @override + Future fetchRate( + {required CryptoCurrency from, + required CryptoCurrency to, + required double amount, + required bool isFixedRateMode, + required bool isReceiveAmount}) async { + try { + if (amount == 0) { + return 0.0; + } + + final params = { + 'coinFrom': _normalizeCurrency(from), + 'coinTo': _normalizeCurrency(to), + 'networkFrom': _networkFor(from), + 'networkTo': _networkFor(to), + 'rateType': getRateType(isFixedRateMode), + }; + + if (isReceiveAmount) { + params['withdrawalAmount'] = amount.toString(); + } else { + params['amount'] = amount.toString(); + } + + final uri = Uri.https(apiBaseUrl, ratePath, params); + final response = await get(uri); + final responseJSON = json.decode(response.body) as Map; + + if (response.statusCode != 200) { + final message = responseJSON['message'] as String?; + throw Exception(message); + } + + final rate = responseJSON['rate'] as double; + + return rate; + } catch (e) { + print(e.toString()); + return 0.0; + } + } + + String _prepareStatus(String status) { + switch (status) { + case 'deleted': + case 'error': + return 'overdue'; + default: + return status; + } + } + + String _networkFor(CryptoCurrency currency) { + switch (currency) { + case CryptoCurrency.arb: + return 'ARBITRUM'; + default: + return currency.tag != null ? _normalizeTag(currency.tag!) : currency.title; + } + } + + String _normalizeCurrency(CryptoCurrency currency) { + switch (currency) { + case CryptoCurrency.nano: + return 'XNO'; + case CryptoCurrency.bttc: + return 'BTT'; + case CryptoCurrency.zec: + return 'ZEC'; + default: + return currency.title; + } + } + + String _normalizeTag(String tag) { + switch (tag) { + case 'POLY': + return 'Polygon'; + default: + return tag; + } + } +} diff --git a/lib/exchange/exolix/exolix_request.dart b/lib/exchange/exolix/exolix_request.dart new file mode 100644 index 000000000..e97ffa386 --- /dev/null +++ b/lib/exchange/exolix/exolix_request.dart @@ -0,0 +1,20 @@ +import 'package:flutter/foundation.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cake_wallet/exchange/trade_request.dart'; + +class ExolixRequest extends TradeRequest { + ExolixRequest( + {required this.from, + required this.to, + required this.address, + required this.fromAmount, + required this.toAmount, + required this.refundAddress}); + + CryptoCurrency from; + CryptoCurrency to; + String address; + String fromAmount; + String toAmount; + String refundAddress; +} diff --git a/lib/exchange/trade_state.dart b/lib/exchange/trade_state.dart index 98737339c..ebf74ce7a 100644 --- a/lib/exchange/trade_state.dart +++ b/lib/exchange/trade_state.dart @@ -35,6 +35,15 @@ class TradeState extends EnumerableItem with Serializable { static const completed = TradeState(raw: 'completed', title: 'Completed'); static const settling = TradeState(raw: 'settling', title: 'Settlement in progress'); static const settled = TradeState(raw: 'settled', title: 'Settlement completed'); + static const wait = TradeState(raw: 'wait', title: 'Waiting'); + static const overdue = TradeState(raw: 'overdue', title: 'Overdue'); + static const refund = TradeState(raw: 'refund', title: 'Refund'); + static const refunded = TradeState(raw: 'refunded', title: 'Refunded'); + static const confirmation = TradeState(raw: 'confirmation', title: 'Confirmation'); + static const confirmed = TradeState(raw: 'confirmed', title: 'Confirmed'); + static const exchanging = TradeState(raw: 'exchanging', title: 'Exchanging'); + static const sending = TradeState(raw: 'sending', title: 'Sending'); + static const success = TradeState(raw: 'success', title: 'Success'); static TradeState deserialize({required String raw}) { switch (raw) { case 'pending': @@ -77,6 +86,24 @@ class TradeState extends EnumerableItem with Serializable { return failed; case 'completed': return completed; + case 'wait': + return wait; + case 'overdue': + return overdue; + case 'refund': + return refund; + case 'refunded': + return refunded; + case 'confirmation': + return confirmation; + case 'confirmed': + return confirmed; + case 'exchanging': + return exchanging; + case 'sending': + return sending; + case 'success': + return success; default: throw Exception('Unexpected token: $raw in TradeState deserialize'); } diff --git a/lib/src/screens/dashboard/widgets/trade_row.dart b/lib/src/screens/dashboard/widgets/trade_row.dart index a42593f24..7f570b98e 100644 --- a/lib/src/screens/dashboard/widgets/trade_row.dart +++ b/lib/src/screens/dashboard/widgets/trade_row.dart @@ -94,6 +94,9 @@ class TradeRow extends StatelessWidget { borderRadius: BorderRadius.circular(50), child: Image.asset('assets/images/trocador.png', width: 36, height: 36)); break; + case ExchangeProviderDescription.exolix: + image = Image.asset('assets/images/exolix.png', width: 36, height: 36); + break; default: image = null; } diff --git a/lib/store/dashboard/trade_filter_store.dart b/lib/store/dashboard/trade_filter_store.dart index c772a35d6..799e8b951 100644 --- a/lib/store/dashboard/trade_filter_store.dart +++ b/lib/store/dashboard/trade_filter_store.dart @@ -13,7 +13,8 @@ abstract class TradeFilterStoreBase with Store { displaySideShift = true, displayMorphToken = true, displaySimpleSwap = true, - displayTrocador = true; + displayTrocador = true, + displayExolix = true; @observable bool displayXMRTO; @@ -33,8 +34,11 @@ abstract class TradeFilterStoreBase with Store { @observable bool displayTrocador; + @observable + bool displayExolix; + @computed - bool get displayAllTrades => displayChangeNow && displaySideShift && displaySimpleSwap && displayTrocador; + bool get displayAllTrades => displayChangeNow && displaySideShift && displaySimpleSwap && displayTrocador && displayExolix; @action void toggleDisplayExchange(ExchangeProviderDescription provider) { @@ -56,7 +60,10 @@ abstract class TradeFilterStoreBase with Store { break; case ExchangeProviderDescription.trocador: displayTrocador = !displayTrocador; - break; + break; + case ExchangeProviderDescription.exolix: + displayExolix = !displayExolix; + break; case ExchangeProviderDescription.all: if (displayAllTrades) { displayChangeNow = false; @@ -65,6 +72,7 @@ abstract class TradeFilterStoreBase with Store { displayMorphToken = false; displaySimpleSwap = false; displayTrocador = false; + displayExolix = false; } else { displayChangeNow = true; displaySideShift = true; @@ -72,6 +80,7 @@ abstract class TradeFilterStoreBase with Store { displayMorphToken = true; displaySimpleSwap = true; displayTrocador = true; + displayExolix = true; } break; } @@ -98,7 +107,8 @@ abstract class TradeFilterStoreBase with Store { ||(displaySimpleSwap && item.trade.provider == ExchangeProviderDescription.simpleSwap) - ||(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador)) + ||(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador) + ||(displayExolix && item.trade.provider == ExchangeProviderDescription.exolix)) .toList() : _trades; } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 9998eb8be..777db8f8d 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -99,6 +99,11 @@ abstract class DashboardViewModelBase with Store { caption: ExchangeProviderDescription.trocador.title, onChanged: () => tradeFilterStore .toggleDisplayExchange(ExchangeProviderDescription.trocador)), + FilterItem( + value: () => tradeFilterStore.displayExolix, + caption: ExchangeProviderDescription.exolix.title, + onChanged: () => tradeFilterStore + .toggleDisplayExchange(ExchangeProviderDescription.exolix)), ] }, subname = '', diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart index cfabd994f..346844171 100644 --- a/lib/view_model/exchange/exchange_trade_view_model.dart +++ b/lib/view_model/exchange/exchange_trade_view_model.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:cake_wallet/exchange/exolix/exolix_exchange_provider.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart'; @@ -53,6 +54,9 @@ abstract class ExchangeTradeViewModelBase with Store { case ExchangeProviderDescription.trocador: _provider = TrocadorExchangeProvider(); break; + case ExchangeProviderDescription.exolix: + _provider = ExolixExchangeProvider(); + break; } _updateItems(); diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 2e90a3a33..bd370cc54 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -6,6 +6,8 @@ import 'package:cake_wallet/core/wallet_change_listener_view_model.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/wallet_contact.dart'; +import 'package:cake_wallet/exchange/exolix/exolix_exchange_provider.dart'; +import 'package:cake_wallet/exchange/exolix/exolix_request.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; @@ -151,6 +153,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with SideShiftExchangeProvider(), SimpleSwapExchangeProvider(), TrocadorExchangeProvider(useTorOnly: _useTorOnly), + ExolixExchangeProvider(), ]; @observable @@ -547,6 +550,17 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with amount = isFixedRateMode ? receiveAmount : depositAmount; } + if (provider is ExolixExchangeProvider) { + request = ExolixRequest( + from: depositCurrency, + to: receiveCurrency, + fromAmount: depositAmount.replaceAll(',', '.'), + toAmount: receiveAmount.replaceAll(',', '.'), + refundAddress: depositAddress, + address: receiveAddress); + amount = isFixedRateMode ? receiveAmount : depositAmount; + } + amount = amount.replaceAll(',', '.'); if (limitsState is LimitsLoadedSuccessfully) { diff --git a/lib/view_model/support_view_model.dart b/lib/view_model/support_view_model.dart index d3b14c59b..ccef76154 100644 --- a/lib/view_model/support_view_model.dart +++ b/lib/view_model/support_view_model.dart @@ -53,6 +53,11 @@ abstract class SupportViewModelBase with Store { icon: 'assets/images/simpleSwap.png', linkTitle: 'support@simpleswap.io', link: 'mailto:support@simpleswap.io'), + LinkListItem( + title: 'Exolix', + icon: 'assets/images/exolix.png', + linkTitle: 'support@exolix.com', + link: 'mailto:support@exolix.com'), if (!isMoneroOnly) ... [ LinkListItem( title: 'Wyre', diff --git a/lib/view_model/trade_details_view_model.dart b/lib/view_model/trade_details_view_model.dart index c0b1ac461..393629237 100644 --- a/lib/view_model/trade_details_view_model.dart +++ b/lib/view_model/trade_details_view_model.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart'; import 'package:cake_wallet/exchange/exchange_provider.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart'; +import 'package:cake_wallet/exchange/exolix/exolix_exchange_provider.dart'; import 'package:cake_wallet/exchange/morphtoken/morphtoken_exchange_provider.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; @@ -54,6 +55,9 @@ abstract class TradeDetailsViewModelBase with Store { case ExchangeProviderDescription.trocador: _provider = TrocadorExchangeProvider(); break; + case ExchangeProviderDescription.exolix: + _provider = ExolixExchangeProvider(); + break; } _updateItems(); @@ -157,6 +161,12 @@ abstract class TradeDetailsViewModelBase with Store { items.add(StandartListItem( title: '${trade.providerName} ${S.current.password}', value: trade.password ?? '')); } + + if (trade.provider == ExchangeProviderDescription.exolix) { + final buildURL = 'https://exolix.com/transaction/${trade.id.toString()}'; + items.add( + TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => _launchUrl(buildURL))); + } } void _launchUrl(String url) { diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index 8c86e1b7f..5e959b99b 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -32,6 +32,7 @@ class SecretKey { SecretKey('fiatApiKey', () => ''), SecretKey('payfuraApiKey', () => ''), SecretKey('chatwootWebsiteToken', () => ''), + SecretKey('exolixApiKey', () => ''), SecretKey('robinhoodApplicationId', () => ''), SecretKey('robinhoodCIdApiSecret', () => ''), ]; From dc36c31197485aff33980d66583cce366aca2cb9 Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Thu, 28 Sep 2023 19:49:46 +0200 Subject: [PATCH 3/6] CW-489 Skip Warning if all monero utxo are selected (#1106) --- cw_monero/lib/monero_wallet.dart | 169 +++++++++++++------------------ 1 file changed, 72 insertions(+), 97 deletions(-) diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index f9b3c1997..75c1df89e 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -37,10 +37,10 @@ const moneroBlockSize = 1000; class MoneroWallet = MoneroWalletBase with _$MoneroWallet; -abstract class MoneroWalletBase extends WalletBase with Store { - MoneroWalletBase({required WalletInfo walletInfo, - required Box unspentCoinsInfo}) +abstract class MoneroWalletBase + extends WalletBase with Store { + MoneroWalletBase( + {required WalletInfo walletInfo, required Box unspentCoinsInfo}) : balance = ObservableMap.of({ CryptoCurrency.xmr: MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: 0), @@ -112,12 +112,12 @@ abstract class MoneroWalletBase extends WalletBase init() async { await walletAddresses.init(); - balance = ObservableMap.of( - { - currency: MoneroBalance( - fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id), - unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id)) - }); + balance = ObservableMap.of({ + currency: MoneroBalance( + fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id), + unlockedBalance: + monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id)) + }); _setListeners(); await updateTransactions(); @@ -125,15 +125,14 @@ abstract class MoneroWalletBase extends WalletBase await save()); + _autoSaveTimer = + Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save()); } + @override Future? updateBalance() => null; @@ -153,7 +152,8 @@ abstract class MoneroWalletBase extends WalletBase 1; final unlockedBalance = - monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); + monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); var allInputsAmount = 0; PendingTransactionDescription pendingTransactionDescription; @@ -208,56 +208,42 @@ abstract class MoneroWalletBase extends WalletBase item.sendAll - || (item.formattedCryptoAmount ?? 0) <= 0)) { + if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) { throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.'); } - final int totalAmount = outputs.fold(0, (acc, value) => - acc + (value.formattedCryptoAmount ?? 0)); + final int totalAmount = + outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)); final estimatedFee = calculateEstimatedFee(_credentials.priority, totalAmount); if (unlockedBalance < totalAmount) { throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.'); } - if (allInputsAmount < totalAmount + estimatedFee) { + if (!spendAllCoins && (allInputsAmount < totalAmount + estimatedFee)) { throw MoneroTransactionNoInputsException(inputs.length); } final moneroOutputs = outputs.map((output) { - final outputAddress = output.isParsedAddress - ? output.extractedAddress - : output.address; + final outputAddress = output.isParsedAddress ? output.extractedAddress : output.address; - return MoneroOutput( - address: outputAddress!, - amount: output.cryptoAmount!.replaceAll(',', '.')); + return MoneroOutput( + address: outputAddress!, amount: output.cryptoAmount!.replaceAll(',', '.')); }).toList(); - pendingTransactionDescription = - await transaction_history.createTransactionMultDest( + pendingTransactionDescription = await transaction_history.createTransactionMultDest( outputs: moneroOutputs, priorityRaw: _credentials.priority.serialize(), accountIndex: walletAddresses.account!.id, preferredInputs: inputs); } else { final output = outputs.first; - final address = output.isParsedAddress - ? output.extractedAddress - : output.address; - final amount = output.sendAll - ? null - : output.cryptoAmount!.replaceAll(',', '.'); - final formattedAmount = output.sendAll - ? null - : output.formattedCryptoAmount; + final address = output.isParsedAddress ? output.extractedAddress : output.address; + final amount = output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.'); + final formattedAmount = output.sendAll ? null : output.formattedCryptoAmount; if ((formattedAmount != null && unlockedBalance < formattedAmount) || (formattedAmount == null && unlockedBalance <= 0)) { @@ -268,8 +254,9 @@ abstract class MoneroWalletBase extends WalletBase - element.walletId.contains(id) && element.hash.contains(coin.hash)); + final coinInfoList = unspentCoinsInfo.values + .where((element) => element.walletId.contains(id) && element.hash.contains(coin.hash)); if (coinInfoList.isNotEmpty) { final coinInfo = coinInfoList.first; @@ -447,16 +430,15 @@ abstract class MoneroWalletBase extends WalletBase _addCoinInfo(MoneroUnspent coin) async { final newInfo = UnspentCoinsInfo( - walletId: id, - hash: coin.hash, - isFrozen: coin.isFrozen, - isSending: coin.isSending, - noteRaw: coin.note, - address: coin.address, - value: coin.value, - vout: 0, - keyImage: coin.keyImage - ); + walletId: id, + hash: coin.hash, + isFrozen: coin.isFrozen, + isSending: coin.isSending, + noteRaw: coin.note, + address: coin.address, + value: coin.value, + vout: 0, + keyImage: coin.keyImage); await unspentCoinsInfo.add(newInfo); } @@ -464,8 +446,8 @@ abstract class MoneroWalletBase extends WalletBase _refreshUnspentCoinsInfo() async { try { final List keys = []; - final currentWalletUnspentCoins = unspentCoinsInfo.values - .where((element) => element.walletId.contains(id)); + final currentWalletUnspentCoins = + unspentCoinsInfo.values.where((element) => element.walletId.contains(id)); if (currentWalletUnspentCoins.isNotEmpty) { currentWalletUnspentCoins.forEach((element) { @@ -486,16 +468,14 @@ abstract class MoneroWalletBase extends WalletBase - monero_wallet.getAddress( - accountIndex: accountIndex, - addressIndex: addressIndex); + monero_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex); @override Future> fetchTransactions() async { transaction_history.refreshTransactions(); - return _getAllTransactionsOfAccount(walletAddresses.account?.id).fold>( - {}, - (Map acc, MoneroTransactionInfo tx) { + return _getAllTransactionsOfAccount(walletAddresses.account?.id) + .fold>({}, + (Map acc, MoneroTransactionInfo tx) { acc[tx.id] = tx; return acc; }); @@ -523,12 +503,11 @@ abstract class MoneroWalletBase extends WalletBase _getAllTransactionsOfAccount(int? accountIndex) => - transaction_history - .getAllTransactions() - .map((row) => MoneroTransactionInfo.fromRow(row)) - .where((element) => element.accountIndex == (accountIndex ?? 0)) - .toList(); + List _getAllTransactionsOfAccount(int? accountIndex) => transaction_history + .getAllTransactions() + .map((row) => MoneroTransactionInfo.fromRow(row)) + .where((element) => element.accountIndex == (accountIndex ?? 0)) + .toList(); void _setListeners() { _listener?.stop(); @@ -550,8 +529,7 @@ abstract class MoneroWalletBase extends WalletBase _askForUpdateTransactionHistory() async => - await updateTransactions(); + Future _askForUpdateTransactionHistory() async => await updateTransactions(); - int _getFullBalance() => - monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id); + int _getFullBalance() => monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id); int _getUnlockedBalance() => monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); @@ -595,8 +571,7 @@ abstract class MoneroWalletBase extends WalletBase Date: Thu, 28 Sep 2023 19:40:15 +0100 Subject: [PATCH 4/6] fix: Cake-2FA-setup-issue (#1097) * fix: Cake 2FA setup issue * fix: 2FA Setup issue * fix: 2FA setup bug --- lib/di.dart | 2 +- lib/entities/preferences_key.dart | 2 +- lib/src/screens/setup_2fa/setup_2fa.dart | 6 ++-- .../screens/setup_2fa/setup_2fa_qr_page.dart | 4 +-- lib/store/settings_store.dart | 21 +++++--------- lib/view_model/set_up_2fa_viewmodel.dart | 29 ++++++++++--------- 6 files changed, 30 insertions(+), 34 deletions(-) diff --git a/lib/di.dart b/lib/di.dart index a308b989a..97dd392c5 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -360,7 +360,7 @@ Future setup({ (onAuthFinished, closable) => AuthPage(getIt.get(), onAuthenticationFinished: onAuthFinished, closable: closable)); - getIt.registerFactory( + getIt.registerLazySingleton( () => Setup2FAViewModel( getIt.get(), getIt.get(), diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index f699f89f8..d32dcbca4 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -19,7 +19,6 @@ class PreferencesKey { 'allow_biometrical_authentication'; static const useTOTP2FA = 'use_totp_2fa'; static const failedTotpTokenTrials = 'failed_token_trials'; - static const totpSecretKey = 'totp_qr_secret_key'; static const disableExchangeKey = 'disable_exchange'; static const exchangeStatusKey = 'exchange_status'; static const currentTheme = 'current_theme'; @@ -75,4 +74,5 @@ class PreferencesKey { static const shouldRequireTOTP2FAForAllSecurityAndBackupSettings = 'should_require_totp_2fa_for_all_security_and_backup_settings'; static const selectedCake2FAPreset = 'selected_cake_2fa_preset'; + static const totpSecretKey = 'totp_secret_key'; } diff --git a/lib/src/screens/setup_2fa/setup_2fa.dart b/lib/src/screens/setup_2fa/setup_2fa.dart index a74152e4f..895fbb9c0 100644 --- a/lib/src/screens/setup_2fa/setup_2fa.dart +++ b/lib/src/screens/setup_2fa/setup_2fa.dart @@ -53,8 +53,10 @@ class Setup2FAPage extends BasePage { SizedBox(height: 86), SettingsCellWithArrow( title: S.current.setup_totp_recommended, - handler: (_) => Navigator.of(context) - .pushReplacementNamed(Routes.setup_2faQRPage), + handler: (_) { + setup2FAViewModel.generateSecretKey(); + return Navigator.of(context).pushReplacementNamed(Routes.setup_2faQRPage); + }, ), StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), ], diff --git a/lib/src/screens/setup_2fa/setup_2fa_qr_page.dart b/lib/src/screens/setup_2fa/setup_2fa_qr_page.dart index e9ab850af..43dbab05f 100644 --- a/lib/src/screens/setup_2fa/setup_2fa_qr_page.dart +++ b/lib/src/screens/setup_2fa/setup_2fa_qr_page.dart @@ -89,7 +89,7 @@ class Setup2FAQRPage extends BasePage { ), SizedBox(height: 8), Text( - '${setup2FAViewModel.secretKey}', + '${setup2FAViewModel.totpSecretKey}', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, @@ -108,7 +108,7 @@ class Setup2FAQRPage extends BasePage { child: InkWell( onTap: () { ClipboardUtil.setSensitiveDataToClipboard( - ClipboardData(text: '${setup2FAViewModel.secretKey}')); + ClipboardData(text: '${setup2FAViewModel.totpSecretKey}')); showBar(context, S.of(context).copied_to_clipboard); }, child: Container( diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 2690dcb64..54607815d 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -280,14 +280,13 @@ abstract class SettingsStoreBase with Store { reaction( (_) => useTOTP2FA, (bool use) => sharedPreferences.setBool(PreferencesKey.useTOTP2FA, use)); + reaction((_) => totpSecretKey, + (String totpKey) => sharedPreferences.setString(PreferencesKey.totpSecretKey, totpKey)); reaction( (_) => numberOfFailedTokenTrials, (int failedTokenTrail) => sharedPreferences.setInt(PreferencesKey.failedTotpTokenTrials, failedTokenTrail)); - reaction((_) => totpSecretKey, - (String totpKey) => sharedPreferences.setString(PreferencesKey.totpSecretKey, totpKey)); - reaction( (_) => shouldShowMarketPlaceInDashboard, (bool value) => @@ -422,15 +421,10 @@ abstract class SettingsStoreBase with Store { bool shouldRequireTOTP2FAForAllSecurityAndBackupSettings; @observable - String totpSecretKey; - - @computed - String get totpVersionOneLink { - return 'otpauth://totp/Cake%20Wallet:$deviceName?secret=$totpSecretKey&issuer=Cake%20Wallet&algorithm=SHA512&digits=8&period=30'; - } + bool useTOTP2FA; @observable - bool useTOTP2FA; + String totpSecretKey; @observable int numberOfFailedTokenTrials; @@ -575,8 +569,8 @@ abstract class SettingsStoreBase with Store { final shouldRequireTOTP2FAForAllSecurityAndBackupSettings = sharedPreferences .getBool(PreferencesKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings) ?? false; - final totpSecretKey = sharedPreferences.getString(PreferencesKey.totpSecretKey) ?? ''; final useTOTP2FA = sharedPreferences.getBool(PreferencesKey.useTOTP2FA) ?? false; + final totpSecretKey = sharedPreferences.getString(PreferencesKey.totpSecretKey) ?? ''; final tokenTrialNumber = sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? 0; final shouldShowMarketPlaceInDashboard = sharedPreferences.getBool(PreferencesKey.shouldShowMarketPlaceInDashboard) ?? true; @@ -677,8 +671,8 @@ abstract class SettingsStoreBase with Store { initialFiatMode: currentFiatApiMode, initialAllowBiometricalAuthentication: allowBiometricalAuthentication, initialCake2FAPresetOptions: selectedCake2FAPreset, - initialTotpSecretKey: totpSecretKey, initialUseTOTP2FA: useTOTP2FA, + initialTotpSecretKey: totpSecretKey, initialFailedTokenTrial: tokenTrialNumber, initialExchangeStatus: exchangeStatus, initialTheme: savedTheme, @@ -752,9 +746,8 @@ abstract class SettingsStoreBase with Store { shouldSaveRecipientAddress = sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? shouldSaveRecipientAddress; - totpSecretKey = sharedPreferences.getString(PreferencesKey.totpSecretKey) ?? totpSecretKey; useTOTP2FA = sharedPreferences.getBool(PreferencesKey.useTOTP2FA) ?? useTOTP2FA; - + totpSecretKey = sharedPreferences.getString(PreferencesKey.totpSecretKey) ?? totpSecretKey; numberOfFailedTokenTrials = sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials; isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure; diff --git a/lib/view_model/set_up_2fa_viewmodel.dart b/lib/view_model/set_up_2fa_viewmodel.dart index 0b4b614ab..eacd3128d 100644 --- a/lib/view_model/set_up_2fa_viewmodel.dart +++ b/lib/view_model/set_up_2fa_viewmodel.dart @@ -27,7 +27,6 @@ abstract class Setup2FAViewModelBase with Store { unhighlightTabs = false, selected2FASettings = ObservableList(), state = InitialExecutionState() { - _getRandomBase32SecretKey(); selectCakePreset(selectedCake2FAPreset); reaction((_) => state, _saveLastAuthTime); } @@ -36,9 +35,12 @@ abstract class Setup2FAViewModelBase with Store { static const banTimeout = 180; // 3 minutes final banTimeoutKey = S.current.auth_store_ban_timeout; - String get secretKey => _settingsStore.totpSecretKey; String get deviceName => _settingsStore.deviceName; - String get totpVersionOneLink => _settingsStore.totpVersionOneLink; + + @computed + String get totpSecretKey => _settingsStore.totpSecretKey; + + String totpVersionOneLink = ''; @observable ExecutionState state; @@ -84,9 +86,14 @@ abstract class Setup2FAViewModelBase with Store { bool get shouldRequireTOTP2FAForAllSecurityAndBackupSettings => _settingsStore.shouldRequireTOTP2FAForAllSecurityAndBackupSettings; - void _getRandomBase32SecretKey() { - final randomBase32Key = Utils.generateRandomBase32SecretKey(16); - _setBase32SecretKey(randomBase32Key); + @action + void generateSecretKey() { + final _totpSecretKey = Utils.generateRandomBase32SecretKey(16); + + totpVersionOneLink = + 'otpauth://totp/Cake%20Wallet:$deviceName?secret=$_totpSecretKey&issuer=Cake%20Wallet&algorithm=SHA512&digits=8&period=30'; + + setTOTPSecretKey(_totpSecretKey); } @action @@ -95,15 +102,10 @@ abstract class Setup2FAViewModelBase with Store { } @action - void _setBase32SecretKey(String value) { + void setTOTPSecretKey(String value) { _settingsStore.totpSecretKey = value; } - @action - void clearBase32SecretKey() { - _settingsStore.totpSecretKey = ''; - } - Duration? banDuration() { final unbanTimestamp = _sharedPreferences.getInt(banTimeoutKey); @@ -145,7 +147,7 @@ abstract class Setup2FAViewModelBase with Store { } final result = Utils.verify( - secretKey: secretKey, + secretKey: totpSecretKey, otp: otpText, ); @@ -156,7 +158,6 @@ abstract class Setup2FAViewModelBase with Store { } else { final value = _settingsStore.numberOfFailedTokenTrials + 1; adjustTokenTrialNumber(value); - print(value); if (_failureCounter >= maxFailedTrials) { final banDuration = await ban(); state = AuthenticationBanned( From b1859b9e6385f3bed049c43e587880e0b05634ff Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Fri, 29 Sep 2023 01:53:53 +0300 Subject: [PATCH 5/6] Generic Fixes (#1103) * Update trade_details_page.dart * let web3dart calculate gas price and max gas while just providing the priority fee per gas needed --- cw_ethereum/lib/ethereum_client.dart | 6 ++---- lib/src/screens/trade_details/trade_details_page.dart | 5 +++-- lib/utils/exception_handler.dart | 1 + 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cw_ethereum/lib/ethereum_client.dart b/cw_ethereum/lib/ethereum_client.dart index e10e79f1e..c2639827f 100644 --- a/cw_ethereum/lib/ethereum_client.dart +++ b/cw_ethereum/lib/ethereum_client.dart @@ -65,13 +65,11 @@ class EthereumClient { bool _isEthereum = currency == CryptoCurrency.eth; - final price = await _client!.getGasPrice(); + final price = _client!.getGasPrice(); final Transaction transaction = Transaction( from: privateKey.address, to: EthereumAddress.fromHex(toAddress), - maxGas: gas, - gasPrice: price, maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip), value: _isEthereum ? EtherAmount.inWei(BigInt.parse(amount)) : EtherAmount.zero(), ); @@ -101,7 +99,7 @@ class EthereumClient { return PendingEthereumTransaction( signedTransaction: signedTransaction, amount: amount, - fee: BigInt.from(gas) * price.getInWei, + fee: BigInt.from(gas) * (await price).getInWei, sendTransaction: _sendTransaction, exponent: exponent, ); diff --git a/lib/src/screens/trade_details/trade_details_page.dart b/lib/src/screens/trade_details/trade_details_page.dart index 17683c600..1bb8872e8 100644 --- a/lib/src/screens/trade_details/trade_details_page.dart +++ b/lib/src/screens/trade_details/trade_details_page.dart @@ -51,10 +51,11 @@ class TradeDetailsPageBodyState extends State { @override Widget build(BuildContext context) { return Observer(builder: (_) { - // FIX-ME: Added `context` it was not used here before, maby bug ? + int itemsCount = tradeDetailsViewModel.items.length; + return SectionStandardList( sectionCount: 1, - itemCounter: (int _) => tradeDetailsViewModel.items.length, + itemCounter: (int _) => itemsCount, itemBuilder: (__, index) { final item = tradeDetailsViewModel.items[index]; diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index 5c51a8757..b9c659872 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -161,6 +161,7 @@ class ExceptionHandler { "Handshake error in client", "Error while launching http", "OS Error: Network is unreachable", + "ClientException: Write failed, uri=https:", ]; static Future _addDeviceInfo(File file) async { From ae71f2045b00662d6457c803083cdc531c38afec Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Fri, 29 Sep 2023 20:28:26 +0300 Subject: [PATCH 6/6] New versions v4.9.2 and v1.6.2 (#1108) --- assets/text/Monerocom_Release_Notes.txt | 6 +++--- assets/text/Release_Notes.txt | 9 ++++----- scripts/android/app_env.sh | 8 ++++---- scripts/ios/app_env.sh | 8 ++++---- scripts/macos/app_env.sh | 4 ++-- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/assets/text/Monerocom_Release_Notes.txt b/assets/text/Monerocom_Release_Notes.txt index cbe201cf8..9393f7768 100644 --- a/assets/text/Monerocom_Release_Notes.txt +++ b/assets/text/Monerocom_Release_Notes.txt @@ -1,3 +1,3 @@ -Enhance Monero coin control -Add Filipino localization -Bug Fixes \ No newline at end of file +Fix 2FA code issue +Bug fixes +Minor enhancements \ No newline at end of file diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt index 263e7ccfe..1fd86c9ca 100644 --- a/assets/text/Release_Notes.txt +++ b/assets/text/Release_Notes.txt @@ -1,5 +1,4 @@ -New Buy Provider Robinhood -Fix sending Ethereum issue -Enhance Monero coin control -Add Filipino localization -Bug Fixes \ No newline at end of file +Ethereum enhancements and bug fixes +Fix 2FA code issue +Bug fixes +Minor enhancements \ No newline at end of file diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index dd8c83948..92c8a0559 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.6.1" -MONERO_COM_BUILD_NUMBER=57 +MONERO_COM_VERSION="1.6.2" +MONERO_COM_BUILD_NUMBER=58 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_SCHEME="monero.com" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.9.1" -CAKEWALLET_BUILD_NUMBER=170 +CAKEWALLET_VERSION="4.9.2" +CAKEWALLET_BUILD_NUMBER=171 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 714441167..078688918 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.6.1" -MONERO_COM_BUILD_NUMBER=55 +MONERO_COM_VERSION="1.6.2" +MONERO_COM_BUILD_NUMBER=56 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.9.1" -CAKEWALLET_BUILD_NUMBER=183 +CAKEWALLET_VERSION="4.9.2" +CAKEWALLET_BUILD_NUMBER=185 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index bb2aa2ebc..5103d42b2 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -15,8 +15,8 @@ if [ -n "$1" ]; then fi CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="1.2.1" -CAKEWALLET_BUILD_NUMBER=32 +CAKEWALLET_VERSION="1.2.2" +CAKEWALLET_BUILD_NUMBER=33 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then