From 24657c6c57de950ac458eee9aef15ffdc66ae928 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 18 Nov 2024 09:20:39 -0500 Subject: [PATCH 01/34] update translation for funding wallet on create offer --- core/src/main/resources/i18n/displayStrings_cs.properties | 7 ++++++- core/src/main/resources/i18n/displayStrings_de.properties | 7 ++++++- core/src/main/resources/i18n/displayStrings_es.properties | 7 ++++++- core/src/main/resources/i18n/displayStrings_fa.properties | 7 ++++++- core/src/main/resources/i18n/displayStrings_fr.properties | 7 ++++++- core/src/main/resources/i18n/displayStrings_it.properties | 7 ++++++- core/src/main/resources/i18n/displayStrings_ja.properties | 7 ++++++- .../main/resources/i18n/displayStrings_pt-br.properties | 7 ++++++- core/src/main/resources/i18n/displayStrings_pt.properties | 7 ++++++- core/src/main/resources/i18n/displayStrings_ru.properties | 7 ++++++- core/src/main/resources/i18n/displayStrings_th.properties | 7 ++++++- core/src/main/resources/i18n/displayStrings_tr.properties | 6 +++--- core/src/main/resources/i18n/displayStrings_vi.properties | 7 ++++++- .../main/resources/i18n/displayStrings_zh-hans.properties | 7 ++++++- .../main/resources/i18n/displayStrings_zh-hant.properties | 7 ++++++- 15 files changed, 87 insertions(+), 17 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index c47bb440..ada00d83 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -458,7 +458,12 @@ createOffer.placeOfferButton=Přehled: Umístěte nabídku {0} monero createOffer.createOfferFundWalletInfo.headline=Financujte svou nabídku # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Výše obchodu: {0}\n -createOffer.createOfferFundWalletInfo.msg=Do této nabídky musíte vložit {0}.\n\nTyto prostředky jsou rezervovány ve vaší lokální peněžence a budou uzamčeny na vkladové multisig adrese, jakmile někdo příjme vaši nabídku.\n\nČástka je součtem:\n{1}- Vaše kauce: {2}\n- Obchodní poplatek: {3}\n- Poplatek za těžbu: {4}\n\nPři financování obchodu si můžete vybrat ze dvou možností:\n- Použijte svou peněženku Haveno (pohodlné, ale transakce mohou být propojitelné) NEBO\n- Přenos z externí peněženky (potenciálně více soukromé)\n\nPo uzavření tohoto vyskakovacího okna se zobrazí všechny možnosti a podrobnosti financování. +createOffer.createOfferFundWalletInfo.msg=Potřebujete vložit {0} do této nabídky.\n\n\ + Tyto prostředky jsou rezervovány ve vaší místní peněžence a budou zablokovány v multisig peněžence, jakmile někdo přijme vaši nabídku.\n\n\ + Částka je součtem:\n\ + {1}\ + - Vaše záloha: {2}\n\ + - Poplatek za obchodování: {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=Při zadávání nabídky došlo k chybě:\n\n{0}\n\nPeněženku ještě neopustily žádné finanční prostředky.\nRestartujte aplikaci a zkontrolujte síťové připojení. diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 5e691f53..79f6704a 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -458,7 +458,12 @@ createOffer.placeOfferButton=Überprüfung: Anbieten moneros zu {0} createOffer.createOfferFundWalletInfo.headline=Ihr Angebot finanzieren # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Handelsbetrag: {0} \n -createOffer.createOfferFundWalletInfo.msg=Sie müssen zum Annehmen dieses Angebots {0} einzahlen.\n\nDiese Gelder werden in Ihrer lokalen Wallet reserviert und in die MultiSig-Kautionsadresse eingesperrt, wenn jemand Ihr Angebot annimmt.\n\nDer Betrag ist die Summe aus:\n{1}- Kaution: {2}\n- Handelsgebühr: {3}\n- Mining-Gebühr: {4}\n\nSie haben zwei Möglichkeiten, Ihren Handel zu finanzieren:\n- Nutzen Sie Ihre Haveno-Wallet (bequem, aber Transaktionen können nachverfolgbar sein) ODER\n- Von einer externen Wallet überweisen (möglicherweise vertraulicher)\n\nSie werden nach dem Schließen dieses Dialogs alle Finanzierungsmöglichkeiten und Details sehen. +createOffer.createOfferFundWalletInfo.msg=Sie müssen {0} in dieses Angebot einzahlen.\n\n\ + Diese Gelder werden in Ihrer lokalen Wallet reserviert und in eine Multisig-Wallet gesperrt, sobald jemand Ihr Angebot annimmt.\n\n\ + Der Betrag ist die Summe aus:\n\ + {1}\ + - Ihre Sicherheitskaution: {2}\n\ + - Handelsgebühr: {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=Es gab einen Fehler beim Erstellen des Angebots:\n\n{0}\n\nEs haben noch keine Gelder Ihre Wallet verlassen.\nBitte starten Sie Ihre Anwendung neu und überprüfen Sie Ihre Netzwerkverbindung. diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index 26be43ee..43a92fe7 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -458,7 +458,12 @@ createOffer.placeOfferButton=Revisar: Poner oferta para {0} monero createOffer.createOfferFundWalletInfo.headline=Dote de fondos su trato. # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Cantidad a intercambiar: {0}\n -createOffer.createOfferFundWalletInfo.msg=Necesita depositar {0} para completar esta oferta.\n\nEsos fondos son reservados en su cartera local y se bloquearán en la dirección de depósito multifirma una vez que alguien tome su oferta.\nLa cantidad es la suma de:\n{1}- Su depósito de seguridad: {2}\n- Comisión de intercambio: {3}\n- Comisión de minado: {4}\n\nPuede elegir entre dos opciones a la hora de depositar fondos para realizar su intercambio:\n- Usar su cartera Haveno (conveniente, pero las transacciones pueden ser trazables) O también\n- Transferir desde una cartera externa (potencialmente con mayor privacidad)\n\nConocerá todos los detalles y opciones para depositar fondos al cerrar esta ventana. +createOffer.createOfferFundWalletInfo.msg=Necesitas depositar {0} para esta oferta.\n\n\ + Estos fondos están reservados en tu billetera local y se bloquearán en una billetera multisig una vez que alguien acepte tu oferta.\n\n\ + El monto es la suma de:\n\ + {1}\ + - Tu depósito de seguridad: {2}\n\ + - Comisión de comercio: {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=Ocurrió un error al colocar la oferta:\n\n{0}\n\nNingún importe de su cartera ha sido deducido aún.\nPor favor, reinicie su aplicación y compruebe su conexión a la red. diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 171f3929..90613ad0 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -457,7 +457,12 @@ createOffer.placeOfferButton=بررسی: پیشنهاد را برای {0} بی createOffer.createOfferFundWalletInfo.headline=پیشنهاد خود را تامین وجه نمایید # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=مقدار معامله:{0}\n -createOffer.createOfferFundWalletInfo.msg=شما باید {0} برای این پیشنهاد، سپرده بگذارید.\nآن وجوه در کیف پول محلی شما ذخیره شده اند و هنگامی که کسی پیشنهاد شما را دریافت می کند، به آدرس سپرده چند امضایی قفل خواهد شد.\n\nمقدار مذکور، مجموع موارد ذیل است:\n{1} - سپرده‌ی اطمینان شما: {2}\n-هزینه معامله: {3}\n-هزینه تراکنش شبکه: {4}\nشما هنگام تامین مالی معامله‌ی خود، می‌توانید بین دو گزینه انتخاب کنید:\n- از کیف پول Haveno خود استفاده کنید (این روش راحت است، اما ممکن است تراکنش‌ها قابل رصد شوند)، یا\n- از کیف پول خارجی انتقال دهید (به طور بالقوه‌ای این روش ایمن‌تر و محافظ حریم خصوصی شما است)\n\nشما تمام گزینه‌ها و جزئیات تامین مالی را پس از بستن این پنجره، خواهید دید. +createOffer.createOfferFundWalletInfo.msg=شما باید {0} را برای این پیشنهاد واریز کنید.\n\n\ +این وجوه در کیف پول محلی شما رزرو می‌شوند و هنگامی که کسی پیشنهاد شما را قبول کند، به یک کیف پول مولتی‌سیگ قفل خواهند شد.\n\n\ +مقدار این مبلغ مجموع موارد زیر است:\n\ +{1}\ +- ودیعه امنیتی شما: {2}\n\ +- هزینه معامله: {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=یک خطا هنگام قرار دادن پیشنهاد، رخ داده است:\n\n{0}\n\nهیچ پولی تاکنون از کیف پول شما کم نشده است.\nلطفاً برنامه را مجدداً راه اندازی کرده و ارتباط اینترنت خود را بررسی نمایید. diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 1dfb4e2b..ab196d4e 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -458,7 +458,12 @@ createOffer.placeOfferButton=Review: Placer un ordre de {0} monero createOffer.createOfferFundWalletInfo.headline=Financer votre ordre # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=Montant du trade: {0}\n\n -createOffer.createOfferFundWalletInfo.msg=Vous devez déposer {0} pour cet ordre.\n\nCes fonds sont réservés dans votre portefeuille local et seront bloqués sur une adresse de dépôt multisig une fois que quelqu''un aura accepté votre ordre.\n\nLe montant correspond à la somme de:\n{1}- Votre dépôt de garantie: {2}\n- Frais de trading: {3}\n- Frais d''exploitation minière: {4}\n\nVous avez le choix entre deux options pour financer votre transaction :\n- Utilisez votre portefeuille Haveno (pratique, mais les transactions peuvent être associables) OU\n- Transfert depuis un portefeuille externe (potentiellement plus privé)\n\nVous pourrez voir toutes les options de financement et les détails après avoir fermé ce popup. +createOffer.createOfferFundWalletInfo.msg=Vous devez déposer {0} à cette offre.\n\n\ + Ces fonds sont réservés dans votre portefeuille local et seront verrouillés dans un portefeuille multisignature dès qu'une personne acceptera votre offre.\n\n\ + Le montant est la somme de :\n\ + {1}\ + - Votre dépôt de garantie : {2}\n\ + - Frais de transaction : {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=Une erreur s''est produite lors du placement de cet ordre:\n\n{0}\n\nAucun fonds n''a été prélevé sur votre portefeuille pour le moment.\nVeuillez redémarrer l''application et vérifier votre connexion réseau. diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 6964d462..e7dd6f01 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -457,7 +457,12 @@ createOffer.placeOfferButton=Revisione: piazza l'offerta a {0} monero createOffer.createOfferFundWalletInfo.headline=Finanzia la tua offerta # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Importo di scambio: {0} \n -createOffer.createOfferFundWalletInfo.msg=Devi depositare {0} a questa offerta.\n\nTali fondi sono riservati nel tuo portafoglio locale e verranno bloccati nell'indirizzo di deposito multisig una volta che qualcuno accetta la tua offerta.\n\nL'importo è la somma di:\n{1} - Il tuo deposito cauzionale: {2}\n- Commissione di scambio: {3}\n- Commissione di mining: {4}\n\nPuoi scegliere tra due opzioni quando finanzi il tuo scambio:\n- Usa il tuo portafoglio Haveno (comodo, ma le transazioni possono essere collegabili) OPPURE\n- Effettua il trasferimento da un portafoglio esterno (potenzialmente più privato)\n\nVedrai tutte le opzioni di finanziamento e i dettagli dopo aver chiuso questo popup. +createOffer.createOfferFundWalletInfo.msg=Devi depositare {0} per questa offerta.\n\n\ + Questi fondi sono riservati nel tuo portafoglio locale e verranno bloccati in un portafoglio multisig una volta che qualcuno accetta la tua offerta.\n\n\ + L'importo è la somma di:\n\ + {1}\ + - Il tuo deposito di sicurezza: {2}\n\ + - Tassa di trading: {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=Si è verificato un errore durante l'immissione dell'offerta:\n\n{0}\n\nNon sono ancora usciti fondi dal tuo portafoglio.\nRiavvia l'applicazione e controlla la connessione di rete. diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 1229dbe1..55a0352d 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -458,7 +458,12 @@ createOffer.placeOfferButton=再確認: ビットコインを{0}オファーを createOffer.createOfferFundWalletInfo.headline=あなたのオファーへ入金 # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- 取引額: {0}\n -createOffer.createOfferFundWalletInfo.msg=このオファーに対して {0} のデポジットを送金する必要があります。\n\nこの資金はあなたのローカルウォレットに予約済として保管され、オファーが受け入れられた時にマルチシグデポジットアドレスに移動しロックされます。\n\n金額の合計は以下の通りです\n{1} - セキュリティデポジット: {2}\n- 取引手数料: {3}\n- マイニング手数料: {4}\n\nこのオファーにデポジットを送金するには、以下の2つの方法があります。\n- Havenoウォレットを使う (便利ですがトランザクションが追跡される可能性があります)\n- 外部のウォレットから送金する (機密性の高い方法です)\n\nこのポップアップを閉じると全ての送金方法について詳細な情報が表示されます。 +createOffer.createOfferFundWalletInfo.msg=このオファーには {0} をデポジットする必要があります。\n\n\ + この資金はあなたのローカルウォレットに予約され、誰かがあなたのオファーを受け入れるとマルチシグウォレットにロックされます。\n\n\ + 金額は以下の合計です:\n\ + {1}\ + - あなたの保証金: {2}\n\ + - 取引手数料: {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=オファーを出す時にエラーが発生しました:\n\n{0}\n\nウォレットにまだ資金がありません。\nアプリケーションを再起動してネットワーク接続を確認してください。 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 7c79f993..20f897fc 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -460,7 +460,12 @@ createOffer.placeOfferButton=Revisar: Criar oferta para {0} monero createOffer.createOfferFundWalletInfo.headline=Financiar sua oferta # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Quantia da negociação: {0} \n -createOffer.createOfferFundWalletInfo.msg=Você precisa depositar {0} para esta oferta.\n\nEsses fundos ficam reservados na sua carteira local e ficarão travados no endereço de depósito multisig quando alguém aceitar a sua oferta.\n\nA quantia equivale à soma de:\n{1}- Seu depósito de segurança: {2}\n- Taxa de negociação: {3}\n- Taxa de mineração: {4}\n\nVocê pode financiar sua negociação das seguintes maneiras:\n- Usando a sua carteira Haveno (conveniente, mas transações poderão ser associadas entre si) OU\n- Usando uma carteira externa (maior privacidade)\n\nVocê verá todas as opções de financiamento e detalhes após fechar esta janela. +createOffer.createOfferFundWalletInfo.msg=Você precisa depositar {0} para esta oferta.\n\n\ + Esses fundos são reservados em sua carteira local e serão bloqueados em uma carteira multisig assim que alguém aceitar sua oferta.\n\n\ + O valor é a soma de:\n\ + {1}\ + - Seu depósito de segurança: {2}\n\ + - Taxa de negociação: {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=Um erro ocorreu ao emitir uma oferta:\n\n{0}\n\nNenhum fundo foi retirado de sua carteira até agora.\nPor favor, reinicie o programa e verifique sua conexão de internet. diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index fd6d3c62..604640c5 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -457,7 +457,12 @@ createOffer.placeOfferButton=Rever: Colocar oferta para {0} monero createOffer.createOfferFundWalletInfo.headline=Financiar sua oferta # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Quantia de negócio: {0} \n -createOffer.createOfferFundWalletInfo.msg=Você precisa depositar {0} para esta oferta.\n\nEsses fundos estão reservados na sua carteira local e serão bloqueados no endereço de depósito multi-assinatura assim que alguém aceitar a sua oferta.\n\nA quantia é a soma de:\n{1} - Seu depósito de segurança: {2}\n- Taxa de negociação: {3}\n- Taxa de mineração: {4}\n\nVocê pode escolher entre duas opções ao financiar o seu negócio:\n- Use sua carteira Haveno (conveniente, mas as transações podem ser conectadas) OU\n- Transferência de uma carteira externa (potencialmente mais privada)\n\nVocê verá todas as opções de financiamento e detalhes depois de fechar este popup. +createOffer.createOfferFundWalletInfo.msg=Você precisa depositar {0} para esta oferta.\n\n\ + Esses fundos são reservados em sua carteira local e serão bloqueados em uma carteira multisig assim que alguém aceitar sua oferta.\n\n\ + O valor é a soma de:\n\ + {1}\ + - Seu depósito de segurança: {2}\n\ + - Taxa de negociação: {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=Ocorreu um erro ao colocar a oferta:\n\n{0}\n\nAinda nenhuns fundos saíram da sua carteira.\nPor favor, reinicie seu programa e verifique sua conexão de rede. diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 5f976feb..fef440d3 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -457,7 +457,12 @@ createOffer.placeOfferButton=Проверка: разместить предло createOffer.createOfferFundWalletInfo.headline=Обеспечить своё предложение # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Сумма сделки: {0} \n -createOffer.createOfferFundWalletInfo.msg=Вы должны внести {0} для обеспечения этого предложения.\n\nЭти средства будут зарезервированы в вашем локальном кошельке, а когда кто-то примет ваше предложение — заблокированы на депозитном multisig-адресе.\n\nСумма состоит из:\n{1}- вашего залога: {2},\n- комиссии за сделку: {3},\n- комиссии майнера: {4}.\n\nВы можете выбрать один из двух вариантов финансирования сделки:\n - использовать свой кошелёк Haveno (удобно, но сделки можно отследить) ИЛИ\n - перевести из внешнего кошелька (потенциально более анонимно).\n\nВы увидите все варианты обеспечения предложения и их подробности после закрытия этого окна. +createOffer.createOfferFundWalletInfo.msg=Вам нужно внести депозит {0} для этого предложения.\n\n\ + Эти средства резервируются в вашем локальном кошельке и будут заблокированы в мультиподписном кошельке, как только кто-то примет ваше предложение.\n\n\ + Сумма состоит из:\n\ + {1}\ + - Ваш залог: {2}\n\ + - Торговая комиссия: {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=Ошибка при создании предложения:\n\n{0}\n\nВаши средства остались в кошельке.\nПерезагрузите приложение и проверьте сетевое соединение. diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 15486e22..e3eca2f0 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -457,7 +457,12 @@ createOffer.placeOfferButton=รีวิว: ใส่ข้อเสนอไ createOffer.createOfferFundWalletInfo.headline=เงินทุนสำหรับข้อเสนอของคุณ # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- ปริมาณการซื้อขาย: {0} -createOffer.createOfferFundWalletInfo.msg=คุณต้องวางเงินมัดจำ {0} ข้อเสนอนี้\n\nเงินเหล่านั้นจะถูกสงวนไว้ใน wallet ภายในประเทศของคุณและจะถูกล็อคไว้ในที่อยู่ที่ฝากเงิน multisig เมื่อมีคนรับข้อเสนอของคุณ\n\nผลรวมของจำนวนของ: \n{1} - เงินประกันของคุณ: {2} \n- ค่าธรรมเนียมการซื้อขาย: {3} \n- ค่าขุด: {4} \n\nคุณสามารถเลือกระหว่างสองตัวเลือกเมื่อมีการระดุมทุนการซื้อขายของคุณ: \n- ใช้กระเป๋าสตางค์ Haveno ของคุณ (สะดวก แต่ธุรกรรมอาจเชื่อมโยงกันได้) หรือ\n- โอนเงินจากเงินภายนอกเข้ามา (อาจเป็นส่วนตัวมากขึ้น) \n\nคุณจะเห็นตัวเลือกและรายละเอียดการระดมทุนทั้งหมดหลังจากปิดป๊อปอัปนี้ +createOffer.createOfferFundWalletInfo.msg=คุณจำเป็นต้องฝากเงิน {0} เพื่อข้อเสนอนี้\n\n\ + เงินเหล่านี้จะถูกสงวนไว้ในกระเป๋าเงินในเครื่องของคุณ และจะถูกล็อกในกระเป๋าเงินมัลติซิกเมื่อมีคนรับข้อเสนอของคุณ\n\n\ + จำนวนเงินคือผลรวมของ:\n\ + {1}\ + - เงินประกันของคุณ: {2}\n\ + - ค่าธรรมเนียมการซื้อขาย: {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=เกิดข้อผิดพลาดขณะใส่ข้อเสนอ: \n\n{0} \n\nยังไม่มีการโอนเงินจาก wallet ของคุณเลย\nโปรดเริ่มแอปพลิเคชันใหม่และตรวจสอบการเชื่อมต่อเครือข่ายของคุณ diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index c405e3c2..a7e587ce 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -495,10 +495,10 @@ createOffer.createOfferFundWalletInfo.headline=Teklifinizi finanse edin # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Ticaret miktarı: {0} \n createOffer.createOfferFundWalletInfo.msg=Bu teklife {0} yatırmanız gerekiyor.\n\n\ - Bu fonlar yerel cüzdanınızda ayrılır ve birisi teklifinizi aldığında multisig bir cüzdana kilitlenir.\n\n\ - Miktar, şunların toplamıdır:\n\ + Bu fonlar yerel cüzdanınızda rezerve edilir ve birisi teklifinizi kabul ettiğinde bir multisig cüzdanda kilitlenir.\n\n\ + Tutarın toplamı şudur:\n\ {1}\ - - Güvenlik teminatınız: {2}\n\ + - Güvenlik depozitonuz: {2}\n\ - İşlem ücreti: {3} # only first part "Bir teklif verirken bir hata oluştu:" has been used before. We added now the rest (need update in existing translations!) diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index c3d7548e..c29e335f 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -457,7 +457,12 @@ createOffer.placeOfferButton=Kiểm tra:: Đặt báo giá cho {0} monero createOffer.createOfferFundWalletInfo.headline=Nộp tiền cho báo giá của bạn # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Khoản tiền giao dịch: {0} \n -createOffer.createOfferFundWalletInfo.msg=Bạn cần đặt cọc {0} cho báo giá này.\n\nCác khoản tiền này sẽ được giữ trong ví nội bộ của bạn và sẽ bị khóa vào địa chỉ đặt cọc multisig khi có người nhận báo giá của bạn.\n\nKhoản tiền này là tổng của:\n{1}- tiền gửi đại lý của bạn: {2}\n- Phí giao dịch: {3}\n- Phí đào: {4}\n\nBạn có thể chọn giữa hai phương án khi nộp tiền cho giao dịch:\n- Sử dụng ví Haveno của bạn (tiện lợi, nhưng giao dịch có thể bị kết nối) OR\n- Chuyển từ ví bên ngoài (riêng tư hơn)\n\nBạn sẽ xem các phương án nộp tiền và thông tin chi tiết sau khi đóng cửa sổ này. +createOffer.createOfferFundWalletInfo.msg=Bạn cần nạp {0} cho lời đề nghị này.\n\n\ + Số tiền này sẽ được giữ trong ví cục bộ của bạn và sẽ được khóa vào ví multisig ngay khi có người chấp nhận lời đề nghị của bạn.\n\n\ + Số tiền bao gồm:\n\ + {1}\ + - Tiền đặt cọc bảo đảm của bạn: {2}\n\ + - Phí giao dịch: {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=Có lỗi xảy ra khi đặt chào giá:\n\n{0}\n\nKhông còn tiền trong ví của bạn.\nHãy khởi động lại ứng dụng và kiểm tra kết nối mạng. diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index f8af6af2..8c0db740 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -458,7 +458,12 @@ createOffer.placeOfferButton=复审:报价挂单 {0} 比特币 createOffer.createOfferFundWalletInfo.headline=为您的报价充值 # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- 交易数量:{0}\n -createOffer.createOfferFundWalletInfo.msg=这个报价您需要 {0} 作为保证金。\n\n这些资金保留在您的本地钱包并会被冻结到多重验证保证金地址直到报价交易成功。\n\n总数量:{1}\n- 保证金:{2}\n- 挂单费:{3}\n- 矿工手续费:{4}\n\n您有两种选项可以充值您的交易:\n- 使用您的 Haveno 钱包(方便,但交易可能会被链接到)或者\n- 从外部钱包转入(或许这样更隐秘一些)\n\n关闭此弹出窗口后,您将看到所有资金选项和详细信息。 +createOffer.createOfferFundWalletInfo.msg=您需要为此报价存入 {0}。\n\n\ + 这些资金将保留在您的本地钱包中,并在有人接受您的报价后锁定到多签钱包中。\n\n\ + 金额是以下各项的总和:\n\ + {1}\ + - 您的保证金:{2}\n\ + - 交易费用:{3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=提交报价发生错误:\n\n{0}\n\n没有资金从您钱包中扣除。\n请检查您的互联网连接或尝试重启应用程序。 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 1dd25d90..a7810b86 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -458,7 +458,12 @@ createOffer.placeOfferButton=複審:報價掛單 {0} 比特幣 createOffer.createOfferFundWalletInfo.headline=為您的報價充值 # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- 交易數量:{0}\n -createOffer.createOfferFundWalletInfo.msg=這個報價您需要 {0} 作為保證金。\n\n這些資金保留在您的本地錢包並會被凍結到多重驗證保證金地址直到報價交易成功。\n\n總數量:{1}\n- 保證金:{2}\n- 掛單費:{3}\n- 礦工手續費:{4}\n\n您有兩種選項可以充值您的交易:\n- 使用您的 Haveno 錢包(方便,但交易可能會被鏈接到)或者\n- 從外部錢包轉入(或許這樣更隱祕一些)\n\n關閉此彈出窗口後,您將看到所有資金選項和詳細信息。 +createOffer.createOfferFundWalletInfo.msg=您需要為此報價存入 {0}。\n\n\ + 這些資金會保留在您的本地錢包中,並在有人接受您的報價後鎖定到多重簽名錢包中。\n\n\ + 金額總和為:\n\ + {1}\ + - 您的保證金:{2}\n\ + - 交易費:{3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=提交報價發生錯誤:\n\n{0}\n\n沒有資金從您錢包中扣除。\n請檢查您的互聯網連接或嘗試重啟應用程序。 From 8fd7f17317cebccab648c8695314ce3d8b2f0a29 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 18 Nov 2024 09:29:30 -0500 Subject: [PATCH 02/34] update translation for funding wallet on take offer --- core/src/main/resources/i18n/displayStrings_es.properties | 2 +- core/src/main/resources/i18n/displayStrings_fa.properties | 2 +- core/src/main/resources/i18n/displayStrings_fr.properties | 2 +- core/src/main/resources/i18n/displayStrings_it.properties | 2 +- core/src/main/resources/i18n/displayStrings_ja.properties | 2 +- core/src/main/resources/i18n/displayStrings_pt-br.properties | 2 +- core/src/main/resources/i18n/displayStrings_pt.properties | 2 +- core/src/main/resources/i18n/displayStrings_ru.properties | 2 +- core/src/main/resources/i18n/displayStrings_th.properties | 2 +- core/src/main/resources/i18n/displayStrings_tr.properties | 2 +- core/src/main/resources/i18n/displayStrings_vi.properties | 2 +- core/src/main/resources/i18n/displayStrings_zh-hans.properties | 2 +- core/src/main/resources/i18n/displayStrings_zh-hant.properties | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index 43a92fe7..c9d60a70 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -519,7 +519,7 @@ takeOffer.noPriceFeedAvailable=No puede tomar esta oferta porque utiliza un prec takeOffer.takeOfferFundWalletInfo.headline=Dotar de fondos su intercambio # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Cantidad a intercambiar: {0}\n -takeOffer.takeOfferFundWalletInfo.msg=Necesita depositar {0} para tomar esta oferta.\n\nLa cantidad es la suma de:\n{1} - Su depósito de seguridad: {2}\n- Comisión de intercambio: {3}\n- Comisiones de minado totales: {4}\n\nPuede elegir entre dos opciones al depositar fondos para realizar su intercambio:\n- Usar su cartera Haveno (conveniente, pero las transacciones pueden ser trazables) O también\n- Transferir desde una cartera externa (potencialmente con mayor privacidad)\n\nVerá todos los detalles y opciones para depositar fondos al cerrar esta ventana. +takeOffer.takeOfferFundWalletInfo.msg=Necesitas depositar {0} para aceptar esta oferta.\n\nLa cantidad es la suma de:\n{1}- Tu depósito de seguridad: {2}\n- Tarifa de transacción: {3} takeOffer.alreadyPaidInFunds=Si ya ha depositado puede retirarlo en la pantalla \"Fondos/Disponible para retirar\". takeOffer.paymentInfo=Información de pago takeOffer.setAmountPrice=Establecer cantidad diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 90613ad0..7f18e3c3 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -518,7 +518,7 @@ takeOffer.noPriceFeedAvailable=امکان پذیرفتن پیشنهاد وجود takeOffer.takeOfferFundWalletInfo.headline=معامله خود را تأمین وجه نمایید # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=مقدار معامله: {0}\n -takeOffer.takeOfferFundWalletInfo.msg=شما باید {0} برای قبول این پیشنهاد، سپرده بگذارید.\nاین مقدار مجموع موارد ذیل است:\n{1} - سپرده‌ی اطمینان شما: {2}\n-هزینه معامله: {3}\n-تمامی هزینه های تراکنش شبکه: {4}\nشما هنگام تامین مالی معامله‌ی خود، می‌توانید بین دو گزینه انتخاب کنید:\n- از کیف پول Haveno خود استفاده کنید (این روش راحت است، اما ممکن است تراکنش‌ها قابل رصد شوند)، یا\n- از کیف پول خارجی انتقال دهید (به طور بالقوه‌ای این روش ایمن‌تر و محافظ حریم خصوصی شما است)\n\nشما تمام گزینه‌ها و جزئیات تامین مالی را پس از بستن این پنجره، خواهید دید. +takeOffer.takeOfferFundWalletInfo.msg=باید {0} را برای پذیرش این پیشنهاد واریز کنید.\n\nمبلغ مجموع موارد زیر است:\n{1}- سپرده امنیتی شما: {2}\n- هزینه معامله: {3} takeOffer.alreadyPaidInFunds=اگر شما در حال حاضر در وجوه، پرداختی داشته اید، می توانید آن را در صفحه ی \"وجوه/ارسال وجوه\" برداشت کنید. takeOffer.paymentInfo=اطلاعات پرداخت takeOffer.setAmountPrice=تنظیم مقدار diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index ab196d4e..a510fbac 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -519,7 +519,7 @@ takeOffer.noPriceFeedAvailable=Vous ne pouvez pas accepter cet ordre, car celui- takeOffer.takeOfferFundWalletInfo.headline=Provisionner votre trade # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Montant du trade: {0}\n -takeOffer.takeOfferFundWalletInfo.msg=Vous devez envoyer {0} pour cet odre.\n\nLe montant est la somme de:\n{1}--Dépôt de garantie: {2}\n- Frais de transaction: {3}\n- Frais de minage: {4}\n\nVous avez deux choix pour payer votre transaction :\n- Utiliser votre portefeuille local Haveno (pratique, mais vos transactions peuvent être tracées) OU\n- Transférer d''un portefeuille externe (potentiellement plus confidentiel)\n\nVous retrouverez toutes les options de provisionnement après fermeture de ce popup. +takeOffer.takeOfferFundWalletInfo.msg=Vous devez déposer {0} pour accepter cette offre.\n\nLe montant est la somme de :\n{1}- Votre dépôt de garantie : {2}\n- Frais de transaction : {3} takeOffer.alreadyPaidInFunds=Si vous avez déjà provisionner des fonds vous pouvez les retirer dans l'onglet \"Fonds/Envoyer des fonds\". takeOffer.paymentInfo=Informations de paiement takeOffer.setAmountPrice=Définir le montant diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index e7dd6f01..09e98ca4 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -518,7 +518,7 @@ takeOffer.noPriceFeedAvailable=Non puoi accettare questa offerta poiché utilizz takeOffer.takeOfferFundWalletInfo.headline=Finanzia il tuo scambio # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Importo di scambio: {0} \n -takeOffer.takeOfferFundWalletInfo.msg=Devi depositare {0} per accettare questa offerta.\n\nL'importo è la somma de:\n{1} - Il tuo deposito cauzionale: {2}\n- La commissione di trading: {3}\n- I costi di mining: {4}\n\nPuoi scegliere tra due opzioni quando finanzi il tuo scambio:\n- Usare il tuo portafoglio Haveno (comodo, ma le transazioni possono essere collegabili) OPPURE\n- Trasferimento da un portafoglio esterno (potenzialmente più privato)\n\nVedrai tutte le opzioni di finanziamento e i dettagli dopo aver chiuso questo popup. +takeOffer.takeOfferFundWalletInfo.msg=Devi depositare {0} per accettare questa offerta.\n\nL'importo è la somma di:\n{1}- Il tuo deposito di sicurezza: {2}\n- Commissione di trading: {3} takeOffer.alreadyPaidInFunds=Se hai già pagato in fondi puoi effettuare il ritiro nella schermata \"Fondi/Invia fondi\". takeOffer.paymentInfo=Informazioni sul pagamento takeOffer.setAmountPrice=Importo stabilito diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 55a0352d..8e6085bc 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -519,7 +519,7 @@ takeOffer.noPriceFeedAvailable=そのオファーは市場価格に基づくパ takeOffer.takeOfferFundWalletInfo.headline=あなたのオファーへ入金 # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount= - 取引額: {0}\n -takeOffer.takeOfferFundWalletInfo.msg=このオファーに対して {0} のデポジットを送金する必要があります。\n\n金額の合計は以下の通りです\n{1} - セキュリティデポジット: {2}\n- 取引手数料: {3}\n- マイニング手数料: {4}\n\nこのオファーにデポジットを送金するには、以下の2つの方法があります。\n- Havenoウォレットを使う (便利ですがトランザクションが追跡される可能性があります)\nまたは\n- 外部のウォレットから送金する (機密性の高い方法です)\n\nこのポップアップを閉じると全ての送金方法について詳細な情報が表示されます。 +takeOffer.takeOfferFundWalletInfo.msg=このオファーを受けるには、{0} を預ける必要があります。\n\n金額は以下の合計です:\n{1}- あなたの保証金: {2}\n- 取引手数料: {3} takeOffer.alreadyPaidInFunds=あなたがすでに資金を支払っている場合は「資金/送金する」画面でそれを出金することができます。 takeOffer.paymentInfo=支払い情報 takeOffer.setAmountPrice=金額を設定 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 20f897fc..51f2e333 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -521,7 +521,7 @@ takeOffer.noPriceFeedAvailable=Você não pode aceitar essa oferta pois ela usa takeOffer.takeOfferFundWalletInfo.headline=Financiar sua negociação # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Quantia a negociar: {0} \n -takeOffer.takeOfferFundWalletInfo.msg=Você precisa depositar {0} para aceitar esta oferta.\n\nA quantia equivale a soma de:\n{1}- Seu depósito de segurança: {2}\n- Taxa de negociação: {3}\n\nVocê pode escolher entre duas opções para financiar sua negociação:\n- Usar a sua carteira Haveno (conveniente, mas transações podem ser associadas entre si) OU\n- Transferir a partir de uma carteira externa (potencialmente mais privado)\n\nVocê verá todas as opções de financiamento e detalhes após fechar esta janela. +takeOffer.takeOfferFundWalletInfo.msg=Você precisa depositar {0} para aceitar esta oferta.\n\nO valor é a soma de:\n{1}- Seu depósito de segurança: {2}\n- Taxa de negociação: {3} takeOffer.alreadyPaidInFunds=Se você já pagou por essa oferta, você pode retirar seus fundos na seção \"Fundos/Enviar fundos\". takeOffer.paymentInfo=Informações de pagamento takeOffer.setAmountPrice=Definir quantia diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 604640c5..9be31eef 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -518,7 +518,7 @@ takeOffer.noPriceFeedAvailable=Você não pode aceitar aquela oferta pois ela ut takeOffer.takeOfferFundWalletInfo.headline=Financiar seu negócio # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Quantia de negócio: {0} \n -takeOffer.takeOfferFundWalletInfo.msg=Você precisa depositar {0} para aceitar esta oferta.\n\nA quantia é a soma de:\n{1} - Seu depósito de segurança: {2}\n- Taxa de negociação: {3}\n\nVocê pode escolher entre duas opções ao financiar o seu negócio:\n- Use sua carteira Haveno (conveniente, mas as transações podem ser conectas) OU\n- Transferência de uma carteira externa (potencialmente mais privada)\n\nVocê verá todas as opções de financiamento e detalhes depois de fechar este popup. +takeOffer.takeOfferFundWalletInfo.msg=Você precisa depositar {0} para aceitar esta oferta.\n\nO valor é a soma de:\n{1}- Seu depósito de segurança: {2}\n- Taxa de negociação: {3} takeOffer.alreadyPaidInFunds=Se você já pagou com seus fundos você pode levantá-los na janela \"Fundos/Enviar fundos\". takeOffer.paymentInfo=Informações de pagamento takeOffer.setAmountPrice=Definir quantia diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index fef440d3..49b37f4c 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -518,7 +518,7 @@ takeOffer.noPriceFeedAvailable=Нельзя принять это предлож takeOffer.takeOfferFundWalletInfo.headline=Обеспечьте свою сделку # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Сумма сделки: {0} \n -takeOffer.takeOfferFundWalletInfo.msg=Вы должны внести {0} для принятия этого предложения.\n\nСумма состоит из:\n{1}- вашего залога: {2},\n- комиссии за сделку: {3}\n\nВы можете выбрать один из двух вариантов финансирования сделки:\n - использовать свой кошелёк Haveno (удобно, но сделки можно отследить) ИЛИ\n - перевести из внешнего кошелька (потенциально более анонимно).\n\nВы увидите все варианты обеспечения предложения и их подробности после закрытия этого окна. +takeOffer.takeOfferFundWalletInfo.msg=Вам нужно внести депозит в размере {0} для принятия этого предложения.\n\nСумма составляет:\n{1}- Ваш залог: {2}\n- Торговая комиссия: {3} takeOffer.alreadyPaidInFunds=Если вы уже внесли средства, их можно вывести в разделе \«Средства/Отправить средства\». takeOffer.paymentInfo=Информация о платеже takeOffer.setAmountPrice=Задайте сумму diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index e3eca2f0..15b87518 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -518,7 +518,7 @@ takeOffer.noPriceFeedAvailable=คุณไม่สามารถรับข takeOffer.takeOfferFundWalletInfo.headline=ทุนการซื้อขายของคุณ # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- ปริมาณการซื้อขาย: {0} -takeOffer.takeOfferFundWalletInfo.msg=คุณต้องวางเงินประกัน {0} เพื่อรับข้อเสนอนี้\n\nจำนวนเงินคือผลรวมของ: \n{1} - เงินประกันของคุณ: {2} \n- ค่าธรรมเนียมการซื้อขาย: {3} \n\nคุณสามารถเลือกระหว่างสองตัวเลือกเมื่อลงทุนการซื้อขายของคุณ: \n- ใช้กระเป๋าสตางค์ Haveno ของคุณ (สะดวก แต่ธุรกรรมอาจเชื่อมโยงกันได้) หรือ\n- โอนเงินจากแหล่งเงินภายนอก (อาจเป็นส่วนตัวมากขึ้น) \n\nคุณจะเห็นตัวเลือกและรายละเอียดการลงทุนทั้งหมดหลังจากปิดป๊อปอัปนี้ +takeOffer.takeOfferFundWalletInfo.msg=คุณต้องฝากเงิน {0} เพื่อรับข้อเสนอนี้。\n\nจำนวนเงินคือผลรวมของ:\n{1}- เงินมัดจำของคุณ: {2}\n- ค่าธรรมเนียมการซื้อขาย: {3} takeOffer.alreadyPaidInFunds=หากคุณได้ชำระเงินแล้วคุณสามารถถอนเงินออกได้ในหน้าจอ \"เงิน / ส่งเงิน \" takeOffer.paymentInfo=ข้อมูลการชำระเงิน takeOffer.setAmountPrice=ตั้งยอดจำนวน diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index a7e587ce..e7161baa 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -561,7 +561,7 @@ takeOffer.noPriceFeedAvailable=Bu teklifi alamazsınız çünkü piyasa fiyatın takeOffer.takeOfferFundWalletInfo.headline=İşleminizi finanse edin # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- İşlem miktarı: {0} \n -takeOffer.takeOfferFundWalletInfo.msg=Bu teklifi almak için {0} yatırmanız gerekiyor.\n\nBu miktar şunların toplamıdır:\n{1}- Güvenlik teminatınız: {2}\n- İşlem ücreti: {3} +takeOffer.takeOfferFundWalletInfo.msg=Bu teklifi kabul etmek için {0} yatırmanız gerekiyor.\n\nMiktar, şu kalemlerin toplamıdır:\n{1}- Güvenlik depozitonuz: {2}\n- İşlem ücreti: {3} takeOffer.alreadyPaidInFunds=Eğer zaten fon yatırdıysanız, \"Fonlar/Fon gönder\" ekranında çekebilirsiniz. takeOffer.setAmountPrice=Miktar ayarla takeOffer.alreadyFunded.askCancel=Bu teklifi zaten finanse ettiniz.\nŞimdi iptal ederseniz, fonlarınız yerel Haveno cüzdanınızda kalacak ve \"Fonlar/Fon gönder\" ekranında çekilebilir olacaktır.\nİptal etmek istediğinizden emin misiniz? diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index c29e335f..5929dd94 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -518,7 +518,7 @@ takeOffer.noPriceFeedAvailable=Bạn không thể nhận báo giá này do sử takeOffer.takeOfferFundWalletInfo.headline=Nộp tiền cho giao dịch của bạn # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Giá trị giao dịch: {0} \n -takeOffer.takeOfferFundWalletInfo.msg=Bạn cần nộp {0} để nhận báo giá này.\n\nGiá trị này là tổng của:\n{1}- Tiền ứng trước của bạn: {2}\n- phí giao dịch: {3}\n\nBạn có thể chọn một trong hai phương án khi nộp tiền cho giao dịch của bạn:\n- Sử dụng ví Haveno (tiện lợi, nhưng giao dịch có thể bị kết nối) OR\n- Chuyển từ ví ngoài (riêng tư hơn)\n\nBạn sẽ thấy các phương án nộp tiền và thông tin chi tiết sau khi đóng cửa sổ này. +takeOffer.takeOfferFundWalletInfo.msg=Bạn cần phải deposit {0} để chấp nhận đề nghị này.\n\nSố tiền là tổng của:\n{1}- Khoản tiền đặt cọc của bạn: {2}\n- Phí giao dịch: {3} takeOffer.alreadyPaidInFunds=Bạn đã thanh toán, bạn có thể rút số tiền này tại màn hình \"Vốn/Gửi vốn\". takeOffer.paymentInfo=Thông tin thanh toán takeOffer.setAmountPrice=Cài đặt số tiền diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 8c0db740..0ea093e1 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -519,7 +519,7 @@ takeOffer.noPriceFeedAvailable=您不能对这笔报价下单,因为它使用 takeOffer.takeOfferFundWalletInfo.headline=为交易充值 # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- 交易数量:{0}\n -takeOffer.takeOfferFundWalletInfo.msg=这个报价您需要付出 {0} 保证金。\n\n这些资金保留在您的本地钱包并会被冻结到多重验证保证金地址直到报价交易成功。\n\n总数量:{1}\n- 保证金:{2}\n- 挂单费:{3}\n\n您有两种选项可以充值您的交易:\n- 使用您的 Haveno 钱包(方便,但交易可能会被链接到)或者\n- 从外部钱包转入(或许这样更隐秘一些)\n\n关闭此弹出窗口后,您将看到所有资金选项和详细信息。 +takeOffer.takeOfferFundWalletInfo.msg=您需要存入 {0} 以接受此报价。\n\n该金额为以下总和:\n{1}- 您的保证金:{2}\n- 交易费用:{3} takeOffer.alreadyPaidInFunds=如果你已经支付,你可以在“资金/提现”提现它。 takeOffer.paymentInfo=付款信息 takeOffer.setAmountPrice=设置数量 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index a7810b86..95fad5ea 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -519,7 +519,7 @@ takeOffer.noPriceFeedAvailable=您不能對這筆報價下單,因為它使用 takeOffer.takeOfferFundWalletInfo.headline=為交易充值 # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- 交易數量:{0}\n -takeOffer.takeOfferFundWalletInfo.msg=這個報價您需要付出 {0} 保證金。\n\n這些資金保留在您的本地錢包並會被凍結到多重驗證保證金地址直到報價交易成功。\n\n總數量:{1}\n- 保證金:{2}\n- 掛單費:{3}\n\n您有兩種選項可以充值您的交易:\n- 使用您的 Haveno 錢包(方便,但交易可能會被鏈接到)或者\n- 從外部錢包轉入(或許這樣更隱祕一些)\n\n關閉此彈出窗口後,您將看到所有資金選項和詳細信息。 +takeOffer.takeOfferFundWalletInfo.msg=您需要存入 {0} 才能接受此報價。\n\n該金額是以下總和:\n{1}- 您的保證金:{2}\n- 交易費用:{3} takeOffer.alreadyPaidInFunds=如果你已經支付,你可以在“資金/提現”提現它。 takeOffer.paymentInfo=付款信息 takeOffer.setAmountPrice=設置數量 From 68b4a0fafbbbb5621d30d18ff05010338173f49c Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 21 Nov 2024 09:54:04 -0500 Subject: [PATCH 03/34] update links to #haveno-development --- README.md | 2 +- docs/CONTRIBUTING.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e8d40e8e..13b96948 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ If you wish to help, take a look at the repositories above and look for open iss Haveno is a community-driven project. For it to be successful it's fundamental to have the support and help of the community. Join the community rooms on our Matrix server: - General discussions: **Haveno** ([#haveno:monero.social](https://matrix.to/#/#haveno:monero.social)) relayed on IRC/Libera (`#haveno`) -- Development discussions: **Haveno Development** ([#haveno-dev:monero.social](https://matrix.to/#/#haveno-dev:monero.social)) relayed on IRC/Libera (`#haveno-dev`) +- Development discussions: **Haveno Development** ([#haveno-development:monero.social](https://matrix.to/#/#haveno-development:monero.social)) relayed on IRC/Libera (`#haveno-development`) Email: contact@haveno.exchange Website: [haveno.exchange](https://haveno.exchange) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 54bebbbf..caa76ac1 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to Haveno -Thanks for wishing to help! Here there are some guidelines and information about the development process. We suggest you to join the [matrix](https://app.element.io/#/room/#haveno-dev:haveno.network) room `#haveno-dev` (relayed on [IRC/Libera](irc://irc.libera.chat/#haveno-dev)) and have a chat with the devs, so that we can help get you started. +Thanks for wishing to help! Here there are some guidelines and information about the development process. We suggest you to join the [matrix](https://app.element.io/#/room/#haveno-development:monero.social) room `#haveno-development` (relayed on [IRC/Libera](irc://irc.libera.chat/#haveno-development)) and have a chat with the devs, so that we can help get you started. Issues are tracked on GitHub. We use [a label system](https://github.com/haveno-dex/haveno/issues/50) and GitHub's [project boards](https://github.com/haveno-dex/haveno/projects) to simplify development. Make sure to take a look at those and to follow the priorities suggested. From ae80935f3a81eae3e3c145c552a7faf33d407099 Mon Sep 17 00:00:00 2001 From: ohchase Date: Sun, 24 Nov 2024 07:51:55 -0500 Subject: [PATCH 04/34] enable hidden files in cache node dependencies --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b0340da0..3d66321d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,7 @@ jobs: - name: cache nodes dependencies uses: actions/upload-artifact@v3 with: + include-hidden-files: true name: cached-localnet path: .localnet - name: Install dependencies From c40e0bea5a614af8b1c3564def2d3d57c7fbfa65 Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 24 Nov 2024 11:08:26 -0500 Subject: [PATCH 05/34] build instructions warn that mainnet is not supported --- README.md | 9 +++++++-- docs/installing.md | 10 +++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 13b96948..46fb3528 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,16 @@ See the [FAQ on our website](https://haveno.exchange/faq/) for more information. ## Installing Haveno -Haveno can be installed on Linux, macOS, and Windows by using a third party installer and network. We do not endorse any networks at this time. +Haveno can be installed on Linux, macOS, and Windows by using a third party installer and network. + +> [!note] +> The official Haveno repository does not support making real trades directly. +> +> To make real trades with Haveno, first find a third party network, and then use their installer or build their repository. We do not endorse any networks at this time. A test network is also available for users to make test trades using Monero's stagenet. See the [instructions](https://github.com/haveno-dex/haveno/blob/master/docs/installing.md) to build Haveno and connect to the test network. -Alternatively, you can [start your own network](https://github.com/haveno-dex/haveno/blob/master/docs/create-mainnet.md). +Alternatively, you can [create your own mainnet network](create-mainnet.md). Note that Haveno is being actively developed. If you find issues or bugs, please let us know. diff --git a/docs/installing.md b/docs/installing.md index 3edb2f23..29a15b54 100644 --- a/docs/installing.md +++ b/docs/installing.md @@ -1,13 +1,13 @@ # Build and run Haveno -These are the steps needed to build and run Haveno. You can test it locally or on our test network using the official Haveno repository. +These are the steps to build and run Haveno using the *official test network*. -> [!note] -> Trying to use Haveno on mainnet? +> [!warning] +> The official Haveno repository does not support making real trades directly. > -> The official Haveno repository does not operate or endorse any mainnet network. +> To make real trades with Haveno, first find a third party network, and then use their installer or build their repository. We do not endorse any networks at this time. > -> Find a third party network and use their installer or build their repository. Alternatively [create your own mainnet network](create-mainnet.md). +> Alternatively, you can [create your own mainnet network](create-mainnet.md). ## Install dependencies From a5417994d65954d93269e477c918d839b7820156 Mon Sep 17 00:00:00 2001 From: ohchase Date: Mon, 25 Nov 2024 10:40:27 -0500 Subject: [PATCH 06/34] flatpak icon support (#1428) --- .../Haveno.AppDir/{haveno.svg => exchange.haveno.Haveno.svg} | 0 desktop/package/linux/Haveno.desktop | 2 +- desktop/package/linux/exchange.haveno.Haveno.yml | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename desktop/package/linux/Haveno.AppDir/{haveno.svg => exchange.haveno.Haveno.svg} (100%) diff --git a/desktop/package/linux/Haveno.AppDir/haveno.svg b/desktop/package/linux/Haveno.AppDir/exchange.haveno.Haveno.svg similarity index 100% rename from desktop/package/linux/Haveno.AppDir/haveno.svg rename to desktop/package/linux/Haveno.AppDir/exchange.haveno.Haveno.svg diff --git a/desktop/package/linux/Haveno.desktop b/desktop/package/linux/Haveno.desktop index a0e32a63..b6d62c22 100644 --- a/desktop/package/linux/Haveno.desktop +++ b/desktop/package/linux/Haveno.desktop @@ -3,7 +3,7 @@ Comment=A decentralized, Tor-based, P2P Monero exchange network. Exec=sh -c "PATH=\"\\$HOME/.local/bin:\\$PATH\"; bin/Haveno %u" GenericName[en_US]=Monero Exchange GenericName=Monero Exchange -Icon=haveno +Icon=exchange.haveno.Haveno Categories=Office;Finance;Java;P2P; Name[en_US]=Haveno Name=Haveno diff --git a/desktop/package/linux/exchange.haveno.Haveno.yml b/desktop/package/linux/exchange.haveno.Haveno.yml index 29246844..50843f8f 100644 --- a/desktop/package/linux/exchange.haveno.Haveno.yml +++ b/desktop/package/linux/exchange.haveno.Haveno.yml @@ -35,7 +35,7 @@ modules: - mkdir -p /app/share/icons/hicolor/128x128/apps/ - mkdir -p /app/share/applications/ - mkdir -p /app/share/metainfo/ - - mv icon.png /app/share/icons/hicolor/128x128/apps/haveno.png + - mv icon.png /app/share/icons/hicolor/128x128/apps/exchange.haveno.Haveno.png - mv Haveno.desktop /app/share/applications/exchange.haveno.Haveno.desktop - mv exchange.haveno.Haveno.metainfo.xml /app/share/metainfo/ From bf452c91da27bd3ff9c98109ce4e29fdde20c10b Mon Sep 17 00:00:00 2001 From: coinstudent2048 <87281755+coinstudent2048@users.noreply.github.com> Date: Mon, 25 Nov 2024 23:42:09 +0800 Subject: [PATCH 07/34] add flatpak version (#1429) --- desktop/package/linux/exchange.haveno.Haveno.metainfo.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml b/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml index 405d8aa6..68051345 100644 --- a/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml +++ b/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml @@ -15,7 +15,6 @@ monero - CC-BY-4.0 AGPL-3.0-only @@ -40,7 +39,6 @@
  • There is No token, because we don't need it. Transactions between traders are secured by non-custodial multisignature transactions on the Monero network.
  • - @@ -61,6 +59,7 @@ intense - - Haveno.desktop + + + From c9cf5351c02111b0d99961b4a5d4f9bbec54002d Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 25 Nov 2024 10:48:27 -0500 Subject: [PATCH 08/34] support usdc (#1439) --- .../haveno/asset/tokens/{USDCoin.java => USDCoinERC20.java} | 6 +++--- .../src/main/resources/META-INF/services/haveno.asset.Asset | 3 ++- core/src/main/java/haveno/core/locale/CurrencyUtil.java | 4 +++- 3 files changed, 8 insertions(+), 5 deletions(-) rename assets/src/main/java/haveno/asset/tokens/{USDCoin.java => USDCoinERC20.java} (85%) diff --git a/assets/src/main/java/haveno/asset/tokens/USDCoin.java b/assets/src/main/java/haveno/asset/tokens/USDCoinERC20.java similarity index 85% rename from assets/src/main/java/haveno/asset/tokens/USDCoin.java rename to assets/src/main/java/haveno/asset/tokens/USDCoinERC20.java index b3e5c121..a65c021d 100644 --- a/assets/src/main/java/haveno/asset/tokens/USDCoin.java +++ b/assets/src/main/java/haveno/asset/tokens/USDCoinERC20.java @@ -19,9 +19,9 @@ package haveno.asset.tokens; import haveno.asset.Erc20Token; -public class USDCoin extends Erc20Token { +public class USDCoinERC20 extends Erc20Token { - public USDCoin() { - super("USD Coin", "USDC"); + public USDCoinERC20() { + super("USD Coin (ERC20)", "USDC-ERC20"); } } diff --git a/assets/src/main/resources/META-INF/services/haveno.asset.Asset b/assets/src/main/resources/META-INF/services/haveno.asset.Asset index 350f6f15..709b9512 100644 --- a/assets/src/main/resources/META-INF/services/haveno.asset.Asset +++ b/assets/src/main/resources/META-INF/services/haveno.asset.Asset @@ -8,4 +8,5 @@ haveno.asset.coins.Ether haveno.asset.coins.Litecoin haveno.asset.coins.Monero haveno.asset.tokens.TetherUSDERC20 -haveno.asset.tokens.TetherUSDTRC20 \ No newline at end of file +haveno.asset.tokens.TetherUSDTRC20 +haveno.asset.tokens.USDCoinERC20 \ No newline at end of file diff --git a/core/src/main/java/haveno/core/locale/CurrencyUtil.java b/core/src/main/java/haveno/core/locale/CurrencyUtil.java index 93f579f4..bd66667d 100644 --- a/core/src/main/java/haveno/core/locale/CurrencyUtil.java +++ b/core/src/main/java/haveno/core/locale/CurrencyUtil.java @@ -201,6 +201,7 @@ public class CurrencyUtil { result.add(new CryptoCurrency("ETH", "Ether")); result.add(new CryptoCurrency("LTC", "Litecoin")); result.add(new CryptoCurrency("USDT-ERC20", "Tether USD (ERC20)")); + result.add(new CryptoCurrency("USDC-ERC20", "USD Coin (ERC20)")); result.sort(TradeCurrency::compareTo); return result; } @@ -328,13 +329,14 @@ public class CurrencyUtil { private static boolean isCryptoCurrencyBase(String currencyCode) { if (currencyCode == null) return false; currencyCode = currencyCode.toUpperCase(); - return currencyCode.equals("USDT"); + return currencyCode.equals("USDT") || currencyCode.equals("USDC"); } public static String getCurrencyCodeBase(String currencyCode) { if (currencyCode == null) return null; currencyCode = currencyCode.toUpperCase(); if (currencyCode.contains("USDT")) return "USDT"; + if (currencyCode.contains("USDC")) return "USDC"; return currencyCode; } From 103c45d4125bae4818cf3cc00379062035ef241e Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 23 Nov 2024 13:41:08 -0500 Subject: [PATCH 09/34] fix showing offer created popup after canceled --- .../java/haveno/desktop/main/offer/MutableOfferViewModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java index 66ca6475..fabad857 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java @@ -613,7 +613,7 @@ public abstract class MutableOfferViewModel ext dataModel.onPlaceOffer(offer, transaction -> { resultHandler.run(); - placeOfferCompleted.set(true); + if (!createOfferCanceled) placeOfferCompleted.set(true); errorMessage.set(null); }, errMessage -> { createOfferRequested = false; From 98e2df3c7ea881918afe5ebbd2c0923c436f2281 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 25 Nov 2024 11:01:15 -0500 Subject: [PATCH 10/34] fix scheduling offers with funds sent to self --- .../haveno/core/offer/OpenOfferManager.java | 22 +++++++++++++------ .../core/xmr/wallet/XmrWalletService.java | 19 ++++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index af898459..c8390a5a 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -1169,16 +1169,24 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe Set scheduledTxs = new HashSet(); for (MoneroTxWallet tx : xmrWalletService.getTxs()) { - // skip if outputs unavailable - if (tx.getIncomingTransfers() == null || tx.getIncomingTransfers().isEmpty()) continue; + // skip if no funds available + BigInteger sentToSelfAmount = xmrWalletService.getAmountSentToSelf(tx); // amount sent to self always shows 0, so compute from destinations manually + if (sentToSelfAmount.equals(BigInteger.ZERO) && (tx.getIncomingTransfers() == null || tx.getIncomingTransfers().isEmpty())) continue; if (!isOutputsAvailable(tx)) continue; if (isTxScheduledByOtherOffer(openOffers, openOffer, tx.getHash())) continue; - // add scheduled tx - for (MoneroIncomingTransfer transfer : tx.getIncomingTransfers()) { - if (transfer.getAccountIndex() == 0) { - scheduledAmount = scheduledAmount.add(transfer.getAmount()); - scheduledTxs.add(tx); + // schedule transaction if funds sent to self, because they are not included in incoming transfers // TODO: fix in libraries? + if (sentToSelfAmount.compareTo(BigInteger.ZERO) > 0) { + scheduledAmount = scheduledAmount.add(sentToSelfAmount); + scheduledTxs.add(tx); + } else if (tx.getIncomingTransfers() != null) { + + // schedule transaction if incoming tranfers to account 0 + for (MoneroIncomingTransfer transfer : tx.getIncomingTransfers()) { + if (transfer.getAccountIndex() == 0) { + scheduledAmount = scheduledAmount.add(transfer.getAmount()); + scheduledTxs.add(tx); + } } } diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 68577665..759dabd0 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -1219,10 +1219,29 @@ public class XmrWalletService extends XmrWalletBase { return cachedAvailableBalance; } + public boolean hasAddress(String address) { + for (MoneroSubaddress subaddress : getSubaddresses()) { + if (subaddress.getAddress().equals(address)) return true; + } + return false; + } + public List getSubaddresses() { return cachedSubaddresses; } + public BigInteger getAmountSentToSelf(MoneroTxWallet tx) { + BigInteger sentToSelfAmount = BigInteger.ZERO; + if (tx.getOutgoingTransfer() != null && tx.getOutgoingTransfer().getDestinations() != null) { + for (MoneroDestination destination : tx.getOutgoingTransfer().getDestinations()) { + if (hasAddress(destination.getAddress())) { + sentToSelfAmount = sentToSelfAmount.add(destination.getAmount()); + } + } + } + return sentToSelfAmount; + } + public List getOutputs(MoneroOutputQuery query) { List filteredOutputs = new ArrayList(); for (MoneroOutputWallet output : cachedOutputs) { From 1f385328deeccc8dff7ad5a0f9ffc2aa11b661db Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 25 Nov 2024 11:50:36 -0500 Subject: [PATCH 11/34] increase rate limit to get offers on testnet --- .../src/main/java/haveno/daemon/grpc/GrpcOffersService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/daemon/src/main/java/haveno/daemon/grpc/GrpcOffersService.java b/daemon/src/main/java/haveno/daemon/grpc/GrpcOffersService.java index 04b294e4..081a0949 100644 --- a/daemon/src/main/java/haveno/daemon/grpc/GrpcOffersService.java +++ b/daemon/src/main/java/haveno/daemon/grpc/GrpcOffersService.java @@ -202,9 +202,9 @@ class GrpcOffersService extends OffersImplBase { new HashMap<>() {{ put(getGetOfferMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 10 : 1, SECONDS)); put(getGetMyOfferMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 10 : 1, SECONDS)); - put(getGetOffersMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 20 : 1, SECONDS)); - put(getGetMyOffersMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 20 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES)); - put(getPostOfferMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 20 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES)); + put(getGetOffersMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 1, SECONDS)); + put(getGetMyOffersMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES)); + put(getPostOfferMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES)); put(getCancelOfferMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 10 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES)); }} ))); From dc8d854709ec23cf39f6ac6b45b1f43e7030e7f1 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 29 Nov 2024 09:52:02 -0500 Subject: [PATCH 12/34] show available monero nodes in network settings --- .../haveno/core/api/XmrConnectionService.java | 41 +++++------- .../java/haveno/core/app/AppStartupState.java | 2 +- .../java/haveno/core/app/P2PNetworkSetup.java | 2 +- .../resources/i18n/displayStrings.properties | 2 + .../i18n/displayStrings_cs.properties | 2 + .../i18n/displayStrings_de.properties | 2 + .../i18n/displayStrings_es.properties | 2 + .../i18n/displayStrings_fa.properties | 2 + .../i18n/displayStrings_fr.properties | 2 + .../i18n/displayStrings_it.properties | 2 + .../i18n/displayStrings_ja.properties | 2 + .../i18n/displayStrings_pt-br.properties | 2 + .../i18n/displayStrings_pt.properties | 2 + .../i18n/displayStrings_ru.properties | 2 + .../i18n/displayStrings_th.properties | 2 + .../i18n/displayStrings_tr.properties | 2 + .../i18n/displayStrings_vi.properties | 2 + .../i18n/displayStrings_zh-hans.properties | 2 + .../i18n/displayStrings_zh-hant.properties | 2 + .../network/MoneroNetworkListItem.java | 29 ++++---- .../settings/network/NetworkSettingsView.fxml | 22 ++---- .../settings/network/NetworkSettingsView.java | 67 +++++++++---------- 22 files changed, 100 insertions(+), 95 deletions(-) diff --git a/core/src/main/java/haveno/core/api/XmrConnectionService.java b/core/src/main/java/haveno/core/api/XmrConnectionService.java index 75be3385..96458a27 100644 --- a/core/src/main/java/haveno/core/api/XmrConnectionService.java +++ b/core/src/main/java/haveno/core/api/XmrConnectionService.java @@ -40,7 +40,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -64,7 +63,6 @@ import monero.common.MoneroRpcConnection; import monero.common.TaskLooper; import monero.daemon.MoneroDaemonRpc; import monero.daemon.model.MoneroDaemonInfo; -import monero.daemon.model.MoneroPeer; @Slf4j @Singleton @@ -85,9 +83,9 @@ public final class XmrConnectionService { private final XmrLocalNode xmrLocalNode; private final MoneroConnectionManager connectionManager; private final EncryptedConnectionList connectionList; - private final ObjectProperty> peers = new SimpleObjectProperty<>(); + private final ObjectProperty> connections = new SimpleObjectProperty<>(); + private final IntegerProperty numConnections = new SimpleIntegerProperty(0); private final ObjectProperty connectionProperty = new SimpleObjectProperty<>(); - private final IntegerProperty numPeers = new SimpleIntegerProperty(0); private final LongProperty chainHeight = new SimpleLongProperty(0); private final DownloadListener downloadListener = new DownloadListener(); @Getter @@ -390,12 +388,12 @@ public final class XmrConnectionService { // ----------------------------- APP METHODS ------------------------------ - public ReadOnlyIntegerProperty numPeersProperty() { - return numPeers; + public ReadOnlyIntegerProperty numConnectionsProperty() { + return numConnections; } - public ReadOnlyObjectProperty> peerConnectionsProperty() { - return peers; + public ReadOnlyObjectProperty> connectionsProperty() { + return connections; } public ReadOnlyObjectProperty connectionProperty() { @@ -403,7 +401,7 @@ public final class XmrConnectionService { } public boolean hasSufficientPeersForBroadcast() { - return numPeers.get() >= getMinBroadcastConnections(); + return numConnections.get() >= getMinBroadcastConnections(); } public LongProperty chainHeightProperty() { @@ -782,16 +780,15 @@ public final class XmrConnectionService { downloadListener.progress(percent, blocksLeft, null); } - // set peer connections - // TODO: peers often uknown due to restricted RPC call, skipping call to get peer connections - // try { - // peers.set(getOnlinePeers()); - // } catch (Exception err) { - // // TODO: peers unknown due to restricted RPC call - // } - // numPeers.set(peers.get().size()); - numPeers.set(lastInfo.getNumOutgoingConnections() + lastInfo.getNumIncomingConnections()); - peers.set(new ArrayList()); + // set available connections + List availableConnections = new ArrayList<>(); + for (MoneroRpcConnection connection : connectionManager.getConnections()) { + if (Boolean.TRUE.equals(connection.isOnline()) && Boolean.TRUE.equals(connection.isAuthenticated())) { + availableConnections.add(connection); + } + } + connections.set(availableConnections); + numConnections.set(availableConnections.size()); // notify update numUpdates.set(numUpdates.get() + 1); @@ -821,12 +818,6 @@ public final class XmrConnectionService { } } - private List getOnlinePeers() { - return daemon.getPeers().stream() - .filter(peer -> peer.isOnline()) - .collect(Collectors.toList()); - } - private boolean isFixedConnection() { return !"".equals(config.xmrNode) || preferences.getMoneroNodesOption() == XmrNodes.MoneroNodesOption.CUSTOM; } diff --git a/core/src/main/java/haveno/core/app/AppStartupState.java b/core/src/main/java/haveno/core/app/AppStartupState.java index 85da2af9..89fe6157 100644 --- a/core/src/main/java/haveno/core/app/AppStartupState.java +++ b/core/src/main/java/haveno/core/app/AppStartupState.java @@ -73,7 +73,7 @@ public class AppStartupState { isWalletSynced.set(true); }); - xmrConnectionService.numPeersProperty().addListener((observable, oldValue, newValue) -> { + xmrConnectionService.numConnectionsProperty().addListener((observable, oldValue, newValue) -> { if (xmrConnectionService.hasSufficientPeersForBroadcast()) hasSufficientPeersForBroadcast.set(true); }); diff --git a/core/src/main/java/haveno/core/app/P2PNetworkSetup.java b/core/src/main/java/haveno/core/app/P2PNetworkSetup.java index 4e90faeb..69609a4b 100644 --- a/core/src/main/java/haveno/core/app/P2PNetworkSetup.java +++ b/core/src/main/java/haveno/core/app/P2PNetworkSetup.java @@ -87,7 +87,7 @@ public class P2PNetworkSetup { BooleanProperty initialP2PNetworkDataReceived = new SimpleBooleanProperty(); p2PNetworkInfoBinding = EasyBind.combine(bootstrapState, bootstrapWarning, p2PService.getNumConnectedPeers(), - xmrConnectionService.numPeersProperty(), hiddenServicePublished, initialP2PNetworkDataReceived, + xmrConnectionService.numConnectionsProperty(), hiddenServicePublished, initialP2PNetworkDataReceived, (state, warning, numP2pPeers, numXmrPeers, hiddenService, dataReceived) -> { String result; int p2pPeers = (int) numP2pPeers; diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 4525c6a3..f3a67e40 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1315,6 +1315,8 @@ settings.net.p2pHeader=Haveno network settings.net.onionAddressLabel=My onion address settings.net.xmrNodesLabel=Use custom Monero nodes settings.net.moneroPeersLabel=Connected peers +settings.net.connection=Connection +settings.net.connected=Connected settings.net.useTorForXmrJLabel=Use Tor for Monero network settings.net.useTorForXmrAfterSyncRadio=After wallet is synchronized settings.net.useTorForXmrOffRadio=Never diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index ada00d83..161a6be7 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -1043,6 +1043,8 @@ settings.net.p2pHeader=Síť Haveno settings.net.onionAddressLabel=Moje onion adresa settings.net.xmrNodesLabel=Použijte vlastní Monero node settings.net.moneroPeersLabel=Připojené peer uzly +settings.net.connection=Připojení +settings.net.connected=Připojeno settings.net.useTorForXmrJLabel=Použít Tor pro Monero síť settings.net.moneroNodesLabel=Monero nody, pro připojení settings.net.useProvidedNodesRadio=Použijte nabízené Monero Core nody diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 79f6704a..198b5999 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -1043,6 +1043,8 @@ settings.net.p2pHeader=Haveno-Netzwerk settings.net.onionAddressLabel=Meine Onion-Adresse settings.net.xmrNodesLabel=Spezifische Monero-Knoten verwenden settings.net.moneroPeersLabel=Verbundene Peers +settings.net.connection=Verbindung +settings.net.connected=Verbunden settings.net.useTorForXmrJLabel=Tor für das Monero-Netzwerk verwenden settings.net.moneroNodesLabel=Mit Monero-Knoten verbinden settings.net.useProvidedNodesRadio=Bereitgestellte Monero-Core-Knoten verwenden diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index c9d60a70..17115d05 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -1044,6 +1044,8 @@ settings.net.p2pHeader=Red Haveno settings.net.onionAddressLabel=Mi dirección onion settings.net.xmrNodesLabel=Utilizar nodos Monero personalizados settings.net.moneroPeersLabel=Pares conectados +settings.net.connection=Conexión +settings.net.connected=Conectado settings.net.useTorForXmrJLabel=Usar Tor para la red Monero settings.net.moneroNodesLabel=Nodos Monero para conectarse settings.net.useProvidedNodesRadio=Utilizar nodos Monero Core proporcionados diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 7f18e3c3..f6db7fff 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -1040,6 +1040,8 @@ settings.net.p2pHeader=Haveno network settings.net.onionAddressLabel=آدرس onion من settings.net.xmrNodesLabel=استفاده از گره‌های Monero اختصاصی settings.net.moneroPeersLabel=همتایان متصل +settings.net.connection=اتصال +settings.net.connected=متصل settings.net.useTorForXmrJLabel=استفاده از Tor برای شبکه مونرو settings.net.moneroNodesLabel=گره‌های Monero در دسترس settings.net.useProvidedNodesRadio=استفاده از نودهای بیتکوین ارائه شده diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index a510fbac..400f58cd 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -1045,6 +1045,8 @@ settings.net.p2pHeader=Le réseau Haveno settings.net.onionAddressLabel=Mon adresse onion settings.net.xmrNodesLabel=Utiliser des nœuds Monero personnalisés settings.net.moneroPeersLabel=Pairs connectés +settings.net.connection=Connexion +settings.net.connected=Connecté settings.net.useTorForXmrJLabel=Utiliser Tor pour le réseau Monero settings.net.moneroNodesLabel=Nœuds Monero pour se connecter à settings.net.useProvidedNodesRadio=Utiliser les nœuds Monero Core fournis diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 09e98ca4..4a8e6844 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -1042,6 +1042,8 @@ settings.net.p2pHeader=Rete Haveno settings.net.onionAddressLabel=Il mio indirizzo onion settings.net.xmrNodesLabel=Usa nodi Monero personalizzati settings.net.moneroPeersLabel=Peer connessi +settings.net.connection=Connessione +settings.net.connected=Connesso settings.net.useTorForXmrJLabel=Usa Tor per la rete Monero settings.net.moneroNodesLabel=Nodi Monero a cui connettersi settings.net.useProvidedNodesRadio=Usa i nodi Monero Core forniti diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 8e6085bc..afd3cd69 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -1043,6 +1043,8 @@ settings.net.p2pHeader=Havenoネットワーク settings.net.onionAddressLabel=私のonionアドレス settings.net.xmrNodesLabel=任意のモネロノードを使う settings.net.moneroPeersLabel=接続されたピア +settings.net.connection=接続 +settings.net.connected=接続されました settings.net.useTorForXmrJLabel=MoneroネットワークにTorを使用 settings.net.moneroNodesLabel=接続するMoneroノード: settings.net.useProvidedNodesRadio=提供されたMonero Core ノードを使う diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 51f2e333..da457db4 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -1044,6 +1044,8 @@ settings.net.p2pHeader=Rede Haveno settings.net.onionAddressLabel=Meu endereço onion settings.net.xmrNodesLabel=Usar nodos personalizados do Monero settings.net.moneroPeersLabel=Pares conectados +settings.net.connection=Conexão +settings.net.connected=Conectado settings.net.useTorForXmrJLabel=Usar Tor na rede Monero settings.net.moneroNodesLabel=Conexão a nodos do Monero settings.net.useProvidedNodesRadio=Usar nodos do Monero Core fornecidos diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 9be31eef..b3985a56 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -1041,6 +1041,8 @@ settings.net.p2pHeader=Rede do Haveno settings.net.onionAddressLabel=O meu endereço onion settings.net.xmrNodesLabel=Usar nós de Monero personalizados settings.net.moneroPeersLabel=Pares conectados +settings.net.connection=Conexão +settings.net.connected=Conectado settings.net.useTorForXmrJLabel=Usar Tor para a rede de Monero settings.net.moneroNodesLabel=Nós de Monero para conectar settings.net.useProvidedNodesRadio=Usar nós de Monero Core providenciados diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 49b37f4c..51d98e1e 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -1040,6 +1040,8 @@ settings.net.p2pHeader=Haveno network settings.net.onionAddressLabel=Мой onion-адрес settings.net.xmrNodesLabel=Использовать особые узлы Monero settings.net.moneroPeersLabel=Подключенные пиры +settings.net.connection=Соединение +settings.net.connected=Подключено settings.net.useTorForXmrJLabel=Использовать Tor для сети Monero settings.net.moneroNodesLabel=Узлы Monero для подключения settings.net.useProvidedNodesRadio=Использовать предоставленные узлы Monero Core diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 15b87518..5460e107 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -1040,6 +1040,8 @@ settings.net.p2pHeader=Haveno network settings.net.onionAddressLabel=ที่อยู่ onion ของฉัน settings.net.xmrNodesLabel=ใช้โหนดเครือข่าย Monero ที่กำหนดเอง settings.net.moneroPeersLabel=เชื่อมต่อกับเน็ตเวิร์ก peers แล้ว +settings.net.connection=การเชื่อมต่อ +settings.net.connected=เชื่อมต่อ settings.net.useTorForXmrJLabel=ใช้ Tor สำหรับเครือข่าย Monero settings.net.moneroNodesLabel=ใช้โหนดเครือข่าย Monero เพื่อเชื่อมต่อ settings.net.useProvidedNodesRadio=ใช้โหนดเครือข่าย Monero ที่ให้มา diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index e7161baa..d807f48a 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -1310,6 +1310,8 @@ settings.net.p2pHeader=Haveno ağı settings.net.onionAddressLabel=Onion adresim settings.net.xmrNodesLabel=Özel Monero düğümleri kullan settings.net.moneroPeersLabel=Bağlı eşler +settings.net.connection=Bağlantı +settings.net.connected=Bağlı settings.net.useTorForXmrJLabel=Monero ağı için Tor kullan settings.net.useTorForXmrAfterSyncRadio=Cüzdan senkronize edildikten sonra settings.net.useTorForXmrOffRadio=Asla diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 5929dd94..321d1bb6 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -1042,6 +1042,8 @@ settings.net.p2pHeader=Haveno network settings.net.onionAddressLabel=Địa chỉ onion của tôi settings.net.xmrNodesLabel=Sử dụng nút Monero thông dụng settings.net.moneroPeersLabel=Các đối tác được kết nối +settings.net.connection=Kết nối +settings.net.connected=Kết nối settings.net.useTorForXmrJLabel=Sử dụng Tor cho mạng Monero settings.net.moneroNodesLabel=nút Monero để kết nối settings.net.useProvidedNodesRadio=Sử dụng các nút Monero Core đã cung cấp diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 0ea093e1..2709bb26 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -1043,6 +1043,8 @@ settings.net.p2pHeader=Haveno 网络 settings.net.onionAddressLabel=我的匿名地址 settings.net.xmrNodesLabel=使用自定义比特币主节点 settings.net.moneroPeersLabel=已连接节点 +settings.net.connection=连接 +settings.net.connected=连接 settings.net.useTorForXmrJLabel=使用 Tor 连接 Monero 网络 settings.net.moneroNodesLabel=需要连接 Monero settings.net.useProvidedNodesRadio=使用公共比特币核心节点 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 95fad5ea..64f5082a 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -1043,6 +1043,8 @@ settings.net.p2pHeader=Haveno 網絡 settings.net.onionAddressLabel=我的匿名地址 settings.net.xmrNodesLabel=使用自定义Monero节点 settings.net.moneroPeersLabel=已連接節點 +settings.net.connection=連接 +settings.net.connected=連接完成 settings.net.useTorForXmrJLabel=使用 Tor 連接 Monero 網絡 settings.net.moneroNodesLabel=需要連接 Monero settings.net.useProvidedNodesRadio=使用公共比特幣核心節點 diff --git a/desktop/src/main/java/haveno/desktop/main/settings/network/MoneroNetworkListItem.java b/desktop/src/main/java/haveno/desktop/main/settings/network/MoneroNetworkListItem.java index 80bd094f..fea0ef6f 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/network/MoneroNetworkListItem.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/network/MoneroNetworkListItem.java @@ -17,28 +17,23 @@ package haveno.desktop.main.settings.network; -import monero.daemon.model.MoneroPeer; +import haveno.core.locale.Res; +import monero.common.MoneroRpcConnection; public class MoneroNetworkListItem { - private final MoneroPeer peer; - - public MoneroNetworkListItem(MoneroPeer peer) { - this.peer = peer; + private final MoneroRpcConnection connection; + private final boolean connected; + + public MoneroNetworkListItem(MoneroRpcConnection connection, boolean connected) { + this.connection = connection; + this.connected = connected; } - public String getOnionAddress() { - return peer.getHost() + ":" + peer.getPort(); + public String getAddress() { + return connection.getUri(); } - public String getVersion() { - return ""; - } - - public String getSubVersion() { - return ""; - } - - public String getHeight() { - return String.valueOf(peer.getHeight()); + public String getConnected() { + return connected ? Res.get("settings.net.connected") : ""; } } diff --git a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml index 3e9d2542..1f3e8840 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml +++ b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml @@ -45,27 +45,17 @@ - - + + - + - + - + - - - - - - - - - - - + diff --git a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java index 931a6e35..546a5550 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java @@ -44,7 +44,6 @@ import haveno.desktop.main.overlays.windows.TorNetworkSettingsWindow; import haveno.desktop.util.GUIUtil; import haveno.network.p2p.P2PService; import haveno.network.p2p.network.Statistic; -import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static javafx.beans.binding.Bindings.createStringBinding; @@ -63,7 +62,6 @@ import javafx.scene.control.TextField; import javafx.scene.control.Toggle; import javafx.scene.control.ToggleGroup; import javafx.scene.layout.GridPane; -import monero.daemon.model.MoneroPeer; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; @@ -79,7 +77,7 @@ public class NetworkSettingsView extends ActivatableView { @FXML TextField onionAddress, sentDataTextField, receivedDataTextField, chainHeightTextField; @FXML - Label p2PPeersLabel, moneroPeersLabel; + Label p2PPeersLabel, moneroConnectionsLabel; @FXML RadioButton useTorForXmrAfterSyncRadio, useTorForXmrOffRadio, useTorForXmrOnRadio; @FXML @@ -87,13 +85,12 @@ public class NetworkSettingsView extends ActivatableView { @FXML TableView p2pPeersTableView; @FXML - TableView moneroPeersTableView; + TableView moneroConnectionsTableView; @FXML TableColumn onionAddressColumn, connectionTypeColumn, creationDateColumn, roundTripTimeColumn, sentBytesColumn, receivedBytesColumn, peerTypeColumn; @FXML - TableColumn moneroPeerAddressColumn, moneroPeerVersionColumn, - moneroPeerSubVersionColumn, moneroPeerHeightColumn; + TableColumn moneroConnectionAddressColumn, moneroConnectionConnectedColumn; @FXML Label rescanOutputsLabel; @FXML @@ -116,7 +113,7 @@ public class NetworkSettingsView extends ActivatableView { private final SortedList moneroSortedList = new SortedList<>(moneroNetworkListItems); private Subscription numP2PPeersSubscription; - private Subscription moneroPeersSubscription; + private Subscription moneroConnectionsSubscription; private Subscription moneroBlockHeightSubscription; private Subscription nodeAddressSubscription; private ChangeListener xmrNodesInputTextFieldFocusListener; @@ -156,17 +153,15 @@ public class NetworkSettingsView extends ActivatableView { p2pHeader.setText(Res.get("settings.net.p2pHeader")); onionAddress.setPromptText(Res.get("settings.net.onionAddressLabel")); xmrNodesLabel.setText(Res.get("settings.net.xmrNodesLabel")); - moneroPeersLabel.setText(Res.get("settings.net.moneroPeersLabel")); + moneroConnectionsLabel.setText(Res.get("settings.net.moneroPeersLabel")); useTorForXmrLabel.setText(Res.get("settings.net.useTorForXmrJLabel")); useTorForXmrAfterSyncRadio.setText(Res.get("settings.net.useTorForXmrAfterSyncRadio")); useTorForXmrOffRadio.setText(Res.get("settings.net.useTorForXmrOffRadio")); useTorForXmrOnRadio.setText(Res.get("settings.net.useTorForXmrOnRadio")); moneroNodesLabel.setText(Res.get("settings.net.moneroNodesLabel")); - moneroPeerAddressColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.onionAddressColumn"))); - moneroPeerAddressColumn.getStyleClass().add("first-column"); - moneroPeerVersionColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.versionColumn"))); - moneroPeerSubVersionColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.subVersionColumn"))); - moneroPeerHeightColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.heightColumn"))); + moneroConnectionAddressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address"))); + moneroConnectionAddressColumn.getStyleClass().add("first-column"); + moneroConnectionConnectedColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.connection"))); localhostXmrNodeInfoLabel.setText(Res.get("settings.net.localhostXmrNodeInfo")); useProvidedNodesRadio.setText(Res.get("settings.net.useProvidedNodesRadio")); useCustomNodesRadio.setText(Res.get("settings.net.useCustomNodesRadio")); @@ -192,19 +187,19 @@ public class NetworkSettingsView extends ActivatableView { rescanOutputsLabel.setVisible(false); rescanOutputsButton.setVisible(false); - GridPane.setMargin(moneroPeersLabel, new Insets(4, 0, 0, 0)); - GridPane.setValignment(moneroPeersLabel, VPos.TOP); + GridPane.setMargin(moneroConnectionsLabel, new Insets(4, 0, 0, 0)); + GridPane.setValignment(moneroConnectionsLabel, VPos.TOP); GridPane.setMargin(p2PPeersLabel, new Insets(4, 0, 0, 0)); GridPane.setValignment(p2PPeersLabel, VPos.TOP); - moneroPeersTableView.setMinHeight(180); - moneroPeersTableView.setPrefHeight(180); - moneroPeersTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - moneroPeersTableView.setPlaceholder(new AutoTooltipLabel(Res.get("table.placeholder.noData"))); - moneroPeersTableView.getSortOrder().add(moneroPeerAddressColumn); - moneroPeerAddressColumn.setSortType(TableColumn.SortType.ASCENDING); - + moneroConnectionAddressColumn.setSortType(TableColumn.SortType.ASCENDING); + moneroConnectionConnectedColumn.setSortType(TableColumn.SortType.DESCENDING); + moneroConnectionsTableView.setMinHeight(180); + moneroConnectionsTableView.setPrefHeight(180); + moneroConnectionsTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + moneroConnectionsTableView.setPlaceholder(new AutoTooltipLabel(Res.get("table.placeholder.noData"))); + moneroConnectionsTableView.getSortOrder().add(moneroConnectionConnectedColumn); p2pPeersTableView.setMinHeight(180); p2pPeersTableView.setPrefHeight(180); @@ -309,11 +304,11 @@ public class NetworkSettingsView extends ActivatableView { rescanOutputsButton.setOnAction(event -> GUIUtil.rescanOutputs(preferences)); - moneroPeersSubscription = EasyBind.subscribe(connectionService.peerConnectionsProperty(), - this::updateMoneroPeersTable); + moneroConnectionsSubscription = EasyBind.subscribe(connectionService.connectionsProperty(), + connections -> updateMoneroConnectionsTable()); moneroBlockHeightSubscription = EasyBind.subscribe(connectionService.chainHeightProperty(), - this::updateChainHeightTextField); + height -> updateMoneroConnectionsTable()); nodeAddressSubscription = EasyBind.subscribe(p2PService.getNetworkNode().nodeAddressProperty(), nodeAddress -> onionAddress.setText(nodeAddress == null ? @@ -333,8 +328,8 @@ public class NetworkSettingsView extends ActivatableView { Statistic.numTotalReceivedMessagesPerSecProperty().get()), Statistic.numTotalReceivedMessagesPerSecProperty())); - moneroSortedList.comparatorProperty().bind(moneroPeersTableView.comparatorProperty()); - moneroPeersTableView.setItems(moneroSortedList); + moneroSortedList.comparatorProperty().bind(moneroConnectionsTableView.comparatorProperty()); + moneroConnectionsTableView.setItems(moneroSortedList); p2pSortedList.comparatorProperty().bind(p2pPeersTableView.comparatorProperty()); p2pPeersTableView.setItems(p2pSortedList); @@ -355,8 +350,8 @@ public class NetworkSettingsView extends ActivatableView { if (nodeAddressSubscription != null) nodeAddressSubscription.unsubscribe(); - if (moneroPeersSubscription != null) - moneroPeersSubscription.unsubscribe(); + if (moneroConnectionsSubscription != null) + moneroConnectionsSubscription.unsubscribe(); if (moneroBlockHeightSubscription != null) moneroBlockHeightSubscription.unsubscribe(); @@ -521,13 +516,15 @@ public class NetworkSettingsView extends ActivatableView { .collect(Collectors.toList())); } - private void updateMoneroPeersTable(List peers) { - moneroNetworkListItems.clear(); - if (peers != null) { - moneroNetworkListItems.setAll(peers.stream() - .map(MoneroNetworkListItem::new) + private void updateMoneroConnectionsTable() { + UserThread.execute(() -> { + if (connectionService.isShutDownStarted()) return; // ignore if shutting down + moneroNetworkListItems.clear(); + moneroNetworkListItems.setAll(connectionService.getConnections().stream() + .map(connection -> new MoneroNetworkListItem(connection, Boolean.TRUE.equals(connection.isConnected()) && connection == connectionService.getConnection())) .collect(Collectors.toList())); - } + updateChainHeightTextField(connectionService.chainHeightProperty().get()); + }); } private void updateChainHeightTextField(Number chainHeight) { From cfaf163bbc42540e3fa594aaa5928c2261bfc259 Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 30 Nov 2024 18:46:32 -0500 Subject: [PATCH 13/34] update account limit hyperlinks --- core/src/main/resources/i18n/displayStrings.properties | 6 +++--- core/src/main/resources/i18n/displayStrings_cs.properties | 4 ++-- core/src/main/resources/i18n/displayStrings_de.properties | 4 ++-- core/src/main/resources/i18n/displayStrings_es.properties | 4 ++-- core/src/main/resources/i18n/displayStrings_fa.properties | 4 ++-- core/src/main/resources/i18n/displayStrings_fr.properties | 4 ++-- core/src/main/resources/i18n/displayStrings_it.properties | 4 ++-- core/src/main/resources/i18n/displayStrings_ja.properties | 4 ++-- .../src/main/resources/i18n/displayStrings_pt-br.properties | 4 ++-- core/src/main/resources/i18n/displayStrings_pt.properties | 4 ++-- core/src/main/resources/i18n/displayStrings_ru.properties | 4 ++-- core/src/main/resources/i18n/displayStrings_th.properties | 4 ++-- core/src/main/resources/i18n/displayStrings_tr.properties | 6 +++--- core/src/main/resources/i18n/displayStrings_vi.properties | 4 ++-- .../main/resources/i18n/displayStrings_zh-hans.properties | 4 ++-- .../main/resources/i18n/displayStrings_zh-hant.properties | 4 ++-- .../desktop/components/AccountStatusTooltipLabel.java | 2 +- 17 files changed, 35 insertions(+), 35 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index f3a67e40..c4becc93 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -414,7 +414,7 @@ offerbook.warning.counterpartyTradeRestrictions=This offer cannot be taken due t offerbook.warning.newVersionAnnouncement=With this version of the software, trading peers can verify and sign each others' payment accounts to create a network of trusted payment accounts.\n\n\ After successfully trading with a peer with a verified payment account, your payment account will be signed and trading limits will be lifted after a certain time interval (length of this interval is based on the verification method).\n\n\ - For more information on account signing, please see the documentation at [HYPERLINK:https://haveno.exchange/wiki/Account_limits#Account_signing]. + For more information on account signing, please see the documentation at [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits/#account-signing]. popup.warning.tradeLimitDueAccountAgeRestriction.seller=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n\ - The buyer''s account has not been signed by an arbitrator or a peer\n\ @@ -2651,7 +2651,7 @@ payment.limits.info=Please be aware that all bank transfers carry a certain amou \n\ This limit only applies to the size of a single trade—you can place as many trades as you like.\n\ \n\ - See more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. + See more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based \ on the following 2 factors:\n\n\ @@ -2669,7 +2669,7 @@ payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade \n\ These limits only apply to the size of a single trade—you can place as many trades as you like. \n\ \n\ - See more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. + See more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=Please confirm your bank allows you to send cash deposits into other peoples' accounts. \ For example, Bank of America and Wells Fargo no longer allow such deposits. diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 161a6be7..cb8af070 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -1969,9 +1969,9 @@ payment.moneyGram.info=Při používání MoneyGram musí XMR kupující zaslat payment.westernUnion.info=Při používání služby Western Union musí kupující XMR zaslat prodejci XMR e-mailem MTCN (sledovací číslo) a fotografii potvrzení. Potvrzení musí jasně uvádět celé jméno prodejce, město, zemi a částku. E-mail prodávajícího se kupujícímu zobrazí během procesu obchodování. payment.halCash.info=Při používání HalCash musí kupující XMR poslat prodejci XMR kód HalCash prostřednictvím textové zprávy z mobilního telefonu.\n\nUjistěte se, že nepřekračujete maximální částku, kterou vám banka umožňuje odesílat pomocí HalCash. Min. částka za výběr je 10 EUR a max. částka je 600 EUR. Pro opakované výběry je to 3000 EUR za příjemce za den a 6000 EUR za příjemce za měsíc. Zkontrolujte prosím tyto limity u své banky, abyste se ujistili, že používají stejné limity, jaké jsou zde uvedeny.\n\nČástka pro výběr musí být násobkem 10 EUR, protože z bankomatu nemůžete vybrat jiné částky. Uživatelské rozhraní na obrazovce vytvořit-nabídku and přijmout-nabídku upraví částku XMR tak, aby částka EUR byla správná. Nemůžete použít tržní cenu, protože částka v EURECH se mění s měnícími se cenami.\n\nV případě sporu musí kupující XMR poskytnout důkaz, že zaslal EURA. # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=Uvědomte si, že u všech bankovních převodů existuje určité riziko zpětného zúčtování. Aby se toto riziko zmírnilo, stanoví Haveno limity pro jednotlivé obchody na základě odhadované úrovně rizika zpětného zúčtování pro použitou platební metodu.\n\nU této platební metody je váš limit pro jednotlivé obchody pro nákup a prodej {2}.\n\nToto omezení se vztahuje pouze na velikost jednoho obchodu - můžete zadat tolik obchodů, kolik chcete.\n\nDalší podrobnosti najdete na wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info=Uvědomte si, že u všech bankovních převodů existuje určité riziko zpětného zúčtování. Aby se toto riziko zmírnilo, stanoví Haveno limity pro jednotlivé obchody na základě odhadované úrovně rizika zpětného zúčtování pro použitou platební metodu.\n\nU této platební metody je váš limit pro jednotlivé obchody pro nákup a prodej {2}.\n\nToto omezení se vztahuje pouze na velikost jednoho obchodu - můžete zadat tolik obchodů, kolik chcete.\n\nDalší podrobnosti najdete na wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=Aby se omezilo riziko zpětného zúčtování, Haveno stanoví limity pro jednotlivé obchody pro tento typ platebního účtu na základě následujících 2 faktorů:\n\n1. Obecné riziko zpětného zúčtování pro platební metodu\n2. Stav podepisování účtu\n\nTento platební účet ještě není podepsán, takže je omezen na nákup {0} za obchod. Po podpisu se limity nákupu zvýší následovně:\n\n● Před podpisem a 30 dní po podpisu bude váš limit nákupu podle obchodu {0}\n● 30 dní po podpisu bude váš limit nákupu podle obchodu {1}\n● 60 dní po podpisu bude váš limit nákupu podle obchodu {2}\n\nPodpisy účtu neovlivňují prodejní limity. Můžete okamžitě prodat {2} v jednom obchodu.\n\nTato omezení platí pouze pro objem jednoho obchodu - můžete zadat tolik obchodů, kolik chcete.\n\nDalší podrobnosti najdete na wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info.withSigning=Aby se omezilo riziko zpětného zúčtování, Haveno stanoví limity pro jednotlivé obchody pro tento typ platebního účtu na základě následujících 2 faktorů:\n\n1. Obecné riziko zpětného zúčtování pro platební metodu\n2. Stav podepisování účtu\n\nTento platební účet ještě není podepsán, takže je omezen na nákup {0} za obchod. Po podpisu se limity nákupu zvýší následovně:\n\n● Před podpisem a 30 dní po podpisu bude váš limit nákupu podle obchodu {0}\n● 30 dní po podpisu bude váš limit nákupu podle obchodu {1}\n● 60 dní po podpisu bude váš limit nákupu podle obchodu {2}\n\nPodpisy účtu neovlivňují prodejní limity. Můžete okamžitě prodat {2} v jednom obchodu.\n\nTato omezení platí pouze pro objem jednoho obchodu - můžete zadat tolik obchodů, kolik chcete.\n\nDalší podrobnosti najdete na wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=Potvrďte, že vám vaše banka umožňuje odesílat hotovostní vklady na účty jiných lidí. Například Bank of America a Wells Fargo již takové vklady nepovolují. diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 198b5999..b920ec9c 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -1970,9 +1970,9 @@ payment.moneyGram.info=Bei der Nutzung von MoneyGram, muss der XMR Käufer die M payment.westernUnion.info=Bei der Nutzung von Western Union, muss der XMR Käufer die MTCN (Tracking-Nummer) Foto der Quittung per E-Mail an den XMR-Verkäufer senden. Die Quittung muss den vollständigen Namen, das Land, die Stadt des Verkäufers und den Betrag deutlich zeigen. Der Käufer bekommt die E-Mail-Adresse des Verkäufers im Handelsprozess angezeigt. payment.halCash.info=Bei Verwendung von HalCash muss der XMR-Käufer dem XMR-Verkäufer den HalCash-Code per SMS vom Mobiltelefon senden.\n\nBitte achten Sie darauf, dass Sie den maximalen Betrag, den Sie bei Ihrer Bank mit HalCash versenden dürfen, nicht überschreiten. Der Mindestbetrag pro Auszahlung beträgt 10 EUR und der Höchstbetrag 600 EUR. Bei wiederholten Abhebungen sind es 3000 EUR pro Empfänger pro Tag und 6000 EUR pro Empfänger pro Monat. Bitte überprüfen Sie diese Limits bei Ihrer Bank, um sicherzustellen, dass sie die gleichen Limits wie hier angegeben verwenden.\n\nDer Auszahlungsbetrag muss ein Vielfaches von 10 EUR betragen, da Sie keine anderen Beträge an einem Geldautomaten abheben können. Die Benutzeroberfläche beim Erstellen und Annehmen eines Angebots passt den XMR-Betrag so an, dass der EUR-Betrag korrekt ist. Sie können keinen marktbasierten Preis verwenden, da sich der EUR-Betrag bei sich ändernden Preisen ändern würde.\n\nIm Streitfall muss der XMR-Käufer den Nachweis erbringen, dass er die EUR geschickt hat. # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=Bitte beachten Sie, dass alle Banküberweisungen mit einem gewissen Rückbuchungsrisiko verbunden sind. Um dieses Risiko zu mindern, setzt Haveno Limits pro Trade fest, je nachdem wie hoch das Rückbuchungsrisiko der Zahlungsmethode ist. \n\nFür diese Zahlungsmethode beträgt Ihr Pro-Trade-Limit zum Kaufen oder Verkaufen {2}.\nDieses Limit gilt nur für die Größe eines einzelnen Trades - Sie können soviele Trades platzieren wie Sie möchten.\n\nFinden Sie mehr Informationen im Wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info=Bitte beachten Sie, dass alle Banküberweisungen mit einem gewissen Rückbuchungsrisiko verbunden sind. Um dieses Risiko zu mindern, setzt Haveno Limits pro Trade fest, je nachdem wie hoch das Rückbuchungsrisiko der Zahlungsmethode ist. \n\nFür diese Zahlungsmethode beträgt Ihr Pro-Trade-Limit zum Kaufen oder Verkaufen {2}.\nDieses Limit gilt nur für die Größe eines einzelnen Trades - Sie können soviele Trades platzieren wie Sie möchten.\n\nFinden Sie mehr Informationen im Wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=Um das Risiko einer Rückbuchung zu minimieren, setzt Haveno für diese Zahlungsmethode Limits pro Trade auf der Grundlage der folgenden 2 Faktoren fest:\n\n1. Allgemeines Rückbuchungsrisiko für die Zahlungsmethode\n2. Status der Kontounterzeichnung\n\nDieses Zahlungskonto ist noch nicht unterzeichnet. Es ist daher auf den Kauf von {0} pro Trade beschränkt ist. Nach der Unterzeichnung werden die Kauflimits wie folgt erhöht:\n\n● Vor der Unterzeichnung und für 30 Tage nach der Unterzeichnung beträgt Ihr Kauflimit pro Trade {0}\n● 30 Tage nach der Unterzeichnung beträgt Ihr Kauflimit pro Trade {1}\n● 60 Tage nach der Unterzeichnung beträgt Ihr Kauflimit pro Trade {2}\n\nVerkaufslimits sind von der Kontounterzeichnung nicht betroffen. Sie können {2} in einem einzigen Trade sofort verkaufen.\n\nDieses Limit gilt nur für die Größe eines einzelnen Trades - Sie können soviele Trades platzieren wie sie möchten.\n\nWeitere Informationen gibt es im Wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info.withSigning=Um das Risiko einer Rückbuchung zu minimieren, setzt Haveno für diese Zahlungsmethode Limits pro Trade auf der Grundlage der folgenden 2 Faktoren fest:\n\n1. Allgemeines Rückbuchungsrisiko für die Zahlungsmethode\n2. Status der Kontounterzeichnung\n\nDieses Zahlungskonto ist noch nicht unterzeichnet. Es ist daher auf den Kauf von {0} pro Trade beschränkt ist. Nach der Unterzeichnung werden die Kauflimits wie folgt erhöht:\n\n● Vor der Unterzeichnung und für 30 Tage nach der Unterzeichnung beträgt Ihr Kauflimit pro Trade {0}\n● 30 Tage nach der Unterzeichnung beträgt Ihr Kauflimit pro Trade {1}\n● 60 Tage nach der Unterzeichnung beträgt Ihr Kauflimit pro Trade {2}\n\nVerkaufslimits sind von der Kontounterzeichnung nicht betroffen. Sie können {2} in einem einzigen Trade sofort verkaufen.\n\nDieses Limit gilt nur für die Größe eines einzelnen Trades - Sie können soviele Trades platzieren wie sie möchten.\n\nWeitere Informationen gibt es im Wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=Bitte bestätigen Sie, dass Ihre Bank Bareinzahlungen in Konten von anderen Personen erlaubt. Zum Beispiel werden diese Einzahlungen bei der Bank of America und Wells Fargo nicht mehr erlaubt. diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index 17115d05..9fd52c57 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -1971,9 +1971,9 @@ payment.moneyGram.info=Al utilizar MoneyGram, el comprador de XMR tiene que envi payment.westernUnion.info=Al utilizar Western Union, el comprador de XMR tiene que enviar el número de seguimiento (MTCN) y una foto del recibo al vendedor de XMR por correo electrónico. El recibo debe mostrar claramente el como el nombre completo del vendedor, país, ciudad y cantidad. Al comprador se le mostrará el correo electrónico del vendedor en el proceso de intercambio. payment.halCash.info=Al usar HalCash el comprador de XMR necesita enviar al vendedor de XMR el código HalCash a través de un mensaje de texto desde el teléfono móvil.\n\nPor favor asegúrese de que no excede la cantidad máxima que su banco le permite enviar con HalCash. La cantidad mínima por retirada es de 10 EUR y el máximo son 600 EUR. Para retiros frecuentes es 3000 por receptor al día y 6000 por receptor al mes. Por favor compruebe estos límites con su banco y asegúrese que son los mismos aquí expuestos.\n\nLa cantidad de retiro debe ser un múltiplo de 10 EUR ya que no se puede retirar otras cantidades desde el cajero automático. La Interfaz de Usuario en la pantalla crear oferta y tomar oferta ajustará la cantidad de XMR para que la cantidad de EUR sea correcta. No puede usar precios basados en el mercado ya que la cantidad de EUR cambiaría con el cambio de precios.\n\nEn caso de disputa el comprador de XMR necesita proveer la prueba de que ha enviado EUR. # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=Por favor, tenga en cuenta que todas las transferencias bancarias tienen cierto riesgo de reversión de pago.\n\nPara disminuir este riesgo, Haveno establece límites por intercambio en función del nivel estimado de riesgo de reversión de pago para el método usado.\n\nPara este método de pago, su límite por intercambio para comprar y vender es {2}.\n\nEste límite solo aplica al tamaño de un intercambio: puede poner tantos intercambios como quira.\n\nConsulte detalles en la wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info=Por favor, tenga en cuenta que todas las transferencias bancarias tienen cierto riesgo de reversión de pago.\n\nPara disminuir este riesgo, Haveno establece límites por intercambio en función del nivel estimado de riesgo de reversión de pago para el método usado.\n\nPara este método de pago, su límite por intercambio para comprar y vender es {2}.\n\nEste límite solo aplica al tamaño de un intercambio: puede poner tantos intercambios como quira.\n\nConsulte detalles en la wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=Para limitar el riesgo de devolución de cargo, Haveno establece límites por compra basados en los 2 siguientes factores:\n\n1. Riesgo general de devolución de cargo para el método de pago\n2. Estado de firmado de cuenta\n\nEsta cuenta de pago aún no ha sido firmada, con lo que ha sido limitada para comprar {0} por intercambio. Después de firmarse, los límites de compra se incrementarán de esta manera:\n\n● Antes de ser firmada, y hasta 30 días después de la firma, su límite por intercambio de compra será {0}\n● 30 días después de la firma, su límite de compra por intercambio será de {1}\n● 60 días después de la firma, su límite de compra por intercambio será de {2}\n\nLos límites de venta no se ven afectados por el firmado de cuentas. Puede vender {2} en un solo \nintercambio inmediatamente.\n\nEstos límites solo aplican al tamaño de un intercambio. Puede hacer tantos intercambios como quiera.\n\n Consulte detalles en la wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits].\n\n +payment.limits.info.withSigning=Para limitar el riesgo de devolución de cargo, Haveno establece límites por compra basados en los 2 siguientes factores:\n\n1. Riesgo general de devolución de cargo para el método de pago\n2. Estado de firmado de cuenta\n\nEsta cuenta de pago aún no ha sido firmada, con lo que ha sido limitada para comprar {0} por intercambio. Después de firmarse, los límites de compra se incrementarán de esta manera:\n\n● Antes de ser firmada, y hasta 30 días después de la firma, su límite por intercambio de compra será {0}\n● 30 días después de la firma, su límite de compra por intercambio será de {1}\n● 60 días después de la firma, su límite de compra por intercambio será de {2}\n\nLos límites de venta no se ven afectados por el firmado de cuentas. Puede vender {2} en un solo \nintercambio inmediatamente.\n\nEstos límites solo aplican al tamaño de un intercambio. Puede hacer tantos intercambios como quiera.\n\n Consulte detalles en la wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits].\n\n payment.cashDeposit.info=Por favor confirme que su banco permite enviar depósitos de efectivo a cuentas de otras personas. Por ejemplo, Bank of America y Wells Fargo ya no permiten estos depósitos. diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index f6db7fff..539c3287 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -1963,9 +1963,9 @@ payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Author payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=زمانی که از HalCash استفاده می‌کنید، خریدار باید کد HalCash را از طریق پیام کوتاه موبایل به فروشنده XMR ارسال کند.\n\nلطفا مطمئن شوید که از حداکثر میزانی که بانک شما برای انتقال از طریق HalCash مجاز می‌داند تجاوز نکرده‌اید. حداقل مقداردر هر برداشت معادل 10 یورو و حداکثر مقدار 600 یورو می‌باشد. این محدودیت برای برداشت‌های تکراری برای هر گیرنده در روز 3000 یورو و در ماه 6000 یورو می‌باشد. لطفا این محدودیت‌ها را با بانک خود مطابقت دهید و مطمئن شوید که آنها هم همین محدودی‌ها را دارند.\n\nمقدار برداشت باید شریبی از 10 یورو باشد چرا که مقادیر غیر از این را نمی‌توانید از طریق ATM برداشت کنید. رابط کاربری در صفحه ساخت پینشهاد و پذیرش پیشنهاد مقدار XMR را به گونه‌ای تنظیم می‌کنند که مقدار EUR درست باشد. شما نمی‌توانید از قیمت بر مبنای بازار استفاده کنید چون مقدار یورو با تغییر قیمت‌ها عوض خواهد شد.\n\nدر صورت بروز اختلاف خریدار XMR باید شواهد مربوط به ارسال یورو را ارائه دهد. # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=لطفا مطمئن شوید که بانک شما اجازه پرداخت سپرده نفد به حساب دیگر افراد را می‌دهد. برای مثال، Bank of America و Wells Fargo دیگر اجازه چنین پرداخت‌هایی را نمی‌دهند. diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 400f58cd..cacd919c 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -1972,9 +1972,9 @@ payment.moneyGram.info=Lors de l'utilisation de MoneyGram, l'acheteur de XMR doi payment.westernUnion.info=Lors de l'utilisation de Western Union, l'acheteur XMR doit envoyer le MTCN (numéro de suivi) et une photo du reçu par e-mail au vendeur de XMR. Le reçu doit indiquer clairement le nom complet du vendeur, la ville, le pays et le montant. L'acheteur verra ensuite s'afficher l'email du vendeur pendant le processus de transaction. payment.halCash.info=Lors de l'utilisation de HalCash, l'acheteur de XMR doit envoyer au vendeur de XMR le code HalCash par SMS depuis son téléphone portable.\n\nVeuillez vous assurer de ne pas dépasser le montant maximum que votre banque vous permet d'envoyer avec HalCash. Le montant minimum par retrait est de 10 EUR et le montant maximum est de 600 EUR. Pour les retraits récurrents, il est de 3000 EUR par destinataire par jour et 6000 EUR par destinataire par mois. Veuillez vérifier ces limites auprès de votre banque pour vous assurer qu'elles utilisent les mêmes limites que celles indiquées ici.\n\nLe montant du retrait doit être un multiple de 10 EUR car vous ne pouvez pas retirer d'autres montants à un distributeur automatique. Pendant les phases de create-offer et take-offer l'affichage de l'interface utilisateur ajustera le montant en XMR afin que le montant en euros soit correct. Vous ne pouvez pas utiliser le prix basé sur le marché, car le montant en euros varierait en fonction de l'évolution des prix.\n\nEn cas de litige, l'acheteur de XMR doit fournir la preuve qu'il a envoyé la somme en EUR. # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=Sachez que tous les virements bancaires comportent un certain risque de rétrofacturation. Pour mitiger ce risque, Haveno fixe des limites par trade en fonction du niveau estimé de risque de rétrofacturation pour la méthode de paiement utilisée.\n\nPour cette méthode de paiement, votre limite de trading pour l'achat et la vente est de {2}.\n\nCette limite ne s'applique qu'à la taille d'une seule transaction. Vous pouvez effectuer autant de transactions que vous le souhaitez.\n\nVous trouverez plus de détails sur le wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info=Sachez que tous les virements bancaires comportent un certain risque de rétrofacturation. Pour mitiger ce risque, Haveno fixe des limites par trade en fonction du niveau estimé de risque de rétrofacturation pour la méthode de paiement utilisée.\n\nPour cette méthode de paiement, votre limite de trading pour l'achat et la vente est de {2}.\n\nCette limite ne s'applique qu'à la taille d'une seule transaction. Vous pouvez effectuer autant de transactions que vous le souhaitez.\n\nVous trouverez plus de détails sur le wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=Afin de limiter le risque de rétrofacturation des achats, Haveno fixe des limites d'achat par transaction pour ce compte de paiement basé sur les 2 facteurs suivants :\n\n1. Risque de rétrofacturation pour le mode de paiement\n2. Statut de signature du compte\n\nCe compte de paiement n'est pas encore signé, il est donc limité à l'achat de {0} par trade. Après sa signature, les limites d'achat augmenteront comme suit :\n\n● Avant la signature, et jusqu'à 30 jours après la signature, votre limite d'achat par trade sera de {0}\n● 30 jours après la signature, votre limite d'achat par trade sera de {1}\n● 60 jours après la signature, votre limite d'achat par trade sera de {2}\n\nLes limites de vente ne sont pas affectées par la signature du compte. Vous pouvez vendre {2} en un seul trade immédiatement.\n\nCes limites s'appliquent uniquement à la taille d'un seul trade-vous pouvez placer autant de trades que vous voulez.\n\n Pour plus d''nformations, rendez vous à [LIEN:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info.withSigning=Afin de limiter le risque de rétrofacturation des achats, Haveno fixe des limites d'achat par transaction pour ce compte de paiement basé sur les 2 facteurs suivants :\n\n1. Risque de rétrofacturation pour le mode de paiement\n2. Statut de signature du compte\n\nCe compte de paiement n'est pas encore signé, il est donc limité à l'achat de {0} par trade. Après sa signature, les limites d'achat augmenteront comme suit :\n\n● Avant la signature, et jusqu'à 30 jours après la signature, votre limite d'achat par trade sera de {0}\n● 30 jours après la signature, votre limite d'achat par trade sera de {1}\n● 60 jours après la signature, votre limite d'achat par trade sera de {2}\n\nLes limites de vente ne sont pas affectées par la signature du compte. Vous pouvez vendre {2} en un seul trade immédiatement.\n\nCes limites s'appliquent uniquement à la taille d'un seul trade-vous pouvez placer autant de trades que vous voulez.\n\n Pour plus d''nformations, rendez vous à [LIEN:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=Veuillez confirmer que votre banque vous permet d'envoyer des dépôts en espèces sur le compte d'autres personnes. Par exemple, Bank of America et Wells Fargo n'autorisent plus de tels dépôts. diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 4a8e6844..2bb5a9bb 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -1966,9 +1966,9 @@ payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Author payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=Quando utilizza HalCash, l'acquirente XMR deve inviare al venditore XMR il codice HalCash tramite un messaggio di testo dal proprio telefono cellulare.\n\nAssicurati di non superare l'importo massimo che la tua banca ti consente di inviare con HalCash. L'importo minimo per prelievo è di 10 EURO, l'importo massimo è di 600 EURO. Per prelievi ripetuti è di 3000 EURO per destinatario al giorno e 6000 EURO per destintario al mese. Verifica i limiti con la tua banca per accertarti che utilizzino gli stessi limiti indicati qui.\n\nL'importo del prelievo deve essere un multiplo di 10 EURO in quanto non è possibile prelevare altri importi da un bancomat. L'interfaccia utente nella schermata di creazione offerta e accettazione offerta modificherà l'importo XMR in modo che l'importo in EURO sia corretto. Non è possibile utilizzare il prezzo di mercato poiché l'importo in EURO cambierebbe al variare dei prezzi.\n\nIn caso di controversia, l'acquirente XMR deve fornire la prova di aver inviato gli EURO. # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=Conferma che la tua banca ti consente di inviare depositi in contanti su conti di altre persone. Ad esempio, Bank of America e Wells Fargo non consentono più tali depositi. diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index afd3cd69..a6a77ca6 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -1969,9 +1969,9 @@ payment.moneyGram.info=MoneyGramを使用する場合、XMRの買い手は認証 payment.westernUnion.info=Western Unionを使用する場合、XMRの買い手はMTCN(追跡番号)と領収書の写真をEメールでXMRの売り手に送信する必要があります。領収書には、売り手の氏名、市区町村、国、金額を明確に記載する必要があります。トレードプロセスにて、売り手のEメールは買い手に表示されます。 payment.halCash.info=HalCashを使用する場合、XMRの買い手は携帯電話からのテキストメッセージを介してXMRの売り手にHalCashコードを送信する必要があります。\n\n銀行がHalCashで送金できる最大額を超えないようにしてください。 1回の出金あたりの最小金額は10EURで、最大金額は600EURです。繰り返し出金する場合は、1日に受取人1人あたり3000EUR、1ヶ月に受取人1人あたり6000EURです。あなたの銀行でも、ここに記載されているのと同じ制限を使用しているか、これらの制限を銀行と照合して確認してください。\n\n出金額は10の倍数EURでなければ、ATMから出金できません。 オファーの作成画面およびオファー受け入れ画面のUIは、EUR金額が正しくなるようにXMR金額を調整します。価格の変化とともにEURの金額は変化するため、市場ベースの価格を使用することはできません。\n\n係争が発生した場合、XMRの買い手はEURを送ったという証明を提出する必要があります。 # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=すべての銀行振込にはある程度の支払取り消しのリスクがあることに気を付けて下さい。\n\nこのリスクを軽減するために、Havenoは使用する支払い方法での支払取り消しリスクの推定レベルに基づいてトレードごとの制限を設定します。\n\n現在使用する支払い方法では、トレードごとの売買制限は{2}です。\n\n制限は各トレードの量のみに適用されることに注意して下さい。トレードできる合計回数には制限はありません。\n\n詳しくはWikiを調べて下さい [HYPERLINK:https://haveno.exchange/wiki/Account_limits] 。 +payment.limits.info=すべての銀行振込にはある程度の支払取り消しのリスクがあることに気を付けて下さい。\n\nこのリスクを軽減するために、Havenoは使用する支払い方法での支払取り消しリスクの推定レベルに基づいてトレードごとの制限を設定します。\n\n現在使用する支払い方法では、トレードごとの売買制限は{2}です。\n\n制限は各トレードの量のみに適用されることに注意して下さい。トレードできる合計回数には制限はありません。\n\n詳しくはWikiを調べて下さい [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits] 。 # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=支払取り消しのリスクを軽減するために、Havenoはこの支払いアカウントに下記の2つの要因に基づいてトレードごとの制限を設定します。\n\n1.使用する支払い方法での支払取り消しリスクの推定レベル\n2.アカウントの署名状況\n\nこの支払いアカウントはまだ無署名ですので、トレードごとに{0}の買い制限があります。 アカウントが署名される後、トレードごとの制限は以下のように成長します:\n\n●署名の前、そして署名から30日間までに、1トレードあたりの買い制限は{0}になります\n●署名から30日間後に、1トレードあたりの買い制限は{1}になります\n●署名から60日間後に、1トレードあたりの買い制限は{2}になります\n\n売り制限は署名状況に関係がありません。現在のところ、1トレードあたりに{2}を売ることができます。\n\n制限は各トレードの量のみに適用されることに注意して下さい。取引できる合計回数には制限はありません。\n\n詳しくは: [HYPERLINK:https://haveno.exchange/wiki/Account_limits] +payment.limits.info.withSigning=支払取り消しのリスクを軽減するために、Havenoはこの支払いアカウントに下記の2つの要因に基づいてトレードごとの制限を設定します。\n\n1.使用する支払い方法での支払取り消しリスクの推定レベル\n2.アカウントの署名状況\n\nこの支払いアカウントはまだ無署名ですので、トレードごとに{0}の買い制限があります。 アカウントが署名される後、トレードごとの制限は以下のように成長します:\n\n●署名の前、そして署名から30日間までに、1トレードあたりの買い制限は{0}になります\n●署名から30日間後に、1トレードあたりの買い制限は{1}になります\n●署名から60日間後に、1トレードあたりの買い制限は{2}になります\n\n売り制限は署名状況に関係がありません。現在のところ、1トレードあたりに{2}を売ることができます。\n\n制限は各トレードの量のみに適用されることに注意して下さい。取引できる合計回数には制限はありません。\n\n詳しくは: [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits] payment.cashDeposit.info=あなたの銀行が他の人の口座に現金入金を送ることを許可していることを確認してください。たとえば、Bank of America と Wells Fargo では、こうした預金は許可されなくなりました。 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index da457db4..2960b16c 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -1973,9 +1973,9 @@ payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Author payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=Ao usar o HalCash, o comprador de XMR precisa enviar ao vendedor de XMR o código HalCash através de uma mensagem de texto do seu telefone.\n\nPor favor, certifique-se de não exceder a quantia máxima que seu banco lhe permite enviar com o HalCash. O valor mínimo de saque é de 10 euros e valor máximo é de 600 EUR. Para saques repetidos é de 3000 euros por destinatário por dia e 6000 euros por destinatário por mês. Por favor confirme esses limites com seu banco para ter certeza de que eles usam os mesmos limites mencionados aqui.\n\nO valor de saque deve ser um múltiplo de 10 euros, pois você não pode sacar notas diferentes de uma ATM. Esse valor em XMR será ajustado na telas de criar e aceitar ofertas para que a quantia de EUR esteja correta. Você não pode usar o preço com base no mercado, pois o valor do EUR estaria mudando com a variação dos preços.\n\nEm caso de disputa, o comprador de XMR precisa fornecer a prova de que enviou o EUR. # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=Certifique-se de que o seu banco permite a realização de depósitos em espécie na conta de terceiros. diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index b3985a56..0f20382b 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -1963,9 +1963,9 @@ payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Author payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=Ao usar o HalCash, o comprador de XMR precisa enviar ao vendedor de XMR o código HalCash através de uma mensagem de texto do seu telemóvel.\n\nPor favor, certifique-se de não exceder a quantia máxima que seu banco lhe permite enviar com o HalCash. A quantia mín. de levantamento é de 10 euros e a quantia máx. é de 600 EUR. Para levantamentos repetidos é de 3000 euros por recipiente por dia e 6000 euros por recipiente por mês. Por favor confirme esses limites com seu banco para ter certeza de que eles usam os mesmos limites mencionados aqui.\n\nA quantia de levantamento deve ser um múltiplo de 10 euros, pois você não pode levantar outras quantias de uma ATM. A interface do utilizador no ecrã para criar oferta e aceitar ofertas ajustará a quantia de XMR para que a quantia de EUR esteja correta. Você não pode usar o preço com base no mercado, pois o valor do EUR estaria mudando com a variação dos preços.\n\nEm caso de disputa, o comprador de XMR precisa fornecer a prova de que enviou o EUR. # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=Por favor, confirme que seu banco permite-lhe enviar depósitos em dinheiro para contas de outras pessoas. Por exemplo, o Bank of America e o Wells Fargo não permitem mais esses depósitos. diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 51d98e1e..864a75b7 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -1964,9 +1964,9 @@ payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Author payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=Используя HalCash, покупатель XMR обязуется отправить продавцу XMR код HalCash через СМС с мобильного телефона.\n\nУбедитесь, что не вы не превысили максимальную сумму, которую ваш банк позволяет отправить с HalCash. Минимальная сумма на вывод средств составляет 10 EUR, а и максимальная — 600 EUR. При повторном выводе средств лимит составляет 3000 EUR на получателя в день и 6000 EUR на получателя в месяц. Просьба сверить эти лимиты с вашим банком и убедиться, что лимиты банка соответствуют лимитам, указанным здесь.\n\nВыводимая сумма должна быть кратна 10 EUR, так как другие суммы снять из банкомата невозможно. Приложение само отрегулирует сумму XMR, чтобы она соответствовала сумме в EUR, во время создания или принятия предложения. Вы не сможете использовать текущий рыночный курс, так как сумма в EUR будет меняться с изменением курса.\n\nВ случае спора покупателю XMR необходимо предоставить доказательство отправки EUR. # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=Убедитесь, что ваш банк позволяет отправлять денежные переводы на счета других лиц. Например, Bank of America и Wells Fargo больше не разрешают такие переводы. diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 5460e107..214d5d9e 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -1964,9 +1964,9 @@ payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Author payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=เมื่อมีการใช้งาน HalCash ผู้ซื้อ XMR จำเป็นต้องส่งรหัส Halcash ให้กับผู้ขายทางข้อความโทรศัพท์มือถือ\n\nโปรดตรวจสอบว่าไม่เกินจำนวนเงินสูงสุดที่ธนาคารของคุณอนุญาตให้คุณส่งด้วย HalCash จำนวนเงินขั้นต่ำในการเบิกถอนคือ 10 EUR และสูงสุดในจำนวนเงิน 600 EUR สำหรับการถอนซ้ำเป็น 3000 EUR ต่อผู้รับและต่อวัน และ 6000 EUR ต่อผู้รับและต่อเดือน โปรดตรวจสอบข้อจำกัดจากทางธนาคารคุณเพื่อให้มั่นใจได้ว่าทางธนาคารได้มีการใช้มาตรฐานข้อกำหนดเดียวกันกับดังที่ระบุไว้ ณ ที่นี่\n\nจำนวนเงินที่ถอนจะต้องเป็นจำนวนเงินหลาย 10 EUR เนื่องจากคุณไม่สามารถถอนเงินอื่น ๆ ออกจากตู้เอทีเอ็มได้ UI ในหน้าจอสร้างข้อเสนอและรับข้อเสนอจะปรับจำนวนเงิน XMR เพื่อให้จำนวนเงิน EUR ถูกต้อง คุณไม่สามารถใช้ราคาตลาดเป็นจำนวนเงิน EUR ซึ่งจะเปลี่ยนแปลงไปตามราคาที่มีการปรับเปลี่ยน\n\nในกรณีที่มีข้อพิพาทผู้ซื้อ XMR ต้องแสดงหลักฐานว่าได้ส่ง EUR แล้ว # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=โปรดยืนยันว่าธนาคารของคุณได้อนุมัติให้คุณสามารถส่งเงินสดให้กับบัญชีบุคคลอื่นได้ ตัวอย่างเช่น บางธนาคารที่ไม่ได้มีการบริการถ่ายโอนเงินสดอย่าง Bank of America และ Wells Fargo diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index d807f48a..cff8e62c 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -411,7 +411,7 @@ offerbook.warning.counterpartyTradeRestrictions=Karşı taraf ticaret kısıtlam offerbook.warning.newVersionAnnouncement=Bu yazılım sürümü ile, ticaret yapan eşler birbirlerinin ödeme hesaplarını doğrulayabilir ve imzalayabilir, böylece güvenilir ödeme hesapları ağı oluşturulabilir.\n\n\ Doğrulanmış ödeme hesabı olan bir eş ile başarılı bir şekilde ticaret yaptıktan sonra, ödeme hesabınız imzalanır ve ticaret limitleri belirli bir zaman aralığından sonra kaldırılır (bu aralığın uzunluğu doğrulama yöntemine bağlıdır).\n\n\ - Hesap imzalama hakkında daha fazla bilgi için, lütfen [HYPERLINK:https://haveno.exchange/wiki/Account_limits#Account_signing] belgelere bakın. + Hesap imzalama hakkında daha fazla bilgi için, lütfen [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits/#account-signing] belgelere bakın. popup.warning.tradeLimitDueAccountAgeRestriction.seller=İzin verilen ticaret miktarı, aşağıdaki kriterlere dayanan güvenlik kısıtlamaları nedeniyle {0} ile sınırlıdır:\n\ - Alıcının hesabı bir hakem veya eş tarafından imzalanmamış\n\ @@ -2644,7 +2644,7 @@ payment.limits.info=Lütfen tüm banka transferlerinin belirli bir miktarda geri \n\ Bu limit yalnızca tek bir işlemin boyutuna uygulanır—istediğiniz kadar işlem yapabilirsiniz.\n\ \n\ - Daha fazla ayrıntı için wiki sayfasına bakın [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. + Daha fazla ayrıntı için wiki sayfasına bakın [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" payment.limits.info.withSigning=Geri ödeme riskini sınırlamak için, Haveno, bu ödeme hesabı türü için işlem başına limitler belirler \ aşağıdaki 2 faktöre dayanır:\n\n\ @@ -2662,7 +2662,7 @@ payment.limits.info.withSigning=Geri ödeme riskini sınırlamak için, Haveno, \n\ Bu limitler yalnızca tek bir işlemin boyutuna uygulanır—istediğiniz kadar işlem yapabilirsiniz. \n\ \n\ - Daha fazla ayrıntı için wiki sayfasına bakın [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. + Daha fazla ayrıntı için wiki sayfasına bakın [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=Lütfen bankanızın başka kişilerin hesaplarına nakit yatırma işlemlerine izin verdiğini onaylayın. \ Örneğin, Bank of America ve Wells Fargo gibi bankalar bu tür yatırımlara artık izin vermemektedir. diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 321d1bb6..03a23db4 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -1966,9 +1966,9 @@ payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Author payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=Khi sử dụng HalCash người mua XMR cần phải gửi cho người bán XMR mã HalCash bằng tin nhắn điện thoại.\n\nVui lòng đảm bảo là lượng tiền này không vượt quá số lượng tối đa mà ngân hàng của bạn cho phép gửi khi dùng HalCash. Số lượng rút tối thiểu là 10 EUR và tối đa là 600 EUR. Nếu rút nhiều lần thì giới hạn sẽ là 3000 EUR/ người nhận/ ngày và 6000 EUR/người nhận/tháng. Vui lòng kiểm tra chéo những giới hạn này với ngân hàng của bạn để chắc chắn là họ cũng dùng những giới hạn như ghi ở đây.\n\nSố tiền rút phải là bội số của 10 EUR vì bạn không thể rút các mệnh giá khác từ ATM. Giao diện người dùng ở phần 'tạo chào giá' và 'chấp nhận chào giá' sẽ điều chỉnh lượng btc sao cho lượng EUR tương ứng sẽ chính xác. Bạn không thể dùng giá thị trường vì lượng EUR có thể sẽ thay đổi khi giá thay đổi.\n\nTrường hợp tranh chấp, người mua XMR cần phải cung cấp bằng chứng chứng minh mình đã gửi EUR. # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Haveno sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://haveno.exchange/wiki/Account_limits]. +payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=Vui lòng xác nhận rằng ngân hàng của bạn cho phép nạp tiền mặt vào tài khoản của người khác. Chẳng hạn, Ngân Hàng Mỹ và Wells Fargo không còn cho phép nạp tiền như vậy nữa. diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 2709bb26..9993a36b 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -1973,9 +1973,9 @@ payment.moneyGram.info=使用 MoneyGram 时,XMR 买方必须将授权号码和 payment.westernUnion.info=使用 Western Union 时,XMR 买方必须通过电子邮件将 MTCN(运单号)和收据照片发送给 XMR 卖方。收据上必须清楚地显示卖方的全名、城市、国家或地区和金额。买方将在交易过程中显示卖方的电子邮件。 payment.halCash.info=使用 HalCash 时,XMR 买方需要通过手机短信向 XMR 卖方发送 HalCash 代码。\n\n请确保不要超过银行允许您用半现金汇款的最高金额。每次取款的最低金额是 10 欧元,最高金额是 10 欧元。金额是 600 欧元。对于重复取款,每天每个接收者 3000 欧元,每月每个接收者 6000 欧元。请与您的银行核对这些限额,以确保它们使用与此处所述相同的限额。\n\n提现金额必须是 10 欧元的倍数,因为您不能从 ATM 机提取其他金额。 创建报价和下单屏幕中的 UI 将调整 XMR 金额,使 EUR 金额正确。你不能使用基于市场的价格,因为欧元的数量会随着价格的变化而变化。\n # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=请注意,所有银行转账都有一定的退款风险。为了降低这一风险,Haveno 基于使用的付款方式的退款风险。\n\n对于付款方式,您的每笔交易的出售和购买的限额为{2}\n\n限制只应用在单笔交易,你可以尽可能多的进行交易。\n\n在 Haveno Wiki 查看更多信息[HYPERLINK:https://haveno.exchange/wiki/Account_limits]。 +payment.limits.info=请注意,所有银行转账都有一定的退款风险。为了降低这一风险,Haveno 基于使用的付款方式的退款风险。\n\n对于付款方式,您的每笔交易的出售和购买的限额为{2}\n\n限制只应用在单笔交易,你可以尽可能多的进行交易。\n\n在 Haveno Wiki 查看更多信息[HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]。 # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=为了降低这一风险,Haveno 基于两个因素对该付款方式每笔交易设置了限制:\n\n1. 使用的付款方法的预估退款风险水平\n2. 您的付款方式的账龄\n\n这个付款账户还没有被验证,所以他每个交易最多购买{0}。在验证之后,购买限制会以以下规则逐渐增加:\n\n●签署前,以及签署后30天内,您的每笔最大交易将限制为{0}\n●签署后30天,每笔最大交易将限制为{1}\n●签署后60天,每笔最大交易将限制为{2}\n\n出售限制不会被账户验证状态限制,你可以理科进行单笔为{2}的交易\n\n限制只应用在单笔交易,你可以尽可能多的进行交易。\n\n在 Haveno Wiki 上查看更多:\nhttps://haveno.exchange/wiki/Account_limits +payment.limits.info.withSigning=为了降低这一风险,Haveno 基于两个因素对该付款方式每笔交易设置了限制:\n\n1. 使用的付款方法的预估退款风险水平\n2. 您的付款方式的账龄\n\n这个付款账户还没有被验证,所以他每个交易最多购买{0}。在验证之后,购买限制会以以下规则逐渐增加:\n\n●签署前,以及签署后30天内,您的每笔最大交易将限制为{0}\n●签署后30天,每笔最大交易将限制为{1}\n●签署后60天,每笔最大交易将限制为{2}\n\n出售限制不会被账户验证状态限制,你可以理科进行单笔为{2}的交易\n\n限制只应用在单笔交易,你可以尽可能多的进行交易。\n\n在 Haveno Wiki 上查看更多:\nhttps://docs.haveno.exchange/the-project/account_limits payment.cashDeposit.info=请确认您的银行允许您将现金存款汇入他人账户。例如,美国银行和富国银行不再允许此类存款。 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 64f5082a..a4123913 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -1967,9 +1967,9 @@ payment.moneyGram.info=使用 MoneyGram 時,XMR 買方必須將授權號碼和 payment.westernUnion.info=使用 Western Union 時,XMR 買方必須通過電子郵件將 MTCN(運單號)和收據照片發送給 XMR 賣方。收據上必須清楚地顯示賣方的全名、城市、國家或地區和金額。買方將在交易過程中顯示賣方的電子郵件。 payment.halCash.info=使用 HalCash 時,XMR 買方需要通過手機短信向 XMR 賣方發送 HalCash 代碼。\n\n請確保不要超過銀行允許您用半現金匯款的最高金額。每次取款的最低金額是 10 歐元,最高金額是 10 歐元。金額是 600 歐元。對於重複取款,每天每個接收者 3000 歐元,每月每個接收者 6000 歐元。請與您的銀行核對這些限額,以確保它們使用與此處所述相同的限額。\n\n提現金額必須是 10 歐元的倍數,因為您不能從 ATM 機提取其他金額。 創建報價和下單屏幕中的 UI 將調整 XMR 金額,使 EUR 金額正確。你不能使用基於市場的價格,因為歐元的數量會隨着價格的變化而變化。\n # suppress inspection "UnusedMessageFormatParameter" -payment.limits.info=請注意,所有銀行轉賬都有一定的退款風險。為了降低這一風險,Haveno 基於使用的付款方式的退款風險。\n\n對於付款方式,您的每筆交易的出售和購買的限額為{2}\n\n限制只應用在單筆交易,你可以儘可能多的進行交易。\n\n在 Haveno Wiki 查看更多信息[HYPERLINK:https://haveno.exchange/wiki/Account_limits]。 +payment.limits.info=請注意,所有銀行轉賬都有一定的退款風險。為了降低這一風險,Haveno 基於使用的付款方式的退款風險。\n\n對於付款方式,您的每筆交易的出售和購買的限額為{2}\n\n限制只應用在單筆交易,你可以儘可能多的進行交易。\n\n在 Haveno Wiki 查看更多信息[HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]。 # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=為了降低這一風險,Haveno 基於兩個因素對該付款方式每筆交易設置了限制:\n\n1. 使用的付款方法的預估退款風險水平\n2. 您的付款方式的賬齡\n\n這個付款賬户還沒有被驗證,所以他每個交易最多購買{0}。在驗證之後,購買限制會以以下規則逐漸增加:\n\n●簽署前,以及簽署後30天內,您的每筆最大交易將限制為{0}\n●簽署後30天,每筆最大交易將限制為{1}\n●簽署後60天,每筆最大交易將限制為{2}\n\n出售限制不會被賬户驗證狀態限制,你可以理科進行單筆為{2}的交易\n\n限制只應用在單筆交易,你可以儘可能多的進行交易。\n\n在 Haveno Wiki 上查看更多:\nhttps://haveno.exchange/wiki/Account_limits +payment.limits.info.withSigning=為了降低這一風險,Haveno 基於兩個因素對該付款方式每筆交易設置了限制:\n\n1. 使用的付款方法的預估退款風險水平\n2. 您的付款方式的賬齡\n\n這個付款賬户還沒有被驗證,所以他每個交易最多購買{0}。在驗證之後,購買限制會以以下規則逐漸增加:\n\n●簽署前,以及簽署後30天內,您的每筆最大交易將限制為{0}\n●簽署後30天,每筆最大交易將限制為{1}\n●簽署後60天,每筆最大交易將限制為{2}\n\n出售限制不會被賬户驗證狀態限制,你可以理科進行單筆為{2}的交易\n\n限制只應用在單筆交易,你可以儘可能多的進行交易。\n\n在 Haveno Wiki 上查看更多:\nhttps://docs.haveno.exchange/the-project/account_limits payment.cashDeposit.info=請確認您的銀行允許您將現金存款匯入他人賬户。例如,美國銀行和富國銀行不再允許此類存款。 diff --git a/desktop/src/main/java/haveno/desktop/components/AccountStatusTooltipLabel.java b/desktop/src/main/java/haveno/desktop/components/AccountStatusTooltipLabel.java index 501efe9a..8ae0b607 100644 --- a/desktop/src/main/java/haveno/desktop/components/AccountStatusTooltipLabel.java +++ b/desktop/src/main/java/haveno/desktop/components/AccountStatusTooltipLabel.java @@ -106,7 +106,7 @@ public class AccountStatusTooltipLabel extends AutoTooltipLabel { learnMoreLink.setWrapText(true); learnMoreLink.setPadding(new Insets(10, 10, 2, 10)); learnMoreLink.getStyleClass().addAll("very-small-text"); - learnMoreLink.setOnAction((e) -> GUIUtil.openWebPage("https://haveno.exchange/wiki/Account_limits")); + learnMoreLink.setOnAction((e) -> GUIUtil.openWebPage("https://docs.haveno.exchange/the-project/account_limits")); VBox vBox = new VBox(2, titleLabel, infoLabel, buyLabel, waitLabel, learnMoreLink); vBox.setPadding(new Insets(2, 0, 2, 0)); From e05ab6f7ed06196377ced113e25b99e0b2d57b76 Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 30 Nov 2024 09:26:31 -0500 Subject: [PATCH 14/34] fix links from offer book chart to buy/sell views --- .../offerbook/OfferBookChartViewModel.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java index 59c90088..c7d0c912 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java @@ -212,7 +212,10 @@ class OfferBookChartViewModel extends ActivatableViewModel { } public boolean isSellOffer(OfferDirection direction) { - return direction == OfferDirection.SELL; + // for cryptocurrency, buy direction is to buy XMR, so we need sell offers + // for traditional currency, buy direction is to sell XMR, so we need buy offers + boolean isCryptoCurrency = CurrencyUtil.isCryptoCurrency(getCurrencyCode()); + return isCryptoCurrency ? direction == OfferDirection.BUY : direction == OfferDirection.SELL; } public boolean isMyOffer(Offer offer) { @@ -423,12 +426,20 @@ class OfferBookChartViewModel extends ActivatableViewModel { private void updateScreenCurrencyInPreferences(OfferDirection direction) { if (isSellOffer(direction)) { - if (CurrencyUtil.isTraditionalCurrency(getCurrencyCode())) { + if (CurrencyUtil.isFiatCurrency(getCurrencyCode())) { preferences.setBuyScreenCurrencyCode(getCurrencyCode()); + } else if (CurrencyUtil.isCryptoCurrency(getCurrencyCode())) { + preferences.setBuyScreenCryptoCurrencyCode(getCurrencyCode()); + } else if (CurrencyUtil.isTraditionalCurrency(getCurrencyCode())) { + preferences.setBuyScreenOtherCurrencyCode(getCurrencyCode()); } } else { - if (CurrencyUtil.isTraditionalCurrency(getCurrencyCode())) { + if (CurrencyUtil.isFiatCurrency(getCurrencyCode())) { preferences.setSellScreenCurrencyCode(getCurrencyCode()); + } else if (CurrencyUtil.isCryptoCurrency(getCurrencyCode())) { + preferences.setSellScreenCryptoCurrencyCode(getCurrencyCode()); + } else if (CurrencyUtil.isTraditionalCurrency(getCurrencyCode())) { + preferences.setSellScreenOtherCurrencyCode(getCurrencyCode()); } } } From 71987400c7c7784c11348fbf9357fec7e1127604 Mon Sep 17 00:00:00 2001 From: woodser Date: Tue, 26 Nov 2024 13:40:11 -0500 Subject: [PATCH 15/34] make or take offer applies wallet funds and computes remaining amount --- .../resources/i18n/displayStrings.properties | 2 +- .../i18n/displayStrings_cs.properties | 2 +- .../i18n/displayStrings_de.properties | 2 +- .../i18n/displayStrings_es.properties | 2 +- .../i18n/displayStrings_fa.properties | 2 +- .../i18n/displayStrings_fr.properties | 2 +- .../i18n/displayStrings_it.properties | 2 +- .../i18n/displayStrings_ja.properties | 2 +- .../i18n/displayStrings_pt-br.properties | 2 +- .../i18n/displayStrings_pt.properties | 2 +- .../i18n/displayStrings_ru.properties | 2 +- .../i18n/displayStrings_th.properties | 2 +- .../i18n/displayStrings_tr.properties | 2 +- .../i18n/displayStrings_vi.properties | 2 +- .../i18n/displayStrings_zh-hans.properties | 2 +- .../i18n/displayStrings_zh-hant.properties | 2 +- .../main/offer/MutableOfferDataModel.java | 31 +++++++++++++------ .../desktop/main/offer/OfferDataModel.java | 26 ++-------------- .../offer/takeoffer/TakeOfferDataModel.java | 28 +++++++++++------ .../main/offer/takeoffer/TakeOfferView.java | 19 +++++++++++- 20 files changed, 77 insertions(+), 59 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index c4becc93..9d5de331 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -107,7 +107,7 @@ shared.chooseTradingAccount=Choose trading account shared.faq=Visit FAQ page shared.yesCancel=Yes, cancel shared.nextStep=Next step -shared.fundFromSavingsWalletButton=Transfer funds from Haveno wallet +shared.fundFromSavingsWalletButton=Apply funds from Haveno wallet shared.fundFromExternalWalletButton=Open your external wallet for funding shared.openDefaultWalletFailed=Failed to open a Monero wallet application. Are you sure you have one installed? shared.belowInPercent=Below % from market price diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index cb8af070..72fe2925 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -103,7 +103,7 @@ shared.faq=Navštívit stránku FAQ shared.yesCancel=Ano, zrušit shared.nextStep=Další krok shared.selectTradingAccount=Vyberte obchodní účet -shared.fundFromSavingsWalletButton=Přesunout finance z Haveno peněženky +shared.fundFromSavingsWalletButton=Použít prostředky z peněženky Haveno shared.fundFromExternalWalletButton=Otevřít vaši externí peněženku pro financování shared.openDefaultWalletFailed=Nepodařilo se otevřít aplikaci moneroové peněženky. Jste si jisti, že máte nějakou nainstalovanou? shared.belowInPercent=% pod tržní cenou diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index b920ec9c..7a7a5643 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -103,7 +103,7 @@ shared.faq=Zur FAQ Seite shared.yesCancel=Ja, abbrechen shared.nextStep=Nächster Schritt shared.selectTradingAccount=Handelskonto auswählen -shared.fundFromSavingsWalletButton=Gelder aus Haveno-Wallet überweisen +shared.fundFromSavingsWalletButton=Wenden Sie Gelder aus der Haveno-Wallet an shared.fundFromExternalWalletButton=Ihre externe Wallet zum Finanzieren öffnen shared.openDefaultWalletFailed=Das Öffnen des Standardprogramms für Monero-Wallets ist fehlgeschlagen. Sind Sie sicher, dass Sie eines installiert haben? shared.belowInPercent=% unter dem Marktpreis diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index 9fd52c57..2ff762f8 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -103,7 +103,7 @@ shared.faq=Visitar web preguntas frecuentes shared.yesCancel=Sí, cancelar shared.nextStep=Siguiente paso shared.selectTradingAccount=Selecionar cuenta de intercambio -shared.fundFromSavingsWalletButton=Transferir fondos desde la cartera Haveno +shared.fundFromSavingsWalletButton=Aplicar fondos desde la billetera de Haveno shared.fundFromExternalWalletButton=Abrir su monedero externo para agregar fondos shared.openDefaultWalletFailed=Fallo al abrir la aplicación de cartera predeterminada. ¿Tal vez no tenga una instalada? shared.belowInPercent=% por debajo del precio de mercado diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 539c3287..ed78e6ec 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -103,7 +103,7 @@ shared.faq=Visit FAQ page shared.yesCancel=بله، لغو شود shared.nextStep=گام بعدی shared.selectTradingAccount=حساب معاملات را انتخاب کنید -shared.fundFromSavingsWalletButton=انتقال وجه از کیف Haveno +shared.fundFromSavingsWalletButton=اعمال وجه از کیف پول هاونو shared.fundFromExternalWalletButton=برای تهیه پول، کیف پول بیرونی خود را باز کنید shared.openDefaultWalletFailed=Failed to open a Monero wallet application. Are you sure you have one installed? shared.belowInPercent= ٪ زیر قیمت بازار diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index cacd919c..252b3146 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -103,7 +103,7 @@ shared.faq=Visitez la page FAQ shared.yesCancel=Oui, annuler shared.nextStep=Étape suivante shared.selectTradingAccount=Sélectionner le compte de trading -shared.fundFromSavingsWalletButton=Transférer des fonds depuis le portefeuille Haveno +shared.fundFromSavingsWalletButton=Appliquer les fonds depuis le portefeuille Haveno shared.fundFromExternalWalletButton=Ouvrez votre portefeuille externe pour provisionner shared.openDefaultWalletFailed=L'ouverture de l'application de portefeuille Monero par défaut a échoué. Êtes-vous sûr de l'avoir installée? shared.belowInPercent=% sous le prix du marché diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 2bb5a9bb..b2f67e19 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -103,7 +103,7 @@ shared.faq=Visit FAQ page shared.yesCancel=Si, annulla shared.nextStep=Passo successivo shared.selectTradingAccount=Seleziona conto di trading -shared.fundFromSavingsWalletButton=Trasferisci fondi dal portafoglio Haveno +shared.fundFromSavingsWalletButton=Applica fondi dal portafoglio Haveno shared.fundFromExternalWalletButton=Apri il tuo portafoglio esterno per aggiungere fondi shared.openDefaultWalletFailed=Failed to open a Monero wallet application. Are you sure you have one installed? shared.belowInPercent=Sotto % del prezzo di mercato diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index a6a77ca6..478e1135 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -103,7 +103,7 @@ shared.faq=FAQを参照する shared.yesCancel=はい、取り消します shared.nextStep=次へ shared.selectTradingAccount=取引アカウントを選択 -shared.fundFromSavingsWalletButton=Havenoウォレットから資金を移動する +shared.fundFromSavingsWalletButton=Havenoウォレットから資金を適用 shared.fundFromExternalWalletButton=外部のwalletを開く shared.openDefaultWalletFailed=ビットコインウォレットのアプリを開けませんでした。インストールされているか確認して下さい。 shared.belowInPercent=市場価格から%以下 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 2960b16c..8fa3d80a 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -103,7 +103,7 @@ shared.faq=Visit FAQ page shared.yesCancel=Sim, cancelar shared.nextStep=Próximo passo shared.selectTradingAccount=Selecionar conta de negociação -shared.fundFromSavingsWalletButton=Transferir fundos da carteira Haveno +shared.fundFromSavingsWalletButton=Aplicar fundos da carteira Haveno shared.fundFromExternalWalletButton=Abrir sua carteira externa para prover fundos shared.openDefaultWalletFailed=Failed to open a Monero wallet application. Are you sure you have one installed? shared.belowInPercent=% abaixo do preço de mercado diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 0f20382b..27f94191 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -103,7 +103,7 @@ shared.faq=Visit FAQ page shared.yesCancel=Sim, cancelar shared.nextStep=Próximo passo shared.selectTradingAccount=Selecionar conta de negociação -shared.fundFromSavingsWalletButton=Transferir fundos da carteira Haveno +shared.fundFromSavingsWalletButton=Aplicar fundos da carteira Haveno shared.fundFromExternalWalletButton=Abrir sua carteira externa para o financiamento shared.openDefaultWalletFailed=Failed to open a Monero wallet application. Are you sure you have one installed? shared.belowInPercent=Abaixo % do preço de mercado diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 864a75b7..6f1ba86d 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -103,7 +103,7 @@ shared.faq=Visit FAQ page shared.yesCancel=Да, отменить shared.nextStep=Далее shared.selectTradingAccount=Выбрать торговый счёт -shared.fundFromSavingsWalletButton=Перевести средства с кошелька Haveno +shared.fundFromSavingsWalletButton=Применить средства из кошелька Haveno shared.fundFromExternalWalletButton=Открыть внешний кошелёк для пополнения shared.openDefaultWalletFailed=Failed to open a Monero wallet application. Are you sure you have one installed? shared.belowInPercent=% ниже рыночного курса diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 214d5d9e..b603454c 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -103,7 +103,7 @@ shared.faq=Visit FAQ page shared.yesCancel=ใช่ ยกเลิก shared.nextStep=ขั้นถัดไป shared.selectTradingAccount=เลือกบัญชีการซื้อขาย -shared.fundFromSavingsWalletButton=โอนเงินจาก Haveno wallet +shared.fundFromSavingsWalletButton=ใช้เงินจากกระเป๋าเงิน Haveno shared.fundFromExternalWalletButton=เริ่มทำการระดมเงินทุนหาแหล่งเงินจากกระเป๋าสตางค์ภายนอกของคุณ shared.openDefaultWalletFailed=Failed to open a Monero wallet application. Are you sure you have one installed? shared.belowInPercent=ต่ำกว่า % จากราคาตลาด diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index cff8e62c..7656d581 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -107,7 +107,7 @@ shared.chooseTradingAccount=İşlem hesabını seç shared.faq=SSS sayfasını ziyaret et shared.yesCancel=Evet, iptal et shared.nextStep=Sonraki adım -shared.fundFromSavingsWalletButton=Haveno cüzdanından fon transfer et +shared.fundFromSavingsWalletButton=Haveno cüzdanından fonları uygula shared.fundFromExternalWalletButton=Fonlama için harici cüzdanını aç shared.openDefaultWalletFailed=Bir Monero cüzdan uygulaması açılamadı. Yüklü olduğundan emin misiniz? shared.belowInPercent=Piyasa fiyatının altında % diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 03a23db4..8530cf46 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -103,7 +103,7 @@ shared.faq=Visit FAQ page shared.yesCancel=Có, hủy shared.nextStep=Bước tiếp theo shared.selectTradingAccount=Chọn tài khoản giao dịch -shared.fundFromSavingsWalletButton=Chuyển tiền từ Ví Haveno +shared.fundFromSavingsWalletButton=Áp dụng tiền từ ví Haveno shared.fundFromExternalWalletButton=Mở ví ngoài để nộp tiền shared.openDefaultWalletFailed=Failed to open a Monero wallet application. Are you sure you have one installed? shared.belowInPercent=Thấp hơn % so với giá thị trường diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 9993a36b..91166e64 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -103,7 +103,7 @@ shared.faq=访问 FAQ 页面 shared.yesCancel=是的,取消 shared.nextStep=下一步 shared.selectTradingAccount=选择交易账户 -shared.fundFromSavingsWalletButton=从 Haveno 钱包资金划转 +shared.fundFromSavingsWalletButton=从 Haveno 钱包申请资金 shared.fundFromExternalWalletButton=从您的外部钱包充值 shared.openDefaultWalletFailed=打开默认的比特币钱包应用程序失败了。您确定您安装了吗? shared.belowInPercent=低于市场价格 % diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index a4123913..37d5bfbb 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -103,7 +103,7 @@ shared.faq=訪問 FAQ 頁面 shared.yesCancel=是的,取消 shared.nextStep=下一步 shared.selectTradingAccount=選擇交易賬户 -shared.fundFromSavingsWalletButton=從 Haveno 錢包資金劃轉 +shared.fundFromSavingsWalletButton=從 Haveno 錢包申請資金 shared.fundFromExternalWalletButton=從您的外部錢包充值 shared.openDefaultWalletFailed=打開默認的比特幣錢包應用程序失敗了。您確定您安裝了嗎? shared.belowInPercent=低於市場價格 % diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java index 10ecb181..325e7f29 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java @@ -20,6 +20,8 @@ package haveno.desktop.main.offer; import static com.google.common.base.Preconditions.checkNotNull; import com.google.inject.Inject; import com.google.inject.name.Named; + +import haveno.common.UserThread; import haveno.common.handlers.ErrorMessageHandler; import haveno.common.util.MathUtils; import haveno.common.util.Utilities; @@ -176,7 +178,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { if (isTabSelected) priceFeedService.setCurrencyCode(tradeCurrencyCode.get()); - updateBalance(); + updateBalances(); } @Override @@ -205,7 +207,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { xmrBalanceListener = new XmrBalanceListener(getAddressEntry().getSubaddressIndex()) { @Override public void onBalanceChanged(BigInteger balance) { - updateBalance(); + updateBalances(); } }; @@ -246,7 +248,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { calculateVolume(); calculateTotalToPay(); - updateBalance(); + updateBalances(); setSuggestedSecurityDeposit(getPaymentAccount()); return true; @@ -273,6 +275,19 @@ public abstract class MutableOfferDataModel extends OfferDataModel { priceFeedService.setCurrencyCode(tradeCurrencyCode.get()); } + protected void updateBalances() { + super.updateBalances(); + + // update remaining balance + UserThread.await(() -> { + missingCoin.set(offerUtil.getBalanceShortage(totalToPay.get(), balance.get())); + isXmrWalletFunded.set(offerUtil.isBalanceSufficient(totalToPay.get(), balance.get())); + if (totalToPay.get() != null && isXmrWalletFunded.get() && !showWalletFundedNotification.get()) { + showWalletFundedNotification.set(true); + } + }); + } + /////////////////////////////////////////////////////////////////////////////////////////// // UI actions /////////////////////////////////////////////////////////////////////////////////////////// @@ -393,11 +408,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { void fundFromSavingsWallet() { this.useSavingsWallet = true; - updateBalance(); - if (!isXmrWalletFunded.get()) { - this.useSavingsWallet = false; - updateBalance(); - } + updateBalances(); } protected void setMarketPriceMarginPct(double marketPriceMargin) { @@ -492,7 +503,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { } } - updateBalance(); + updateBalances(); } void calculateMinVolume() { @@ -545,7 +556,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { BigInteger feeAndSecDeposit = getSecurityDeposit().add(makerFee); BigInteger total = isBuyOffer() ? feeAndSecDeposit : feeAndSecDeposit.add(amount.get()); totalToPay.set(total); - updateBalance(); + updateBalances(); } } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/OfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/offer/OfferDataModel.java index 533f2834..8b92477e 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/OfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/OfferDataModel.java @@ -65,29 +65,7 @@ public abstract class OfferDataModel extends ActivatableDataModel { this.offerUtil = offerUtil; } - protected void updateBalance() { - updateBalances(); - UserThread.await(() -> { - missingCoin.set(offerUtil.getBalanceShortage(totalToPay.get(), balance.get())); - isXmrWalletFunded.set(offerUtil.isBalanceSufficient(totalToPay.get(), balance.get())); - if (totalToPay.get() != null && isXmrWalletFunded.get() && !showWalletFundedNotification.get()) { - showWalletFundedNotification.set(true); - } - }); - } - - protected void updateAvailableBalance() { - updateBalances(); - UserThread.await(() -> { - missingCoin.set(offerUtil.getBalanceShortage(totalToPay.get(), availableBalance.get())); - isXmrWalletFunded.set(offerUtil.isBalanceSufficient(totalToPay.get(), availableBalance.get())); - if (totalToPay.get() != null && isXmrWalletFunded.get() && !showWalletFundedNotification.get()) { - showWalletFundedNotification.set(true); - } - }); - } - - private void updateBalances() { + protected void updateBalances() { BigInteger tradeWalletBalance = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex()); BigInteger tradeWalletAvailableBalance = xmrWalletService.getAvailableBalanceForSubaddress(addressEntry.getSubaddressIndex()); BigInteger walletBalance = xmrWalletService.getBalance(); @@ -101,6 +79,8 @@ public abstract class OfferDataModel extends ActivatableDataModel { availableBalance.set(totalToPay.get().min(totalAvailableBalance)); } } else { + totalBalance = tradeWalletBalance; + totalAvailableBalance = tradeWalletAvailableBalance; balance.set(tradeWalletBalance); availableBalance.set(tradeWalletAvailableBalance); } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferDataModel.java index 84210538..beae7c11 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferDataModel.java @@ -20,6 +20,7 @@ package haveno.desktop.main.offer.takeoffer; import com.google.inject.Inject; import haveno.common.ThreadUtils; +import haveno.common.UserThread; import haveno.common.handlers.ErrorMessageHandler; import haveno.core.account.witness.AccountAgeWitnessService; import haveno.core.filter.FilterManager; @@ -131,7 +132,7 @@ class TakeOfferDataModel extends OfferDataModel { addListeners(); - updateAvailableBalance(); + updateBalances(); // TODO In case that we have funded but restarted, or canceled but took again the offer we would need to // store locally the result when we received the funding tx(s). @@ -192,7 +193,7 @@ class TakeOfferDataModel extends OfferDataModel { balanceListener = new XmrBalanceListener(addressEntry.getSubaddressIndex()) { @Override public void onBalanceChanged(BigInteger balance) { - updateAvailableBalance(); + updateBalances(); } }; @@ -227,6 +228,19 @@ class TakeOfferDataModel extends OfferDataModel { ThreadUtils.submitToPool(() -> xmrWalletService.resetAddressEntriesForOpenOffer(offer.getId())); } + protected void updateBalances() { + super.updateBalances(); + + // update remaining balance + UserThread.await(() -> { + missingCoin.set(offerUtil.getBalanceShortage(totalToPay.get(), balance.get())); + isXmrWalletFunded.set(offerUtil.isBalanceSufficient(totalToPay.get(), availableBalance.get())); + if (totalToPay.get() != null && isXmrWalletFunded.get() && !showWalletFundedNotification.get()) { + showWalletFundedNotification.set(true); + } + }); + } + /////////////////////////////////////////////////////////////////////////////////////////// // UI actions @@ -287,11 +301,7 @@ class TakeOfferDataModel extends OfferDataModel { void fundFromSavingsWallet() { useSavingsWallet = true; - updateAvailableBalance(); - if (!isXmrWalletFunded.get()) { - this.useSavingsWallet = false; - updateAvailableBalance(); - } + updateBalances(); } @@ -369,7 +379,7 @@ class TakeOfferDataModel extends OfferDataModel { volume.set(volumeByAmount); - updateAvailableBalance(); + updateBalances(); } } @@ -391,7 +401,7 @@ class TakeOfferDataModel extends OfferDataModel { totalToPay.set(feeAndSecDeposit.add(amount.get())); else totalToPay.set(feeAndSecDeposit); - updateAvailableBalance(); + updateBalances(); log.debug("totalToPay {}", totalToPay.get()); } } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java index 6f4e9635..6f999133 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java @@ -154,6 +154,7 @@ public class TakeOfferView extends ActivatableViewAndModel missingCoinListener; private int gridRow = 0; private final HashMap paymentAccountWarningDisplayed = new HashMap<>(); @@ -191,6 +192,8 @@ public class TakeOfferView extends ActivatableViewAndModel { + if (!newValue.toString().equals("")) { + updateQrCode(); + } + }; + } + private void addListeners() { amountTextField.focusedProperty().addListener(amountFocusedListener); model.dataModel.getShowWalletFundedNotification().addListener(getShowWalletFundedNotificationListener); + model.dataModel.getMissingCoin().addListener(missingCoinListener); } private void removeListeners() { amountTextField.focusedProperty().removeListener(amountFocusedListener); model.dataModel.getShowWalletFundedNotification().removeListener(getShowWalletFundedNotificationListener); + model.dataModel.getMissingCoin().removeListener(missingCoinListener); } /////////////////////////////////////////////////////////////////////////////////////////// From 1aef8a6babf7ce5207790d035a54dbffcc8fd4e9 Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 30 Nov 2024 10:06:42 -0500 Subject: [PATCH 16/34] fix deadlock on startup while awaiting monero connection --- core/src/main/java/haveno/core/api/XmrConnectionService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/api/XmrConnectionService.java b/core/src/main/java/haveno/core/api/XmrConnectionService.java index 96458a27..3f50f4aa 100644 --- a/core/src/main/java/haveno/core/api/XmrConnectionService.java +++ b/core/src/main/java/haveno/core/api/XmrConnectionService.java @@ -142,7 +142,7 @@ public final class XmrConnectionService { p2PService.addP2PServiceListener(new P2PServiceListener() { @Override public void onTorNodeReady() { - initialize(); + ThreadUtils.submitToPool(() -> initialize()); } @Override public void onHiddenServicePublished() {} From 7f6d28f1fbb5033f85ac35c68d0f5c1f8d4d71de Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 30 Nov 2024 13:17:05 -0500 Subject: [PATCH 17/34] prompt to fall back on startup error with custom node --- .../haveno/core/api/XmrConnectionService.java | 52 +++++++++++++------ .../haveno/core/app/HavenoHeadlessApp.java | 1 + .../java/haveno/core/app/HavenoSetup.java | 13 +++++ .../java/haveno/core/app/WalletAppSetup.java | 2 +- .../core/xmr/wallet/XmrWalletService.java | 7 +-- .../resources/i18n/displayStrings.properties | 3 ++ .../java/haveno/desktop/main/MainView.java | 1 + .../haveno/desktop/main/MainViewModel.java | 30 +++++++++++ 8 files changed, 89 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/haveno/core/api/XmrConnectionService.java b/core/src/main/java/haveno/core/api/XmrConnectionService.java index 3f50f4aa..664daaca 100644 --- a/core/src/main/java/haveno/core/api/XmrConnectionService.java +++ b/core/src/main/java/haveno/core/api/XmrConnectionService.java @@ -43,6 +43,7 @@ import java.util.Set; import org.apache.commons.lang3.exception.ExceptionUtils; +import javafx.beans.property.BooleanProperty; import javafx.beans.property.IntegerProperty; import javafx.beans.property.LongProperty; import javafx.beans.property.ObjectProperty; @@ -50,6 +51,7 @@ import javafx.beans.property.ReadOnlyDoubleProperty; import javafx.beans.property.ReadOnlyIntegerProperty; import javafx.beans.property.ReadOnlyLongProperty; import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleLongProperty; import javafx.beans.property.SimpleObjectProperty; @@ -89,6 +91,8 @@ public final class XmrConnectionService { private final LongProperty chainHeight = new SimpleLongProperty(0); private final DownloadListener downloadListener = new DownloadListener(); @Getter + private final BooleanProperty connectionServiceFallbackHandlerActive = new SimpleBooleanProperty(); + @Getter private final StringProperty connectionServiceErrorMsg = new SimpleStringProperty(); private final LongProperty numUpdates = new SimpleLongProperty(0); private Socks5ProxyProvider socks5ProxyProvider; @@ -99,6 +103,7 @@ public final class XmrConnectionService { private Boolean isConnected = false; @Getter private MoneroDaemonInfo lastInfo; + private Long lastFallbackInvocation; private Long lastLogPollErrorTimestamp; private long lastLogDaemonNotSyncedTimestamp; private Long syncStartHeight; @@ -115,6 +120,8 @@ public final class XmrConnectionService { private int numRequestsLastMinute; private long lastSwitchTimestamp; private Set excludedConnections = new HashSet<>(); + private static final long FALLBACK_INVOCATION_PERIOD_MS = 1000 * 60 * 1; // offer to fallback up to once every minute + private boolean fallbackApplied; @Inject public XmrConnectionService(P2PService p2PService, @@ -424,6 +431,19 @@ public final class XmrConnectionService { return numUpdates; } + public void fallbackToBestConnection() { + if (isShutDownStarted) return; + if (xmrNodes.getProvidedXmrNodes().isEmpty()) { + log.warn("Falling back to public nodes"); + preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PUBLIC.ordinal()); + } else { + log.warn("Falling back to provided nodes"); + preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PROVIDED.ordinal()); + } + fallbackApplied = true; + initializeConnections(); + } + // ------------------------------- HELPERS -------------------------------- private void doneDownload() { @@ -533,7 +553,7 @@ public final class XmrConnectionService { } // restore connections - if ("".equals(config.xmrNode)) { + if (!isFixedConnection()) { // load previous or default connections if (coreContext.isApiUser()) { @@ -569,10 +589,7 @@ public final class XmrConnectionService { } // restore last connection - if (isFixedConnection()) { - if (getConnections().size() != 1) throw new IllegalStateException("Expected connection list to have 1 fixed connection but was: " + getConnections().size()); - connectionManager.setConnection(getConnections().get(0)); - } else if (connectionList.getCurrentConnectionUri().isPresent() && connectionManager.hasConnection(connectionList.getCurrentConnectionUri().get())) { + if (connectionList.getCurrentConnectionUri().isPresent() && connectionManager.hasConnection(connectionList.getCurrentConnectionUri().get())) { if (!xmrLocalNode.shouldBeIgnored() || !xmrLocalNode.equalsUri(connectionList.getCurrentConnectionUri().get())) { connectionManager.setConnection(connectionList.getCurrentConnectionUri().get()); } @@ -592,7 +609,7 @@ public final class XmrConnectionService { maybeStartLocalNode(); // update connection - if (!isFixedConnection() && (connectionManager.getConnection() == null || connectionManager.getAutoSwitch())) { + if (connectionManager.getConnection() == null || connectionManager.getAutoSwitch()) { MoneroRpcConnection bestConnection = getBestAvailableConnection(); if (bestConnection != null) setConnection(bestConnection); } @@ -614,6 +631,7 @@ public final class XmrConnectionService { } // notify initial connection + lastRefreshPeriodMs = getRefreshPeriodMs(); onConnectionChanged(connectionManager.getConnection()); } @@ -716,16 +734,14 @@ public final class XmrConnectionService { // skip handling if shutting down if (isShutDownStarted) return; - // fallback to provided or public nodes if custom connection fails on startup - if (lastInfo == null && "".equals(config.xmrNode) && preferences.getMoneroNodesOption() == XmrNodes.MoneroNodesOption.CUSTOM) { - if (xmrNodes.getProvidedXmrNodes().isEmpty()) { - log.warn("Failed to fetch daemon info from custom node on startup, falling back to public nodes: " + e.getMessage()); - preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PUBLIC.ordinal()); - } else { - log.warn("Failed to fetch daemon info from custom node on startup, falling back to provided nodes: " + e.getMessage()); - preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PROVIDED.ordinal()); + // invoke fallback handling on startup error + boolean canFallback = isFixedConnection() || isCustomConnections(); + if (lastInfo == null && canFallback) { + if (!connectionServiceFallbackHandlerActive.get() && (lastFallbackInvocation == null || System.currentTimeMillis() - lastFallbackInvocation > FALLBACK_INVOCATION_PERIOD_MS)) { + log.warn("Failed to fetch daemon info from custom connection on startup: " + e.getMessage()); + lastFallbackInvocation = System.currentTimeMillis(); + connectionServiceFallbackHandlerActive.set(true); } - initializeConnections(); return; } @@ -819,6 +835,10 @@ public final class XmrConnectionService { } private boolean isFixedConnection() { - return !"".equals(config.xmrNode) || preferences.getMoneroNodesOption() == XmrNodes.MoneroNodesOption.CUSTOM; + return !"".equals(config.xmrNode) && !fallbackApplied; + } + + private boolean isCustomConnections() { + return preferences.getMoneroNodesOption() == XmrNodes.MoneroNodesOption.CUSTOM; } } diff --git a/core/src/main/java/haveno/core/app/HavenoHeadlessApp.java b/core/src/main/java/haveno/core/app/HavenoHeadlessApp.java index 7235efce..0cf18224 100644 --- a/core/src/main/java/haveno/core/app/HavenoHeadlessApp.java +++ b/core/src/main/java/haveno/core/app/HavenoHeadlessApp.java @@ -75,6 +75,7 @@ public class HavenoHeadlessApp implements HeadlessApp { log.info("onDisplayTacHandler: We accept the tacs automatically in headless mode"); acceptedHandler.run(); }); + havenoSetup.setDisplayMoneroConnectionFallbackHandler(show -> log.info("onDisplayMoneroConnectionFallbackHandler: show={}", show)); havenoSetup.setDisplayTorNetworkSettingsHandler(show -> log.info("onDisplayTorNetworkSettingsHandler: show={}", show)); havenoSetup.setChainFileLockedExceptionHandler(msg -> log.error("onChainFileLockedExceptionHandler: msg={}", msg)); tradeManager.setLockedUpFundsHandler(msg -> log.info("onLockedUpFundsHandler: msg={}", msg)); diff --git a/core/src/main/java/haveno/core/app/HavenoSetup.java b/core/src/main/java/haveno/core/app/HavenoSetup.java index d80ca807..a291da50 100644 --- a/core/src/main/java/haveno/core/app/HavenoSetup.java +++ b/core/src/main/java/haveno/core/app/HavenoSetup.java @@ -158,6 +158,9 @@ public class HavenoSetup { rejectedTxErrorMessageHandler; @Setter @Nullable + private Consumer displayMoneroConnectionFallbackHandler; + @Setter + @Nullable private Consumer displayTorNetworkSettingsHandler; @Setter @Nullable @@ -426,6 +429,12 @@ public class HavenoSetup { getXmrDaemonSyncProgress().addListener((observable, oldValue, newValue) -> resetStartupTimeout()); getXmrWalletSyncProgress().addListener((observable, oldValue, newValue) -> resetStartupTimeout()); + // listen for fallback handling + getConnectionServiceFallbackHandlerActive().addListener((observable, oldValue, newValue) -> { + if (displayMoneroConnectionFallbackHandler == null) return; + displayMoneroConnectionFallbackHandler.accept(newValue); + }); + log.info("Init P2P network"); havenoSetupListeners.forEach(HavenoSetupListener::onInitP2pNetwork); p2pNetworkReady = p2PNetworkSetup.init(this::initWallet, displayTorNetworkSettingsHandler); @@ -725,6 +734,10 @@ public class HavenoSetup { return xmrConnectionService.getConnectionServiceErrorMsg(); } + public BooleanProperty getConnectionServiceFallbackHandlerActive() { + return xmrConnectionService.getConnectionServiceFallbackHandlerActive(); + } + public StringProperty getTopErrorMsg() { return topErrorMsg; } diff --git a/core/src/main/java/haveno/core/app/WalletAppSetup.java b/core/src/main/java/haveno/core/app/WalletAppSetup.java index 1f7946ea..d17c7ba3 100644 --- a/core/src/main/java/haveno/core/app/WalletAppSetup.java +++ b/core/src/main/java/haveno/core/app/WalletAppSetup.java @@ -120,7 +120,7 @@ public class WalletAppSetup { @Nullable Runnable showPopupIfInvalidBtcConfigHandler, Runnable downloadCompleteHandler, Runnable walletInitializedHandler) { - log.info("Initialize WalletAppSetup with monero-java version {}", MoneroUtils.getVersion()); + log.info("Initialize WalletAppSetup with monero-java v{}", MoneroUtils.getVersion()); ObjectProperty walletServiceException = new SimpleObjectProperty<>(); xmrInfoBinding = EasyBind.combine( diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 759dabd0..79e9248c 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -1298,9 +1298,10 @@ public class XmrWalletService extends XmrWalletBase { } else { // force restart main wallet if connection changed while syncing - log.warn("Force restarting main wallet because connection changed while syncing"); - forceRestartMainWallet(); - return; + if (wallet != null) { + log.warn("Force restarting main wallet because connection changed while syncing"); + forceRestartMainWallet(); + } } }); diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 9d5de331..c1f98ce4 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2044,6 +2044,9 @@ closedTradesSummaryWindow.totalTradeFeeInXmr.title=Sum of all trade fees paid in closedTradesSummaryWindow.totalTradeFeeInXmr.value={0} ({1} of total trade amount) walletPasswordWindow.headline=Enter password to unlock +connectionFallback.headline=Connection error +connectionFallback.msg=Error connecting to your custom Monero node(s).\n\nDo you want to try the next best available Monero node? + torNetworkSettingWindow.header=Tor networks settings torNetworkSettingWindow.noBridges=Don't use bridges torNetworkSettingWindow.providedBridges=Connect with provided bridges diff --git a/desktop/src/main/java/haveno/desktop/main/MainView.java b/desktop/src/main/java/haveno/desktop/main/MainView.java index 7290f768..9bf5f378 100644 --- a/desktop/src/main/java/haveno/desktop/main/MainView.java +++ b/desktop/src/main/java/haveno/desktop/main/MainView.java @@ -674,6 +674,7 @@ public class MainView extends InitializableView { } } else { xmrInfoLabel.setId("footer-pane"); + xmrInfoLabel.getStyleClass().remove("error-text"); if (xmrNetworkWarnMsgPopup != null) xmrNetworkWarnMsgPopup.hide(); } diff --git a/desktop/src/main/java/haveno/desktop/main/MainViewModel.java b/desktop/src/main/java/haveno/desktop/main/MainViewModel.java index 4681431b..f120b794 100644 --- a/desktop/src/main/java/haveno/desktop/main/MainViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/MainViewModel.java @@ -140,6 +140,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener @SuppressWarnings("FieldCanBeLocal") private MonadicBinding tradesAndUIReady; private final Queue> popupQueue = new PriorityQueue<>(Comparator.comparing(Overlay::getDisplayOrderPriority)); + private Popup moneroConnectionFallbackPopup; /////////////////////////////////////////////////////////////////////////////////////////// @@ -334,9 +335,38 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener tacWindow.onAction(acceptedHandler::run).show(); }, 1)); + havenoSetup.setDisplayMoneroConnectionFallbackHandler(show -> { + if (moneroConnectionFallbackPopup == null) { + moneroConnectionFallbackPopup = new Popup() + .headLine(Res.get("connectionFallback.headline")) + .warning(Res.get("connectionFallback.msg")) + .closeButtonText(Res.get("shared.no")) + .actionButtonText(Res.get("shared.yes")) + .onAction(() -> { + havenoSetup.getConnectionServiceFallbackHandlerActive().set(false); + new Thread(() -> HavenoUtils.xmrConnectionService.fallbackToBestConnection()).start(); + }) + .onClose(() -> { + log.warn("User has declined to fallback to the next best available Monero node."); + havenoSetup.getConnectionServiceFallbackHandlerActive().set(false); + }); + } + if (show) { + moneroConnectionFallbackPopup.show(); + } else if (moneroConnectionFallbackPopup.isDisplayed()) { + moneroConnectionFallbackPopup.hide(); + } + }); + havenoSetup.setDisplayTorNetworkSettingsHandler(show -> { if (show) { torNetworkSettingsWindow.show(); + + // bring connection fallback popup to front if displayed + if (moneroConnectionFallbackPopup != null && moneroConnectionFallbackPopup.isDisplayed()) { + moneroConnectionFallbackPopup.hide(); + moneroConnectionFallbackPopup.show(); + } } else if (torNetworkSettingsWindow.isDisplayed()) { torNetworkSettingsWindow.hide(); } From b586bc57f67ec3d2d118b7e0a9116bd2b6ab6ab5 Mon Sep 17 00:00:00 2001 From: slrslr <6596726+slrslr@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:30:19 +0100 Subject: [PATCH 18/34] Fixing some words displayStrings_cs.properties (#1454) --- .../i18n/displayStrings_cs.properties | 102 +++++++++--------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 72fe2925..f1c8fc6a 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -105,7 +105,7 @@ shared.nextStep=Další krok shared.selectTradingAccount=Vyberte obchodní účet shared.fundFromSavingsWalletButton=Použít prostředky z peněženky Haveno shared.fundFromExternalWalletButton=Otevřít vaši externí peněženku pro financování -shared.openDefaultWalletFailed=Nepodařilo se otevřít aplikaci moneroové peněženky. Jste si jisti, že máte nějakou nainstalovanou? +shared.openDefaultWalletFailed=Nepodařilo se otevřít aplikaci peněženky Monero. Jste si jisti, že máte nějakou nainstalovanou? shared.belowInPercent=% pod tržní cenou shared.aboveInPercent=% nad tržní cenou shared.enterPercentageValue=Zadejte % hodnotu @@ -124,7 +124,7 @@ shared.noDetailsAvailable=Detaily nejsou k dispozici shared.notUsedYet=Ještě nepoužito shared.date=Datum # suppress inspection "TrailingSpacesInProperty" -shared.sendFundsDetailsDust=Haveno zjistil, že tato transakce by vytvořila drobné mince, které jsou pod limitem drobných mincí (a není to povoleno pravidly pro moneroový konsenzus). Místo toho budou tyto drobné mince ({0} satoshi {1}) přidány k poplatku za těžbu.\n\n\n +shared.sendFundsDetailsDust=Haveno zjistil, že tato transakce by vytvořila drobné mince, které jsou pod limitem drobných mincí (a není to povoleno pravidly pro konsenzus Monero). Místo toho budou tyto drobné mince ({0} satoshi {1}) přidány k poplatku za těžbu.\n\n\n shared.copyToClipboard=Kopírovat do schránky shared.language=Jazyk shared.country=Země @@ -258,19 +258,19 @@ mainView.footer.xmrInfo.synchronizingWalletWith=Synchronizace peněženky s {0} mainView.footer.xmrInfo.syncedWith=Synchronizováno s {0} na bloku {1} mainView.footer.xmrInfo.connectingTo=Připojování mainView.footer.xmrInfo.connectionFailed=Připojení se nezdařilo -mainView.footer.xmrPeers=Monero síťové nody: {0} -mainView.footer.p2pPeers=Haveno síťové nody: {0} +mainView.footer.xmrPeers=Monero síťové uzly: {0} +mainView.footer.p2pPeers=Haveno síťové uzly: {0} mainView.bootstrapState.connectionToTorNetwork=(1/4) Připojování do sítě Tor... -mainView.bootstrapState.torNodeCreated=(2/4) Tor node vytvořen +mainView.bootstrapState.torNodeCreated=(2/4) Tor uzel vytvořen mainView.bootstrapState.hiddenServicePublished=(3/4) Skrytá služba publikována mainView.bootstrapState.initialDataReceived=(4/4) Iniciační data přijata -mainView.bootstrapWarning.noSeedNodesAvailable=Žádné seed nody nejsou k dispozici -mainView.bootstrapWarning.noNodesAvailable=Žádné seed ani peer nody k dispozici +mainView.bootstrapWarning.noSeedNodesAvailable=Žádné seed uzly nejsou k dispozici +mainView.bootstrapWarning.noNodesAvailable=Žádné seed ani peer uzly k dispozici mainView.bootstrapWarning.bootstrappingToP2PFailed=Zavádění do sítě Haveno se nezdařilo -mainView.p2pNetworkWarnMsg.noNodesAvailable=Pro vyžádání dat nejsou k dispozici žádné seed ani peer nody.\nZkontrolujte připojení k internetu nebo zkuste aplikaci restartovat. +mainView.p2pNetworkWarnMsg.noNodesAvailable=Pro vyžádání dat nejsou k dispozici žádné seed ani peer uzly.\nZkontrolujte připojení k internetu nebo zkuste aplikaci restartovat. mainView.p2pNetworkWarnMsg.connectionToP2PFailed=Připojení k síti Haveno selhalo (nahlášená chyba: {0}).\nZkontrolujte připojení k internetu nebo zkuste aplikaci restartovat. mainView.walletServiceErrorMsg.timeout=Připojení k síti Monero selhalo kvůli vypršení časového limitu. @@ -278,8 +278,8 @@ mainView.walletServiceErrorMsg.connectionError=Připojení k síti Monero selhal mainView.walletServiceErrorMsg.rejectedTxException=Transakce byla ze sítě zamítnuta.\n\n{0} -mainView.networkWarning.allConnectionsLost=Ztratili jste připojení ke všem {0} síťovým peer nodům.\nMožná jste ztratili připojení k internetu nebo byl váš počítač v pohotovostním režimu. -mainView.networkWarning.localhostMoneroLost=Ztratili jste připojení k Moneroovému localhost nodu.\nRestartujte aplikaci Haveno a připojte se k jiným Moneroovým nodům nebo restartujte Moneroový localhost node. +mainView.networkWarning.allConnectionsLost=Ztratili jste připojení ke všem {0} síťovým peer uzlům.\nMožná jste ztratili připojení k internetu nebo byl váš počítač v pohotovostním režimu. +mainView.networkWarning.localhostMoneroLost=Ztratili jste připojení k localhost uzlu Monero.\nRestartujte aplikaci Haveno a připojte se k jiným uzlům Monero nebo restartujte localhost Monero uzel. mainView.version.update=(Dostupná aktualizace) @@ -360,7 +360,7 @@ offerbook.timeSinceSigning.notSigned.noNeed=N/A shared.notSigned=Tento účet ještě nebyl podepsán a byl vytvořen před {0} dny shared.notSigned.noNeed=Tento typ účtu nevyžaduje podepisování shared.notSigned.noNeedDays=Tento typ účtu nevyžaduje podepisování a byl vytvořen před {0} dny -shared.notSigned.noNeedAlts=Cryptoové účty neprocházejí kontrolou podpisu a stáří +shared.notSigned.noNeedAlts=Kryptoměnové účty neprocházejí kontrolou podpisu a stáří offerbook.nrOffers=Počet nabídek: {0} offerbook.volume={0} (min - max) @@ -747,7 +747,7 @@ portfolio.pending.step5_buyer.alreadyWithdrawn=Vaše finanční prostředky již portfolio.pending.step5_buyer.confirmWithdrawal=Potvrďte žádost o výběr portfolio.pending.step5_buyer.amountTooLow=Částka k převodu je nižší než transakční poplatek a min. možná hodnota tx (drobné). portfolio.pending.step5_buyer.withdrawalCompleted.headline=Výběr byl dokončen -portfolio.pending.step5_buyer.withdrawalCompleted.msg=Vaše dokončené obchody jsou uloženy na \"Portfolio/Historie\".\nVšechny své moneroové transakce si můžete prohlédnout v sekci \"Prostředky/Transakce\" +portfolio.pending.step5_buyer.withdrawalCompleted.msg=Vaše dokončené obchody jsou uloženy na \"Portfolio/Historie\".\nVšechny své transakce Monero si můžete prohlédnout v sekci \"Finance/Transakce\" portfolio.pending.step5_buyer.bought=Koupili jste portfolio.pending.step5_buyer.paid=Zaplatili jste @@ -1015,8 +1015,8 @@ setting.preferences.prefCurrency=Preferovaná měna setting.preferences.displayTraditional=Zobrazit národní měny setting.preferences.noTraditional=Nejsou vybrány žádné národní měny setting.preferences.cannotRemovePrefCurrency=Vybranou zobrazovanou měnu nelze odebrat. -setting.preferences.displayCryptos=Zobrazit cryptoy -setting.preferences.noCryptos=Nejsou vybrány žádné cryptoy +setting.preferences.displayCryptos=Zobrazit kryptoměny +setting.preferences.noCryptos=Nejsou vybrány žádné kryptoměny setting.preferences.addTraditional=Přidejte národní měnu setting.preferences.addCrypto=Přidejte crypto setting.preferences.displayOptions=Zobrazit možnosti @@ -1038,24 +1038,24 @@ settings.preferences.editCustomExplorer.name=Jméno settings.preferences.editCustomExplorer.txUrl=Transakční URL settings.preferences.editCustomExplorer.addressUrl=Adresa URL -settings.net.xmrHeader=Moneroová síť +settings.net.xmrHeader=Síť Monero settings.net.p2pHeader=Síť Haveno settings.net.onionAddressLabel=Moje onion adresa -settings.net.xmrNodesLabel=Použijte vlastní Monero node +settings.net.xmrNodesLabel=Použijte vlastní Monero uzel settings.net.moneroPeersLabel=Připojené peer uzly settings.net.connection=Připojení settings.net.connected=Připojeno settings.net.useTorForXmrJLabel=Použít Tor pro Monero síť -settings.net.moneroNodesLabel=Monero nody, pro připojení -settings.net.useProvidedNodesRadio=Použijte nabízené Monero Core nody -settings.net.usePublicNodesRadio=Použít veřejnou Moneroovou síť -settings.net.useCustomNodesRadio=Použijte vlastní Monero Core node -settings.net.warn.usePublicNodes=Pokud používáte veřejné Monero nody, jste vystaveni riziku spojenému s používáním nedůvěryhodných vzdálených nodů.\n\nProsím, přečtěte si více podrobností na [HYPERLINK:https://www.getmonero.org/resources/moneropedia/remote-node.html].\n\nJste si jistí, že chcete použít veřejné nody? -settings.net.warn.usePublicNodes.useProvided=Ne, použijte nabízené nody +settings.net.moneroNodesLabel=Monero uzly, pro připojení +settings.net.useProvidedNodesRadio=Použijte nabízené Monero Core uzly +settings.net.usePublicNodesRadio=Použít veřejnou síť Monero +settings.net.useCustomNodesRadio=Použijte vlastní Monero Core uzel +settings.net.warn.usePublicNodes=Pokud používáte veřejné Monero uzly, jste vystaveni riziku spojenému s používáním nedůvěryhodných vzdálených uzlů.\n\nProsím, přečtěte si více podrobností na [HYPERLINK:https://www.getmonero.org/resources/moneropedia/remote-node.html].\n\nJste si jistí, že chcete použít veřejné uzly? +settings.net.warn.usePublicNodes.useProvided=Ne, použijte nabízené uzly settings.net.warn.usePublicNodes.usePublic=Ano, použít veřejnou síť -settings.net.warn.useCustomNodes.B2XWarning=Ujistěte se, že váš moneroový node je důvěryhodný Monero Core node!\n\nPřipojení k nodům, které nedodržují pravidla konsensu Monero Core, může poškodit vaši peněženku a způsobit problémy v obchodním procesu.\n\nUživatelé, kteří se připojují k nodům, které porušují pravidla konsensu, odpovídají za případné škody, které z toho vyplývají. Jakékoli výsledné spory budou rozhodnuty ve prospěch druhého obchodníka. Uživatelům, kteří ignorují tyto varovné a ochranné mechanismy, nebude poskytována technická podpora! -settings.net.warn.invalidXmrConfig=Připojení k moneroové síti selhalo, protože je vaše konfigurace neplatná.\n\nVaše konfigurace byla resetována, aby se místo toho použily poskytnuté moneroové uzly. Budete muset restartovat aplikaci. -settings.net.localhostXmrNodeInfo=Základní informace: Haveno při spuštění hledá místní Moneroový uzel. Pokud je nalezen, Haveno bude komunikovat se sítí Monero výhradně skrze něj. +settings.net.warn.useCustomNodes.B2XWarning=Ujistěte se, že váš Monero uzel je důvěryhodný Monero Core uzel!\n\nPřipojení k uzlům, které nedodržují pravidla konsensu Monero Core, může poškodit vaši peněženku a způsobit problémy v obchodním procesu.\n\nUživatelé, kteří se připojují k uzlům, které porušují pravidla konsensu, odpovídají za případné škody, které z toho vyplývají. Jakékoli výsledné spory budou rozhodnuty ve prospěch druhého obchodníka. Uživatelům, kteří ignorují tyto varovné a ochranné mechanismy, nebude poskytována technická podpora! +settings.net.warn.invalidXmrConfig=Připojení k síti Monero selhalo, protože je vaše konfigurace neplatná.\n\nVaše konfigurace byla resetována, aby byly místo toho použity poskytnuté uzly Monero. Budete muset restartovat aplikaci. +settings.net.localhostXmrNodeInfo=Základní informace: Haveno při spuštění hledá místní Monero uzel. Pokud je nalezen, Haveno bude komunikovat se sítí Monero výhradně skrze něj. settings.net.p2PPeersLabel=Připojené uzly settings.net.onionAddressColumn=Onion adresa settings.net.creationDateColumn=Založeno @@ -1079,7 +1079,7 @@ settings.net.sentData=Odeslaná data: {0}, {1} zprávy, {2} zprávy/sekundu settings.net.receivedData=Přijatá data: {0}, {1} zprávy, {2} zprávy/sekundu settings.net.chainHeight=Monero Peers: {0} settings.net.ips=[IP adresa:port | název hostitele:port | onion adresa:port] (oddělené čárkou). Pokud je použit výchozí port (8333), lze port vynechat. -settings.net.seedNode=Seed node +settings.net.seedNode=Seed uzel settings.net.directPeer=Peer uzel (přímý) settings.net.initialDataExchange={0} [Bootstrapping] settings.net.peer=Peer @@ -1158,10 +1158,10 @@ account.tab.mediatorRegistration=Registrace mediátora account.tab.refundAgentRegistration=Registrace rozhodce pro vrácení peněz account.tab.signing=Podepisování account.info.headline=Vítejte ve vašem účtu Haveno -account.info.msg=Zde můžete přidat obchodní účty pro národní měny & cryptoy a vytvořit zálohu dat vaší peněženky a účtu.\n\nPři prvním spuštění Haveno byla vytvořena nová moneroová peněženka.\n\nDůrazně doporučujeme zapsat si seed slova moneroových peněženek (viz záložka nahoře) a před financováním zvážit přidání hesla. Vklady a výběry moneroů jsou spravovány v sekci \ "Finance \".\n\nOchrana osobních údajů a zabezpečení: protože Haveno je decentralizovaná směnárna, všechna data jsou uložena ve vašem počítači. Neexistují žádné servery, takže nemáme přístup k vašim osobním informacím, vašim finančním prostředkům ani vaší IP adrese. Údaje, jako jsou čísla bankovních účtů, adresy cryptoů a monerou atd., jsou sdíleny pouze s obchodním partnerem za účelem uskutečnění obchodů, které zahájíte (v případě sporu uvidí Prostředník nebo Rozhodce stejná data jako váš obchodní partner). +account.info.msg=Zde můžete přidat obchodní účty pro národní měny & kryptoměny a vytvořit zálohu dat vaší peněženky a účtu.\n\nPři prvním spuštění Haveno byla vytvořena nová peněženka Monero.\n\nDůrazně doporučujeme zapsat si seed slova peněženek (viz záložka nahoře) a před financováním zvážit přidání hesla. Vklady a výběry moneroů jsou spravovány v sekci \ "Finance \".\n\nOchrana osobních údajů a zabezpečení: protože Haveno je decentralizovaná směnárna, všechna data jsou uložena ve vašem počítači. Neexistují žádné servery, takže nemáme přístup k vašim osobním informacím, vašim finančním prostředkům ani vaší IP adrese. Údaje, jako jsou čísla bankovních účtů, adresy cryptoů a monerou atd., jsou sdíleny pouze s obchodním partnerem za účelem uskutečnění obchodů, které zahájíte (v případě sporu uvidí Prostředník nebo Rozhodce stejná data jako váš obchodní partner). account.menu.paymentAccount=Účty v národní měně -account.menu.altCoinsAccountView=Cryptoové účty +account.menu.altCoinsAccountView=Kryptoměnové účty account.menu.password=Heslo peněženky account.menu.seedWords=Seed peněženky account.menu.walletInfo=Info o peněžence @@ -1190,7 +1190,7 @@ account.arbitratorRegistration.removedFailed=Registraci se nepodařilo odebrat. account.arbitratorRegistration.registerSuccess=Úspěšně jste se zaregistrovali do sítě Haveno. account.arbitratorRegistration.registerFailed=Registraci se nepodařilo dokončit. {0} -account.crypto.yourCryptoAccounts=Vaše cryptoové účty +account.crypto.yourCryptoAccounts=Vaše kryptoměnové účty account.crypto.popup.wallet.msg=Ujistěte se, že dodržujete požadavky na používání peněženek {0}, jak je popsáno na webové stránce {1}.\nPoužití peněženek z centralizovaných směnáren, kde (a) nevlastníte své soukromé klíče nebo (b) které nepoužívají kompatibilní software peněženky, je riskantní: může to vést ke ztrátě obchodovaných prostředků!\nMediátor nebo rozhodce není specialista {2} a v takových případech nemůže pomoci. account.crypto.popup.wallet.confirm=Rozumím a potvrzuji, že vím, jakou peněženku musím použít. # suppress inspection "UnusedProperty" @@ -1244,7 +1244,7 @@ account.password.removePw.button=Odstraňte heslo account.password.removePw.headline=Odstraňte ochranu peněženky pomocí hesla account.password.setPw.button=Nastavit heslo account.password.setPw.headline=Nastavte ochranu peněženky pomocí hesla -account.password.info=S ochranou pomocí hesla budete muset zadat heslo při spuštění aplikace, při výběru monera z vaší peněženky a při zobrazení vašich slov z klíčového základu. +account.password.info=S ochranou pomocí hesla budete muset zadat heslo při spuštění aplikace, při výběru monera z vaší peněženky a při zobrazení slov seedu peněženky. account.seed.backup.title=Zálohujte svá klíčová slova peněženky. account.seed.info=Prosím, zapište si jak klíčová slova peněženky, tak datum. Kdykoliv můžete obnovit svou peněženku pomocí klíčových slov a data.\n\nKlíčová slova byste měli zapsat na kus papíru. Neukládejte je na počítač.\n\nVezměte prosím na vědomí, že klíčová slova NEJSOU náhradou za zálohu.\nMusíte vytvořit zálohu celého adresáře aplikace z obrazovky "Účet/Záloha", abyste mohli obnovit stav a data aplikace. @@ -1252,7 +1252,7 @@ account.seed.backup.warning=Prosím, poznamenejte si, že klíčová slova nejso account.seed.warn.noPw.msg=Nenastavili jste si heslo k peněžence, které by chránilo zobrazení seed slov.\n\nChcete zobrazit seed slova? account.seed.warn.noPw.yes=Ano, a už se mě znovu nezeptat account.seed.enterPw=Chcete-li zobrazit seed slova, zadejte heslo -account.seed.restore.info=Před použitím obnovení ze seed slov si vytvořte zálohu. Uvědomte si, že obnova peněženky je pouze pro naléhavé případy a může způsobit problémy s interní databází peněženky.\nNení to způsob, jak použít zálohu! K obnovení předchozího stavu aplikace použijte zálohu z adresáře dat aplikace.\n\nPo obnovení se aplikace automaticky vypne. Po restartování aplikace se bude znovu synchronizovat s moneroovou sítí. To může chvíli trvat a může spotřebovat hodně CPU, zejména pokud byla peněženka starší a měla mnoho transakcí. Vyhněte se přerušování tohoto procesu, jinak budete možná muset znovu odstranit soubor řetězu SPV nebo opakovat proces obnovy. +account.seed.restore.info=Před použitím obnovení ze seed slov si vytvořte zálohu. Uvědomte si, že obnova peněženky je pouze pro naléhavé případy a může způsobit problémy s interní databází peněženky.\nNení to způsob, jak použít zálohu! K obnovení předchozího stavu aplikace použijte zálohu z adresáře dat aplikace.\n\nPo obnovení se aplikace automaticky vypne. Po restartování aplikace se bude znovu synchronizovat se sítí Monero. To může chvíli trvat a může spotřebovat hodně CPU, zejména pokud byla peněženka starší a měla mnoho transakcí. Vyhněte se přerušování tohoto procesu, jinak budete možná muset znovu odstranit soubor řetězu SPV nebo opakovat proces obnovy. account.seed.restore.ok=Dobře, proveďte obnovu a vypněte Haveno @@ -1323,7 +1323,7 @@ inputControlWindow.balanceLabel=Dostupný zůstatek contractWindow.title=Podrobnosti o sporu contractWindow.dates=Datum nabídky / Datum obchodu -contractWindow.xmrAddresses=Moneroová adresa kupujícího XMR / prodávajícího XMR +contractWindow.xmrAddresses=Monero adresa kupujícího XMR / prodávajícího XMR contractWindow.onions=Síťová adresa kupující XMR / prodávající XMR contractWindow.accountAge=Stáří účtu XMR kupující / XMR prodejce contractWindow.numDisputes=Počet sporů XMR kupující / XMR prodejce @@ -1436,10 +1436,10 @@ filterWindow.bannedPrivilegedDevPubKeys=Filtrované privilegované klíče pub d filterWindow.arbitrators=Filtrovaní rozhodci (onion adresy oddělené čárkami) filterWindow.mediators=Filtrovaní mediátoři (onion adresy oddělené čárkami) filterWindow.refundAgents=Filtrovaní rozhodci pro vrácení peněz (onion adresy oddělené čárkami) -filterWindow.seedNode=Filtrované seed nody (onion adresy oddělené čárkami) -filterWindow.priceRelayNode=Filtrované cenové relay nody (onion adresy oddělené čárkami) -filterWindow.xmrNode=Filtrované Moneroové nody (adresy+porty oddělené čárkami) -filterWindow.preventPublicXmrNetwork=Zabraňte použití veřejné moneroové sítě +filterWindow.seedNode=Filtrované seed uzly (onion adresy oddělené čárkami) +filterWindow.priceRelayNode=Filtrované cenové relay uzly (onion adresy oddělené čárkami) +filterWindow.xmrNode=Filtrované uzly Monero (adresy+porty oddělené čárkami) +filterWindow.preventPublicXmrNetwork=Zabraňte použití veřejné sítě Monero filterWindow.disableAutoConf=Zakázat automatické potvrzení filterWindow.autoConfExplorers=Filtrované průzkumníky s automatickým potvrzením (adresy oddělené čárkami) filterWindow.disableTradeBelowVersion=Min. verze nutná pro obchodování @@ -1595,12 +1595,12 @@ popup.warning.startupFailed.twoInstances=Haveno již běží. Nemůžete spustit popup.warning.tradePeriod.halfReached=Váš obchod s ID {0} dosáhl poloviny max. povoleného obchodního období a stále není dokončen.\n\nObdobí obchodování končí {1}\n\nDalší informace o stavu obchodu naleznete na adrese \"Portfolio/Otevřené obchody\". popup.warning.tradePeriod.ended=Váš obchod s ID {0} dosáhl max. povoleného obchodního období a není dokončen.\n\nObdobí obchodování skončilo {1}\n\nZkontrolujte prosím svůj obchod v sekci "Portfolio/Otevřené obchody\", abyste kontaktovali mediátora. popup.warning.noTradingAccountSetup.headline=Nemáte nastaven obchodní účet -popup.warning.noTradingAccountSetup.msg=Než budete moci vytvořit nabídku, musíte si nastavit národní měnu nebo cryptoový účet.\nChcete si založit účet? +popup.warning.noTradingAccountSetup.msg=Než budete moci vytvořit nabídku, musíte si nastavit národní měnu nebo kryptoměnový účet.\nChcete si založit účet? popup.warning.noArbitratorsAvailable=Nejsou k dispozici žádní rozhodci. popup.warning.noMediatorsAvailable=Nejsou k dispozici žádní mediátoři. popup.warning.notFullyConnected=Musíte počkat, až budete plně připojeni k síti.\nTo může při spuštění trvat až 2 minuty. -popup.warning.notSufficientConnectionsToXmrNetwork=Musíte počkat, až budete mít alespoň {0} připojení k moneroové síti. -popup.warning.downloadNotComplete=Musíte počkat, až bude stahování chybějících moneroových bloků kompletní. +popup.warning.notSufficientConnectionsToXmrNetwork=Musíte počkat, až budete mít alespoň {0} připojení k síti Monero. +popup.warning.downloadNotComplete=Musíte počkat, až bude dokončeno stahování chybějících bloků Monero. popup.warning.walletNotSynced=Haveno peněženka není synchronizována s nejnovější výškou blockchainu. Počkejte, dokud se peněženka nesynchronizuje, nebo zkontrolujte své připojení. popup.warning.removeOffer=Opravdu chcete tuto nabídku odebrat? popup.warning.tooLargePercentageValue=Nelze nastavit procento 100% nebo větší. @@ -1622,11 +1622,11 @@ popup.warning.seed=seed popup.warning.mandatoryUpdate.trading=Aktualizujte prosím na nejnovější verzi Haveno. Byla vydána povinná aktualizace, která zakazuje obchodování se starými verzemi. Další informace naleznete na fóru Haveno. popup.warning.burnXMR=Tato transakce není možná, protože poplatky za těžbu {0} by přesáhly částku převodu {1}. Počkejte prosím, dokud nebudou poplatky za těžbu opět nízké nebo dokud nenahromadíte více XMR k převodu. -popup.warning.openOffer.makerFeeTxRejected=Transakční poplatek tvůrce za nabídku s ID {0} byl moneroovou sítí odmítnut.\nID transakce = {1}.\nNabídka byla odstraněna, aby se předešlo dalším problémům.\nPřejděte do \"Nastavení/Informace o síti\" a proveďte synchronizaci SPV.\nPro další pomoc prosím kontaktujte podpůrný kanál v Haveno Keybase týmu. +popup.warning.openOffer.makerFeeTxRejected=Transakční poplatek tvůrce za nabídku s ID {0} byl odmítnut sítí Monero.\nID transakce = {1}.\nNabídka byla odstraněna, aby se předešlo dalším problémům.\nPřejděte do \"Nastavení/Informace o síti\" a proveďte synchronizaci SPV.\nPro další pomoc prosím kontaktujte podpůrný kanál v Haveno Keybase týmu. popup.warning.trade.txRejected.tradeFee=obchodní poplatek popup.warning.trade.txRejected.deposit=vklad -popup.warning.trade.txRejected=Moneroová síť odmítla {0} transakci pro obchod s ID {1}.\nID transakce = {2}\nObchod byl přesunut do neúspěšných obchodů.\nPřejděte do části \"Nastavení/Informace o síti\" a proveďte synchronizaci SPV.\nPro další pomoc prosím kontaktujte podpůrný kanál v Haveno Keybase týmu. +popup.warning.trade.txRejected=Síť Monero odmítla {0} transakci pro obchod s ID {1}.\nID transakce = {2}\nObchod byl přesunut do neúspěšných obchodů.\nPřejděte do části \"Nastavení/Informace o síti\" a proveďte synchronizaci SPV.\nPro další pomoc prosím kontaktujte podpůrný kanál v Haveno Keybase týmu. popup.warning.openOfferWithInvalidMakerFeeTx=Transakční poplatek tvůrce za nabídku s ID {0} je neplatný.\nID transakce = {1}.\nPřejděte do \"Nastavení/Informace o síti\" a proveďte synchronizaci SPV.\nPro další pomoc prosím kontaktujte podpůrný kanál v Haveno Keybase týmu. @@ -1641,9 +1641,9 @@ popup.warn.downGradePrevention=Downgrade z verze {0} na verzi {1} není podporov popup.privateNotification.headline=Důležité soukromé oznámení! popup.securityRecommendation.headline=Důležité bezpečnostní doporučení -popup.securityRecommendation.msg=Chtěli bychom vám připomenout, abyste zvážili použití ochrany heslem pro vaši peněženku, pokud jste ji již neaktivovali.\n\nDůrazně se také doporučuje zapsat seed slova peněženky. Tato seed slova jsou jako hlavní heslo pro obnovení vaší moneroové peněženky.\nV sekci "Seed peněženky" naleznete další informace.\n\nDále byste měli zálohovat úplnou složku dat aplikace v sekci \"Záloha\". +popup.securityRecommendation.msg=Chtěli bychom vám připomenout, abyste zvážili použití ochrany heslem pro vaši peněženku, pokud jste ji již neaktivovali.\n\nDůrazně se také doporučuje zapsat seed slova peněženky. Tato seed slova jsou jako hlavní heslo pro obnovení vaší peněženky Monero.\nV sekci "Seed peněženky" naleznete další informace.\n\nDále byste měli zálohovat úplnou složku dat aplikace v sekci \"Záloha\". -popup.xmrLocalNode.msg=Haveno zjistil, že na tomto stroji (na localhostu) běží Monero node.\n\nUjistěte se, prosím, že tento node je plně synchronizován před spuštěním Havena. +popup.xmrLocalNode.msg=Haveno zjistil, že na tomto stroji (na localhostu) běží Monero uzel.\n\nUjistěte se, prosím, že tento uzel je plně synchronizován před spuštěním Havena. popup.shutDownInProgress.headline=Probíhá vypínání popup.shutDownInProgress.msg=Vypnutí aplikace může trvat několik sekund.\nProsím, nepřerušujte tento proces. @@ -1779,10 +1779,10 @@ peerInfo.age.noRisk=Stáří platebního účtu peerInfo.age.chargeBackRisk=Čas od podpisu peerInfo.unknownAge=Stáří není známo -addressTextField.openWallet=Otevřete výchozí moneroovou peněženku +addressTextField.openWallet=Otevřete výchozí peněženku Monero addressTextField.copyToClipboard=Zkopírujte adresu do schránky addressTextField.addressCopiedToClipboard=Adresa byla zkopírována do schránky -addressTextField.openWallet.failed=Otevření výchozí moneroové peněženky se nezdařilo. Možná nemáte žádnou nainstalovanou? +addressTextField.openWallet.failed=Otevření výchozí peněženky Monero selhalo. Možná nemáte žádnou nainstalovanou? peerInfoIcon.tooltip={0}\nŠtítek: {1} @@ -1872,7 +1872,7 @@ seed.date=Datum peněženky seed.restore.title=Obnovit peněženky z seed slov seed.restore=Obnovit peněženky seed.creationDate=Datum vzniku -seed.warn.walletNotEmpty.msg=Vaše moneroová peněženka není prázdná.\n\nTuto peněženku musíte vyprázdnit, než se pokusíte obnovit starší, protože smíchání peněženek může vést ke zneplatnění záloh.\n\nDokončete své obchody, uzavřete všechny otevřené nabídky a přejděte do sekce Prostředky, kde si můžete vybrat své moneroy.\nV případě, že nemáte přístup ke svým moneroům, můžete použít nouzový nástroj k vyprázdnění peněženky.\nNouzový nástroj otevřete stisknutím kombinace kláves \"Alt+e\" nebo \"Cmd/Ctrl+e\". +seed.warn.walletNotEmpty.msg=Vaše peněženka Monero není prázdná.\n\nTuto peněženku musíte vyprázdnit, než se pokusíte obnovit starší, protože smíchání peněženek může vést ke zneplatnění záloh.\n\nDokončete své obchody, uzavřete všechny otevřené nabídky a přejděte do sekce Prostředky, kde si můžete vybrat své moneroy.\nV případě, že nemáte přístup ke svým moneroům, můžete použít nouzový nástroj k vyprázdnění peněženky.\nNouzový nástroj otevřete stisknutím kombinace kláves \"Alt+e\" nebo \"Cmd/Ctrl+e\". seed.warn.walletNotEmpty.restore=Chci přesto obnovit seed.warn.walletNotEmpty.emptyWallet=Nejprve vyprázdním své peněženky seed.warn.notEncryptedAnymore=Vaše peněženky jsou šifrovány.\n\nPo obnovení již nebudou peněženky šifrovány a musíte nastavit nové heslo.\n\nChcete pokračovat? @@ -1939,7 +1939,7 @@ shared.accountSigningState=Stav podpisu účtu #new payment.crypto.address.dyn={0} adresa -payment.crypto.receiver.address=Cryptoová adresa příjemce +payment.crypto.receiver.address=Kryptoměnová adresa příjemce payment.accountNr=Číslo účtu payment.emailOrMobile=E-mail nebo mobilní číslo payment.useCustomAccountName=Použijte vlastní název účtu @@ -2080,7 +2080,7 @@ INTERAC_E_TRANSFER=Interac e-Transfer # suppress inspection "UnusedProperty" HAL_CASH=HalCash # suppress inspection "UnusedProperty" -BLOCK_CHAINS=Cryptoy +BLOCK_CHAINS=Kryptoměny # suppress inspection "UnusedProperty" PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" @@ -2090,7 +2090,7 @@ TRANSFERWISE=TransferWise # suppress inspection "UnusedProperty" AMAZON_GIFT_CARD=Amazon eGift Card # suppress inspection "UnusedProperty" -BLOCK_CHAINS_INSTANT=Instantní Cryptoy +BLOCK_CHAINS_INSTANT=Instantní kryptoměny # Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" @@ -2132,7 +2132,7 @@ INTERAC_E_TRANSFER_SHORT=Interac e-Transfer # suppress inspection "UnusedProperty" HAL_CASH_SHORT=HalCash # suppress inspection "UnusedProperty" -BLOCK_CHAINS_SHORT=Cryptoy +BLOCK_CHAINS_SHORT=Kryptoměny # suppress inspection "UnusedProperty" PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" @@ -2142,7 +2142,7 @@ TRANSFERWISE_SHORT=TransferWise # suppress inspection "UnusedProperty" AMAZON_GIFT_CARD_SHORT=Amazon eGift Card # suppress inspection "UnusedProperty" -BLOCK_CHAINS_INSTANT_SHORT=Instantní Cryptoy +BLOCK_CHAINS_INSTANT_SHORT=Instantní kryptoměny # Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" From 0275de3ff6991ed7e6e39a12bb7725c5b5ebc631 Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 14 Dec 2024 11:40:52 -0500 Subject: [PATCH 19/34] increase limits: crypto to 528, very low risk to 132, pay by mail to 48 Co-authored-by: XMRZombie --- .../java/haveno/core/payment/TradeLimits.java | 2 +- .../core/payment/payload/PaymentMethod.java | 31 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/haveno/core/payment/TradeLimits.java b/core/src/main/java/haveno/core/payment/TradeLimits.java index e2de303e..2bfdcdbc 100644 --- a/core/src/main/java/haveno/core/payment/TradeLimits.java +++ b/core/src/main/java/haveno/core/payment/TradeLimits.java @@ -30,7 +30,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j @Singleton public class TradeLimits { - private static final BigInteger MAX_TRADE_LIMIT = HavenoUtils.xmrToAtomicUnits(96.0); // max trade limit for lowest risk payment method. Others will get derived from that. + private static final BigInteger MAX_TRADE_LIMIT = HavenoUtils.xmrToAtomicUnits(528); // max trade limit for lowest risk payment method. Others will get derived from that. @Nullable @Getter private static TradeLimits INSTANCE; diff --git a/core/src/main/java/haveno/core/payment/payload/PaymentMethod.java b/core/src/main/java/haveno/core/payment/payload/PaymentMethod.java index cf75df36..49302f61 100644 --- a/core/src/main/java/haveno/core/payment/payload/PaymentMethod.java +++ b/core/src/main/java/haveno/core/payment/payload/PaymentMethod.java @@ -124,13 +124,8 @@ public final class PaymentMethod implements PersistablePayload, Comparable Date: Sat, 14 Dec 2024 18:15:26 -0500 Subject: [PATCH 20/34] throttle warnings in KeepAlive and PeerExchange handlers #1468 --- .../network/p2p/network/Connection.java | 16 +++++++------- .../p2p/peers/keepalive/KeepAliveHandler.java | 21 +++++++++++++++---- .../peerexchange/PeerExchangeHandler.java | 19 ++++++++++++++--- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/p2p/src/main/java/haveno/network/p2p/network/Connection.java b/p2p/src/main/java/haveno/network/p2p/network/Connection.java index 1a7f1b84..8165ecd0 100644 --- a/p2p/src/main/java/haveno/network/p2p/network/Connection.java +++ b/p2p/src/main/java/haveno/network/p2p/network/Connection.java @@ -175,9 +175,9 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { // throttle logs of reported invalid requests private static final long LOG_THROTTLE_INTERVAL_MS = 30000; // throttle logging rule violations and warnings to once every 30 seconds private static long lastLoggedInvalidRequestReportTs = 0; - private static int numUnloggedInvalidRequestReports = 0; + private static int numThrottledInvalidRequestReports = 0; private static long lastLoggedWarningTs = 0; - private static int numUnloggedWarnings = 0; + private static int numThrottledWarnings = 0; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor @@ -620,7 +620,7 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { boolean logReport = System.currentTimeMillis() - lastLoggedInvalidRequestReportTs > LOG_THROTTLE_INTERVAL_MS; // count the number of unlogged reports since last log entry - if (!logReport) numUnloggedInvalidRequestReports++; + if (!logReport) numThrottledInvalidRequestReports++; // handle report if (logReport) log.warn("We got reported the ruleViolation {} at connection with address={}, uid={}, errorMessage={}", ruleViolation, connection.getPeersNodeAddressProperty(), connection.getUid(), errorMessage); @@ -654,8 +654,8 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { private static synchronized void resetReportedInvalidRequestsThrottle(boolean logReport) { if (logReport) { - if (numUnloggedInvalidRequestReports > 0) log.warn("We received {} other reports of invalid requests since the last log entry", numUnloggedInvalidRequestReports); - numUnloggedInvalidRequestReports = 0; + if (numThrottledInvalidRequestReports > 0) log.warn("We received {} other reports of invalid requests since the last log entry", numThrottledInvalidRequestReports); + numThrottledInvalidRequestReports = 0; lastLoggedInvalidRequestReportTs = System.currentTimeMillis(); } } @@ -940,11 +940,11 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { boolean logWarning = System.currentTimeMillis() - lastLoggedWarningTs > LOG_THROTTLE_INTERVAL_MS; if (logWarning) { log.warn(msg); - if (numUnloggedWarnings > 0) log.warn("We received {} other log warnings since the last log entry", numUnloggedWarnings); - numUnloggedWarnings = 0; + if (numThrottledWarnings > 0) log.warn("{} warnings were throttled since the last log entry", numThrottledWarnings); + numThrottledWarnings = 0; lastLoggedWarningTs = System.currentTimeMillis(); } else { - numUnloggedWarnings++; + numThrottledWarnings++; } } } diff --git a/p2p/src/main/java/haveno/network/p2p/peers/keepalive/KeepAliveHandler.java b/p2p/src/main/java/haveno/network/p2p/peers/keepalive/KeepAliveHandler.java index 91ba2278..07ea9397 100644 --- a/p2p/src/main/java/haveno/network/p2p/peers/keepalive/KeepAliveHandler.java +++ b/p2p/src/main/java/haveno/network/p2p/peers/keepalive/KeepAliveHandler.java @@ -40,8 +40,10 @@ import java.util.concurrent.TimeUnit; class KeepAliveHandler implements MessageListener { private static final Logger log = LoggerFactory.getLogger(KeepAliveHandler.class); - private static final int DELAY_MS = 10_000; + private static final long LOG_THROTTLE_INTERVAL_MS = 60000; // throttle logging warnings to once every 60 seconds + private static long lastLoggedWarningTs = 0; + private static int numThrottledWarnings = 0; /////////////////////////////////////////////////////////////////////////////////////////// @@ -147,9 +149,8 @@ class KeepAliveHandler implements MessageListener { cleanup(); listener.onComplete(); } else { - log.warn("Nonce not matching. That should never happen.\n\t" + - "We drop that message. nonce={} / requestNonce={}", - nonce, pong.getRequestNonce()); + throttleWarn("Nonce not matching. That should never happen.\n" + + "\tWe drop that message. nonce=" + nonce + ", requestNonce=" + pong.getRequestNonce() + ", peerNodeAddress=" + connection.getPeersNodeAddressOptional().orElseGet(null)); } } else { log.trace("We have stopped already. We ignore that onMessage call."); @@ -167,4 +168,16 @@ class KeepAliveHandler implements MessageListener { delayTimer = null; } } + + private synchronized void throttleWarn(String msg) { + boolean logWarning = System.currentTimeMillis() - lastLoggedWarningTs > LOG_THROTTLE_INTERVAL_MS; + if (logWarning) { + log.warn(msg); + if (numThrottledWarnings > 0) log.warn("{} warnings were throttled since the last log entry", numThrottledWarnings); + numThrottledWarnings = 0; + lastLoggedWarningTs = System.currentTimeMillis(); + } else { + numThrottledWarnings++; + } + } } diff --git a/p2p/src/main/java/haveno/network/p2p/peers/peerexchange/PeerExchangeHandler.java b/p2p/src/main/java/haveno/network/p2p/peers/peerexchange/PeerExchangeHandler.java index 1a929289..0b10307c 100644 --- a/p2p/src/main/java/haveno/network/p2p/peers/peerexchange/PeerExchangeHandler.java +++ b/p2p/src/main/java/haveno/network/p2p/peers/peerexchange/PeerExchangeHandler.java @@ -45,6 +45,9 @@ class PeerExchangeHandler implements MessageListener { // We want to keep timeout short here private static final long TIMEOUT = 90; private static final int DELAY_MS = 500; + private static final long LOG_THROTTLE_INTERVAL_MS = 60000; // throttle logging warnings to once every 60 seconds + private static long lastLoggedWarningTs = 0; + private static int numThrottledWarnings = 0; /////////////////////////////////////////////////////////////////////////////////////////// @@ -173,9 +176,8 @@ class PeerExchangeHandler implements MessageListener { cleanup(); listener.onComplete(); } else { - log.warn("Nonce not matching. That should never happen.\n\t" + - "We drop that message. nonce={} / requestNonce={}", - nonce, getPeersResponse.getRequestNonce()); + throttleWarn("Nonce not matching. That should never happen.\n" + + "\tWe drop that message. nonce=" + nonce + ", requestNonce=" + getPeersResponse.getRequestNonce() + ", peerNodeAddress=" + connection.getPeersNodeAddressOptional().orElseGet(null)); } } else { log.trace("We have stopped that handler already. We ignore that onMessage call."); @@ -216,4 +218,15 @@ class PeerExchangeHandler implements MessageListener { } } + private synchronized void throttleWarn(String msg) { + boolean logWarning = System.currentTimeMillis() - lastLoggedWarningTs > LOG_THROTTLE_INTERVAL_MS; + if (logWarning) { + log.warn(msg); + if (numThrottledWarnings > 0) log.warn("{} warnings were throttled since the last log entry", numThrottledWarnings); + numThrottledWarnings = 0; + lastLoggedWarningTs = System.currentTimeMillis(); + } else { + numThrottledWarnings++; + } + } } From 85acb8aeb33e31dfbda3b2c8cc2870a5420543cd Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 15 Dec 2024 10:39:45 -0500 Subject: [PATCH 21/34] fix sorting of dispute state column --- .../java/haveno/desktop/main/support/dispute/DisputeView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java index 14e85b78..7e3cad8d 100644 --- a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java +++ b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java @@ -927,6 +927,7 @@ public abstract class DisputeView extends ActivatableView implements buyerOnionAddressColumn.setComparator(Comparator.comparing(this::getBuyerOnionAddressColumnLabel)); sellerOnionAddressColumn.setComparator(Comparator.comparing(this::getSellerOnionAddressColumnLabel)); marketColumn.setComparator((o1, o2) -> CurrencyUtil.getCurrencyPair(o1.getContract().getOfferPayload().getCurrencyCode()).compareTo(o2.getContract().getOfferPayload().getCurrencyCode())); + stateColumn.setComparator(Comparator.comparing(this::getDisputeStateText)); dateColumn.setSortType(TableColumn.SortType.DESCENDING); tableView.getSortOrder().add(dateColumn); From 9ec2794931bfad5bcfef2ad3db647004747ae6f4 Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 15 Dec 2024 11:34:26 -0500 Subject: [PATCH 22/34] do not auto complete trades resolved by arbitration --- core/src/main/java/haveno/core/trade/TradeManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/haveno/core/trade/TradeManager.java b/core/src/main/java/haveno/core/trade/TradeManager.java index dc0427c4..9a3d84cb 100644 --- a/core/src/main/java/haveno/core/trade/TradeManager.java +++ b/core/src/main/java/haveno/core/trade/TradeManager.java @@ -994,7 +994,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi if (tradeOptional.isPresent()) { Trade trade = tradeOptional.get(); trade.setDisputeState(disputeState); - onTradeCompleted(trade); xmrWalletService.resetAddressEntriesForTrade(trade.getId()); requestPersistence(); } From 140961d885825628569c594d8583fe984cd54ddb Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 16 Dec 2024 09:34:12 -0500 Subject: [PATCH 23/34] show either dispute payout tx id or normal payout tx id --- .../overlays/windows/TradeDetailsWindow.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeDetailsWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeDetailsWindow.java index 6575769d..04843057 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeDetailsWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeDetailsWindow.java @@ -183,12 +183,12 @@ public class TradeDetailsWindow extends Overlay { rows++; } - if (trade.getPayoutTxId() != null) - rows++; boolean showDisputedTx = arbitrationManager.findOwnDispute(trade.getId()).isPresent() && arbitrationManager.findOwnDispute(trade.getId()).get().getDisputePayoutTxId() != null; if (showDisputedTx) rows++; + else if (trade.getPayoutTxId() != null) + rows++; if (trade.hasFailed()) rows += 2; if (trade.getTradePeerNodeAddress() != null) @@ -244,16 +244,18 @@ public class TradeDetailsWindow extends Overlay { if (trade.getMaker().getDepositTxHash() != null) addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.makerDepositTransactionId"), trade.getMaker().getDepositTxHash()); - if (trade.getTaker().getDepositTxHash() != null) + if (trade.getTaker().getDepositTxHash() != null) addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.takerDepositTransactionId"), trade.getTaker().getDepositTxHash()); - if (trade.getPayoutTxId() != null && !trade.getPayoutTxId().isBlank()) - addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.payoutTxId"), - trade.getPayoutTxId()); - if (showDisputedTx) + + if (showDisputedTx) { addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.disputedPayoutTxId"), arbitrationManager.findOwnDispute(trade.getId()).get().getDisputePayoutTxId()); + } else if (trade.getPayoutTxId() != null && !trade.getPayoutTxId().isBlank()) { + addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.payoutTxId"), + trade.getPayoutTxId()); + } if (trade.hasFailed()) { textArea = addConfirmationLabelTextArea(gridPane, ++rowIndex, Res.get("shared.errorMessage"), "", 0).second; From 4b7db9a1ae3fe2f4140f40a430df1dd548e4098e Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 16 Dec 2024 09:38:33 -0500 Subject: [PATCH 24/34] remove colon from disputed payout transaction id --- core/src/main/resources/i18n/displayStrings.properties | 2 +- core/src/main/resources/i18n/displayStrings_cs.properties | 2 +- core/src/main/resources/i18n/displayStrings_de.properties | 2 +- core/src/main/resources/i18n/displayStrings_fr.properties | 2 +- core/src/main/resources/i18n/displayStrings_it.properties | 2 +- core/src/main/resources/i18n/displayStrings_ja.properties | 2 +- core/src/main/resources/i18n/displayStrings_pt-br.properties | 2 +- core/src/main/resources/i18n/displayStrings_pt.properties | 2 +- core/src/main/resources/i18n/displayStrings_th.properties | 2 +- core/src/main/resources/i18n/displayStrings_tr.properties | 2 +- core/src/main/resources/i18n/displayStrings_vi.properties | 2 +- core/src/main/resources/i18n/displayStrings_zh-hans.properties | 2 +- core/src/main/resources/i18n/displayStrings_zh-hant.properties | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index c1f98ce4..c0d2d88f 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2016,7 +2016,7 @@ tacWindow.disagree=I disagree and quit tacWindow.arbitrationSystem=Dispute resolution tradeDetailsWindow.headline=Trade -tradeDetailsWindow.disputedPayoutTxId=Disputed payout transaction ID: +tradeDetailsWindow.disputedPayoutTxId=Disputed payout transaction ID tradeDetailsWindow.tradeDate=Trade date tradeDetailsWindow.txFee=Mining fee tradeDetailsWindow.tradePeersOnion=Trading peers onion address diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index f1c8fc6a..789eb761 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -1504,7 +1504,7 @@ tacWindow.disagree=Nesouhlasím a odcházím tacWindow.arbitrationSystem=Řešení sporů tradeDetailsWindow.headline=Obchod -tradeDetailsWindow.disputedPayoutTxId=ID sporné platební transakce: +tradeDetailsWindow.disputedPayoutTxId=ID sporné platební transakce tradeDetailsWindow.tradeDate=Datum obchodu tradeDetailsWindow.txFee=Poplatek za těžbu tradeDetailsWindow.tradePeersOnion=Onion adresa obchodního partnera diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 7a7a5643..5d03c34b 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -1504,7 +1504,7 @@ tacWindow.disagree=Ich stimme nicht zu und beende tacWindow.arbitrationSystem=Streitbeilegung tradeDetailsWindow.headline=Handel -tradeDetailsWindow.disputedPayoutTxId=Transaktions-ID der strittigen Auszahlung: +tradeDetailsWindow.disputedPayoutTxId=Transaktions-ID der strittigen Auszahlung tradeDetailsWindow.tradeDate=Handelsdatum tradeDetailsWindow.txFee=Mining-Gebühr tradeDetailsWindow.tradePeersOnion=Onion-Adresse des Handelspartners diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 252b3146..e87a0cac 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -1506,7 +1506,7 @@ tacWindow.disagree=Je ne suis pas d'accord et je quitte tacWindow.arbitrationSystem=Règlement du litige tradeDetailsWindow.headline=Échange -tradeDetailsWindow.disputedPayoutTxId=ID de la transaction de versement contestée : +tradeDetailsWindow.disputedPayoutTxId=ID de la transaction de versement contestée tradeDetailsWindow.tradeDate=Date de l'échange tradeDetailsWindow.txFee=Frais de minage tradeDetailsWindow.tradePeersOnion=Adresse onion du pair de trading diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index b2f67e19..212c89f3 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -1503,7 +1503,7 @@ tacWindow.disagree=Non accetto ed esco tacWindow.arbitrationSystem=Risoluzione disputa tradeDetailsWindow.headline=Scambio -tradeDetailsWindow.disputedPayoutTxId=ID transazione di pagamento contestato: +tradeDetailsWindow.disputedPayoutTxId=ID transazione di pagamento contestato tradeDetailsWindow.tradeDate=Data di scambio tradeDetailsWindow.txFee=Commissione di mining tradeDetailsWindow.tradePeersOnion=Indirizzi onion peer di trading diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 478e1135..80542bc3 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -1504,7 +1504,7 @@ tacWindow.disagree=同意せずにに終了 tacWindow.arbitrationSystem=紛争解決 tradeDetailsWindow.headline=トレード -tradeDetailsWindow.disputedPayoutTxId=係争中の支払い取引ID: +tradeDetailsWindow.disputedPayoutTxId=係争中の支払い取引ID tradeDetailsWindow.tradeDate=取引日 tradeDetailsWindow.txFee=マイニング手数料 tradeDetailsWindow.tradePeersOnion=トレード相手のonionアドレス diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 8fa3d80a..f6659b16 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -1507,7 +1507,7 @@ tacWindow.disagree=Eu não concordo e desisto tacWindow.arbitrationSystem=Resolução de disputas tradeDetailsWindow.headline=Negociação -tradeDetailsWindow.disputedPayoutTxId=ID de transação do pagamento disputado: +tradeDetailsWindow.disputedPayoutTxId=ID de transação do pagamento disputado tradeDetailsWindow.tradeDate=Data da negociação tradeDetailsWindow.txFee=Taxa de mineração tradeDetailsWindow.tradePeersOnion=Endereço onion dos parceiros de negociação diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 27f94191..d7891bea 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -1500,7 +1500,7 @@ tacWindow.disagree=Eu não concordo e desisto tacWindow.arbitrationSystem=Resolução da disputa tradeDetailsWindow.headline=Negócio -tradeDetailsWindow.disputedPayoutTxId=ID de transação do pagamento disputado: +tradeDetailsWindow.disputedPayoutTxId=ID de transação do pagamento disputado tradeDetailsWindow.tradeDate=Data de negócio tradeDetailsWindow.txFee=Taxa de mineração tradeDetailsWindow.tradePeersOnion=Endereço onion dos parceiros de negociação diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index b603454c..db2855c5 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -1501,7 +1501,7 @@ tacWindow.disagree=ฉันไม่เห็นด้วยและออก tacWindow.arbitrationSystem=Dispute resolution tradeDetailsWindow.headline=ซื้อขาย -tradeDetailsWindow.disputedPayoutTxId=รหัส ID ธุรกรรมการจ่ายเงินที่พิพาท: +tradeDetailsWindow.disputedPayoutTxId=รหัส ID ธุรกรรมการจ่ายเงินที่พิพาท tradeDetailsWindow.tradeDate=วันที่ซื้อขาย tradeDetailsWindow.txFee=ค่าธรรมเนียมการขุด tradeDetailsWindow.tradePeersOnion=ที่อยู่ของ onion คู่ค้า diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index 7656d581..d8436a0f 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -2011,7 +2011,7 @@ tacWindow.disagree=Kabul etmiyorum ve çıkıyorum tacWindow.arbitrationSystem=Uyuşmazlık çözümü tradeDetailsWindow.headline=Ticaret -tradeDetailsWindow.disputedPayoutTxId=Uyuşmazlık konusu olan ödeme işlem kimliği: +tradeDetailsWindow.disputedPayoutTxId=Uyuşmazlık konusu olan ödeme işlem kimliği tradeDetailsWindow.tradeDate=Ticaret tarihi tradeDetailsWindow.txFee=Madencilik ücreti tradeDetailsWindow.tradePeersOnion=Ticaret ortaklarının onion adresi diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 8530cf46..828e81e4 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -1503,7 +1503,7 @@ tacWindow.disagree=Tôi không đồng ý và thoát tacWindow.arbitrationSystem=Dispute resolution tradeDetailsWindow.headline=giao dịch -tradeDetailsWindow.disputedPayoutTxId=ID giao dịch hoàn tiền khiếu nại: +tradeDetailsWindow.disputedPayoutTxId=ID giao dịch hoàn tiền khiếu nại tradeDetailsWindow.tradeDate=Ngày giao dịch tradeDetailsWindow.txFee=Phí đào tradeDetailsWindow.tradePeersOnion=Địa chỉ onion Đối tác giao dịch diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 91166e64..684ba4c6 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -1505,7 +1505,7 @@ tacWindow.disagree=我不同意并退出 tacWindow.arbitrationSystem=纠纷解决方案 tradeDetailsWindow.headline=交易 -tradeDetailsWindow.disputedPayoutTxId=纠纷支付交易 ID: +tradeDetailsWindow.disputedPayoutTxId=纠纷支付交易 ID tradeDetailsWindow.tradeDate=交易时间 tradeDetailsWindow.txFee=矿工手续费 tradeDetailsWindow.tradePeersOnion=交易伙伴匿名地址 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 37d5bfbb..ebd7194b 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -1505,7 +1505,7 @@ tacWindow.disagree=我不同意並退出 tacWindow.arbitrationSystem=糾紛解決方案 tradeDetailsWindow.headline=交易 -tradeDetailsWindow.disputedPayoutTxId=糾紛支付交易 ID: +tradeDetailsWindow.disputedPayoutTxId=糾紛支付交易 ID tradeDetailsWindow.tradeDate=交易時間 tradeDetailsWindow.txFee=礦工手續費 tradeDetailsWindow.tradePeersOnion=交易夥伴匿名地址 From ece3b0fec01132b8f8fa9d65bf5088a8e1e44715 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 16 Dec 2024 05:59:43 -0500 Subject: [PATCH 25/34] fix concurrency exception updating capabilities #1473 --- .../java/haveno/common/app/Capabilities.java | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/common/src/main/java/haveno/common/app/Capabilities.java b/common/src/main/java/haveno/common/app/Capabilities.java index 39266a25..5bb3bd0b 100644 --- a/common/src/main/java/haveno/common/app/Capabilities.java +++ b/common/src/main/java/haveno/common/app/Capabilities.java @@ -59,8 +59,10 @@ public class Capabilities { } public Capabilities(Collection capabilities) { - synchronized (this.capabilities) { - this.capabilities.addAll(capabilities); + synchronized (capabilities) { + synchronized (this.capabilities) { + this.capabilities.addAll(capabilities); + } } } @@ -73,9 +75,11 @@ public class Capabilities { } public void set(Collection capabilities) { - synchronized (this.capabilities) { - this.capabilities.clear(); - this.capabilities.addAll(capabilities); + synchronized (capabilities) { + synchronized (this.capabilities) { + this.capabilities.clear(); + this.capabilities.addAll(capabilities); + } } } @@ -87,15 +91,19 @@ public class Capabilities { public void addAll(Capabilities capabilities) { if (capabilities != null) { - synchronized (this.capabilities) { - this.capabilities.addAll(capabilities.capabilities); + synchronized (capabilities.capabilities) { + synchronized (this.capabilities) { + this.capabilities.addAll(capabilities.capabilities); + } } } } public boolean containsAll(final Set requiredItems) { - synchronized (this.capabilities) { - return capabilities.containsAll(requiredItems); + synchronized(requiredItems) { + synchronized (this.capabilities) { + return capabilities.containsAll(requiredItems); + } } } @@ -129,7 +137,9 @@ public class Capabilities { * @return int list of Capability ordinals */ public static List toIntList(Capabilities capabilities) { - return capabilities.capabilities.stream().map(Enum::ordinal).sorted().collect(Collectors.toList()); + synchronized (capabilities.capabilities) { + return capabilities.capabilities.stream().map(Enum::ordinal).sorted().collect(Collectors.toList()); + } } /** @@ -139,11 +149,13 @@ public class Capabilities { * @return a {@link Capabilities} object */ public static Capabilities fromIntList(List capabilities) { - return new Capabilities(capabilities.stream() - .filter(integer -> integer < Capability.values().length) - .filter(integer -> integer >= 0) - .map(integer -> Capability.values()[integer]) - .collect(Collectors.toSet())); + synchronized (capabilities) { + return new Capabilities(capabilities.stream() + .filter(integer -> integer < Capability.values().length) + .filter(integer -> integer >= 0) + .map(integer -> Capability.values()[integer]) + .collect(Collectors.toSet())); + } } /** @@ -181,7 +193,9 @@ public class Capabilities { } public static boolean hasMandatoryCapability(Capabilities capabilities, Capability mandatoryCapability) { - return capabilities.capabilities.stream().anyMatch(c -> c == mandatoryCapability); + synchronized (capabilities.capabilities) { + return capabilities.capabilities.stream().anyMatch(c -> c == mandatoryCapability); + } } @Override @@ -211,8 +225,10 @@ public class Capabilities { // Neither would support removal of past capabilities, a use case we never had so far and which might have // backward compatibility issues, so we should treat capabilities as an append-only data structure. public int findHighestCapability(Capabilities capabilities) { - return (int) capabilities.capabilities.stream() - .mapToLong(e -> (long) e.ordinal()) - .sum(); + synchronized (capabilities.capabilities) { + return (int) capabilities.capabilities.stream() + .mapToLong(e -> (long) e.ordinal()) + .sum(); + } } } From 775fbc41c2720f5c4a71dff36424dd0288f3fa4f Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 16 Dec 2024 07:04:53 -0500 Subject: [PATCH 26/34] support buying xmr without deposit or fee using passphrase --- .../haveno/apitest/method/MethodTest.java | 6 +- .../apitest/method/offer/CancelOfferTest.java | 2 +- .../offer/CreateOfferUsingFixedPriceTest.java | 6 +- ...CreateOfferUsingMarketPriceMarginTest.java | 10 +- .../method/offer/CreateXMROffersTest.java | 8 +- .../method/offer/ValidateCreateOfferTest.java | 6 +- .../method/trade/TakeBuyBTCOfferTest.java | 2 +- ...keBuyBTCOfferWithNationalBankAcctTest.java | 2 +- .../method/trade/TakeBuyXMROfferTest.java | 2 +- .../method/trade/TakeSellBTCOfferTest.java | 2 +- .../method/trade/TakeSellXMROfferTest.java | 2 +- .../LongRunningOfferDeactivationTest.java | 4 +- .../apitest/scenario/bot/RandomOffer.java | 6 +- .../cli/request/OffersServiceRequest.java | 2 +- .../witness/AccountAgeWitnessService.java | 8 +- .../main/java/haveno/core/api/CoreApi.java | 22 +- .../haveno/core/api/CoreDisputesService.java | 29 +- .../haveno/core/api/CoreOffersService.java | 22 +- .../haveno/core/api/CoreTradesService.java | 2 +- .../java/haveno/core/api/model/OfferInfo.java | 15 +- .../java/haveno/core/api/model/TradeInfo.java | 16 +- .../api/model/builder/OfferInfoBuilder.java | 12 + .../haveno/core/offer/CreateOfferService.java | 112 +- .../main/java/haveno/core/offer/Offer.java | 18 + .../haveno/core/offer/OfferFilterService.java | 2 +- .../java/haveno/core/offer/OfferPayload.java | 25 +- .../java/haveno/core/offer/OfferUtil.java | 19 +- .../java/haveno/core/offer/OpenOffer.java | 14 +- .../haveno/core/offer/OpenOfferManager.java | 109 +- .../tasks/SendOfferAvailabilityRequest.java | 3 +- .../offer/placeoffer/tasks/ValidateOffer.java | 22 +- .../core/offer/takeoffer/TakeOfferModel.java | 3 +- .../core/payment/PaymentAccountUtil.java | 2 +- .../java/haveno/core/payment/TradeLimits.java | 11 + .../validation/SecurityDepositValidator.java | 4 +- .../haveno/core/trade/ArbitratorTrade.java | 10 +- .../haveno/core/trade/BuyerAsMakerTrade.java | 11 +- .../haveno/core/trade/BuyerAsTakerTrade.java | 9 +- .../java/haveno/core/trade/BuyerTrade.java | 6 +- .../main/java/haveno/core/trade/Contract.java | 57 +- .../java/haveno/core/trade/HavenoUtils.java | 43 +- .../haveno/core/trade/SellerAsMakerTrade.java | 9 +- .../haveno/core/trade/SellerAsTakerTrade.java | 9 +- .../java/haveno/core/trade/SellerTrade.java | 6 +- .../main/java/haveno/core/trade/Trade.java | 60 +- .../java/haveno/core/trade/TradeManager.java | 29 +- .../core/trade/messages/DepositRequest.java | 17 +- .../core/trade/messages/InitTradeRequest.java | 11 +- .../trade/messages/SignContractRequest.java | 11 +- .../haveno/core/trade/protocol/TradePeer.java | 1 - .../ArbitratorProcessDepositRequest.java | 87 +- .../tasks/ArbitratorProcessReserveTx.java | 62 +- ...tratorSendInitTradeOrMultisigRequests.java | 3 +- .../tasks/BuyerPreparePaymentSentMessage.java | 2 +- ...MakerSendInitTradeRequestToArbitrator.java | 3 +- .../tasks/MaybeSendSignContractRequest.java | 81 +- .../tasks/ProcessSignContractRequest.java | 20 +- .../protocol/tasks/SendDepositRequest.java | 4 +- .../tasks/TakerReserveTradeFunds.java | 91 +- ...TakerSendInitTradeRequestToArbitrator.java | 7 +- .../TakerSendInitTradeRequestToMaker.java | 8 +- .../java/haveno/core/user/Preferences.java | 27 +- .../haveno/core/user/PreferencesPayload.java | 17 +- .../java/haveno/core/util/coin/CoinUtil.java | 34 +- .../haveno/core/xmr/wallet/Restrictions.java | 37 +- core/src/main/resources/bip39_english.txt | 2048 +++++++++++++++++ .../resources/i18n/displayStrings.properties | 20 +- .../i18n/displayStrings_cs.properties | 17 +- .../i18n/displayStrings_de.properties | 17 +- .../i18n/displayStrings_es.properties | 17 +- .../i18n/displayStrings_fa.properties | 19 +- .../i18n/displayStrings_fr.properties | 17 +- .../i18n/displayStrings_it.properties | 19 +- .../i18n/displayStrings_ja.properties | 17 +- .../i18n/displayStrings_pt-br.properties | 19 +- .../i18n/displayStrings_pt.properties | 21 +- .../i18n/displayStrings_ru.properties | 19 +- .../i18n/displayStrings_th.properties | 19 +- .../i18n/displayStrings_tr.properties | 17 +- .../i18n/displayStrings_vi.properties | 19 +- .../i18n/displayStrings_zh-hans.properties | 19 +- .../i18n/displayStrings_zh-hant.properties | 19 +- .../haveno/daemon/grpc/GrpcOffersService.java | 4 +- .../haveno/daemon/grpc/GrpcTradesService.java | 1 + .../paymentmethods/PaymentMethodForm.java | 6 +- .../src/main/java/haveno/desktop/images.css | 4 + .../main/offer/MutableOfferDataModel.java | 81 +- .../desktop/main/offer/MutableOfferView.java | 167 +- .../main/offer/MutableOfferViewModel.java | 129 +- .../desktop/main/offer/OfferDataModel.java | 4 + .../main/offer/offerbook/OfferBookView.java | 48 +- .../offer/offerbook/OfferBookViewModel.java | 10 + .../offer/takeoffer/TakeOfferDataModel.java | 2 +- .../main/offer/takeoffer/TakeOfferView.java | 42 +- .../main/overlays/editor/PasswordPopup.java | 245 ++ .../main/overlays/windows/ContractWindow.java | 2 +- .../windows/DisputeSummaryWindow.java | 46 +- .../overlays/windows/OfferDetailsWindow.java | 82 +- .../DuplicateOfferDataModel.java | 17 +- .../editoffer/EditOfferDataModel.java | 14 +- .../editoffer/EditOfferViewModel.java | 2 +- .../failedtrades/FailedTradesView.java | 2 +- .../openoffer/OpenOffersViewModel.java | 2 +- .../pendingtrades/PendingTradesDataModel.java | 24 +- .../pendingtrades/PendingTradesView.java | 2 +- .../pendingtrades/steps/TradeStepView.java | 48 +- .../main/java/haveno/desktop/theme-dark.css | 9 + .../main/java/haveno/desktop/theme-light.css | 5 + .../haveno/desktop/util/DisplayUtils.java | 18 +- desktop/src/main/resources/images/lock.png | Bin 22292 -> 0 bytes desktop/src/main/resources/images/lock@2x.png | Bin 0 -> 721 bytes .../createoffer/CreateOfferDataModelTest.java | 2 +- .../createoffer/CreateOfferViewModelTest.java | 3 +- proto/src/main/proto/grpc.proto | 7 +- proto/src/main/proto/pb.proto | 10 +- 115 files changed, 3845 insertions(+), 838 deletions(-) create mode 100644 core/src/main/resources/bip39_english.txt create mode 100644 desktop/src/main/java/haveno/desktop/main/overlays/editor/PasswordPopup.java delete mode 100644 desktop/src/main/resources/images/lock.png create mode 100644 desktop/src/main/resources/images/lock@2x.png diff --git a/apitest/src/test/java/haveno/apitest/method/MethodTest.java b/apitest/src/test/java/haveno/apitest/method/MethodTest.java index 739c7c03..01c7a3bf 100644 --- a/apitest/src/test/java/haveno/apitest/method/MethodTest.java +++ b/apitest/src/test/java/haveno/apitest/method/MethodTest.java @@ -43,7 +43,7 @@ import java.util.stream.Collectors; import static haveno.apitest.config.ApiTestConfig.BTC; import static haveno.apitest.config.ApiTestRateMeterInterceptorConfig.getTestRateMeterInterceptorConfig; import static haveno.cli.table.builder.TableType.BTC_BALANCE_TBL; -import static haveno.core.xmr.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; +import static haveno.core.xmr.wallet.Restrictions.getDefaultSecurityDepositAsPercent; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.stream; @@ -157,8 +157,8 @@ public class MethodTest extends ApiTestCase { return haveno.core.payment.PaymentAccount.fromProto(paymentAccount, CORE_PROTO_RESOLVER); } - public static final Supplier defaultBuyerSecurityDepositPct = () -> { - var defaultPct = BigDecimal.valueOf(getDefaultBuyerSecurityDepositAsPercent()); + public static final Supplier defaultSecurityDepositPct = () -> { + var defaultPct = BigDecimal.valueOf(getDefaultSecurityDepositAsPercent()); if (defaultPct.precision() != 2) throw new IllegalStateException(format( "Unexpected decimal precision, expected 2 but actual is %d%n." diff --git a/apitest/src/test/java/haveno/apitest/method/offer/CancelOfferTest.java b/apitest/src/test/java/haveno/apitest/method/offer/CancelOfferTest.java index a7776dd6..18676055 100644 --- a/apitest/src/test/java/haveno/apitest/method/offer/CancelOfferTest.java +++ b/apitest/src/test/java/haveno/apitest/method/offer/CancelOfferTest.java @@ -47,7 +47,7 @@ public class CancelOfferTest extends AbstractOfferTest { 10000000L, 10000000L, 0.00, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), paymentAccountId, NO_TRIGGER_PRICE); }; diff --git a/apitest/src/test/java/haveno/apitest/method/offer/CreateOfferUsingFixedPriceTest.java b/apitest/src/test/java/haveno/apitest/method/offer/CreateOfferUsingFixedPriceTest.java index 38d83f69..49c01d5a 100644 --- a/apitest/src/test/java/haveno/apitest/method/offer/CreateOfferUsingFixedPriceTest.java +++ b/apitest/src/test/java/haveno/apitest/method/offer/CreateOfferUsingFixedPriceTest.java @@ -49,7 +49,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { 10_000_000L, 10_000_000L, "36000", - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), audAccount.getId()); log.debug("Offer #1:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); @@ -97,7 +97,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { 10_000_000L, 10_000_000L, "30000.1234", - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), usdAccount.getId()); log.debug("Offer #2:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); @@ -145,7 +145,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { 10_000_000L, 5_000_000L, "29500.1234", - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), eurAccount.getId()); log.debug("Offer #3:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); diff --git a/apitest/src/test/java/haveno/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java b/apitest/src/test/java/haveno/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java index cc6f53ac..f4dff640 100644 --- a/apitest/src/test/java/haveno/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java +++ b/apitest/src/test/java/haveno/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java @@ -66,7 +66,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { 10_000_000L, 10_000_000L, priceMarginPctInput, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), usdAccount.getId(), NO_TRIGGER_PRICE); log.debug("Offer #1:\n{}", toOfferTable.apply(newOffer)); @@ -114,7 +114,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { 10_000_000L, 10_000_000L, priceMarginPctInput, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), nzdAccount.getId(), NO_TRIGGER_PRICE); log.debug("Offer #2:\n{}", toOfferTable.apply(newOffer)); @@ -162,7 +162,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { 10_000_000L, 5_000_000L, priceMarginPctInput, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), gbpAccount.getId(), NO_TRIGGER_PRICE); log.debug("Offer #3:\n{}", toOfferTable.apply(newOffer)); @@ -210,7 +210,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { 10_000_000L, 5_000_000L, priceMarginPctInput, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), brlAccount.getId(), NO_TRIGGER_PRICE); log.debug("Offer #4:\n{}", toOfferTable.apply(newOffer)); @@ -259,7 +259,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { 10_000_000L, 5_000_000L, 0.0, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), usdAccount.getId(), triggerPrice); assertTrue(newOffer.getIsMyOffer()); diff --git a/apitest/src/test/java/haveno/apitest/method/offer/CreateXMROffersTest.java b/apitest/src/test/java/haveno/apitest/method/offer/CreateXMROffersTest.java index 4b7032c8..8571c0c5 100644 --- a/apitest/src/test/java/haveno/apitest/method/offer/CreateXMROffersTest.java +++ b/apitest/src/test/java/haveno/apitest/method/offer/CreateXMROffersTest.java @@ -62,7 +62,7 @@ public class CreateXMROffersTest extends AbstractOfferTest { 100_000_000L, 75_000_000L, "0.005", // FIXED PRICE IN BTC FOR 1 XMR - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), alicesXmrAcct.getId()); log.debug("Sell XMR (Buy BTC) offer:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); @@ -108,7 +108,7 @@ public class CreateXMROffersTest extends AbstractOfferTest { 100_000_000L, 50_000_000L, "0.005", // FIXED PRICE IN BTC (satoshis) FOR 1 XMR - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), alicesXmrAcct.getId()); log.debug("Buy XMR (Sell BTC) offer:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); @@ -156,7 +156,7 @@ public class CreateXMROffersTest extends AbstractOfferTest { 100_000_000L, 75_000_000L, priceMarginPctInput, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), alicesXmrAcct.getId(), triggerPrice); log.debug("Pending Sell XMR (Buy BTC) offer:\n{}", toOfferTable.apply(newOffer)); @@ -211,7 +211,7 @@ public class CreateXMROffersTest extends AbstractOfferTest { 100_000_000L, 50_000_000L, priceMarginPctInput, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), alicesXmrAcct.getId(), NO_TRIGGER_PRICE); log.debug("Buy XMR (Sell BTC) offer:\n{}", toOfferTable.apply(newOffer)); diff --git a/apitest/src/test/java/haveno/apitest/method/offer/ValidateCreateOfferTest.java b/apitest/src/test/java/haveno/apitest/method/offer/ValidateCreateOfferTest.java index f299801c..e8745c1b 100644 --- a/apitest/src/test/java/haveno/apitest/method/offer/ValidateCreateOfferTest.java +++ b/apitest/src/test/java/haveno/apitest/method/offer/ValidateCreateOfferTest.java @@ -47,7 +47,7 @@ public class ValidateCreateOfferTest extends AbstractOfferTest { 100000000000L, // exceeds amount limit 100000000000L, "10000.0000", - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), usdAccount.getId())); assertEquals("UNKNOWN: An error occurred at task: ValidateOffer", exception.getMessage()); } @@ -63,7 +63,7 @@ public class ValidateCreateOfferTest extends AbstractOfferTest { 10000000L, 10000000L, "40000.0000", - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), chfAccount.getId())); String expectedError = format("UNKNOWN: cannot create EUR offer with payment account %s", chfAccount.getId()); assertEquals(expectedError, exception.getMessage()); @@ -80,7 +80,7 @@ public class ValidateCreateOfferTest extends AbstractOfferTest { 10000000L, 10000000L, "63000.0000", - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), audAccount.getId())); String expectedError = format("UNKNOWN: cannot create CAD offer with payment account %s", audAccount.getId()); assertEquals(expectedError, exception.getMessage()); diff --git a/apitest/src/test/java/haveno/apitest/method/trade/TakeBuyBTCOfferTest.java b/apitest/src/test/java/haveno/apitest/method/trade/TakeBuyBTCOfferTest.java index fee6e798..abbec38f 100644 --- a/apitest/src/test/java/haveno/apitest/method/trade/TakeBuyBTCOfferTest.java +++ b/apitest/src/test/java/haveno/apitest/method/trade/TakeBuyBTCOfferTest.java @@ -52,7 +52,7 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest { 12_500_000L, 12_500_000L, // min-amount = amount 0.00, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), alicesUsdAccount.getId(), NO_TRIGGER_PRICE); var offerId = alicesOffer.getId(); diff --git a/apitest/src/test/java/haveno/apitest/method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java b/apitest/src/test/java/haveno/apitest/method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java index 10be976c..3199a6b0 100644 --- a/apitest/src/test/java/haveno/apitest/method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java +++ b/apitest/src/test/java/haveno/apitest/method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java @@ -96,7 +96,7 @@ public class TakeBuyBTCOfferWithNationalBankAcctTest extends AbstractTradeTest { 1_000_000L, 1_000_000L, // min-amount = amount 0.00, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), alicesPaymentAccount.getId(), NO_TRIGGER_PRICE); var offerId = alicesOffer.getId(); diff --git a/apitest/src/test/java/haveno/apitest/method/trade/TakeBuyXMROfferTest.java b/apitest/src/test/java/haveno/apitest/method/trade/TakeBuyXMROfferTest.java index 40289e1d..f6120340 100644 --- a/apitest/src/test/java/haveno/apitest/method/trade/TakeBuyXMROfferTest.java +++ b/apitest/src/test/java/haveno/apitest/method/trade/TakeBuyXMROfferTest.java @@ -65,7 +65,7 @@ public class TakeBuyXMROfferTest extends AbstractTradeTest { 15_000_000L, 7_500_000L, "0.00455500", // FIXED PRICE IN BTC (satoshis) FOR 1 XMR - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), alicesXmrAcct.getId()); log.debug("Alice's BUY XMR (SELL BTC) Offer:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build()); genBtcBlocksThenWait(1, 5000); diff --git a/apitest/src/test/java/haveno/apitest/method/trade/TakeSellBTCOfferTest.java b/apitest/src/test/java/haveno/apitest/method/trade/TakeSellBTCOfferTest.java index a4257597..4f43c21c 100644 --- a/apitest/src/test/java/haveno/apitest/method/trade/TakeSellBTCOfferTest.java +++ b/apitest/src/test/java/haveno/apitest/method/trade/TakeSellBTCOfferTest.java @@ -58,7 +58,7 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest { 12_500_000L, 12_500_000L, // min-amount = amount 0.00, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), alicesUsdAccount.getId(), NO_TRIGGER_PRICE); var offerId = alicesOffer.getId(); diff --git a/apitest/src/test/java/haveno/apitest/method/trade/TakeSellXMROfferTest.java b/apitest/src/test/java/haveno/apitest/method/trade/TakeSellXMROfferTest.java index 68daa640..9a769b5f 100644 --- a/apitest/src/test/java/haveno/apitest/method/trade/TakeSellXMROfferTest.java +++ b/apitest/src/test/java/haveno/apitest/method/trade/TakeSellXMROfferTest.java @@ -71,7 +71,7 @@ public class TakeSellXMROfferTest extends AbstractTradeTest { 20_000_000L, 10_500_000L, priceMarginPctInput, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), alicesXmrAcct.getId(), NO_TRIGGER_PRICE); log.debug("Alice's SELL XMR (BUY BTC) Offer:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build()); diff --git a/apitest/src/test/java/haveno/apitest/scenario/LongRunningOfferDeactivationTest.java b/apitest/src/test/java/haveno/apitest/scenario/LongRunningOfferDeactivationTest.java index 13b72ff7..356b77ef 100644 --- a/apitest/src/test/java/haveno/apitest/scenario/LongRunningOfferDeactivationTest.java +++ b/apitest/src/test/java/haveno/apitest/scenario/LongRunningOfferDeactivationTest.java @@ -57,7 +57,7 @@ public class LongRunningOfferDeactivationTest extends AbstractOfferTest { 1_000_000, 1_000_000, 0.00, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), paymentAcct.getId(), triggerPrice); log.info("SELL offer {} created with margin based price {}.", @@ -103,7 +103,7 @@ public class LongRunningOfferDeactivationTest extends AbstractOfferTest { 1_000_000, 1_000_000, 0.00, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), paymentAcct.getId(), triggerPrice); log.info("BUY offer {} created with margin based price {}.", diff --git a/apitest/src/test/java/haveno/apitest/scenario/bot/RandomOffer.java b/apitest/src/test/java/haveno/apitest/scenario/bot/RandomOffer.java index eebaec8b..4e8b86af 100644 --- a/apitest/src/test/java/haveno/apitest/scenario/bot/RandomOffer.java +++ b/apitest/src/test/java/haveno/apitest/scenario/bot/RandomOffer.java @@ -28,7 +28,7 @@ import java.text.DecimalFormat; import java.util.Objects; import java.util.function.Supplier; -import static haveno.apitest.method.offer.AbstractOfferTest.defaultBuyerSecurityDepositPct; +import static haveno.apitest.method.offer.AbstractOfferTest.defaultSecurityDepositPct; import static haveno.cli.CurrencyFormat.formatInternalFiatPrice; import static haveno.cli.CurrencyFormat.formatSatoshis; import static haveno.common.util.MathUtils.scaleDownByPowerOf10; @@ -119,7 +119,7 @@ public class RandomOffer { amount, minAmount, priceMargin, - defaultBuyerSecurityDepositPct.get(), + defaultSecurityDepositPct.get(), "0" /*no trigger price*/); } else { this.offer = botClient.createOfferAtFixedPrice(paymentAccount, @@ -128,7 +128,7 @@ public class RandomOffer { amount, minAmount, fixedOfferPrice, - defaultBuyerSecurityDepositPct.get()); + defaultSecurityDepositPct.get()); } this.id = offer.getId(); return this; diff --git a/cli/src/main/java/haveno/cli/request/OffersServiceRequest.java b/cli/src/main/java/haveno/cli/request/OffersServiceRequest.java index eaa0cac1..2fcb3426 100644 --- a/cli/src/main/java/haveno/cli/request/OffersServiceRequest.java +++ b/cli/src/main/java/haveno/cli/request/OffersServiceRequest.java @@ -81,7 +81,7 @@ public class OffersServiceRequest { .setUseMarketBasedPrice(useMarketBasedPrice) .setPrice(fixedPrice) .setMarketPriceMarginPct(marketPriceMarginPct) - .setBuyerSecurityDepositPct(securityDepositPct) + .setSecurityDepositPct(securityDepositPct) .setPaymentAccountId(paymentAcctId) .setTriggerPrice(triggerPrice) .build(); diff --git a/core/src/main/java/haveno/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/haveno/core/account/witness/AccountAgeWitnessService.java index 4d91d75e..978b6dd7 100644 --- a/core/src/main/java/haveno/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/haveno/core/account/witness/AccountAgeWitnessService.java @@ -40,6 +40,7 @@ import haveno.core.offer.OfferDirection; import haveno.core.offer.OfferRestrictions; import haveno.core.payment.ChargeBackRisk; import haveno.core.payment.PaymentAccount; +import haveno.core.payment.TradeLimits; import haveno.core.payment.payload.PaymentAccountPayload; import haveno.core.payment.payload.PaymentMethod; import haveno.core.support.dispute.Dispute; @@ -498,10 +499,15 @@ public class AccountAgeWitnessService { return getAccountAge(getMyWitness(paymentAccountPayload), new Date()); } - public long getMyTradeLimit(PaymentAccount paymentAccount, String currencyCode, OfferDirection direction) { + public long getMyTradeLimit(PaymentAccount paymentAccount, String currencyCode, OfferDirection direction, boolean buyerAsTakerWithoutDeposit) { if (paymentAccount == null) return 0; + if (buyerAsTakerWithoutDeposit) { + TradeLimits tradeLimits = new TradeLimits(); + return tradeLimits.getMaxTradeLimitBuyerAsTakerWithoutDeposit().longValueExact(); + } + AccountAgeWitness accountAgeWitness = getMyWitness(paymentAccount.getPaymentAccountPayload()); BigInteger maxTradeLimit = paymentAccount.getPaymentMethod().getMaxTradeLimit(currencyCode); if (hasTradeLimitException(accountAgeWitness)) { diff --git a/core/src/main/java/haveno/core/api/CoreApi.java b/core/src/main/java/haveno/core/api/CoreApi.java index 14ec0f6d..c91d4a40 100644 --- a/core/src/main/java/haveno/core/api/CoreApi.java +++ b/core/src/main/java/haveno/core/api/CoreApi.java @@ -419,10 +419,12 @@ public class CoreApi { double marketPriceMargin, long amountAsLong, long minAmountAsLong, - double buyerSecurityDeposit, + double securityDepositPct, String triggerPriceAsString, boolean reserveExactAmount, String paymentAccountId, + boolean isPrivateOffer, + boolean buyerAsTakerWithoutDeposit, Consumer resultHandler, ErrorMessageHandler errorMessageHandler) { coreOffersService.postOffer(currencyCode, @@ -432,10 +434,12 @@ public class CoreApi { marketPriceMargin, amountAsLong, minAmountAsLong, - buyerSecurityDeposit, + securityDepositPct, triggerPriceAsString, reserveExactAmount, paymentAccountId, + isPrivateOffer, + buyerAsTakerWithoutDeposit, resultHandler, errorMessageHandler); } @@ -448,8 +452,10 @@ public class CoreApi { double marketPriceMargin, BigInteger amount, BigInteger minAmount, - double buyerSecurityDeposit, - PaymentAccount paymentAccount) { + double securityDepositPct, + PaymentAccount paymentAccount, + boolean isPrivateOffer, + boolean buyerAsTakerWithoutDeposit) { return coreOffersService.editOffer(offerId, currencyCode, direction, @@ -458,8 +464,10 @@ public class CoreApi { marketPriceMargin, amount, minAmount, - buyerSecurityDeposit, - paymentAccount); + securityDepositPct, + paymentAccount, + isPrivateOffer, + buyerAsTakerWithoutDeposit); } public void cancelOffer(String id, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { @@ -535,9 +543,11 @@ public class CoreApi { public void takeOffer(String offerId, String paymentAccountId, long amountAsLong, + String challenge, Consumer resultHandler, ErrorMessageHandler errorMessageHandler) { Offer offer = coreOffersService.getOffer(offerId); + offer.setChallenge(challenge); coreTradesService.takeOffer(offer, paymentAccountId, amountAsLong, resultHandler, errorMessageHandler); } diff --git a/core/src/main/java/haveno/core/api/CoreDisputesService.java b/core/src/main/java/haveno/core/api/CoreDisputesService.java index f193287e..f4bb4c80 100644 --- a/core/src/main/java/haveno/core/api/CoreDisputesService.java +++ b/core/src/main/java/haveno/core/api/CoreDisputesService.java @@ -62,11 +62,12 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class CoreDisputesService { - public enum DisputePayout { + // TODO: persist in DisputeResult? + public enum PayoutSuggestion { BUYER_GETS_TRADE_AMOUNT, - BUYER_GETS_ALL, // used in desktop + BUYER_GETS_ALL, SELLER_GETS_TRADE_AMOUNT, - SELLER_GETS_ALL, // used in desktop + SELLER_GETS_ALL, CUSTOM } @@ -172,17 +173,17 @@ public class CoreDisputesService { // create dispute result var closeDate = new Date(); var winnerDisputeResult = createDisputeResult(winningDispute, winner, reason, summaryNotes, closeDate); - DisputePayout payout; + PayoutSuggestion payoutSuggestion; if (customWinnerAmount > 0) { - payout = DisputePayout.CUSTOM; + payoutSuggestion = PayoutSuggestion.CUSTOM; } else if (winner == DisputeResult.Winner.BUYER) { - payout = DisputePayout.BUYER_GETS_TRADE_AMOUNT; + payoutSuggestion = PayoutSuggestion.BUYER_GETS_TRADE_AMOUNT; } else if (winner == DisputeResult.Winner.SELLER) { - payout = DisputePayout.SELLER_GETS_TRADE_AMOUNT; + payoutSuggestion = PayoutSuggestion.SELLER_GETS_TRADE_AMOUNT; } else { throw new IllegalStateException("Unexpected DisputeResult.Winner: " + winner); } - applyPayoutAmountsToDisputeResult(payout, winningDispute, winnerDisputeResult, customWinnerAmount); + applyPayoutAmountsToDisputeResult(payoutSuggestion, winningDispute, winnerDisputeResult, customWinnerAmount); // close winning dispute ticket closeDisputeTicket(arbitrationManager, winningDispute, winnerDisputeResult, () -> { @@ -227,26 +228,26 @@ public class CoreDisputesService { * Sets payout amounts given a payout type. If custom is selected, the winner gets a custom amount, and the peer * receives the remaining amount minus the mining fee. */ - public void applyPayoutAmountsToDisputeResult(DisputePayout payout, Dispute dispute, DisputeResult disputeResult, long customWinnerAmount) { + public void applyPayoutAmountsToDisputeResult(PayoutSuggestion payoutSuggestion, Dispute dispute, DisputeResult disputeResult, long customWinnerAmount) { Contract contract = dispute.getContract(); Trade trade = tradeManager.getTrade(dispute.getTradeId()); BigInteger buyerSecurityDeposit = trade.getBuyer().getSecurityDeposit(); BigInteger sellerSecurityDeposit = trade.getSeller().getSecurityDeposit(); BigInteger tradeAmount = contract.getTradeAmount(); disputeResult.setSubtractFeeFrom(DisputeResult.SubtractFeeFrom.BUYER_AND_SELLER); - if (payout == DisputePayout.BUYER_GETS_TRADE_AMOUNT) { + if (payoutSuggestion == PayoutSuggestion.BUYER_GETS_TRADE_AMOUNT) { disputeResult.setBuyerPayoutAmountBeforeCost(tradeAmount.add(buyerSecurityDeposit)); disputeResult.setSellerPayoutAmountBeforeCost(sellerSecurityDeposit); - } else if (payout == DisputePayout.BUYER_GETS_ALL) { + } else if (payoutSuggestion == PayoutSuggestion.BUYER_GETS_ALL) { disputeResult.setBuyerPayoutAmountBeforeCost(tradeAmount.add(buyerSecurityDeposit).add(sellerSecurityDeposit)); // TODO (woodser): apply min payout to incentivize loser? (see post v1.1.7) disputeResult.setSellerPayoutAmountBeforeCost(BigInteger.ZERO); - } else if (payout == DisputePayout.SELLER_GETS_TRADE_AMOUNT) { + } else if (payoutSuggestion == PayoutSuggestion.SELLER_GETS_TRADE_AMOUNT) { disputeResult.setBuyerPayoutAmountBeforeCost(buyerSecurityDeposit); disputeResult.setSellerPayoutAmountBeforeCost(tradeAmount.add(sellerSecurityDeposit)); - } else if (payout == DisputePayout.SELLER_GETS_ALL) { + } else if (payoutSuggestion == PayoutSuggestion.SELLER_GETS_ALL) { disputeResult.setBuyerPayoutAmountBeforeCost(BigInteger.ZERO); disputeResult.setSellerPayoutAmountBeforeCost(tradeAmount.add(sellerSecurityDeposit).add(buyerSecurityDeposit)); - } else if (payout == DisputePayout.CUSTOM) { + } else if (payoutSuggestion == PayoutSuggestion.CUSTOM) { if (customWinnerAmount > trade.getWallet().getBalance().longValueExact()) throw new RuntimeException("Winner payout is more than the trade wallet's balance"); long loserAmount = tradeAmount.add(buyerSecurityDeposit).add(sellerSecurityDeposit).subtract(BigInteger.valueOf(customWinnerAmount)).longValueExact(); if (loserAmount < 0) throw new RuntimeException("Loser payout cannot be negative"); diff --git a/core/src/main/java/haveno/core/api/CoreOffersService.java b/core/src/main/java/haveno/core/api/CoreOffersService.java index 5bdd2f16..82328278 100644 --- a/core/src/main/java/haveno/core/api/CoreOffersService.java +++ b/core/src/main/java/haveno/core/api/CoreOffersService.java @@ -172,10 +172,12 @@ public class CoreOffersService { double marketPriceMargin, long amountAsLong, long minAmountAsLong, - double securityDeposit, + double securityDepositPct, String triggerPriceAsString, boolean reserveExactAmount, String paymentAccountId, + boolean isPrivateOffer, + boolean buyerAsTakerWithoutDeposit, Consumer resultHandler, ErrorMessageHandler errorMessageHandler) { coreWalletsService.verifyWalletsAreAvailable(); @@ -199,8 +201,10 @@ public class CoreOffersService { price, useMarketBasedPrice, exactMultiply(marketPriceMargin, 0.01), - securityDeposit, - paymentAccount); + securityDepositPct, + paymentAccount, + isPrivateOffer, + buyerAsTakerWithoutDeposit); verifyPaymentAccountIsValidForNewOffer(offer, paymentAccount); @@ -223,8 +227,10 @@ public class CoreOffersService { double marketPriceMargin, BigInteger amount, BigInteger minAmount, - double buyerSecurityDeposit, - PaymentAccount paymentAccount) { + double securityDepositPct, + PaymentAccount paymentAccount, + boolean isPrivateOffer, + boolean buyerAsTakerWithoutDeposit) { return createOfferService.createAndGetOffer(offerId, direction, currencyCode.toUpperCase(), @@ -233,8 +239,10 @@ public class CoreOffersService { price, useMarketBasedPrice, exactMultiply(marketPriceMargin, 0.01), - buyerSecurityDeposit, - paymentAccount); + securityDepositPct, + paymentAccount, + isPrivateOffer, + buyerAsTakerWithoutDeposit); } void cancelOffer(String id, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { diff --git a/core/src/main/java/haveno/core/api/CoreTradesService.java b/core/src/main/java/haveno/core/api/CoreTradesService.java index 431ab9a6..14f10b4f 100644 --- a/core/src/main/java/haveno/core/api/CoreTradesService.java +++ b/core/src/main/java/haveno/core/api/CoreTradesService.java @@ -132,7 +132,7 @@ class CoreTradesService { // adjust amount for fixed-price offer (based on TakeOfferViewModel) String currencyCode = offer.getCurrencyCode(); OfferDirection direction = offer.getOfferPayload().getDirection(); - long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction); + long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction, offer.hasBuyerAsTakerWithoutDeposit()); if (offer.getPrice() != null) { if (PaymentMethod.isRoundedForAtmCash(paymentAccount.getPaymentMethod().getId())) { amount = CoinUtil.getRoundedAtmCashAmount(amount, offer.getPrice(), maxTradeLimit); diff --git a/core/src/main/java/haveno/core/api/model/OfferInfo.java b/core/src/main/java/haveno/core/api/model/OfferInfo.java index b489aaa8..537cc9ab 100644 --- a/core/src/main/java/haveno/core/api/model/OfferInfo.java +++ b/core/src/main/java/haveno/core/api/model/OfferInfo.java @@ -78,6 +78,8 @@ public class OfferInfo implements Payload { @Nullable private final String splitOutputTxHash; private final long splitOutputTxFee; + private final boolean isPrivateOffer; + private final String challenge; public OfferInfo(OfferInfoBuilder builder) { this.id = builder.getId(); @@ -111,6 +113,8 @@ public class OfferInfo implements Payload { this.arbitratorSigner = builder.getArbitratorSigner(); this.splitOutputTxHash = builder.getSplitOutputTxHash(); this.splitOutputTxFee = builder.getSplitOutputTxFee(); + this.isPrivateOffer = builder.isPrivateOffer(); + this.challenge = builder.getChallenge(); } public static OfferInfo toOfferInfo(Offer offer) { @@ -137,6 +141,7 @@ public class OfferInfo implements Payload { .withIsActivated(isActivated) .withSplitOutputTxHash(openOffer.getSplitOutputTxHash()) .withSplitOutputTxFee(openOffer.getSplitOutputTxFee()) + .withChallenge(openOffer.getChallenge()) .build(); } @@ -177,7 +182,9 @@ public class OfferInfo implements Payload { .withPubKeyRing(offer.getOfferPayload().getPubKeyRing().toString()) .withVersionNumber(offer.getOfferPayload().getVersionNr()) .withProtocolVersion(offer.getOfferPayload().getProtocolVersion()) - .withArbitratorSigner(offer.getOfferPayload().getArbitratorSigner() == null ? null : offer.getOfferPayload().getArbitratorSigner().getFullAddress()); + .withArbitratorSigner(offer.getOfferPayload().getArbitratorSigner() == null ? null : offer.getOfferPayload().getArbitratorSigner().getFullAddress()) + .withIsPrivateOffer(offer.isPrivateOffer()) + .withChallenge(offer.getChallenge()); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -215,9 +222,11 @@ public class OfferInfo implements Payload { .setPubKeyRing(pubKeyRing) .setVersionNr(versionNumber) .setProtocolVersion(protocolVersion) - .setSplitOutputTxFee(splitOutputTxFee); + .setSplitOutputTxFee(splitOutputTxFee) + .setIsPrivateOffer(isPrivateOffer); Optional.ofNullable(arbitratorSigner).ifPresent(builder::setArbitratorSigner); Optional.ofNullable(splitOutputTxHash).ifPresent(builder::setSplitOutputTxHash); + Optional.ofNullable(challenge).ifPresent(builder::setChallenge); return builder.build(); } @@ -255,6 +264,8 @@ public class OfferInfo implements Payload { .withArbitratorSigner(proto.getArbitratorSigner()) .withSplitOutputTxHash(proto.getSplitOutputTxHash()) .withSplitOutputTxFee(proto.getSplitOutputTxFee()) + .withIsPrivateOffer(proto.getIsPrivateOffer()) + .withChallenge(proto.getChallenge()) .build(); } } diff --git a/core/src/main/java/haveno/core/api/model/TradeInfo.java b/core/src/main/java/haveno/core/api/model/TradeInfo.java index fa94fd27..8df26368 100644 --- a/core/src/main/java/haveno/core/api/model/TradeInfo.java +++ b/core/src/main/java/haveno/core/api/model/TradeInfo.java @@ -172,14 +172,14 @@ public class TradeInfo implements Payload { .withAmount(trade.getAmount().longValueExact()) .withMakerFee(trade.getMakerFee().longValueExact()) .withTakerFee(trade.getTakerFee().longValueExact()) - .withBuyerSecurityDeposit(trade.getBuyer().getSecurityDeposit() == null ? -1 : trade.getBuyer().getSecurityDeposit().longValueExact()) - .withSellerSecurityDeposit(trade.getSeller().getSecurityDeposit() == null ? -1 : trade.getSeller().getSecurityDeposit().longValueExact()) - .withBuyerDepositTxFee(trade.getBuyer().getDepositTxFee() == null ? -1 : trade.getBuyer().getDepositTxFee().longValueExact()) - .withSellerDepositTxFee(trade.getSeller().getDepositTxFee() == null ? -1 : trade.getSeller().getDepositTxFee().longValueExact()) - .withBuyerPayoutTxFee(trade.getBuyer().getPayoutTxFee() == null ? -1 : trade.getBuyer().getPayoutTxFee().longValueExact()) - .withSellerPayoutTxFee(trade.getSeller().getPayoutTxFee() == null ? -1 : trade.getSeller().getPayoutTxFee().longValueExact()) - .withBuyerPayoutAmount(trade.getBuyer().getPayoutAmount() == null ? -1 : trade.getBuyer().getPayoutAmount().longValueExact()) - .withSellerPayoutAmount(trade.getSeller().getPayoutAmount() == null ? -1 : trade.getSeller().getPayoutAmount().longValueExact()) + .withBuyerSecurityDeposit(trade.getBuyer().getSecurityDeposit().longValueExact()) + .withSellerSecurityDeposit(trade.getSeller().getSecurityDeposit().longValueExact()) + .withBuyerDepositTxFee(trade.getBuyer().getDepositTxFee().longValueExact()) + .withSellerDepositTxFee(trade.getSeller().getDepositTxFee().longValueExact()) + .withBuyerPayoutTxFee(trade.getBuyer().getPayoutTxFee().longValueExact()) + .withSellerPayoutTxFee(trade.getSeller().getPayoutTxFee().longValueExact()) + .withBuyerPayoutAmount(trade.getBuyer().getPayoutAmount().longValueExact()) + .withSellerPayoutAmount(trade.getSeller().getPayoutAmount().longValueExact()) .withTotalTxFee(trade.getTotalTxFee().longValueExact()) .withPrice(toPreciseTradePrice.apply(trade)) .withVolume(toRoundedVolume.apply(trade)) diff --git a/core/src/main/java/haveno/core/api/model/builder/OfferInfoBuilder.java b/core/src/main/java/haveno/core/api/model/builder/OfferInfoBuilder.java index 35d532f6..36801cdb 100644 --- a/core/src/main/java/haveno/core/api/model/builder/OfferInfoBuilder.java +++ b/core/src/main/java/haveno/core/api/model/builder/OfferInfoBuilder.java @@ -63,6 +63,8 @@ public final class OfferInfoBuilder { private String arbitratorSigner; private String splitOutputTxHash; private long splitOutputTxFee; + private boolean isPrivateOffer; + private String challenge; public OfferInfoBuilder withId(String id) { this.id = id; @@ -234,6 +236,16 @@ public final class OfferInfoBuilder { return this; } + public OfferInfoBuilder withIsPrivateOffer(boolean isPrivateOffer) { + this.isPrivateOffer = isPrivateOffer; + return this; + } + + public OfferInfoBuilder withChallenge(String challenge) { + this.challenge = challenge; + return this; + } + public OfferInfo build() { return new OfferInfo(this); } diff --git a/core/src/main/java/haveno/core/offer/CreateOfferService.java b/core/src/main/java/haveno/core/offer/CreateOfferService.java index afd4366a..407953b0 100644 --- a/core/src/main/java/haveno/core/offer/CreateOfferService.java +++ b/core/src/main/java/haveno/core/offer/CreateOfferService.java @@ -33,10 +33,8 @@ import haveno.core.provider.price.PriceFeedService; import haveno.core.support.dispute.arbitration.arbitrator.ArbitratorManager; import haveno.core.trade.HavenoUtils; import haveno.core.trade.statistics.TradeStatisticsManager; -import haveno.core.user.Preferences; import haveno.core.user.User; import haveno.core.util.coin.CoinUtil; -import haveno.core.xmr.wallet.Restrictions; import haveno.core.xmr.wallet.XmrWalletService; import haveno.network.p2p.NodeAddress; import haveno.network.p2p.P2PService; @@ -102,9 +100,10 @@ public class CreateOfferService { Price fixedPrice, boolean useMarketBasedPrice, double marketPriceMargin, - double securityDepositAsDouble, - PaymentAccount paymentAccount) { - + double securityDepositPct, + PaymentAccount paymentAccount, + boolean isPrivateOffer, + boolean buyerAsTakerWithoutDeposit) { log.info("create and get offer with offerId={}, " + "currencyCode={}, " + "direction={}, " + @@ -113,7 +112,9 @@ public class CreateOfferService { "marketPriceMargin={}, " + "amount={}, " + "minAmount={}, " + - "securityDeposit={}", + "securityDepositPct={}, " + + "isPrivateOffer={}, " + + "buyerAsTakerWithoutDeposit={}", offerId, currencyCode, direction, @@ -122,7 +123,16 @@ public class CreateOfferService { marketPriceMargin, amount, minAmount, - securityDepositAsDouble); + securityDepositPct, + isPrivateOffer, + buyerAsTakerWithoutDeposit); + + + // verify buyer as taker security deposit + boolean isBuyerMaker = offerUtil.isBuyOffer(direction); + if (!isBuyerMaker && !isPrivateOffer && buyerAsTakerWithoutDeposit) { + throw new IllegalArgumentException("Buyer as taker deposit is required for public offers"); + } // verify fixed price xor market price with margin if (fixedPrice != null) { @@ -143,10 +153,17 @@ public class CreateOfferService { } // adjust amount and min amount for fixed-price offer - long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction); if (fixedPrice != null) { - amount = CoinUtil.getRoundedAmount(amount, fixedPrice, maxTradeLimit, currencyCode, paymentAccount.getPaymentMethod().getId()); - minAmount = CoinUtil.getRoundedAmount(minAmount, fixedPrice, maxTradeLimit, currencyCode, paymentAccount.getPaymentMethod().getId()); + amount = CoinUtil.getRoundedAmount(amount, fixedPrice, null, currencyCode, paymentAccount.getPaymentMethod().getId()); + minAmount = CoinUtil.getRoundedAmount(minAmount, fixedPrice, null, currencyCode, paymentAccount.getPaymentMethod().getId()); + } + + // generate one-time challenge for private offer + String challenge = null; + String challengeHash = null; + if (isPrivateOffer) { + challenge = HavenoUtils.generateChallenge(); + challengeHash = HavenoUtils.getChallengeHash(challenge); } long priceAsLong = fixedPrice != null ? fixedPrice.getValue() : 0L; @@ -161,21 +178,16 @@ public class CreateOfferService { String bankId = PaymentAccountUtil.getBankId(paymentAccount); List acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount); long maxTradePeriod = paymentAccount.getMaxTradePeriod(); - - // reserved for future use cases - // Use null values if not set - boolean isPrivateOffer = false; + boolean hasBuyerAsTakerWithoutDeposit = !isBuyerMaker && isPrivateOffer && buyerAsTakerWithoutDeposit; + long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction, hasBuyerAsTakerWithoutDeposit); boolean useAutoClose = false; boolean useReOpenAfterAutoClose = false; long lowerClosePrice = 0; long upperClosePrice = 0; - String hashOfChallenge = null; - Map extraDataMap = offerUtil.getExtraDataMap(paymentAccount, - currencyCode, - direction); + Map extraDataMap = offerUtil.getExtraDataMap(paymentAccount, currencyCode, direction); offerUtil.validateOfferData( - securityDepositAsDouble, + securityDepositPct, paymentAccount, currencyCode); @@ -189,11 +201,11 @@ public class CreateOfferService { useMarketBasedPriceValue, amountAsLong, minAmountAsLong, - HavenoUtils.MAKER_FEE_PCT, - HavenoUtils.TAKER_FEE_PCT, + hasBuyerAsTakerWithoutDeposit ? HavenoUtils.MAKER_FEE_FOR_TAKER_WITHOUT_DEPOSIT_PCT : HavenoUtils.MAKER_FEE_PCT, + hasBuyerAsTakerWithoutDeposit ? 0d : HavenoUtils.TAKER_FEE_PCT, HavenoUtils.PENALTY_FEE_PCT, - securityDepositAsDouble, - securityDepositAsDouble, + hasBuyerAsTakerWithoutDeposit ? 0d : securityDepositPct, // buyer as taker security deposit is optional for private offers + securityDepositPct, baseCurrencyCode, counterCurrencyCode, paymentAccount.getPaymentMethod().getId(), @@ -211,7 +223,7 @@ public class CreateOfferService { upperClosePrice, lowerClosePrice, isPrivateOffer, - hashOfChallenge, + challengeHash, extraDataMap, Version.TRADE_PROTOCOL_VERSION, null, @@ -219,38 +231,10 @@ public class CreateOfferService { null); Offer offer = new Offer(offerPayload); offer.setPriceFeedService(priceFeedService); + offer.setChallenge(challenge); return offer; } - public BigInteger getReservedFundsForOffer(OfferDirection direction, - BigInteger amount, - double buyerSecurityDeposit, - double sellerSecurityDeposit) { - - BigInteger reservedFundsForOffer = getSecurityDeposit(direction, - amount, - buyerSecurityDeposit, - sellerSecurityDeposit); - if (!offerUtil.isBuyOffer(direction)) - reservedFundsForOffer = reservedFundsForOffer.add(amount); - - return reservedFundsForOffer; - } - - public BigInteger getSecurityDeposit(OfferDirection direction, - BigInteger amount, - double buyerSecurityDeposit, - double sellerSecurityDeposit) { - return offerUtil.isBuyOffer(direction) ? - getBuyerSecurityDeposit(amount, buyerSecurityDeposit) : - getSellerSecurityDeposit(amount, sellerSecurityDeposit); - } - - public double getSellerSecurityDepositAsDouble(double buyerSecurityDeposit) { - return Preferences.USE_SYMMETRIC_SECURITY_DEPOSIT ? buyerSecurityDeposit : - Restrictions.getSellerSecurityDepositAsPercent(); - } - /////////////////////////////////////////////////////////////////////////////////////////// // Private /////////////////////////////////////////////////////////////////////////////////////////// @@ -259,26 +243,4 @@ public class CreateOfferService { MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode); return marketPrice != null && marketPrice.isExternallyProvidedPrice(); } - - private BigInteger getBuyerSecurityDeposit(BigInteger amount, double buyerSecurityDeposit) { - BigInteger percentOfAmount = CoinUtil.getPercentOfAmount(buyerSecurityDeposit, amount); - return getBoundedBuyerSecurityDeposit(percentOfAmount); - } - - private BigInteger getSellerSecurityDeposit(BigInteger amount, double sellerSecurityDeposit) { - BigInteger percentOfAmount = CoinUtil.getPercentOfAmount(sellerSecurityDeposit, amount); - return getBoundedSellerSecurityDeposit(percentOfAmount); - } - - private BigInteger getBoundedBuyerSecurityDeposit(BigInteger value) { - // We need to ensure that for small amount values we don't get a too low BTC amount. We limit it with using the - // MinBuyerSecurityDeposit from Restrictions. - return Restrictions.getMinBuyerSecurityDeposit().max(value); - } - - private BigInteger getBoundedSellerSecurityDeposit(BigInteger value) { - // We need to ensure that for small amount values we don't get a too low BTC amount. We limit it with using the - // MinSellerSecurityDeposit from Restrictions. - return Restrictions.getMinSellerSecurityDeposit().max(value); - } } diff --git a/core/src/main/java/haveno/core/offer/Offer.java b/core/src/main/java/haveno/core/offer/Offer.java index 675fbeb5..3dc566b3 100644 --- a/core/src/main/java/haveno/core/offer/Offer.java +++ b/core/src/main/java/haveno/core/offer/Offer.java @@ -115,6 +115,12 @@ public class Offer implements NetworkPayload, PersistablePayload { @Setter transient private boolean isReservedFundsSpent; + @JsonExclude + @Getter + @Setter + @Nullable + transient private String challenge; + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor @@ -337,6 +343,18 @@ public class Offer implements NetworkPayload, PersistablePayload { return offerPayload.getSellerSecurityDepositPct(); } + public boolean isPrivateOffer() { + return offerPayload.isPrivateOffer(); + } + + public String getChallengeHash() { + return offerPayload.getChallengeHash(); + } + + public boolean hasBuyerAsTakerWithoutDeposit() { + return getDirection() == OfferDirection.SELL && getBuyerSecurityDepositPct() == 0; + } + public BigInteger getMaxTradeLimit() { return BigInteger.valueOf(offerPayload.getMaxTradeLimit()); } diff --git a/core/src/main/java/haveno/core/offer/OfferFilterService.java b/core/src/main/java/haveno/core/offer/OfferFilterService.java index 51ac8cde..e64a1ee6 100644 --- a/core/src/main/java/haveno/core/offer/OfferFilterService.java +++ b/core/src/main/java/haveno/core/offer/OfferFilterService.java @@ -201,7 +201,7 @@ public class OfferFilterService { accountAgeWitnessService); long myTradeLimit = accountOptional .map(paymentAccount -> accountAgeWitnessService.getMyTradeLimit(paymentAccount, - offer.getCurrencyCode(), offer.getMirroredDirection())) + offer.getCurrencyCode(), offer.getMirroredDirection(), offer.hasBuyerAsTakerWithoutDeposit())) .orElse(0L); long offerMinAmount = offer.getMinAmount().longValueExact(); log.debug("isInsufficientTradeLimit accountOptional={}, myTradeLimit={}, offerMinAmount={}, ", diff --git a/core/src/main/java/haveno/core/offer/OfferPayload.java b/core/src/main/java/haveno/core/offer/OfferPayload.java index fa05685d..0dadf882 100644 --- a/core/src/main/java/haveno/core/offer/OfferPayload.java +++ b/core/src/main/java/haveno/core/offer/OfferPayload.java @@ -156,7 +156,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay // Reserved for possible future use to support private trades where the taker needs to have an accessKey private final boolean isPrivateOffer; @Nullable - private final String hashOfChallenge; + private final String challengeHash; /////////////////////////////////////////////////////////////////////////////////////////// @@ -195,7 +195,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay long lowerClosePrice, long upperClosePrice, boolean isPrivateOffer, - @Nullable String hashOfChallenge, + @Nullable String challengeHash, @Nullable Map extraDataMap, int protocolVersion, @Nullable NodeAddress arbitratorSigner, @@ -238,7 +238,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay this.lowerClosePrice = lowerClosePrice; this.upperClosePrice = upperClosePrice; this.isPrivateOffer = isPrivateOffer; - this.hashOfChallenge = hashOfChallenge; + this.challengeHash = challengeHash; } public byte[] getHash() { @@ -284,7 +284,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay lowerClosePrice, upperClosePrice, isPrivateOffer, - hashOfChallenge, + challengeHash, extraDataMap, protocolVersion, arbitratorSigner, @@ -328,12 +328,17 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay public BigInteger getBuyerSecurityDepositForTradeAmount(BigInteger tradeAmount) { BigInteger securityDepositUnadjusted = HavenoUtils.multiply(tradeAmount, getBuyerSecurityDepositPct()); - return Restrictions.getMinBuyerSecurityDeposit().max(securityDepositUnadjusted); + boolean isBuyerTaker = getDirection() == OfferDirection.SELL; + if (isPrivateOffer() && isBuyerTaker) { + return securityDepositUnadjusted; + } else { + return Restrictions.getMinSecurityDeposit().max(securityDepositUnadjusted); + } } public BigInteger getSellerSecurityDepositForTradeAmount(BigInteger tradeAmount) { BigInteger securityDepositUnadjusted = HavenoUtils.multiply(tradeAmount, getSellerSecurityDepositPct()); - return Restrictions.getMinSellerSecurityDeposit().max(securityDepositUnadjusted); + return Restrictions.getMinSecurityDeposit().max(securityDepositUnadjusted); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -376,7 +381,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay Optional.ofNullable(bankId).ifPresent(builder::setBankId); Optional.ofNullable(acceptedBankIds).ifPresent(builder::addAllAcceptedBankIds); Optional.ofNullable(acceptedCountryCodes).ifPresent(builder::addAllAcceptedCountryCodes); - Optional.ofNullable(hashOfChallenge).ifPresent(builder::setHashOfChallenge); + Optional.ofNullable(challengeHash).ifPresent(builder::setChallengeHash); Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData); Optional.ofNullable(arbitratorSigner).ifPresent(e -> builder.setArbitratorSigner(arbitratorSigner.toProtoMessage())); Optional.ofNullable(arbitratorSignature).ifPresent(e -> builder.setArbitratorSignature(ByteString.copyFrom(e))); @@ -392,7 +397,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay null : new ArrayList<>(proto.getAcceptedCountryCodesList()); List reserveTxKeyImages = proto.getReserveTxKeyImagesList().isEmpty() ? null : new ArrayList<>(proto.getReserveTxKeyImagesList()); - String hashOfChallenge = ProtoUtil.stringOrNullFromProto(proto.getHashOfChallenge()); + String challengeHash = ProtoUtil.stringOrNullFromProto(proto.getChallengeHash()); Map extraDataMapMap = CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap(); @@ -428,7 +433,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay proto.getLowerClosePrice(), proto.getUpperClosePrice(), proto.getIsPrivateOffer(), - hashOfChallenge, + challengeHash, extraDataMapMap, proto.getProtocolVersion(), proto.hasArbitratorSigner() ? NodeAddress.fromProto(proto.getArbitratorSigner()) : null, @@ -475,7 +480,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay ",\r\n lowerClosePrice=" + lowerClosePrice + ",\r\n upperClosePrice=" + upperClosePrice + ",\r\n isPrivateOffer=" + isPrivateOffer + - ",\r\n hashOfChallenge='" + hashOfChallenge + '\'' + + ",\r\n challengeHash='" + challengeHash + '\'' + ",\r\n arbitratorSigner=" + arbitratorSigner + ",\r\n arbitratorSignature=" + Utilities.bytesAsHexString(arbitratorSignature) + "\r\n} "; diff --git a/core/src/main/java/haveno/core/offer/OfferUtil.java b/core/src/main/java/haveno/core/offer/OfferUtil.java index 72593ab5..3d4c1c37 100644 --- a/core/src/main/java/haveno/core/offer/OfferUtil.java +++ b/core/src/main/java/haveno/core/offer/OfferUtil.java @@ -58,8 +58,8 @@ import haveno.core.trade.statistics.ReferralIdService; import haveno.core.user.AutoConfirmSettings; import haveno.core.user.Preferences; import haveno.core.util.coin.CoinFormatter; -import static haveno.core.xmr.wallet.Restrictions.getMaxBuyerSecurityDepositAsPercent; -import static haveno.core.xmr.wallet.Restrictions.getMinBuyerSecurityDepositAsPercent; +import static haveno.core.xmr.wallet.Restrictions.getMaxSecurityDepositAsPercent; +import static haveno.core.xmr.wallet.Restrictions.getMinSecurityDepositAsPercent; import haveno.network.p2p.P2PService; import java.math.BigInteger; import java.util.HashMap; @@ -120,9 +120,10 @@ public class OfferUtil { public long getMaxTradeLimit(PaymentAccount paymentAccount, String currencyCode, - OfferDirection direction) { + OfferDirection direction, + boolean buyerAsTakerWithoutDeposit) { return paymentAccount != null - ? accountAgeWitnessService.getMyTradeLimit(paymentAccount, currencyCode, direction) + ? accountAgeWitnessService.getMyTradeLimit(paymentAccount, currencyCode, direction, buyerAsTakerWithoutDeposit) : 0; } @@ -228,16 +229,16 @@ public class OfferUtil { return extraDataMap.isEmpty() ? null : extraDataMap; } - public void validateOfferData(double buyerSecurityDeposit, + public void validateOfferData(double securityDeposit, PaymentAccount paymentAccount, String currencyCode) { checkNotNull(p2PService.getAddress(), "Address must not be null"); - checkArgument(buyerSecurityDeposit <= getMaxBuyerSecurityDepositAsPercent(), + checkArgument(securityDeposit <= getMaxSecurityDepositAsPercent(), "securityDeposit must not exceed " + - getMaxBuyerSecurityDepositAsPercent()); - checkArgument(buyerSecurityDeposit >= getMinBuyerSecurityDepositAsPercent(), + getMaxSecurityDepositAsPercent()); + checkArgument(securityDeposit >= getMinSecurityDepositAsPercent(), "securityDeposit must not be less than " + - getMinBuyerSecurityDepositAsPercent() + " but was " + buyerSecurityDeposit); + getMinSecurityDepositAsPercent() + " but was " + securityDeposit); checkArgument(!filterManager.isCurrencyBanned(currencyCode), Res.get("offerbook.warning.currencyBanned")); checkArgument(!filterManager.isPaymentMethodBanned(paymentAccount.getPaymentMethod()), diff --git a/core/src/main/java/haveno/core/offer/OpenOffer.java b/core/src/main/java/haveno/core/offer/OpenOffer.java index b0df3e0e..1f177959 100644 --- a/core/src/main/java/haveno/core/offer/OpenOffer.java +++ b/core/src/main/java/haveno/core/offer/OpenOffer.java @@ -96,6 +96,9 @@ public final class OpenOffer implements Tradable { @Getter private String reserveTxKey; @Getter + @Setter + private String challenge; + @Getter private final long triggerPrice; @Getter @Setter @@ -107,7 +110,6 @@ public final class OpenOffer implements Tradable { @Getter @Setter transient int numProcessingAttempts = 0; - public OpenOffer(Offer offer) { this(offer, 0, false); } @@ -120,6 +122,7 @@ public final class OpenOffer implements Tradable { this.offer = offer; this.triggerPrice = triggerPrice; this.reserveExactAmount = reserveExactAmount; + this.challenge = offer.getChallenge(); state = State.PENDING; } @@ -137,6 +140,7 @@ public final class OpenOffer implements Tradable { this.reserveTxHash = openOffer.reserveTxHash; this.reserveTxHex = openOffer.reserveTxHex; this.reserveTxKey = openOffer.reserveTxKey; + this.challenge = openOffer.challenge; } /////////////////////////////////////////////////////////////////////////////////////////// @@ -153,7 +157,8 @@ public final class OpenOffer implements Tradable { long splitOutputTxFee, @Nullable String reserveTxHash, @Nullable String reserveTxHex, - @Nullable String reserveTxKey) { + @Nullable String reserveTxKey, + @Nullable String challenge) { this.offer = offer; this.state = state; this.triggerPrice = triggerPrice; @@ -164,6 +169,7 @@ public final class OpenOffer implements Tradable { this.reserveTxHash = reserveTxHash; this.reserveTxHex = reserveTxHex; this.reserveTxKey = reserveTxKey; + this.challenge = challenge; // reset reserved state to available if (this.state == State.RESERVED) setState(State.AVAILABLE); @@ -184,6 +190,7 @@ public final class OpenOffer implements Tradable { Optional.ofNullable(reserveTxHash).ifPresent(e -> builder.setReserveTxHash(reserveTxHash)); Optional.ofNullable(reserveTxHex).ifPresent(e -> builder.setReserveTxHex(reserveTxHex)); Optional.ofNullable(reserveTxKey).ifPresent(e -> builder.setReserveTxKey(reserveTxKey)); + Optional.ofNullable(challenge).ifPresent(e -> builder.setChallenge(challenge)); return protobuf.Tradable.newBuilder().setOpenOffer(builder).build(); } @@ -199,7 +206,8 @@ public final class OpenOffer implements Tradable { proto.getSplitOutputTxFee(), ProtoUtil.stringOrNullFromProto(proto.getReserveTxHash()), ProtoUtil.stringOrNullFromProto(proto.getReserveTxHex()), - ProtoUtil.stringOrNullFromProto(proto.getReserveTxKey())); + ProtoUtil.stringOrNullFromProto(proto.getReserveTxKey()), + ProtoUtil.stringOrNullFromProto(proto.getChallenge())); return openOffer; } diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index c8390a5a..86f45566 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -79,6 +79,7 @@ import haveno.core.util.JsonUtil; import haveno.core.util.Validator; import haveno.core.xmr.model.XmrAddressEntry; import haveno.core.xmr.wallet.BtcWalletService; +import haveno.core.xmr.wallet.Restrictions; import haveno.core.xmr.wallet.XmrKeyImageListener; import haveno.core.xmr.wallet.XmrKeyImagePoller; import haveno.core.xmr.wallet.TradeWalletService; @@ -1307,7 +1308,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe NodeAddress thisAddress = p2PService.getNetworkNode().getNodeAddress(); if (thisArbitrator == null || !thisArbitrator.getNodeAddress().equals(thisAddress)) { errorMessage = "Cannot sign offer because we are not a registered arbitrator"; - log.info(errorMessage); + log.warn(errorMessage); sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); return; } @@ -1315,47 +1316,109 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe // verify arbitrator is signer of offer payload if (!thisAddress.equals(request.getOfferPayload().getArbitratorSigner())) { errorMessage = "Cannot sign offer because offer payload is for a different arbitrator"; - log.info(errorMessage); + log.warn(errorMessage); sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); return; } - // verify maker's trade fee + // private offers must have challenge hash Offer offer = new Offer(request.getOfferPayload()); - if (offer.getMakerFeePct() != HavenoUtils.MAKER_FEE_PCT) { - errorMessage = "Wrong maker fee for offer " + request.offerId; - log.info(errorMessage); + if (offer.isPrivateOffer() && (offer.getChallengeHash() == null || offer.getChallengeHash().length() == 0)) { + errorMessage = "Private offer must have challenge hash for offer " + request.offerId; + log.warn(errorMessage); sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); return; } - // verify taker's trade fee - if (offer.getTakerFeePct() != HavenoUtils.TAKER_FEE_PCT) { - errorMessage = "Wrong taker fee for offer " + request.offerId; - log.info(errorMessage); - sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); - return; + // verify maker and taker fees + boolean hasBuyerAsTakerWithoutDeposit = offer.getDirection() == OfferDirection.SELL && offer.isPrivateOffer() && offer.getChallengeHash() != null && offer.getChallengeHash().length() > 0 && offer.getTakerFeePct() == 0; + if (hasBuyerAsTakerWithoutDeposit) { + + // verify maker's trade fee + if (offer.getMakerFeePct() != HavenoUtils.MAKER_FEE_FOR_TAKER_WITHOUT_DEPOSIT_PCT) { + errorMessage = "Wrong maker fee for offer " + request.offerId; + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } + + // verify taker's trade fee + if (offer.getTakerFeePct() != 0) { + errorMessage = "Wrong taker fee for offer " + request.offerId + ". Expected 0 but got " + offer.getTakerFeePct(); + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } + + // verify maker security deposit + if (offer.getSellerSecurityDepositPct() != Restrictions.MIN_SECURITY_DEPOSIT_PCT) { + errorMessage = "Wrong seller security deposit for offer " + request.offerId + ". Expected " + Restrictions.MIN_SECURITY_DEPOSIT_PCT + " but got " + offer.getSellerSecurityDepositPct(); + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } + + // verify taker's security deposit + if (offer.getBuyerSecurityDepositPct() != 0) { + errorMessage = "Wrong buyer security deposit for offer " + request.offerId + ". Expected 0 but got " + offer.getBuyerSecurityDepositPct(); + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } + } else { + + // verify maker's trade fee + if (offer.getMakerFeePct() != HavenoUtils.MAKER_FEE_PCT) { + errorMessage = "Wrong maker fee for offer " + request.offerId; + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } + + // verify taker's trade fee + if (offer.getTakerFeePct() != HavenoUtils.TAKER_FEE_PCT) { + errorMessage = "Wrong taker fee for offer " + request.offerId + ". Expected " + HavenoUtils.TAKER_FEE_PCT + " but got " + offer.getTakerFeePct(); + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } + + // verify seller's security deposit + if (offer.getSellerSecurityDepositPct() < Restrictions.MIN_SECURITY_DEPOSIT_PCT) { + errorMessage = "Insufficient seller security deposit for offer " + request.offerId + ". Expected at least " + Restrictions.MIN_SECURITY_DEPOSIT_PCT + " but got " + offer.getSellerSecurityDepositPct(); + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } + + // verify buyer's security deposit + if (offer.getBuyerSecurityDepositPct() < Restrictions.MIN_SECURITY_DEPOSIT_PCT) { + errorMessage = "Insufficient buyer security deposit for offer " + request.offerId + ". Expected at least " + Restrictions.MIN_SECURITY_DEPOSIT_PCT + " but got " + offer.getBuyerSecurityDepositPct(); + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } + + // security deposits must be equal + if (offer.getBuyerSecurityDepositPct() != offer.getSellerSecurityDepositPct()) { + errorMessage = "Buyer and seller security deposits are not equal for offer " + request.offerId + ": " + offer.getSellerSecurityDepositPct() + " vs " + offer.getBuyerSecurityDepositPct(); + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } } // verify penalty fee if (offer.getPenaltyFeePct() != HavenoUtils.PENALTY_FEE_PCT) { errorMessage = "Wrong penalty fee for offer " + request.offerId; - log.info(errorMessage); - sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); - return; - } - - // verify security deposits are equal - if (offer.getBuyerSecurityDepositPct() != offer.getSellerSecurityDepositPct()) { - errorMessage = "Buyer and seller security deposits are not equal for offer " + request.offerId; - log.info(errorMessage); + log.warn(errorMessage); sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); return; } // verify maker's reserve tx (double spend, trade fee, trade amount, mining fee) BigInteger penaltyFee = HavenoUtils.multiply(offer.getAmount(), HavenoUtils.PENALTY_FEE_PCT); - BigInteger maxTradeFee = HavenoUtils.multiply(offer.getAmount(), HavenoUtils.MAKER_FEE_PCT); + BigInteger maxTradeFee = HavenoUtils.multiply(offer.getAmount(), hasBuyerAsTakerWithoutDeposit ? HavenoUtils.MAKER_FEE_FOR_TAKER_WITHOUT_DEPOSIT_PCT : HavenoUtils.MAKER_FEE_PCT); BigInteger sendTradeAmount = offer.getDirection() == OfferDirection.BUY ? BigInteger.ZERO : offer.getAmount(); BigInteger securityDeposit = offer.getDirection() == OfferDirection.BUY ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit(); MoneroTx verifiedTx = xmrWalletService.verifyReserveTx( @@ -1710,7 +1773,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe originalOfferPayload.getLowerClosePrice(), originalOfferPayload.getUpperClosePrice(), originalOfferPayload.isPrivateOffer(), - originalOfferPayload.getHashOfChallenge(), + originalOfferPayload.getChallengeHash(), updatedExtraDataMap, protocolVersion, originalOfferPayload.getArbitratorSigner(), diff --git a/core/src/main/java/haveno/core/offer/availability/tasks/SendOfferAvailabilityRequest.java b/core/src/main/java/haveno/core/offer/availability/tasks/SendOfferAvailabilityRequest.java index 493ded67..d6080e61 100644 --- a/core/src/main/java/haveno/core/offer/availability/tasks/SendOfferAvailabilityRequest.java +++ b/core/src/main/java/haveno/core/offer/availability/tasks/SendOfferAvailabilityRequest.java @@ -88,7 +88,8 @@ public class SendOfferAvailabilityRequest extends Task { null, // reserve tx not sent from taker to maker null, null, - payoutAddress); + payoutAddress, + null); // challenge is required when offer taken // save trade request to later send to arbitrator model.setTradeRequest(tradeRequest); diff --git a/core/src/main/java/haveno/core/offer/placeoffer/tasks/ValidateOffer.java b/core/src/main/java/haveno/core/offer/placeoffer/tasks/ValidateOffer.java index dfb1b2fa..3b1a01be 100644 --- a/core/src/main/java/haveno/core/offer/placeoffer/tasks/ValidateOffer.java +++ b/core/src/main/java/haveno/core/offer/placeoffer/tasks/ValidateOffer.java @@ -21,6 +21,7 @@ import haveno.common.taskrunner.Task; import haveno.common.taskrunner.TaskRunner; import haveno.core.account.witness.AccountAgeWitnessService; import haveno.core.offer.Offer; +import haveno.core.offer.OfferDirection; import haveno.core.offer.placeoffer.PlaceOfferModel; import haveno.core.trade.HavenoUtils; import haveno.core.trade.messages.TradeMessage; @@ -63,8 +64,21 @@ public class ValidateOffer extends Task { checkBINotNullOrZero(offer.getMaxTradeLimit(), "MaxTradeLimit"); if (offer.getMakerFeePct() < 0) throw new IllegalArgumentException("Maker fee must be >= 0% but was " + offer.getMakerFeePct()); if (offer.getTakerFeePct() < 0) throw new IllegalArgumentException("Taker fee must be >= 0% but was " + offer.getTakerFeePct()); - if (offer.getBuyerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Buyer security deposit percent must be positive but was " + offer.getBuyerSecurityDepositPct()); - if (offer.getSellerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Seller security deposit percent must be positive but was " + offer.getSellerSecurityDepositPct()); + offer.isPrivateOffer(); + if (offer.isPrivateOffer()) { + boolean isBuyerMaker = offer.getDirection() == OfferDirection.BUY; + if (isBuyerMaker) { + if (offer.getBuyerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Buyer security deposit percent must be positive but was " + offer.getBuyerSecurityDepositPct()); + if (offer.getSellerSecurityDepositPct() < 0) throw new IllegalArgumentException("Seller security deposit percent must be >= 0% but was " + offer.getSellerSecurityDepositPct()); + } else { + if (offer.getBuyerSecurityDepositPct() < 0) throw new IllegalArgumentException("Buyer security deposit percent must be >= 0% but was " + offer.getBuyerSecurityDepositPct()); + if (offer.getSellerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Seller security deposit percent must be positive but was " + offer.getSellerSecurityDepositPct()); + } + } else { + if (offer.getBuyerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Buyer security deposit percent must be positive but was " + offer.getBuyerSecurityDepositPct()); + if (offer.getSellerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Seller security deposit percent must be positive but was " + offer.getSellerSecurityDepositPct()); + } + // We remove those checks to be more flexible with future changes. /*checkArgument(offer.getMakerFee().value >= FeeService.getMinMakerFee(offer.isCurrencyForMakerFeeBtc()).value, @@ -82,9 +96,9 @@ public class ValidateOffer extends Task { /*checkArgument(offer.getMinAmount().compareTo(ProposalConsensus.getMinTradeAmount()) >= 0, "MinAmount is less than " + ProposalConsensus.getMinTradeAmount().toFriendlyString());*/ - long maxAmount = accountAgeWitnessService.getMyTradeLimit(user.getPaymentAccount(offer.getMakerPaymentAccountId()), offer.getCurrencyCode(), offer.getDirection()); + long maxAmount = accountAgeWitnessService.getMyTradeLimit(user.getPaymentAccount(offer.getMakerPaymentAccountId()), offer.getCurrencyCode(), offer.getDirection(), offer.hasBuyerAsTakerWithoutDeposit()); checkArgument(offer.getAmount().longValueExact() <= maxAmount, - "Amount is larger than " + HavenoUtils.atomicUnitsToXmr(offer.getPaymentMethod().getMaxTradeLimit(offer.getCurrencyCode())) + " XMR"); + "Amount is larger than " + HavenoUtils.atomicUnitsToXmr(maxAmount) + " XMR"); checkArgument(offer.getAmount().compareTo(offer.getMinAmount()) >= 0, "MinAmount is larger than Amount"); checkNotNull(offer.getPrice(), "Price is null"); diff --git a/core/src/main/java/haveno/core/offer/takeoffer/TakeOfferModel.java b/core/src/main/java/haveno/core/offer/takeoffer/TakeOfferModel.java index 85d0b99b..49c6da12 100644 --- a/core/src/main/java/haveno/core/offer/takeoffer/TakeOfferModel.java +++ b/core/src/main/java/haveno/core/offer/takeoffer/TakeOfferModel.java @@ -148,7 +148,8 @@ public class TakeOfferModel implements Model { private long getMaxTradeLimit() { return accountAgeWitnessService.getMyTradeLimit(paymentAccount, offer.getCurrencyCode(), - offer.getMirroredDirection()); + offer.getMirroredDirection(), + offer.hasBuyerAsTakerWithoutDeposit()); } @NotNull diff --git a/core/src/main/java/haveno/core/payment/PaymentAccountUtil.java b/core/src/main/java/haveno/core/payment/PaymentAccountUtil.java index 4d7b4ad0..11575aeb 100644 --- a/core/src/main/java/haveno/core/payment/PaymentAccountUtil.java +++ b/core/src/main/java/haveno/core/payment/PaymentAccountUtil.java @@ -124,7 +124,7 @@ public class PaymentAccountUtil { AccountAgeWitnessService accountAgeWitnessService) { boolean hasChargebackRisk = hasChargebackRisk(offer.getPaymentMethod(), offer.getCurrencyCode()); boolean hasValidAccountAgeWitness = accountAgeWitnessService.getMyTradeLimit(paymentAccount, - offer.getCurrencyCode(), offer.getMirroredDirection()) >= offer.getMinAmount().longValueExact(); + offer.getCurrencyCode(), offer.getMirroredDirection(), offer.hasBuyerAsTakerWithoutDeposit()) >= offer.getMinAmount().longValueExact(); return !hasChargebackRisk || hasValidAccountAgeWitness; } diff --git a/core/src/main/java/haveno/core/payment/TradeLimits.java b/core/src/main/java/haveno/core/payment/TradeLimits.java index 2bfdcdbc..92ac29b0 100644 --- a/core/src/main/java/haveno/core/payment/TradeLimits.java +++ b/core/src/main/java/haveno/core/payment/TradeLimits.java @@ -31,6 +31,8 @@ import lombok.extern.slf4j.Slf4j; @Singleton public class TradeLimits { private static final BigInteger MAX_TRADE_LIMIT = HavenoUtils.xmrToAtomicUnits(528); // max trade limit for lowest risk payment method. Others will get derived from that. + private static final BigInteger MAX_TRADE_LIMIT_WITHOUT_BUYER_AS_TAKER_DEPOSIT = HavenoUtils.xmrToAtomicUnits(1); // max trade limit without deposit from buyer + @Nullable @Getter private static TradeLimits INSTANCE; @@ -57,6 +59,15 @@ public class TradeLimits { return MAX_TRADE_LIMIT; } + /** + * The maximum trade limit without a buyer deposit. + * + * @return the maximum trade limit for a buyer without a deposit + */ + public BigInteger getMaxTradeLimitBuyerAsTakerWithoutDeposit() { + return MAX_TRADE_LIMIT_WITHOUT_BUYER_AS_TAKER_DEPOSIT; + } + // We possibly rounded value for the first month gets multiplied by 4 to get the trade limit after the account // age witness is not considered anymore (> 2 months). diff --git a/core/src/main/java/haveno/core/payment/validation/SecurityDepositValidator.java b/core/src/main/java/haveno/core/payment/validation/SecurityDepositValidator.java index 7bf873ce..4545a4e2 100644 --- a/core/src/main/java/haveno/core/payment/validation/SecurityDepositValidator.java +++ b/core/src/main/java/haveno/core/payment/validation/SecurityDepositValidator.java @@ -59,7 +59,7 @@ public class SecurityDepositValidator extends NumberValidator { private ValidationResult validateIfNotTooLowPercentageValue(String input) { try { double percentage = ParsingUtils.parsePercentStringToDouble(input); - double minPercentage = Restrictions.getMinBuyerSecurityDepositAsPercent(); + double minPercentage = Restrictions.getMinSecurityDepositAsPercent(); if (percentage < minPercentage) return new ValidationResult(false, Res.get("validation.inputTooSmall", FormattingUtils.formatToPercentWithSymbol(minPercentage))); @@ -73,7 +73,7 @@ public class SecurityDepositValidator extends NumberValidator { private ValidationResult validateIfNotTooHighPercentageValue(String input) { try { double percentage = ParsingUtils.parsePercentStringToDouble(input); - double maxPercentage = Restrictions.getMaxBuyerSecurityDepositAsPercent(); + double maxPercentage = Restrictions.getMaxSecurityDepositAsPercent(); if (percentage > maxPercentage) return new ValidationResult(false, Res.get("validation.inputTooLarge", FormattingUtils.formatToPercentWithSymbol(maxPercentage))); diff --git a/core/src/main/java/haveno/core/trade/ArbitratorTrade.java b/core/src/main/java/haveno/core/trade/ArbitratorTrade.java index 93f03dec..ea179a65 100644 --- a/core/src/main/java/haveno/core/trade/ArbitratorTrade.java +++ b/core/src/main/java/haveno/core/trade/ArbitratorTrade.java @@ -28,6 +28,8 @@ import lombok.extern.slf4j.Slf4j; import java.math.BigInteger; import java.util.UUID; +import javax.annotation.Nullable; + /** * Trade in the context of an arbitrator. */ @@ -42,8 +44,9 @@ public class ArbitratorTrade extends Trade { String uid, NodeAddress makerNodeAddress, NodeAddress takerNodeAddress, - NodeAddress arbitratorNodeAddress) { - super(offer, tradeAmount, tradePrice, xmrWalletService, processModel, uid, makerNodeAddress, takerNodeAddress, arbitratorNodeAddress); + NodeAddress arbitratorNodeAddress, + @Nullable String challenge) { + super(offer, tradeAmount, tradePrice, xmrWalletService, processModel, uid, makerNodeAddress, takerNodeAddress, arbitratorNodeAddress, challenge); } @Override @@ -81,7 +84,8 @@ public class ArbitratorTrade extends Trade { uid, proto.getProcessModel().getMaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getMaker().getNodeAddress()) : null, proto.getProcessModel().getTaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getTaker().getNodeAddress()) : null, - proto.getProcessModel().getArbitrator().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getArbitrator().getNodeAddress()) : null), + proto.getProcessModel().getArbitrator().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getArbitrator().getNodeAddress()) : null, + ProtoUtil.stringOrNullFromProto(proto.getChallenge())), proto, coreProtoResolver); } diff --git a/core/src/main/java/haveno/core/trade/BuyerAsMakerTrade.java b/core/src/main/java/haveno/core/trade/BuyerAsMakerTrade.java index 99fad94e..ca38f1ca 100644 --- a/core/src/main/java/haveno/core/trade/BuyerAsMakerTrade.java +++ b/core/src/main/java/haveno/core/trade/BuyerAsMakerTrade.java @@ -28,6 +28,8 @@ import lombok.extern.slf4j.Slf4j; import java.math.BigInteger; import java.util.UUID; +import javax.annotation.Nullable; + @Slf4j public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade { @@ -43,7 +45,8 @@ public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade { String uid, NodeAddress makerNodeAddress, NodeAddress takerNodeAddress, - NodeAddress arbitratorNodeAddress) { + NodeAddress arbitratorNodeAddress, + @Nullable String challenge) { super(offer, tradeAmount, tradePrice, @@ -52,7 +55,8 @@ public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade { uid, makerNodeAddress, takerNodeAddress, - arbitratorNodeAddress); + arbitratorNodeAddress, + challenge); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -85,7 +89,8 @@ public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade { uid, proto.getProcessModel().getMaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getMaker().getNodeAddress()) : null, proto.getProcessModel().getTaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getTaker().getNodeAddress()) : null, - proto.getProcessModel().getArbitrator().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getArbitrator().getNodeAddress()) : null); + proto.getProcessModel().getArbitrator().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getArbitrator().getNodeAddress()) : null, + ProtoUtil.stringOrNullFromProto(proto.getChallenge())); trade.setPrice(proto.getPrice()); diff --git a/core/src/main/java/haveno/core/trade/BuyerAsTakerTrade.java b/core/src/main/java/haveno/core/trade/BuyerAsTakerTrade.java index 1cb7b20d..2b950191 100644 --- a/core/src/main/java/haveno/core/trade/BuyerAsTakerTrade.java +++ b/core/src/main/java/haveno/core/trade/BuyerAsTakerTrade.java @@ -44,7 +44,8 @@ public final class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade { String uid, @Nullable NodeAddress makerNodeAddress, @Nullable NodeAddress takerNodeAddress, - @Nullable NodeAddress arbitratorNodeAddress) { + @Nullable NodeAddress arbitratorNodeAddress, + @Nullable String challenge) { super(offer, tradeAmount, tradePrice, @@ -53,7 +54,8 @@ public final class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade { uid, makerNodeAddress, takerNodeAddress, - arbitratorNodeAddress); + arbitratorNodeAddress, + challenge); } @@ -87,7 +89,8 @@ public final class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade { uid, proto.getProcessModel().getMaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getMaker().getNodeAddress()) : null, proto.getProcessModel().getTaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getTaker().getNodeAddress()) : null, - proto.getProcessModel().getArbitrator().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getArbitrator().getNodeAddress()) : null), + proto.getProcessModel().getArbitrator().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getArbitrator().getNodeAddress()) : null, + ProtoUtil.stringOrNullFromProto(proto.getChallenge())), proto, coreProtoResolver); } diff --git a/core/src/main/java/haveno/core/trade/BuyerTrade.java b/core/src/main/java/haveno/core/trade/BuyerTrade.java index dbf73db1..9de8f1e0 100644 --- a/core/src/main/java/haveno/core/trade/BuyerTrade.java +++ b/core/src/main/java/haveno/core/trade/BuyerTrade.java @@ -38,7 +38,8 @@ public abstract class BuyerTrade extends Trade { String uid, @Nullable NodeAddress takerNodeAddress, @Nullable NodeAddress makerNodeAddress, - @Nullable NodeAddress arbitratorNodeAddress) { + @Nullable NodeAddress arbitratorNodeAddress, + @Nullable String challenge) { super(offer, tradeAmount, tradePrice, @@ -47,7 +48,8 @@ public abstract class BuyerTrade extends Trade { uid, takerNodeAddress, makerNodeAddress, - arbitratorNodeAddress); + arbitratorNodeAddress, + challenge); } @Override diff --git a/core/src/main/java/haveno/core/trade/Contract.java b/core/src/main/java/haveno/core/trade/Contract.java index b0950c55..9a88eaff 100644 --- a/core/src/main/java/haveno/core/trade/Contract.java +++ b/core/src/main/java/haveno/core/trade/Contract.java @@ -36,6 +36,7 @@ package haveno.core.trade; import com.google.protobuf.ByteString; import haveno.common.crypto.PubKeyRing; +import haveno.common.proto.ProtoUtil; import haveno.common.proto.network.NetworkPayload; import haveno.common.util.JsonExclude; import haveno.common.util.Utilities; @@ -53,6 +54,7 @@ import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; import java.math.BigInteger; +import java.util.Optional; import static com.google.common.base.Preconditions.checkArgument; @@ -79,6 +81,7 @@ public final class Contract implements NetworkPayload { private final String makerPayoutAddressString; private final String takerPayoutAddressString; private final String makerDepositTxHash; + @Nullable private final String takerDepositTxHash; public Contract(OfferPayload offerPayload, @@ -99,7 +102,7 @@ public final class Contract implements NetworkPayload { String makerPayoutAddressString, String takerPayoutAddressString, String makerDepositTxHash, - String takerDepositTxHash) { + @Nullable String takerDepositTxHash) { this.offerPayload = offerPayload; this.tradeAmount = tradeAmount; this.tradePrice = tradePrice; @@ -134,6 +137,31 @@ public final class Contract implements NetworkPayload { // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// + @Override + public protobuf.Contract toProtoMessage() { + protobuf.Contract.Builder builder = protobuf.Contract.newBuilder() + .setOfferPayload(offerPayload.toProtoMessage().getOfferPayload()) + .setTradeAmount(tradeAmount) + .setTradePrice(tradePrice) + .setBuyerNodeAddress(buyerNodeAddress.toProtoMessage()) + .setSellerNodeAddress(sellerNodeAddress.toProtoMessage()) + .setArbitratorNodeAddress(arbitratorNodeAddress.toProtoMessage()) + .setIsBuyerMakerAndSellerTaker(isBuyerMakerAndSellerTaker) + .setMakerAccountId(makerAccountId) + .setTakerAccountId(takerAccountId) + .setMakerPaymentMethodId(makerPaymentMethodId) + .setTakerPaymentMethodId(takerPaymentMethodId) + .setMakerPaymentAccountPayloadHash(ByteString.copyFrom(makerPaymentAccountPayloadHash)) + .setTakerPaymentAccountPayloadHash(ByteString.copyFrom(takerPaymentAccountPayloadHash)) + .setMakerPubKeyRing(makerPubKeyRing.toProtoMessage()) + .setTakerPubKeyRing(takerPubKeyRing.toProtoMessage()) + .setMakerPayoutAddressString(makerPayoutAddressString) + .setTakerPayoutAddressString(takerPayoutAddressString) + .setMakerDepositTxHash(makerDepositTxHash); + Optional.ofNullable(takerDepositTxHash).ifPresent(builder::setTakerDepositTxHash); + return builder.build(); + } + public static Contract fromProto(protobuf.Contract proto, CoreProtoResolver coreProtoResolver) { return new Contract(OfferPayload.fromProto(proto.getOfferPayload()), proto.getTradeAmount(), @@ -153,32 +181,7 @@ public final class Contract implements NetworkPayload { proto.getMakerPayoutAddressString(), proto.getTakerPayoutAddressString(), proto.getMakerDepositTxHash(), - proto.getTakerDepositTxHash()); - } - - @Override - public protobuf.Contract toProtoMessage() { - return protobuf.Contract.newBuilder() - .setOfferPayload(offerPayload.toProtoMessage().getOfferPayload()) - .setTradeAmount(tradeAmount) - .setTradePrice(tradePrice) - .setBuyerNodeAddress(buyerNodeAddress.toProtoMessage()) - .setSellerNodeAddress(sellerNodeAddress.toProtoMessage()) - .setArbitratorNodeAddress(arbitratorNodeAddress.toProtoMessage()) - .setIsBuyerMakerAndSellerTaker(isBuyerMakerAndSellerTaker) - .setMakerAccountId(makerAccountId) - .setTakerAccountId(takerAccountId) - .setMakerPaymentMethodId(makerPaymentMethodId) - .setTakerPaymentMethodId(takerPaymentMethodId) - .setMakerPaymentAccountPayloadHash(ByteString.copyFrom(makerPaymentAccountPayloadHash)) - .setTakerPaymentAccountPayloadHash(ByteString.copyFrom(takerPaymentAccountPayloadHash)) - .setMakerPubKeyRing(makerPubKeyRing.toProtoMessage()) - .setTakerPubKeyRing(takerPubKeyRing.toProtoMessage()) - .setMakerPayoutAddressString(makerPayoutAddressString) - .setTakerPayoutAddressString(takerPayoutAddressString) - .setMakerDepositTxHash(makerDepositTxHash) - .setTakerDepositTxHash(takerDepositTxHash) - .build(); + ProtoUtil.stringOrNullFromProto(proto.getTakerDepositTxHash())); } diff --git a/core/src/main/java/haveno/core/trade/HavenoUtils.java b/core/src/main/java/haveno/core/trade/HavenoUtils.java index 06133e16..fcd5c556 100644 --- a/core/src/main/java/haveno/core/trade/HavenoUtils.java +++ b/core/src/main/java/haveno/core/trade/HavenoUtils.java @@ -28,6 +28,7 @@ import haveno.common.crypto.KeyRing; import haveno.common.crypto.PubKeyRing; import haveno.common.crypto.Sig; import haveno.common.file.FileUtil; +import haveno.common.util.Base64; import haveno.common.util.Utilities; import haveno.core.api.CoreNotificationService; import haveno.core.api.XmrConnectionService; @@ -48,7 +49,10 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.net.InetAddress; import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.security.PrivateKey; +import java.security.SecureRandom; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.SimpleDateFormat; @@ -87,13 +91,15 @@ public class HavenoUtils { // configure fees public static final boolean ARBITRATOR_ASSIGNS_TRADE_FEE_ADDRESS = true; + public static final double PENALTY_FEE_PCT = 0.02; // 2% public static final double MAKER_FEE_PCT = 0.0015; // 0.15% public static final double TAKER_FEE_PCT = 0.0075; // 0.75% - public static final double PENALTY_FEE_PCT = 0.02; // 2% + public static final double MAKER_FEE_FOR_TAKER_WITHOUT_DEPOSIT_PCT = MAKER_FEE_PCT + TAKER_FEE_PCT; // customize maker's fee when no deposit or fee from taker // other configuration public static final long LOG_POLL_ERROR_PERIOD_MS = 1000 * 60 * 4; // log poll errors up to once every 4 minutes public static final long LOG_DAEMON_NOT_SYNCED_WARN_PERIOD_MS = 1000 * 30; // log warnings when daemon not synced once every 30s + public static final int PRIVATE_OFFER_PASSPHRASE_NUM_WORDS = 8; // number of words in a private offer passphrase // synchronize requests to the daemon private static boolean SYNC_DAEMON_REQUESTS = false; // sync long requests to daemon (e.g. refresh, update pool) // TODO: performance suffers by syncing daemon requests, but otherwise we sometimes get sporadic errors? @@ -286,6 +292,41 @@ public class HavenoUtils { // ------------------------ SIGNING AND VERIFYING ------------------------- + public static String generateChallenge() { + try { + + // load bip39 words + String fileName = "bip39_english.txt"; + File bip39File = new File(havenoSetup.getConfig().appDataDir, fileName); + if (!bip39File.exists()) FileUtil.resourceToFile(fileName, bip39File); + List bip39Words = Files.readAllLines(bip39File.toPath(), StandardCharsets.UTF_8); + + // select words randomly + List passphraseWords = new ArrayList(); + SecureRandom secureRandom = new SecureRandom(); + for (int i = 0; i < PRIVATE_OFFER_PASSPHRASE_NUM_WORDS; i++) { + passphraseWords.add(bip39Words.get(secureRandom.nextInt(bip39Words.size()))); + } + return String.join(" ", passphraseWords); + } catch (Exception e) { + throw new IllegalStateException("Failed to generate challenge", e); + } + } + + public static String getChallengeHash(String challenge) { + if (challenge == null) return null; + + // tokenize passphrase + String[] words = challenge.toLowerCase().split(" "); + + // collect first 4 letters of each word, which are unique in bip39 + List prefixes = new ArrayList(); + for (String word : words) prefixes.add(word.substring(0, Math.min(word.length(), 4))); + + // hash the result + return Base64.encode(Hash.getSha256Hash(String.join(" ", prefixes).getBytes())); + } + public static byte[] sign(KeyRing keyRing, String message) { return sign(keyRing.getSignatureKeyPair().getPrivate(), message); } diff --git a/core/src/main/java/haveno/core/trade/SellerAsMakerTrade.java b/core/src/main/java/haveno/core/trade/SellerAsMakerTrade.java index c31c3253..07f4a161 100644 --- a/core/src/main/java/haveno/core/trade/SellerAsMakerTrade.java +++ b/core/src/main/java/haveno/core/trade/SellerAsMakerTrade.java @@ -44,7 +44,8 @@ public final class SellerAsMakerTrade extends SellerTrade implements MakerTrade String uid, @Nullable NodeAddress makerNodeAddress, @Nullable NodeAddress takerNodeAddress, - @Nullable NodeAddress arbitratorNodeAddress) { + @Nullable NodeAddress arbitratorNodeAddress, + @Nullable String challenge) { super(offer, tradeAmount, tradePrice, @@ -53,7 +54,8 @@ public final class SellerAsMakerTrade extends SellerTrade implements MakerTrade uid, makerNodeAddress, takerNodeAddress, - arbitratorNodeAddress); + arbitratorNodeAddress, + challenge); } @@ -87,7 +89,8 @@ public final class SellerAsMakerTrade extends SellerTrade implements MakerTrade uid, proto.getProcessModel().getMaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getMaker().getNodeAddress()) : null, proto.getProcessModel().getTaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getTaker().getNodeAddress()) : null, - proto.getProcessModel().getArbitrator().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getArbitrator().getNodeAddress()) : null); + proto.getProcessModel().getArbitrator().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getArbitrator().getNodeAddress()) : null, + ProtoUtil.stringOrNullFromProto(proto.getChallenge())); trade.setPrice(proto.getPrice()); diff --git a/core/src/main/java/haveno/core/trade/SellerAsTakerTrade.java b/core/src/main/java/haveno/core/trade/SellerAsTakerTrade.java index afca9346..4b5e594a 100644 --- a/core/src/main/java/haveno/core/trade/SellerAsTakerTrade.java +++ b/core/src/main/java/haveno/core/trade/SellerAsTakerTrade.java @@ -44,7 +44,8 @@ public final class SellerAsTakerTrade extends SellerTrade implements TakerTrade String uid, @Nullable NodeAddress makerNodeAddress, @Nullable NodeAddress takerNodeAddress, - @Nullable NodeAddress arbitratorNodeAddress) { + @Nullable NodeAddress arbitratorNodeAddress, + @Nullable String challenge) { super(offer, tradeAmount, tradePrice, @@ -53,7 +54,8 @@ public final class SellerAsTakerTrade extends SellerTrade implements TakerTrade uid, makerNodeAddress, takerNodeAddress, - arbitratorNodeAddress); + arbitratorNodeAddress, + challenge); } @@ -87,7 +89,8 @@ public final class SellerAsTakerTrade extends SellerTrade implements TakerTrade uid, proto.getProcessModel().getMaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getMaker().getNodeAddress()) : null, proto.getProcessModel().getTaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getTaker().getNodeAddress()) : null, - proto.getProcessModel().getArbitrator().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getArbitrator().getNodeAddress()) : null), + proto.getProcessModel().getArbitrator().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getArbitrator().getNodeAddress()) : null, + ProtoUtil.stringOrNullFromProto(proto.getChallenge())), proto, coreProtoResolver); } diff --git a/core/src/main/java/haveno/core/trade/SellerTrade.java b/core/src/main/java/haveno/core/trade/SellerTrade.java index 457ea10a..fae3cce7 100644 --- a/core/src/main/java/haveno/core/trade/SellerTrade.java +++ b/core/src/main/java/haveno/core/trade/SellerTrade.java @@ -36,7 +36,8 @@ public abstract class SellerTrade extends Trade { String uid, @Nullable NodeAddress makerNodeAddress, @Nullable NodeAddress takerNodeAddress, - @Nullable NodeAddress arbitratorNodeAddress) { + @Nullable NodeAddress arbitratorNodeAddress, + @Nullable String challenge) { super(offer, tradeAmount, tradePrice, @@ -45,7 +46,8 @@ public abstract class SellerTrade extends Trade { uid, makerNodeAddress, takerNodeAddress, - arbitratorNodeAddress); + arbitratorNodeAddress, + challenge); } @Override diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index b5403511..95b067b2 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -486,6 +486,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { private IdlePayoutSyncer idlePayoutSyncer; @Getter private boolean isCompleted; + @Getter + private final String challenge; /////////////////////////////////////////////////////////////////////////////////////////// // Constructors @@ -500,7 +502,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { String uid, @Nullable NodeAddress makerNodeAddress, @Nullable NodeAddress takerNodeAddress, - @Nullable NodeAddress arbitratorNodeAddress) { + @Nullable NodeAddress arbitratorNodeAddress, + @Nullable String challenge) { super(); this.offer = offer; this.amount = tradeAmount.longValueExact(); @@ -511,6 +514,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { this.uid = uid; this.takeOfferDate = new Date().getTime(); this.tradeListeners = new ArrayList(); + this.challenge = challenge; getMaker().setNodeAddress(makerNodeAddress); getTaker().setNodeAddress(takerNodeAddress); @@ -534,7 +538,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { String uid, @Nullable NodeAddress makerNodeAddress, @Nullable NodeAddress takerNodeAddress, - @Nullable NodeAddress arbitratorNodeAddress) { + @Nullable NodeAddress arbitratorNodeAddress, + @Nullable String challenge) { this(offer, tradeAmount, @@ -544,7 +549,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { uid, makerNodeAddress, takerNodeAddress, - arbitratorNodeAddress); + arbitratorNodeAddress, + challenge); } // TODO: remove these constructors @@ -559,7 +565,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { NodeAddress arbitratorNodeAddress, XmrWalletService xmrWalletService, ProcessModel processModel, - String uid) { + String uid, + @Nullable String challenge) { this(offer, tradeAmount, @@ -569,7 +576,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { uid, makerNodeAddress, takerNodeAddress, - arbitratorNodeAddress); + arbitratorNodeAddress, + challenge); setAmount(tradeAmount); } @@ -1233,7 +1241,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { Preconditions.checkNotNull(sellerPayoutAddress, "Seller payout address must not be null"); Preconditions.checkNotNull(buyerPayoutAddress, "Buyer payout address must not be null"); BigInteger sellerDepositAmount = getSeller().getDepositTx().getIncomingAmount(); - BigInteger buyerDepositAmount = getBuyer().getDepositTx().getIncomingAmount(); + BigInteger buyerDepositAmount = hasBuyerAsTakerWithoutDeposit() ? BigInteger.ZERO : getBuyer().getDepositTx().getIncomingAmount(); BigInteger tradeAmount = getAmount(); BigInteger buyerPayoutAmount = buyerDepositAmount.add(tradeAmount); BigInteger sellerPayoutAmount = sellerDepositAmount.subtract(tradeAmount); @@ -1324,7 +1332,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { MoneroWallet wallet = getWallet(); Contract contract = getContract(); BigInteger sellerDepositAmount = getSeller().getDepositTx().getIncomingAmount(); - BigInteger buyerDepositAmount = getBuyer().getDepositTx().getIncomingAmount(); + BigInteger buyerDepositAmount = hasBuyerAsTakerWithoutDeposit() ? BigInteger.ZERO : getBuyer().getDepositTx().getIncomingAmount(); BigInteger tradeAmount = getAmount(); // describe payout tx @@ -2091,9 +2099,9 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { final long tradeTime = getTakeOfferDate().getTime(); MoneroDaemon daemonRpc = xmrWalletService.getDaemon(); if (daemonRpc == null) throw new RuntimeException("Cannot set start time for trade " + getId() + " because it has no connection to monerod"); - if (getMakerDepositTx() == null || getTakerDepositTx() == null) throw new RuntimeException("Cannot set start time for trade " + getId() + " because its unlocked deposit tx is null. Is client connected to a daemon?"); + if (getMakerDepositTx() == null || (getTakerDepositTx() == null && !hasBuyerAsTakerWithoutDeposit())) throw new RuntimeException("Cannot set start time for trade " + getId() + " because its unlocked deposit tx is null. Is client connected to a daemon?"); - long maxHeight = Math.max(getMakerDepositTx().getHeight(), getTakerDepositTx().getHeight()); + long maxHeight = Math.max(getMakerDepositTx().getHeight(), hasBuyerAsTakerWithoutDeposit() ? 0l : getTakerDepositTx().getHeight()); long blockTime = daemonRpc.getBlockByHeight(maxHeight).getTimestamp(); // If block date is in future (Date in blocks can be off by +/- 2 hours) we use our current date. @@ -2125,7 +2133,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { public boolean isDepositsPublished() { if (isDepositFailed()) return false; - return getState().getPhase().ordinal() >= Phase.DEPOSITS_PUBLISHED.ordinal() && getMaker().getDepositTxHash() != null && getTaker().getDepositTxHash() != null; + return getState().getPhase().ordinal() >= Phase.DEPOSITS_PUBLISHED.ordinal() && getMaker().getDepositTxHash() != null && (getTaker().getDepositTxHash() != null || hasBuyerAsTakerWithoutDeposit()); } public boolean isFundsLockedIn() { @@ -2277,7 +2285,11 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } public BigInteger getTakerFee() { - return offer.getTakerFee(getAmount()); + return hasBuyerAsTakerWithoutDeposit() ? BigInteger.ZERO : offer.getTakerFee(getAmount()); + } + + public BigInteger getSecurityDepositBeforeMiningFee() { + return isBuyer() ? getBuyerSecurityDepositBeforeMiningFee() : getSellerSecurityDepositBeforeMiningFee(); } public BigInteger getBuyerSecurityDepositBeforeMiningFee() { @@ -2288,6 +2300,14 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { return offer.getOfferPayload().getSellerSecurityDepositForTradeAmount(getAmount()); } + public boolean isBuyerAsTakerWithoutDeposit() { + return isBuyer() && isTaker() && BigInteger.ZERO.equals(getBuyerSecurityDepositBeforeMiningFee()); + } + + public boolean hasBuyerAsTakerWithoutDeposit() { + return getBuyer() == getTaker() && BigInteger.ZERO.equals(getBuyerSecurityDepositBeforeMiningFee()); + } + @Override public BigInteger getTotalTxFee() { return getSelf().getDepositTxFee().add(getSelf().getPayoutTxFee()); // sum my tx fees @@ -2303,7 +2323,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } public boolean isTxChainInvalid() { - return processModel.getMaker().getDepositTxHash() == null || processModel.getTaker().getDepositTxHash() == null; + return processModel.getMaker().getDepositTxHash() == null || (processModel.getTaker().getDepositTxHash() == null && !hasBuyerAsTakerWithoutDeposit()); } /** @@ -2537,7 +2557,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { if (isPayoutUnlocked()) return; // skip if deposit txs unknown or not requested - if (processModel.getMaker().getDepositTxHash() == null || processModel.getTaker().getDepositTxHash() == null || !isDepositRequested()) return; + if (!isDepositRequested() || processModel.getMaker().getDepositTxHash() == null || (processModel.getTaker().getDepositTxHash() == null && !hasBuyerAsTakerWithoutDeposit())) return; // skip if daemon not synced if (xmrConnectionService.getTargetHeight() == null || !xmrConnectionService.isSyncedWithinTolerance()) return; @@ -2553,7 +2573,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { // get txs from trade wallet MoneroTxQuery query = new MoneroTxQuery().setIncludeOutputs(true); - Boolean updatePool = !isDepositsConfirmed() && (getMaker().getDepositTx() == null || getTaker().getDepositTx() == null); + Boolean updatePool = !isDepositsConfirmed() && (getMaker().getDepositTx() == null || (getTaker().getDepositTx() == null && hasBuyerAsTakerWithoutDeposit())); if (!updatePool) query.setInTxPool(false); // avoid updating from pool if possible List txs; if (!updatePool) txs = wallet.getTxs(query); @@ -2565,22 +2585,22 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } } setDepositTxs(txs); - if (getMaker().getDepositTx() == null || getTaker().getDepositTx() == null) return; // skip if either deposit tx not seen + if (getMaker().getDepositTx() == null || (getTaker().getDepositTx() == null && !hasBuyerAsTakerWithoutDeposit())) return; // skip if either deposit tx not seen setStateDepositsSeen(); // set actual security deposits if (getBuyer().getSecurityDeposit().longValueExact() == 0) { - BigInteger buyerSecurityDeposit = ((MoneroTxWallet) getBuyer().getDepositTx()).getIncomingAmount(); + BigInteger buyerSecurityDeposit = hasBuyerAsTakerWithoutDeposit() ? BigInteger.ZERO : ((MoneroTxWallet) getBuyer().getDepositTx()).getIncomingAmount(); BigInteger sellerSecurityDeposit = ((MoneroTxWallet) getSeller().getDepositTx()).getIncomingAmount().subtract(getAmount()); getBuyer().setSecurityDeposit(buyerSecurityDeposit); getSeller().setSecurityDeposit(sellerSecurityDeposit); } // check for deposit txs confirmation - if (getMaker().getDepositTx().isConfirmed() && getTaker().getDepositTx().isConfirmed()) setStateDepositsConfirmed(); + if (getMaker().getDepositTx().isConfirmed() && (hasBuyerAsTakerWithoutDeposit() || getTaker().getDepositTx().isConfirmed())) setStateDepositsConfirmed(); // check for deposit txs unlocked - if (getMaker().getDepositTx().getNumConfirmations() >= XmrWalletService.NUM_BLOCKS_UNLOCK && getTaker().getDepositTx().getNumConfirmations() >= XmrWalletService.NUM_BLOCKS_UNLOCK) { + if (getMaker().getDepositTx().getNumConfirmations() >= XmrWalletService.NUM_BLOCKS_UNLOCK && (hasBuyerAsTakerWithoutDeposit() || getTaker().getDepositTx().getNumConfirmations() >= XmrWalletService.NUM_BLOCKS_UNLOCK)) { setStateDepositsUnlocked(); } } @@ -2750,7 +2770,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { log.warn("Missing maker deposit tx for {} {}", getClass().getSimpleName(), getId()); return true; } - if (getTakerDepositTx() == null) { + if (getTakerDepositTx() == null && !hasBuyerAsTakerWithoutDeposit()) { log.warn("Missing taker deposit tx for {} {}", getClass().getSimpleName(), getId()); return true; } @@ -2913,6 +2933,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { Optional.ofNullable(payoutTxHex).ifPresent(e -> builder.setPayoutTxHex(payoutTxHex)); Optional.ofNullable(payoutTxKey).ifPresent(e -> builder.setPayoutTxKey(payoutTxKey)); Optional.ofNullable(counterCurrencyExtraData).ifPresent(e -> builder.setCounterCurrencyExtraData(counterCurrencyExtraData)); + Optional.ofNullable(challenge).ifPresent(e -> builder.setChallenge(challenge)); return builder.build(); } @@ -2982,6 +3003,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { ",\n refundResultState=" + refundResultState + ",\n refundResultStateProperty=" + refundResultStateProperty + ",\n isCompleted=" + isCompleted + + ",\n challenge='" + challenge + '\'' + "\n}"; } } diff --git a/core/src/main/java/haveno/core/trade/TradeManager.java b/core/src/main/java/haveno/core/trade/TradeManager.java index 9a3d84cb..a3cca849 100644 --- a/core/src/main/java/haveno/core/trade/TradeManager.java +++ b/core/src/main/java/haveno/core/trade/TradeManager.java @@ -561,6 +561,12 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi OpenOffer openOffer = openOfferOptional.get(); if (openOffer.getState() != OpenOffer.State.AVAILABLE) return; Offer offer = openOffer.getOffer(); + + // validate challenge + if (openOffer.getChallenge() != null && !HavenoUtils.getChallengeHash(openOffer.getChallenge()).equals(HavenoUtils.getChallengeHash(request.getChallenge()))) { + log.warn("Ignoring InitTradeRequest to maker because challenge is incorrect, tradeId={}, sender={}", request.getOfferId(), sender); + return; + } // ensure trade does not already exist Optional tradeOptional = getOpenTrade(request.getOfferId()); @@ -583,7 +589,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi UUID.randomUUID().toString(), request.getMakerNodeAddress(), request.getTakerNodeAddress(), - request.getArbitratorNodeAddress()); + request.getArbitratorNodeAddress(), + openOffer.getChallenge()); else trade = new SellerAsMakerTrade(offer, BigInteger.valueOf(request.getTradeAmount()), @@ -593,7 +600,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi UUID.randomUUID().toString(), request.getMakerNodeAddress(), request.getTakerNodeAddress(), - request.getArbitratorNodeAddress()); + request.getArbitratorNodeAddress(), + openOffer.getChallenge()); trade.getMaker().setPaymentAccountId(trade.getOffer().getOfferPayload().getMakerPaymentAccountId()); trade.getTaker().setPaymentAccountId(request.getTakerPaymentAccountId()); trade.getMaker().setPubKeyRing(trade.getOffer().getPubKeyRing()); @@ -646,6 +654,12 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi return; } + // validate challenge hash + if (offer.getChallengeHash() != null && !offer.getChallengeHash().equals(HavenoUtils.getChallengeHash(request.getChallenge()))) { + log.warn("Ignoring InitTradeRequest to arbitrator because challenge hash is incorrect, tradeId={}, sender={}", request.getOfferId(), sender); + return; + } + // handle trade Trade trade; Optional tradeOptional = getOpenTrade(offer.getId()); @@ -679,7 +693,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi UUID.randomUUID().toString(), request.getMakerNodeAddress(), request.getTakerNodeAddress(), - request.getArbitratorNodeAddress()); + request.getArbitratorNodeAddress(), + request.getChallenge()); // set reserve tx hash if available Optional signedOfferOptional = openOfferManager.getSignedOfferById(request.getOfferId()); @@ -873,7 +888,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi UUID.randomUUID().toString(), offer.getMakerNodeAddress(), P2PService.getMyNodeAddress(), - null); + null, + offer.getChallenge()); } else { trade = new BuyerAsTakerTrade(offer, amount, @@ -883,7 +899,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi UUID.randomUUID().toString(), offer.getMakerNodeAddress(), P2PService.getMyNodeAddress(), - null); + null, + offer.getChallenge()); } trade.getProcessModel().setUseSavingsWallet(useSavingsWallet); trade.getProcessModel().setFundsNeededForTrade(fundsNeededForTrade.longValueExact()); @@ -1127,7 +1144,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi log.warn("We found a closed trade with locked up funds. " + "That should never happen. trade ID={} ID={}, state={}, payoutState={}, disputeState={}", trade.getClass().getSimpleName(), trade.getId(), trade.getState(), trade.getPayoutState(), trade.getDisputeState()); } - } else { + } else if (!trade.hasBuyerAsTakerWithoutDeposit()) { log.warn("Closed trade with locked up funds missing taker deposit tx. {} ID={}, state={}, payoutState={}, disputeState={}", trade.getClass().getSimpleName(), trade.getId(), trade.getState(), trade.getPayoutState(), trade.getDisputeState()); tradeTxException.set(new TradeTxException(Res.get("error.closedTradeWithNoDepositTx", trade.getShortId()))); } diff --git a/core/src/main/java/haveno/core/trade/messages/DepositRequest.java b/core/src/main/java/haveno/core/trade/messages/DepositRequest.java index c743595e..b0ac60af 100644 --- a/core/src/main/java/haveno/core/trade/messages/DepositRequest.java +++ b/core/src/main/java/haveno/core/trade/messages/DepositRequest.java @@ -33,7 +33,9 @@ import java.util.Optional; public final class DepositRequest extends TradeMessage implements DirectMessage { private final long currentDate; private final byte[] contractSignature; + @Nullable private final String depositTxHex; + @Nullable private final String depositTxKey; @Nullable private final byte[] paymentAccountKey; @@ -43,8 +45,8 @@ public final class DepositRequest extends TradeMessage implements DirectMessage String messageVersion, long currentDate, byte[] contractSignature, - String depositTxHex, - String depositTxKey, + @Nullable String depositTxHex, + @Nullable String depositTxKey, @Nullable byte[] paymentAccountKey) { super(messageVersion, tradeId, uid); this.currentDate = currentDate; @@ -63,13 +65,12 @@ public final class DepositRequest extends TradeMessage implements DirectMessage public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { protobuf.DepositRequest.Builder builder = protobuf.DepositRequest.newBuilder() .setTradeId(offerId) - .setUid(uid) - .setDepositTxHex(depositTxHex) - .setDepositTxKey(depositTxKey); + .setUid(uid); builder.setCurrentDate(currentDate); Optional.ofNullable(paymentAccountKey).ifPresent(e -> builder.setPaymentAccountKey(ByteString.copyFrom(e))); + Optional.ofNullable(depositTxHex).ifPresent(builder::setDepositTxHex); + Optional.ofNullable(depositTxKey).ifPresent(builder::setDepositTxKey); Optional.ofNullable(contractSignature).ifPresent(e -> builder.setContractSignature(ByteString.copyFrom(e))); - return getNetworkEnvelopeBuilder().setDepositRequest(builder).build(); } @@ -81,8 +82,8 @@ public final class DepositRequest extends TradeMessage implements DirectMessage messageVersion, proto.getCurrentDate(), ProtoUtil.byteArrayOrNullFromProto(proto.getContractSignature()), - proto.getDepositTxHex(), - proto.getDepositTxKey(), + ProtoUtil.stringOrNullFromProto(proto.getDepositTxHex()), + ProtoUtil.stringOrNullFromProto(proto.getDepositTxKey()), ProtoUtil.byteArrayOrNullFromProto(proto.getPaymentAccountKey())); } diff --git a/core/src/main/java/haveno/core/trade/messages/InitTradeRequest.java b/core/src/main/java/haveno/core/trade/messages/InitTradeRequest.java index 48464875..817162ad 100644 --- a/core/src/main/java/haveno/core/trade/messages/InitTradeRequest.java +++ b/core/src/main/java/haveno/core/trade/messages/InitTradeRequest.java @@ -58,6 +58,8 @@ public final class InitTradeRequest extends TradeMessage implements DirectMessag private final String reserveTxKey; @Nullable private final String payoutAddress; + @Nullable + private final String challenge; public InitTradeRequest(TradeProtocolVersion tradeProtocolVersion, String offerId, @@ -79,7 +81,8 @@ public final class InitTradeRequest extends TradeMessage implements DirectMessag @Nullable String reserveTxHash, @Nullable String reserveTxHex, @Nullable String reserveTxKey, - @Nullable String payoutAddress) { + @Nullable String payoutAddress, + @Nullable String challenge) { super(messageVersion, offerId, uid); this.tradeProtocolVersion = tradeProtocolVersion; this.tradeAmount = tradeAmount; @@ -99,6 +102,7 @@ public final class InitTradeRequest extends TradeMessage implements DirectMessag this.reserveTxHex = reserveTxHex; this.reserveTxKey = reserveTxKey; this.payoutAddress = payoutAddress; + this.challenge = challenge; } @@ -129,6 +133,7 @@ public final class InitTradeRequest extends TradeMessage implements DirectMessag Optional.ofNullable(reserveTxHex).ifPresent(e -> builder.setReserveTxHex(reserveTxHex)); Optional.ofNullable(reserveTxKey).ifPresent(e -> builder.setReserveTxKey(reserveTxKey)); Optional.ofNullable(payoutAddress).ifPresent(e -> builder.setPayoutAddress(payoutAddress)); + Optional.ofNullable(challenge).ifPresent(e -> builder.setChallenge(challenge)); Optional.ofNullable(accountAgeWitnessSignatureOfOfferId).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfOfferId(ByteString.copyFrom(e))); builder.setCurrentDate(currentDate); @@ -158,7 +163,8 @@ public final class InitTradeRequest extends TradeMessage implements DirectMessag ProtoUtil.stringOrNullFromProto(proto.getReserveTxHash()), ProtoUtil.stringOrNullFromProto(proto.getReserveTxHex()), ProtoUtil.stringOrNullFromProto(proto.getReserveTxKey()), - ProtoUtil.stringOrNullFromProto(proto.getPayoutAddress())); + ProtoUtil.stringOrNullFromProto(proto.getPayoutAddress()), + ProtoUtil.stringOrNullFromProto(proto.getChallenge())); } @Override @@ -183,6 +189,7 @@ public final class InitTradeRequest extends TradeMessage implements DirectMessag ",\n reserveTxHex=" + reserveTxHex + ",\n reserveTxKey=" + reserveTxKey + ",\n payoutAddress=" + payoutAddress + + ",\n challenge=" + challenge + "\n} " + super.toString(); } } diff --git a/core/src/main/java/haveno/core/trade/messages/SignContractRequest.java b/core/src/main/java/haveno/core/trade/messages/SignContractRequest.java index 89459044..ab82e13d 100644 --- a/core/src/main/java/haveno/core/trade/messages/SignContractRequest.java +++ b/core/src/main/java/haveno/core/trade/messages/SignContractRequest.java @@ -35,7 +35,9 @@ public final class SignContractRequest extends TradeMessage implements DirectMes private final String accountId; private final byte[] paymentAccountPayloadHash; private final String payoutAddress; + @Nullable private final String depositTxHash; + @Nullable private final byte[] accountAgeWitnessSignatureOfDepositHash; public SignContractRequest(String tradeId, @@ -45,7 +47,7 @@ public final class SignContractRequest extends TradeMessage implements DirectMes String accountId, byte[] paymentAccountPayloadHash, String payoutAddress, - String depositTxHash, + @Nullable String depositTxHash, @Nullable byte[] accountAgeWitnessSignatureOfDepositHash) { super(messageVersion, tradeId, uid); this.currentDate = currentDate; @@ -68,10 +70,9 @@ public final class SignContractRequest extends TradeMessage implements DirectMes .setUid(uid) .setAccountId(accountId) .setPaymentAccountPayloadHash(ByteString.copyFrom(paymentAccountPayloadHash)) - .setPayoutAddress(payoutAddress) - .setDepositTxHash(depositTxHash); - + .setPayoutAddress(payoutAddress); Optional.ofNullable(accountAgeWitnessSignatureOfDepositHash).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfDepositHash(ByteString.copyFrom(e))); + Optional.ofNullable(depositTxHash).ifPresent(builder::setDepositTxHash); builder.setCurrentDate(currentDate); return getNetworkEnvelopeBuilder().setSignContractRequest(builder).build(); @@ -87,7 +88,7 @@ public final class SignContractRequest extends TradeMessage implements DirectMes proto.getAccountId(), proto.getPaymentAccountPayloadHash().toByteArray(), proto.getPayoutAddress(), - proto.getDepositTxHash(), + ProtoUtil.stringOrNullFromProto(proto.getDepositTxHash()), ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfDepositHash())); } diff --git a/core/src/main/java/haveno/core/trade/protocol/TradePeer.java b/core/src/main/java/haveno/core/trade/protocol/TradePeer.java index b076826b..eeef2d4d 100644 --- a/core/src/main/java/haveno/core/trade/protocol/TradePeer.java +++ b/core/src/main/java/haveno/core/trade/protocol/TradePeer.java @@ -158,7 +158,6 @@ public final class TradePeer implements PersistablePayload { } public BigInteger getSecurityDeposit() { - if (depositTxHash == null) return null; return BigInteger.valueOf(securityDeposit); } diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java index 54ced825..5bd7ab9d 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java @@ -36,8 +36,9 @@ import monero.daemon.model.MoneroSubmitTxResult; import monero.daemon.model.MoneroTx; import java.math.BigInteger; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.UUID; @Slf4j @@ -83,72 +84,86 @@ public class ArbitratorProcessDepositRequest extends TradeTask { byte[] signature = request.getContractSignature(); // get trader info - TradePeer trader = trade.getTradePeer(processModel.getTempTradePeerNodeAddress()); - if (trader == null) throw new RuntimeException(request.getClass().getSimpleName() + " is not from maker, taker, or arbitrator"); - PubKeyRing peerPubKeyRing = trader.getPubKeyRing(); + TradePeer sender = trade.getTradePeer(processModel.getTempTradePeerNodeAddress()); + if (sender == null) throw new RuntimeException(request.getClass().getSimpleName() + " is not from maker, taker, or arbitrator"); + PubKeyRing senderPubKeyRing = sender.getPubKeyRing(); // verify signature - if (!HavenoUtils.isSignatureValid(peerPubKeyRing, contractAsJson, signature)) { + if (!HavenoUtils.isSignatureValid(senderPubKeyRing, contractAsJson, signature)) { throw new RuntimeException("Peer's contract signature is invalid"); } // set peer's signature - trader.setContractSignature(signature); + sender.setContractSignature(signature); // collect expected values Offer offer = trade.getOffer(); - boolean isFromTaker = trader == trade.getTaker(); - boolean isFromBuyer = trader == trade.getBuyer(); + boolean isFromTaker = sender == trade.getTaker(); + boolean isFromBuyer = sender == trade.getBuyer(); BigInteger tradeFee = isFromTaker ? trade.getTakerFee() : trade.getMakerFee(); BigInteger sendTradeAmount = isFromBuyer ? BigInteger.ZERO : trade.getAmount(); BigInteger securityDeposit = isFromBuyer ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee(); String depositAddress = processModel.getMultisigAddress(); + sender.setSecurityDeposit(securityDeposit); // verify deposit tx - MoneroTx verifiedTx; - try { - verifiedTx = trade.getXmrWalletService().verifyDepositTx( - offer.getId(), - tradeFee, - trade.getProcessModel().getTradeFeeAddress(), - sendTradeAmount, - securityDeposit, - depositAddress, - trader.getDepositTxHash(), - request.getDepositTxHex(), - request.getDepositTxKey(), - null); - } catch (Exception e) { - throw new RuntimeException("Error processing deposit tx from " + (isFromTaker ? "taker " : "maker ") + trader.getNodeAddress() + ", offerId=" + offer.getId() + ": " + e.getMessage()); + boolean isFromBuyerAsTakerWithoutDeposit = isFromBuyer && isFromTaker && trade.hasBuyerAsTakerWithoutDeposit(); + if (!isFromBuyerAsTakerWithoutDeposit) { + MoneroTx verifiedTx; + try { + verifiedTx = trade.getXmrWalletService().verifyDepositTx( + offer.getId(), + tradeFee, + trade.getProcessModel().getTradeFeeAddress(), + sendTradeAmount, + securityDeposit, + depositAddress, + sender.getDepositTxHash(), + request.getDepositTxHex(), + request.getDepositTxKey(), + null); + } catch (Exception e) { + throw new RuntimeException("Error processing deposit tx from " + (isFromTaker ? "taker " : "maker ") + sender.getNodeAddress() + ", offerId=" + offer.getId() + ": " + e.getMessage()); + } + + // update trade state + sender.setSecurityDeposit(sender.getSecurityDeposit().subtract(verifiedTx.getFee())); // subtract mining fee from security deposit + sender.setDepositTxFee(verifiedTx.getFee()); + sender.setDepositTxHex(request.getDepositTxHex()); + sender.setDepositTxKey(request.getDepositTxKey()); } // update trade state - trader.setSecurityDeposit(securityDeposit.subtract(verifiedTx.getFee())); // subtract mining fee from security deposit - trader.setDepositTxFee(verifiedTx.getFee()); - trader.setDepositTxHex(request.getDepositTxHex()); - trader.setDepositTxKey(request.getDepositTxKey()); - if (request.getPaymentAccountKey() != null) trader.setPaymentAccountKey(request.getPaymentAccountKey()); + if (request.getPaymentAccountKey() != null) sender.setPaymentAccountKey(request.getPaymentAccountKey()); processModel.getTradeManager().requestPersistence(); - // relay deposit txs when both available + // relay deposit txs when both requests received MoneroDaemon daemon = trade.getXmrWalletService().getDaemon(); - if (processModel.getMaker().getDepositTxHex() != null && processModel.getTaker().getDepositTxHex() != null) { + if (processModel.getMaker().getContractSignature() != null && processModel.getTaker().getContractSignature() != null) { // check timeout and extend just before relaying if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out before relaying deposit txs for {} {}" + trade.getClass().getSimpleName() + " " + trade.getShortId()); trade.addInitProgressStep(); + // relay deposit txs boolean depositTxsRelayed = false; + List txHashes = new ArrayList<>(); try { - // submit txs to pool but do not relay + // submit maker tx to pool but do not relay MoneroSubmitTxResult makerResult = daemon.submitTxHex(processModel.getMaker().getDepositTxHex(), true); - MoneroSubmitTxResult takerResult = daemon.submitTxHex(processModel.getTaker().getDepositTxHex(), true); if (!makerResult.isGood()) throw new RuntimeException("Error submitting maker deposit tx: " + JsonUtils.serialize(makerResult)); - if (!takerResult.isGood()) throw new RuntimeException("Error submitting taker deposit tx: " + JsonUtils.serialize(takerResult)); + txHashes.add(processModel.getMaker().getDepositTxHash()); + + // submit taker tx to pool but do not relay + if (!trade.hasBuyerAsTakerWithoutDeposit()) { + MoneroSubmitTxResult takerResult = daemon.submitTxHex(processModel.getTaker().getDepositTxHex(), true); + if (!takerResult.isGood()) throw new RuntimeException("Error submitting taker deposit tx: " + JsonUtils.serialize(takerResult)); + txHashes.add(processModel.getTaker().getDepositTxHash()); + } // relay txs - daemon.relayTxsByHash(Arrays.asList(processModel.getMaker().getDepositTxHash(), processModel.getTaker().getDepositTxHash())); + daemon.relayTxsByHash(txHashes); depositTxsRelayed = true; // update trade state @@ -160,7 +175,7 @@ public class ArbitratorProcessDepositRequest extends TradeTask { // flush txs from pool try { - daemon.flushTxPool(processModel.getMaker().getDepositTxHash(), processModel.getTaker().getDepositTxHash()); + daemon.flushTxPool(txHashes); } catch (Exception e2) { log.warn("Error flushing deposit txs from pool for trade {}: {}\n", trade.getId(), e2.getMessage(), e2); } @@ -180,7 +195,7 @@ public class ArbitratorProcessDepositRequest extends TradeTask { }); if (processModel.getMaker().getDepositTxHex() == null) log.info("Arbitrator waiting for deposit request from maker for trade " + trade.getId()); - if (processModel.getTaker().getDepositTxHex() == null) log.info("Arbitrator waiting for deposit request from taker for trade " + trade.getId()); + if (processModel.getTaker().getDepositTxHex() == null && !trade.hasBuyerAsTakerWithoutDeposit()) log.info("Arbitrator waiting for deposit request from taker for trade " + trade.getId()); } } diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java index 10baac85..18e97dd4 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java @@ -53,38 +53,44 @@ public class ArbitratorProcessReserveTx extends TradeTask { TradePeer sender = trade.getTradePeer(processModel.getTempTradePeerNodeAddress()); boolean isFromMaker = sender == trade.getMaker(); boolean isFromBuyer = isFromMaker ? offer.getDirection() == OfferDirection.BUY : offer.getDirection() == OfferDirection.SELL; + sender = isFromMaker ? processModel.getMaker() : processModel.getTaker(); + BigInteger securityDeposit = isFromMaker ? isFromBuyer ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit() : isFromBuyer ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee(); + sender.setSecurityDeposit(securityDeposit); // TODO (woodser): if signer online, should never be called by maker? - // process reserve tx with expected values - BigInteger penaltyFee = HavenoUtils.multiply(isFromMaker ? offer.getAmount() : trade.getAmount(), offer.getPenaltyFeePct()); - BigInteger tradeFee = isFromMaker ? offer.getMaxMakerFee() : trade.getTakerFee(); - BigInteger sendAmount = isFromBuyer ? BigInteger.ZERO : isFromMaker ? offer.getAmount() : trade.getAmount(); // maker reserve tx is for offer amount - BigInteger securityDeposit = isFromMaker ? isFromBuyer ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit() : isFromBuyer ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee(); - MoneroTx verifiedTx; - try { - verifiedTx = trade.getXmrWalletService().verifyReserveTx( - offer.getId(), - penaltyFee, - tradeFee, - sendAmount, - securityDeposit, - request.getPayoutAddress(), - request.getReserveTxHash(), - request.getReserveTxHex(), - request.getReserveTxKey(), - null); - } catch (Exception e) { - log.error(ExceptionUtils.getStackTrace(e)); - throw new RuntimeException("Error processing reserve tx from " + (isFromMaker ? "maker " : "taker ") + processModel.getTempTradePeerNodeAddress() + ", offerId=" + offer.getId() + ": " + e.getMessage()); - } + // process reserve tx unless from buyer as taker without deposit + boolean isFromBuyerAsTakerWithoutDeposit = isFromBuyer && !isFromMaker && trade.hasBuyerAsTakerWithoutDeposit(); + if (!isFromBuyerAsTakerWithoutDeposit) { - // save reserve tx to model - TradePeer trader = isFromMaker ? processModel.getMaker() : processModel.getTaker(); - trader.setSecurityDeposit(securityDeposit.subtract(verifiedTx.getFee())); // subtract mining fee from security deposit - trader.setReserveTxHash(request.getReserveTxHash()); - trader.setReserveTxHex(request.getReserveTxHex()); - trader.setReserveTxKey(request.getReserveTxKey()); + // process reserve tx with expected values + BigInteger penaltyFee = HavenoUtils.multiply(isFromMaker ? offer.getAmount() : trade.getAmount(), offer.getPenaltyFeePct()); + BigInteger tradeFee = isFromMaker ? offer.getMaxMakerFee() : trade.getTakerFee(); + BigInteger sendAmount = isFromBuyer ? BigInteger.ZERO : isFromMaker ? offer.getAmount() : trade.getAmount(); // maker reserve tx is for offer amount + MoneroTx verifiedTx; + try { + verifiedTx = trade.getXmrWalletService().verifyReserveTx( + offer.getId(), + penaltyFee, + tradeFee, + sendAmount, + securityDeposit, + request.getPayoutAddress(), + request.getReserveTxHash(), + request.getReserveTxHex(), + request.getReserveTxKey(), + null); + } catch (Exception e) { + log.error(ExceptionUtils.getStackTrace(e)); + throw new RuntimeException("Error processing reserve tx from " + (isFromMaker ? "maker " : "taker ") + processModel.getTempTradePeerNodeAddress() + ", offerId=" + offer.getId() + ": " + e.getMessage()); + } + + // save reserve tx to model + sender.setSecurityDeposit(sender.getSecurityDeposit().subtract(verifiedTx.getFee())); // subtract mining fee from security deposit + sender.setReserveTxHash(request.getReserveTxHash()); + sender.setReserveTxHex(request.getReserveTxHex()); + sender.setReserveTxKey(request.getReserveTxKey()); + } // persist trade processModel.getTradeManager().requestPersistence(); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorSendInitTradeOrMultisigRequests.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorSendInitTradeOrMultisigRequests.java index 9ec7aebe..a846077d 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorSendInitTradeOrMultisigRequests.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorSendInitTradeOrMultisigRequests.java @@ -78,6 +78,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask { null, null, null, + null, null); // send request to taker @@ -118,7 +119,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask { // ensure arbitrator has reserve txs if (processModel.getMaker().getReserveTxHash() == null) throw new RuntimeException("Arbitrator does not have maker's reserve tx after initializing trade"); - if (processModel.getTaker().getReserveTxHash() == null) throw new RuntimeException("Arbitrator does not have taker's reserve tx after initializing trade"); + if (processModel.getTaker().getReserveTxHash() == null && !trade.hasBuyerAsTakerWithoutDeposit()) throw new RuntimeException("Arbitrator does not have taker's reserve tx after initializing trade"); // create wallet for multisig MoneroWallet multisigWallet = trade.createWallet(); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/BuyerPreparePaymentSentMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/BuyerPreparePaymentSentMessage.java index 8b739011..05fee137 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/BuyerPreparePaymentSentMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/BuyerPreparePaymentSentMessage.java @@ -74,7 +74,7 @@ public class BuyerPreparePaymentSentMessage extends TradeTask { Preconditions.checkNotNull(trade.getSeller().getPaymentAccountPayload(), "Seller's payment account payload is null"); Preconditions.checkNotNull(trade.getAmount(), "trade.getTradeAmount() must not be null"); Preconditions.checkNotNull(trade.getMakerDepositTx(), "trade.getMakerDepositTx() must not be null"); - Preconditions.checkNotNull(trade.getTakerDepositTx(), "trade.getTakerDepositTx() must not be null"); + if (!trade.hasBuyerAsTakerWithoutDeposit()) Preconditions.checkNotNull(trade.getTakerDepositTx(), "trade.getTakerDepositTx() must not be null"); checkNotNull(trade.getOffer(), "offer must not be null"); // create payout tx if we have seller's updated multisig hex diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/MakerSendInitTradeRequestToArbitrator.java b/core/src/main/java/haveno/core/trade/protocol/tasks/MakerSendInitTradeRequestToArbitrator.java index 91398e82..9f191131 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/MakerSendInitTradeRequestToArbitrator.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/MakerSendInitTradeRequestToArbitrator.java @@ -138,7 +138,8 @@ public class MakerSendInitTradeRequestToArbitrator extends TradeTask { trade.getSelf().getReserveTxHash(), trade.getSelf().getReserveTxHex(), trade.getSelf().getReserveTxKey(), - model.getXmrWalletService().getOrCreateAddressEntry(trade.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString()); + model.getXmrWalletService().getOrCreateAddressEntry(trade.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(), + trade.getChallenge()); // send request to arbitrator log.info("Sending {} with offerId {} and uid {} to arbitrator {}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getOfferId(), arbitratorRequest.getUid(), trade.getArbitrator().getNodeAddress()); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeSendSignContractRequest.java b/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeSendSignContractRequest.java index 69d1620a..e1c4cce5 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeSendSignContractRequest.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeSendSignContractRequest.java @@ -83,7 +83,7 @@ public class MaybeSendSignContractRequest extends TradeTask { if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while getting lock to create deposit tx, tradeId=" + trade.getShortId()); trade.startProtocolTimeout(); - // collect relevant info + // collect info Integer subaddressIndex = null; boolean reserveExactAmount = false; if (trade instanceof MakerTrade) { @@ -97,53 +97,60 @@ public class MaybeSendSignContractRequest extends TradeTask { } // attempt creating deposit tx - try { - synchronized (HavenoUtils.getWalletFunctionLock()) { - for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) { - MoneroRpcConnection sourceConnection = trade.getXmrConnectionService().getConnection(); - try { - depositTx = trade.getXmrWalletService().createDepositTx(trade, reserveExactAmount, subaddressIndex); - } catch (Exception e) { - log.warn("Error creating deposit tx, tradeId={}, attempt={}/{}, error={}", trade.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); - trade.getXmrWalletService().handleWalletError(e, sourceConnection); + if (!trade.isBuyerAsTakerWithoutDeposit()) { + try { + synchronized (HavenoUtils.getWalletFunctionLock()) { + for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) { + MoneroRpcConnection sourceConnection = trade.getXmrConnectionService().getConnection(); + try { + depositTx = trade.getXmrWalletService().createDepositTx(trade, reserveExactAmount, subaddressIndex); + } catch (Exception e) { + log.warn("Error creating deposit tx, tradeId={}, attempt={}/{}, error={}", trade.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); + trade.getXmrWalletService().handleWalletError(e, sourceConnection); + if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating deposit tx, tradeId=" + trade.getShortId()); + if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; + HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying + } + + // check for timeout if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating deposit tx, tradeId=" + trade.getShortId()); - if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; - HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying + if (depositTx != null) break; } - - // check for timeout - if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating deposit tx, tradeId=" + trade.getShortId()); - if (depositTx != null) break; } + } catch (Exception e) { + + // thaw deposit inputs + if (depositTx != null) { + trade.getXmrWalletService().thawOutputs(HavenoUtils.getInputKeyImages(depositTx)); + trade.getSelf().setReserveTxKeyImages(null); + } + + // re-freeze maker offer inputs + if (trade instanceof MakerTrade) { + trade.getXmrWalletService().freezeOutputs(trade.getOffer().getOfferPayload().getReserveTxKeyImages()); + } + + throw e; } - } catch (Exception e) { - - // thaw deposit inputs - if (depositTx != null) { - trade.getXmrWalletService().thawOutputs(HavenoUtils.getInputKeyImages(depositTx)); - trade.getSelf().setReserveTxKeyImages(null); - } - - // re-freeze maker offer inputs - if (trade instanceof MakerTrade) { - trade.getXmrWalletService().freezeOutputs(trade.getOffer().getOfferPayload().getReserveTxKeyImages()); - } - - throw e; } // reset protocol timeout trade.addInitProgressStep(); // update trade state - BigInteger securityDeposit = trade instanceof BuyerTrade ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee(); - trade.getSelf().setSecurityDeposit(securityDeposit.subtract(depositTx.getFee())); - trade.getSelf().setDepositTx(depositTx); - trade.getSelf().setDepositTxHash(depositTx.getHash()); - trade.getSelf().setDepositTxFee(depositTx.getFee()); - trade.getSelf().setReserveTxKeyImages(HavenoUtils.getInputKeyImages(depositTx)); trade.getSelf().setPayoutAddressString(trade.getXmrWalletService().getOrCreateAddressEntry(trade.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString()); // TODO (woodser): allow custom payout address? trade.getSelf().setPaymentAccountPayload(trade.getProcessModel().getPaymentAccountPayload(trade.getSelf().getPaymentAccountId())); + trade.getSelf().setPaymentAccountPayloadHash(trade.getSelf().getPaymentAccountPayload().getHash()); + BigInteger securityDeposit = trade instanceof BuyerTrade ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee(); + if (depositTx == null) { + trade.getSelf().setSecurityDeposit(securityDeposit); + } else { + trade.getSelf().setSecurityDeposit(securityDeposit.subtract(depositTx.getFee())); + trade.getSelf().setDepositTx(depositTx); + trade.getSelf().setDepositTxHash(depositTx.getHash()); + trade.getSelf().setDepositTxFee(depositTx.getFee()); + trade.getSelf().setReserveTxKeyImages(HavenoUtils.getInputKeyImages(depositTx)); + } } // maker signs deposit hash nonce to avoid challenge protocol @@ -161,7 +168,7 @@ public class MaybeSendSignContractRequest extends TradeTask { trade.getProcessModel().getAccountId(), trade.getSelf().getPaymentAccountPayload().getHash(), trade.getSelf().getPayoutAddressString(), - depositTx.getHash(), + depositTx == null ? null : depositTx.getHash(), sig); // send request to trading peer diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessSignContractRequest.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessSignContractRequest.java index 8fc93df9..1ce805ac 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessSignContractRequest.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessSignContractRequest.java @@ -63,20 +63,20 @@ public class ProcessSignContractRequest extends TradeTask { // extract fields from request // TODO (woodser): verify request and from maker or taker SignContractRequest request = (SignContractRequest) processModel.getTradeMessage(); - TradePeer trader = trade.getTradePeer(processModel.getTempTradePeerNodeAddress()); - trader.setDepositTxHash(request.getDepositTxHash()); - trader.setAccountId(request.getAccountId()); - trader.setPaymentAccountPayloadHash(request.getPaymentAccountPayloadHash()); - trader.setPayoutAddressString(request.getPayoutAddress()); + TradePeer sender = trade.getTradePeer(processModel.getTempTradePeerNodeAddress()); + sender.setDepositTxHash(request.getDepositTxHash()); + sender.setAccountId(request.getAccountId()); + sender.setPaymentAccountPayloadHash(request.getPaymentAccountPayloadHash()); + sender.setPayoutAddressString(request.getPayoutAddress()); // maker sends witness signature of deposit tx hash - if (trader == trade.getMaker()) { - trader.setAccountAgeWitnessNonce(request.getDepositTxHash().getBytes(Charsets.UTF_8)); - trader.setAccountAgeWitnessSignature(request.getAccountAgeWitnessSignatureOfDepositHash()); + if (sender == trade.getMaker()) { + sender.setAccountAgeWitnessNonce(request.getDepositTxHash().getBytes(Charsets.UTF_8)); + sender.setAccountAgeWitnessSignature(request.getAccountAgeWitnessSignatureOfDepositHash()); } - // sign contract only when both deposit txs hashes known - if (processModel.getMaker().getDepositTxHash() == null || processModel.getTaker().getDepositTxHash() == null) { + // sign contract only when received from both peers + if (processModel.getMaker().getPaymentAccountPayloadHash() == null || processModel.getTaker().getPaymentAccountPayloadHash() == null) { complete(); return; } diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/SendDepositRequest.java b/core/src/main/java/haveno/core/trade/protocol/tasks/SendDepositRequest.java index dd43d694..7101c488 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/SendDepositRequest.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/SendDepositRequest.java @@ -82,8 +82,8 @@ public class SendDepositRequest extends TradeTask { Version.getP2PMessageVersion(), new Date().getTime(), trade.getSelf().getContractSignature(), - trade.getSelf().getDepositTx().getFullHex(), - trade.getSelf().getDepositTx().getKey(), + trade.getSelf().getDepositTx() == null ? null : trade.getSelf().getDepositTx().getFullHex(), + trade.getSelf().getDepositTx() == null ? null : trade.getSelf().getDepositTx().getKey(), trade.getSelf().getPaymentAccountKey()); // update trade state diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java index 5ab91343..e6c71032 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java @@ -47,62 +47,63 @@ public class TakerReserveTradeFunds extends TradeTask { throw new RuntimeException("Expected taker trade but was " + trade.getClass().getSimpleName() + " " + trade.getShortId() + ". That should never happen."); } - // create reserve tx + // create reserve tx unless deposit not required from buyer as taker MoneroTxWallet reserveTx = null; - synchronized (HavenoUtils.xmrWalletService.getWalletLock()) { + if (!trade.isBuyerAsTakerWithoutDeposit()) { + synchronized (HavenoUtils.xmrWalletService.getWalletLock()) { - // check for timeout - if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while getting lock to create reserve tx, tradeId=" + trade.getShortId()); - trade.startProtocolTimeout(); + // check for timeout + if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while getting lock to create reserve tx, tradeId=" + trade.getShortId()); + trade.startProtocolTimeout(); - // collect relevant info - BigInteger penaltyFee = HavenoUtils.multiply(trade.getAmount(), trade.getOffer().getPenaltyFeePct()); - BigInteger takerFee = trade.getTakerFee(); - BigInteger sendAmount = trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getAmount() : BigInteger.ZERO; - BigInteger securityDeposit = trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getSellerSecurityDepositBeforeMiningFee() : trade.getBuyerSecurityDepositBeforeMiningFee(); - String returnAddress = trade.getXmrWalletService().getOrCreateAddressEntry(trade.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(); + // collect relevant info + BigInteger penaltyFee = HavenoUtils.multiply(trade.getAmount(), trade.getOffer().getPenaltyFeePct()); + BigInteger takerFee = trade.getTakerFee(); + BigInteger sendAmount = trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getAmount() : BigInteger.ZERO; + BigInteger securityDeposit = trade.getSecurityDepositBeforeMiningFee(); + String returnAddress = trade.getXmrWalletService().getOrCreateAddressEntry(trade.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(); - // attempt creating reserve tx - try { - synchronized (HavenoUtils.getWalletFunctionLock()) { - for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) { - MoneroRpcConnection sourceConnection = trade.getXmrConnectionService().getConnection(); - try { - reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, takerFee, sendAmount, securityDeposit, returnAddress, false, null); - } catch (Exception e) { - log.warn("Error creating reserve tx, tradeId={}, attempt={}/{}, error={}", trade.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); - trade.getXmrWalletService().handleWalletError(e, sourceConnection); + // attempt creating reserve tx + try { + synchronized (HavenoUtils.getWalletFunctionLock()) { + for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) { + MoneroRpcConnection sourceConnection = trade.getXmrConnectionService().getConnection(); + try { + reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, takerFee, sendAmount, securityDeposit, returnAddress, false, null); + } catch (Exception e) { + log.warn("Error creating reserve tx, tradeId={}, attempt={}/{}, error={}", trade.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); + trade.getXmrWalletService().handleWalletError(e, sourceConnection); + if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating reserve tx, tradeId=" + trade.getShortId()); + if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; + HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying + } + + // check for timeout if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating reserve tx, tradeId=" + trade.getShortId()); - if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; - HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying + if (reserveTx != null) break; } - - // check for timeout - if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating reserve tx, tradeId=" + trade.getShortId()); - if (reserveTx != null) break; } - } - } catch (Exception e) { + } catch (Exception e) { - // reset state with wallet lock - model.getXmrWalletService().resetAddressEntriesForTrade(trade.getId()); - if (reserveTx != null) { - model.getXmrWalletService().thawOutputs(HavenoUtils.getInputKeyImages(reserveTx)); - trade.getSelf().setReserveTxKeyImages(null); + // reset state with wallet lock + model.getXmrWalletService().resetAddressEntriesForTrade(trade.getId()); + if (reserveTx != null) { + model.getXmrWalletService().thawOutputs(HavenoUtils.getInputKeyImages(reserveTx)); + trade.getSelf().setReserveTxKeyImages(null); + } + + throw e; } - throw e; + // reset protocol timeout + trade.startProtocolTimeout(); + + // update trade state + trade.getTaker().setReserveTxHash(reserveTx.getHash()); + trade.getTaker().setReserveTxHex(reserveTx.getFullHex()); + trade.getTaker().setReserveTxKey(reserveTx.getKey()); + trade.getTaker().setReserveTxKeyImages(HavenoUtils.getInputKeyImages(reserveTx)); } - - - // reset protocol timeout - trade.startProtocolTimeout(); - - // update trade state - trade.getTaker().setReserveTxHash(reserveTx.getHash()); - trade.getTaker().setReserveTxHex(reserveTx.getFullHex()); - trade.getTaker().setReserveTxKey(reserveTx.getKey()); - trade.getTaker().setReserveTxKeyImages(HavenoUtils.getInputKeyImages(reserveTx)); } // save process state diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerSendInitTradeRequestToArbitrator.java b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerSendInitTradeRequestToArbitrator.java index b5a6e362..eb86f151 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerSendInitTradeRequestToArbitrator.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerSendInitTradeRequestToArbitrator.java @@ -48,7 +48,9 @@ public class TakerSendInitTradeRequestToArbitrator extends TradeTask { InitTradeRequest sourceRequest = (InitTradeRequest) processModel.getTradeMessage(); // arbitrator's InitTradeRequest to taker checkNotNull(sourceRequest); checkTradeId(processModel.getOfferId(), sourceRequest); - if (trade.getSelf().getReserveTxHash() == null || trade.getSelf().getReserveTxHash().isEmpty()) throw new IllegalStateException("Reserve tx id is not initialized: " + trade.getSelf().getReserveTxHash()); + if (!trade.isBuyerAsTakerWithoutDeposit() && trade.getSelf().getReserveTxHash() == null) { + throw new IllegalStateException("Taker reserve tx id is not initialized: " + trade.getSelf().getReserveTxHash()); + } // create request to arbitrator Offer offer = processModel.getOffer(); @@ -73,7 +75,8 @@ public class TakerSendInitTradeRequestToArbitrator extends TradeTask { trade.getSelf().getReserveTxHash(), trade.getSelf().getReserveTxHex(), trade.getSelf().getReserveTxKey(), - model.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).get().getAddressString()); + model.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).get().getAddressString(), + trade.getChallenge()); // send request to arbitrator log.info("Sending {} with offerId {} and uid {} to arbitrator {}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getOfferId(), arbitratorRequest.getUid(), trade.getArbitrator().getNodeAddress()); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerSendInitTradeRequestToMaker.java b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerSendInitTradeRequestToMaker.java index c6315eb1..1118cc34 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerSendInitTradeRequestToMaker.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerSendInitTradeRequestToMaker.java @@ -47,7 +47,9 @@ public class TakerSendInitTradeRequestToMaker extends TradeTask { runInterceptHook(); // verify trade state - if (trade.getSelf().getReserveTxHash() == null || trade.getSelf().getReserveTxHash().isEmpty()) throw new IllegalStateException("Reserve tx id is not initialized: " + trade.getSelf().getReserveTxHash()); + if (!trade.isBuyerAsTakerWithoutDeposit() && trade.getSelf().getReserveTxHash() == null) { + throw new IllegalStateException("Taker reserve tx id is not initialized: " + trade.getSelf().getReserveTxHash()); + } // collect fields Offer offer = model.getOffer(); @@ -55,6 +57,7 @@ public class TakerSendInitTradeRequestToMaker extends TradeTask { P2PService p2PService = processModel.getP2PService(); XmrWalletService walletService = model.getXmrWalletService(); String payoutAddress = walletService.getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(); + String challenge = model.getChallenge(); // taker signs offer using offer id as nonce to avoid challenge protocol byte[] sig = HavenoUtils.sign(p2PService.getKeyRing(), offer.getId()); @@ -81,7 +84,8 @@ public class TakerSendInitTradeRequestToMaker extends TradeTask { null, // reserve tx not sent from taker to maker null, null, - payoutAddress); + payoutAddress, + challenge); // send request to maker log.info("Sending {} with offerId {} and uid {} to maker {}", makerRequest.getClass().getSimpleName(), makerRequest.getOfferId(), makerRequest.getUid(), trade.getMaker().getNodeAddress()); diff --git a/core/src/main/java/haveno/core/user/Preferences.java b/core/src/main/java/haveno/core/user/Preferences.java index 3c09126c..a3756041 100644 --- a/core/src/main/java/haveno/core/user/Preferences.java +++ b/core/src/main/java/haveno/core/user/Preferences.java @@ -616,14 +616,14 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid requestPersistence(); } - public void setBuyerSecurityDepositAsPercent(double buyerSecurityDepositAsPercent, PaymentAccount paymentAccount) { - double max = Restrictions.getMaxBuyerSecurityDepositAsPercent(); - double min = Restrictions.getMinBuyerSecurityDepositAsPercent(); + public void setSecurityDepositAsPercent(double securityDepositAsPercent, PaymentAccount paymentAccount) { + double max = Restrictions.getMaxSecurityDepositAsPercent(); + double min = Restrictions.getMinSecurityDepositAsPercent(); if (PaymentAccountUtil.isCryptoCurrencyAccount(paymentAccount)) - prefPayload.setBuyerSecurityDepositAsPercentForCrypto(Math.min(max, Math.max(min, buyerSecurityDepositAsPercent))); + prefPayload.setSecurityDepositAsPercentForCrypto(Math.min(max, Math.max(min, securityDepositAsPercent))); else - prefPayload.setBuyerSecurityDepositAsPercent(Math.min(max, Math.max(min, buyerSecurityDepositAsPercent))); + prefPayload.setSecurityDepositAsPercent(Math.min(max, Math.max(min, securityDepositAsPercent))); requestPersistence(); } @@ -755,6 +755,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid requestPersistence(); } + public void setShowPrivateOffers(boolean value) { + prefPayload.setShowPrivateOffers(value); + requestPersistence(); + } + public void setDenyApiTaker(boolean value) { prefPayload.setDenyApiTaker(value); requestPersistence(); @@ -838,16 +843,16 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid return prefPayload.isSplitOfferOutput(); } - public double getBuyerSecurityDepositAsPercent(PaymentAccount paymentAccount) { + public double getSecurityDepositAsPercent(PaymentAccount paymentAccount) { double value = PaymentAccountUtil.isCryptoCurrencyAccount(paymentAccount) ? - prefPayload.getBuyerSecurityDepositAsPercentForCrypto() : prefPayload.getBuyerSecurityDepositAsPercent(); + prefPayload.getSecurityDepositAsPercentForCrypto() : prefPayload.getSecurityDepositAsPercent(); - if (value < Restrictions.getMinBuyerSecurityDepositAsPercent()) { - value = Restrictions.getMinBuyerSecurityDepositAsPercent(); - setBuyerSecurityDepositAsPercent(value, paymentAccount); + if (value < Restrictions.getMinSecurityDepositAsPercent()) { + value = Restrictions.getMinSecurityDepositAsPercent(); + setSecurityDepositAsPercent(value, paymentAccount); } - return value == 0 ? Restrictions.getDefaultBuyerSecurityDepositAsPercent() : value; + return value == 0 ? Restrictions.getDefaultSecurityDepositAsPercent() : value; } @Override diff --git a/core/src/main/java/haveno/core/user/PreferencesPayload.java b/core/src/main/java/haveno/core/user/PreferencesPayload.java index 6d3d41f3..44e6aef5 100644 --- a/core/src/main/java/haveno/core/user/PreferencesPayload.java +++ b/core/src/main/java/haveno/core/user/PreferencesPayload.java @@ -41,7 +41,7 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; -import static haveno.core.xmr.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; +import static haveno.core.xmr.wallet.Restrictions.getDefaultSecurityDepositAsPercent; @Slf4j @Data @@ -120,10 +120,10 @@ public final class PreferencesPayload implements PersistableEnvelope { private String rpcPw; @Nullable private String takeOfferSelectedPaymentAccountId; - private double buyerSecurityDepositAsPercent = getDefaultBuyerSecurityDepositAsPercent(); + private double securityDepositAsPercent = getDefaultSecurityDepositAsPercent(); private int ignoreDustThreshold = 600; private int clearDataAfterDays = Preferences.CLEAR_DATA_AFTER_DAYS_INITIAL; - private double buyerSecurityDepositAsPercentForCrypto = getDefaultBuyerSecurityDepositAsPercent(); + private double securityDepositAsPercentForCrypto = getDefaultSecurityDepositAsPercent(); private int blockNotifyPort; private boolean tacAcceptedV120; private double bsqAverageTrimThreshold = 0.05; @@ -134,6 +134,7 @@ public final class PreferencesPayload implements PersistableEnvelope { // Added in 1.5.5 private boolean hideNonAccountPaymentMethods; private boolean showOffersMatchingMyAccounts; + private boolean showPrivateOffers; private boolean denyApiTaker; private boolean notifyOnPreRelease; @@ -193,10 +194,10 @@ public final class PreferencesPayload implements PersistableEnvelope { .setUseStandbyMode(useStandbyMode) .setUseSoundForNotifications(useSoundForNotifications) .setUseSoundForNotificationsInitialized(useSoundForNotificationsInitialized) - .setBuyerSecurityDepositAsPercent(buyerSecurityDepositAsPercent) + .setSecurityDepositAsPercent(securityDepositAsPercent) .setIgnoreDustThreshold(ignoreDustThreshold) .setClearDataAfterDays(clearDataAfterDays) - .setBuyerSecurityDepositAsPercentForCrypto(buyerSecurityDepositAsPercentForCrypto) + .setSecurityDepositAsPercentForCrypto(securityDepositAsPercentForCrypto) .setBlockNotifyPort(blockNotifyPort) .setTacAcceptedV120(tacAcceptedV120) .setBsqAverageTrimThreshold(bsqAverageTrimThreshold) @@ -205,6 +206,7 @@ public final class PreferencesPayload implements PersistableEnvelope { .collect(Collectors.toList())) .setHideNonAccountPaymentMethods(hideNonAccountPaymentMethods) .setShowOffersMatchingMyAccounts(showOffersMatchingMyAccounts) + .setShowPrivateOffers(showPrivateOffers) .setDenyApiTaker(denyApiTaker) .setNotifyOnPreRelease(notifyOnPreRelease); @@ -297,10 +299,10 @@ public final class PreferencesPayload implements PersistableEnvelope { proto.getRpcUser().isEmpty() ? null : proto.getRpcUser(), proto.getRpcPw().isEmpty() ? null : proto.getRpcPw(), proto.getTakeOfferSelectedPaymentAccountId().isEmpty() ? null : proto.getTakeOfferSelectedPaymentAccountId(), - proto.getBuyerSecurityDepositAsPercent(), + proto.getSecurityDepositAsPercent(), proto.getIgnoreDustThreshold(), proto.getClearDataAfterDays(), - proto.getBuyerSecurityDepositAsPercentForCrypto(), + proto.getSecurityDepositAsPercentForCrypto(), proto.getBlockNotifyPort(), proto.getTacAcceptedV120(), proto.getBsqAverageTrimThreshold(), @@ -310,6 +312,7 @@ public final class PreferencesPayload implements PersistableEnvelope { .collect(Collectors.toList())), proto.getHideNonAccountPaymentMethods(), proto.getShowOffersMatchingMyAccounts(), + proto.getShowPrivateOffers(), proto.getDenyApiTaker(), proto.getNotifyOnPreRelease(), XmrNodeSettings.fromProto(proto.getXmrNodeSettings()) diff --git a/core/src/main/java/haveno/core/util/coin/CoinUtil.java b/core/src/main/java/haveno/core/util/coin/CoinUtil.java index ec7ff113..bf194b3e 100644 --- a/core/src/main/java/haveno/core/util/coin/CoinUtil.java +++ b/core/src/main/java/haveno/core/util/coin/CoinUtil.java @@ -47,35 +47,35 @@ public class CoinUtil { } /** - * @param value Btc amount to be converted to percent value. E.g. 0.01 BTC is 1% (of 1 BTC) + * @param value Xmr amount to be converted to percent value. E.g. 0.01 XMR is 1% (of 1 XMR) * @return The percentage value as double (e.g. 1% is 0.01) */ - public static double getAsPercentPerBtc(BigInteger value) { - return getAsPercentPerBtc(value, HavenoUtils.xmrToAtomicUnits(1.0)); + public static double getAsPercentPerXmr(BigInteger value) { + return getAsPercentPerXmr(value, HavenoUtils.xmrToAtomicUnits(1.0)); } /** - * @param part Btc amount to be converted to percent value, based on total value passed. - * E.g. 0.1 BTC is 25% (of 0.4 BTC) - * @param total Total Btc amount the percentage part is calculated from + * @param part Xmr amount to be converted to percent value, based on total value passed. + * E.g. 0.1 XMR is 25% (of 0.4 XMR) + * @param total Total Xmr amount the percentage part is calculated from * * @return The percentage value as double (e.g. 1% is 0.01) */ - public static double getAsPercentPerBtc(BigInteger part, BigInteger total) { + public static double getAsPercentPerXmr(BigInteger part, BigInteger total) { return MathUtils.roundDouble(HavenoUtils.divide(part == null ? BigInteger.ZERO : part, total == null ? BigInteger.valueOf(1) : total), 4); } /** * @param percent The percentage value as double (e.g. 1% is 0.01) * @param amount The amount as atomic units for the percentage calculation - * @return The percentage as atomic units (e.g. 1% of 1 BTC is 0.01 BTC) + * @return The percentage as atomic units (e.g. 1% of 1 XMR is 0.01 XMR) */ public static BigInteger getPercentOfAmount(double percent, BigInteger amount) { if (amount == null) amount = BigInteger.ZERO; return BigDecimal.valueOf(percent).multiply(new BigDecimal(amount)).setScale(8, RoundingMode.DOWN).toBigInteger(); } - public static BigInteger getRoundedAmount(BigInteger amount, Price price, long maxTradeLimit, String currencyCode, String paymentMethodId) { + public static BigInteger getRoundedAmount(BigInteger amount, Price price, Long maxTradeLimit, String currencyCode, String paymentMethodId) { if (PaymentMethod.isRoundedForAtmCash(paymentMethodId)) { return getRoundedAtmCashAmount(amount, price, maxTradeLimit); } else if (CurrencyUtil.isVolumeRoundedToNearestUnit(currencyCode)) { @@ -86,7 +86,7 @@ public class CoinUtil { return amount; } - public static BigInteger getRoundedAtmCashAmount(BigInteger amount, Price price, long maxTradeLimit) { + public static BigInteger getRoundedAtmCashAmount(BigInteger amount, Price price, Long maxTradeLimit) { return getAdjustedAmount(amount, price, maxTradeLimit, 10); } @@ -99,11 +99,11 @@ public class CoinUtil { * @param maxTradeLimit The max. trade limit of the users account, in atomic units. * @return The adjusted amount */ - public static BigInteger getRoundedAmountUnit(BigInteger amount, Price price, long maxTradeLimit) { + public static BigInteger getRoundedAmountUnit(BigInteger amount, Price price, Long maxTradeLimit) { return getAdjustedAmount(amount, price, maxTradeLimit, 1); } - public static BigInteger getRoundedAmount4Decimals(BigInteger amount, Price price, long maxTradeLimit) { + public static BigInteger getRoundedAmount4Decimals(BigInteger amount, Price price, Long maxTradeLimit) { DecimalFormat decimalFormat = new DecimalFormat("#.####"); double roundedXmrAmount = Double.parseDouble(decimalFormat.format(HavenoUtils.atomicUnitsToXmr(amount))); return HavenoUtils.xmrToAtomicUnits(roundedXmrAmount); @@ -121,7 +121,7 @@ public class CoinUtil { * @return The adjusted amount */ @VisibleForTesting - static BigInteger getAdjustedAmount(BigInteger amount, Price price, long maxTradeLimit, int factor) { + static BigInteger getAdjustedAmount(BigInteger amount, Price price, Long maxTradeLimit, int factor) { checkArgument( amount.longValueExact() >= Restrictions.getMinTradeAmount().longValueExact(), "amount needs to be above minimum of " + HavenoUtils.atomicUnitsToXmr(Restrictions.getMinTradeAmount()) + " xmr" @@ -163,11 +163,13 @@ public class CoinUtil { // If we are above our trade limit we reduce the amount by the smallestUnitForAmount BigInteger smallestUnitForAmountUnadjusted = price.getAmountByVolume(smallestUnitForVolume); - while (adjustedAmount > maxTradeLimit) { - adjustedAmount -= smallestUnitForAmountUnadjusted.longValueExact(); + if (maxTradeLimit != null) { + while (adjustedAmount > maxTradeLimit) { + adjustedAmount -= smallestUnitForAmountUnadjusted.longValueExact(); + } } adjustedAmount = Math.max(minTradeAmount, adjustedAmount); - adjustedAmount = Math.min(maxTradeLimit, adjustedAmount); + if (maxTradeLimit != null) adjustedAmount = Math.min(maxTradeLimit, adjustedAmount); return BigInteger.valueOf(adjustedAmount); } } diff --git a/core/src/main/java/haveno/core/xmr/wallet/Restrictions.java b/core/src/main/java/haveno/core/xmr/wallet/Restrictions.java index a70d2cc1..b270762d 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/Restrictions.java +++ b/core/src/main/java/haveno/core/xmr/wallet/Restrictions.java @@ -24,11 +24,13 @@ import org.bitcoinj.core.Coin; import java.math.BigInteger; public class Restrictions { + + // configure restrictions + public static final double MIN_SECURITY_DEPOSIT_PCT = 0.15; + public static final double MAX_SECURITY_DEPOSIT_PCT = 0.5; public static BigInteger MIN_TRADE_AMOUNT = HavenoUtils.xmrToAtomicUnits(0.1); - public static BigInteger MIN_BUYER_SECURITY_DEPOSIT = HavenoUtils.xmrToAtomicUnits(0.1); - // For the seller we use a fixed one as there is no way the seller can cancel the trade - // To make it editable would just increase complexity. - public static BigInteger MIN_SELLER_SECURITY_DEPOSIT = MIN_BUYER_SECURITY_DEPOSIT; + public static BigInteger MIN_SECURITY_DEPOSIT = HavenoUtils.xmrToAtomicUnits(0.1); + // At mediation we require a min. payout to the losing party to keep incentive for the trader to accept the // mediated payout. For Refund agent cases we do not have that restriction. private static BigInteger MIN_REFUND_AT_MEDIATED_DISPUTE; @@ -53,31 +55,20 @@ public class Restrictions { return MIN_TRADE_AMOUNT; } - public static double getDefaultBuyerSecurityDepositAsPercent() { - return 0.15; // 15% of trade amount. + public static double getDefaultSecurityDepositAsPercent() { + return MIN_SECURITY_DEPOSIT_PCT; } - public static double getMinBuyerSecurityDepositAsPercent() { - return 0.15; // 15% of trade amount. + public static double getMinSecurityDepositAsPercent() { + return MIN_SECURITY_DEPOSIT_PCT; } - public static double getMaxBuyerSecurityDepositAsPercent() { - return 0.5; // 50% of trade amount. For a 1 BTC trade it is about 3500 USD @ 7000 USD/BTC + public static double getMaxSecurityDepositAsPercent() { + return MAX_SECURITY_DEPOSIT_PCT; } - // We use MIN_BUYER_SECURITY_DEPOSIT as well as lower bound in case of small trade amounts. - // So 0.0005 BTC is the min. buyer security deposit even with amount of 0.0001 BTC and 0.05% percentage value. - public static BigInteger getMinBuyerSecurityDeposit() { - return MIN_BUYER_SECURITY_DEPOSIT; - } - - - public static double getSellerSecurityDepositAsPercent() { - return 0.15; // 15% of trade amount. - } - - public static BigInteger getMinSellerSecurityDeposit() { - return MIN_SELLER_SECURITY_DEPOSIT; + public static BigInteger getMinSecurityDeposit() { + return MIN_SECURITY_DEPOSIT; } // This value must be lower than MIN_BUYER_SECURITY_DEPOSIT and SELLER_SECURITY_DEPOSIT diff --git a/core/src/main/resources/bip39_english.txt b/core/src/main/resources/bip39_english.txt new file mode 100644 index 00000000..942040ed --- /dev/null +++ b/core/src/main/resources/bip39_english.txt @@ -0,0 +1,2048 @@ +abandon +ability +able +about +above +absent +absorb +abstract +absurd +abuse +access +accident +account +accuse +achieve +acid +acoustic +acquire +across +act +action +actor +actress +actual +adapt +add +addict +address +adjust +admit +adult +advance +advice +aerobic +affair +afford +afraid +again +age +agent +agree +ahead +aim +air +airport +aisle +alarm +album +alcohol +alert +alien +all +alley +allow +almost +alone +alpha +already +also +alter +always +amateur +amazing +among +amount +amused +analyst +anchor +ancient +anger +angle +angry +animal +ankle +announce +annual +another +answer +antenna +antique +anxiety +any +apart +apology +appear +apple +approve +april +arch +arctic +area +arena +argue +arm +armed +armor +army +around +arrange +arrest +arrive +arrow +art +artefact +artist +artwork +ask +aspect +assault +asset +assist +assume +asthma +athlete +atom +attack +attend +attitude +attract +auction +audit +august +aunt +author +auto +autumn +average +avocado +avoid +awake +aware +away +awesome +awful +awkward +axis +baby +bachelor +bacon +badge +bag +balance +balcony +ball +bamboo +banana +banner +bar +barely +bargain +barrel +base +basic +basket +battle +beach +bean +beauty +because +become +beef +before +begin +behave +behind +believe +below +belt +bench +benefit +best +betray +better +between +beyond +bicycle +bid +bike +bind +biology +bird +birth +bitter +black +blade +blame +blanket +blast +bleak +bless +blind +blood +blossom +blouse +blue +blur +blush +board +boat +body +boil +bomb +bone +bonus +book +boost +border +boring +borrow +boss +bottom +bounce +box +boy +bracket +brain +brand +brass +brave +bread +breeze +brick +bridge +brief +bright +bring +brisk +broccoli +broken +bronze +broom +brother +brown +brush +bubble +buddy +budget +buffalo +build +bulb +bulk +bullet +bundle +bunker +burden +burger +burst +bus +business +busy +butter +buyer +buzz +cabbage +cabin +cable +cactus +cage +cake +call +calm +camera +camp +can +canal +cancel +candy +cannon +canoe +canvas +canyon +capable +capital +captain +car +carbon +card +cargo +carpet +carry +cart +case +cash +casino +castle +casual +cat +catalog +catch +category +cattle +caught +cause +caution +cave +ceiling +celery +cement +census +century +cereal +certain +chair +chalk +champion +change +chaos +chapter +charge +chase +chat +cheap +check +cheese +chef +cherry +chest +chicken +chief +child +chimney +choice +choose +chronic +chuckle +chunk +churn +cigar +cinnamon +circle +citizen +city +civil +claim +clap +clarify +claw +clay +clean +clerk +clever +click +client +cliff +climb +clinic +clip +clock +clog +close +cloth +cloud +clown +club +clump +cluster +clutch +coach +coast +coconut +code +coffee +coil +coin +collect +color +column +combine +come +comfort +comic +common +company +concert +conduct +confirm +congress +connect +consider +control +convince +cook +cool +copper +copy +coral +core +corn +correct +cost +cotton +couch +country +couple +course +cousin +cover +coyote +crack +cradle +craft +cram +crane +crash +crater +crawl +crazy +cream +credit +creek +crew +cricket +crime +crisp +critic +crop +cross +crouch +crowd +crucial +cruel +cruise +crumble +crunch +crush +cry +crystal +cube +culture +cup +cupboard +curious +current +curtain +curve +cushion +custom +cute +cycle +dad +damage +damp +dance +danger +daring +dash +daughter +dawn +day +deal +debate +debris +decade +december +decide +decline +decorate +decrease +deer +defense +define +defy +degree +delay +deliver +demand +demise +denial +dentist +deny +depart +depend +deposit +depth +deputy +derive +describe +desert +design +desk +despair +destroy +detail +detect +develop +device +devote +diagram +dial +diamond +diary +dice +diesel +diet +differ +digital +dignity +dilemma +dinner +dinosaur +direct +dirt +disagree +discover +disease +dish +dismiss +disorder +display +distance +divert +divide +divorce +dizzy +doctor +document +dog +doll +dolphin +domain +donate +donkey +donor +door +dose +double +dove +draft +dragon +drama +drastic +draw +dream +dress +drift +drill +drink +drip +drive +drop +drum +dry +duck +dumb +dune +during +dust +dutch +duty +dwarf +dynamic +eager +eagle +early +earn +earth +easily +east +easy +echo +ecology +economy +edge +edit +educate +effort +egg +eight +either +elbow +elder +electric +elegant +element +elephant +elevator +elite +else +embark +embody +embrace +emerge +emotion +employ +empower +empty +enable +enact +end +endless +endorse +enemy +energy +enforce +engage +engine +enhance +enjoy +enlist +enough +enrich +enroll +ensure +enter +entire +entry +envelope +episode +equal +equip +era +erase +erode +erosion +error +erupt +escape +essay +essence +estate +eternal +ethics +evidence +evil +evoke +evolve +exact +example +excess +exchange +excite +exclude +excuse +execute +exercise +exhaust +exhibit +exile +exist +exit +exotic +expand +expect +expire +explain +expose +express +extend +extra +eye +eyebrow +fabric +face +faculty +fade +faint +faith +fall +false +fame +family +famous +fan +fancy +fantasy +farm +fashion +fat +fatal +father +fatigue +fault +favorite +feature +february +federal +fee +feed +feel +female +fence +festival +fetch +fever +few +fiber +fiction +field +figure +file +film +filter +final +find +fine +finger +finish +fire +firm +first +fiscal +fish +fit +fitness +fix +flag +flame +flash +flat +flavor +flee +flight +flip +float +flock +floor +flower +fluid +flush +fly +foam +focus +fog +foil +fold +follow +food +foot +force +forest +forget +fork +fortune +forum +forward +fossil +foster +found +fox +fragile +frame +frequent +fresh +friend +fringe +frog +front +frost +frown +frozen +fruit +fuel +fun +funny +furnace +fury +future +gadget +gain +galaxy +gallery +game +gap +garage +garbage +garden +garlic +garment +gas +gasp +gate +gather +gauge +gaze +general +genius +genre +gentle +genuine +gesture +ghost +giant +gift +giggle +ginger +giraffe +girl +give +glad +glance +glare +glass +glide +glimpse +globe +gloom +glory +glove +glow +glue +goat +goddess +gold +good +goose +gorilla +gospel +gossip +govern +gown +grab +grace +grain +grant +grape +grass +gravity +great +green +grid +grief +grit +grocery +group +grow +grunt +guard +guess +guide +guilt +guitar +gun +gym +habit +hair +half +hammer +hamster +hand +happy +harbor +hard +harsh +harvest +hat +have +hawk +hazard +head +health +heart +heavy +hedgehog +height +hello +helmet +help +hen +hero +hidden +high +hill +hint +hip +hire +history +hobby +hockey +hold +hole +holiday +hollow +home +honey +hood +hope +horn +horror +horse +hospital +host +hotel +hour +hover +hub +huge +human +humble +humor +hundred +hungry +hunt +hurdle +hurry +hurt +husband +hybrid +ice +icon +idea +identify +idle +ignore +ill +illegal +illness +image +imitate +immense +immune +impact +impose +improve +impulse +inch +include +income +increase +index +indicate +indoor +industry +infant +inflict +inform +inhale +inherit +initial +inject +injury +inmate +inner +innocent +input +inquiry +insane +insect +inside +inspire +install +intact +interest +into +invest +invite +involve +iron +island +isolate +issue +item +ivory +jacket +jaguar +jar +jazz +jealous +jeans +jelly +jewel +job +join +joke +journey +joy +judge +juice +jump +jungle +junior +junk +just +kangaroo +keen +keep +ketchup +key +kick +kid +kidney +kind +kingdom +kiss +kit +kitchen +kite +kitten +kiwi +knee +knife +knock +know +lab +label +labor +ladder +lady +lake +lamp +language +laptop +large +later +latin +laugh +laundry +lava +law +lawn +lawsuit +layer +lazy +leader +leaf +learn +leave +lecture +left +leg +legal +legend +leisure +lemon +lend +length +lens +leopard +lesson +letter +level +liar +liberty +library +license +life +lift +light +like +limb +limit +link +lion +liquid +list +little +live +lizard +load +loan +lobster +local +lock +logic +lonely +long +loop +lottery +loud +lounge +love +loyal +lucky +luggage +lumber +lunar +lunch +luxury +lyrics +machine +mad +magic +magnet +maid +mail +main +major +make +mammal +man +manage +mandate +mango +mansion +manual +maple +marble +march +margin +marine +market +marriage +mask +mass +master +match +material +math +matrix +matter +maximum +maze +meadow +mean +measure +meat +mechanic +medal +media +melody +melt +member +memory +mention +menu +mercy +merge +merit +merry +mesh +message +metal +method +middle +midnight +milk +million +mimic +mind +minimum +minor +minute +miracle +mirror +misery +miss +mistake +mix +mixed +mixture +mobile +model +modify +mom +moment +monitor +monkey +monster +month +moon +moral +more +morning +mosquito +mother +motion +motor +mountain +mouse +move +movie +much +muffin +mule +multiply +muscle +museum +mushroom +music +must +mutual +myself +mystery +myth +naive +name +napkin +narrow +nasty +nation +nature +near +neck +need +negative +neglect +neither +nephew +nerve +nest +net +network +neutral +never +news +next +nice +night +noble +noise +nominee +noodle +normal +north +nose +notable +note +nothing +notice +novel +now +nuclear +number +nurse +nut +oak +obey +object +oblige +obscure +observe +obtain +obvious +occur +ocean +october +odor +off +offer +office +often +oil +okay +old +olive +olympic +omit +once +one +onion +online +only +open +opera +opinion +oppose +option +orange +orbit +orchard +order +ordinary +organ +orient +original +orphan +ostrich +other +outdoor +outer +output +outside +oval +oven +over +own +owner +oxygen +oyster +ozone +pact +paddle +page +pair +palace +palm +panda +panel +panic +panther +paper +parade +parent +park +parrot +party +pass +patch +path +patient +patrol +pattern +pause +pave +payment +peace +peanut +pear +peasant +pelican +pen +penalty +pencil +people +pepper +perfect +permit +person +pet +phone +photo +phrase +physical +piano +picnic +picture +piece +pig +pigeon +pill +pilot +pink +pioneer +pipe +pistol +pitch +pizza +place +planet +plastic +plate +play +please +pledge +pluck +plug +plunge +poem +poet +point +polar +pole +police +pond +pony +pool +popular +portion +position +possible +post +potato +pottery +poverty +powder +power +practice +praise +predict +prefer +prepare +present +pretty +prevent +price +pride +primary +print +priority +prison +private +prize +problem +process +produce +profit +program +project +promote +proof +property +prosper +protect +proud +provide +public +pudding +pull +pulp +pulse +pumpkin +punch +pupil +puppy +purchase +purity +purpose +purse +push +put +puzzle +pyramid +quality +quantum +quarter +question +quick +quit +quiz +quote +rabbit +raccoon +race +rack +radar +radio +rail +rain +raise +rally +ramp +ranch +random +range +rapid +rare +rate +rather +raven +raw +razor +ready +real +reason +rebel +rebuild +recall +receive +recipe +record +recycle +reduce +reflect +reform +refuse +region +regret +regular +reject +relax +release +relief +rely +remain +remember +remind +remove +render +renew +rent +reopen +repair +repeat +replace +report +require +rescue +resemble +resist +resource +response +result +retire +retreat +return +reunion +reveal +review +reward +rhythm +rib +ribbon +rice +rich +ride +ridge +rifle +right +rigid +ring +riot +ripple +risk +ritual +rival +river +road +roast +robot +robust +rocket +romance +roof +rookie +room +rose +rotate +rough +round +route +royal +rubber +rude +rug +rule +run +runway +rural +sad +saddle +sadness +safe +sail +salad +salmon +salon +salt +salute +same +sample +sand +satisfy +satoshi +sauce +sausage +save +say +scale +scan +scare +scatter +scene +scheme +school +science +scissors +scorpion +scout +scrap +screen +script +scrub +sea +search +season +seat +second +secret +section +security +seed +seek +segment +select +sell +seminar +senior +sense +sentence +series +service +session +settle +setup +seven +shadow +shaft +shallow +share +shed +shell +sheriff +shield +shift +shine +ship +shiver +shock +shoe +shoot +shop +short +shoulder +shove +shrimp +shrug +shuffle +shy +sibling +sick +side +siege +sight +sign +silent +silk +silly +silver +similar +simple +since +sing +siren +sister +situate +six +size +skate +sketch +ski +skill +skin +skirt +skull +slab +slam +sleep +slender +slice +slide +slight +slim +slogan +slot +slow +slush +small +smart +smile +smoke +smooth +snack +snake +snap +sniff +snow +soap +soccer +social +sock +soda +soft +solar +soldier +solid +solution +solve +someone +song +soon +sorry +sort +soul +sound +soup +source +south +space +spare +spatial +spawn +speak +special +speed +spell +spend +sphere +spice +spider +spike +spin +spirit +split +spoil +sponsor +spoon +sport +spot +spray +spread +spring +spy +square +squeeze +squirrel +stable +stadium +staff +stage +stairs +stamp +stand +start +state +stay +steak +steel +stem +step +stereo +stick +still +sting +stock +stomach +stone +stool +story +stove +strategy +street +strike +strong +struggle +student +stuff +stumble +style +subject +submit +subway +success +such +sudden +suffer +sugar +suggest +suit +summer +sun +sunny +sunset +super +supply +supreme +sure +surface +surge +surprise +surround +survey +suspect +sustain +swallow +swamp +swap +swarm +swear +sweet +swift +swim +swing +switch +sword +symbol +symptom +syrup +system +table +tackle +tag +tail +talent +talk +tank +tape +target +task +taste +tattoo +taxi +teach +team +tell +ten +tenant +tennis +tent +term +test +text +thank +that +theme +then +theory +there +they +thing +this +thought +three +thrive +throw +thumb +thunder +ticket +tide +tiger +tilt +timber +time +tiny +tip +tired +tissue +title +toast +tobacco +today +toddler +toe +together +toilet +token +tomato +tomorrow +tone +tongue +tonight +tool +tooth +top +topic +topple +torch +tornado +tortoise +toss +total +tourist +toward +tower +town +toy +track +trade +traffic +tragic +train +transfer +trap +trash +travel +tray +treat +tree +trend +trial +tribe +trick +trigger +trim +trip +trophy +trouble +truck +true +truly +trumpet +trust +truth +try +tube +tuition +tumble +tuna +tunnel +turkey +turn +turtle +twelve +twenty +twice +twin +twist +two +type +typical +ugly +umbrella +unable +unaware +uncle +uncover +under +undo +unfair +unfold +unhappy +uniform +unique +unit +universe +unknown +unlock +until +unusual +unveil +update +upgrade +uphold +upon +upper +upset +urban +urge +usage +use +used +useful +useless +usual +utility +vacant +vacuum +vague +valid +valley +valve +van +vanish +vapor +various +vast +vault +vehicle +velvet +vendor +venture +venue +verb +verify +version +very +vessel +veteran +viable +vibrant +vicious +victory +video +view +village +vintage +violin +virtual +virus +visa +visit +visual +vital +vivid +vocal +voice +void +volcano +volume +vote +voyage +wage +wagon +wait +walk +wall +walnut +want +warfare +warm +warrior +wash +wasp +waste +water +wave +way +wealth +weapon +wear +weasel +weather +web +wedding +weekend +weird +welcome +west +wet +whale +what +wheat +wheel +when +where +whip +whisper +wide +width +wife +wild +will +win +window +wine +wing +wink +winner +winter +wire +wisdom +wise +wish +witness +wolf +woman +wonder +wood +wool +word +work +world +worry +worth +wrap +wreck +wrestle +wrist +write +wrong +yard +year +yellow +you +young +youth +zebra +zero +zone +zoo diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index c0d2d88f..86fd962d 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -43,6 +43,8 @@ shared.buyMonero=Buy Monero shared.sellMonero=Sell Monero shared.buyCurrency=Buy {0} shared.sellCurrency=Sell {0} +shared.buyCurrencyLocked=Buy {0} 🔒 +shared.sellCurrencyLocked=Sell {0} 🔒 shared.buyingXMRWith=buying XMR with {0} shared.sellingXMRFor=selling XMR for {0} shared.buyingCurrency=buying {0} (selling XMR) @@ -349,6 +351,7 @@ market.trades.showVolumeInUSD=Show volume in USD offerbook.createOffer=Create offer offerbook.takeOffer=Take offer offerbook.takeOffer.createAccount=Create account and take offer +offerbook.takeOffer.enterChallenge=Enter the offer passphrase offerbook.trader=Trader offerbook.offerersBankId=Maker''s bank ID (BIC/SWIFT): {0} offerbook.offerersBankName=Maker''s bank name: {0} @@ -360,6 +363,8 @@ offerbook.availableOffersToSell=Sell {0} for {1} offerbook.filterByCurrency=Choose currency offerbook.filterByPaymentMethod=Choose payment method offerbook.matchingOffers=Offers matching my accounts +offerbook.filterNoDeposit=No deposit +offerbook.noDepositOffers=Offers with no deposit (passphrase required) offerbook.timeSinceSigning=Account info offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts offerbook.timeSinceSigning.info.peer=signed by a peer, waiting %d days for limits to be lifted @@ -527,7 +532,10 @@ createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) createOffer.setDepositForBothTraders=Set both traders' security deposit (%) createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} -createOffer.minSecurityDepositUsed=Min. buyer security deposit is used +createOffer.minSecurityDepositUsed=Minimum security deposit is used +createOffer.buyerAsTakerWithoutDeposit=No deposit required from buyer (passphrase protected) +createOffer.myDeposit=My security deposit (%) +createOffer.myDepositInfo=Your security deposit will be {0} #################################################################### @@ -553,6 +561,8 @@ takeOffer.fundsBox.networkFee=Total mining fees takeOffer.fundsBox.takeOfferSpinnerInfo=Taking offer: {0} takeOffer.fundsBox.paymentLabel=Haveno trade with ID {0} takeOffer.fundsBox.fundsStructure=({0} security deposit, {1} trade fee) +takeOffer.fundsBox.noFundingRequiredTitle=No funding required +takeOffer.fundsBox.noFundingRequiredDescription=Get the offer passphrase from the seller outside Haveno to take this offer. takeOffer.success.headline=You have successfully taken an offer. takeOffer.success.info=You can see the status of your trade at \"Portfolio/Open trades\". takeOffer.error.message=An error occurred when taking the offer.\n\n{0} @@ -1968,6 +1978,7 @@ offerDetailsWindow.confirm.taker=Confirm: Take offer to {0} monero offerDetailsWindow.confirm.takerCrypto=Confirm: Take offer to {0} {1} offerDetailsWindow.creationDate=Creation date offerDetailsWindow.makersOnion=Maker's onion address +offerDetailsWindow.challenge=Offer passphrase qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. @@ -2269,6 +2280,12 @@ popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign +popup.info.buyerAsTakerWithoutDeposit.headline=No deposit required from buyer +popup.info.buyerAsTakerWithoutDeposit=\ + Your offer will not require a security deposit or fee from the XMR buyer.\n\n\ + To accept your offer, you must share a passphrase with your trade partner outside Haveno.\n\n\ + The passphrase is automatically generated and shown in the offer details after creation.\ + popup.info.torMigration.msg=Your Haveno node is probably using a deprecated Tor v2 address. \ Please switch your Haveno node to a Tor v3 address. \ Make sure to back up your data directory beforehand. @@ -2408,6 +2425,7 @@ navigation.support=\"Support\" formatter.formatVolumeLabel={0} amount{1} formatter.makerTaker=Maker as {0} {1} / Taker as {2} {3} +formatter.makerTakerLocked=Maker as {0} {1} / Taker as {2} {3} 🔒 formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2} formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2} formatter.youAre=You are {0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 789eb761..85f021d4 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -40,6 +40,8 @@ shared.buyMonero=Koupit monero shared.sellMonero=Prodat monero shared.buyCurrency=Koupit {0} shared.sellCurrency=Prodat {0} +shared.buyCurrencyLocked=Koupit {0} 🔒 +shared.sellCurrencyLocked=Prodat {0} 🔒 shared.buyingXMRWith=nakoupit XMR za {0} shared.sellingXMRFor=prodat XMR za {0} shared.buyingCurrency=nakoupit {0} (prodat XMR) @@ -330,6 +332,7 @@ offerbook.createOffer=Vytvořit nabídku offerbook.takeOffer=Přijmout nabídku offerbook.takeOfferToBuy=Přijmout nabídku na nákup {0} offerbook.takeOfferToSell=Přijmout nabídku k prodeji {0} +offerbook.takeOffer.enterChallenge=Zadejte heslo nabídky offerbook.trader=Obchodník offerbook.offerersBankId=ID banky tvůrce (BIC/SWIFT): {0} offerbook.offerersBankName=Jméno banky tvůrce: {0} @@ -340,6 +343,8 @@ offerbook.availableOffers=Dostupné nabídky offerbook.filterByCurrency=Filtrovat podle měny offerbook.filterByPaymentMethod=Filtrovat podle platební metody offerbook.matchingOffers=Nabídky odpovídající mým účtům +offerbook.filterNoDeposit=Žádný vklad +offerbook.noDepositOffers=Nabídky bez zálohy (vyžaduje se heslo) offerbook.timeSinceSigning=Informace o účtu offerbook.timeSinceSigning.info=Tento účet byl ověřen a {0} offerbook.timeSinceSigning.info.arbitrator=podepsán rozhodcem a může podepisovat účty partnerů @@ -485,7 +490,10 @@ createOffer.setDepositAsBuyer=Nastavit mou kauci jako kupujícího (%) createOffer.setDepositForBothTraders=Nastavit kauci obou obchodníků (%) createOffer.securityDepositInfo=Kauce vašeho kupujícího bude {0} createOffer.securityDepositInfoAsBuyer=Vaše kauce jako kupující bude {0} -createOffer.minSecurityDepositUsed=Je použita min. záloha kupujícího +createOffer.minSecurityDepositUsed=Minimální bezpečnostní záloha je použita +createOffer.buyerAsTakerWithoutDeposit=Žádný vklad od kupujícího (chráněno heslem) +createOffer.myDeposit=Můj bezpečnostní vklad (%) +createOffer.myDepositInfo=Vaše záloha na bezpečnost bude {0} #################################################################### @@ -509,6 +517,8 @@ takeOffer.fundsBox.networkFee=Celkové poplatky za těžbu takeOffer.fundsBox.takeOfferSpinnerInfo=Přijímám nabídku: {0} takeOffer.fundsBox.paymentLabel=Haveno obchod s ID {0} takeOffer.fundsBox.fundsStructure=(kauce {0}, obchodní poplatek {1}, poplatek za těžbu {2}) +takeOffer.fundsBox.noFundingRequiredTitle=Žádné financování požadováno +takeOffer.fundsBox.noFundingRequiredDescription=Získejte passphrase nabídky od prodávajícího mimo Haveno, abyste tuto nabídku přijali. takeOffer.success.headline=Úspěšně jste přijali nabídku. takeOffer.success.info=Stav vašeho obchodu můžete vidět v \"Portfolio/Otevřené obchody\". takeOffer.error.message=Při převzetí nabídky došlo k chybě.\n\n{0} @@ -1464,6 +1474,7 @@ offerDetailsWindow.confirm.maker=Potvrďte: Umístit nabídku {0} monero offerDetailsWindow.confirm.taker=Potvrďte: Využít nabídku {0} monero offerDetailsWindow.creationDate=Datum vzniku offerDetailsWindow.makersOnion=Onion adresa tvůrce +offerDetailsWindow.challenge=Passphrase nabídky qRCodeWindow.headline=QR Kód qRCodeWindow.msg=Použijte tento QR kód k financování vaší peněženky Haveno z vaší externí peněženky. @@ -1689,6 +1700,9 @@ popup.accountSigning.unsignedPubKeys.signed=Pubkeys byly podepsány popup.accountSigning.unsignedPubKeys.result.signed=Podepsané pubkeys popup.accountSigning.unsignedPubKeys.result.failed=Nepodařilo se podepsat +popup.info.buyerAsTakerWithoutDeposit.headline=Žádný vklad není od kupujícího požadován +popup.info.buyerAsTakerWithoutDeposit=Vaše nabídka nebude vyžadovat bezpečnostní zálohu ani poplatek od kupujícího XMR.\n\nPro přijetí vaší nabídky musíte sdílet heslo se svým obchodním partnerem mimo Haveno.\n\nHeslo je automaticky vygenerováno a zobrazeno v detailech nabídky po jejím vytvoření. + #################################################################### # Notifications #################################################################### @@ -1814,6 +1828,7 @@ navigation.support=\"Podpora\" formatter.formatVolumeLabel={0} částka{1} formatter.makerTaker=Tvůrce jako {0} {1} / Příjemce jako {2} {3} +formatter.makerTakerLocked=Tvůrce jako {0} {1} / Příjemce jako {2} {3} 🔒 formatter.youAreAsMaker=Jste {1} {0} (jako tvůrce) / Příjemce je {3} {2} formatter.youAreAsTaker=Jste {1} {0} (jako příjemce) / Tvůrce je {3} {2} formatter.youAre={0}te {1} ({2}te {3}) diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 5d03c34b..e8b8584c 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -40,6 +40,8 @@ shared.buyMonero=Monero kaufen shared.sellMonero=Monero verkaufen shared.buyCurrency={0} kaufen shared.sellCurrency={0} verkaufen +shared.buyCurrencyLocked={0} kaufen 🔒 +shared.sellCurrencyLocked={0} verkaufen 🔒 shared.buyingXMRWith=kaufe XMR mit {0} shared.sellingXMRFor=verkaufe XMR für {0} shared.buyingCurrency=kaufe {0} (verkaufe XMR) @@ -330,6 +332,7 @@ offerbook.createOffer=Angebot erstellen offerbook.takeOffer=Angebot annehmen offerbook.takeOfferToBuy=Angebot annehmen {0} zu kaufen offerbook.takeOfferToSell=Angebot annehmen {0} zu verkaufen +offerbook.takeOffer.enterChallenge=Geben Sie das Angebots-Passphrase ein offerbook.trader=Händler offerbook.offerersBankId=Bankkennung des Erstellers (BIC/SWIFT): {0} offerbook.offerersBankName=Bankname des Erstellers: {0} @@ -340,6 +343,8 @@ offerbook.availableOffers=Verfügbare Angebote offerbook.filterByCurrency=Nach Währung filtern offerbook.filterByPaymentMethod=Nach Zahlungsmethode filtern offerbook.matchingOffers=Angebote die meinen Zahlungskonten entsprechen +offerbook.filterNoDeposit=Kein Deposit +offerbook.noDepositOffers=Angebote ohne Einzahlung (Passphrase erforderlich) offerbook.timeSinceSigning=Informationen zum Zahlungskonto offerbook.timeSinceSigning.info=Dieses Konto wurde verifiziert und {0} offerbook.timeSinceSigning.info.arbitrator=von einem Vermittler unterzeichnet und kann Partner-Konten unterzeichnen @@ -485,7 +490,10 @@ createOffer.setDepositAsBuyer=Meine Kaution als Käufer festlegen (%) createOffer.setDepositForBothTraders=Legen Sie die Kaution für beide Handelspartner fest (%) createOffer.securityDepositInfo=Die Kaution ihres Käufers wird {0} createOffer.securityDepositInfoAsBuyer=Ihre Kaution als Käufer wird {0} -createOffer.minSecurityDepositUsed=Min. Kaution des Käufers wird verwendet +createOffer.minSecurityDepositUsed=Der Mindest-Sicherheitsbetrag wird verwendet. +createOffer.buyerAsTakerWithoutDeposit=Kein Deposit erforderlich vom Käufer (Passphrase geschützt) +createOffer.myDeposit=Meine Sicherheitsleistung (%) +createOffer.myDepositInfo=Ihre Sicherheitsleistung beträgt {0} #################################################################### @@ -509,6 +517,8 @@ takeOffer.fundsBox.networkFee=Gesamte Mining-Gebühr takeOffer.fundsBox.takeOfferSpinnerInfo=Angebot annehmen: {0} takeOffer.fundsBox.paymentLabel=Haveno-Handel mit der ID {0} takeOffer.fundsBox.fundsStructure=({0} Kaution, {1} Handelsgebühr, {2} Mining-Gebühr) +takeOffer.fundsBox.noFundingRequiredTitle=Keine Finanzierung erforderlich +takeOffer.fundsBox.noFundingRequiredDescription=Holen Sie sich das Angebots-Passwort vom Verkäufer außerhalb von Haveno, um dieses Angebot anzunehmen. takeOffer.success.headline=Sie haben erfolgreich ein Angebot angenommen. takeOffer.success.info=Sie können den Status Ihres Trades unter \"Portfolio/Offene Trades\" einsehen. takeOffer.error.message=Bei der Angebotsannahme trat ein Fehler auf.\n\n{0} @@ -1464,6 +1474,7 @@ offerDetailsWindow.confirm.maker=Bestätigen: Anbieten monero zu {0} offerDetailsWindow.confirm.taker=Bestätigen: Angebot annehmen monero zu {0} offerDetailsWindow.creationDate=Erstellungsdatum offerDetailsWindow.makersOnion=Onion-Adresse des Erstellers +offerDetailsWindow.challenge=Angebots-Passphrase qRCodeWindow.headline=QR Code qRCodeWindow.msg=Bitte nutzen Sie diesen QR Code um Ihr Haveno Wallet von Ihrem externen Wallet aufzuladen. @@ -1690,6 +1701,9 @@ popup.accountSigning.unsignedPubKeys.signed=Pubkeys wurden unterzeichnet popup.accountSigning.unsignedPubKeys.result.signed=Unterzeichnete Pubkeys popup.accountSigning.unsignedPubKeys.result.failed=Unterzeichnung fehlgeschlagen +popup.info.buyerAsTakerWithoutDeposit.headline=Kein Depositum vom Käufer erforderlich +popup.info.buyerAsTakerWithoutDeposit=Ihr Angebot erfordert keine Sicherheitsleistung oder Gebühr vom XMR-Käufer.\n\nUm Ihr Angebot anzunehmen, müssen Sie ein Passwort mit Ihrem Handelspartner außerhalb von Haveno teilen.\n\nDas Passwort wird automatisch generiert und nach der Erstellung in den Angebotsdetails angezeigt. + #################################################################### # Notifications #################################################################### @@ -1815,6 +1829,7 @@ navigation.support=\"Support\" formatter.formatVolumeLabel={0} Betrag{1} formatter.makerTaker=Ersteller als {0} {1} / Abnehmer als {2} {3} +formatter.makerTakerLocked=Ersteller als {0} {1} / Abnehmer als {2} {3} 🔒 formatter.youAreAsMaker=Sie sind: {1} {0} (Ersteller) / Abnehmer ist: {3} {2} formatter.youAreAsTaker=Sie sind: {1} {0} (Abnehmer) / Ersteller ist: {3} {2} formatter.youAre=Sie {0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index 2ff762f8..d3f61ada 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -40,6 +40,8 @@ shared.buyMonero=Comprar monero shared.sellMonero=Vender monero shared.buyCurrency=Comprar {0} shared.sellCurrency=Vender {0} +shared.buyCurrencyLocked=Comprar {0} 🔒 +shared.sellCurrencyLocked=Vender {0} 🔒 shared.buyingXMRWith=Comprando XMR con {0} shared.sellingXMRFor=Vendiendo XMR por {0} shared.buyingCurrency=comprando {0} (Vendiendo XMR) @@ -330,6 +332,7 @@ offerbook.createOffer=Crear oferta offerbook.takeOffer=Tomar oferta offerbook.takeOfferToBuy=Tomar oferta de compra de {0} offerbook.takeOfferToSell=Tomar oferta de venta de {0} +offerbook.takeOffer.enterChallenge=Introduzca la frase secreta de la oferta offerbook.trader=Trader offerbook.offerersBankId=ID del banco del creador (BIC/SWIFT): {0} offerbook.offerersBankName=Nombre del banco del creador: {0} @@ -340,6 +343,8 @@ offerbook.availableOffers=Ofertas disponibles offerbook.filterByCurrency=Filtrar por moneda offerbook.filterByPaymentMethod=Filtrar por método de pago offerbook.matchingOffers=Ofertas que concuerden con mis cuentas +offerbook.filterNoDeposit=Sin depósito +offerbook.noDepositOffers=Ofertas sin depósito (se requiere frase de paso) offerbook.timeSinceSigning=Información de la cuenta offerbook.timeSinceSigning.info=Esta cuenta fue verificada y {0} offerbook.timeSinceSigning.info.arbitrator=firmada por un árbitro y puede firmar cuentas de pares @@ -485,7 +490,10 @@ createOffer.setDepositAsBuyer=Establecer mi depósito de seguridad como comprado createOffer.setDepositForBothTraders=Establecer el depósito de seguridad para los comerciantes (%) createOffer.securityDepositInfo=Su depósito de seguridad como comprador será {0} createOffer.securityDepositInfoAsBuyer=Su depósito de seguridad como comprador será {0} -createOffer.minSecurityDepositUsed=En uso el depósito de seguridad mínimo +createOffer.minSecurityDepositUsed=Se utiliza un depósito de seguridad mínimo +createOffer.buyerAsTakerWithoutDeposit=No se requiere depósito del comprador (protegido por passphrase) +createOffer.myDeposit=Mi depósito de seguridad (%) +createOffer.myDepositInfo=Tu depósito de seguridad será {0} #################################################################### @@ -509,6 +517,8 @@ takeOffer.fundsBox.networkFee=Comisiones de minado totales takeOffer.fundsBox.takeOfferSpinnerInfo=Aceptando oferta: {0} takeOffer.fundsBox.paymentLabel=Intercambio Haveno con ID {0} takeOffer.fundsBox.fundsStructure=({0} depósito de seguridad {1} tasa de intercambio, {2} tarifa de minado) +takeOffer.fundsBox.noFundingRequiredTitle=No se requiere financiamiento +takeOffer.fundsBox.noFundingRequiredDescription=Obtén la frase de acceso de la oferta del vendedor fuera de Haveno para aceptar esta oferta. takeOffer.success.headline=Ha aceptado la oferta con éxito. takeOffer.success.info=Puede ver el estado de su intercambio en \"Portafolio/Intercambios abiertos\". takeOffer.error.message=Un error ocurrió al tomar la oferta.\n\n{0} @@ -1465,6 +1475,7 @@ offerDetailsWindow.confirm.maker=Confirmar: Poner oferta para {0} monero offerDetailsWindow.confirm.taker=Confirmar: Tomar oferta {0} monero offerDetailsWindow.creationDate=Fecha de creación offerDetailsWindow.makersOnion=Dirección onion del creador +offerDetailsWindow.challenge=Frase de contraseña de la oferta qRCodeWindow.headline=Código QR qRCodeWindow.msg=Por favor, utilice este código QR para fondear su billetera Haveno desde su billetera externa. @@ -1691,6 +1702,9 @@ popup.accountSigning.unsignedPubKeys.signed=Las claves públicas se firmaron popup.accountSigning.unsignedPubKeys.result.signed=Claves públicas firmadas popup.accountSigning.unsignedPubKeys.result.failed=Error al firmar +popup.info.buyerAsTakerWithoutDeposit.headline=No se requiere depósito del comprador +popup.info.buyerAsTakerWithoutDeposit=Tu oferta no requerirá un depósito de seguridad ni una tarifa del comprador de XMR.\n\nPara aceptar tu oferta, debes compartir una frase de acceso con tu compañero de comercio fuera de Haveno.\n\nLa frase de acceso se genera automáticamente y se muestra en los detalles de la oferta después de la creación. + #################################################################### # Notifications #################################################################### @@ -1816,6 +1830,7 @@ navigation.support=\"Soporte\" formatter.formatVolumeLabel={0} cantidad{1} formatter.makerTaker=Creador como {0} {1} / Tomador como {2} {3} +formatter.makerTakerLocked=Creador como {0} {1} / Tomador como {2} {3} 🔒 formatter.youAreAsMaker=Usted es: {1} {0} (creador) / El tomador es: {3} {2} formatter.youAreAsTaker=Usted es: {1} {0} (tomador) / Creador es: {3} {2} formatter.youAre=Usted es {0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index ed78e6ec..710bbb6b 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -40,6 +40,8 @@ shared.buyMonero=خرید بیتکوین shared.sellMonero=بیتکوین بفروشید shared.buyCurrency=خرید {0} shared.sellCurrency=فروش {0} +shared.buyCurrencyLocked=بخر {0} 🔒 +shared.sellCurrencyLocked=فروش {0} 🔒 shared.buyingXMRWith=خرید بیتکوین با {0} shared.sellingXMRFor=فروش بیتکوین با {0} shared.buyingCurrency=خرید {0} ( فروش بیتکوین) @@ -330,6 +332,7 @@ offerbook.createOffer=ایجاد پیشنهاد offerbook.takeOffer=برداشتن پیشنهاد offerbook.takeOfferToBuy=پیشنهاد خرید {0} را بردار offerbook.takeOfferToSell=پیشنهاد فروش {0} را بردار +offerbook.takeOffer.enterChallenge=عبارت عبور پیشنهاد را وارد کنید offerbook.trader=معامله‌گر offerbook.offerersBankId=شناسه بانک سفارش‌گذار (BIC/SWIFT): {0} offerbook.offerersBankName= نام بانک سفارش‌گذار : {0} @@ -339,7 +342,9 @@ offerbook.offerersAcceptedBankSeats=بانک‌های کشورهای پذیرف offerbook.availableOffers=پیشنهادهای موجود offerbook.filterByCurrency=فیلتر بر اساس ارز offerbook.filterByPaymentMethod=فیلتر بر اساس روش پرداخت -offerbook.matchingOffers=Offers matching my accounts +offerbook.matchingOffers=پیشنهادات متناسب با حساب‌های من +offerbook.filterNoDeposit=هیچ سپرده‌ای +offerbook.noDepositOffers=پیشنهادهایی بدون ودیعه (نیاز به عبارت عبور) offerbook.timeSinceSigning=Account info offerbook.timeSinceSigning.info=This account was verified and {0} offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts @@ -484,7 +489,10 @@ createOffer.setDepositAsBuyer=تنظیم سپرده‌ی اطمینان من ب createOffer.setDepositForBothTraders=Set both traders' security deposit (%) createOffer.securityDepositInfo=سپرده‌ی اطمینان خریدار شما {0} خواهد بود createOffer.securityDepositInfoAsBuyer=سپرده‌ی اطمینان شما به عنوان خریدار {0} خواهد بود -createOffer.minSecurityDepositUsed=Min. buyer security deposit is used +createOffer.minSecurityDepositUsed=حداقل سپرده امنیتی استفاده می‌شود +createOffer.buyerAsTakerWithoutDeposit=هیچ سپرده‌ای از خریدار مورد نیاز نیست (محافظت شده با پس‌عبارت) +createOffer.myDeposit=سپرده امنیتی من (%) +createOffer.myDepositInfo=ودیعه امنیتی شما {0} خواهد بود #################################################################### @@ -508,6 +516,8 @@ takeOffer.fundsBox.networkFee=کل کارمزد استخراج takeOffer.fundsBox.takeOfferSpinnerInfo=پذیرفتن پیشنهاد: {0} takeOffer.fundsBox.paymentLabel=معامله Haveno با شناسه‌ی {0} takeOffer.fundsBox.fundsStructure=({0} سپرده‌ی اطمینان، {1} هزینه‌ی معامله، {2} هزینه تراکنش شبکه) +takeOffer.fundsBox.noFundingRequiredTitle=نیاز به تأمین مالی نیست +takeOffer.fundsBox.noFundingRequiredDescription=برای پذیرش این پیشنهاد، رمزعبور آن را از فروشنده خارج از هاونئو دریافت کنید. takeOffer.success.headline=با موفقیت یک پیشنهاد را قبول کرده‌اید. takeOffer.success.info=شما می‌توانید وضعیت معامله‌ی خود را در \"سبد سهام /معاملات باز\" ببینید. takeOffer.error.message=هنگام قبول کردن پیشنهاد، اتفاقی رخ داده است.\n\n{0} @@ -1460,6 +1470,7 @@ offerDetailsWindow.confirm.maker=تأیید: پیشنهاد را به {0} بگذ offerDetailsWindow.confirm.taker=تأیید: پیشنهاد را به {0} بپذیرید offerDetailsWindow.creationDate=تاریخ ایجاد offerDetailsWindow.makersOnion=آدرس Onion سفارش گذار +offerDetailsWindow.challenge=Passphrase de l'offre qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. @@ -1683,6 +1694,9 @@ popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign +popup.info.buyerAsTakerWithoutDeposit.headline=هیچ پیش‌پرداختی از خریدار مورد نیاز نیست +popup.info.buyerAsTakerWithoutDeposit=پیشنهاد شما نیاز به ودیعه امنیتی یا هزینه از خریدار XMR ندارد.\n\nبرای پذیرفتن پیشنهاد شما، باید یک پس‌عبارت را با شریک تجاری خود خارج از Haveno به اشتراک بگذارید.\n\nپس‌عبارت به‌طور خودکار تولید می‌شود و پس از ایجاد در جزئیات پیشنهاد نمایش داده می‌شود. + #################################################################### # Notifications #################################################################### @@ -1808,6 +1822,7 @@ navigation.support=\"پشتیبانی\" formatter.formatVolumeLabel={0} مبلغ {1} formatter.makerTaker=سفارش گذار به عنوان {0} {1} / پذیرنده به عنوان {2} {3} +formatter.makerTakerLocked=سفارش گذار به عنوان {0} {1} / پذیرنده به عنوان {2} {3} 🔒 formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2} formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2} formatter.youAre=شما {0} {1} ({2} {3}) هستید diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index e87a0cac..5e734ac9 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -40,6 +40,8 @@ shared.buyMonero=Achat Monero shared.sellMonero=Vendre des Moneros shared.buyCurrency=Achat {0} shared.sellCurrency=Vendre {0} +shared.buyCurrencyLocked=Achat {0} 🔒 +shared.sellCurrencyLocked=Vendre {0} 🔒 shared.buyingXMRWith=achat XMR avec {0} shared.sellingXMRFor=vendre XMR pour {0} shared.buyingCurrency=achat {0} (vente XMR) @@ -330,6 +332,7 @@ offerbook.createOffer=Créer un ordre offerbook.takeOffer=Accepter un ordre offerbook.takeOfferToBuy=Accepter l''ordre d''achat {0} offerbook.takeOfferToSell=Accepter l''ordre de vente {0} +offerbook.takeOffer.enterChallenge=Entrez la phrase secrète de l'offre offerbook.trader=Échanger offerbook.offerersBankId=ID de la banque du maker (BIC/SWIFT): {0} offerbook.offerersBankName=Nom de la banque du maker: {0} @@ -340,6 +343,8 @@ offerbook.availableOffers=Ordres disponibles offerbook.filterByCurrency=Filtrer par devise offerbook.filterByPaymentMethod=Filtrer par mode de paiement offerbook.matchingOffers=Offres correspondants à mes comptes +offerbook.filterNoDeposit=Aucun dépôt +offerbook.noDepositOffers=Offres sans dépôt (passphrase requise) offerbook.timeSinceSigning=Informations du compte offerbook.timeSinceSigning.info=Ce compte a été vérifié et {0} offerbook.timeSinceSigning.info.arbitrator=signé par un arbitre et pouvant signer des comptes pairs @@ -485,7 +490,10 @@ createOffer.setDepositAsBuyer=Définir mon dépôt de garantie en tant qu'achete createOffer.setDepositForBothTraders=Établissez le dépôt de sécurité des deux traders (%) createOffer.securityDepositInfo=Le dépôt de garantie de votre acheteur sera de {0} createOffer.securityDepositInfoAsBuyer=Votre dépôt de garantie en tant qu''acheteur sera de {0} -createOffer.minSecurityDepositUsed=Le minimum de dépôt de garantie de l'acheteur est utilisé +createOffer.minSecurityDepositUsed=Le dépôt de sécurité minimum est utilisé +createOffer.buyerAsTakerWithoutDeposit=Aucun dépôt requis de la part de l'acheteur (protégé par un mot de passe) +createOffer.myDeposit=Mon dépôt de garantie (%) +createOffer.myDepositInfo=Votre dépôt de garantie sera de {0} #################################################################### @@ -509,6 +517,8 @@ takeOffer.fundsBox.networkFee=Total des frais de minage takeOffer.fundsBox.takeOfferSpinnerInfo=Acceptation de l'offre : {0} takeOffer.fundsBox.paymentLabel=Transaction Haveno avec l''ID {0} takeOffer.fundsBox.fundsStructure=({0} dépôt de garantie, {1} frais de transaction, {2} frais de minage) +takeOffer.fundsBox.noFundingRequiredTitle=Aucun financement requis +takeOffer.fundsBox.noFundingRequiredDescription=Obtenez la phrase secrète de l'offre auprès du vendeur en dehors de Haveno pour accepter cette offre. takeOffer.success.headline=Vous avez accepté un ordre avec succès. takeOffer.success.info=Vous pouvez voir vos transactions dans \"Portfolio/Échanges en cours\". takeOffer.error.message=Une erreur s''est produite pendant l’'acceptation de l''ordre.\n\n{0} @@ -1466,6 +1476,7 @@ offerDetailsWindow.confirm.maker=Confirmer: Placer un ordre de {0} monero offerDetailsWindow.confirm.taker=Confirmer: Acceptez l''ordre de {0} monero offerDetailsWindow.creationDate=Date de création offerDetailsWindow.makersOnion=Adresse onion du maker +offerDetailsWindow.challenge=Phrase secrète de l'offre qRCodeWindow.headline=QR Code qRCodeWindow.msg=Veuillez utiliser le code QR pour recharger du portefeuille externe au portefeuille Haveno. @@ -1692,6 +1703,9 @@ popup.accountSigning.unsignedPubKeys.signed=Les clés publiques ont été signé popup.accountSigning.unsignedPubKeys.result.signed=Clés publiques signées popup.accountSigning.unsignedPubKeys.result.failed=Échec de la signature +popup.info.buyerAsTakerWithoutDeposit.headline=Aucun dépôt requis de la part de l'acheteur +popup.info.buyerAsTakerWithoutDeposit=Votre offre ne nécessitera pas de dépôt de sécurité ni de frais de la part de l'acheteur XMR.\n\nPour accepter votre offre, vous devez partager un mot de passe avec votre partenaire commercial en dehors de Haveno.\n\nLe mot de passe est généré automatiquement et affiché dans les détails de l'offre après sa création. + #################################################################### # Notifications #################################################################### @@ -1817,6 +1831,7 @@ navigation.support=\"Assistance\" formatter.formatVolumeLabel={0} montant{1} formatter.makerTaker=Maker comme {0} {1} / Taker comme {2} {3} +formatter.makerTakerLocked=Maker comme {0} {1} / Taker comme {2} {3} 🔒 formatter.youAreAsMaker=Vous êtes {1} {0} (maker) / Le preneur est: {3} {2} formatter.youAreAsTaker=Vous êtes: {1} {0} (preneur) / Le maker est: {3} {2} formatter.youAre=Vous êtes {0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 212c89f3..b968d839 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -40,6 +40,8 @@ shared.buyMonero=Acquista monero shared.sellMonero=Vendi monero shared.buyCurrency=Acquista {0} shared.sellCurrency=Vendi {0} +shared.buyCurrencyLocked=Acquista {0} 🔒 +shared.sellCurrencyLocked=Vendi {0} 🔒 shared.buyingXMRWith=acquistando XMR con {0} shared.sellingXMRFor=vendendo XMR per {0} shared.buyingCurrency=comprando {0} (vendendo XMR) @@ -330,6 +332,7 @@ offerbook.createOffer=Crea offerta offerbook.takeOffer=Accetta offerta offerbook.takeOfferToBuy=Accetta l'offerta per acquistare {0} offerbook.takeOfferToSell=Accetta l'offerta per vendere {0} +offerbook.takeOffer.enterChallenge=Inserisci la passphrase dell'offerta offerbook.trader=Trader offerbook.offerersBankId=ID banca del Maker (BIC/SWIFT): {0} offerbook.offerersBankName=Nome della banca del Maker: {0} @@ -339,7 +342,9 @@ offerbook.offerersAcceptedBankSeats=Sede accettata dei paesi bancari (acquirente offerbook.availableOffers=Offerte disponibili offerbook.filterByCurrency=Filtra per valuta offerbook.filterByPaymentMethod=Filtra per metodo di pagamento -offerbook.matchingOffers=Offers matching my accounts +offerbook.matchingOffers=Offerte che corrispondono ai miei account +offerbook.filterNoDeposit=Nessun deposito +offerbook.noDepositOffers=Offerte senza deposito (passphrase richiesta) offerbook.timeSinceSigning=Account info offerbook.timeSinceSigning.info=Questo account è stato verificato e {0} offerbook.timeSinceSigning.info.arbitrator=firmato da un arbitro e può firmare account peer @@ -484,7 +489,10 @@ createOffer.setDepositAsBuyer=Imposta il mio deposito cauzionale come acquirente createOffer.setDepositForBothTraders=Set both traders' security deposit (%) createOffer.securityDepositInfo=Il deposito cauzionale dell'acquirente sarà {0} createOffer.securityDepositInfoAsBuyer=Il tuo deposito cauzionale come acquirente sarà {0} -createOffer.minSecurityDepositUsed=Viene utilizzato il minimo deposito cauzionale dell'acquirente +createOffer.minSecurityDepositUsed=Il deposito di sicurezza minimo è utilizzato +createOffer.buyerAsTakerWithoutDeposit=Nessun deposito richiesto dal compratore (protetto da passphrase) +createOffer.myDeposit=Il mio deposito di sicurezza (%) +createOffer.myDepositInfo=Il tuo deposito di sicurezza sarà {0} #################################################################### @@ -508,6 +516,8 @@ takeOffer.fundsBox.networkFee=Totale commissioni di mining takeOffer.fundsBox.takeOfferSpinnerInfo=Accettare l'offerta: {0} takeOffer.fundsBox.paymentLabel=Scambia Haveno con ID {0} takeOffer.fundsBox.fundsStructure=({0} deposito cauzionale, {1} commissione commerciale, {2} commissione mineraria) +takeOffer.fundsBox.noFundingRequiredTitle=Nessun finanziamento richiesto +takeOffer.fundsBox.noFundingRequiredDescription=Ottieni la passphrase dell'offerta dal venditore fuori da Haveno per accettare questa offerta. takeOffer.success.headline=Hai accettato con successo un'offerta. takeOffer.success.info=Puoi vedere lo stato del tuo scambio su \"Portafoglio/Scambi aperti\". takeOffer.error.message=Si è verificato un errore durante l'accettazione dell'offerta.\n\n{0} @@ -1463,6 +1473,7 @@ offerDetailsWindow.confirm.maker=Conferma: Piazza l'offerta a {0} monero offerDetailsWindow.confirm.taker=Conferma: Accetta l'offerta a {0} monero offerDetailsWindow.creationDate=Data di creazione offerDetailsWindow.makersOnion=Indirizzo .onion del maker +offerDetailsWindow.challenge=Passphrase dell'offerta qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. @@ -1686,6 +1697,9 @@ popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign +popup.info.buyerAsTakerWithoutDeposit.headline=Nessun deposito richiesto dal compratore +popup.info.buyerAsTakerWithoutDeposit=La tua offerta non richiederà un deposito di sicurezza o una commissione da parte dell'acquirente XMR.\n\nPer accettare la tua offerta, devi condividere una passphrase con il tuo partner commerciale al di fuori di Haveno.\n\nLa passphrase viene generata automaticamente e mostrata nei dettagli dell'offerta dopo la creazione. + #################################################################### # Notifications #################################################################### @@ -1811,6 +1825,7 @@ navigation.support=\"Supporto\" formatter.formatVolumeLabel={0} importo{1} formatter.makerTaker=Maker come {0} {1} / Taker come {2} {3} +formatter.makerTakerLocked=Maker come {0} {1} / Taker come {2} {3} 🔒 formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2} formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2} formatter.youAre=Sei {0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 80542bc3..70a35361 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -40,6 +40,8 @@ shared.buyMonero=ビットコインを買う shared.sellMonero=ビットコインを売る shared.buyCurrency={0}を買う shared.sellCurrency={0}を売る +shared.buyCurrencyLocked={0}を買う 🔒 +shared.sellCurrencyLocked={0}を売る 🔒 shared.buyingXMRWith=XMRを{0}で買う shared.sellingXMRFor=XMRを{0}で売る shared.buyingCurrency={0}を購入中 (XMRを売却中) @@ -330,6 +332,7 @@ offerbook.createOffer=オファーを作る offerbook.takeOffer=オファーを受ける offerbook.takeOfferToBuy={0}購入オファーを受ける offerbook.takeOfferToSell={0}売却オファーを受ける +offerbook.takeOffer.enterChallenge=オファーのパスフレーズを入力してください offerbook.trader=取引者 offerbook.offerersBankId=メイカーの銀行ID (BIC/SWIFT): {0} offerbook.offerersBankName=メーカーの銀行名: {0} @@ -340,6 +343,8 @@ offerbook.availableOffers=利用可能なオファー offerbook.filterByCurrency=通貨でフィルター offerbook.filterByPaymentMethod=支払い方法でフィルター offerbook.matchingOffers=アカウントと一致するオファー +offerbook.filterNoDeposit=デポジットなし +offerbook.noDepositOffers=預金不要のオファー(パスフレーズ必須) offerbook.timeSinceSigning=アカウント情報 offerbook.timeSinceSigning.info=このアカウントは認証されまして、{0} offerbook.timeSinceSigning.info.arbitrator=調停人に署名されました。ピアアカウントも署名できます @@ -485,7 +490,10 @@ createOffer.setDepositAsBuyer=購入時のセキュリティデポジット (%) createOffer.setDepositForBothTraders=両方の取引者の保証金を設定する(%) createOffer.securityDepositInfo=あなたの買い手のセキュリティデポジットは{0}です createOffer.securityDepositInfoAsBuyer=あなたの購入時のセキュリティデポジットは{0}です -createOffer.minSecurityDepositUsed=最小値の買い手の保証金は使用されます +createOffer.minSecurityDepositUsed=最低セキュリティデポジットが使用されます +createOffer.buyerAsTakerWithoutDeposit=購入者に保証金は不要(パスフレーズ保護) +createOffer.myDeposit=私の保証金(%) +createOffer.myDepositInfo=あなたのセキュリティデポジットは{0}です #################################################################### @@ -509,6 +517,8 @@ takeOffer.fundsBox.networkFee=合計マイニング手数料 takeOffer.fundsBox.takeOfferSpinnerInfo=オファーを受け入れる: {0} takeOffer.fundsBox.paymentLabel=次のIDとのHavenoトレード: {0} takeOffer.fundsBox.fundsStructure=({0} セキュリティデポジット, {1} 取引手数料, {2}マイニング手数料) +takeOffer.fundsBox.noFundingRequiredTitle=資金は必要ありません +takeOffer.fundsBox.noFundingRequiredDescription=このオファーを受けるには、Haveno外で売り手からオファーパスフレーズを取得してください。 takeOffer.success.headline=オファー受け入れに成功しました takeOffer.success.info=あなたのトレード状態は「ポートフォリオ/オープントレード」で見られます takeOffer.error.message=オファーの受け入れ時にエラーが発生しました。\n\n{0} @@ -1464,6 +1474,7 @@ offerDetailsWindow.confirm.maker=承認: ビットコインを{0}オファーを offerDetailsWindow.confirm.taker=承認: ビットコインを{0}オファーを受ける offerDetailsWindow.creationDate=作成日 offerDetailsWindow.makersOnion=メイカーのonionアドレス +offerDetailsWindow.challenge=オファーパスフレーズ qRCodeWindow.headline=QRコード qRCodeWindow.msg=外部ウォレットからHavenoウォレットへ送金するのに、このQRコードを利用して下さい。 @@ -1689,6 +1700,9 @@ popup.accountSigning.unsignedPubKeys.signed=パブリックキーは署名され popup.accountSigning.unsignedPubKeys.result.signed=署名されたパブリックキー popup.accountSigning.unsignedPubKeys.result.failed=署名が失敗しました +popup.info.buyerAsTakerWithoutDeposit.headline=購入者による保証金は不要 +popup.info.buyerAsTakerWithoutDeposit=あなたのオファーには、XMR購入者からのセキュリティデポジットや手数料は必要ありません。\n\nオファーを受け入れるには、Haveno外で取引相手とパスフレーズを共有する必要があります。\n\nパスフレーズは自動的に生成され、作成後にオファーの詳細に表示されます。 + #################################################################### # Notifications #################################################################### @@ -1814,6 +1828,7 @@ navigation.support=「サポート」 formatter.formatVolumeLabel={0} 額{1} formatter.makerTaker=メイカーは{0} {1} / テイカーは{2} {3} +formatter.makerTakerLocked=メイカーは{0} {1} / テイカーは{2} {3} 🔒 formatter.youAreAsMaker=あなたは:{1} {0}(メイカー) / テイカーは:{3} {2} formatter.youAreAsTaker=あなたは:{1} {0}(テイカー) / メイカーは{3} {2} formatter.youAre=あなたは{0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index f6659b16..93179908 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -40,6 +40,8 @@ shared.buyMonero=Comprar monero shared.sellMonero=Vender monero shared.buyCurrency=Comprar {0} shared.sellCurrency=Vender {0} +shared.buyCurrencyLocked=Comprar {0} 🔒 +shared.sellCurrencyLocked=Vender {0} 🔒 shared.buyingXMRWith=comprando XMR com {0} shared.sellingXMRFor=vendendo XMR por {0} shared.buyingCurrency=comprando {0} (vendendo XMR) @@ -333,6 +335,7 @@ offerbook.createOffer=Criar oferta offerbook.takeOffer=Aceitar oferta offerbook.takeOfferToBuy=Comprar {0} offerbook.takeOfferToSell=Vender {0} +offerbook.takeOffer.enterChallenge=Digite a senha da oferta offerbook.trader=Trader offerbook.offerersBankId=ID do banco do ofertante (BIC/SWIFT): {0} offerbook.offerersBankName=Nome do banco do ofertante: {0} @@ -342,7 +345,9 @@ offerbook.offerersAcceptedBankSeats=Países aceitos como sede bancária (tomador offerbook.availableOffers=Ofertas disponíveis offerbook.filterByCurrency=Filtrar por moeda offerbook.filterByPaymentMethod=Filtrar por método de pagamento -offerbook.matchingOffers=Offers matching my accounts +offerbook.matchingOffers=Ofertas que correspondem às minhas contas +offerbook.filterNoDeposit=Sem depósito +offerbook.noDepositOffers=Ofertas sem depósito (senha necessária) offerbook.timeSinceSigning=Account info offerbook.timeSinceSigning.info=Esta conta foi verificada e {0} offerbook.timeSinceSigning.info.arbitrator=assinada por um árbitro e pode assinar contas de pares @@ -487,7 +492,10 @@ createOffer.setDepositAsBuyer=Definir o meu depósito de segurança como comprad createOffer.setDepositForBothTraders=Set both traders' security deposit (%) createOffer.securityDepositInfo=O seu depósito de segurança do comprador será de {0} createOffer.securityDepositInfoAsBuyer=O seu depósito de segurança como comprador será de {0} -createOffer.minSecurityDepositUsed=Depósito de segurança mínimo para compradores foi usado +createOffer.minSecurityDepositUsed=O depósito de segurança mínimo é utilizado +createOffer.buyerAsTakerWithoutDeposit=Nenhum depósito necessário do comprador (protegido por senha) +createOffer.myDeposit=Meu depósito de segurança (%) +createOffer.myDepositInfo=Seu depósito de segurança será {0} #################################################################### @@ -511,6 +519,8 @@ takeOffer.fundsBox.networkFee=Total em taxas de mineração takeOffer.fundsBox.takeOfferSpinnerInfo=Aceitando a oferta: {0} takeOffer.fundsBox.paymentLabel=negociação Haveno com ID {0} takeOffer.fundsBox.fundsStructure=({0} depósito de segurança, {1} taxa de transação, {2} taxa de mineração) +takeOffer.fundsBox.noFundingRequiredTitle=Sem financiamento necessário +takeOffer.fundsBox.noFundingRequiredDescription=Obtenha a frase secreta da oferta com o vendedor fora do Haveno para aceitar esta oferta. takeOffer.success.headline=Você aceitou uma oferta com sucesso. takeOffer.success.info=Você pode ver o status de sua negociação em \"Portfolio/Negociações em aberto\". takeOffer.error.message=Ocorreu um erro ao aceitar a oferta.\n\n{0} @@ -1467,6 +1477,7 @@ offerDetailsWindow.confirm.maker=Criar oferta para {0} monero offerDetailsWindow.confirm.taker=Confirmar: Aceitar oferta de {0} monero offerDetailsWindow.creationDate=Criada em offerDetailsWindow.makersOnion=Endereço onion do ofertante +offerDetailsWindow.challenge=Passphrase da oferta qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. @@ -1693,6 +1704,9 @@ popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign +popup.info.buyerAsTakerWithoutDeposit.headline=Nenhum depósito exigido do comprador +popup.info.buyerAsTakerWithoutDeposit=Sua oferta não exigirá um depósito de segurança ou taxa do comprador de XMR.\n\nPara aceitar sua oferta, você deve compartilhar uma senha com seu parceiro de negociação fora do Haveno.\n\nA senha é gerada automaticamente e exibida nos detalhes da oferta após a criação. + #################################################################### # Notifications #################################################################### @@ -1818,6 +1832,7 @@ navigation.support=\"Suporte\" formatter.formatVolumeLabel={0} quantia{1} formatter.makerTaker=Ofertante: {1} de {0} / Aceitador: {3} de {2} +formatter.makerTakerLocked=Ofertante: {1} de {0} / Aceitador: {3} de {2} 🔒 formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2} formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2} formatter.youAre=Você está {0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index d7891bea..1b1b5de6 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -40,6 +40,8 @@ shared.buyMonero=Comprar monero shared.sellMonero=Vender monero shared.buyCurrency=Comprar {0} shared.sellCurrency=Vender {0} +shared.buyCurrencyLocked=Comprar {0} 🔒 +shared.sellCurrencyLocked=Vender {0} 🔒 shared.buyingXMRWith=comprando XMR com {0} shared.sellingXMRFor=vendendo XMR por {0} shared.buyingCurrency=comprando {0} (vendendo XMR) @@ -193,7 +195,7 @@ shared.iConfirm=Eu confirmo shared.openURL=Abrir {0} shared.fiat=Moeda fiduciária shared.crypto=Cripto -shared.preciousMetals=TODO +shared.preciousMetals=Metais Preciosos shared.all=Tudo shared.edit=Editar shared.advancedOptions=Opções avançadas @@ -330,6 +332,7 @@ offerbook.createOffer=Criar oferta offerbook.takeOffer=Aceitar oferta offerbook.takeOfferToBuy=Aceitar oferta para comprar {0} offerbook.takeOfferToSell=Aceitar oferta para vender {0} +offerbook.takeOffer.enterChallenge=Digite a senha da oferta offerbook.trader=Negociador offerbook.offerersBankId=ID do banco do ofertante (BIC/SWIFT): {0} offerbook.offerersBankName=Nome do banco do ofertante: {0} @@ -339,7 +342,9 @@ offerbook.offerersAcceptedBankSeats=Sede do banco aceite (aceitador):\n {0} offerbook.availableOffers=Ofertas disponíveis offerbook.filterByCurrency=Filtrar por moeda offerbook.filterByPaymentMethod=Filtrar por método de pagamento -offerbook.matchingOffers=Offers matching my accounts +offerbook.matchingOffers=Ofertas que correspondem às minhas contas +offerbook.filterNoDeposit=Sem depósito +offerbook.noDepositOffers=Ofertas sem depósito (senha necessária) offerbook.timeSinceSigning=Account info offerbook.timeSinceSigning.info=Esta conta foi verificada e {0} offerbook.timeSinceSigning.info.arbitrator=assinada pelo árbitro e pode assinar contas de pares @@ -484,7 +489,10 @@ createOffer.setDepositAsBuyer=Definir o meu depósito de segurança enquanto com createOffer.setDepositForBothTraders=Set both traders' security deposit (%) createOffer.securityDepositInfo=O depósito de segurança do seu comprador será {0} createOffer.securityDepositInfoAsBuyer=O seu depósito de segurança enquanto comprador será {0} -createOffer.minSecurityDepositUsed=O mín. depósito de segurança para o comprador é utilizado +createOffer.minSecurityDepositUsed=O depósito de segurança mínimo é utilizado +createOffer.buyerAsTakerWithoutDeposit=Nenhum depósito exigido do comprador (protegido por frase secreta) +createOffer.myDeposit=Meu depósito de segurança (%) +createOffer.myDepositInfo=Seu depósito de segurança será {0} #################################################################### @@ -508,6 +516,8 @@ takeOffer.fundsBox.networkFee=Total de taxas de mineração takeOffer.fundsBox.takeOfferSpinnerInfo=Aceitando a oferta: {0} takeOffer.fundsBox.paymentLabel=negócio do Haveno com ID {0} takeOffer.fundsBox.fundsStructure=({0} depósito de segurança, {1} taxa de negócio, {2} taxa de mineração) +takeOffer.fundsBox.noFundingRequiredTitle=Nenhum financiamento necessário +takeOffer.fundsBox.noFundingRequiredDescription=Obtenha a senha da oferta com o vendedor fora do Haveno para aceitar esta oferta. takeOffer.success.headline=Você aceitou uma oferta com sucesso. takeOffer.success.info=Você pode ver o estado de seu negócio em \"Portefólio/Negócios abertos\". takeOffer.error.message=Ocorreu um erro ao aceitar a oferta .\n\n{0} @@ -1460,6 +1470,7 @@ offerDetailsWindow.confirm.maker=Confirmar: Criar oferta para {0} monero offerDetailsWindow.confirm.taker=Confirmar: Aceitar oferta de {0} monero offerDetailsWindow.creationDate=Data de criação offerDetailsWindow.makersOnion=Endereço onion do ofertante +offerDetailsWindow.challenge=Passphrase da oferta qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. @@ -1683,6 +1694,9 @@ popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign +popup.info.buyerAsTakerWithoutDeposit.headline=Nenhum depósito exigido do comprador +popup.info.buyerAsTakerWithoutDeposit=Sua oferta não exigirá um depósito de segurança ou taxa do comprador de XMR.\n\nPara aceitar sua oferta, você deve compartilhar uma senha com seu parceiro comercial fora do Haveno.\n\nA senha é gerada automaticamente e exibida nos detalhes da oferta após a criação. + #################################################################### # Notifications #################################################################### @@ -1808,6 +1822,7 @@ navigation.support=\"Apoio\" formatter.formatVolumeLabel={0} quantia{1} formatter.makerTaker=Ofertante como {0} {1} / Aceitador como {2} {3} +formatter.makerTakerLocked=Ofertante como {0} {1} / Aceitador como {2} {3} 🔒 formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2} formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2} formatter.youAre=Você é {0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 6f1ba86d..d40f9af7 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -40,6 +40,8 @@ shared.buyMonero=Купить биткойн shared.sellMonero=Продать биткойн shared.buyCurrency=Купить {0} shared.sellCurrency=Продать {0} +shared.buyCurrencyLocked=Купить {0} 🔒 +shared.sellCurrencyLocked=Продать {0} 🔒 shared.buyingXMRWith=покупка ВТС за {0} shared.sellingXMRFor=продажа ВТС за {0} shared.buyingCurrency=покупка {0} (продажа ВТС) @@ -330,6 +332,7 @@ offerbook.createOffer=Создать предложение offerbook.takeOffer=Принять предложение offerbook.takeOfferToBuy=Принять предложение купить {0} offerbook.takeOfferToSell=Принять предложение продать {0} +offerbook.takeOffer.enterChallenge=Введите фразу-пароль предложения offerbook.trader=Трейдер offerbook.offerersBankId=Идент. банка (BIC/SWIFT) мейкера: {0} offerbook.offerersBankName=Название банка мейкера: {0} @@ -339,7 +342,9 @@ offerbook.offerersAcceptedBankSeats=Допустимые страны банка offerbook.availableOffers=Доступные предложения offerbook.filterByCurrency=Фильтровать по валюте offerbook.filterByPaymentMethod=Фильтровать по способу оплаты -offerbook.matchingOffers=Offers matching my accounts +offerbook.matchingOffers=Предложения, соответствующие моим аккаунтам +offerbook.filterNoDeposit=Нет депозита +offerbook.noDepositOffers=Предложения без депозита (требуется пароль) offerbook.timeSinceSigning=Account info offerbook.timeSinceSigning.info=This account was verified and {0} offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts @@ -484,7 +489,10 @@ createOffer.setDepositAsBuyer=Установить мой залог как по createOffer.setDepositForBothTraders=Set both traders' security deposit (%) createOffer.securityDepositInfo=Сумма залога покупателя: {0} createOffer.securityDepositInfoAsBuyer=Сумма вашего залога: {0} -createOffer.minSecurityDepositUsed=Min. buyer security deposit is used +createOffer.minSecurityDepositUsed=Минимальный залог используется +createOffer.buyerAsTakerWithoutDeposit=Залог от покупателя не требуется (защищено паролем) +createOffer.myDeposit=Мой залог (%) +createOffer.myDepositInfo=Ваш залог составит {0} #################################################################### @@ -508,6 +516,8 @@ takeOffer.fundsBox.networkFee=Oбщая комиссия майнера takeOffer.fundsBox.takeOfferSpinnerInfo=Принятие предложения: {0} takeOffer.fundsBox.paymentLabel=Сделка в Haveno с идентификатором {0} takeOffer.fundsBox.fundsStructure=({0} — залог, {1} — комиссия за сделку, {2} — комиссия майнера) +takeOffer.fundsBox.noFundingRequiredTitle=Не требуется финансирование +takeOffer.fundsBox.noFundingRequiredDescription=Получите пароль предложения от продавца вне Haveno, чтобы принять это предложение. takeOffer.success.headline=Вы успешно приняли предложение. takeOffer.success.info=Статус вашей сделки отображается в разделе \«Папка/Текущие сделки\». takeOffer.error.message=Ошибка при принятии предложения:\n\n{0} @@ -1461,6 +1471,7 @@ offerDetailsWindow.confirm.maker=Подтвердите: разместить п offerDetailsWindow.confirm.taker=Подтвердите: принять предложение {0} биткойн offerDetailsWindow.creationDate=Дата создания offerDetailsWindow.makersOnion=Onion-адрес мейкера +offerDetailsWindow.challenge=Пароль предложения qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. @@ -1684,6 +1695,9 @@ popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign +popup.info.buyerAsTakerWithoutDeposit.headline=Депозит от покупателя не требуется +popup.info.buyerAsTakerWithoutDeposit=Ваше предложение не потребует залога или комиссии от покупателя XMR.\n\nЧтобы принять ваше предложение, вы должны поделиться парольной фразой с вашим торговым партнером вне Haveno.\n\nПарольная фраза генерируется автоматически и отображается в деталях предложения после его создания. + #################################################################### # Notifications #################################################################### @@ -1809,6 +1823,7 @@ navigation.support=\«Поддержка\» formatter.formatVolumeLabel={0} сумма {1} formatter.makerTaker=Мейкер как {0} {1} / Тейкер как {2} {3} +formatter.makerTakerLocked=Мейкер как {0} {1} / Тейкер как {2} {3} 🔒 formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2} formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2} formatter.youAre=Вы {0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index db2855c5..81955b6f 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -40,6 +40,8 @@ shared.buyMonero=ซื้อ monero (บิตคอยน์) shared.sellMonero=ขาย monero (บิตคอยน์) shared.buyCurrency=ซื้อ {0} shared.sellCurrency=ขาย {0} +shared.buyCurrencyLocked=ซื้อ {0} 🔒 +shared.sellCurrencyLocked=ขาย {0} 🔒 shared.buyingXMRWith=การซื้อ XMR กับ {0} shared.sellingXMRFor=การขาย XMR แก่ {0} shared.buyingCurrency=การซื้อ {0} (การขาย XMR) @@ -330,6 +332,7 @@ offerbook.createOffer=สร้างข้อเสนอ offerbook.takeOffer=รับข้อเสนอ offerbook.takeOfferToBuy=Take offer to buy {0} offerbook.takeOfferToSell=Take offer to sell {0} +offerbook.takeOffer.enterChallenge=กรอกพาสเฟรสข้อเสนอ offerbook.trader=Trader (เทรดเดอร์) offerbook.offerersBankId=รหัสธนาคารของผู้สร้าง (BIC / SWIFT): {0} offerbook.offerersBankName=ชื่อธนาคารของผู้สร้าง: {0} @@ -339,7 +342,9 @@ offerbook.offerersAcceptedBankSeats=ยอมรับตำแหน่งป offerbook.availableOffers=ข้อเสนอที่พร้อมใช้งาน offerbook.filterByCurrency=กรองตามสกุลเงิน offerbook.filterByPaymentMethod=ตัวกรองตามวิธีการชำระเงิน -offerbook.matchingOffers=Offers matching my accounts +offerbook.matchingOffers=ข้อเสนอที่ตรงกับบัญชีของฉัน +offerbook.filterNoDeposit=ไม่มีเงินมัดจำ +offerbook.noDepositOffers=ข้อเสนอที่ไม่มีเงินมัดจำ (ต้องการรหัสผ่าน) offerbook.timeSinceSigning=Account info offerbook.timeSinceSigning.info=This account was verified and {0} offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts @@ -484,7 +489,10 @@ createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) createOffer.setDepositForBothTraders=Set both traders' security deposit (%) createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} -createOffer.minSecurityDepositUsed=Min. buyer security deposit is used +createOffer.minSecurityDepositUsed=เงินประกันความปลอดภัยขั้นต่ำถูกใช้ +createOffer.buyerAsTakerWithoutDeposit=ไม่ต้องวางมัดจำจากผู้ซื้อ (ป้องกันด้วยรหัสผ่าน) +createOffer.myDeposit=เงินประกันความปลอดภัยของฉัน (%) +createOffer.myDepositInfo=เงินประกันความปลอดภัยของคุณจะเป็น {0} #################################################################### @@ -508,6 +516,8 @@ takeOffer.fundsBox.networkFee=ยอดรวมค่าธรรมเนี takeOffer.fundsBox.takeOfferSpinnerInfo=ยอมรับข้อเสนอ: {0} takeOffer.fundsBox.paymentLabel=การซื้อขาย Haveno ด้วย ID {0} takeOffer.fundsBox.fundsStructure=({0} เงินประกัน {1} ค่าธรรมเนียมการซื้อขาย {2} ค่าธรรมเนียมการขุด) +takeOffer.fundsBox.noFundingRequiredTitle=ไม่ต้องใช้เงินทุน +takeOffer.fundsBox.noFundingRequiredDescription=รับรหัสผ่านข้อเสนอจากผู้ขายภายนอก Haveno เพื่อรับข้อเสนอนี้ takeOffer.success.headline=คุณได้รับข้อเสนอเป็นที่เรีบยร้อยแล้ว takeOffer.success.info=คุณสามารถดูสถานะการค้าของคุณได้ที่ \ "Portfolio (แฟ้มผลงาน) / เปิดการซื้อขาย \" takeOffer.error.message=เกิดข้อผิดพลาดขณะรับข้อเสนอ\n\n{0} @@ -1461,6 +1471,7 @@ offerDetailsWindow.confirm.maker=ยืนยัน: ยื่นข้อเส offerDetailsWindow.confirm.taker=ยืนยัน: รับข้อเสนอไปยัง {0} บิทคอยน์ offerDetailsWindow.creationDate=วันที่สร้าง offerDetailsWindow.makersOnion=ที่อยู่ onion ของผู้สร้าง +offerDetailsWindow.challenge=รหัสผ่านสำหรับข้อเสนอ qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. @@ -1684,6 +1695,9 @@ popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign +popup.info.buyerAsTakerWithoutDeposit.headline=ไม่ต้องมีเงินมัดจำจากผู้ซื้อ +popup.info.buyerAsTakerWithoutDeposit=ข้อเสนอของคุณจะไม่ต้องการเงินมัดจำหรือค่าธรรมเนียมจากผู้ซื้อ XMR\n\nในการยอมรับข้อเสนอของคุณ คุณต้องแบ่งปันรหัสผ่านกับคู่ค้าการค้าของคุณภายนอก Haveno\n\nรหัสผ่านจะถูกสร้างโดยอัตโนมัติและแสดงในรายละเอียดข้อเสนอหลังจากการสร้าง + #################################################################### # Notifications #################################################################### @@ -1809,6 +1823,7 @@ navigation.support=\"ช่วยเหลือและสนับสนุ formatter.formatVolumeLabel={0} จำนวนยอด{1} formatter.makerTaker=ผู้สร้าง เป็น {0} {1} / ผู้รับเป็น {2} {3} +formatter.makerTakerLocked=ผู้สร้าง เป็น {0} {1} / ผู้รับเป็น {2} {3} 🔒 formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2} formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2} formatter.youAre=คุณคือ {0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index d8436a0f..275b9567 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -43,6 +43,8 @@ shared.buyMonero=Monero Satın Al shared.sellMonero=Monero Sat shared.buyCurrency={0} satın al shared.sellCurrency={0} sat +shared.buyCurrencyLocked={0} satın al 🔒 +shared.sellCurrencyLocked={0} sat 🔒 shared.buyingXMRWith={0} ile XMR satın alınıyor shared.sellingXMRFor={0} karşılığında XMR satılıyor shared.buyingCurrency={0} satın alınıyor (XMR satılıyor) @@ -346,6 +348,7 @@ market.trades.showVolumeInUSD=Hacmi USD olarak göster offerbook.createOffer=Teklif oluştur offerbook.takeOffer=Teklif al offerbook.takeOffer.createAccount=Hesap oluştur ve teklifi al +offerbook.takeOffer.enterChallenge=Teklif şifresini girin offerbook.trader=Yatırımcı offerbook.offerersBankId=Yapıcının banka kimliği (BIC/SWIFT): {0} offerbook.offerersBankName=Yapıcının banka adı: {0} @@ -357,6 +360,8 @@ offerbook.availableOffersToSell={0} için {1} sat offerbook.filterByCurrency=Para birimini seç offerbook.filterByPaymentMethod=Ödeme yöntemini seç offerbook.matchingOffers=Uygun Teklif +offerbook.filterNoDeposit=Depozito yok +offerbook.noDepositOffers=Depozitosuz teklifler (şifre gereklidir) offerbook.timeSinceSigning=Hesap bilgisi offerbook.timeSinceSigning.info.arbitrator=bir hakem tarafından imzalandı ve eş hesaplarını imzalayabilir offerbook.timeSinceSigning.info.peer=bir eş tarafından imzalandı, limitlerin kaldırılması için %d gün bekleniyor @@ -524,7 +529,10 @@ createOffer.setDepositAsBuyer=Alıcı olarak benim güvenlik teminatımı ayarla createOffer.setDepositForBothTraders=Tüccarların güvenlik teminatı (%) createOffer.securityDepositInfo=Alıcının güvenlik teminatı {0} olacak createOffer.securityDepositInfoAsBuyer=Alıcı olarak güvenlik teminatınız {0} olacak -createOffer.minSecurityDepositUsed=Minimum alıcı güvenlik teminatı kullanıldı +createOffer.minSecurityDepositUsed=Minimum güvenlik depozitosu kullanılır +createOffer.buyerAsTakerWithoutDeposit=Alıcıdan depozito gerekmez (şifre korumalı) +createOffer.myDeposit=Güvenlik depozitam (%) +createOffer.myDepositInfo=Güvenlik depozitonuz {0} olacaktır. #################################################################### @@ -550,6 +558,8 @@ takeOffer.fundsBox.networkFee=Toplam madencilik ücretleri takeOffer.fundsBox.takeOfferSpinnerInfo=Teklif alınıyor: {0} takeOffer.fundsBox.paymentLabel=ID {0} ile Haveno işlemi takeOffer.fundsBox.fundsStructure=({0} güvenlik teminatı, {1} işlem ücreti) +takeOffer.fundsBox.noFundingRequiredTitle=Fonlama gerekmez +takeOffer.fundsBox.noFundingRequiredDescription=Bu teklifi almak için satıcıdan passphrase'i Haveno dışında alınız. takeOffer.success.headline=Teklifi başarıyla aldınız. takeOffer.success.info=İşleminizin durumunu \"Portföy/Açık işlemler\" kısmında görebilirsiniz. takeOffer.error.message=Teklif alımı sırasında bir hata oluştu.\n\n{0} @@ -1963,6 +1973,7 @@ offerDetailsWindow.confirm.taker=Onayla: {0} monero teklifi al offerDetailsWindow.confirm.takerCrypto=Onayla: {0} {1} teklifi al offerDetailsWindow.creationDate=Oluşturma tarihi offerDetailsWindow.makersOnion=Yapıcı'nın onion adresi +offerDetailsWindow.challenge=Teklif şifresi qRCodeWindow.headline=QR Kodu qRCodeWindow.msg=Harici cüzdanınızdan Haveno cüzdanınızı finanse etmek için bu QR kodunu kullanın. @@ -2260,6 +2271,9 @@ popup.accountSigning.unsignedPubKeys.signed=Pubkey'ler imzalandı popup.accountSigning.unsignedPubKeys.result.signed=İmzalanmış pubkey'ler popup.accountSigning.unsignedPubKeys.result.failed=İmzalama başarısız oldu +popup.info.buyerAsTakerWithoutDeposit.headline=Alıcıdan depozito gerekmez +popup.info.buyerAsTakerWithoutDeposit=Teklifiniz, XMR alıcısından güvenlik depozitosu veya ücret talep etmeyecektir.\n\nTeklifinizi kabul etmek için, ticaret ortağınızla Haveno dışında bir şifre paylaşmalısınız.\n\nŞifre otomatik olarak oluşturulur ve oluşturulduktan sonra teklif detaylarında görüntülenir. + popup.info.torMigration.msg=Haveno düğümünüz muhtemelen eski bir Tor v2 adresi kullanıyor. \ Lütfen Haveno düğümünüzü bir Tor v3 adresine geçirin. \ Önceden veri dizininizi yedeklediğinizden emin olun. @@ -2399,6 +2413,7 @@ navigation.support="Destek" formatter.formatVolumeLabel={0} miktar{1} formatter.makerTaker=Yapan olarak {0} {1} / Alan olarak {2} {3} +formatter.makerTakerLocked=Yapıcı olarak {0} {1} / Alan olarak {2} {3} 🔒 formatter.youAreAsMaker=Yapan sizsiniz: {1} {0} (maker) / Alan: {3} {2} formatter.youAreAsTaker=Alan sizsiniz: {1} {0} (taker) / Yapan: {3} {2} formatter.youAre=Şu anda sizsiniz {0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 828e81e4..8e541353 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -40,6 +40,8 @@ shared.buyMonero=Mua monero shared.sellMonero=Bán monero shared.buyCurrency=Mua {0} shared.sellCurrency=Bán {0} +shared.buyCurrencyLocked=Mua {0} 🔒 +shared.sellCurrencyLocked=Bán {0} 🔒 shared.buyingXMRWith=đang mua XMR với {0} shared.sellingXMRFor=đang bán XMR với {0} shared.buyingCurrency=đang mua {0} (đang bán XMR) @@ -330,6 +332,7 @@ offerbook.createOffer=Tạo chào giá offerbook.takeOffer=Nhận chào giá offerbook.takeOfferToBuy=Nhận chào giá mua {0} offerbook.takeOfferToSell=Nhận chào giá bán {0} +offerbook.takeOffer.enterChallenge=Nhập mật khẩu đề nghị offerbook.trader=Trader offerbook.offerersBankId=ID ngân hàng của người tạo (BIC/SWIFT): {0} offerbook.offerersBankName=Tên ngân hàng của người tạo: {0} @@ -339,7 +342,9 @@ offerbook.offerersAcceptedBankSeats=Các quốc gia có ngân hàng được ch offerbook.availableOffers=Các chào giá hiện có offerbook.filterByCurrency=Lọc theo tiền tệ offerbook.filterByPaymentMethod=Lọc theo phương thức thanh toán -offerbook.matchingOffers=Offers matching my accounts +offerbook.matchingOffers=Các ưu đãi phù hợp với tài khoản của tôi +offerbook.filterNoDeposit=Không đặt cọc +offerbook.noDepositOffers=Các ưu đãi không yêu cầu đặt cọc (cần mật khẩu) offerbook.timeSinceSigning=Account info offerbook.timeSinceSigning.info=This account was verified and {0} offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts @@ -484,7 +489,10 @@ createOffer.setDepositAsBuyer=Cài đặt tiền đặt cọc của tôi với v createOffer.setDepositForBothTraders=Set both traders' security deposit (%) createOffer.securityDepositInfo=Số tiền đặt cọc cho người mua của bạn sẽ là {0} createOffer.securityDepositInfoAsBuyer=Số tiền đặt cọc của bạn với vai trò người mua sẽ là {0} -createOffer.minSecurityDepositUsed=Min. buyer security deposit is used +createOffer.minSecurityDepositUsed=Khoản tiền đặt cọc bảo mật tối thiểu được sử dụng +createOffer.buyerAsTakerWithoutDeposit=Không cần đặt cọc từ người mua (được bảo vệ bằng mật khẩu) +createOffer.myDeposit=Tiền đặt cọc bảo mật của tôi (%) +createOffer.myDepositInfo=Khoản tiền đặt cọc của bạn sẽ là {0} #################################################################### @@ -508,6 +516,8 @@ takeOffer.fundsBox.networkFee=Tổng phí đào takeOffer.fundsBox.takeOfferSpinnerInfo=Chấp nhận đề xuất: {0} takeOffer.fundsBox.paymentLabel=giao dịch Haveno có ID {0} takeOffer.fundsBox.fundsStructure=({0} tiền gửi đại lý, {1} phí giao dịch, {2} phí đào) +takeOffer.fundsBox.noFundingRequiredTitle=Không cần tài trợ +takeOffer.fundsBox.noFundingRequiredDescription=Lấy mật khẩu giao dịch từ người bán ngoài Haveno để nhận đề nghị này. takeOffer.success.headline=Bạn đã nhận báo giá thành công. takeOffer.success.info=Bạn có thể xem trạng thái giao dịch của bạn tại \"Portfolio/Các giao dịch mở\". takeOffer.error.message=Có lỗi xảy ra khi nhận báo giá.\n\n{0} @@ -1463,6 +1473,7 @@ offerDetailsWindow.confirm.maker=Xác nhận: Đặt chào giá cho {0} monero offerDetailsWindow.confirm.taker=Xác nhận: Nhận chào giáo cho {0} monero offerDetailsWindow.creationDate=Ngày tạo offerDetailsWindow.makersOnion=Địa chỉ onion của người tạo +offerDetailsWindow.challenge=Mã bảo vệ giao dịch qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. @@ -1686,6 +1697,9 @@ popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign +popup.info.buyerAsTakerWithoutDeposit.headline=Không cần đặt cọc từ người mua +popup.info.buyerAsTakerWithoutDeposit=Lời đề nghị của bạn sẽ không yêu cầu khoản đặt cọc bảo mật hoặc phí từ người mua XMR.\n\nĐể chấp nhận lời đề nghị của bạn, bạn phải chia sẻ một mật khẩu với đối tác giao dịch ngoài Haveno.\n\nMật khẩu được tạo tự động và hiển thị trong chi tiết lời đề nghị sau khi tạo. + #################################################################### # Notifications #################################################################### @@ -1811,6 +1825,7 @@ navigation.support=\"Hỗ trợ\" formatter.formatVolumeLabel={0} giá trị {1} formatter.makerTaker=Người tạo là {0} {1} / Người nhận là {2} {3} +formatter.makerTakerLocked=Người tạo là {0} {1} / Người nhận là {2} {3} 🔒 formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2} formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2} formatter.youAre=Bạn là {0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 684ba4c6..0f42566e 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -40,6 +40,8 @@ shared.buyMonero=买入比特币 shared.sellMonero=卖出比特币 shared.buyCurrency=买入 {0} shared.sellCurrency=卖出 {0} +shared.buyCurrencyLocked=买入 {0} 🔒 +shared.sellCurrencyLocked=卖出 {0} 🔒 shared.buyingXMRWith=用 {0} 买入 XMR shared.sellingXMRFor=卖出 XMR 为 {0} shared.buyingCurrency=买入 {0}(卖出 XMR) @@ -330,6 +332,7 @@ offerbook.createOffer=创建报价 offerbook.takeOffer=接受报价 offerbook.takeOfferToBuy=接受报价来收购 {0} offerbook.takeOfferToSell=接受报价来出售 {0} +offerbook.takeOffer.enterChallenge=输入报价密码 offerbook.trader=商人 offerbook.offerersBankId=卖家的银行 ID(BIC/SWIFT):{0} offerbook.offerersBankName=卖家的银行名称:{0} @@ -339,7 +342,9 @@ offerbook.offerersAcceptedBankSeats=接受的银行所在国家(买家):\n offerbook.availableOffers=可用报价 offerbook.filterByCurrency=以货币筛选 offerbook.filterByPaymentMethod=以支付方式筛选 -offerbook.matchingOffers=Offers matching my accounts +offerbook.matchingOffers=匹配我的账户的报价 +offerbook.filterNoDeposit=无押金 +offerbook.noDepositOffers=无押金的报价(需要密码短语) offerbook.timeSinceSigning=账户信息 offerbook.timeSinceSigning.info=此账户已验证,{0} offerbook.timeSinceSigning.info.arbitrator=由仲裁员验证,并可以验证伙伴账户 @@ -485,7 +490,10 @@ createOffer.setDepositAsBuyer=设置自己作为买家的保证金(%) createOffer.setDepositForBothTraders=设置双方的保证金比例(%) createOffer.securityDepositInfo=您的买家的保证金将会是 {0} createOffer.securityDepositInfoAsBuyer=您作为买家的保证金将会是 {0} -createOffer.minSecurityDepositUsed=已使用最低买家保证金 +createOffer.minSecurityDepositUsed=最低安全押金已使用 +createOffer.buyerAsTakerWithoutDeposit=无需买家支付押金(使用口令保护) +createOffer.myDeposit=我的安全押金 (%) +createOffer.myDepositInfo=您的保证金为 {0} #################################################################### @@ -509,6 +517,8 @@ takeOffer.fundsBox.networkFee=总共挖矿手续费 takeOffer.fundsBox.takeOfferSpinnerInfo=接受报价:{0} takeOffer.fundsBox.paymentLabel=Haveno 交易 ID {0} takeOffer.fundsBox.fundsStructure=({0} 保证金,{1} 交易费,{2} 采矿费) +takeOffer.fundsBox.noFundingRequiredTitle=无需资金 +takeOffer.fundsBox.noFundingRequiredDescription=从卖方处获取交易密码(在Haveno之外)以接受此报价。 takeOffer.success.headline=你已成功下单一个报价。 takeOffer.success.info=你可以在“业务/未完成交易”页面内查看您的未完成交易。 takeOffer.error.message=下单时发生了一个错误。\n\n{0} @@ -1465,6 +1475,7 @@ offerDetailsWindow.confirm.maker=确定:发布报价 {0} 比特币 offerDetailsWindow.confirm.taker=确定:下单买入 {0} 比特币 offerDetailsWindow.creationDate=创建时间 offerDetailsWindow.makersOnion=卖家的匿名地址 +offerDetailsWindow.challenge=提供密码 qRCodeWindow.headline=二维码 qRCodeWindow.msg=请使用二维码从外部钱包充值至 Haveno 钱包 @@ -1693,6 +1704,9 @@ popup.accountSigning.unsignedPubKeys.signed=公钥已被验证 popup.accountSigning.unsignedPubKeys.result.signed=已验证公钥 popup.accountSigning.unsignedPubKeys.result.failed=未能验证公钥 +popup.info.buyerAsTakerWithoutDeposit.headline=买家无需支付保证金 +popup.info.buyerAsTakerWithoutDeposit=您的报价将不需要来自XMR买家的保证金或费用。\n\n要接受您的报价,您必须与交易伙伴在Haveno外共享一个密码短语。\n\n密码短语会自动生成,并在创建后显示在报价详情中。 + #################################################################### # Notifications #################################################################### @@ -1818,6 +1832,7 @@ navigation.support=“帮助” formatter.formatVolumeLabel={0} 数量 {1} formatter.makerTaker=卖家 {0} {1} / 买家 {2} {3} +formatter.makerTakerLocked=卖家 {0} {1} / 买家 {2} {3} 🔒 formatter.youAreAsMaker=您是 {1} {0} 卖家 / 买家是 {3} {2} formatter.youAreAsTaker=您是 {1} {0} 买家 / 卖家是 {3} {2} formatter.youAre=您是 {0} {1} ({2} {3}) diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index ebd7194b..ccdbbcdf 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -40,6 +40,8 @@ shared.buyMonero=買入比特幣 shared.sellMonero=賣出比特幣 shared.buyCurrency=買入 {0} shared.sellCurrency=賣出 {0} +shared.buyCurrencyLocked=買入 {0} 🔒 +shared.sellCurrencyLocked=賣出 {0} 🔒 shared.buyingXMRWith=用 {0} 買入 XMR shared.sellingXMRFor=賣出 XMR 為 {0} shared.buyingCurrency=買入 {0}(賣出 XMR) @@ -330,6 +332,7 @@ offerbook.createOffer=創建報價 offerbook.takeOffer=接受報價 offerbook.takeOfferToBuy=接受報價來收購 {0} offerbook.takeOfferToSell=接受報價來出售 {0} +offerbook.takeOffer.enterChallenge=輸入報價密碼 offerbook.trader=商人 offerbook.offerersBankId=賣家的銀行 ID(BIC/SWIFT):{0} offerbook.offerersBankName=賣家的銀行名稱:{0} @@ -339,7 +342,9 @@ offerbook.offerersAcceptedBankSeats=接受的銀行所在國家(買家):\n offerbook.availableOffers=可用報價 offerbook.filterByCurrency=以貨幣篩選 offerbook.filterByPaymentMethod=以支付方式篩選 -offerbook.matchingOffers=Offers matching my accounts +offerbook.matchingOffers=符合我的帳戶的報價 +offerbook.filterNoDeposit=無押金 +offerbook.noDepositOffers=無押金的報價(需要密碼短語) offerbook.timeSinceSigning=賬户信息 offerbook.timeSinceSigning.info=此賬户已驗證,{0} offerbook.timeSinceSigning.info.arbitrator=由仲裁員驗證,並可以驗證夥伴賬户 @@ -485,7 +490,10 @@ createOffer.setDepositAsBuyer=設置自己作為買家的保證金(%) createOffer.setDepositForBothTraders=設置雙方的保證金比例(%) createOffer.securityDepositInfo=您的買家的保證金將會是 {0} createOffer.securityDepositInfoAsBuyer=您作為買家的保證金將會是 {0} -createOffer.minSecurityDepositUsed=已使用最低買家保證金 +createOffer.minSecurityDepositUsed=最低保證金已使用 +createOffer.buyerAsTakerWithoutDeposit=買家無需支付保證金(通行密碼保護) +createOffer.myDeposit=我的保證金(%) +createOffer.myDepositInfo=您的保證金將為 {0} #################################################################### @@ -509,6 +517,8 @@ takeOffer.fundsBox.networkFee=總共挖礦手續費 takeOffer.fundsBox.takeOfferSpinnerInfo=接受報價:{0} takeOffer.fundsBox.paymentLabel=Haveno 交易 ID {0} takeOffer.fundsBox.fundsStructure=({0} 保證金,{1} 交易費,{2} 採礦費) +takeOffer.fundsBox.noFundingRequiredTitle=無需資金 +takeOffer.fundsBox.noFundingRequiredDescription=從賣家那裡在 Haveno 之外獲取優惠密碼以接受此優惠。 takeOffer.success.headline=你已成功下單一個報價。 takeOffer.success.info=你可以在“業務/未完成交易”頁面內查看您的未完成交易。 takeOffer.error.message=下單時發生了一個錯誤。\n\n{0} @@ -1465,6 +1475,7 @@ offerDetailsWindow.confirm.maker=確定:發佈報價 {0} 比特幣 offerDetailsWindow.confirm.taker=確定:下單買入 {0} 比特幣 offerDetailsWindow.creationDate=創建時間 offerDetailsWindow.makersOnion=賣家的匿名地址 +offerDetailsWindow.challenge=提供密碼 qRCodeWindow.headline=二維碼 qRCodeWindow.msg=請使用二維碼從外部錢包充值至 Haveno 錢包 @@ -1687,6 +1698,9 @@ popup.accountSigning.unsignedPubKeys.signed=公鑰已被驗證 popup.accountSigning.unsignedPubKeys.result.signed=已驗證公鑰 popup.accountSigning.unsignedPubKeys.result.failed=未能驗證公鑰 +popup.info.buyerAsTakerWithoutDeposit.headline=買家無需支付保證金 +popup.info.buyerAsTakerWithoutDeposit=您的報價不需要來自XMR買家的保證金或費用。\n\n要接受您的報價,您必須與您的交易夥伴在Haveno之外分享密碼短語。\n\n密碼短語會自動生成並在報價創建後顯示在報價詳情中。 + #################################################################### # Notifications #################################################################### @@ -1812,6 +1826,7 @@ navigation.support=“幫助” formatter.formatVolumeLabel={0} 數量 {1} formatter.makerTaker=賣家 {0} {1} / 買家 {2} {3} +formatter.makerTakerLocked=賣家 {0} {1} / 買家 {2} {3} 🔒 formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2} formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2} formatter.youAre=您是 {0} {1} ({2} {3}) diff --git a/daemon/src/main/java/haveno/daemon/grpc/GrpcOffersService.java b/daemon/src/main/java/haveno/daemon/grpc/GrpcOffersService.java index 081a0949..7768522b 100644 --- a/daemon/src/main/java/haveno/daemon/grpc/GrpcOffersService.java +++ b/daemon/src/main/java/haveno/daemon/grpc/GrpcOffersService.java @@ -150,10 +150,12 @@ class GrpcOffersService extends OffersImplBase { req.getMarketPriceMarginPct(), req.getAmount(), req.getMinAmount(), - req.getBuyerSecurityDepositPct(), + req.getSecurityDepositPct(), req.getTriggerPrice(), req.getReserveExactAmount(), req.getPaymentAccountId(), + req.getIsPrivateOffer(), + req.getBuyerAsTakerWithoutDeposit(), offer -> { // This result handling consumer's accept operation will return // the new offer to the gRPC client after async placement is done. diff --git a/daemon/src/main/java/haveno/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/haveno/daemon/grpc/GrpcTradesService.java index 123078b2..286fccf5 100644 --- a/daemon/src/main/java/haveno/daemon/grpc/GrpcTradesService.java +++ b/daemon/src/main/java/haveno/daemon/grpc/GrpcTradesService.java @@ -138,6 +138,7 @@ class GrpcTradesService extends TradesImplBase { coreApi.takeOffer(req.getOfferId(), req.getPaymentAccountId(), req.getAmount(), + req.getChallenge(), trade -> { TradeInfo tradeInfo = toTradeInfo(trade); var reply = TakeOfferReply.newBuilder() diff --git a/desktop/src/main/java/haveno/desktop/components/paymentmethods/PaymentMethodForm.java b/desktop/src/main/java/haveno/desktop/components/paymentmethods/PaymentMethodForm.java index 64d0066d..5abdee0d 100644 --- a/desktop/src/main/java/haveno/desktop/components/paymentmethods/PaymentMethodForm.java +++ b/desktop/src/main/java/haveno/desktop/components/paymentmethods/PaymentMethodForm.java @@ -184,14 +184,14 @@ public abstract class PaymentMethodForm { Res.get("payment.maxPeriodAndLimitCrypto", getTimeText(hours), HavenoUtils.formatXmr(accountAgeWitnessService.getMyTradeLimit( - paymentAccount, tradeCurrency.getCode(), OfferDirection.BUY), true)) + paymentAccount, tradeCurrency.getCode(), OfferDirection.BUY, false), true)) : Res.get("payment.maxPeriodAndLimit", getTimeText(hours), HavenoUtils.formatXmr(accountAgeWitnessService.getMyTradeLimit( - paymentAccount, tradeCurrency.getCode(), OfferDirection.BUY), true), + paymentAccount, tradeCurrency.getCode(), OfferDirection.BUY, false), true), HavenoUtils.formatXmr(accountAgeWitnessService.getMyTradeLimit( - paymentAccount, tradeCurrency.getCode(), OfferDirection.SELL), true), + paymentAccount, tradeCurrency.getCode(), OfferDirection.SELL, false), true), DisplayUtils.formatAccountAge(accountAge)); return limitationsText; } diff --git a/desktop/src/main/java/haveno/desktop/images.css b/desktop/src/main/java/haveno/desktop/images.css index 28887daa..39f0c7e8 100644 --- a/desktop/src/main/java/haveno/desktop/images.css +++ b/desktop/src/main/java/haveno/desktop/images.css @@ -59,6 +59,10 @@ -fx-image: url("../../images/sell_red.png"); } +#image-lock2x { + -fx-image: url("../../images/lock@2x.png"); +} + #image-expand { -fx-image: url("../../images/expand.png"); } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java index 325e7f29..bad2ee75 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java @@ -107,7 +107,8 @@ public abstract class MutableOfferDataModel extends OfferDataModel { protected final ObjectProperty minVolume = new SimpleObjectProperty<>(); // Percentage value of buyer security deposit. E.g. 0.01 means 1% of trade amount - protected final DoubleProperty buyerSecurityDepositPct = new SimpleDoubleProperty(); + protected final DoubleProperty securityDepositPct = new SimpleDoubleProperty(); + protected final BooleanProperty buyerAsTakerWithoutDeposit = new SimpleBooleanProperty(); protected final ObservableList paymentAccounts = FXCollections.observableArrayList(); @@ -166,7 +167,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { reserveExactAmount = preferences.getSplitOfferOutput(); useMarketBasedPrice.set(preferences.isUsePercentageBasedPrice()); - buyerSecurityDepositPct.set(Restrictions.getMinBuyerSecurityDepositAsPercent()); + securityDepositPct.set(Restrictions.getMinSecurityDepositAsPercent()); paymentAccountsChangeListener = change -> fillPaymentAccounts(); } @@ -301,8 +302,10 @@ public abstract class MutableOfferDataModel extends OfferDataModel { useMarketBasedPrice.get() ? null : price.get(), useMarketBasedPrice.get(), useMarketBasedPrice.get() ? marketPriceMargin : 0, - buyerSecurityDepositPct.get(), - paymentAccount); + securityDepositPct.get(), + paymentAccount, + buyerAsTakerWithoutDeposit.get(), // private offer if buyer as taker without deposit + buyerAsTakerWithoutDeposit.get()); } void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { @@ -329,10 +332,10 @@ public abstract class MutableOfferDataModel extends OfferDataModel { } private void setSuggestedSecurityDeposit(PaymentAccount paymentAccount) { - var minSecurityDeposit = Restrictions.getMinBuyerSecurityDepositAsPercent(); + var minSecurityDeposit = Restrictions.getMinSecurityDepositAsPercent(); try { if (getTradeCurrency() == null) { - setBuyerSecurityDeposit(minSecurityDeposit); + setSecurityDepositPct(minSecurityDeposit); return; } // Get average historic prices over for the prior trade period equaling the lock time @@ -355,16 +358,16 @@ public abstract class MutableOfferDataModel extends OfferDataModel { var min = extremes[0]; var max = extremes[1]; if (min == 0d || max == 0d) { - setBuyerSecurityDeposit(minSecurityDeposit); + setSecurityDepositPct(minSecurityDeposit); return; } // Suggested deposit is double the trade range over the previous lock time period, bounded by min/max deposit var suggestedSecurityDeposit = - Math.min(2 * (max - min) / max, Restrictions.getMaxBuyerSecurityDepositAsPercent()); - buyerSecurityDepositPct.set(Math.max(suggestedSecurityDeposit, minSecurityDeposit)); + Math.min(2 * (max - min) / max, Restrictions.getMaxSecurityDepositAsPercent()); + securityDepositPct.set(Math.max(suggestedSecurityDeposit, minSecurityDeposit)); } catch (Throwable t) { log.error(t.toString()); - buyerSecurityDepositPct.set(minSecurityDeposit); + securityDepositPct.set(minSecurityDeposit); } } @@ -455,6 +458,10 @@ public abstract class MutableOfferDataModel extends OfferDataModel { preferences.setUsePercentageBasedPrice(useMarketBasedPrice); } + protected void setBuyerAsTakerWithoutDeposit(boolean buyerAsTakerWithoutDeposit) { + this.buyerAsTakerWithoutDeposit.set(buyerAsTakerWithoutDeposit); + } + public ObservableList getPaymentAccounts() { return paymentAccounts; } @@ -467,11 +474,11 @@ public abstract class MutableOfferDataModel extends OfferDataModel { // disallow offers which no buyer can take due to trade limits on release if (HavenoUtils.isReleasedWithinDays(HavenoUtils.RELEASE_LIMIT_DAYS)) { - return accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrencyCode.get(), OfferDirection.BUY); + return accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrencyCode.get(), OfferDirection.BUY, buyerAsTakerWithoutDeposit.get()); } if (paymentAccount != null) { - return accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrencyCode.get(), direction); + return accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrencyCode.get(), direction, buyerAsTakerWithoutDeposit.get()); } else { return 0; } @@ -560,10 +567,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel { } } - BigInteger getSecurityDeposit() { - return isBuyOffer() ? getBuyerSecurityDeposit() : getSellerSecurityDeposit(); - } - void swapTradeToSavings() { xmrWalletService.resetAddressEntriesForOpenOffer(offerId); } @@ -588,8 +591,8 @@ public abstract class MutableOfferDataModel extends OfferDataModel { this.volume.set(volume); } - protected void setBuyerSecurityDeposit(double value) { - this.buyerSecurityDepositPct.set(value); + protected void setSecurityDepositPct(double value) { + this.securityDepositPct.set(value); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -620,6 +623,10 @@ public abstract class MutableOfferDataModel extends OfferDataModel { return minVolume; } + public ReadOnlyBooleanProperty getBuyerAsTakerWithoutDeposit() { + return buyerAsTakerWithoutDeposit; + } + protected void setMinAmount(BigInteger minAmount) { this.minAmount.set(minAmount); } @@ -644,35 +651,19 @@ public abstract class MutableOfferDataModel extends OfferDataModel { return useMarketBasedPrice; } - ReadOnlyDoubleProperty getBuyerSecurityDepositPct() { - return buyerSecurityDepositPct; + ReadOnlyDoubleProperty getSecurityDepositPct() { + return securityDepositPct; } - protected BigInteger getBuyerSecurityDeposit() { - BigInteger percentOfAmount = CoinUtil.getPercentOfAmount(buyerSecurityDepositPct.get(), amount.get()); - return getBoundedBuyerSecurityDeposit(percentOfAmount); - } - - private BigInteger getSellerSecurityDeposit() { + protected BigInteger getSecurityDeposit() { BigInteger amount = this.amount.get(); - if (amount == null) - amount = BigInteger.ZERO; - - BigInteger percentOfAmount = CoinUtil.getPercentOfAmount( - createOfferService.getSellerSecurityDepositAsDouble(buyerSecurityDepositPct.get()), amount); - return getBoundedSellerSecurityDeposit(percentOfAmount); + if (amount == null) amount = BigInteger.ZERO; + BigInteger percentOfAmount = CoinUtil.getPercentOfAmount(securityDepositPct.get(), amount); + return getBoundedSecurityDeposit(percentOfAmount); } - protected BigInteger getBoundedBuyerSecurityDeposit(BigInteger value) { - // We need to ensure that for small amount values we don't get a too low BTC amount. We limit it with using the - // MinBuyerSecurityDeposit from Restrictions. - return Restrictions.getMinBuyerSecurityDeposit().max(value); - } - - private BigInteger getBoundedSellerSecurityDeposit(BigInteger value) { - // We need to ensure that for small amount values we don't get a too low BTC amount. We limit it with using the - // MinSellerSecurityDeposit from Restrictions. - return Restrictions.getMinSellerSecurityDeposit().max(value); + protected BigInteger getBoundedSecurityDeposit(BigInteger value) { + return Restrictions.getMinSecurityDeposit().max(value); } ReadOnlyObjectProperty totalToPayAsProperty() { @@ -684,7 +675,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { } public BigInteger getMaxMakerFee() { - return HavenoUtils.multiply(amount.get(), HavenoUtils.MAKER_FEE_PCT); + return HavenoUtils.multiply(amount.get(), buyerAsTakerWithoutDeposit.get() ? HavenoUtils.MAKER_FEE_FOR_TAKER_WITHOUT_DEPOSIT_PCT : HavenoUtils.MAKER_FEE_PCT); } boolean canPlaceOffer() { @@ -692,8 +683,8 @@ public abstract class MutableOfferDataModel extends OfferDataModel { GUIUtil.canCreateOrTakeOfferOrShowPopup(user, navigation); } - public boolean isMinBuyerSecurityDeposit() { - return getBuyerSecurityDeposit().compareTo(Restrictions.getMinBuyerSecurityDeposit()) <= 0; + public boolean isMinSecurityDeposit() { + return getSecurityDeposit().compareTo(Restrictions.getMinSecurityDeposit()) <= 0; } public void setTriggerPrice(long triggerPrice) { diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java index 3c6ed097..9115c644 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java @@ -77,6 +77,7 @@ import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.Separator; import javafx.scene.control.TextField; +import javafx.scene.control.ToggleButton; import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -132,16 +133,17 @@ public abstract class MutableOfferView> exten private AutoTooltipButton nextButton, cancelButton1, cancelButton2, placeOfferButton, fundFromSavingsWalletButton; private Button priceTypeToggleButton; private InputTextField fixedPriceTextField, marketBasedPriceTextField, triggerPriceInputTextField; - protected InputTextField amountTextField, minAmountTextField, volumeTextField, buyerSecurityDepositInputTextField; + protected InputTextField amountTextField, minAmountTextField, volumeTextField, securityDepositInputTextField; private TextField currencyTextField; private AddressTextField addressTextField; private BalanceTextField balanceTextField; private CheckBox reserveExactAmountCheckbox; + private ToggleButton buyerAsTakerWithoutDepositSlider; private FundsTextField totalToPayTextField; private Label amountDescriptionLabel, priceCurrencyLabel, priceDescriptionLabel, volumeDescriptionLabel, waitingForFundsLabel, marketBasedPriceLabel, percentagePriceDescriptionLabel, tradeFeeDescriptionLabel, - resultLabel, tradeFeeInXmrLabel, xLabel, fakeXLabel, buyerSecurityDepositLabel, - buyerSecurityDepositPercentageLabel, triggerPriceCurrencyLabel, triggerPriceDescriptionLabel; + resultLabel, tradeFeeInXmrLabel, xLabel, fakeXLabel, securityDepositLabel, + securityDepositPercentageLabel, triggerPriceCurrencyLabel, triggerPriceDescriptionLabel; protected Label amountBtcLabel, volumeCurrencyLabel, minAmountBtcLabel; private ComboBox paymentAccountsComboBox; private ComboBox currencyComboBox; @@ -149,16 +151,16 @@ public abstract class MutableOfferView> exten private VBox currencySelection, fixedPriceBox, percentagePriceBox, currencyTextFieldBox, triggerPriceVBox; private HBox fundingHBox, firstRowHBox, secondRowHBox, placeOfferBox, amountValueCurrencyBox, priceAsPercentageValueCurrencyBox, volumeValueCurrencyBox, priceValueCurrencyBox, - minAmountValueCurrencyBox, advancedOptionsBox, triggerPriceHBox; + minAmountValueCurrencyBox, securityDepositAndFeeBox, triggerPriceHBox; private Subscription isWaitingForFundsSubscription, balanceSubscription; private ChangeListener amountFocusedListener, minAmountFocusedListener, volumeFocusedListener, - buyerSecurityDepositFocusedListener, priceFocusedListener, placeOfferCompletedListener, + securityDepositFocusedListener, priceFocusedListener, placeOfferCompletedListener, priceAsPercentageFocusedListener, getShowWalletFundedNotificationListener, - isMinBuyerSecurityDepositListener, triggerPriceFocusedListener; + isMinSecurityDepositListener, buyerAsTakerWithoutDepositListener, triggerPriceFocusedListener; private ChangeListener missingCoinListener; private ChangeListener tradeCurrencyCodeListener, errorMessageListener, - marketPriceMarginListener, volumeListener, buyerSecurityDepositInBTCListener; + marketPriceMarginListener, volumeListener, securityDepositInXMRListener; private ChangeListener marketPriceAvailableListener; private EventHandler currencyComboBoxSelectionHandler, paymentAccountsComboBoxSelectionHandler; private OfferView.CloseHandler closeHandler; @@ -168,7 +170,7 @@ public abstract class MutableOfferView> exten private final HashMap paymentAccountWarningDisplayed = new HashMap<>(); private boolean zelleWarningDisplayed, fasterPaymentsWarningDisplayed, isActivated; private InfoInputTextField marketBasedPriceInfoInputTextField, volumeInfoInputTextField, - buyerSecurityDepositInfoInputTextField, triggerPriceInfoInputTextField; + securityDepositInfoInputTextField, triggerPriceInfoInputTextField; private Text xIcon, fakeXIcon; @Setter @@ -252,6 +254,8 @@ public abstract class MutableOfferView> exten Label popOverLabel = OfferViewUtil.createPopOverLabel(Res.get("createOffer.triggerPrice.tooltip")); triggerPriceInfoInputTextField.setContentForPopOver(popOverLabel, AwesomeIcon.SHIELD); + + buyerAsTakerWithoutDepositSlider.setSelected(model.dataModel.getBuyerAsTakerWithoutDeposit().get()); } } @@ -323,6 +327,9 @@ public abstract class MutableOfferView> exten fundFromSavingsWalletButton.setId("sell-button"); } + buyerAsTakerWithoutDepositSlider.setVisible(model.isSellOffer()); + buyerAsTakerWithoutDepositSlider.setManaged(model.isSellOffer()); + placeOfferButton.updateText(placeOfferButtonLabel); updatePriceToggle(); } @@ -375,8 +382,11 @@ public abstract class MutableOfferView> exten setDepositTitledGroupBg.setVisible(false); setDepositTitledGroupBg.setManaged(false); - advancedOptionsBox.setVisible(false); - advancedOptionsBox.setManaged(false); + securityDepositAndFeeBox.setVisible(false); + securityDepositAndFeeBox.setManaged(false); + + buyerAsTakerWithoutDepositSlider.setVisible(false); + buyerAsTakerWithoutDepositSlider.setManaged(false); updateQrCode(); @@ -556,8 +566,8 @@ public abstract class MutableOfferView> exten volumeTextField.promptTextProperty().bind(model.volumePromptLabel); totalToPayTextField.textProperty().bind(model.totalToPay); addressTextField.amountAsProperty().bind(model.getDataModel().getMissingCoin()); - buyerSecurityDepositInputTextField.textProperty().bindBidirectional(model.buyerSecurityDeposit); - buyerSecurityDepositLabel.textProperty().bind(model.buyerSecurityDepositLabel); + securityDepositInputTextField.textProperty().bindBidirectional(model.securityDeposit); + securityDepositLabel.textProperty().bind(model.securityDepositLabel); tradeFeeInXmrLabel.textProperty().bind(model.tradeFeeInXmrWithFiat); tradeFeeDescriptionLabel.textProperty().bind(model.tradeFeeDescription); @@ -567,7 +577,7 @@ public abstract class MutableOfferView> exten fixedPriceTextField.validationResultProperty().bind(model.priceValidationResult); triggerPriceInputTextField.validationResultProperty().bind(model.triggerPriceValidationResult); volumeTextField.validationResultProperty().bind(model.volumeValidationResult); - buyerSecurityDepositInputTextField.validationResultProperty().bind(model.buyerSecurityDepositValidationResult); + securityDepositInputTextField.validationResultProperty().bind(model.securityDepositValidationResult); // funding fundingHBox.visibleProperty().bind(model.getDataModel().getIsXmrWalletFunded().not().and(model.showPayFundsScreenDisplayed)); @@ -604,8 +614,8 @@ public abstract class MutableOfferView> exten volumeTextField.promptTextProperty().unbindBidirectional(model.volume); totalToPayTextField.textProperty().unbind(); addressTextField.amountAsProperty().unbind(); - buyerSecurityDepositInputTextField.textProperty().unbindBidirectional(model.buyerSecurityDeposit); - buyerSecurityDepositLabel.textProperty().unbind(); + securityDepositInputTextField.textProperty().unbindBidirectional(model.securityDeposit); + securityDepositLabel.textProperty().unbind(); tradeFeeInXmrLabel.textProperty().unbind(); tradeFeeDescriptionLabel.textProperty().unbind(); tradeFeeInXmrLabel.visibleProperty().unbind(); @@ -617,7 +627,7 @@ public abstract class MutableOfferView> exten fixedPriceTextField.validationResultProperty().unbind(); triggerPriceInputTextField.validationResultProperty().unbind(); volumeTextField.validationResultProperty().unbind(); - buyerSecurityDepositInputTextField.validationResultProperty().unbind(); + securityDepositInputTextField.validationResultProperty().unbind(); // funding fundingHBox.visibleProperty().unbind(); @@ -679,9 +689,9 @@ public abstract class MutableOfferView> exten model.onFocusOutVolumeTextField(oldValue, newValue); volumeTextField.setText(model.volume.get()); }; - buyerSecurityDepositFocusedListener = (o, oldValue, newValue) -> { - model.onFocusOutBuyerSecurityDepositTextField(oldValue, newValue); - buyerSecurityDepositInputTextField.setText(model.buyerSecurityDeposit.get()); + securityDepositFocusedListener = (o, oldValue, newValue) -> { + model.onFocusOutSecurityDepositTextField(oldValue, newValue); + securityDepositInputTextField.setText(model.securityDeposit.get()); }; triggerPriceFocusedListener = (o, oldValue, newValue) -> { @@ -750,12 +760,11 @@ public abstract class MutableOfferView> exten } }; - buyerSecurityDepositInBTCListener = (observable, oldValue, newValue) -> { + securityDepositInXMRListener = (observable, oldValue, newValue) -> { if (!newValue.equals("")) { - Label depositInBTCInfo = OfferViewUtil.createPopOverLabel(model.getSecurityDepositPopOverLabel(newValue)); - buyerSecurityDepositInfoInputTextField.setContentForInfoPopOver(depositInBTCInfo); + updateSecurityDepositLabels(); } else { - buyerSecurityDepositInfoInputTextField.setContentForInfoPopOver(null); + securityDepositInfoInputTextField.setContentForInfoPopOver(null); } }; @@ -805,17 +814,29 @@ public abstract class MutableOfferView> exten } }; - isMinBuyerSecurityDepositListener = ((observable, oldValue, newValue) -> { - if (newValue) { - // show BTC - buyerSecurityDepositPercentageLabel.setText(Res.getBaseCurrencyCode()); - buyerSecurityDepositInputTextField.setDisable(true); - } else { - // show % - buyerSecurityDepositPercentageLabel.setText("%"); - buyerSecurityDepositInputTextField.setDisable(false); - } + isMinSecurityDepositListener = ((observable, oldValue, newValue) -> { + updateSecurityDepositLabels(); }); + + buyerAsTakerWithoutDepositListener = ((observable, oldValue, newValue) -> { + updateSecurityDepositLabels(); + }); + } + + private void updateSecurityDepositLabels() { + if (model.isMinSecurityDeposit.get()) { + // show XMR + securityDepositPercentageLabel.setText(Res.getBaseCurrencyCode()); + securityDepositInputTextField.setDisable(true); + } else { + // show % + securityDepositPercentageLabel.setText("%"); + securityDepositInputTextField.setDisable(model.getDataModel().buyerAsTakerWithoutDeposit.get()); + } + if (model.securityDepositInXMR.get() != null && !model.securityDepositInXMR.get().equals("")) { + Label depositInBTCInfo = OfferViewUtil.createPopOverLabel(model.getSecurityDepositPopOverLabel(model.securityDepositInXMR.get())); + securityDepositInfoInputTextField.setContentForInfoPopOver(depositInBTCInfo); + } } private void updateQrCode() { @@ -856,8 +877,9 @@ public abstract class MutableOfferView> exten model.marketPriceMargin.addListener(marketPriceMarginListener); model.volume.addListener(volumeListener); model.getDataModel().missingCoin.addListener(missingCoinListener); - model.buyerSecurityDepositInBTC.addListener(buyerSecurityDepositInBTCListener); - model.isMinBuyerSecurityDeposit.addListener(isMinBuyerSecurityDepositListener); + model.securityDepositInXMR.addListener(securityDepositInXMRListener); + model.isMinSecurityDeposit.addListener(isMinSecurityDepositListener); + model.getDataModel().buyerAsTakerWithoutDeposit.addListener(buyerAsTakerWithoutDepositListener); // focus out amountTextField.focusedProperty().addListener(amountFocusedListener); @@ -866,7 +888,7 @@ public abstract class MutableOfferView> exten triggerPriceInputTextField.focusedProperty().addListener(triggerPriceFocusedListener); marketBasedPriceTextField.focusedProperty().addListener(priceAsPercentageFocusedListener); volumeTextField.focusedProperty().addListener(volumeFocusedListener); - buyerSecurityDepositInputTextField.focusedProperty().addListener(buyerSecurityDepositFocusedListener); + securityDepositInputTextField.focusedProperty().addListener(securityDepositFocusedListener); // notifications model.getDataModel().getShowWalletFundedNotification().addListener(getShowWalletFundedNotificationListener); @@ -888,8 +910,9 @@ public abstract class MutableOfferView> exten model.marketPriceMargin.removeListener(marketPriceMarginListener); model.volume.removeListener(volumeListener); model.getDataModel().missingCoin.removeListener(missingCoinListener); - model.buyerSecurityDepositInBTC.removeListener(buyerSecurityDepositInBTCListener); - model.isMinBuyerSecurityDeposit.removeListener(isMinBuyerSecurityDepositListener); + model.securityDepositInXMR.removeListener(securityDepositInXMRListener); + model.isMinSecurityDeposit.removeListener(isMinSecurityDepositListener); + model.getDataModel().buyerAsTakerWithoutDeposit.removeListener(buyerAsTakerWithoutDepositListener); // focus out amountTextField.focusedProperty().removeListener(amountFocusedListener); @@ -898,7 +921,7 @@ public abstract class MutableOfferView> exten triggerPriceInputTextField.focusedProperty().removeListener(triggerPriceFocusedListener); marketBasedPriceTextField.focusedProperty().removeListener(priceAsPercentageFocusedListener); volumeTextField.focusedProperty().removeListener(volumeFocusedListener); - buyerSecurityDepositInputTextField.focusedProperty().removeListener(buyerSecurityDepositFocusedListener); + securityDepositInputTextField.focusedProperty().removeListener(securityDepositFocusedListener); // notifications model.getDataModel().getShowWalletFundedNotification().removeListener(getShowWalletFundedNotificationListener); @@ -997,22 +1020,46 @@ public abstract class MutableOfferView> exten } private void addOptionsGroup() { - setDepositTitledGroupBg = addTitledGroupBg(gridPane, ++gridRow, 1, + setDepositTitledGroupBg = addTitledGroupBg(gridPane, ++gridRow, 2, Res.get("shared.advancedOptions"), Layout.COMPACT_GROUP_DISTANCE); - advancedOptionsBox = new HBox(); - advancedOptionsBox.setSpacing(40); + securityDepositAndFeeBox = new HBox(); + securityDepositAndFeeBox.setSpacing(40); - GridPane.setRowIndex(advancedOptionsBox, gridRow); - GridPane.setColumnSpan(advancedOptionsBox, GridPane.REMAINING); - GridPane.setColumnIndex(advancedOptionsBox, 0); - GridPane.setHalignment(advancedOptionsBox, HPos.LEFT); - GridPane.setMargin(advancedOptionsBox, new Insets(Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0)); - gridPane.getChildren().add(advancedOptionsBox); + GridPane.setRowIndex(securityDepositAndFeeBox, gridRow); + GridPane.setColumnSpan(securityDepositAndFeeBox, GridPane.REMAINING); + GridPane.setColumnIndex(securityDepositAndFeeBox, 0); + GridPane.setHalignment(securityDepositAndFeeBox, HPos.LEFT); + GridPane.setMargin(securityDepositAndFeeBox, new Insets(Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0)); + gridPane.getChildren().add(securityDepositAndFeeBox); VBox tradeFeeFieldsBox = getTradeFeeFieldsBox(); tradeFeeFieldsBox.setMinWidth(240); - advancedOptionsBox.getChildren().addAll(getBuyerSecurityDepositBox(), tradeFeeFieldsBox); + securityDepositAndFeeBox.getChildren().addAll(getSecurityDepositBox(), tradeFeeFieldsBox); + + buyerAsTakerWithoutDepositSlider = FormBuilder.addSlideToggleButton(gridPane, ++gridRow, Res.get("createOffer.buyerAsTakerWithoutDeposit")); + buyerAsTakerWithoutDepositSlider.setOnAction(event -> { + + // popup info box + String key = "popup.info.buyerAsTakerWithoutDeposit"; + if (buyerAsTakerWithoutDepositSlider.isSelected() && DontShowAgainLookup.showAgain(key)) { + new Popup().headLine(Res.get(key + ".headline")) + .information(Res.get(key)) + .closeButtonText(Res.get("shared.cancel")) + .actionButtonText(Res.get("shared.ok")) + .onAction(() -> model.dataModel.setBuyerAsTakerWithoutDeposit(true)) + .onClose(() -> { + buyerAsTakerWithoutDepositSlider.setSelected(false); + model.dataModel.setBuyerAsTakerWithoutDeposit(false); + }) + .dontShowAgainId(key) + .show(); + } else { + model.dataModel.setBuyerAsTakerWithoutDeposit(buyerAsTakerWithoutDepositSlider.isSelected()); + } + }); + GridPane.setHalignment(buyerAsTakerWithoutDepositSlider, HPos.LEFT); + GridPane.setMargin(buyerAsTakerWithoutDepositSlider, new Insets(0, 0, 0, 0)); Tuple2 tuple = add2ButtonsAfterGroup(gridPane, ++gridRow, Res.get("shared.nextStep"), Res.get("shared.cancel")); @@ -1060,26 +1107,28 @@ public abstract class MutableOfferView> exten nextButton.setManaged(false); cancelButton1.setVisible(false); cancelButton1.setManaged(false); - advancedOptionsBox.setVisible(false); - advancedOptionsBox.setManaged(false); + securityDepositAndFeeBox.setVisible(false); + securityDepositAndFeeBox.setManaged(false); + buyerAsTakerWithoutDepositSlider.setVisible(false); + buyerAsTakerWithoutDepositSlider.setManaged(false); } - private VBox getBuyerSecurityDepositBox() { + private VBox getSecurityDepositBox() { Tuple3 tuple = getEditableValueBoxWithInfo( Res.get("createOffer.securityDeposit.prompt")); - buyerSecurityDepositInfoInputTextField = tuple.second; - buyerSecurityDepositInputTextField = buyerSecurityDepositInfoInputTextField.getInputTextField(); - buyerSecurityDepositPercentageLabel = tuple.third; + securityDepositInfoInputTextField = tuple.second; + securityDepositInputTextField = securityDepositInfoInputTextField.getInputTextField(); + securityDepositPercentageLabel = tuple.third; // getEditableValueBox delivers BTC, so we overwrite it with % - buyerSecurityDepositPercentageLabel.setText("%"); + securityDepositPercentageLabel.setText("%"); Tuple2 tradeInputBoxTuple = getTradeInputBox(tuple.first, model.getSecurityDepositLabel()); VBox depositBox = tradeInputBoxTuple.second; - buyerSecurityDepositLabel = tradeInputBoxTuple.first; + securityDepositLabel = tradeInputBoxTuple.first; depositBox.setMaxWidth(310); - editOfferElements.add(buyerSecurityDepositInputTextField); - editOfferElements.add(buyerSecurityDepositPercentageLabel); + editOfferElements.add(securityDepositInputTextField); + editOfferElements.add(securityDepositPercentageLabel); return depositBox; } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java index fabad857..8ededde3 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java @@ -113,9 +113,9 @@ public abstract class MutableOfferViewModel ext public final StringProperty amount = new SimpleStringProperty(); public final StringProperty minAmount = new SimpleStringProperty(); - protected final StringProperty buyerSecurityDeposit = new SimpleStringProperty(); - final StringProperty buyerSecurityDepositInBTC = new SimpleStringProperty(); - final StringProperty buyerSecurityDepositLabel = new SimpleStringProperty(); + protected final StringProperty securityDeposit = new SimpleStringProperty(); + final StringProperty securityDepositInXMR = new SimpleStringProperty(); + final StringProperty securityDepositLabel = new SimpleStringProperty(); // Price in the viewModel is always dependent on fiat/crypto: Fiat Fiat/BTC, for cryptos we use inverted price. // The domain (dataModel) uses always the same price model (otherCurrencyBTC) @@ -151,14 +151,14 @@ public abstract class MutableOfferViewModel ext final BooleanProperty showPayFundsScreenDisplayed = new SimpleBooleanProperty(); private final BooleanProperty showTransactionPublishedScreen = new SimpleBooleanProperty(); final BooleanProperty isWaitingForFunds = new SimpleBooleanProperty(); - final BooleanProperty isMinBuyerSecurityDeposit = new SimpleBooleanProperty(); + final BooleanProperty isMinSecurityDeposit = new SimpleBooleanProperty(); final ObjectProperty amountValidationResult = new SimpleObjectProperty<>(); final ObjectProperty minAmountValidationResult = new SimpleObjectProperty<>(); final ObjectProperty priceValidationResult = new SimpleObjectProperty<>(); final ObjectProperty triggerPriceValidationResult = new SimpleObjectProperty<>(new InputValidator.ValidationResult(true)); final ObjectProperty volumeValidationResult = new SimpleObjectProperty<>(); - final ObjectProperty buyerSecurityDepositValidationResult = new SimpleObjectProperty<>(); + final ObjectProperty securityDepositValidationResult = new SimpleObjectProperty<>(); private ChangeListener amountStringListener; private ChangeListener minAmountStringListener; @@ -171,6 +171,7 @@ public abstract class MutableOfferViewModel ext private ChangeListener priceListener; private ChangeListener volumeListener; private ChangeListener securityDepositAsDoubleListener; + private ChangeListener buyerAsTakerWithoutDepositListener; private ChangeListener isWalletFundedListener; private ChangeListener errorMessageListener; @@ -303,7 +304,7 @@ public abstract class MutableOfferViewModel ext dataModel.calculateVolume(); dataModel.calculateTotalToPay(); } - updateBuyerSecurityDeposit(); + updateSecurityDeposit(); updateButtonDisableState(); } }; @@ -419,34 +420,36 @@ public abstract class MutableOfferViewModel ext updateButtonDisableState(); } }; + securityDepositStringListener = (ov, oldValue, newValue) -> { if (!ignoreSecurityDepositStringListener) { if (securityDepositValidator.validate(newValue).isValid) { - setBuyerSecurityDepositToModel(); + setSecurityDepositToModel(); dataModel.calculateTotalToPay(); } updateButtonDisableState(); } }; - amountListener = (ov, oldValue, newValue) -> { if (newValue != null) { amount.set(HavenoUtils.formatXmr(newValue)); - buyerSecurityDepositInBTC.set(HavenoUtils.formatXmr(dataModel.getBuyerSecurityDeposit(), true)); + securityDepositInXMR.set(HavenoUtils.formatXmr(dataModel.getSecurityDeposit(), true)); } else { amount.set(""); - buyerSecurityDepositInBTC.set(""); + securityDepositInXMR.set(""); } applyMakerFee(); }; + minAmountListener = (ov, oldValue, newValue) -> { if (newValue != null) minAmount.set(HavenoUtils.formatXmr(newValue)); else minAmount.set(""); }; + priceListener = (ov, oldValue, newValue) -> { ignorePriceStringListener = true; if (newValue != null) @@ -457,6 +460,7 @@ public abstract class MutableOfferViewModel ext ignorePriceStringListener = false; applyMakerFee(); }; + volumeListener = (ov, oldValue, newValue) -> { ignoreVolumeStringListener = true; if (newValue != null) @@ -470,17 +474,26 @@ public abstract class MutableOfferViewModel ext securityDepositAsDoubleListener = (ov, oldValue, newValue) -> { if (newValue != null) { - buyerSecurityDeposit.set(FormattingUtils.formatToPercent((double) newValue)); + securityDeposit.set(FormattingUtils.formatToPercent((double) newValue)); if (dataModel.getAmount().get() != null) { - buyerSecurityDepositInBTC.set(HavenoUtils.formatXmr(dataModel.getBuyerSecurityDeposit(), true)); + securityDepositInXMR.set(HavenoUtils.formatXmr(dataModel.getSecurityDeposit(), true)); } - updateBuyerSecurityDeposit(); + updateSecurityDeposit(); } else { - buyerSecurityDeposit.set(""); - buyerSecurityDepositInBTC.set(""); + securityDeposit.set(""); + securityDepositInXMR.set(""); } }; + buyerAsTakerWithoutDepositListener = (ov, oldValue, newValue) -> { + if (dataModel.paymentAccount != null) xmrValidator.setMaxValue(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimit(dataModel.getTradeCurrencyCode().get())); + xmrValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit())); + if (amount.get() != null) amountValidationResult.set(isXmrInputValid(amount.get())); + updateSecurityDeposit(); + applyMakerFee(); + dataModel.calculateTotalToPay(); + updateButtonDisableState(); + }; isWalletFundedListener = (ov, oldValue, newValue) -> updateButtonDisableState(); /* feeFromFundingTxListener = (ov, oldValue, newValue) -> { @@ -525,14 +538,15 @@ public abstract class MutableOfferViewModel ext marketPriceMargin.addListener(marketPriceMarginStringListener); dataModel.getUseMarketBasedPrice().addListener(useMarketBasedPriceListener); volume.addListener(volumeStringListener); - buyerSecurityDeposit.addListener(securityDepositStringListener); + securityDeposit.addListener(securityDepositStringListener); // Binding with Bindings.createObjectBinding does not work because of bi-directional binding dataModel.getAmount().addListener(amountListener); dataModel.getMinAmount().addListener(minAmountListener); dataModel.getPrice().addListener(priceListener); dataModel.getVolume().addListener(volumeListener); - dataModel.getBuyerSecurityDepositPct().addListener(securityDepositAsDoubleListener); + dataModel.getSecurityDepositPct().addListener(securityDepositAsDoubleListener); + dataModel.getBuyerAsTakerWithoutDeposit().addListener(buyerAsTakerWithoutDepositListener); // dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener); dataModel.getIsXmrWalletFunded().addListener(isWalletFundedListener); @@ -547,14 +561,15 @@ public abstract class MutableOfferViewModel ext marketPriceMargin.removeListener(marketPriceMarginStringListener); dataModel.getUseMarketBasedPrice().removeListener(useMarketBasedPriceListener); volume.removeListener(volumeStringListener); - buyerSecurityDeposit.removeListener(securityDepositStringListener); + securityDeposit.removeListener(securityDepositStringListener); // Binding with Bindings.createObjectBinding does not work because of bi-directional binding dataModel.getAmount().removeListener(amountListener); dataModel.getMinAmount().removeListener(minAmountListener); dataModel.getPrice().removeListener(priceListener); dataModel.getVolume().removeListener(volumeListener); - dataModel.getBuyerSecurityDepositPct().removeListener(securityDepositAsDoubleListener); + dataModel.getSecurityDepositPct().removeListener(securityDepositAsDoubleListener); + dataModel.getBuyerAsTakerWithoutDeposit().removeListener(buyerAsTakerWithoutDepositListener); //dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener); dataModel.getIsXmrWalletFunded().removeListener(isWalletFundedListener); @@ -593,9 +608,9 @@ public abstract class MutableOfferViewModel ext } securityDepositValidator.setPaymentAccount(dataModel.paymentAccount); - validateAndSetBuyerSecurityDepositToModel(); - buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDepositPct().get())); - buyerSecurityDepositLabel.set(getSecurityDepositLabel()); + validateAndSetSecurityDepositToModel(); + securityDeposit.set(FormattingUtils.formatToPercent(dataModel.getSecurityDepositPct().get())); + securityDepositLabel.set(getSecurityDepositLabel()); applyMakerFee(); return result; @@ -932,14 +947,14 @@ public abstract class MutableOfferViewModel ext } } - void onFocusOutBuyerSecurityDepositTextField(boolean oldValue, boolean newValue) { + void onFocusOutSecurityDepositTextField(boolean oldValue, boolean newValue) { if (oldValue && !newValue) { - InputValidator.ValidationResult result = securityDepositValidator.validate(buyerSecurityDeposit.get()); - buyerSecurityDepositValidationResult.set(result); + InputValidator.ValidationResult result = securityDepositValidator.validate(securityDeposit.get()); + securityDepositValidationResult.set(result); if (result.isValid) { - double defaultSecurityDeposit = Restrictions.getDefaultBuyerSecurityDepositAsPercent(); + double defaultSecurityDeposit = Restrictions.getDefaultSecurityDepositAsPercent(); String key = "buyerSecurityDepositIsLowerAsDefault"; - double depositAsDouble = ParsingUtils.parsePercentStringToDouble(buyerSecurityDeposit.get()); + double depositAsDouble = ParsingUtils.parsePercentStringToDouble(securityDeposit.get()); if (preferences.showAgain(key) && depositAsDouble < defaultSecurityDeposit) { String postfix = dataModel.isBuyOffer() ? Res.get("createOffer.tooLowSecDeposit.makerIsBuyer") : @@ -950,26 +965,26 @@ public abstract class MutableOfferViewModel ext .width(800) .actionButtonText(Res.get("createOffer.resetToDefault")) .onAction(() -> { - dataModel.setBuyerSecurityDeposit(defaultSecurityDeposit); + dataModel.setSecurityDepositPct(defaultSecurityDeposit); ignoreSecurityDepositStringListener = true; - buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDepositPct().get())); + securityDeposit.set(FormattingUtils.formatToPercent(dataModel.getSecurityDepositPct().get())); ignoreSecurityDepositStringListener = false; }) .closeButtonText(Res.get("createOffer.useLowerValue")) - .onClose(this::applyBuyerSecurityDepositOnFocusOut) + .onClose(this::applySecurityDepositOnFocusOut) .dontShowAgainId(key) .show(); } else { - applyBuyerSecurityDepositOnFocusOut(); + applySecurityDepositOnFocusOut(); } } } } - private void applyBuyerSecurityDepositOnFocusOut() { - setBuyerSecurityDepositToModel(); + private void applySecurityDepositOnFocusOut() { + setSecurityDepositToModel(); ignoreSecurityDepositStringListener = true; - buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDepositPct().get())); + securityDeposit.set(FormattingUtils.formatToPercent(dataModel.getSecurityDepositPct().get())); ignoreSecurityDepositStringListener = false; } @@ -1024,13 +1039,15 @@ public abstract class MutableOfferViewModel ext } public String getSecurityDepositLabel() { - return Preferences.USE_SYMMETRIC_SECURITY_DEPOSIT ? Res.get("createOffer.setDepositForBothTraders") : + return dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer() ? Res.get("createOffer.myDeposit") : + Preferences.USE_SYMMETRIC_SECURITY_DEPOSIT ? Res.get("createOffer.setDepositForBothTraders") : dataModel.isBuyOffer() ? Res.get("createOffer.setDepositAsBuyer") : Res.get("createOffer.setDeposit"); } - public String getSecurityDepositPopOverLabel(String depositInBTC) { - return dataModel.isBuyOffer() ? Res.get("createOffer.securityDepositInfoAsBuyer", depositInBTC) : - Res.get("createOffer.securityDepositInfo", depositInBTC); + public String getSecurityDepositPopOverLabel(String depositInXMR) { + return dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer() ? Res.get("createOffer.myDepositInfo", depositInXMR) : + dataModel.isBuyOffer() ? Res.get("createOffer.securityDepositInfoAsBuyer", depositInXMR) : + Res.get("createOffer.securityDepositInfo", depositInXMR); } public String getSecurityDepositInfo() { @@ -1193,19 +1210,19 @@ public abstract class MutableOfferViewModel ext } } - private void setBuyerSecurityDepositToModel() { - if (buyerSecurityDeposit.get() != null && !buyerSecurityDeposit.get().isEmpty()) { - dataModel.setBuyerSecurityDeposit(ParsingUtils.parsePercentStringToDouble(buyerSecurityDeposit.get())); + private void setSecurityDepositToModel() { + if (!(dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer()) && securityDeposit.get() != null && !securityDeposit.get().isEmpty()) { + dataModel.setSecurityDepositPct(ParsingUtils.parsePercentStringToDouble(securityDeposit.get())); } else { - dataModel.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent()); + dataModel.setSecurityDepositPct(Restrictions.getDefaultSecurityDepositAsPercent()); } } - private void validateAndSetBuyerSecurityDepositToModel() { + private void validateAndSetSecurityDepositToModel() { // If the security deposit in the model is not valid percent - String value = FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDepositPct().get()); + String value = FormattingUtils.formatToPercent(dataModel.getSecurityDepositPct().get()); if (!securityDepositValidator.validate(value).isValid) { - dataModel.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent()); + dataModel.setSecurityDepositPct(Restrictions.getDefaultSecurityDepositAsPercent()); } } @@ -1263,15 +1280,17 @@ public abstract class MutableOfferViewModel ext isWaitingForFunds.set(!waitingForFundsText.get().isEmpty()); } - private void updateBuyerSecurityDeposit() { - isMinBuyerSecurityDeposit.set(dataModel.isMinBuyerSecurityDeposit()); - - if (dataModel.isMinBuyerSecurityDeposit()) { - buyerSecurityDepositLabel.set(Res.get("createOffer.minSecurityDepositUsed")); - buyerSecurityDeposit.set(HavenoUtils.formatXmr(Restrictions.getMinBuyerSecurityDeposit())); + private void updateSecurityDeposit() { + isMinSecurityDeposit.set(dataModel.isMinSecurityDeposit()); + if (dataModel.isMinSecurityDeposit()) { + securityDepositLabel.set(Res.get("createOffer.minSecurityDepositUsed")); + securityDeposit.set(HavenoUtils.formatXmr(Restrictions.getMinSecurityDeposit())); } else { - buyerSecurityDepositLabel.set(getSecurityDepositLabel()); - buyerSecurityDeposit.set(FormattingUtils.formatToPercent(dataModel.getBuyerSecurityDepositPct().get())); + securityDepositLabel.set(getSecurityDepositLabel()); + boolean hasBuyerAsTakerWithoutDeposit = dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer(); + securityDeposit.set(FormattingUtils.formatToPercent(hasBuyerAsTakerWithoutDeposit ? + Restrictions.getDefaultSecurityDepositAsPercent() : // use default percent if no deposit from buyer + dataModel.getSecurityDepositPct().get())); } } @@ -1293,8 +1312,8 @@ public abstract class MutableOfferViewModel ext } // validating the percentage deposit value only makes sense if it is actually used - if (!dataModel.isMinBuyerSecurityDeposit()) { - inputDataValid = inputDataValid && securityDepositValidator.validate(buyerSecurityDeposit.get()).isValid; + if (!dataModel.isMinSecurityDeposit()) { + inputDataValid = inputDataValid && securityDepositValidator.validate(securityDeposit.get()).isValid; } isNextButtonDisabled.set(!inputDataValid); diff --git a/desktop/src/main/java/haveno/desktop/main/offer/OfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/offer/OfferDataModel.java index 8b92477e..71a827ff 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/OfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/OfferDataModel.java @@ -87,4 +87,8 @@ public abstract class OfferDataModel extends ActivatableDataModel { }); } + + public boolean hasTotalToPay() { + return totalToPay.get() != null && totalToPay.get().compareTo(BigInteger.ZERO) > 0; + } } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java index 6d9575b0..9b357145 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java @@ -17,6 +17,7 @@ package haveno.desktop.main.offer.offerbook; +import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; import haveno.common.UserThread; @@ -44,7 +45,6 @@ import haveno.desktop.common.view.ActivatableViewAndModel; import haveno.desktop.components.AccountStatusTooltipLabel; import haveno.desktop.components.AutoTooltipButton; import haveno.desktop.components.AutoTooltipLabel; -import haveno.desktop.components.AutoTooltipSlideToggleButton; import haveno.desktop.components.AutoTooltipTableColumn; import haveno.desktop.components.AutoTooltipTextField; import haveno.desktop.components.AutocompleteComboBox; @@ -83,6 +83,7 @@ import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableRow; import javafx.scene.control.TableView; +import javafx.scene.control.ToggleButton; import javafx.scene.control.Tooltip; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; @@ -120,7 +121,8 @@ abstract public class OfferBookView paymentMethodComboBox; private AutoTooltipButton createOfferButton; private AutoTooltipTextField filterInputField; - private AutoTooltipSlideToggleButton matchingOffersToggle; + private ToggleButton matchingOffersToggleButton; + private ToggleButton noDepositOffersToggleButton; private AutoTooltipTableColumn amountColumn; private AutoTooltipTableColumn volumeColumn; private AutoTooltipTableColumn marketColumn; @@ -183,9 +185,17 @@ abstract public class OfferBookView model.onShowOffersMatchingMyAccounts(matchingOffersToggle.isSelected())); + matchingOffersToggleButton.setSelected(model.useOffersMatchingMyAccountsFilter); + matchingOffersToggleButton.disableProperty().bind(model.disableMatchToggle); + matchingOffersToggleButton.setOnAction(e -> model.onShowOffersMatchingMyAccounts(matchingOffersToggleButton.isSelected())); + + noDepositOffersToggleButton.setSelected(model.showPrivateOffers); + noDepositOffersToggleButton.setOnAction(e -> model.onShowPrivateOffers(noDepositOffersToggleButton.isSelected())); model.getOfferList().comparatorProperty().bind(tableView.comparatorProperty()); @@ -452,8 +465,10 @@ abstract public class OfferBookView account = model.getMostMaturePaymentAccountForOffer(offer); if (account.isPresent()) { long tradeLimit = model.accountAgeWitnessService.getMyTradeLimit(account.get(), - offer.getCurrencyCode(), offer.getMirroredDirection()); + offer.getCurrencyCode(), offer.getMirroredDirection(), offer.hasBuyerAsTakerWithoutDeposit()); new Popup() .warning(Res.get("popup.warning.tradeLimitDueAccountAgeRestriction.buyer", HavenoUtils.formatXmr(tradeLimit, true), @@ -1123,7 +1142,10 @@ abstract public class OfferBookView offerBookListItem.getOffer().isPrivateOffer() == showPrivateOffers); + if (!filterText.isEmpty()) { // filter node address diff --git a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferDataModel.java index beae7c11..0f6c5db8 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferDataModel.java @@ -341,7 +341,7 @@ class TakeOfferDataModel extends OfferDataModel { long getMaxTradeLimit() { if (paymentAccount != null) { return accountAgeWitnessService.getMyTradeLimit(paymentAccount, getCurrencyCode(), - offer.getMirroredDirection()); + offer.getMirroredDirection(), offer.hasBuyerAsTakerWithoutDeposit()); } else { return 0; } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java index 6f999133..1d662aeb 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java @@ -123,6 +123,8 @@ public class TakeOfferView extends ActivatableViewAndModel { String key = "CreateOfferCancelAndFunded"; - if (model.dataModel.getIsXmrWalletFunded().get() && + if (model.dataModel.getIsXmrWalletFunded().get() && model.dataModel.hasTotalToPay() && model.dataModel.preferences.showAgain(key)) { new Popup().backgroundInfo(Res.get("takeOffer.alreadyFunded.askCancel")) .closeButtonText(Res.get("shared.no")) diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/editor/PasswordPopup.java b/desktop/src/main/java/haveno/desktop/main/overlays/editor/PasswordPopup.java new file mode 100644 index 00000000..bd169b3a --- /dev/null +++ b/desktop/src/main/java/haveno/desktop/main/overlays/editor/PasswordPopup.java @@ -0,0 +1,245 @@ +/* + * This file is part of Haveno. + * + * Haveno is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Haveno is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Haveno. If not, see . + */ + +package haveno.desktop.main.overlays.editor; + +import haveno.common.util.Utilities; +import haveno.core.locale.GlobalSettings; +import haveno.desktop.components.InputTextField; +import haveno.desktop.main.overlays.Overlay; +import haveno.desktop.util.FormBuilder; +import javafx.animation.Interpolator; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; +import javafx.beans.value.ChangeListener; +import javafx.collections.ObservableList; +import javafx.event.EventHandler; +import javafx.geometry.HPos; +import javafx.geometry.Insets; +import javafx.scene.Camera; +import javafx.scene.PerspectiveCamera; +import javafx.scene.Scene; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.GridPane; +import javafx.scene.transform.Rotate; +import javafx.stage.Modality; +import javafx.util.Duration; +import lombok.extern.slf4j.Slf4j; + +import java.util.function.Consumer; + +import de.jensd.fx.fontawesome.AwesomeIcon; + +import static haveno.desktop.util.FormBuilder.addInputTextField; + +@Slf4j +public class PasswordPopup extends Overlay { + private InputTextField inputTextField; + private static PasswordPopup INSTANCE; + private Consumer actionHandler; + private ChangeListener focusListener; + private EventHandler keyEventEventHandler; + + public PasswordPopup() { + width = 600; + type = Type.Confirmation; + if (INSTANCE != null) + INSTANCE.hide(); + INSTANCE = this; + } + + public PasswordPopup onAction(Consumer confirmHandler) { + this.actionHandler = confirmHandler; + return this; + } + + @Override + public void show() { + actionButtonText("CONFIRM"); + createGridPane(); + addHeadLine(); + addContent(); + addButtons(); + applyStyles(); + onShow(); + } + + @Override + protected void onShow() { + super.display(); + + if (stage != null) { + focusListener = (observable, oldValue, newValue) -> { + if (!newValue) + hide(); + }; + stage.focusedProperty().addListener(focusListener); + + Scene scene = stage.getScene(); + if (scene != null) + scene.addEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler); + } + } + + @Override + public void hide() { + animateHide(); + } + + @Override + protected void onHidden() { + INSTANCE = null; + + if (stage != null) { + if (focusListener != null) + stage.focusedProperty().removeListener(focusListener); + + Scene scene = stage.getScene(); + if (scene != null) + scene.removeEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler); + } + } + + private void addContent() { + gridPane.setPadding(new Insets(64)); + + inputTextField = addInputTextField(gridPane, ++rowIndex, null, -10d); + GridPane.setColumnSpan(inputTextField, 2); + inputTextField.requestFocus(); + + keyEventEventHandler = event -> { + if (Utilities.isAltOrCtrlPressed(KeyCode.R, event)) { + doClose(); + } + }; + } + + @Override + protected void addHeadLine() { + super.addHeadLine(); + GridPane.setHalignment(headLineLabel, HPos.CENTER); + } + + protected void setupKeyHandler(Scene scene) { + scene.setOnKeyPressed(e -> { + if (e.getCode() == KeyCode.ESCAPE) { + e.consume(); + doClose(); + } + if (e.getCode() == KeyCode.ENTER) { + e.consume(); + apply(); + } + }); + } + + @Override + protected void animateHide(Runnable onFinishedHandler) { + if (GlobalSettings.getUseAnimations()) { + double duration = getDuration(300); + Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1); + + gridPane.setRotationAxis(Rotate.X_AXIS); + Camera camera = gridPane.getScene().getCamera(); + gridPane.getScene().setCamera(new PerspectiveCamera()); + + Timeline timeline = new Timeline(); + ObservableList keyFrames = timeline.getKeyFrames(); + keyFrames.add(new KeyFrame(Duration.millis(0), + new KeyValue(gridPane.rotateProperty(), 0, interpolator), + new KeyValue(gridPane.opacityProperty(), 1, interpolator) + )); + keyFrames.add(new KeyFrame(Duration.millis(duration), + new KeyValue(gridPane.rotateProperty(), -90, interpolator), + new KeyValue(gridPane.opacityProperty(), 0, interpolator) + )); + timeline.setOnFinished(event -> { + gridPane.setRotate(0); + gridPane.setRotationAxis(Rotate.Z_AXIS); + gridPane.getScene().setCamera(camera); + onFinishedHandler.run(); + }); + timeline.play(); + } else { + onFinishedHandler.run(); + } + } + + @Override + protected void animateDisplay() { + if (GlobalSettings.getUseAnimations()) { + double startY = -160; + double duration = getDuration(400); + Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1); + Timeline timeline = new Timeline(); + ObservableList keyFrames = timeline.getKeyFrames(); + keyFrames.add(new KeyFrame(Duration.millis(0), + new KeyValue(gridPane.opacityProperty(), 0, interpolator), + new KeyValue(gridPane.translateYProperty(), startY, interpolator) + )); + + keyFrames.add(new KeyFrame(Duration.millis(duration), + new KeyValue(gridPane.opacityProperty(), 1, interpolator), + new KeyValue(gridPane.translateYProperty(), 0, interpolator) + )); + + timeline.play(); + } + } + + @Override + protected void createGridPane() { + super.createGridPane(); + gridPane.setPadding(new Insets(15, 15, 30, 30)); + } + + @Override + protected void addButtons() { + buttonDistance = 10; + super.addButtons(); + + actionButton.setOnAction(event -> apply()); + } + + private void apply() { + hide(); + if (actionHandler != null && inputTextField != null) + actionHandler.accept(inputTextField.getText()); + } + + @Override + protected void applyStyles() { + super.applyStyles(); + FormBuilder.getIconForLabel(AwesomeIcon.LOCK, headlineIcon, "1.5em"); + } + + @Override + protected void setModality() { + stage.initOwner(owner.getScene().getWindow()); + stage.initModality(Modality.NONE); + } + + @Override + protected void addEffectToBackground() { + } + + @Override + protected void removeEffectFromBackground() { + } +} diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/ContractWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/ContractWindow.java index 262ec5ef..4cefcbde 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/ContractWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/ContractWindow.java @@ -141,7 +141,7 @@ public class ContractWindow extends Overlay { DisplayUtils.formatDateTime(offer.getDate()) + " / " + DisplayUtils.formatDateTime(dispute.getTradeDate())); String currencyCode = offer.getCurrencyCode(); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.offerType"), - DisplayUtils.getDirectionBothSides(offer.getDirection())); + DisplayUtils.getDirectionBothSides(offer.getDirection(), offer.isPrivateOffer())); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.tradePrice"), FormattingUtils.formatPrice(contract.getPrice())); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.tradeAmount"), diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java index 997a7242..ebf1c253 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java @@ -99,6 +99,7 @@ public class DisputeSummaryWindow extends Overlay { reasonWasOtherRadioButton, reasonWasBankRadioButton, reasonWasOptionTradeRadioButton, reasonWasSellerNotRespondingRadioButton, reasonWasWrongSenderAccountRadioButton, reasonWasPeerWasLateRadioButton, reasonWasTradeAlreadySettledRadioButton; + private CoreDisputesService.PayoutSuggestion payoutSuggestion; // Dispute object of other trade peer. The dispute field is the one from which we opened the close dispute window. private Optional peersDisputeOptional; @@ -700,33 +701,28 @@ public class DisputeSummaryWindow extends Overlay { } private void applyPayoutAmountsToDisputeResult(Toggle selectedTradeAmountToggle) { - CoreDisputesService.DisputePayout payout; if (selectedTradeAmountToggle == buyerGetsTradeAmountRadioButton) { - payout = CoreDisputesService.DisputePayout.BUYER_GETS_TRADE_AMOUNT; + payoutSuggestion = CoreDisputesService.PayoutSuggestion.BUYER_GETS_TRADE_AMOUNT; disputeResult.setWinner(DisputeResult.Winner.BUYER); } else if (selectedTradeAmountToggle == buyerGetsAllRadioButton) { - payout = CoreDisputesService.DisputePayout.BUYER_GETS_ALL; + payoutSuggestion = CoreDisputesService.PayoutSuggestion.BUYER_GETS_ALL; disputeResult.setWinner(DisputeResult.Winner.BUYER); } else if (selectedTradeAmountToggle == sellerGetsTradeAmountRadioButton) { - payout = CoreDisputesService.DisputePayout.SELLER_GETS_TRADE_AMOUNT; + payoutSuggestion = CoreDisputesService.PayoutSuggestion.SELLER_GETS_TRADE_AMOUNT; disputeResult.setWinner(DisputeResult.Winner.SELLER); } else if (selectedTradeAmountToggle == sellerGetsAllRadioButton) { - payout = CoreDisputesService.DisputePayout.SELLER_GETS_ALL; + payoutSuggestion = CoreDisputesService.PayoutSuggestion.SELLER_GETS_ALL; disputeResult.setWinner(DisputeResult.Winner.SELLER); } else { // should not happen throw new IllegalStateException("Unknown radio button"); } - disputesService.applyPayoutAmountsToDisputeResult(payout, dispute, disputeResult, -1); + disputesService.applyPayoutAmountsToDisputeResult(payoutSuggestion, dispute, disputeResult, -1); buyerPayoutAmountInputTextField.setText(HavenoUtils.formatXmr(disputeResult.getBuyerPayoutAmountBeforeCost())); sellerPayoutAmountInputTextField.setText(HavenoUtils.formatXmr(disputeResult.getSellerPayoutAmountBeforeCost())); } private void applyTradeAmountRadioButtonStates() { - Contract contract = dispute.getContract(); - BigInteger buyerSecurityDeposit = trade.getBuyer().getSecurityDeposit(); - BigInteger sellerSecurityDeposit = trade.getSeller().getSecurityDeposit(); - BigInteger tradeAmount = contract.getTradeAmount(); BigInteger buyerPayoutAmount = disputeResult.getBuyerPayoutAmountBeforeCost(); BigInteger sellerPayoutAmount = disputeResult.getSellerPayoutAmountBeforeCost(); @@ -734,20 +730,22 @@ public class DisputeSummaryWindow extends Overlay { buyerPayoutAmountInputTextField.setText(HavenoUtils.formatXmr(buyerPayoutAmount)); sellerPayoutAmountInputTextField.setText(HavenoUtils.formatXmr(sellerPayoutAmount)); - if (buyerPayoutAmount.equals(tradeAmount.add(buyerSecurityDeposit)) && - sellerPayoutAmount.equals(sellerSecurityDeposit)) { - buyerGetsTradeAmountRadioButton.setSelected(true); - } else if (buyerPayoutAmount.equals(tradeAmount.add(buyerSecurityDeposit).add(sellerSecurityDeposit)) && - sellerPayoutAmount.equals(BigInteger.ZERO)) { - buyerGetsAllRadioButton.setSelected(true); - } else if (sellerPayoutAmount.equals(tradeAmount.add(sellerSecurityDeposit)) - && buyerPayoutAmount.equals(buyerSecurityDeposit)) { - sellerGetsTradeAmountRadioButton.setSelected(true); - } else if (sellerPayoutAmount.equals(tradeAmount.add(buyerSecurityDeposit).add(sellerSecurityDeposit)) - && buyerPayoutAmount.equals(BigInteger.ZERO)) { - sellerGetsAllRadioButton.setSelected(true); - } else { - customRadioButton.setSelected(true); + switch (payoutSuggestion) { + case BUYER_GETS_TRADE_AMOUNT: + buyerGetsTradeAmountRadioButton.setSelected(true); + break; + case BUYER_GETS_ALL: + buyerGetsAllRadioButton.setSelected(true); + break; + case SELLER_GETS_TRADE_AMOUNT: + sellerGetsTradeAmountRadioButton.setSelected(true); + break; + case SELLER_GETS_ALL: + sellerGetsAllRadioButton.setSelected(true); + break; + case CUSTOM: + customRadioButton.setSelected(true); + break; } } } diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/OfferDetailsWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/OfferDetailsWindow.java index e93dc647..7825c7a6 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/OfferDetailsWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/OfferDetailsWindow.java @@ -29,6 +29,7 @@ import haveno.core.locale.Res; import haveno.core.monetary.Price; import haveno.core.offer.Offer; import haveno.core.offer.OfferDirection; +import haveno.core.offer.OpenOffer; import haveno.core.payment.PaymentAccount; import haveno.core.payment.payload.PaymentMethod; import haveno.core.trade.HavenoUtils; @@ -42,6 +43,8 @@ import haveno.desktop.Navigation; import haveno.desktop.components.AutoTooltipButton; import haveno.desktop.components.BusyAnimation; import haveno.desktop.main.overlays.Overlay; +import haveno.desktop.main.overlays.editor.PasswordPopup; +import haveno.desktop.main.overlays.popups.Popup; import haveno.desktop.util.CssTheme; import haveno.desktop.util.DisplayUtils; import static haveno.desktop.util.FormBuilder.addButtonAfterGroup; @@ -195,7 +198,7 @@ public class OfferDetailsWindow extends Overlay { rows++; } - addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.Offer")); + addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get(offer.isPrivateOffer() ? "shared.Offer" : "shared.Offer")); String counterCurrencyDirectionInfo = ""; String xmrDirectionInfo = ""; @@ -217,7 +220,7 @@ public class OfferDetailsWindow extends Overlay { xmrDirectionInfo = direction == OfferDirection.BUY ? toReceive : toSpend; } else { addConfirmationLabelLabel(gridPane, rowIndex, offerTypeLabel, - DisplayUtils.getDirectionBothSides(direction), firstRowDistance); + DisplayUtils.getDirectionBothSides(direction, offer.isPrivateOffer()), firstRowDistance); } String amount = Res.get("shared.xmrAmount"); if (takeOfferHandlerOptional.isPresent()) { @@ -342,6 +345,10 @@ public class OfferDetailsWindow extends Overlay { // get amount reserved for the offer BigInteger reservedAmount = isMyOffer ? offer.getReservedAmount() : null; + // get offer challenge + OpenOffer myOpenOffer = HavenoUtils.openOfferManager.getOpenOfferById(offer.getId()).orElse(null); + String offerChallenge = myOpenOffer == null ? null : myOpenOffer.getChallenge(); + rows = 3; if (countryCode != null) rows++; @@ -349,6 +356,8 @@ public class OfferDetailsWindow extends Overlay { rows++; if (reservedAmount != null) rows++; + if (offerChallenge != null) + rows++; addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.details"), Layout.GROUP_DISTANCE); addConfirmationLabelTextFieldWithCopyIcon(gridPane, rowIndex, Res.get("shared.offerId"), offer.getId(), @@ -365,6 +374,7 @@ public class OfferDetailsWindow extends Overlay { " " + HavenoUtils.formatXmr(offer.getOfferPayload().getMaxSellerSecurityDeposit(), true); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), value); + if (reservedAmount != null) { addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.reservedAmount"), HavenoUtils.formatXmr(reservedAmount, true)); } @@ -373,6 +383,9 @@ public class OfferDetailsWindow extends Overlay { addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.countryBank"), CountryUtil.getNameAndCode(countryCode)); + if (offerChallenge != null) + addConfirmationLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, Res.get("offerDetailsWindow.challenge"), offerChallenge); + if (placeOfferHandlerOptional.isPresent()) { addTitledGroupBg(gridPane, ++rowIndex, 1, Res.get("offerDetailsWindow.commitment"), Layout.GROUP_DISTANCE); final Tuple2 labelLabelTuple2 = addConfirmationLabelLabel(gridPane, rowIndex, Res.get("offerDetailsWindow.agree"), Res.get("createOffer.tac"), @@ -416,13 +429,13 @@ public class OfferDetailsWindow extends Overlay { ++rowIndex, 1, isPlaceOffer ? placeOfferButtonText : takeOfferButtonText); - AutoTooltipButton button = (AutoTooltipButton) placeOfferTuple.first; - button.setMinHeight(40); - button.setPadding(new Insets(0, 20, 0, 20)); - button.setGraphic(iconView); - button.setGraphicTextGap(10); - button.setId(isBuyerRole ? "buy-button-big" : "sell-button-big"); - button.updateText(isPlaceOffer ? placeOfferButtonText : takeOfferButtonText); + AutoTooltipButton confirmButton = (AutoTooltipButton) placeOfferTuple.first; + confirmButton.setMinHeight(40); + confirmButton.setPadding(new Insets(0, 20, 0, 20)); + confirmButton.setGraphic(iconView); + confirmButton.setGraphicTextGap(10); + confirmButton.setId(isBuyerRole ? "buy-button-big" : "sell-button-big"); + confirmButton.updateText(isPlaceOffer ? placeOfferButtonText : takeOfferButtonText); busyAnimation = placeOfferTuple.second; Label spinnerInfoLabel = placeOfferTuple.third; @@ -436,29 +449,48 @@ public class OfferDetailsWindow extends Overlay { placeOfferTuple.fourth.getChildren().add(cancelButton); - button.setOnAction(e -> { + confirmButton.setOnAction(e -> { if (GUIUtil.canCreateOrTakeOfferOrShowPopup(user, navigation)) { - button.setDisable(true); - cancelButton.setDisable(isPlaceOffer ? false : true); // TODO: enable cancel button for taking an offer until messages sent - // temporarily disabled due to high CPU usage (per issue #4649) - // busyAnimation.play(); - if (isPlaceOffer) { - spinnerInfoLabel.setText(Res.get("createOffer.fundsBox.placeOfferSpinnerInfo")); - placeOfferHandlerOptional.ifPresent(Runnable::run); + if (!isPlaceOffer && offer.isPrivateOffer()) { + new PasswordPopup() + .headLine(Res.get("offerbook.takeOffer.enterChallenge")) + .onAction(password -> { + if (offer.getChallengeHash().equals(HavenoUtils.getChallengeHash(password))) { + offer.setChallenge(password); + confirmTakeOfferAux(confirmButton, cancelButton, spinnerInfoLabel, isPlaceOffer); + } else { + new Popup().warning(Res.get("password.wrongPw")).show(); + } + }) + .closeButtonText(Res.get("shared.cancel")) + .show(); } else { - - // subscribe to trade progress - spinnerInfoLabel.setText(Res.get("takeOffer.fundsBox.takeOfferSpinnerInfo", "0%")); - numTradesSubscription = EasyBind.subscribe(tradeManager.getNumPendingTrades(), newNum -> { - subscribeToProgress(spinnerInfoLabel); - }); - - takeOfferHandlerOptional.ifPresent(Runnable::run); + confirmTakeOfferAux(confirmButton, cancelButton, spinnerInfoLabel, isPlaceOffer); } } }); } + private void confirmTakeOfferAux(Button button, Button cancelButton, Label spinnerInfoLabel, boolean isPlaceOffer) { + button.setDisable(true); + cancelButton.setDisable(isPlaceOffer ? false : true); // TODO: enable cancel button for taking an offer until messages sent + // temporarily disabled due to high CPU usage (per issue #4649) + // busyAnimation.play(); + if (isPlaceOffer) { + spinnerInfoLabel.setText(Res.get("createOffer.fundsBox.placeOfferSpinnerInfo")); + placeOfferHandlerOptional.ifPresent(Runnable::run); + } else { + + // subscribe to trade progress + spinnerInfoLabel.setText(Res.get("takeOffer.fundsBox.takeOfferSpinnerInfo", "0%")); + numTradesSubscription = EasyBind.subscribe(tradeManager.getNumPendingTrades(), newNum -> { + subscribeToProgress(spinnerInfoLabel); + }); + + takeOfferHandlerOptional.ifPresent(Runnable::run); + } + } + private void subscribeToProgress(Label spinnerInfoLabel) { Trade trade = tradeManager.getTrade(offer.getId()); if (trade == null || initProgressSubscription != null) return; diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/duplicateoffer/DuplicateOfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/portfolio/duplicateoffer/DuplicateOfferDataModel.java index 75514e43..e3fd44c0 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/duplicateoffer/DuplicateOfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/duplicateoffer/DuplicateOfferDataModel.java @@ -52,7 +52,7 @@ class DuplicateOfferDataModel extends MutableOfferDataModel { DuplicateOfferDataModel(CreateOfferService createOfferService, OpenOfferManager openOfferManager, OfferUtil offerUtil, - XmrWalletService btcWalletService, + XmrWalletService xmrWalletService, Preferences preferences, User user, P2PService p2PService, @@ -65,7 +65,7 @@ class DuplicateOfferDataModel extends MutableOfferDataModel { super(createOfferService, openOfferManager, offerUtil, - btcWalletService, + xmrWalletService, preferences, user, p2PService, @@ -85,20 +85,21 @@ class DuplicateOfferDataModel extends MutableOfferDataModel { setPrice(offer.getPrice()); setVolume(offer.getVolume()); setUseMarketBasedPrice(offer.isUseMarketBasedPrice()); + setBuyerAsTakerWithoutDeposit(offer.hasBuyerAsTakerWithoutDeposit()); - setBuyerSecurityDeposit(getBuyerSecurityAsPercent(offer)); + setSecurityDepositPct(getSecurityAsPercent(offer)); if (offer.isUseMarketBasedPrice()) { setMarketPriceMarginPct(offer.getMarketPriceMarginPct()); } } - private double getBuyerSecurityAsPercent(Offer offer) { - BigInteger offerBuyerSecurityDeposit = getBoundedBuyerSecurityDeposit(offer.getMaxBuyerSecurityDeposit()); - double offerBuyerSecurityDepositAsPercent = CoinUtil.getAsPercentPerBtc(offerBuyerSecurityDeposit, + private double getSecurityAsPercent(Offer offer) { + BigInteger offerSellerSecurityDeposit = getBoundedSecurityDeposit(offer.getMaxSellerSecurityDeposit()); + double offerSellerSecurityDepositAsPercent = CoinUtil.getAsPercentPerXmr(offerSellerSecurityDeposit, offer.getAmount()); - return Math.min(offerBuyerSecurityDepositAsPercent, - Restrictions.getMaxBuyerSecurityDepositAsPercent()); + return Math.min(offerSellerSecurityDepositAsPercent, + Restrictions.getMaxSecurityDepositAsPercent()); } @Override diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/editoffer/EditOfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/portfolio/editoffer/EditOfferDataModel.java index be2b811f..d7ab366b 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/editoffer/EditOfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/editoffer/EditOfferDataModel.java @@ -95,7 +95,7 @@ class EditOfferDataModel extends MutableOfferDataModel { price.set(null); volume.set(null); minVolume.set(null); - buyerSecurityDepositPct.set(0); + securityDepositPct.set(0); paymentAccounts.clear(); paymentAccount = null; marketPriceMargin = 0; @@ -127,12 +127,12 @@ class EditOfferDataModel extends MutableOfferDataModel { // If the security deposit got bounded because it was below the coin amount limit, it can be bigger // by percentage than the restriction. We can't determine the percentage originally entered at offer // creation, so just use the default value as it doesn't matter anyway. - double buyerSecurityDepositPercent = CoinUtil.getAsPercentPerBtc(offer.getMaxBuyerSecurityDeposit(), offer.getAmount()); - if (buyerSecurityDepositPercent > Restrictions.getMaxBuyerSecurityDepositAsPercent() - && offer.getMaxBuyerSecurityDeposit().equals(Restrictions.getMinBuyerSecurityDeposit())) - buyerSecurityDepositPct.set(Restrictions.getDefaultBuyerSecurityDepositAsPercent()); + double securityDepositPercent = CoinUtil.getAsPercentPerXmr(offer.getMaxSellerSecurityDeposit(), offer.getAmount()); + if (securityDepositPercent > Restrictions.getMaxSecurityDepositAsPercent() + && offer.getMaxSellerSecurityDeposit().equals(Restrictions.getMinSecurityDeposit())) + securityDepositPct.set(Restrictions.getDefaultSecurityDepositAsPercent()); else - buyerSecurityDepositPct.set(buyerSecurityDepositPercent); + securityDepositPct.set(securityDepositPercent); allowAmountUpdate = false; } @@ -211,7 +211,7 @@ class EditOfferDataModel extends MutableOfferDataModel { offerPayload.getLowerClosePrice(), offerPayload.getUpperClosePrice(), offerPayload.isPrivateOffer(), - offerPayload.getHashOfChallenge(), + offerPayload.getChallengeHash(), offerPayload.getExtraDataMap(), offerPayload.getProtocolVersion(), offerPayload.getArbitratorSigner(), diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/editoffer/EditOfferViewModel.java b/desktop/src/main/java/haveno/desktop/main/portfolio/editoffer/EditOfferViewModel.java index 721f21bb..34b78be6 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/editoffer/EditOfferViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/editoffer/EditOfferViewModel.java @@ -111,7 +111,7 @@ class EditOfferViewModel extends MutableOfferViewModel { } public boolean isSecurityDepositValid() { - return securityDepositValidator.validate(buyerSecurityDeposit.get()).isValid; + return securityDepositValidator.validate(securityDeposit.get()).isValid; } @Override diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesView.java index 9c351e62..b95aad88 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesView.java @@ -357,7 +357,7 @@ public class FailedTradesView extends ActivatableViewAndModel if ((item == null)) return ""; - return DisplayUtils.getDirectionWithCode(dataModel.getDirection(item.getOffer()), item.getOffer().getCurrencyCode()); + return DisplayUtils.getDirectionWithCode(dataModel.getDirection(item.getOffer()), item.getOffer().getCurrencyCode(), item.getOffer().isPrivateOffer()); } String getMarketLabel(OpenOfferListItem item) { diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java index 81191669..e4fc8b1b 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -380,14 +380,11 @@ public class PendingTradesDataModel extends ActivatableDataModel { tradeStateChangeListener = (observable, oldValue, newValue) -> { String makerDepositTxHash = selectedTrade.getMaker().getDepositTxHash(); String takerDepositTxHash = selectedTrade.getTaker().getDepositTxHash(); - if (makerDepositTxHash != null && takerDepositTxHash != null) { // TODO (woodser): this treats separate deposit ids as one unit, being both available or unavailable - makerTxId.set(makerDepositTxHash); - takerTxId.set(takerDepositTxHash); + makerTxId.set(nullToEmptyString(makerDepositTxHash)); + takerTxId.set(nullToEmptyString(takerDepositTxHash)); + if (makerDepositTxHash != null || takerDepositTxHash != null) { notificationCenter.setSelectedTradeId(tradeId); selectedTrade.stateProperty().removeListener(tradeStateChangeListener); - } else { - makerTxId.set(""); - takerTxId.set(""); } }; selectedTrade.stateProperty().addListener(tradeStateChangeListener); @@ -401,13 +398,8 @@ public class PendingTradesDataModel extends ActivatableDataModel { isMaker = tradeManager.isMyOffer(offer); String makerDepositTxHash = selectedTrade.getMaker().getDepositTxHash(); String takerDepositTxHash = selectedTrade.getTaker().getDepositTxHash(); - if (makerDepositTxHash != null && takerDepositTxHash != null) { - makerTxId.set(makerDepositTxHash); - takerTxId.set(takerDepositTxHash); - } else { - makerTxId.set(""); - takerTxId.set(""); - } + makerTxId.set(nullToEmptyString(makerDepositTxHash)); + takerTxId.set(nullToEmptyString(takerDepositTxHash)); notificationCenter.setSelectedTradeId(tradeId); } else { selectedTrade = null; @@ -419,6 +411,10 @@ public class PendingTradesDataModel extends ActivatableDataModel { }); } + private String nullToEmptyString(String str) { + return str == null ? "" : str; + } + private void tryOpenDispute(boolean isSupportTicket) { Trade trade = getTrade(); if (trade == null) { @@ -446,7 +442,7 @@ public class PendingTradesDataModel extends ActivatableDataModel { } depositTxId = trade.getMaker().getDepositTxHash(); } else { - if (trade.getTaker().getDepositTxHash() == null) { + if (trade.getTaker().getDepositTxHash() == null && !trade.hasBuyerAsTakerWithoutDeposit()) { log.error("Deposit tx must not be null"); new Popup().instruction(Res.get("portfolio.pending.error.depositTxNull")).show(); return; diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java index 73160484..8c31e3d8 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java @@ -403,7 +403,7 @@ public class PendingTradesView extends ActivatableViewAndModel labelSelfTxIdTextFieldVBoxTuple3 = - addTopLabelTxIdTextField(gridPane, gridRow, Res.get("shared.yourDepositTransactionId"), - Layout.COMPACT_FIRST_ROW_DISTANCE); + boolean showSelfTxId = model.dataModel.isMaker() || !trade.hasBuyerAsTakerWithoutDeposit(); + if (showSelfTxId) { + final Tuple3 labelSelfTxIdTextFieldVBoxTuple3 = + addTopLabelTxIdTextField(gridPane, gridRow, Res.get("shared.yourDepositTransactionId"), + Layout.COMPACT_FIRST_ROW_DISTANCE); - GridPane.setColumnSpan(labelSelfTxIdTextFieldVBoxTuple3.third, 2); - selfTxIdTextField = labelSelfTxIdTextFieldVBoxTuple3.second; + GridPane.setColumnSpan(labelSelfTxIdTextFieldVBoxTuple3.third, 2); + selfTxIdTextField = labelSelfTxIdTextFieldVBoxTuple3.second; - String selfTxId = model.dataModel.isMaker() ? model.dataModel.makerTxId.get() : model.dataModel.takerTxId.get(); - if (!selfTxId.isEmpty()) - selfTxIdTextField.setup(selfTxId, trade); - else - selfTxIdTextField.cleanup(); + String selfTxId = model.dataModel.isMaker() ? model.dataModel.makerTxId.get() : model.dataModel.takerTxId.get(); + if (!selfTxId.isEmpty()) + selfTxIdTextField.setup(selfTxId, trade); + else + selfTxIdTextField.cleanup(); + } // peer's deposit tx id - final Tuple3 labelPeerTxIdTextFieldVBoxTuple3 = - addTopLabelTxIdTextField(gridPane, ++gridRow, Res.get("shared.peerDepositTransactionId"), - -Layout.GROUP_DISTANCE_WITHOUT_SEPARATOR); + boolean showPeerTxId = !model.dataModel.isMaker() || !trade.hasBuyerAsTakerWithoutDeposit(); + if (showPeerTxId) { + final Tuple3 labelPeerTxIdTextFieldVBoxTuple3 = + addTopLabelTxIdTextField(gridPane, showSelfTxId ? ++gridRow : gridRow, Res.get("shared.peerDepositTransactionId"), + showSelfTxId ? -Layout.GROUP_DISTANCE_WITHOUT_SEPARATOR : Layout.COMPACT_FIRST_ROW_DISTANCE); - GridPane.setColumnSpan(labelPeerTxIdTextFieldVBoxTuple3.third, 2); - peerTxIdTextField = labelPeerTxIdTextFieldVBoxTuple3.second; + GridPane.setColumnSpan(labelPeerTxIdTextFieldVBoxTuple3.third, 2); + peerTxIdTextField = labelPeerTxIdTextFieldVBoxTuple3.second; - String peerTxId = model.dataModel.isMaker() ? model.dataModel.takerTxId.get() : model.dataModel.makerTxId.get(); - if (!peerTxId.isEmpty()) - peerTxIdTextField.setup(peerTxId, trade); - else - peerTxIdTextField.cleanup(); + String peerTxId = model.dataModel.isMaker() ? model.dataModel.takerTxId.get() : model.dataModel.makerTxId.get(); + if (!peerTxId.isEmpty()) + peerTxIdTextField.setup(peerTxId, trade); + else + peerTxIdTextField.cleanup(); + } if (model.dataModel.getTrade() != null) { checkNotNull(model.dataModel.getTrade().getOffer(), "Offer must not be null in TradeStepView"); @@ -648,7 +654,7 @@ public abstract class TradeStepView extends AnchorPane { model.dataModel.onMoveInvalidTradeToFailedTrades(trade); new Popup().warning(Res.get("portfolio.pending.mediationResult.error.depositTxNull")).show(); // TODO (woodser): separate error messages for maker/taker return; - } else if (trade instanceof TakerTrade && trade.getTakerDepositTx() == null) { + } else if (trade instanceof TakerTrade && trade.getTakerDepositTx() == null && !trade.hasBuyerAsTakerWithoutDeposit()) { log.error("trade.getTakerDepositTx() was null at openMediationResultPopup. " + "We add the trade to failed trades. TradeId={}", trade.getId()); //model.dataModel.addTradeToFailedTrades(); diff --git a/desktop/src/main/java/haveno/desktop/theme-dark.css b/desktop/src/main/java/haveno/desktop/theme-dark.css index 8e7345b3..37903692 100644 --- a/desktop/src/main/java/haveno/desktop/theme-dark.css +++ b/desktop/src/main/java/haveno/desktop/theme-dark.css @@ -557,3 +557,12 @@ -fx-text-fill: -bs-text-color; -fx-fill: -bs-text-color; } + +.toggle-button-no-slider { + -fx-focus-color: transparent; + -fx-faint-focus-color: transparent; +} + +.toggle-button-no-slider:selected { + -fx-background-color: -bs-color-gray-ddd; +} diff --git a/desktop/src/main/java/haveno/desktop/theme-light.css b/desktop/src/main/java/haveno/desktop/theme-light.css index b4ab888f..7605eb18 100644 --- a/desktop/src/main/java/haveno/desktop/theme-light.css +++ b/desktop/src/main/java/haveno/desktop/theme-light.css @@ -125,3 +125,8 @@ .progress-bar > .secondary-bar { -fx-background-color: -bs-color-gray-3; } + +.toggle-button-no-slider { + -fx-focus-color: transparent; + -fx-faint-focus-color: transparent; +} diff --git a/desktop/src/main/java/haveno/desktop/util/DisplayUtils.java b/desktop/src/main/java/haveno/desktop/util/DisplayUtils.java index 2a388954..250c8ad3 100644 --- a/desktop/src/main/java/haveno/desktop/util/DisplayUtils.java +++ b/desktop/src/main/java/haveno/desktop/util/DisplayUtils.java @@ -117,17 +117,21 @@ public class DisplayUtils { /////////////////////////////////////////////////////////////////////////////////////////// public static String getDirectionWithCode(OfferDirection direction, String currencyCode) { - if (CurrencyUtil.isTraditionalCurrency(currencyCode)) - return (direction == OfferDirection.BUY) ? Res.get("shared.buyCurrency", Res.getBaseCurrencyCode()) : Res.get("shared.sellCurrency", Res.getBaseCurrencyCode()); - else - return (direction == OfferDirection.SELL) ? Res.get("shared.buyCurrency", currencyCode) : Res.get("shared.sellCurrency", currencyCode); + return getDirectionWithCode(direction, currencyCode, false); } - public static String getDirectionBothSides(OfferDirection direction) { + public static String getDirectionWithCode(OfferDirection direction, String currencyCode, boolean isPrivate) { + if (CurrencyUtil.isTraditionalCurrency(currencyCode)) + return (direction == OfferDirection.BUY) ? Res.get(isPrivate ? "shared.buyCurrencyLocked" : "shared.buyCurrency", Res.getBaseCurrencyCode()) : Res.get(isPrivate ? "shared.sellCurrencyLocked" : "shared.sellCurrency", Res.getBaseCurrencyCode()); + else + return (direction == OfferDirection.SELL) ? Res.get(isPrivate ? "shared.buyCurrencyLocked" : "shared.buyCurrency", currencyCode) : Res.get(isPrivate ? "shared.sellCurrencyLocked" : "shared.sellCurrency", currencyCode); + } + + public static String getDirectionBothSides(OfferDirection direction, boolean isLocked) { String currencyCode = Res.getBaseCurrencyCode(); return direction == OfferDirection.BUY ? - Res.get("formatter.makerTaker", currencyCode, Res.get("shared.buyer"), currencyCode, Res.get("shared.seller")) : - Res.get("formatter.makerTaker", currencyCode, Res.get("shared.seller"), currencyCode, Res.get("shared.buyer")); + Res.get(isLocked ? "formatter.makerTakerLocked" : "formatter.makerTaker", currencyCode, Res.get("shared.buyer"), currencyCode, Res.get("shared.seller")) : + Res.get(isLocked ? "formatter.makerTakerLocked" : "formatter.makerTaker", currencyCode, Res.get("shared.seller"), currencyCode, Res.get("shared.buyer")); } public static String getDirectionForBuyer(boolean isMyOffer, String currencyCode) { diff --git a/desktop/src/main/resources/images/lock.png b/desktop/src/main/resources/images/lock.png deleted file mode 100644 index 3a4bba5d91e1919cd73c6b5830508436da6a946b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22292 zcmeFZWpErz(l#n)SDWGLOTar6LTwLLML}SV?tv$a}yvSx8%AZWgQ z2w**W(~P^=K)CnSh|93)>iT%0U2Hh6<86q&jp6PL zLPt-(Hb@r(T)I-pClY47$#`n+jf~lsyRMGgk9-^-JZ)q@y7C{$gb&xbKX?TVw6NaKtG-wOno%RjWau>_iHcd`wn#sNSqJ-44~q&)7`#njiVWZ6OM>L zn3DTC2(El-f)PJkAfM_Htm{s(G_$B~KN7kiY7(-;_q||{wLAGb_QZ4_E4;k}I z1cnpUX+}s~gOU8Sq(1`w4)vQ1)t>Slx_`O6>a?p zgSBk8>oS?C4)3JUqi5mVjhkCx(+oho&;w%zE87u5I+LWDS!~2$4wsFcz`V1iF|)a4 z<>?e0Vse#pjoCLF|VF8C-+{k9JtPl(EKow+%4Eil&SJMX>V z)#8v`%W1D^1;RKdes9frOI>yg8Jg#=q-Q@1@i(J6Gef=skwpYS=fUP?m37#&n_sK0 zgIkkY&DMgwm0y0Z(!72(D5y)4${|`XT|&_m!Y1wBQj;lyymg$CP-$TihYza(5lFZ5F{+SVJq>@xeJ*XiKCGlJHaa1wKl}* zAJ`K=IlMEL#5ACXknTIW;e9dViFUk)TioX>I0YRv|s3(Y`plT`pDeLH#K*NdZV|BuJ# z*AkTOaC8IP;f0c0y4Q5$i3t>hm{D*R&Xp<1h4O2+en?CXz8kDO0Em^uzIS6|t%gX& zi!p9KE&>xGy0x1PWvuPY6~_(`mtgq7G2L2HZ(tiY1sCIZNj8PEDT5Gde=i5{>AZ4*DnaUx z!VDM~RMupLZ=xI0QgDYCq|MBCIIOM>&03~=2AWaey$7{anKVfr^To6C7>ajM-t+V4CIDu_$gFLVb`JcLnLAXlU$Cb|10?* z&^{!P==*r^slNo_k8?jZ{DyJG*sss20@8CO3yIR2^6Sn-t;W}>iZtL@uDvA+%6;$9 ztqp~49vs$Y&Z!hyv}!eOW>OrNfjhzG3dl(8wiD0?0#ZWttgx`7wy;nxR%tP}-|xO+ zQ9^+1LtvF5n|%8=?r{`hEHIM~QhK+RtLK14hr5SY55ZL*Ouyo7oKB}~FA&PKBm zEDgm4(R^PG7J$p6Gsut($FRN7b|C7s$~?k^^HF$AEYc_XTwMbxgQ3~WaA4j8VykY2 z_0|o79zQHV0Xq@~mX&9o42oz?*f%yFfBlM@FnoE0i0IQMU+RCfr`%GA+_uj z@^=3n(<()++nXWSC7XOTDQ}`CMk~1|2s~+V?#?dBAC1;2Z#b1+N)K^Bh=;=)pAf?+ z1P|DQrz%=jpMoD{R3AAZdJA%NmPdL6alAucV6oDa`AD5`XGuN-LLm2x3p-bx5`}?7 zGx`BtRDPX>)x{_r?_POZ4StPmUH51N6qu*rvESjg;AWT&4U$oUFlZW1+bD=CT59u~7+gJca-kd%;_W3jAKK3uzOad7eG6%`l`TlSW%S;00XPw)SXuph zQbyD@PNKkI>#>1xBEdKv$i*}M(dN^>Fo_&~kd%R+w}gSeU^6yV82ttOv2`&O1cx;> zp)h(1B(R?eK>J7;o2q|iF!M- z*w2afkY3`x?=Xu5=j|XPhRYqhSZvS>eO<^BrrZul4eHJ%7ElpU#Z$V^Eer|u+;t9% zIguUv5mbrz6Nr4%gq+#bs;plWu6vsy&d_yWu77&_MMB{N>GOVrm24gbHJL(dg~wN0 zr>(IBpaEA&?vp7j`?s{*ISmAX|oP47EszT?qp~ z`V}tdfFXh8Z-4@}>s~;Gz6HV%<~Yi%uNRjIKpUou2Txk^Q2b7)Z|RNv1?Vec?){ON z^?;1PT@ggEPLK$tvJVjdIc-vgoYzH>*riIV{@f};NYYRH-USelbkW*Vs~hFZebz!> zePdBO=l*e$E!{+0WM=U6R!WILXz+MgJWe2KgGjq~e2^xR4k;r9`G!mBV`@53koKaK z(#6GP)LUTH<~t%BnI(hhi)-|IH*ABz{*Yb#226QfLPTu@v4kNU`6^dQZ@mb#BshCM zGal(4Y?9tVpMZEq22c1*TvlFbF;TF#xGZg8;THzAJ%JRuTntdeS?;cH$Q1S17k&3Y zG3jkA-UBuAPTD*ZYmI(b%>tfELwHK)Z7BXEr9V!ukkEs$31*TNfPbni5iknXsprx0lXrC?XS)Yp72dN(jOA(0^;GVa=-P{sEy z+XE)7t8V$!Kdbb2w z=OxKk;W$bd!*{Zdw@@`$@=1Vq2d2*?^%rQ=ivrwa?O*ZSi1&{87)LVAW&XBJb4c^SLG4bpVdq5Kk-j0aEJG_ph?`ZW*wW(PZ89t)t|kn+Rcuz=Bcc zhR20^tK%}veJmoi4k18ejQj@WouxS|ttW8&Uh}Aw&{QMwU28mqxkq3tx*eUPAHF&d ztFRhQQ^R6|n2EINT%{2KhURK)VMCf6LLv23_4ua^fsW87M`Jrs3Pz@vL{L2PD6)6K zaI#PqozSq(p2|s=M9o;d);$AcOEb zWl94DA)DfN2*~L5#~8)n{w>2v56S#GcB#FulxQ5=g ztcgQRGOHr4zWB0#wRn#zAHa_y4wBs-8I5|PWo#L_ziJnN2~7Xd=W2DqaCq;4q$r+! z=w2Eal}w`_DG{gAen%X&czCRqLTT^)U8`t_IL-{$pHLWr*DdHgOguG^I#etOCL3lx zI?)4V$=a?ct-DB2IfKFeD^kpNXTjf4Z_^U)yI*Uz@#LVo2qIdu9f@WPK?Akh`tkUn zUr&mP(c(myxIxydE_bp6@F>uf3$StBw2*#Ztq3DZVj;tkv5h}os~8{*{_fUf*9WBp zrKIR9tqhBDZc*Vpu#F~2GfhIg(LvD#+2wAuQ--OVGh_Uf{rF+wM;;|IR%z=%FmRVq?lIuw< zOeC82%dm;W^imW`G~t!;$FFVvFEt+uxaBeHesIE3a;(fMUXKTH73HK3>1^1RMCu93 zLnobaiE3!>&Bc61u2To6sA{+BNH(ET4c7urvvl7g#>kNJ(ultiDi5%zymo(U8l%&1 zm#dq{zCc_OtIuc>JihvACTsIwFn zVUv%vP1@+_sINk<77#X3wWQ0qo;M0{Su`C)jPup1{6|r1A+iiJsy#&ix8+1r9{Z-2 z`Wx9=5q15g0m}Y8dupGF%c3GH^=Jby`no3x|Ez4{kI3UkO%N9Ujo`J)M z`@bd@Z6m;HCc^#LY$FjPgBGV%CcTec+iX96^u9m!i5s8%zNyAqMpzz@oI1l8Wp7ka z1)**2{+Lus5R)laMI^JL*BkD7dOf9`*s+=DPiy^R$JSCK5%th%PaN-Asu5Isaa z4GJ;OSXlG!Utu>Vxt?wTg5RZbOC3T{HdCq5r+y1o+-k7EBT8EKXV}9Zpl*=pHx)qq zEg}kT$h)PQMLD)mIS5E9`lgJBYNVAVmV~;i=N90sS)N}P(X}f^iQ-j0AB=Pa=8FGo zXHDd2uTK8U$OYT)O0hY#Ew<>>cQA*pT#q?q@SiRTQ3#LJeHODj?n`~))-6D(2r9ja z*93P-)jxii+XNO2*H^i?c%q6_@g!VpS3N3@07;L)thpoAKzouET#(wzVagIlj|)h- zKJLSh(%)MRp6nVJ##mWDc<59pquj)%4z&Xzw`HQ@HzvZl5I7`YlD#GaEDK!eeAi$N z*yFKHcN9~=;D+7UG3KrezDjshB=uW8koUPHpN$OJzpJs{579-BBxL+_wEXEEhU=9z z$_M;#$@Yuj_=|sS!y`OzJm$}$Q0jUCSvTs#{&H_F2%PDtITHRP&Dm!FnsMou@S)O8 zGq$CB=6+JsCNtLLyHUR2$AVGy({f19uehPfSjx_^v%8CPYvXEI@t_R{t*D8@IUqlX zvqp1H?lDH{V9Dckc_utcc5k*xM+l&z9+kF!HP}?`m+GWxtyTw;(+X7y1XK-!o$E`w z9pTLJ^muFzc@5(!`WVGvWsTM&Q4%dR=p~MdbB@Ll_mA}$k3ZaZMj@+7H3;IUP;gT# za<)ioTsQ*uQ3(Q8(FLMn#r50Z4Cqfka)v=ec5gDvE32;CJMLEI>?(#82L<_MTNU?xhB<*4A1 z0E&t%q-e5uaXto1X1FI<$4q+sy@??K$%y8VADV7GKY-An&?6VwbGPysY`ZL>Vd|$g zeWWztk-u-EPx~EL%}SbYpv|JaLP!)x1N;zrBF1qOCmS-o= z8a2%orq8|+%Nqh1TFJ<{R45Q8f|YLq$q=4K_r?~p97pT!Rs8SVXKg&x9u=6bHIq&o zYpJ7`;XOShNQVb4T3WY-te7?oU1#JBTu&2DHl{K%?RZY%?n;BP0V?(yXLEZkbs2+i zT2iN2O7e82%17kDUT*PZ+Pos}&0S&jBItxX&~^$-Xa4Ba8@f%Loi4JCs(M^DyCV_Z z*U23)qVvCdjOMDc$V%Ca?4k5TV!=y-!Qj4JVpSh82?i+h1R!H3PDzF6rpXceD^mc8 zPc}8%ifM_$Y~si8I8@0IH|p&SrDjp%--zIIkx0}SHNL>=+k>C*U#H$sFOcAfiIVXh zhM%z~!Ga?z_@}G|1m4WMNH6Tp^OqE&1(f-87aT9Q^nA5eYi&K<0c18a%odX${N)!F;RczsHX6$b=6Eq>N~2X&4D;b-n%fTrj)040T9HDqIi0EYAbH70z|GOQ zKvfi&O7>rHpeFS-M>=}9R zJV)6K(`wLtIjExgYRn%C3lhgS*hLh@8#-B1rF&98P>ae2^EXm=) z(#@`%&l_~S$MSP}#uDv({yb;0CGCbVLg2OV<`qUVDK%e2eTlfkJtYlz7kDCK?ERaE z_6W-S6>|T|LV~@E(V@evdx9QgJcFwjkcDIYbLz^z;V@UfTZcsI0O^9jgewZkw*UNa z#asbl><-8F__6~MH_yIa)7-XClji2hD4gKkjPxYtRb%M`8etH<2wA6pP3w)R=hFx($i%&}d}oVm~htG^cznxfbn4z?|g;@A!zyKU&I^S>`e+V0fjA zAXsIiV9-ZViu5 zRR~S8#n_@AN>K_!E))Wt%uo?@lRE9@SEwWo7IN^UvKm;E(!JpH1!ava1+10ow^{8G zm%=#nsA0FPT5Qz+2Dr{hykCm~jM)N&v2AAMgU2vdO`YQL^hlc=Ib5|f2shNDKxnD08^g7XQ# zE_X*2Moa!?!P0V;6sQwxV7~yQ*4Boz{cGLY zJk&cy=%o&qr+n;^;{rA~$rdOvUK=7n2k`R42uqNU#nzjpu)zkE4x^oGn`QGkHuckKSuyVSoy%5=`_6>zm~_aK0Y{7IPG|eT z&N-xC61%^{_9@V9o#kiln|vE=b2OE(Z7Qg{%XZSMqMIB#e?3`_H&8Pxa^f7KdsI;U zNXZxK2Gfk(Au2sjd(^R|Jjc*?2)Fyd+4E#hRW!zbUsbDAC_>X37|sV4%lP60wk%8l z4OU}MQ_qNOWeL+g1%{hD`~6_B^i5Lhq2j0X?@A|xq{N#ezAw=fmJ;DCIJ~7cq3Xa? z&fl$|nueiUNA=m5fi;I}67@QILK65y0ZgJO`5a~|`qoqPn;R=4uT+B)7V6;;P5LcL zoLY(t@5r!d8rO@??Y|(q#aOG^`c$T2L2R z+pd@g?|nTdjBJoZZzu%5^OFuElk`InTV<^V7T63^#H0VM;AZFOmb~6n=1-?@tfhQ$ z1Og%0?h9_yL?Al|&6Mk@VwD>X=X3z7xCO7NLlFWUURh~AQVDVuR7X?n+QI<*c6Dqm zC(0EJ8eZKHigN)V(zujIeKh!#;K2@>xay@aO?R|;o76UPti@OJ^ zRDQu(agXwyL20&OPRxPY84c&Ko^>zUj@R%x@TDp#Zl5zK z9jGOZPI1YIxLK@;5QP6jXldMzL!%hbEqwT8i#pnUUXShsKD&xkp4P=rsyrO8f)BcD zs>*7gll1|J)vn^TpUb!GurtGF6Mc!r^-#$7WF7@X%1#ZULTJ#EjLG$IcLA9yEY?3k z0(S$8@j(%INhc)zH9RyhQR6c$Dm;?1#;VA6>O`)q!fl;LbwtAy!teAq@vyax7y|DbXB#Tu$;Q^Y)Ui2=m1bZCaE>-QVM1Qp3O%ISYQ$S z>1raVeN<}%$*zM;LSES-Es-do>FW$rtB%>f2(nEqr;jdCJD0`}8+h}Rmt!K+bc@~g z`i!)Ly^G8vO4j%rt|FGOg<$fb;|6qU{))tScJu`KHDp#2#Ws3frzW$#m0b6QM~az9 zR1aFYo}ydvb-rqw!OqYYN9E@v4L|sLM!ke1wP9QS?zs3Sn>n zi3AM9mpC!eMgGOyv=ofjpXSWY-&rZGHk8uxe0-rHIN*Qb!;i17BdMYE%_v-XBUJ=4 z=%A#Qv!}f4W8u7Ksc85e>+7ZKO)g~0leT&__6g1Cr>Rx+n5*vnR1a6(?V}c1vrTw? z+9M?vQDE(Z$VRqJN8+dKm;gpjI=OX4pNg-zHk(DKttQlE$&lqjfAZ??s_QSOceGLA zR?u_l(&E)*tGy(zq~@XUQp4n%Q7{Uso4RP(&T(L{lo9Onz@w91d|<<=WUHTXC2CFt zsvwMHEjwMQ)QyTm=@$=D*s=n;AHS2sYr+{ld51f+#QYI_Ag8fbLn@;c2T@?y(!`;w z2NZ%hJT*isvjO?WWfqG6@(l#56lMlKi6a0R`1mH+?|o?I zuV%;5o=?HSi{!%!g2irGz02waPS|fRosUBZ%%T}4+n2Sqw6CZdTQ#c0A7eLLsd_1W zjS?Ti{a4w{wh6xyXXXs`<~jqNPtKZ^&dlrca0?(B*Gh&M3ih++0xaY7MU$&g?Fr(} zuDO!Hha0M-^Dh+329NVYsx2HCq?yix8j*2cD;Pa+_P=1~yIBo`v&S66$n5P2BN1=h z2`1!DNb@R82vAFv=_*zU>ah9yAA*QuYXIhI5jP1`F+H^B!=Rbi!Q*7hx2*5?o_w9B zp0p*iz`1~cV7SbMg%uRcY@KW!&1~%mC546mXKBFaf`H7dWLIV71@zFV4p)!72?kUu z;foG2#1X-M`aSrugt`6tsEUg2 z%=M~HdwU%zDH{Z)-j+EX54@%q-t(h z{<5V2xH@mK{@F)V#y(2M8&OEqy7p8QjHSzlDPr!(w5&luq`KEWoC~(rI4|*fI@xX* z<`ss$B6>;!?Y0*2ybM~gz38hdZpNU235XwY_!-C-8p-}*V)Cm#p;eJ~R3KccE1K^g z5#X7){*T{mj<8z1Yz*W5s>h}aGM1K^4%ahJmA?zj!aIn?K`LsZU?;QeH>k~-_AWm4II5w zBCKU4bfXr-9B#MPO)Jd=dwG~ZBn;%?yt#*c*l+6P^f3e_bZzPSj_psu|TYd%?E5z$ry{B3$nYw=a-KNjO-}z1N7}TyRR1qVpluV zT=+oC$JfJ+hF;@svV@FB&PiI*s7d7O2K0yiDdb#WUh+qapOd|fMgenrSOn&1;5W7(B0`Ce?sNe&F;fM&xLQlWQOTnl^%k#KO zp`Y=&z;*npg#v%^nxUo#_=K+aLCFM^)iPDWXLQ3Yg5L-BZ6?_fw_{HR!fu_}p=l!! z1g>nsUOGRr@qqA$nj-ljB4Ou;U=qRg!$XVY$AJaKR0>DLNfqLo3%17U?vgVF$dFtp zV4{MHieu%SDz2CAQKDP0THuwVszh@IScu={+T@EVGMCDmF=;|u`~4Jt$tC$6erV-{ z)QY?oW+SX8v>_H#u%XX@i7f^R2)r5;F^FfhWGG5~OI00rA15?{V~!j!M5~2RhqNM0 z4oB0Eto5uLsy(O`UbH{e!bK{HU>J4-f!GT*5)>UoEd;xTyhXHy7AK)hv5XuTuDuOmh<3$s1!JH1 z_?=XEnxZAnP(oMik+%>ma7cAOzB(Sw}xp{bLJy~BbK9Y zM+MVrMHI%hOv_C8Ojc<^X~Ikx#)FhlqJi6D28wKo*pw-hJIZh>p~u3q#mNd?1@}s( zM#e_8#(l<)MwUiX!{2`)rn60iCQH#2WaX~qb)+aIJI3t{TPIk?;lyVo=n-Fhorm>v(Jqc2;YSz#NeU6B`;E4SNlnIlVj`IQ=PoBK@K9QWLI< zw2D`=L(_4YYZ+-->j(D_3g=yCFQ;p#bZ7kIc;_r9wUhai_36Gt#@lbV_a|$=Lhq_> z6K*YUA>s5vEkUb$d;$f9f8yO&qhC?<)YQq#8!9silDyUn6D15 zZe4f$^V!TAFwtaFmtFt+M|i`MDf4(PQvhaB6wOaFEwz=qpC#5COByTCr*eyLCbmX7 zhQ6~t4rPxW21Vv^`*N-u# zU{n`Zdo8dnaI$kZHQF4kA-U2&7(VzQV8!g=&*8`L&het~X7HM}SGLc)Wj%;_zPMl9 z;@>OY8$7W*jXy3wR^Odnkvs)ItGw`DEj%2&xPo^BcLKM8;M2`$4rlYaz#+Q(#kEL#0E8g4lznM4QDE z#iK;hMXICQ@ry7AqK~5n#X`l|#ipt+)$2S5+@e%u6vwlYxM&}7_7g$7**jfjNy>x3&w z%1I_k&SQG>(B5S3pq+y=f(tahYp`nAHuN>f?@Jt9Iy^hvIX3Od?Yi%K zw?Q<9G!}E~(fR`34>eO7olkO|uTEMQEGy}?`8#_o`{JU5qN#9vsTpV~>D@Z043*UK z9JWGw>n3zFG9pPM9!2}KPMcoY>aUDAE)HK)7XbY_sZ#FF) z?J9Sy*Z|#yTdJ$uuEcO$HhD&IaeyU4RW9d3@9^qw~(t6-b!PDkNM*Q25gsJkuCe?=0 zn$EqBH9z);+vAVrqjcta)6q%fN%U+Tjucm$d#}N#BiL|^!B#*Ufp%28TLndP$w&M~ z5lsp&z22$w5wGjO*62}%i<-O+K>MWG#=E0qs=Vfmq9SMcx$0#F(h7phc5(d>Tbs+x zW!U_%SN0(z#rA2n)yH$`tcQE-VioT@7JkcEC$~HO-TK&z#=`-Dm0OsP`m4mT;vS%A z=#Ky7ZI#QA*TH1g`^SakVFDzevhJYE=>6KD{gUd*d@tx&WTxlDNUpHITQiBI)oRqqPP z1?GjvhIo5U>eRd6o5G#q1z>@qOCAAZ8Xf%|95nUjl?@kR`g6G{w)y9>QgvAwE`Y5y zoq>_9p)sACwcY2kQy?H7em6SfoGf{X z)nye3g>4;-30de^=on~4-OOE>i1}a$c^r&PxW0*q{S)HzjF;HV$;pn3p5E2fmClu! z&ep+{o{^K2lb(Tzo{5R}Q-apf-Nwnljn>AIk0qIK5IK!+5d&Nar`F>pM21}8Q9S?(lO9mThsr$hNF|H%O}V`4f;Q7I4Xax z&ZYlm>}c!k05BGHF}86c`F98-!2jyoIXhVW<&F`6-q_06`cu^LvsK3bXi{8KR^fj& z{!n0QZf*CM)+gEjA?ajp@-MRf!?r&)f4TGThJ333FYbRx|Bvr~34cn-%5sU=0-XPt zCn>^9{HJ{`BU^yE5!YXb%p5F?MkbuBv`ox~?6fS*9Bj0l9PGxl492WX#sD^E17iT^ zzd=dbI64{F0F3`YeS*`Of8sE+7#Ok{FmTYa85^?FvM{i-(;BiHu+SQ?aT>64FflT- zGO+y{guH|KXH*(k{kv6vpo~7Dm>8K10S4?Ww5$LlmQN@qMp^?SV?$a)24iDGLuLa5 zBSz!Dpo{=qVzv&}2A|z&Zf#&{OmAmn`d7st!np($BzcLM=otQ0qF`mKVurdBiNY&WE@iP+tU@|h$G5-ViPq%P= zI`c`a!Jjz&1o%ti(-$sb2V(;#TL)!ZTPt4TKPD0W(fljC33>j}DH7(6pAznWBL1H- zuVifhkF$Ra0W0&rst5`H3R^A%z&{#sG;lFC`m3Q&y?;~z%nWQyjX%ftKLhH2%FX`| zon^vkWXuR)H=$+bEBgPPuKvl{9~%EJe*W1P{})^Mg#Npc|BBzg z>H3?l|B8YCO8D>S`kSu*ih=)1`0wcY|BWt~e?9OR+kD;yxqd#*fb~p%{=e@3GuJK= z{&%kZ?_B%ex%R(v?SJRm|IW4loooL)*Zy~|{qJ1+f8^T9T+y9B6Y3C#(&8dOAAi2N z9VH2$CD3*f8je6fbR>T^V7DTEm(M~-CrMdR$O8~GNN%dUHPjFwAk=k95kY0Q<%tZ> zSRJ*bbzjdM$Mcta;WdxORf{sRvE;8yz$g%~D?tX=epDuj(4fK*k|C)E@ltaFD)0Fq zGAh(45^<=(B@&ESNC4#ccpW&_Y(opmzC`d;xSuTX#$UgVpj+^^&Q;m0oqN7?PA+*? zz1mN9SX98-&(N;uEOQ;)pLyPEZoJPo%`YEl_nq7pl;1n%ysRddm6bt~Q&1pSt^Bn1 z(%09Q5OFdv*m_=SFr|OrrQrWe&~rZZg%bF;A_$zOtQnf;_cK#e23(LUJFyL&ZsloX~b##xZJR)o}6X5QiR-snI#LX>T zrCP9N1DaW;Or7&QZpwFqmuneyWh(`wJO?Bwru8ym>(k(My*om$*Wqy|92ptObJ-1g zy4)C9SI79%dSy_cQ7SRgE?ii#-Zu=YQ8PL?7@SV0wf|~oNHRW1DDSed!}Gys8Q1eae_GCpS0u zXM$O*Vae!be*zQG%*8x0o)kGgu5GZjj2jurnw*kym^~+ zh7WfJoAvhR`z4~je|LDkH4h+kyS~Z!c=e3IZWU z#O3$Nq{wy)K%tOh;QN^t-G_Fa$E5yq7j$3~378_JI`BIJ;c&y^Ap>(;_dBTT&ADq= zhUa-pC@cmYA{elE41SSrjqfq6-ed2<0w_=$KJsM|z4y^~3|+S^ls$*E&koCUe7y|i z-#yX$;QHfDcH09phXP1AS?#mh#Lw4?7BbFD8iY>U+YA23^=LKAtF<)|KK$*jlB;^v zNMKKwwLwRUkMH96?m-3OG4+640 z!D^{KxFp9XeF)z(O0J}Dcb9Zx&{;^<$4Y9?_lDwQrw*lK>a9uqqurGYfd}Dr>B4vS zm-jkr=Qi}kdV^(v;{pBk<4#P%*k1F;`-_IQ_U=(h4&C`;-A+UKb6q34q)FB7$0rNi z?)&3u7!Ma4wyqcJ6Vmh@{RJ_6qqny#D^0eh20mX%-A<8m@{p%snSsd%4%nOx2hlo4(xS{tK>hh{z(>m6O7L20x#Z4l~&l?x{qYfZKUMc=|B zG3Xu6S2;sP;J$lIhp%0*WtpgXNNV#T{LZen4OA6iB*Eegys(i=)Hh|*$ zBz}xF z%}i;YweuZld}0DVhr>SLGZgm^4vt*ftk&Dp1_nfX8F=pJYJ8k0&{pC|#H+ys?L6%5 zA=&%MWR_g0YK3US66tqs>%7@6LUye)tvf>^2uc@Q&VV7t}X zYQQj#5Q-8#5)&X1AH?w;2`Qe`eMibbWdJj<{C+Bsup2Y5(Pa&YVeq!bx=K{!`34KH z&H?0+@BpO#)Ygc7AGxp90@UHYfe7S5Q#YNO1mMNfbMHuU?A)68B^BE7&;;g(|BE=_ z0Uya7{LvN@h$b4u1`B8~wR48I=M1=Z-hCb%NJUWeYI4ilV$;LI?|~0o4eS89-L2u=0{PeF zh`M`*0cwHb^m?T*@B%8myGWo~ zlTeQ~z9M;8g=Uu;&7p{?#W0*1qdNbJX>IVUQ%bB{bYY7CQIO@BjsvH(*&U#SzNQV;jy5$kdqcdCMe`lbjtT*P)~7E9J2sSF?)MZ2iI*(3mO)Fb_f&At1vZRR3=P> zb9N=W9}s|uBC=Q-Hjz}<0wZVulu=<=zyx`ozAG=UvpE*?3ciikqTRpkkkZ8eD8Ja* z7jLo6)z9bV}==Y4kpe;XMbr84_R9s2?(1hxLxW$4AG z8Yx6Nq(8aYtE(uZ@oF|mOF!t z;g-4hR(=o3a=5Yd@Ff-KiBP@7#1V}VtoSStLNW?KD7n@CO$bt#t17J&S^E*MOMadu} z2|L82M&5_iQ6+4-g!$+xLuzZm7qZE!<+pZ#EF)QP80oH@G<7u3f=J6&=5M!TyPrWe zx!}=tpV|Cls8pm;D~zuLRU85XQ6bn6RoD=d*bvb}m>8rMYuQJsgoPwo1(r*l6lDbr zm3hl`5Mv~wp26a5Je(M}4QFA=afz$-iW@GO)&?y5qam&w5(?)G+{=Uu_QFEg6{|y< z2w^b;BM2n}E`CUY420mLkYb{MTK^(Hwlw=j6edLlrHF-t%4k4V#KLQek_a|iQuIU0 zgYHYxE#*29pIH2t`2NQ|bL|TMn2a!a#GJP(3m15uyZ1v&NPXi9n)FU|U?jvG>}?dp zP^tJY8=_IbcbL(Py2c#^x7vOqm#_{?<#Hb(gGv&>4+-X9O7dzFXM}*d{^fKXcrQii6$Z*X!yG z5N+3{q3A22y8+`EVbKZ9OenG2hqSRRqKEF&Kp>-ep;0)&p@6613)zF_k%ZM)jN zbxYkN2^3fBN@dEaSy_7n79{&z~GuJ@uK5h>EK*KmwYhp0PmNFOCGt znMaSM{Owe*OKExqSmpxXk4-%YH7X(S(K5~+!8ocI+#dp0So{FAYA2 zspNUrhtK-cq$?9AXWIIM9(G?UY^f8I>eJWRRT#WqiNHc#5Qoryqg4`tA!x)BdUnrO zDQC+O4`omuq1d2oYu(!Au8Xyn%q`m4!l7Rnd3h)L5cs3YuX_-DVqM#J@T@jEv-0wY z=;Yp_?#HFUrVCmg*Ir|@Z}xK1&rl*ehPfr*l)X$>_-wS%1It(-5klEkI zJD0NDT#{Rz!3kqh5eI+k_34J^M^^+3z^|b~?r%%?i<`mXO9v*$XCBEsC|>`W0L6?F2koOD7~G zr1_+}&26D=U9?Qu(8>yqzIUL+x4UK|L6~cV1{*Kw$9*gs5k>qmu|wY%fNBc(sf7?> z5f){L@@3-`*p){@PbUImxdgJ~N-=TQ4Gn90qd7S_Ip*iH@kXaNn4mz>H-r6A<8?>1 zY_FHu=-Hps?kQg`e(-laXxwE%=TYk0hC@xuGg`2=(H6TMyi7>Q=k|L>vE~Le-gdp_ zuQ!;s_xirOrJ*R%+h6U$J88l9Qs%qgvH04WAvL_>Qkk%IBqylB#Rki83D)o?lDm|vM^p16K4QelXLk}%6k&NSd9ePu@!Zu=zmNEM7N`Ys zK{cNtslhzt9n!8g5V;NBj^Y&$vei)x@t`)b^HNl4v`yYTiM%#2)2vDhKK1l|(0)^@ z<~2v>?HB5E#>T7=(Q83OIS^iRe_d6&&1!w! zo-&Rp*<*))!9!=?e7b6yl<67}lklz>ue|4ZaVJbkDDdt1E_E|=h^t2(60A%B zyC;>!7CYhmIo)c3{A^R^XN_rn*4K`USr`g)|V~zuRIlFNAsRXNjI=ruJ5w&wYqh3cDmoT9PxL&(wXKA2mYS`NCLP0U`RJWO|N<~83T01 zCI(q(N!m6`_V}+z8uz_Wa?YntXN6FD-V@^<1EAfNjj@xBp*2g^YCE+2To$}403Ug9 z%ybe76R*Wi?1wvvZ0W>)g#3-Bs~3BsJsn76-6Srnl`O`CF@ni9D^bb1Dt*P)8Yau* zLVC+wKnb#}BKXAN5}%&?3r?RwMhO3;o?kn4DJ?ow^8G?qUbcNno&OZiZvf>rASG3o^C)r2AhM#)9l4wwal z?YGSsn>8$1-EI`D$r`B)91#o#)L|_cRg6`H>`RuSB%&lvH1SH3h;$Q`*pWE(&Gxm$ zg-dhIh2e5&N`=4{a@H1e)&^njRcpjojg6oEpI=SC2&tM=rJ~9PQQab<+q~B?=XGP^ zWH@nJj-3?aL~@CfD0UJhPDAH46eTnm8(0&Bp|wF^EviPrXtqOY>BVjei%Hob=_a#^ zhX4+b&jWM&vaNQW1V7h?p z{P5qrHUM4$Li&Y-JWvA4*(d=8AeX%#W+PZ}v@4FiXZm|b^K)v=-jJJy&yKxV_Sv}u z5+DXT>Fp_+*=PaX-e+FT`PX!JI0M427{N`iS337vW2pAO7B|pBVdpdw(cNGC?t100000NkvXXu0mjf D;@nZw diff --git a/desktop/src/main/resources/images/lock@2x.png b/desktop/src/main/resources/images/lock@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..371f6aeb5d8845ffaaef57e46036d90f05797826 GIT binary patch literal 721 zcmV;?0xtcDP)0EtA|W9mp&}N)5*Y$lmAxlAT8HZsXL znJ`mJ6uamTu!0LO)JzJcsC=Gtn8>J=If*k!z`(ni^S$r;e91}3p^1AbigL8AuFteI zeI6@gX`VbIXPy;Hz1}goW8B1LpIe#%2T9h99l4|5AhRxSy?#>G^qy4$)n_fpNi|q0 zi&R{-PM^s?VDwEzF;2Nu{^B?e+$O;_S%DN1;8Bh{P)Q`v84A%c-pGbA^;hKpa2Ri; zwydpVT%Hy!7!DYa4(H_0=6MrO#Y1)(b>V{Cx7YFs2iO{EKq`2sD^ z6wBM3D4zIrHeXqr#%FKw44I`K>u$`B4S=_QPC^Q`1}#qu#g7P~BY&DFXUjtA6P~4> z;kz(a`I@eWaKP6_TZJWS`&CIg>*V9=PiL%;35XXj@kP_L;K%@cy>TT|Dnrb=UeIQ_mZrU0b;q_C40!j9EVuFWY+E_ zSbguJRp;qKx+NM~=s zgTx&utX^XUAhX!BK)f{L<&)^}3^@aI{ky}U4KWIQ_9`^_bMQfpumaH8X5l>bZdA|M z&in%gfB<+YX8<(-CLe@BIs}81E23E@3PN9H0KnzM_ zAUJRYz(ayp0A@18I|7(-Uzk7;TKCFn7Bb_%|0VklVx(?C;s@SE00000NkvXXu0mjf Dtl2{# literal 0 HcmV?d00001 diff --git a/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferDataModelTest.java b/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferDataModelTest.java index 98c933ed..33d43b7b 100644 --- a/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferDataModelTest.java +++ b/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferDataModelTest.java @@ -53,7 +53,7 @@ public class CreateOfferDataModelTest { when(xmrWalletService.getOrCreateAddressEntry(anyString(), any())).thenReturn(addressEntry); when(preferences.isUsePercentageBasedPrice()).thenReturn(true); - when(preferences.getBuyerSecurityDepositAsPercent(null)).thenReturn(0.01); + when(preferences.getSecurityDepositAsPercent(null)).thenReturn(0.01); when(createOfferService.getRandomOfferId()).thenReturn(UUID.randomUUID().toString()); when(tradeStats.getObservableTradeStatisticsSet()).thenReturn(FXCollections.observableSet()); diff --git a/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferViewModelTest.java b/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferViewModelTest.java index 4b33c8ba..a8c6ede5 100644 --- a/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferViewModelTest.java +++ b/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferViewModelTest.java @@ -55,6 +55,7 @@ import static haveno.desktop.maker.PreferenceMakers.empty; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -99,7 +100,7 @@ public class CreateOfferViewModelTest { when(paymentAccount.getPaymentMethod()).thenReturn(PaymentMethod.ZELLE); when(user.getPaymentAccountsAsObservable()).thenReturn(FXCollections.observableSet()); when(securityDepositValidator.validate(any())).thenReturn(new InputValidator.ValidationResult(false)); - when(accountAgeWitnessService.getMyTradeLimit(any(), any(), any())).thenReturn(100000000L); + when(accountAgeWitnessService.getMyTradeLimit(any(), any(), any(), anyBoolean())).thenReturn(100000000L); when(preferences.getUserCountry()).thenReturn(new Country("ES", "Spain", null)); when(createOfferService.getRandomOfferId()).thenReturn(UUID.randomUUID().toString()); when(tradeStats.getObservableTradeStatisticsSet()).thenReturn(FXCollections.observableSet()); diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index cb6ba818..c9b8a754 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -521,10 +521,12 @@ message PostOfferRequest { double market_price_margin_pct = 5; uint64 amount = 6 [jstype = JS_STRING]; uint64 min_amount = 7 [jstype = JS_STRING]; - double buyer_security_deposit_pct = 8; + double security_deposit_pct = 8; string trigger_price = 9; bool reserve_exact_amount = 10; string payment_account_id = 11; + bool is_private_offer = 12; + bool buyer_as_taker_without_deposit = 13; } message PostOfferReply { @@ -570,6 +572,8 @@ message OfferInfo { string arbitrator_signer = 29; string split_output_tx_hash = 30; uint64 split_output_tx_fee = 31 [jstype = JS_STRING]; + bool is_private_offer = 32; + string challenge = 33; } message AvailabilityResultWithDescription { @@ -785,6 +789,7 @@ message TakeOfferRequest { string offer_id = 1; string payment_account_id = 2; uint64 amount = 3 [jstype = JS_STRING]; + string challenge = 4; } message TakeOfferReply { diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index db5aa49d..b52274ae 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -250,6 +250,7 @@ message InitTradeRequest { string reserve_tx_hex = 18; string reserve_tx_key = 19; string payout_address = 20; + string challenge = 21; } message InitMultisigRequest { @@ -650,7 +651,7 @@ message OfferPayload { int64 lower_close_price = 30; int64 upper_close_price = 31; bool is_private_offer = 32; - string hash_of_challenge = 33; + string challenge_hash = 33; map extra_data = 34; int32 protocol_version = 35; NodeAddress arbitrator_signer = 36; @@ -1412,6 +1413,7 @@ message OpenOffer { string reserve_tx_hash = 9; string reserve_tx_hex = 10; string reserve_tx_key = 11; + string challenge = 12; } message Tradable { @@ -1528,6 +1530,7 @@ message Trade { string counter_currency_extra_data = 26; string uid = 27; bool is_completed = 28; + string challenge = 29; } message BuyerAsMakerTrade { @@ -1723,9 +1726,9 @@ message PreferencesPayload { string rpc_user = 43; string rpc_pw = 44; string take_offer_selected_payment_account_id = 45; - double buyer_security_deposit_as_percent = 46; + double security_deposit_as_percent = 46; int32 ignore_dust_threshold = 47; - double buyer_security_deposit_as_percent_for_crypto = 48; + double security_deposit_as_percent_for_crypto = 48; int32 block_notify_port = 49; int32 css_theme = 50; bool tac_accepted_v120 = 51; @@ -1744,6 +1747,7 @@ message PreferencesPayload { bool use_sound_for_notifications_initialized = 64; string buy_screen_other_currency_code = 65; string sell_screen_other_currency_code = 66; + bool show_private_offers = 67; } message AutoConfirmSettings { From bd5accb5a5c19acb4075d9d942acb85d1de0a036 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 16 Dec 2024 10:57:41 -0500 Subject: [PATCH 27/34] update translations for reserving only necessary funds --- core/src/main/resources/i18n/displayStrings.properties | 2 +- core/src/main/resources/i18n/displayStrings_cs.properties | 2 +- core/src/main/resources/i18n/displayStrings_de.properties | 2 +- core/src/main/resources/i18n/displayStrings_es.properties | 2 +- core/src/main/resources/i18n/displayStrings_fa.properties | 2 +- core/src/main/resources/i18n/displayStrings_fr.properties | 2 +- core/src/main/resources/i18n/displayStrings_it.properties | 2 +- core/src/main/resources/i18n/displayStrings_ja.properties | 2 +- core/src/main/resources/i18n/displayStrings_pt-br.properties | 2 +- core/src/main/resources/i18n/displayStrings_pt.properties | 2 +- core/src/main/resources/i18n/displayStrings_ru.properties | 2 +- core/src/main/resources/i18n/displayStrings_th.properties | 2 +- core/src/main/resources/i18n/displayStrings_tr.properties | 2 +- core/src/main/resources/i18n/displayStrings_vi.properties | 2 +- core/src/main/resources/i18n/displayStrings_zh-hans.properties | 2 +- core/src/main/resources/i18n/displayStrings_zh-hant.properties | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 86fd962d..71ed6e31 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -200,7 +200,7 @@ shared.total=Total shared.totalsNeeded=Funds needed shared.tradeWalletAddress=Trade wallet address shared.tradeWalletBalance=Trade wallet balance -shared.reserveExactAmount=Reserve only the funds needed. May require a mining fee and 10 confirmations (~20 minutes) before your offer is live. +shared.reserveExactAmount=Reserve only the necessary funds. Requires a mining fee and ~20 minutes before your offer goes live. shared.makerTxFee=Maker: {0} shared.takerTxFee=Taker: {0} shared.iConfirm=I confirm diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 85f021d4..e2e692c4 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -188,7 +188,7 @@ shared.total=Celkem shared.totalsNeeded=Potřebné prostředky shared.tradeWalletAddress=Adresa obchodní peněženky shared.tradeWalletBalance=Zůstatek obchodní peněženky -shared.reserveExactAmount=Rezervujte pouze potřebné finanční prostředky. Před aktivací vaší nabídky může být vyžadována těžební poplatek a 10 potvrzení (~20 minut). +shared.reserveExactAmount=Rezervujte pouze nezbytné prostředky. Vyžaduje poplatek za těžbu a přibližně 20 minut, než vaše nabídka půjde živě. shared.makerTxFee=Tvůrce: {0} shared.takerTxFee=Příjemce: {0} shared.iConfirm=Potvrzuji diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index e8b8584c..d1bfa9f0 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -188,7 +188,7 @@ shared.total=Insgesamt shared.totalsNeeded=Benötigte Gelder shared.tradeWalletAddress=Adresse der Handels-Wallet shared.tradeWalletBalance=Guthaben der Handels-Wallet -shared.reserveExactAmount=Reservieren Sie nur die benötigten Mittel. Es kann erforderlich sein, eine Mining-Gebühr zu zahlen und 10 Bestätigungen (~20 Minuten) abzuwarten, bevor Ihr Angebot aktiv ist. +shared.reserveExactAmount=Reserviere nur die notwendigen Mittel. Erfordert eine Mining-Gebühr und ca. 20 Minuten, bevor dein Angebot live geht. shared.makerTxFee=Ersteller: {0} shared.takerTxFee=Abnehmer: {0} shared.iConfirm=Ich bestätige diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index d3f61ada..af7539e7 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -188,7 +188,7 @@ shared.total=Total shared.totalsNeeded=Fondos necesarios shared.tradeWalletAddress=Dirección de la cartera para intercambio shared.tradeWalletBalance=Saldo de la cartera de intercambio -shared.reserveExactAmount=Reserve solo los fondos necesarios. Podría requerir una tarifa de minería y 10 confirmaciones (~20 minutos) antes de que su oferta esté activa. +shared.reserveExactAmount=Reserve solo los fondos necesarios. Requiere una tarifa de minería y aproximadamente 20 minutos antes de que tu oferta se haga pública. shared.makerTxFee=Creador: {0} shared.takerTxFee=Tomador: {0} shared.iConfirm=Confirmo diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 710bbb6b..6c528640 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -188,7 +188,7 @@ shared.total=مجموع shared.totalsNeeded=وجه مورد نیاز shared.tradeWalletAddress=آدرس کیف‌پول معاملات shared.tradeWalletBalance=موجودی کیف‌پول معاملات -shared.reserveExactAmount=رزرو فقط مقدار مورد نیاز پول. قبل از فعال شدن پیشنهاد شما، ممکن است نیاز به هزینه استخراج و 10 تایید (~20 دقیقه) باشد. +shared.reserveExactAmount=فقط وجوه مورد نیاز را رزرو کنید. نیاز به هزینه استخراج و حدود ۲۰ دقیقه زمان قبل از فعال شدن پیشنهاد شما دارد. shared.makerTxFee=سفارش گذار: {0} shared.takerTxFee=پذیرنده سفارش: {0} shared.iConfirm=تایید می‌کنم diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 5e734ac9..2712ff41 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -188,7 +188,7 @@ shared.total=Total shared.totalsNeeded=Fonds nécessaires shared.tradeWalletAddress=Adresse du portefeuille de trading shared.tradeWalletBalance=Solde du portefeuille de trading -shared.reserveExactAmount=Réservez uniquement les fonds nécessaires. Il peut être nécessaire de payer des frais de minage et d'attendre 10 confirmations (~20 minutes) avant que votre offre ne soit active. +shared.reserveExactAmount=Réservez uniquement les fonds nécessaires. Nécessite des frais de minage et environ 20 minutes avant que votre offre ne soit mise en ligne. shared.makerTxFee=Maker: {0} shared.takerTxFee=Taker: {0} shared.iConfirm=Je confirme diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index b968d839..9e52fd89 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -188,7 +188,7 @@ shared.total=Totale shared.totalsNeeded=Fondi richiesti shared.tradeWalletAddress=Indirizzo del portafoglio per gli scambi shared.tradeWalletBalance=Saldo del portafogli per gli scambi -shared.reserveExactAmount=Riserva solo i fondi necessari. Potrebbe essere richiesta una tassa di mining e 10 conferme (~20 minuti) prima che la tua offerta sia attiva. +shared.reserveExactAmount=Riserva solo i fondi necessari. Richiede una tassa di mining e circa 20 minuti prima che la tua offerta diventi attiva. shared.makerTxFee=Maker: {0} shared.takerTxFee=Taker: {0} shared.iConfirm=Confermo diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 70a35361..c1331ac5 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -188,7 +188,7 @@ shared.total=合計 shared.totalsNeeded=必要な資金 shared.tradeWalletAddress=トレードウォレットアドレス shared.tradeWalletBalance=トレードウォレット残高 -shared.reserveExactAmount=必要な資金のみを予約してください。提供が有効になる前に、マイニング手数料と10回の確認(約20分)が必要な場合があります。 +shared.reserveExactAmount=必要な資金のみを予約してください。オファーが公開されるまでにマイニング手数料と約20分が必要です。 shared.makerTxFee=メイカー: {0} shared.takerTxFee=テイカー: {0} shared.iConfirm=確認します diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 93179908..d7006d46 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -188,7 +188,7 @@ shared.total=Total shared.totalsNeeded=Fundos necessária shared.tradeWalletAddress=Endereço da carteira de negociação shared.tradeWalletBalance=Saldo da carteira de negociação -shared.reserveExactAmount=Reserve apenas os fundos necessários. Pode ser necessário uma taxa de mineração e 10 confirmações (~20 minutos) antes que sua oferta esteja ativa. +shared.reserveExactAmount=Reserve apenas os fundos necessários. Requer uma taxa de mineração e cerca de 20 minutos antes que sua oferta seja publicada. shared.makerTxFee=Ofertante: {0} shared.takerTxFee=Aceitador: {0} shared.iConfirm=Eu confirmo diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 1b1b5de6..cd016e3a 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -188,7 +188,7 @@ shared.total=Total shared.totalsNeeded=Fundos necessários shared.tradeWalletAddress=Endereço da carteira do negócio shared.tradeWalletBalance=Saldo da carteira de negócio -shared.reserveExactAmount=Reserve apenas os fundos necessários. Pode ser necessário uma taxa de mineração e 10 confirmações (~20 minutos) antes que sua oferta seja ativada. +shared.reserveExactAmount=Reserve apenas os fundos necessários. Requer uma taxa de mineração e ~20 minutos antes que sua oferta seja publicada. shared.makerTxFee=Ofertante: {0} shared.takerTxFee=Aceitador: {0} shared.iConfirm=Eu confirmo diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index d40f9af7..664aebda 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -188,7 +188,7 @@ shared.total=Всего shared.totalsNeeded=Требуемая сумма shared.tradeWalletAddress=Адрес кошелька сделки shared.tradeWalletBalance=Баланс кошелька сделки -shared.reserveExactAmount=Зарезервируйте только необходимые средства. Может потребоваться комиссия за майнинг и 10 подтверждений (~20 минут), прежде чем ваше предложение станет активным. +shared.reserveExactAmount=Резервируйте только необходимые средства. Требуется комиссия за майнинг и ~20 минут, прежде чем ваше предложение станет активным. shared.makerTxFee=Мейкер: {0} shared.takerTxFee=Тейкер: {0} shared.iConfirm=Подтверждаю diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 81955b6f..2688b08d 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -188,7 +188,7 @@ shared.total=ยอดทั้งหมด shared.totalsNeeded=เงินที่จำเป็น shared.tradeWalletAddress=ที่อยู่ Trade wallet shared.tradeWalletBalance=ยอดคงเหลือของ Trade wallet -shared.reserveExactAmount=สงวนเฉพาะเงินทุนที่จำเป็นเท่านั้น อาจจะต้องเสียค่าขุดแร่และรับรอง 10 ครั้ง (~20 นาที) ก่อนที่ข้อเสนอของคุณจะเป็นสถานะออนไลน์ +shared.reserveExactAmount=สำรองเฉพาะเงินที่จำเป็น ต้องใช้ค่าธรรมเนียมการขุดและเวลาประมาณ 20 นาทีก่อนที่ข้อเสนอของคุณจะเผยแพร่ shared.makerTxFee=ผู้ทำ: {0} shared.takerTxFee=ผู้รับ: {0} shared.iConfirm=ฉันยืนยัน diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index 275b9567..f3863027 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -200,7 +200,7 @@ shared.total=Toplam shared.totalsNeeded=Gereken fonlar shared.tradeWalletAddress=İşlem cüzdan adresi shared.tradeWalletBalance=İşlem cüzdan bakiyesi -shared.reserveExactAmount=Sadece gerekli fonları ayırın. Teklifinizin aktif olması için madencilik ücreti ve 10 onay (yaklaşık 20 dakika) gerekebilir. +shared.reserveExactAmount=Yalnızca gerekli fonları ayırın. Teklifinizin aktif hale gelmesi için bir madencilik ücreti ve yaklaşık 20 dakika gereklidir. shared.makerTxFee=Yapıcı: {0} shared.takerTxFee=Alıcı: {0} shared.iConfirm=Onaylıyorum diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 8e541353..40b2b679 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -188,7 +188,7 @@ shared.total=Tổng shared.totalsNeeded=Số tiền cần shared.tradeWalletAddress=Địa chỉ ví giao dịch shared.tradeWalletBalance=Số dư ví giao dịch -shared.reserveExactAmount=Dự trữ chỉ số tiền cần thiết. Có thể yêu cầu một phí đào và 10 xác nhận (~20 phút) trước khi giao dịch của bạn trở nên hiệu lực. +shared.reserveExactAmount=Chỉ giữ lại số tiền cần thiết. Yêu cầu phí khai thác và khoảng 20 phút trước khi đề nghị của bạn được công khai. shared.makerTxFee=Người tạo: {0} shared.takerTxFee=Người nhận: {0} shared.iConfirm=Tôi xác nhận diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 0f42566e..b495893a 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -188,7 +188,7 @@ shared.total=合计 shared.totalsNeeded=需要资金 shared.tradeWalletAddress=交易钱包地址 shared.tradeWalletBalance=交易钱包余额 -shared.reserveExactAmount=仅保留所需的资金。在您的交易生效前可能需要支付挖矿费和10次确认(约20分钟)。 +shared.reserveExactAmount=仅保留必要的资金。需要支付矿工费用,并且大约需要 20 分钟后您的报价才会生效。 shared.makerTxFee=卖家:{0} shared.takerTxFee=买家:{0} shared.iConfirm=我确认 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index ccdbbcdf..9132242e 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -188,7 +188,7 @@ shared.total=合計 shared.totalsNeeded=需要資金 shared.tradeWalletAddress=交易錢包地址 shared.tradeWalletBalance=交易錢包餘額 -shared.reserveExactAmount=僅保留所需的資金。在您的交易生效前可能需要支付礦工費和 10 次確認(約20分鐘)。 +shared.reserveExactAmount=僅保留必要的資金。需支付礦工費,約 20 分鐘後您的報價才會上線。 shared.makerTxFee=賣家:{0} shared.takerTxFee=買家:{0} shared.iConfirm=我確認 From c75e3aa4559cd1d233b18dc55ee1415033ccdb6c Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 16 Dec 2024 11:30:08 -0500 Subject: [PATCH 28/34] replace checkbox to reserve necessary funds with slider --- .../desktop/main/offer/MutableOfferView.java | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java index 9115c644..7339e5b0 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java @@ -71,7 +71,6 @@ import javafx.geometry.Pos; import javafx.geometry.VPos; import javafx.scene.Node; import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; @@ -137,7 +136,7 @@ public abstract class MutableOfferView> exten private TextField currencyTextField; private AddressTextField addressTextField; private BalanceTextField balanceTextField; - private CheckBox reserveExactAmountCheckbox; + private ToggleButton reserveExactAmountSlider; private ToggleButton buyerAsTakerWithoutDepositSlider; private FundsTextField totalToPayTextField; private Label amountDescriptionLabel, priceCurrencyLabel, priceDescriptionLabel, volumeDescriptionLabel, @@ -176,6 +175,8 @@ public abstract class MutableOfferView> exten @Setter private OfferView.OfferActionHandler offerActionHandler; + private int heightAdjustment = -5; + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, lifecycle @@ -436,7 +437,7 @@ public abstract class MutableOfferView> exten qrCodeImageView.setVisible(true); balanceTextField.setVisible(true); cancelButton2.setVisible(true); - reserveExactAmountCheckbox.setVisible(true); + reserveExactAmountSlider.setVisible(true); } private void updateOfferElementsStyle() { @@ -958,7 +959,7 @@ public abstract class MutableOfferView> exten } private void addPaymentGroup() { - paymentTitledGroupBg = addTitledGroupBg(gridPane, gridRow, 1, Res.get("offerbook.createOffer")); + paymentTitledGroupBg = addTitledGroupBg(gridPane, gridRow, 1, Res.get("offerbook.createOffer"), heightAdjustment); GridPane.setColumnSpan(paymentTitledGroupBg, 2); HBox paymentGroupBox = new HBox(); @@ -976,7 +977,7 @@ public abstract class MutableOfferView> exten GridPane.setRowIndex(paymentGroupBox, gridRow); GridPane.setColumnSpan(paymentGroupBox, 2); - GridPane.setMargin(paymentGroupBox, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 0)); + GridPane.setMargin(paymentGroupBox, new Insets(Layout.FIRST_ROW_DISTANCE + heightAdjustment, 0, 0, 0)); gridPane.getChildren().add(paymentGroupBox); tradingAccountBoxTuple.first.setMinWidth(800); @@ -1012,7 +1013,7 @@ public abstract class MutableOfferView> exten private void addAmountPriceGroup() { amountTitledGroupBg = addTitledGroupBg(gridPane, ++gridRow, 2, - Res.get("createOffer.setAmountPrice"), 25); + Res.get("createOffer.setAmountPrice"), 25 + heightAdjustment); GridPane.setColumnSpan(amountTitledGroupBg, 2); addAmountPriceFields(); @@ -1021,7 +1022,7 @@ public abstract class MutableOfferView> exten private void addOptionsGroup() { setDepositTitledGroupBg = addTitledGroupBg(gridPane, ++gridRow, 2, - Res.get("shared.advancedOptions"), Layout.COMPACT_GROUP_DISTANCE); + Res.get("shared.advancedOptions"), 25 + heightAdjustment); securityDepositAndFeeBox = new HBox(); securityDepositAndFeeBox.setSpacing(40); @@ -1136,7 +1137,7 @@ public abstract class MutableOfferView> exten private void addFundingGroup() { // don't increase gridRow as we removed button when this gets visible payFundsTitledGroupBg = addTitledGroupBg(gridPane, gridRow, 3, - Res.get("createOffer.fundsBox.title"), 25); + Res.get("createOffer.fundsBox.title"), 20 + heightAdjustment); payFundsTitledGroupBg.getStyleClass().add("last"); GridPane.setColumnSpan(payFundsTitledGroupBg, 2); payFundsTitledGroupBg.setVisible(false); @@ -1144,7 +1145,7 @@ public abstract class MutableOfferView> exten totalToPayTextField = addFundsTextfield(gridPane, gridRow, Res.get("shared.totalsNeeded"), Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE); totalToPayTextField.setVisible(false); - GridPane.setMargin(totalToPayTextField, new Insets(65, 10, 0, 0)); + GridPane.setMargin(totalToPayTextField, new Insets(60 + heightAdjustment, 10, 0, 0)); qrCodeImageView = new ImageView(); qrCodeImageView.setVisible(false); @@ -1170,15 +1171,15 @@ public abstract class MutableOfferView> exten Res.get("shared.tradeWalletBalance")); balanceTextField.setVisible(false); - reserveExactAmountCheckbox = FormBuilder.addLabelCheckBox(gridPane, ++gridRow, - Res.get("shared.reserveExactAmount")); - - GridPane.setHalignment(reserveExactAmountCheckbox, HPos.LEFT); - - reserveExactAmountCheckbox.setVisible(false); - reserveExactAmountCheckbox.setSelected(preferences.getSplitOfferOutput()); - reserveExactAmountCheckbox.setOnAction(event -> { - boolean selected = reserveExactAmountCheckbox.isSelected(); + + reserveExactAmountSlider = FormBuilder.addSlideToggleButton(gridPane, ++gridRow, Res.get("shared.reserveExactAmount"), heightAdjustment); + GridPane.setHalignment(reserveExactAmountSlider, HPos.LEFT); + GridPane.setMargin(reserveExactAmountSlider, new Insets(-5, 0, -5, 0)); + reserveExactAmountSlider.setPadding(new Insets(0, 0, 0, 0)); + reserveExactAmountSlider.setVisible(false); + reserveExactAmountSlider.setSelected(preferences.getSplitOfferOutput()); + reserveExactAmountSlider.setOnAction(event -> { + boolean selected = reserveExactAmountSlider.isSelected(); if (selected != preferences.getSplitOfferOutput()) { preferences.setSplitOfferOutput(selected); model.dataModel.setReserveExactAmount(selected); @@ -1338,7 +1339,7 @@ public abstract class MutableOfferView> exten firstRowHBox.getChildren().addAll(amountBox, xLabel, percentagePriceBox, resultLabel, volumeBox); GridPane.setColumnSpan(firstRowHBox, 2); GridPane.setRowIndex(firstRowHBox, gridRow); - GridPane.setMargin(firstRowHBox, new Insets(40, 10, 0, 0)); + GridPane.setMargin(firstRowHBox, new Insets(40 + heightAdjustment, 10, 0, 0)); gridPane.getChildren().add(firstRowHBox); } From 544d69827a2929a109acdcde5fb4b34a0d5f54b5 Mon Sep 17 00:00:00 2001 From: woodser Date: Tue, 17 Dec 2024 09:12:35 -0500 Subject: [PATCH 29/34] show locked symbol for private offers in trade history --- .../main/portfolio/closedtrades/ClosedTradesListItem.java | 2 +- .../main/portfolio/failedtrades/FailedTradesViewModel.java | 2 +- desktop/src/main/java/haveno/desktop/util/DisplayUtils.java | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/closedtrades/ClosedTradesListItem.java b/desktop/src/main/java/haveno/desktop/main/portfolio/closedtrades/ClosedTradesListItem.java index 6c1e30b8..7b6ca92a 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/closedtrades/ClosedTradesListItem.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/closedtrades/ClosedTradesListItem.java @@ -105,7 +105,7 @@ public class ClosedTradesListItem implements FilterableListItem { ? offer.getDirection() : offer.getMirroredDirection(); String currencyCode = tradable.getOffer().getCurrencyCode(); - return DisplayUtils.getDirectionWithCode(direction, currencyCode); + return DisplayUtils.getDirectionWithCode(direction, currencyCode, offer.isPrivateOffer()); } public Date getDate() { diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesViewModel.java b/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesViewModel.java index eef2ffa4..b6dd35d3 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesViewModel.java @@ -68,7 +68,7 @@ class FailedTradesViewModel extends ActivatableWithDataModel Date: Tue, 17 Dec 2024 10:36:14 -0500 Subject: [PATCH 30/34] fix scheduling offers by computing spendable amount from txs --- .../haveno/core/offer/CreateOfferService.java | 1 - .../haveno/core/offer/OpenOfferManager.java | 66 +++++++++++-------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/CreateOfferService.java b/core/src/main/java/haveno/core/offer/CreateOfferService.java index 407953b0..e135bfa1 100644 --- a/core/src/main/java/haveno/core/offer/CreateOfferService.java +++ b/core/src/main/java/haveno/core/offer/CreateOfferService.java @@ -127,7 +127,6 @@ public class CreateOfferService { isPrivateOffer, buyerAsTakerWithoutDeposit); - // verify buyer as taker security deposit boolean isBuyerMaker = offerUtil.isBuyOffer(direction); if (!isBuyerMaker && !isPrivateOffer && buyerAsTakerWithoutDeposit) { diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index 86f45566..25aebaec 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -948,7 +948,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe if (openOffer.getScheduledTxHashes() != null) { boolean scheduledTxsAvailable = true; for (MoneroTxWallet tx : xmrWalletService.getTxs(openOffer.getScheduledTxHashes())) { - if (!tx.isLocked() && !isOutputsAvailable(tx)) { + if (!tx.isLocked() && !hasSpendableAmount(tx)) { scheduledTxsAvailable = false; break; } @@ -1165,31 +1165,21 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe throw new RuntimeException("Not enough money in Haveno wallet"); } - // get earliest available or pending txs with sufficient incoming amount + // get earliest available or pending txs with sufficient spendable amount BigInteger scheduledAmount = BigInteger.ZERO; Set scheduledTxs = new HashSet(); for (MoneroTxWallet tx : xmrWalletService.getTxs()) { - // skip if no funds available - BigInteger sentToSelfAmount = xmrWalletService.getAmountSentToSelf(tx); // amount sent to self always shows 0, so compute from destinations manually - if (sentToSelfAmount.equals(BigInteger.ZERO) && (tx.getIncomingTransfers() == null || tx.getIncomingTransfers().isEmpty())) continue; - if (!isOutputsAvailable(tx)) continue; + // get spendable amount + BigInteger spendableAmount = getSpendableAmount(tx); + + // skip if no spendable amount or already scheduled + if (spendableAmount.equals(BigInteger.ZERO)) continue; if (isTxScheduledByOtherOffer(openOffers, openOffer, tx.getHash())) continue; - // schedule transaction if funds sent to self, because they are not included in incoming transfers // TODO: fix in libraries? - if (sentToSelfAmount.compareTo(BigInteger.ZERO) > 0) { - scheduledAmount = scheduledAmount.add(sentToSelfAmount); - scheduledTxs.add(tx); - } else if (tx.getIncomingTransfers() != null) { - - // schedule transaction if incoming tranfers to account 0 - for (MoneroIncomingTransfer transfer : tx.getIncomingTransfers()) { - if (transfer.getAccountIndex() == 0) { - scheduledAmount = scheduledAmount.add(transfer.getAmount()); - scheduledTxs.add(tx); - } - } - } + // schedule tx + scheduledAmount = scheduledAmount.add(spendableAmount); + scheduledTxs.add(tx); // break if sufficient funds if (scheduledAmount.compareTo(offerReserveAmount) >= 0) break; @@ -1202,6 +1192,34 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe openOffer.setState(OpenOffer.State.PENDING); } + private BigInteger getSpendableAmount(MoneroTxWallet tx) { + + // compute spendable amount from outputs if confirmed + if (tx.isConfirmed()) { + BigInteger spendableAmount = BigInteger.ZERO; + if (tx.getOutputsWallet() != null) { + for (MoneroOutputWallet output : tx.getOutputsWallet()) { + if (!output.isSpent() && !output.isFrozen() && output.getAccountIndex() == 0) { + spendableAmount = spendableAmount.add(output.getAmount()); + } + } + } + return spendableAmount; + } + + // funds sent to self always show 0 incoming amount, so compute from destinations manually + // TODO: this excludes change output, so change is missing from spendable amount until confirmed + BigInteger sentToSelfAmount = xmrWalletService.getAmountSentToSelf(tx); + if (sentToSelfAmount.compareTo(BigInteger.ZERO) > 0) return sentToSelfAmount; + + // if not confirmed and not sent to self, return incoming amount + return tx.getIncomingAmount() == null ? BigInteger.ZERO : tx.getIncomingAmount(); + } + + private boolean hasSpendableAmount(MoneroTxWallet tx) { + return getSpendableAmount(tx).compareTo(BigInteger.ZERO) > 0; + } + private BigInteger getScheduledAmount(List openOffers) { BigInteger scheduledAmount = BigInteger.ZERO; for (OpenOffer openOffer : openOffers) { @@ -1233,14 +1251,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe return false; } - private boolean isOutputsAvailable(MoneroTxWallet tx) { - if (tx.getOutputsWallet() == null) return false; - for (MoneroOutputWallet output : tx.getOutputsWallet()) { - if (output.isSpent() || output.isFrozen()) return false; - } - return true; - } - private void signAndPostOffer(OpenOffer openOffer, boolean useSavingsWallet, // TODO: remove this? TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { From af3c7059a9100619d4453b04ead6755fb0b1bdfe Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 18 Dec 2024 10:43:12 -0500 Subject: [PATCH 31/34] play chime when buyer can send payment --- core/src/main/java/haveno/core/trade/Trade.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index 95b067b2..e88f70bd 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -657,6 +657,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { ThreadUtils.submitToPool(() -> { if (newValue == Trade.Phase.DEPOSIT_REQUESTED) startPolling(); if (newValue == Trade.Phase.DEPOSITS_PUBLISHED) onDepositsPublished(); + if (newValue == Trade.Phase.DEPOSITS_CONFIRMED) onDepositsConfirmed(); + if (newValue == Trade.Phase.DEPOSITS_UNLOCKED) onDepositsUnlocked(); if (newValue == Trade.Phase.PAYMENT_SENT) onPaymentSent(); if (isDepositsPublished() && !isPayoutUnlocked()) updatePollPeriod(); if (isPaymentReceived()) { @@ -2892,10 +2894,16 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { ThreadUtils.submitToPool(() -> xmrWalletService.freezeOutputs(getSelf().getReserveTxKeyImages())); } + private void onDepositsConfirmed() { + HavenoUtils.notificationService.sendTradeNotification(this, Phase.DEPOSITS_CONFIRMED, "Trade Deposits Confirmed", "The deposit transactions have confirmed"); + } + + private void onDepositsUnlocked() { + HavenoUtils.notificationService.sendTradeNotification(this, Phase.DEPOSITS_UNLOCKED, "Trade Deposits Unlocked", "The deposit transactions have unlocked"); + } + private void onPaymentSent() { - if (this instanceof SellerTrade) { - HavenoUtils.notificationService.sendTradeNotification(this, Phase.PAYMENT_SENT, "Payment Sent", "The buyer has sent the payment"); // TODO (woodser): use language translation - } + HavenoUtils.notificationService.sendTradeNotification(this, Phase.PAYMENT_SENT, "Payment Sent", "The buyer has sent the payment"); } /////////////////////////////////////////////////////////////////////////////////////////// From 5c79380e638a304b42ceb4a15f34f9eebd9ec43b Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 18 Dec 2024 12:14:16 -0500 Subject: [PATCH 32/34] remove padding from no deposit slider --- .../main/java/haveno/desktop/main/offer/MutableOfferView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java index 7339e5b0..0bcfa790 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java @@ -1039,6 +1039,7 @@ public abstract class MutableOfferView> exten securityDepositAndFeeBox.getChildren().addAll(getSecurityDepositBox(), tradeFeeFieldsBox); buyerAsTakerWithoutDepositSlider = FormBuilder.addSlideToggleButton(gridPane, ++gridRow, Res.get("createOffer.buyerAsTakerWithoutDeposit")); + buyerAsTakerWithoutDepositSlider.setPadding(new Insets(0, 0, 0, 0)); buyerAsTakerWithoutDepositSlider.setOnAction(event -> { // popup info box From 323d14feb0387e881690c5cda6401447430d2e74 Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 18 Dec 2024 11:11:58 -0500 Subject: [PATCH 33/34] bump version to 1.0.15 --- build.gradle | 2 +- common/src/main/java/haveno/common/app/Version.java | 2 +- desktop/package/linux/exchange.haveno.Haveno.metainfo.xml | 2 +- desktop/package/macosx/Info.plist | 4 ++-- seednode/src/main/java/haveno/seednode/SeedNodeMain.java | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index d35c6bd0..5024c62c 100644 --- a/build.gradle +++ b/build.gradle @@ -610,7 +610,7 @@ configure(project(':desktop')) { apply plugin: 'com.github.johnrengelman.shadow' apply from: 'package/package.gradle' - version = '1.0.14-SNAPSHOT' + version = '1.0.15-SNAPSHOT' jar.manifest.attributes( "Implementation-Title": project.name, diff --git a/common/src/main/java/haveno/common/app/Version.java b/common/src/main/java/haveno/common/app/Version.java index 2b4b01b0..a263b657 100644 --- a/common/src/main/java/haveno/common/app/Version.java +++ b/common/src/main/java/haveno/common/app/Version.java @@ -28,7 +28,7 @@ import static com.google.common.base.Preconditions.checkArgument; public class Version { // The application versions // We use semantic versioning with major, minor and patch - public static final String VERSION = "1.0.14"; + public static final String VERSION = "1.0.15"; /** * Holds a list of the tagged resource files for optimizing the getData requests. diff --git a/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml b/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml index 68051345..6ff3b23d 100644 --- a/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml +++ b/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml @@ -60,6 +60,6 @@ - + diff --git a/desktop/package/macosx/Info.plist b/desktop/package/macosx/Info.plist index 31325683..62cc8f88 100644 --- a/desktop/package/macosx/Info.plist +++ b/desktop/package/macosx/Info.plist @@ -5,10 +5,10 @@ CFBundleVersion - 1.0.14 + 1.0.15 CFBundleShortVersionString - 1.0.14 + 1.0.15 CFBundleExecutable Haveno diff --git a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java index e0f5ad7c..03f0cc51 100644 --- a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java +++ b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java @@ -41,7 +41,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class SeedNodeMain extends ExecutableForAppWithP2p { private static final long CHECK_CONNECTION_LOSS_SEC = 30; - private static final String VERSION = "1.0.14"; + private static final String VERSION = "1.0.15"; private SeedNode seedNode; private Timer checkConnectionLossTime; From 7e4e9507105c687f63b006d644a7dac72e02b778 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 19 Dec 2024 06:57:49 -0500 Subject: [PATCH 34/34] update flatpak release date --- desktop/package/linux/exchange.haveno.Haveno.metainfo.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml b/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml index 6ff3b23d..64730a45 100644 --- a/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml +++ b/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml @@ -60,6 +60,6 @@ - +