mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 07:07:43 +01:00
Make tx fee dynamic by base currency
This commit is contained in:
parent
5a0d493d02
commit
9aba9e607a
8 changed files with 71 additions and 107 deletions
|
@ -1,14 +0,0 @@
|
|||
package io.bisq.core.provider.fee;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class FeeData {
|
||||
private static final Logger log = LoggerFactory.getLogger(FeeData.class);
|
||||
|
||||
public final long txFeePerByte;
|
||||
|
||||
public FeeData(long txFeePerByte) {
|
||||
this.txFeePerByte = txFeePerByte;
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ public class FeeProvider extends HttpClientProvider {
|
|||
super(httpClient, baseUrl, false);
|
||||
}
|
||||
|
||||
public Tuple2<Map<String, Long>, FeeData> getFees() throws IOException {
|
||||
public Tuple2<Map<String, Long>, Map<String, Long>> getFees() throws IOException {
|
||||
String json = httpClient.requestWithGET("getFees", "User-Agent", "bisq/" + Version.VERSION + ", uid:" + httpClient.getUid());
|
||||
//noinspection unchecked
|
||||
LinkedTreeMap<String, Object> linkedTreeMap = new Gson().fromJson(json, LinkedTreeMap.class);
|
||||
|
@ -29,9 +29,15 @@ public class FeeProvider extends HttpClientProvider {
|
|||
tsMap.put("bitcoinFeesTs", ((Double) linkedTreeMap.get("bitcoinFeesTs")).longValue());
|
||||
|
||||
//noinspection unchecked
|
||||
LinkedTreeMap<String, Double> dataMap = (LinkedTreeMap<String, Double>) linkedTreeMap.get("data");
|
||||
FeeData feeData = new FeeData(dataMap.get("txFee").longValue());
|
||||
return new Tuple2<>(tsMap, feeData);
|
||||
LinkedTreeMap<String, Double> dataMap = (LinkedTreeMap<String, Double>) linkedTreeMap.get("dataMap");
|
||||
Long btcTxFee = dataMap.get("btcTxFee").longValue();
|
||||
Long ltcTxFee = dataMap.get("ltcTxFee").longValue();
|
||||
Long dogeTxFee = dataMap.get("dogeTxFee").longValue();
|
||||
Map<String, Long> map = new HashMap<>();
|
||||
map.put("BTC", btcTxFee);
|
||||
map.put("LTC", ltcTxFee);
|
||||
map.put("DOGE", dogeTxFee);
|
||||
return new Tuple2<>(tsMap, map);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,15 +17,15 @@ public class FeeRequest {
|
|||
public FeeRequest() {
|
||||
}
|
||||
|
||||
public SettableFuture<Tuple2<Map<String, Long>, FeeData>> getFees(FeeProvider provider) {
|
||||
final SettableFuture<Tuple2<Map<String, Long>, FeeData>> resultFuture = SettableFuture.create();
|
||||
ListenableFuture<Tuple2<Map<String, Long>, FeeData>> future = executorService.submit(() -> {
|
||||
public SettableFuture<Tuple2<Map<String, Long>, Map<String, Long>>> getFees(FeeProvider provider) {
|
||||
final SettableFuture<Tuple2<Map<String, Long>, Map<String, Long>>> resultFuture = SettableFuture.create();
|
||||
ListenableFuture<Tuple2<Map<String, Long>, Map<String, Long>>> future = executorService.submit(() -> {
|
||||
Thread.currentThread().setName("FeeRequest-" + provider.toString());
|
||||
return provider.getFees();
|
||||
});
|
||||
|
||||
Futures.addCallback(future, new FutureCallback<Tuple2<Map<String, Long>, FeeData>>() {
|
||||
public void onSuccess(Tuple2<Map<String, Long>, FeeData> feeData) {
|
||||
Futures.addCallback(future, new FutureCallback<Tuple2<Map<String, Long>, Map<String, Long>>>() {
|
||||
public void onSuccess(Tuple2<Map<String, Long>, Map<String, Long>> feeData) {
|
||||
log.debug("Received feeData of {}\nfrom provider {}", feeData, provider);
|
||||
resultFuture.set(feeData);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import com.google.inject.Inject;
|
|||
import io.bisq.common.UserThread;
|
||||
import io.bisq.common.handlers.FaultHandler;
|
||||
import io.bisq.common.util.Tuple2;
|
||||
import io.bisq.core.app.BisqEnvironment;
|
||||
import io.bisq.core.provider.ProvidersRepository;
|
||||
import io.bisq.network.http.HttpClient;
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
@ -40,20 +41,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
public class FeeService {
|
||||
private static final Logger log = LoggerFactory.getLogger(FeeService.class);
|
||||
|
||||
// TODO check min fee for LTC
|
||||
// https://litecoin.info/Transaction_fees
|
||||
//0.001 (LTC)/kb -> 0.00100000 sat/kb -> 100 sat/byte
|
||||
public static final long MIN_TX_FEE = 100; // satoshi/byte // 0.01 USD at LTC price 30 USD for a 300 byte tx
|
||||
public static final long MAX_TX_FEE = 400;
|
||||
public static final long DEFAULT_TX_FEE = 100;
|
||||
|
||||
/*
|
||||
BTC
|
||||
public static final long MIN_TX_FEE = 40; // satoshi/byte
|
||||
public static final long MAX_TX_FEE = 1000;
|
||||
public static final long DEFAULT_TX_FEE = 150;
|
||||
*/
|
||||
|
||||
public static final long LTC_DEFAULT_TX_FEE = 100;
|
||||
public static final long BTC_DEFAULT_TX_FEE = 150;
|
||||
public static final long DOGE_DEFAULT_TX_FEE = 100;
|
||||
|
||||
// Dust limit for LTC is 100 000 sat
|
||||
// https://litecoin.info/Transaction_fees
|
||||
|
@ -73,8 +65,8 @@ public class FeeService {
|
|||
public static final long MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN = 10;
|
||||
|
||||
private final FeeProvider feeProvider;
|
||||
@Nullable
|
||||
private FeeData feeData;
|
||||
private final String baseCurrencyCode;
|
||||
private long txFeePerByte;
|
||||
private Map<String, Long> timeStampMap;
|
||||
private long epochInSecondAtLastRequest;
|
||||
private long lastRequest;
|
||||
|
@ -84,8 +76,23 @@ public class FeeService {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public FeeService(HttpClient httpClient, ProvidersRepository providersRepository) {
|
||||
this.feeProvider = new FeeProvider(httpClient, providersRepository.getBaseUrl());
|
||||
public FeeService(HttpClient httpClient, ProvidersRepository providersRepository, BisqEnvironment bisqEnvironment) {
|
||||
baseCurrencyCode = bisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode();
|
||||
feeProvider = new FeeProvider(httpClient, providersRepository.getBaseUrl());
|
||||
|
||||
switch (baseCurrencyCode) {
|
||||
case "BTC":
|
||||
txFeePerByte = BTC_DEFAULT_TX_FEE;
|
||||
break;
|
||||
case "LTC":
|
||||
txFeePerByte = LTC_DEFAULT_TX_FEE;
|
||||
break;
|
||||
case "DOGE":
|
||||
txFeePerByte = DOGE_DEFAULT_TX_FEE;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("baseCurrencyCode not defined. baseCurrencyCode=" + baseCurrencyCode);
|
||||
}
|
||||
}
|
||||
|
||||
public void onAllServicesInitialized() {
|
||||
|
@ -94,19 +101,20 @@ public class FeeService {
|
|||
|
||||
public void requestFees(@Nullable Runnable resultHandler, @Nullable FaultHandler faultHandler) {
|
||||
long now = Instant.now().getEpochSecond();
|
||||
if (feeData == null || now - lastRequest > MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN * 60) {
|
||||
if (now - lastRequest > MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN * 60) {
|
||||
lastRequest = now;
|
||||
FeeRequest feeRequest = new FeeRequest();
|
||||
SettableFuture<Tuple2<Map<String, Long>, FeeData>> future = feeRequest.getFees(feeProvider);
|
||||
Futures.addCallback(future, new FutureCallback<Tuple2<Map<String, Long>, FeeData>>() {
|
||||
SettableFuture<Tuple2<Map<String, Long>, Map<String, Long>>> future = feeRequest.getFees(feeProvider);
|
||||
Futures.addCallback(future, new FutureCallback<Tuple2<Map<String, Long>, Map<String, Long>>>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Tuple2<Map<String, Long>, FeeData> result) {
|
||||
public void onSuccess(@Nullable Tuple2<Map<String, Long>, Map<String, Long>> result) {
|
||||
UserThread.execute(() -> {
|
||||
checkNotNull(result, "Result must not be null at getFees");
|
||||
timeStampMap = result.first;
|
||||
epochInSecondAtLastRequest = timeStampMap.get("bitcoinFeesTs");
|
||||
feeData = result.second;
|
||||
log.info("Tx fee: txFeePerByte=" + feeData.txFeePerByte);
|
||||
final Map<String, Long> map = result.second;
|
||||
txFeePerByte = map.get(baseCurrencyCode);
|
||||
log.info("Tx fee: txFeePerByte={} for currency {}", txFeePerByte, baseCurrencyCode);
|
||||
if (resultHandler != null)
|
||||
resultHandler.run();
|
||||
});
|
||||
|
@ -133,10 +141,7 @@ public class FeeService {
|
|||
}
|
||||
|
||||
public Coin getTxFeePerByte() {
|
||||
if (feeData != null)
|
||||
return Coin.valueOf(feeData.txFeePerByte);
|
||||
else
|
||||
return Coin.valueOf(DEFAULT_TX_FEE);
|
||||
return Coin.valueOf(txFeePerByte);
|
||||
}
|
||||
|
||||
public static Coin getMakerFeePerBtc(boolean currencyForMakerFeeBtc) {
|
||||
|
|
|
@ -19,8 +19,7 @@ package io.bisq.provider.fee;
|
|||
|
||||
import io.bisq.common.util.Utilities;
|
||||
import io.bisq.core.provider.fee.FeeService;
|
||||
import io.bisq.provider.fee.providers.FeesProvider;
|
||||
import io.bisq.provider.fee.providers.LtcFeesProvider;
|
||||
import io.bisq.provider.fee.providers.BtcFeesProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -38,16 +37,23 @@ public class FeeRequestService {
|
|||
|
||||
private static final long INTERVAL_BTC_FEES_MS = 600_000; // 10 min
|
||||
|
||||
public static final long BTC_MIN_TX_FEE = 40; // satoshi/byte
|
||||
public static final long BTC_MAX_TX_FEE = 2000;
|
||||
|
||||
private final Timer timerBitcoinFeesLocal = new Timer();
|
||||
|
||||
private final FeesProvider feesProvider;
|
||||
private final Map<String, Long> allFeesMap = new ConcurrentHashMap<>();
|
||||
private final BtcFeesProvider btcFeesProvider;
|
||||
private final Map<String, Long> dataMap = new ConcurrentHashMap<>();
|
||||
private long bitcoinFeesTs;
|
||||
private String json;
|
||||
|
||||
public FeeRequestService() throws IOException {
|
||||
// feesProvider = new BtcFeesProvider();
|
||||
feesProvider = new LtcFeesProvider();
|
||||
btcFeesProvider = new BtcFeesProvider();
|
||||
|
||||
// For now we don't need a fee estimation for LTC so we set it fixed, but we keep it in the provider to
|
||||
// be flexible if fee pressure grows on LTC
|
||||
dataMap.put("ltcTxFee", FeeService.LTC_DEFAULT_TX_FEE);
|
||||
dataMap.put("dogeTxFee", FeeService.DOGE_DEFAULT_TX_FEE);
|
||||
|
||||
writeToJson();
|
||||
startRequests();
|
||||
|
@ -72,15 +78,15 @@ public class FeeRequestService {
|
|||
|
||||
private void requestBitcoinFees() throws IOException {
|
||||
long ts = System.currentTimeMillis();
|
||||
long result = feesProvider.getFee();
|
||||
long btcFee = btcFeesProvider.getFee();
|
||||
log.info("requestBitcoinFees took {} ms.", (System.currentTimeMillis() - ts));
|
||||
if (result < FeeService.MIN_TX_FEE) {
|
||||
log.warn("Response for fee is lower as min fee. Fee=" + result);
|
||||
} else if (result > FeeService.MAX_TX_FEE) {
|
||||
log.warn("Response for fee is larger as max fee. Fee=" + result);
|
||||
if (btcFee < FeeRequestService.BTC_MIN_TX_FEE) {
|
||||
log.warn("Response for fee is lower as min fee. Fee=" + btcFee);
|
||||
} else if (btcFee > FeeRequestService.BTC_MAX_TX_FEE) {
|
||||
log.warn("Response for fee is larger as max fee. Fee=" + btcFee);
|
||||
} else {
|
||||
bitcoinFeesTs = Instant.now().getEpochSecond();
|
||||
allFeesMap.put("txFee", result);
|
||||
dataMap.put("btcTxFee", btcFee);
|
||||
writeToJson();
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +94,7 @@ public class FeeRequestService {
|
|||
private void writeToJson() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("bitcoinFeesTs", bitcoinFeesTs);
|
||||
map.put("data", allFeesMap);
|
||||
map.put("dataMap", dataMap);
|
||||
json = Utilities.objectToJson(map);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ package io.bisq.provider.fee.providers;
|
|||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.internal.LinkedTreeMap;
|
||||
import io.bisq.core.provider.fee.FeeService;
|
||||
import io.bisq.network.http.HttpClient;
|
||||
import io.bisq.provider.fee.FeeRequestService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -12,7 +12,7 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
//TODO use protobuffer instead of json
|
||||
public class BtcFeesProvider implements FeesProvider {
|
||||
public class BtcFeesProvider {
|
||||
private static final Logger log = LoggerFactory.getLogger(BtcFeesProvider.class);
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
@ -29,13 +29,13 @@ public class BtcFeesProvider implements FeesProvider {
|
|||
LinkedTreeMap<String, Double> treeMap = new Gson().fromJson(response, LinkedTreeMap.class);
|
||||
treeMap.entrySet().stream().forEach(e -> map.put(e.getKey(), e.getValue().longValue()));
|
||||
|
||||
if (map.get("fastestFee") < FeeService.MAX_TX_FEE)
|
||||
if (map.get("fastestFee") < FeeRequestService.BTC_MAX_TX_FEE)
|
||||
return map.get("fastestFee");
|
||||
else if (map.get("halfHourFee") < FeeService.MAX_TX_FEE)
|
||||
else if (map.get("halfHourFee") < FeeRequestService.BTC_MAX_TX_FEE)
|
||||
return map.get("halfHourFee");
|
||||
else if (map.get("hourFee") < FeeService.MAX_TX_FEE)
|
||||
else if (map.get("hourFee") < FeeRequestService.BTC_MAX_TX_FEE)
|
||||
return map.get("hourFee");
|
||||
else
|
||||
return FeeService.MAX_TX_FEE;
|
||||
return FeeRequestService.BTC_MAX_TX_FEE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* 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 io.bisq.provider.fee.providers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface FeesProvider {
|
||||
Long getFee() throws IOException;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package io.bisq.provider.fee.providers;
|
||||
|
||||
import io.bisq.core.provider.fee.FeeService;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class LtcFeesProvider implements FeesProvider {
|
||||
|
||||
public LtcFeesProvider() {
|
||||
}
|
||||
|
||||
public Long getFee() throws IOException {
|
||||
return FeeService.DEFAULT_TX_FEE;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue