mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-11-16 15:58:08 +00:00
support USDT-ERC20 and USDT-TRC20
This commit is contained in:
parent
6730d023d6
commit
3b0e985cb8
11 changed files with 176 additions and 7 deletions
29
assets/src/main/java/haveno/asset/Trc20Token.java
Normal file
29
assets/src/main/java/haveno/asset/Trc20Token.java
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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}"));
|
||||||
|
}
|
||||||
|
}
|
11
assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java
Normal file
11
assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java
Normal file
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
11
assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java
Normal file
11
assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java
Normal file
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,3 +7,5 @@ haveno.asset.coins.BitcoinCash
|
||||||
haveno.asset.coins.Ether
|
haveno.asset.coins.Ether
|
||||||
haveno.asset.coins.Litecoin
|
haveno.asset.coins.Litecoin
|
||||||
haveno.asset.coins.Monero
|
haveno.asset.coins.Monero
|
||||||
|
haveno.asset.tokens.TetherUSDERC20
|
||||||
|
haveno.asset.tokens.TetherUSDTRC20
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -200,6 +200,8 @@ public class CurrencyUtil {
|
||||||
result.add(new CryptoCurrency("BCH", "Bitcoin Cash"));
|
result.add(new CryptoCurrency("BCH", "Bitcoin Cash"));
|
||||||
result.add(new CryptoCurrency("ETH", "Ether"));
|
result.add(new CryptoCurrency("ETH", "Ether"));
|
||||||
result.add(new CryptoCurrency("LTC", "Litecoin"));
|
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);
|
result.sort(TradeCurrency::compareTo);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -295,6 +297,9 @@ public class CurrencyUtil {
|
||||||
if (currencyCode != null && isCryptoCurrencyMap.containsKey(currencyCode.toUpperCase())) {
|
if (currencyCode != null && isCryptoCurrencyMap.containsKey(currencyCode.toUpperCase())) {
|
||||||
return isCryptoCurrencyMap.get(currencyCode.toUpperCase());
|
return isCryptoCurrencyMap.get(currencyCode.toUpperCase());
|
||||||
}
|
}
|
||||||
|
if (isCryptoCurrencyBase(currencyCode)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
boolean isCryptoCurrency;
|
boolean isCryptoCurrency;
|
||||||
if (currencyCode == null) {
|
if (currencyCode == null) {
|
||||||
|
@ -321,6 +326,19 @@ public class CurrencyUtil {
|
||||||
return isCryptoCurrency;
|
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<CryptoCurrency> getCryptoCurrency(String currencyCode) {
|
public static Optional<CryptoCurrency> getCryptoCurrency(String currencyCode) {
|
||||||
return Optional.ofNullable(cryptoCurrencyMapSupplier.get().get(currencyCode));
|
return Optional.ofNullable(cryptoCurrencyMapSupplier.get().get(currencyCode));
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,15 +292,16 @@ public class PriceFeedService {
|
||||||
@Nullable
|
@Nullable
|
||||||
public MarketPrice getMarketPrice(String currencyCode) {
|
public MarketPrice getMarketPrice(String currencyCode) {
|
||||||
synchronized (cache) {
|
synchronized (cache) {
|
||||||
return cache.getOrDefault(currencyCode, null);
|
return cache.getOrDefault(CurrencyUtil.getCurrencyCodeBase(currencyCode), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setHavenoMarketPrice(String currencyCode, Price price) {
|
private void setHavenoMarketPrice(String currencyCode, Price price) {
|
||||||
UserThread.execute(() -> {
|
UserThread.execute(() -> {
|
||||||
|
String currencyCodeBase = CurrencyUtil.getCurrencyCodeBase(currencyCode);
|
||||||
synchronized (cache) {
|
synchronized (cache) {
|
||||||
if (!cache.containsKey(currencyCode) || !cache.get(currencyCode).isExternallyProvidedPrice()) {
|
if (!cache.containsKey(currencyCodeBase) || !cache.get(currencyCodeBase).isExternallyProvidedPrice()) {
|
||||||
cache.put(currencyCode, new MarketPrice(currencyCode,
|
cache.put(currencyCodeBase, new MarketPrice(currencyCodeBase,
|
||||||
MathUtils.scaleDownByPowerOf10(price.getValue(), CurrencyUtil.isCryptoCurrency(currencyCode) ? CryptoMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT),
|
MathUtils.scaleDownByPowerOf10(price.getValue(), CurrencyUtil.isCryptoCurrency(currencyCode) ? CryptoMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT),
|
||||||
0,
|
0,
|
||||||
false));
|
false));
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.google.gson.Gson;
|
||||||
import com.google.gson.internal.LinkedTreeMap;
|
import com.google.gson.internal.LinkedTreeMap;
|
||||||
import haveno.common.app.Version;
|
import haveno.common.app.Version;
|
||||||
import haveno.common.util.MathUtils;
|
import haveno.common.util.MathUtils;
|
||||||
|
import haveno.core.locale.CurrencyUtil;
|
||||||
import haveno.core.provider.HttpClientProvider;
|
import haveno.core.provider.HttpClientProvider;
|
||||||
import haveno.network.http.HttpClient;
|
import haveno.network.http.HttpClient;
|
||||||
import haveno.network.p2p.P2PService;
|
import haveno.network.p2p.P2PService;
|
||||||
|
@ -63,6 +64,7 @@ public class PriceProvider extends HttpClientProvider {
|
||||||
String baseCurrencyCode = (String) treeMap.get("baseCurrencyCode");
|
String baseCurrencyCode = (String) treeMap.get("baseCurrencyCode");
|
||||||
String counterCurrencyCode = (String) treeMap.get("counterCurrencyCode");
|
String counterCurrencyCode = (String) treeMap.get("counterCurrencyCode");
|
||||||
String currencyCode = baseCurrencyCode.equals("XMR") ? counterCurrencyCode : baseCurrencyCode;
|
String currencyCode = baseCurrencyCode.equals("XMR") ? counterCurrencyCode : baseCurrencyCode;
|
||||||
|
currencyCode = CurrencyUtil.getCurrencyCodeBase(currencyCode);
|
||||||
double price = (Double) treeMap.get("price");
|
double price = (Double) treeMap.get("price");
|
||||||
// json uses double for our timestampSec long value...
|
// json uses double for our timestampSec long value...
|
||||||
long timestampSec = MathUtils.doubleToLong((Double) treeMap.get("timestampSec"));
|
long timestampSec = MathUtils.doubleToLong((Double) treeMap.get("timestampSec"));
|
||||||
|
|
|
@ -268,7 +268,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
dataModel.getTradeCurrencyCode()));
|
dataModel.getTradeCurrencyCode()));
|
||||||
}
|
}
|
||||||
volumePromptLabel.bind(createStringBinding(
|
volumePromptLabel.bind(createStringBinding(
|
||||||
() -> Res.get("createOffer.volume.prompt", dataModel.getTradeCurrencyCode().get()),
|
() -> Res.get("createOffer.volume.prompt", CurrencyUtil.getCurrencyCodeBase(dataModel.getTradeCurrencyCode().get())),
|
||||||
dataModel.getTradeCurrencyCode()));
|
dataModel.getTradeCurrencyCode()));
|
||||||
|
|
||||||
totalToPay.bind(createStringBinding(() -> HavenoUtils.formatXmr(dataModel.totalToPayAsProperty().get(), true),
|
totalToPay.bind(createStringBinding(() -> HavenoUtils.formatXmr(dataModel.totalToPayAsProperty().get(), true),
|
||||||
|
|
|
@ -110,9 +110,19 @@ public class MarketPricePresentation {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillPriceFeedComboBoxItems() {
|
private void fillPriceFeedComboBoxItems() {
|
||||||
List<PriceFeedComboBoxItem> currencyItems = preferences.getTradeCurrenciesAsObservable()
|
|
||||||
|
// collect unique currency code bases
|
||||||
|
List<String> uniqueCurrencyCodeBases = preferences.getTradeCurrenciesAsObservable()
|
||||||
.stream()
|
.stream()
|
||||||
.map(tradeCurrency -> new PriceFeedComboBoxItem(tradeCurrency.getCode()))
|
.map(TradeCurrency::getCode)
|
||||||
|
.map(CurrencyUtil::getCurrencyCodeBase)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// create price feed items
|
||||||
|
List<PriceFeedComboBoxItem> currencyItems = uniqueCurrencyCodeBases
|
||||||
|
.stream()
|
||||||
|
.map(currencyCodeBase -> new PriceFeedComboBoxItem(currencyCodeBase))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
priceFeedComboBoxItems.setAll(currencyItems);
|
priceFeedComboBoxItems.setAll(currencyItems);
|
||||||
}
|
}
|
||||||
|
@ -171,7 +181,7 @@ public class MarketPricePresentation {
|
||||||
|
|
||||||
private Optional<PriceFeedComboBoxItem> findPriceFeedComboBoxItem(String currencyCode) {
|
private Optional<PriceFeedComboBoxItem> findPriceFeedComboBoxItem(String currencyCode) {
|
||||||
return priceFeedComboBoxItems.stream()
|
return priceFeedComboBoxItems.stream()
|
||||||
.filter(item -> item.currencyCode.equals(currencyCode))
|
.filter(item -> CurrencyUtil.getCurrencyCodeBase(item.currencyCode).equals(CurrencyUtil.getCurrencyCodeBase(currencyCode)))
|
||||||
.findAny();
|
.findAny();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue