diff --git a/assets/src/main/java/haveno/asset/Trc20Token.java b/assets/src/main/java/haveno/asset/Trc20Token.java
new file mode 100644
index 00000000..3cffa344
--- /dev/null
+++ b/assets/src/main/java/haveno/asset/Trc20Token.java
@@ -0,0 +1,29 @@
+/*
+ * 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.asset;
+
+/**
+ * Abstract base class for Tron-based {@link Token}s that implement the
+ * TRC-20 Token Standard.
+ */
+public abstract class Trc20Token extends Token {
+
+ public Trc20Token(String name, String tickerSymbol) {
+ super(name, tickerSymbol, new RegexAddressValidator("T[A-Za-z1-9]{33}"));
+ }
+}
diff --git a/assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java b/assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java
new file mode 100644
index 00000000..1afb7ff1
--- /dev/null
+++ b/assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java
@@ -0,0 +1,11 @@
+package haveno.asset.tokens;
+
+import haveno.asset.Erc20Token;
+
+public class TetherUSDERC20 extends Erc20Token {
+ public TetherUSDERC20() {
+ // If you add a new USDT variant or want to change this ticker symbol you should also look here:
+ // core/src/main/java/haveno/core/provider/price/PriceProvider.java:getAll()
+ super("Tether USD (ERC20)", "USDT-ERC20");
+ }
+}
diff --git a/assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java b/assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java
new file mode 100644
index 00000000..c5669d12
--- /dev/null
+++ b/assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java
@@ -0,0 +1,11 @@
+package haveno.asset.tokens;
+
+import haveno.asset.Trc20Token;
+
+public class TetherUSDTRC20 extends Trc20Token {
+ public TetherUSDTRC20() {
+ // If you add a new USDT variant or want to change this ticker symbol you should also look here:
+ // core/src/main/java/haveno/core/provider/price/PriceProvider.java:getAll()
+ super("Tether USD (TRC20)", "USDT-TRC20");
+ }
+}
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 7108c288..350f6f15 100644
--- a/assets/src/main/resources/META-INF/services/haveno.asset.Asset
+++ b/assets/src/main/resources/META-INF/services/haveno.asset.Asset
@@ -7,3 +7,5 @@ haveno.asset.coins.BitcoinCash
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
diff --git a/assets/src/test/java/haveno/asset/coins/TetherUSDERC20Test.java b/assets/src/test/java/haveno/asset/coins/TetherUSDERC20Test.java
new file mode 100644
index 00000000..fd7d97ce
--- /dev/null
+++ b/assets/src/test/java/haveno/asset/coins/TetherUSDERC20Test.java
@@ -0,0 +1,43 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq 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.
+ *
+ * Bisq 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 Bisq. If not, see .
+ */
+
+package haveno.asset.coins;
+
+import haveno.asset.AbstractAssetTest;
+import haveno.asset.tokens.TetherUSDERC20;
+
+import org.junit.jupiter.api.Test;
+
+ public class TetherUSDERC20Test extends AbstractAssetTest {
+
+ public TetherUSDERC20Test() {
+ super(new TetherUSDERC20());
+ }
+
+ @Test
+ public void testValidAddresses() {
+ assertValidAddress("0x2a65Aca4D5fC5B5C859090a6c34d164135398226");
+ assertValidAddress("2a65Aca4D5fC5B5C859090a6c34d164135398226");
+ }
+
+ @Test
+ public void testInvalidAddresses() {
+ assertInvalidAddress("0x2a65Aca4D5fC5B5C859090a6c34d1641353982266");
+ assertInvalidAddress("0x2a65Aca4D5fC5B5C859090a6c34d16413539822g");
+ assertInvalidAddress("2a65Aca4D5fC5B5C859090a6c34d16413539822g");
+ }
+ }
\ No newline at end of file
diff --git a/assets/src/test/java/haveno/asset/coins/TetherUSDTRC20Test.java b/assets/src/test/java/haveno/asset/coins/TetherUSDTRC20Test.java
new file mode 100644
index 00000000..7fef554c
--- /dev/null
+++ b/assets/src/test/java/haveno/asset/coins/TetherUSDTRC20Test.java
@@ -0,0 +1,42 @@
+/*
+ * 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.asset.coins;
+
+import haveno.asset.AbstractAssetTest;
+import haveno.asset.tokens.TetherUSDTRC20;
+
+import org.junit.jupiter.api.Test;
+
+ public class TetherUSDTRC20Test extends AbstractAssetTest {
+
+ public TetherUSDTRC20Test() {
+ super(new TetherUSDTRC20());
+ }
+
+ @Test
+ public void testValidAddresses() {
+ assertValidAddress("TVnmu3E6DYVL4bpAoZnPNEPVUrgC7eSWaX");
+ }
+
+ @Test
+ public void testInvalidAddresses() {
+ assertInvalidAddress("0x2a65Aca4D5fC5B5C859090a6c34d1641353982266");
+ assertInvalidAddress("0x2a65Aca4D5fC5B5C859090a6c34d16413539822g");
+ assertInvalidAddress("2a65Aca4D5fC5B5C859090a6c34d16413539822g");
+ }
+ }
\ 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 45371225..5775d61d 100644
--- a/core/src/main/java/haveno/core/locale/CurrencyUtil.java
+++ b/core/src/main/java/haveno/core/locale/CurrencyUtil.java
@@ -200,6 +200,8 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("BCH", "Bitcoin Cash"));
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("USDT-TRC20", "Tether USD (TRC20)"));
result.sort(TradeCurrency::compareTo);
return result;
}
@@ -295,6 +297,9 @@ public class CurrencyUtil {
if (currencyCode != null && isCryptoCurrencyMap.containsKey(currencyCode.toUpperCase())) {
return isCryptoCurrencyMap.get(currencyCode.toUpperCase());
}
+ if (isCryptoCurrencyBase(currencyCode)) {
+ return true;
+ }
boolean isCryptoCurrency;
if (currencyCode == null) {
@@ -321,6 +326,19 @@ public class CurrencyUtil {
return isCryptoCurrency;
}
+ private static boolean isCryptoCurrencyBase(String currencyCode) {
+ if (currencyCode == null) return false;
+ currencyCode = currencyCode.toUpperCase();
+ return currencyCode.equals("USDT");
+ }
+
+ public static String getCurrencyCodeBase(String currencyCode) {
+ if (currencyCode == null) return null;
+ currencyCode = currencyCode.toUpperCase();
+ if (currencyCode.contains("USDT")) return "USDT";
+ return currencyCode;
+ }
+
public static Optional getCryptoCurrency(String currencyCode) {
return Optional.ofNullable(cryptoCurrencyMapSupplier.get().get(currencyCode));
}
diff --git a/core/src/main/java/haveno/core/provider/price/PriceFeedService.java b/core/src/main/java/haveno/core/provider/price/PriceFeedService.java
index 6f3566d1..c5827779 100644
--- a/core/src/main/java/haveno/core/provider/price/PriceFeedService.java
+++ b/core/src/main/java/haveno/core/provider/price/PriceFeedService.java
@@ -292,15 +292,16 @@ public class PriceFeedService {
@Nullable
public MarketPrice getMarketPrice(String currencyCode) {
synchronized (cache) {
- return cache.getOrDefault(currencyCode, null);
+ return cache.getOrDefault(CurrencyUtil.getCurrencyCodeBase(currencyCode), null);
}
}
private void setHavenoMarketPrice(String currencyCode, Price price) {
UserThread.execute(() -> {
+ String currencyCodeBase = CurrencyUtil.getCurrencyCodeBase(currencyCode);
synchronized (cache) {
- if (!cache.containsKey(currencyCode) || !cache.get(currencyCode).isExternallyProvidedPrice()) {
- cache.put(currencyCode, new MarketPrice(currencyCode,
+ if (!cache.containsKey(currencyCodeBase) || !cache.get(currencyCodeBase).isExternallyProvidedPrice()) {
+ cache.put(currencyCodeBase, new MarketPrice(currencyCodeBase,
MathUtils.scaleDownByPowerOf10(price.getValue(), CurrencyUtil.isCryptoCurrency(currencyCode) ? CryptoMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT),
0,
false));
diff --git a/core/src/main/java/haveno/core/provider/price/PriceProvider.java b/core/src/main/java/haveno/core/provider/price/PriceProvider.java
index 17bef33a..93162947 100644
--- a/core/src/main/java/haveno/core/provider/price/PriceProvider.java
+++ b/core/src/main/java/haveno/core/provider/price/PriceProvider.java
@@ -21,6 +21,7 @@ import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import haveno.common.app.Version;
import haveno.common.util.MathUtils;
+import haveno.core.locale.CurrencyUtil;
import haveno.core.provider.HttpClientProvider;
import haveno.network.http.HttpClient;
import haveno.network.p2p.P2PService;
@@ -63,6 +64,7 @@ public class PriceProvider extends HttpClientProvider {
String baseCurrencyCode = (String) treeMap.get("baseCurrencyCode");
String counterCurrencyCode = (String) treeMap.get("counterCurrencyCode");
String currencyCode = baseCurrencyCode.equals("XMR") ? counterCurrencyCode : baseCurrencyCode;
+ currencyCode = CurrencyUtil.getCurrencyCodeBase(currencyCode);
double price = (Double) treeMap.get("price");
// json uses double for our timestampSec long value...
long timestampSec = MathUtils.doubleToLong((Double) treeMap.get("timestampSec"));
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 33233083..66ca6475 100644
--- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java
+++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java
@@ -268,7 +268,7 @@ public abstract class MutableOfferViewModel ext
dataModel.getTradeCurrencyCode()));
}
volumePromptLabel.bind(createStringBinding(
- () -> Res.get("createOffer.volume.prompt", dataModel.getTradeCurrencyCode().get()),
+ () -> Res.get("createOffer.volume.prompt", CurrencyUtil.getCurrencyCodeBase(dataModel.getTradeCurrencyCode().get())),
dataModel.getTradeCurrencyCode()));
totalToPay.bind(createStringBinding(() -> HavenoUtils.formatXmr(dataModel.totalToPayAsProperty().get(), true),
diff --git a/desktop/src/main/java/haveno/desktop/main/presentation/MarketPricePresentation.java b/desktop/src/main/java/haveno/desktop/main/presentation/MarketPricePresentation.java
index ab77b5e0..35293692 100644
--- a/desktop/src/main/java/haveno/desktop/main/presentation/MarketPricePresentation.java
+++ b/desktop/src/main/java/haveno/desktop/main/presentation/MarketPricePresentation.java
@@ -110,9 +110,19 @@ public class MarketPricePresentation {
}
private void fillPriceFeedComboBoxItems() {
- List currencyItems = preferences.getTradeCurrenciesAsObservable()
+
+ // collect unique currency code bases
+ List uniqueCurrencyCodeBases = preferences.getTradeCurrenciesAsObservable()
.stream()
- .map(tradeCurrency -> new PriceFeedComboBoxItem(tradeCurrency.getCode()))
+ .map(TradeCurrency::getCode)
+ .map(CurrencyUtil::getCurrencyCodeBase)
+ .distinct()
+ .collect(Collectors.toList());
+
+ // create price feed items
+ List currencyItems = uniqueCurrencyCodeBases
+ .stream()
+ .map(currencyCodeBase -> new PriceFeedComboBoxItem(currencyCodeBase))
.collect(Collectors.toList());
priceFeedComboBoxItems.setAll(currencyItems);
}
@@ -171,7 +181,7 @@ public class MarketPricePresentation {
private Optional findPriceFeedComboBoxItem(String currencyCode) {
return priceFeedComboBoxItems.stream()
- .filter(item -> item.currencyCode.equals(currencyCode))
+ .filter(item -> CurrencyUtil.getCurrencyCodeBase(item.currencyCode).equals(CurrencyUtil.getCurrencyCodeBase(currencyCode)))
.findAny();
}