mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-07-30 10:18:44 -04:00
remove btc fee service
This commit is contained in:
parent
3314eac881
commit
31dfdd7710
49 changed files with 66 additions and 1797 deletions
|
@ -23,7 +23,6 @@ import bisq.core.api.model.MarketDepthInfo;
|
|||
import bisq.core.api.model.MarketPriceInfo;
|
||||
import bisq.core.api.model.PaymentAccountForm;
|
||||
import bisq.core.api.model.PaymentAccountFormField;
|
||||
import bisq.core.api.model.TxFeeRateInfo;
|
||||
import bisq.core.app.AppStartupState;
|
||||
import bisq.core.monetary.Price;
|
||||
import bisq.core.offer.Offer;
|
||||
|
@ -315,24 +314,6 @@ public class CoreApi {
|
|||
walletsService.sendBtc(address, amount, txFeeRate, memo, callback);
|
||||
}
|
||||
|
||||
|
||||
public void getTxFeeRate(ResultHandler resultHandler) {
|
||||
walletsService.getTxFeeRate(resultHandler);
|
||||
}
|
||||
|
||||
public void setTxFeeRatePreference(long txFeeRate,
|
||||
ResultHandler resultHandler) {
|
||||
walletsService.setTxFeeRatePreference(txFeeRate, resultHandler);
|
||||
}
|
||||
|
||||
public void unsetTxFeeRatePreference(ResultHandler resultHandler) {
|
||||
walletsService.unsetTxFeeRatePreference(resultHandler);
|
||||
}
|
||||
|
||||
public TxFeeRateInfo getMostRecentTxFeeRateInfo() {
|
||||
return walletsService.getMostRecentTxFeeRateInfo();
|
||||
}
|
||||
|
||||
public Transaction getTransaction(String txId) {
|
||||
return walletsService.getTransaction(txId);
|
||||
}
|
||||
|
|
|
@ -103,12 +103,10 @@ class CoreTradesService {
|
|||
var useSavingsWallet = true;
|
||||
|
||||
// synchronize access to take offer model // TODO (woodser): to avoid synchronizing, don't use stateful model
|
||||
Coin txFeeFromFeeService; // TODO (woodser): remove this and other unused fields
|
||||
Coin takerFee;
|
||||
Coin fundsNeededForTrade;
|
||||
synchronized (takeOfferModel) {
|
||||
takeOfferModel.initModel(offer, paymentAccount, useSavingsWallet);
|
||||
txFeeFromFeeService = takeOfferModel.getTxFeeFromFeeService();
|
||||
takerFee = takeOfferModel.getTakerFee();
|
||||
fundsNeededForTrade = takeOfferModel.getFundsNeededForTrade();
|
||||
log.info("Initiating take {} offer, {}", offer.isBuyOffer() ? "buy" : "sell", takeOfferModel);
|
||||
|
@ -116,7 +114,6 @@ class CoreTradesService {
|
|||
|
||||
// take offer
|
||||
tradeManager.onTakeOffer(offer.getAmount(),
|
||||
txFeeFromFeeService,
|
||||
takerFee,
|
||||
fundsNeededForTrade,
|
||||
offer,
|
||||
|
@ -127,6 +124,7 @@ class CoreTradesService {
|
|||
errorMessageHandler
|
||||
);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMessageHandler.handleErrorMessage(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ package bisq.core.api;
|
|||
import bisq.core.api.model.AddressBalanceInfo;
|
||||
import bisq.core.api.model.BalancesInfo;
|
||||
import bisq.core.api.model.BtcBalanceInfo;
|
||||
import bisq.core.api.model.TxFeeRateInfo;
|
||||
import bisq.core.api.model.XmrBalanceInfo;
|
||||
import bisq.core.app.AppStartupState;
|
||||
import bisq.core.btc.Balances;
|
||||
|
@ -31,15 +30,12 @@ import bisq.core.btc.setup.WalletsSetup;
|
|||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.WalletsManager;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
import bisq.common.Timer;
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.handlers.ResultHandler;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
@ -57,10 +53,6 @@ import com.google.common.cache.CacheBuilder;
|
|||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
|
||||
|
@ -97,8 +89,6 @@ class CoreWalletsService {
|
|||
private final BtcWalletService btcWalletService;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final CoinFormatter btcFormatter;
|
||||
private final FeeService feeService;
|
||||
private final Preferences preferences;
|
||||
|
||||
@Nullable
|
||||
private Timer lockTimer;
|
||||
|
@ -106,8 +96,6 @@ class CoreWalletsService {
|
|||
@Nullable
|
||||
private KeyParameter tempAesKey;
|
||||
|
||||
private final ListeningExecutorService executor = Utilities.getSingleThreadListeningExecutor("CoreWalletsService");
|
||||
|
||||
@Inject
|
||||
public CoreWalletsService(AppStartupState appStartupState,
|
||||
CoreContext coreContext,
|
||||
|
@ -118,7 +106,6 @@ class CoreWalletsService {
|
|||
BtcWalletService btcWalletService,
|
||||
XmrWalletService xmrWalletService,
|
||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
||||
FeeService feeService,
|
||||
Preferences preferences) {
|
||||
this.appStartupState = appStartupState;
|
||||
this.coreContext = coreContext;
|
||||
|
@ -129,8 +116,6 @@ class CoreWalletsService {
|
|||
this.btcWalletService = btcWalletService;
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.btcFormatter = btcFormatter;
|
||||
this.feeService = feeService;
|
||||
this.preferences = preferences;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -311,58 +296,6 @@ class CoreWalletsService {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void getTxFeeRate(ResultHandler resultHandler) {
|
||||
try {
|
||||
@SuppressWarnings({"unchecked", "Convert2MethodRef"})
|
||||
ListenableFuture<Void> future =
|
||||
(ListenableFuture<Void>) executor.submit(() -> feeService.requestFees());
|
||||
Futures.addCallback(future, new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Void ignored) {
|
||||
resultHandler.handleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
log.error("", t);
|
||||
throw new IllegalStateException("could not request fees from fee service", t);
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
|
||||
} catch (Exception ex) {
|
||||
log.error("", ex);
|
||||
throw new IllegalStateException("could not request fees from fee service", ex);
|
||||
}
|
||||
}
|
||||
|
||||
void setTxFeeRatePreference(long txFeeRate,
|
||||
ResultHandler resultHandler) {
|
||||
long minFeePerVbyte = feeService.getMinFeePerVByte();
|
||||
if (txFeeRate < minFeePerVbyte)
|
||||
throw new IllegalStateException(
|
||||
format("tx fee rate preference must be >= %d sats/byte", minFeePerVbyte));
|
||||
|
||||
preferences.setUseCustomWithdrawalTxFee(true);
|
||||
Coin satsPerByte = Coin.valueOf(txFeeRate);
|
||||
preferences.setWithdrawalTxFeeInVbytes(satsPerByte.value);
|
||||
getTxFeeRate(resultHandler);
|
||||
}
|
||||
|
||||
void unsetTxFeeRatePreference(ResultHandler resultHandler) {
|
||||
preferences.setUseCustomWithdrawalTxFee(false);
|
||||
getTxFeeRate(resultHandler);
|
||||
}
|
||||
|
||||
TxFeeRateInfo getMostRecentTxFeeRateInfo() {
|
||||
return new TxFeeRateInfo(
|
||||
preferences.isUseCustomWithdrawalTxFee(),
|
||||
preferences.getWithdrawalTxFeeInVbytes(),
|
||||
feeService.getMinFeePerVByte(),
|
||||
feeService.getTxFeePerVbyte().value,
|
||||
feeService.getLastRequest());
|
||||
}
|
||||
|
||||
Transaction getTransaction(String txId) {
|
||||
if (txId.length() != 64)
|
||||
throw new IllegalArgumentException(format("%s is not a transaction id", txId));
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* 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 bisq.core.api.model;
|
||||
|
||||
import bisq.common.Payload;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
@EqualsAndHashCode
|
||||
@Getter
|
||||
public class TxFeeRateInfo implements Payload {
|
||||
|
||||
private final boolean useCustomTxFeeRate;
|
||||
private final long customTxFeeRate;
|
||||
private final long minFeeServiceRate;
|
||||
private final long feeServiceRate;
|
||||
private final long lastFeeServiceRequestTs;
|
||||
|
||||
public TxFeeRateInfo(boolean useCustomTxFeeRate,
|
||||
long customTxFeeRate,
|
||||
long minFeeServiceRate,
|
||||
long feeServiceRate,
|
||||
long lastFeeServiceRequestTs) {
|
||||
this.useCustomTxFeeRate = useCustomTxFeeRate;
|
||||
this.customTxFeeRate = customTxFeeRate;
|
||||
this.minFeeServiceRate = minFeeServiceRate;
|
||||
this.feeServiceRate = feeServiceRate;
|
||||
this.lastFeeServiceRequestTs = lastFeeServiceRequestTs;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// PROTO BUFFER
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public bisq.proto.grpc.TxFeeRateInfo toProtoMessage() {
|
||||
return bisq.proto.grpc.TxFeeRateInfo.newBuilder()
|
||||
.setUseCustomTxFeeRate(useCustomTxFeeRate)
|
||||
.setCustomTxFeeRate(customTxFeeRate)
|
||||
.setMinFeeServiceRate(minFeeServiceRate)
|
||||
.setFeeServiceRate(feeServiceRate)
|
||||
.setLastFeeServiceRequestTs(lastFeeServiceRequestTs)
|
||||
.build();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static TxFeeRateInfo fromProto(bisq.proto.grpc.TxFeeRateInfo proto) {
|
||||
return new TxFeeRateInfo(proto.getUseCustomTxFeeRate(),
|
||||
proto.getCustomTxFeeRate(),
|
||||
proto.getMinFeeServiceRate(),
|
||||
proto.getFeeServiceRate(),
|
||||
proto.getLastFeeServiceRequestTs());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TxFeeRateInfo{" + "\n" +
|
||||
" useCustomTxFeeRate=" + useCustomTxFeeRate + "\n" +
|
||||
", customTxFeeRate=" + customTxFeeRate + " sats/byte" + "\n" +
|
||||
", minFeeServiceRate=" + minFeeServiceRate + " sats/byte" + "\n" +
|
||||
", feeServiceRate=" + feeServiceRate + " sats/byte" + "\n" +
|
||||
", lastFeeServiceRequestTs=" + lastFeeServiceRequestTs + "\n" +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
/*
|
||||
* 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 bisq.core.api.model;
|
||||
|
||||
import bisq.common.Payload;
|
||||
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
@EqualsAndHashCode
|
||||
@Getter
|
||||
public class TxInfo implements Payload {
|
||||
|
||||
// The client cannot see an instance of an org.bitcoinj.core.Transaction. We use the
|
||||
// lighter weight TxInfo proto wrapper instead, containing just enough fields to
|
||||
// view some transaction details. A block explorer or bitcoin-core client can be
|
||||
// used to see more detail.
|
||||
|
||||
private final String txId;
|
||||
private final long inputSum;
|
||||
private final long outputSum;
|
||||
private final long fee;
|
||||
private final int size;
|
||||
private final boolean isPending;
|
||||
private final String memo;
|
||||
|
||||
public TxInfo(TxInfoBuilder builder) {
|
||||
this.txId = builder.txId;
|
||||
this.inputSum = builder.inputSum;
|
||||
this.outputSum = builder.outputSum;
|
||||
this.fee = builder.fee;
|
||||
this.size = builder.size;
|
||||
this.isPending = builder.isPending;
|
||||
this.memo = builder.memo;
|
||||
}
|
||||
|
||||
public static TxInfo toTxInfo(Transaction transaction) {
|
||||
if (transaction == null)
|
||||
throw new IllegalStateException("server created a null transaction");
|
||||
|
||||
if (transaction.getFee() != null)
|
||||
return new TxInfoBuilder()
|
||||
.withTxId(transaction.getTxId().toString())
|
||||
.withInputSum(transaction.getInputSum().value)
|
||||
.withOutputSum(transaction.getOutputSum().value)
|
||||
.withFee(transaction.getFee().value)
|
||||
.withSize(transaction.getMessageSize())
|
||||
.withIsPending(transaction.isPending())
|
||||
.withMemo(transaction.getMemo())
|
||||
.build();
|
||||
else
|
||||
return new TxInfoBuilder()
|
||||
.withTxId(transaction.getTxId().toString())
|
||||
.withInputSum(transaction.getInputSum().value)
|
||||
.withOutputSum(transaction.getOutputSum().value)
|
||||
// Do not set fee == null.
|
||||
.withSize(transaction.getMessageSize())
|
||||
.withIsPending(transaction.isPending())
|
||||
.withMemo(transaction.getMemo())
|
||||
.build();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// PROTO BUFFER
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public bisq.proto.grpc.TxInfo toProtoMessage() {
|
||||
return bisq.proto.grpc.TxInfo.newBuilder()
|
||||
.setTxId(txId)
|
||||
.setInputSum(inputSum)
|
||||
.setOutputSum(outputSum)
|
||||
.setFee(fee)
|
||||
.setSize(size)
|
||||
.setIsPending(isPending)
|
||||
.setMemo(memo == null ? "" : memo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static TxInfo fromProto(bisq.proto.grpc.TxInfo proto) {
|
||||
return new TxInfoBuilder()
|
||||
.withTxId(proto.getTxId())
|
||||
.withInputSum(proto.getInputSum())
|
||||
.withOutputSum(proto.getOutputSum())
|
||||
.withFee(proto.getFee())
|
||||
.withSize(proto.getSize())
|
||||
.withIsPending(proto.getIsPending())
|
||||
.withMemo(proto.getMemo())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static class TxInfoBuilder {
|
||||
private String txId;
|
||||
private long inputSum;
|
||||
private long outputSum;
|
||||
private long fee;
|
||||
private int size;
|
||||
private boolean isPending;
|
||||
private String memo;
|
||||
|
||||
public TxInfoBuilder withTxId(String txId) {
|
||||
this.txId = txId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TxInfoBuilder withInputSum(long inputSum) {
|
||||
this.inputSum = inputSum;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TxInfoBuilder withOutputSum(long outputSum) {
|
||||
this.outputSum = outputSum;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TxInfoBuilder withFee(long fee) {
|
||||
this.fee = fee;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TxInfoBuilder withSize(int size) {
|
||||
this.size = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TxInfoBuilder withIsPending(boolean isPending) {
|
||||
this.isPending = isPending;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TxInfoBuilder withMemo(String memo) {
|
||||
this.memo = memo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TxInfo build() {
|
||||
return new TxInfo(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TxInfo{" + "\n" +
|
||||
" txId='" + txId + '\'' + "\n" +
|
||||
", inputSum=" + inputSum + "\n" +
|
||||
", outputSum=" + outputSum + "\n" +
|
||||
", fee=" + fee + "\n" +
|
||||
", size=" + size + "\n" +
|
||||
", isPending=" + isPending + "\n" +
|
||||
", memo='" + memo + '\'' + "\n" +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static String getTransactionDetailString(Transaction tx) {
|
||||
if (tx == null)
|
||||
throw new IllegalArgumentException("Cannot print details for null transaction");
|
||||
|
||||
StringBuilder builder = new StringBuilder("Transaction " + requireNonNull(tx).getTxId() + ":").append("\n");
|
||||
|
||||
builder.append("\tisPending: ").append(tx.isPending()).append("\n");
|
||||
builder.append("\tfee: ").append(tx.getFee()).append("\n");
|
||||
builder.append("\tweight: ").append(tx.getWeight()).append("\n");
|
||||
builder.append("\tVsize: ").append(tx.getVsize()).append("\n");
|
||||
builder.append("\tinputSum: ").append(tx.getInputSum()).append("\n");
|
||||
builder.append("\toutputSum: ").append(tx.getOutputSum()).append("\n");
|
||||
|
||||
Map<Sha256Hash, Integer> appearsInHashes = tx.getAppearsInHashes();
|
||||
if (appearsInHashes != null)
|
||||
builder.append("\tappearsInHashes: yes, count: ").append(appearsInHashes.size()).append("\n");
|
||||
else
|
||||
builder.append("\tappearsInHashes: ").append("no").append("\n");
|
||||
|
||||
builder.append("\tanyOutputSpent: ").append(tx.isAnyOutputSpent()).append("\n");
|
||||
builder.append("\tupdateTime: ").append(tx.getUpdateTime()).append("\n");
|
||||
builder.append("\tincludedInBestChainAt: ").append(tx.getIncludedInBestChainAt()).append("\n");
|
||||
builder.append("\thasWitnesses: ").append(tx.hasWitnesses()).append("\n");
|
||||
builder.append("\tlockTime: ").append(tx.getLockTime()).append("\n");
|
||||
builder.append("\tversion: ").append(tx.getVersion()).append("\n");
|
||||
builder.append("\thasConfidence: ").append(tx.hasConfidence()).append("\n");
|
||||
builder.append("\tsigOpCount: ").append(tx.getSigOpCount()).append("\n");
|
||||
builder.append("\tisTimeLocked: ").append(tx.isTimeLocked()).append("\n");
|
||||
builder.append("\thasRelativeLockTime: ").append(tx.hasRelativeLockTime()).append("\n");
|
||||
builder.append("\tisOptInFullRBF: ").append(tx.isOptInFullRBF()).append("\n");
|
||||
builder.append("\tpurpose: ").append(tx.getPurpose()).append("\n");
|
||||
builder.append("\texchangeRate: ").append(tx.getExchangeRate()).append("\n");
|
||||
builder.append("\tmemo: ").append(tx.getMemo()).append("\n");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
|
@ -33,7 +33,6 @@ import bisq.core.offer.OpenOfferManager;
|
|||
import bisq.core.offer.TriggerPriceService;
|
||||
import bisq.core.payment.AmazonGiftCardAccount;
|
||||
import bisq.core.payment.RevolutAccount;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.provider.mempool.MempoolService;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
import bisq.core.support.dispute.arbitration.ArbitrationManager;
|
||||
|
@ -88,7 +87,6 @@ public class DomainInitialisation {
|
|||
private final RefundAgentManager refundAgentManager;
|
||||
private final PrivateNotificationManager privateNotificationManager;
|
||||
private final P2PService p2PService;
|
||||
private final FeeService feeService;
|
||||
private final TradeStatisticsManager tradeStatisticsManager;
|
||||
private final AccountAgeWitnessService accountAgeWitnessService;
|
||||
private final SignedWitnessService signedWitnessService;
|
||||
|
@ -122,7 +120,6 @@ public class DomainInitialisation {
|
|||
RefundAgentManager refundAgentManager,
|
||||
PrivateNotificationManager privateNotificationManager,
|
||||
P2PService p2PService,
|
||||
FeeService feeService,
|
||||
TradeStatisticsManager tradeStatisticsManager,
|
||||
AccountAgeWitnessService accountAgeWitnessService,
|
||||
SignedWitnessService signedWitnessService,
|
||||
|
@ -154,7 +151,6 @@ public class DomainInitialisation {
|
|||
this.refundAgentManager = refundAgentManager;
|
||||
this.privateNotificationManager = privateNotificationManager;
|
||||
this.p2PService = p2PService;
|
||||
this.feeService = feeService;
|
||||
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||
this.signedWitnessService = signedWitnessService;
|
||||
|
@ -207,9 +203,6 @@ public class DomainInitialisation {
|
|||
|
||||
p2PService.onAllServicesInitialized();
|
||||
|
||||
feeService.onAllServicesInitialized();
|
||||
|
||||
|
||||
tradeStatisticsManager.onAllServicesInitialized();
|
||||
|
||||
accountAgeWitnessService.onAllServicesInitialized();
|
||||
|
|
|
@ -25,7 +25,6 @@ import bisq.core.btc.setup.WalletsSetup;
|
|||
import bisq.core.btc.wallet.WalletsManager;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.offer.OpenOfferManager;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.trade.TradeManager;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
|
@ -69,7 +68,6 @@ public class WalletAppSetup {
|
|||
private final WalletsManager walletsManager;
|
||||
private final WalletsSetup walletsSetup;
|
||||
private final CoreMoneroConnectionsService connectionService;
|
||||
private final FeeService feeService;
|
||||
private final Config config;
|
||||
private final Preferences preferences;
|
||||
|
||||
|
@ -94,14 +92,12 @@ public class WalletAppSetup {
|
|||
WalletsManager walletsManager,
|
||||
WalletsSetup walletsSetup,
|
||||
CoreMoneroConnectionsService connectionService,
|
||||
FeeService feeService,
|
||||
Config config,
|
||||
Preferences preferences) {
|
||||
this.coreContext = coreContext;
|
||||
this.walletsManager = walletsManager;
|
||||
this.walletsSetup = walletsSetup;
|
||||
this.connectionService = connectionService;
|
||||
this.feeService = feeService;
|
||||
this.config = config;
|
||||
this.preferences = preferences;
|
||||
this.useTorForBTC.set(preferences.getUseTorForBitcoinJ());
|
||||
|
@ -121,9 +117,8 @@ public class WalletAppSetup {
|
|||
ObjectProperty<Throwable> walletServiceException = new SimpleObjectProperty<>();
|
||||
btcInfoBinding = EasyBind.combine(connectionService.downloadPercentageProperty(), // TODO (woodser): update to XMR
|
||||
connectionService.chainHeightProperty(),
|
||||
feeService.feeUpdateCounterProperty(),
|
||||
walletServiceException,
|
||||
(downloadPercentage, chainHeight, feeUpdate, exception) -> {
|
||||
(downloadPercentage, chainHeight, exception) -> {
|
||||
String result;
|
||||
if (exception == null) {
|
||||
double percentage = (double) downloadPercentage;
|
||||
|
|
|
@ -29,7 +29,6 @@ import bisq.core.btc.wallet.TradeWalletService;
|
|||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.provider.ProvidersRepository;
|
||||
import bisq.core.provider.fee.FeeProvider;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
|
||||
import bisq.common.app.AppModule;
|
||||
|
@ -99,8 +98,6 @@ public class BitcoinModule extends AppModule {
|
|||
bind(ProvidersRepository.class).in(Singleton.class);
|
||||
bind(FeeProvider.class).in(Singleton.class);
|
||||
bind(PriceFeedService.class).in(Singleton.class);
|
||||
bind(FeeService.class).in(Singleton.class);
|
||||
bind(TxFeeEstimationService.class).in(Singleton.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
/*
|
||||
* 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 bisq.core.btc;
|
||||
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.user.Preferences;
|
||||
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
/**
|
||||
* Util class for getting the estimated tx fee for maker or taker fee tx.
|
||||
*/
|
||||
@Slf4j
|
||||
public class TxFeeEstimationService {
|
||||
|
||||
// Size/vsize of typical trade txs
|
||||
// Real txs size/vsize may vary in 1 or 2 bytes from the estimated values.
|
||||
// Values calculated with https://gist.github.com/oscarguindzberg/3d1349cb65d9fd9af9de0feaa3fd27ac
|
||||
// legacy fee tx with 1 input, maker/taker fee paid in btc size/vsize = 258
|
||||
// legacy deposit tx without change size/vsize = 381
|
||||
// legacy deposit tx with change size/vsize = 414
|
||||
// legacy payout tx size/vsize = 337
|
||||
// legacy delayed payout tx size/vsize = 302
|
||||
// segwit fee tx with 1 input, maker/taker fee paid in btc vsize = 173
|
||||
// segwit deposit tx without change vsize = 232
|
||||
// segwit deposit tx with change vsize = 263
|
||||
// segwit payout tx vsize = 169
|
||||
// segwit delayed payout tx vsize = 139
|
||||
public static int TYPICAL_TX_WITH_1_INPUT_VSIZE = 175;
|
||||
private static int DEPOSIT_TX_VSIZE = 233;
|
||||
|
||||
private static int MAX_ITERATIONS = 10;
|
||||
|
||||
private final FeeService feeService;
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final Preferences preferences;
|
||||
|
||||
@Inject
|
||||
public TxFeeEstimationService(FeeService feeService,
|
||||
BtcWalletService btcWalletService,
|
||||
Preferences preferences) {
|
||||
|
||||
this.feeService = feeService;
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.preferences = preferences;
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsizeForTaker(Coin fundsNeededForTrade, Coin tradeFee) {
|
||||
return getEstimatedFeeAndTxVsize(true,
|
||||
fundsNeededForTrade,
|
||||
tradeFee,
|
||||
feeService,
|
||||
btcWalletService,
|
||||
preferences);
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsizeForMaker(Coin reservedFundsForOffer,
|
||||
Coin tradeFee) {
|
||||
return getEstimatedFeeAndTxVsize(false,
|
||||
reservedFundsForOffer,
|
||||
tradeFee,
|
||||
feeService,
|
||||
btcWalletService,
|
||||
preferences);
|
||||
}
|
||||
|
||||
private Tuple2<Coin, Integer> getEstimatedFeeAndTxVsize(boolean isTaker,
|
||||
Coin amount,
|
||||
Coin tradeFee,
|
||||
FeeService feeService,
|
||||
BtcWalletService btcWalletService,
|
||||
Preferences preferences) {
|
||||
Coin txFeePerVbyte = feeService.getTxFeePerVbyte();
|
||||
// We start with min taker fee vsize of 175
|
||||
int estimatedTxVsize = TYPICAL_TX_WITH_1_INPUT_VSIZE;
|
||||
try {
|
||||
estimatedTxVsize = getEstimatedTxVsize(List.of(tradeFee, amount), estimatedTxVsize, txFeePerVbyte, btcWalletService);
|
||||
} catch (InsufficientMoneyException e) {
|
||||
if (isTaker) {
|
||||
// If we cannot do the estimation, we use the vsize o the largest of our txs which is the deposit tx.
|
||||
estimatedTxVsize = DEPOSIT_TX_VSIZE;
|
||||
}
|
||||
log.info("We cannot do the fee estimation because there are not enough funds in the wallet. This is expected " +
|
||||
"if the user pays from an external wallet. In that case we use an estimated tx vsize of {} vbytes.", estimatedTxVsize);
|
||||
}
|
||||
|
||||
Coin txFee;
|
||||
int vsize;
|
||||
if (isTaker) {
|
||||
int averageVsize = (estimatedTxVsize + DEPOSIT_TX_VSIZE) / 2; // deposit tx has about 233 vbytes
|
||||
// We use at least the vsize of the deposit tx to not underpay it.
|
||||
vsize = Math.max(DEPOSIT_TX_VSIZE, averageVsize);
|
||||
txFee = txFeePerVbyte.multiply(vsize);
|
||||
log.info("Fee estimation resulted in a tx vsize of {} vbytes.\n" +
|
||||
"We use an average between the taker fee tx and the deposit tx (233 vbytes) which results in {} vbytes.\n" +
|
||||
"The deposit tx has 233 vbytes, we use that as our min value. Vsize for fee calculation is {} vbytes.\n" +
|
||||
"The tx fee of {} Sat", estimatedTxVsize, averageVsize, vsize, txFee.value);
|
||||
} else {
|
||||
vsize = estimatedTxVsize;
|
||||
txFee = txFeePerVbyte.multiply(vsize);
|
||||
log.info("Fee estimation resulted in a tx vsize of {} vbytes and a tx fee of {} Sat.", vsize, txFee.value);
|
||||
}
|
||||
|
||||
return new Tuple2<>(txFee, vsize);
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsize(Coin amount,
|
||||
BtcWalletService btcWalletService) {
|
||||
Coin txFeePerVbyte = btcWalletService.getTxFeeForWithdrawalPerVbyte();
|
||||
// We start with min taker fee vsize of 175
|
||||
int estimatedTxVsize = TYPICAL_TX_WITH_1_INPUT_VSIZE;
|
||||
try {
|
||||
estimatedTxVsize = getEstimatedTxVsize(List.of(amount), estimatedTxVsize, txFeePerVbyte, btcWalletService);
|
||||
} catch (InsufficientMoneyException e) {
|
||||
log.info("We cannot do the fee estimation because there are not enough funds in the wallet. This is expected " +
|
||||
"if the user pays from an external wallet. In that case we use an estimated tx vsize of {} vbytes.", estimatedTxVsize);
|
||||
}
|
||||
|
||||
Coin txFee = txFeePerVbyte.multiply(estimatedTxVsize);
|
||||
log.info("Fee estimation resulted in a tx vsize of {} vbytes and a tx fee of {} Sat.", estimatedTxVsize, txFee.value);
|
||||
|
||||
return new Tuple2<>(txFee, estimatedTxVsize);
|
||||
}
|
||||
|
||||
// We start with the initialEstimatedTxVsize for a tx with 1 input (175) vbytes and get from BitcoinJ a tx back which
|
||||
// contains the required inputs to fund that tx (outputs + miner fee). The miner fee in that case is based on
|
||||
// the assumption that we only need 1 input. Once we receive back the real tx vsize from the tx BitcoinJ has created
|
||||
// with the required inputs we compare if the vsize is not more then 20% different to our assumed tx vsize. If we are inside
|
||||
// that tolerance we use that tx vsize for our fee estimation, if not (if there has been more then 1 inputs) we
|
||||
// apply the new fee based on the reported tx vsize and request again from BitcoinJ to fill that tx with the inputs
|
||||
// to be sufficiently funded. The algorithm how BitcoinJ selects utxos is complex and contains several aspects
|
||||
// (minimize fee, don't create too many tiny utxos,...). We treat that algorithm as an unknown and it is not
|
||||
// guaranteed that there are more inputs required if we increase the fee (it could be that there is a better
|
||||
// selection of inputs chosen if we have increased the fee and therefore less inputs and smaller tx vsize). As the increased fee might
|
||||
// change the number of inputs we need to repeat that process until we are inside of a certain tolerance. To avoid
|
||||
// potential endless loops we add a counter (we use 10, usually it takes just very few iterations).
|
||||
// Worst case would be that the last vsize we got reported is > 20% off to
|
||||
// the real tx vsize but as fee estimation is anyway a educated guess in the best case we don't worry too much.
|
||||
// If we have underpaid the tx might take longer to get confirmed.
|
||||
@VisibleForTesting
|
||||
static int getEstimatedTxVsize(List<Coin> outputValues,
|
||||
int initialEstimatedTxVsize,
|
||||
Coin txFeePerVbyte,
|
||||
BtcWalletService btcWalletService)
|
||||
throws InsufficientMoneyException {
|
||||
boolean isInTolerance;
|
||||
int estimatedTxVsize = initialEstimatedTxVsize;
|
||||
int realTxVsize;
|
||||
int counter = 0;
|
||||
do {
|
||||
Coin txFee = txFeePerVbyte.multiply(estimatedTxVsize);
|
||||
realTxVsize = btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee);
|
||||
isInTolerance = isInTolerance(estimatedTxVsize, realTxVsize, 0.2);
|
||||
if (!isInTolerance) {
|
||||
estimatedTxVsize = realTxVsize;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
while (!isInTolerance && counter < MAX_ITERATIONS);
|
||||
if (!isInTolerance) {
|
||||
log.warn("We could not find a tx which satisfies our tolerance requirement of 20%. " +
|
||||
"realTxVsize={}, estimatedTxVsize={}",
|
||||
realTxVsize, estimatedTxVsize);
|
||||
}
|
||||
return estimatedTxVsize;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static boolean isInTolerance(int estimatedVsize, int txVsize, double tolerance) {
|
||||
checkArgument(estimatedVsize > 0, "estimatedVsize must be positive");
|
||||
checkArgument(txVsize > 0, "txVsize must be positive");
|
||||
checkArgument(tolerance > 0, "tolerance must be positive");
|
||||
double deviation = Math.abs(1 - ((double) estimatedVsize / (double) txVsize));
|
||||
return deviation <= tolerance;
|
||||
}
|
||||
}
|
|
@ -25,7 +25,6 @@ import bisq.core.btc.model.AddressEntry;
|
|||
import bisq.core.btc.model.AddressEntryList;
|
||||
import bisq.core.btc.setup.WalletsSetup;
|
||||
import bisq.core.btc.wallet.http.MemPoolSpaceTxBroadcaster;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.user.Preferences;
|
||||
|
||||
import bisq.common.handlers.ErrorMessageHandler;
|
||||
|
@ -88,11 +87,9 @@ public class BtcWalletService extends WalletService {
|
|||
@Inject
|
||||
public BtcWalletService(WalletsSetup walletsSetup,
|
||||
AddressEntryList addressEntryList,
|
||||
Preferences preferences,
|
||||
FeeService feeService) {
|
||||
Preferences preferences) {
|
||||
super(walletsSetup,
|
||||
preferences,
|
||||
feeService);
|
||||
preferences);
|
||||
|
||||
this.addressEntryList = addressEntryList;
|
||||
|
||||
|
@ -574,6 +571,10 @@ public class BtcWalletService extends WalletService {
|
|||
// Withdrawal Fee calculation
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Coin getTxFeeForWithdrawalPerVbyte() {
|
||||
throw new RuntimeException("BTC fee estimation removed");
|
||||
}
|
||||
|
||||
public Transaction getFeeEstimationTransaction(String fromAddress,
|
||||
String toAddress,
|
||||
Coin amount,
|
||||
|
|
|
@ -24,7 +24,6 @@ import bisq.core.btc.listeners.BalanceListener;
|
|||
import bisq.core.btc.listeners.TxConfidenceListener;
|
||||
import bisq.core.btc.setup.WalletsSetup;
|
||||
import bisq.core.btc.wallet.http.MemPoolSpaceTxBroadcaster;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.user.Preferences;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
|
@ -117,7 +116,6 @@ import monero.wallet.model.MoneroTxWallet;
|
|||
public abstract class WalletService {
|
||||
protected final WalletsSetup walletsSetup;
|
||||
protected final Preferences preferences;
|
||||
protected final FeeService feeService;
|
||||
protected final NetworkParameters params;
|
||||
private final BisqWalletListener walletEventListener = new BisqWalletListener();
|
||||
private final CopyOnWriteArraySet<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArraySet<>();
|
||||
|
@ -140,11 +138,9 @@ public abstract class WalletService {
|
|||
|
||||
@Inject
|
||||
WalletService(WalletsSetup walletsSetup,
|
||||
Preferences preferences,
|
||||
FeeService feeService) {
|
||||
Preferences preferences) {
|
||||
this.walletsSetup = walletsSetup;
|
||||
this.preferences = preferences;
|
||||
this.feeService = feeService;
|
||||
|
||||
params = walletsSetup.getParams();
|
||||
|
||||
|
@ -519,14 +515,6 @@ public abstract class WalletService {
|
|||
return getBalanceForAddress(getAddressFromOutput(output));
|
||||
}
|
||||
|
||||
public Coin getTxFeeForWithdrawalPerVbyte() {
|
||||
Coin fee = (preferences.isUseCustomWithdrawalTxFee()) ?
|
||||
Coin.valueOf(preferences.getWithdrawalTxFeeInVbytes()) :
|
||||
feeService.getTxFeePerVbyte();
|
||||
log.info("tx fee = " + fee.toFriendlyString());
|
||||
return fee;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Tx outputs
|
||||
|
@ -578,7 +566,6 @@ public abstract class WalletService {
|
|||
throws InsufficientMoneyException, AddressFormatException {
|
||||
SendRequest sendRequest = SendRequest.emptyWallet(Address.fromString(params, toAddress));
|
||||
sendRequest.fee = Coin.ZERO;
|
||||
sendRequest.feePerKb = getTxFeeForWithdrawalPerVbyte().multiply(1000);
|
||||
sendRequest.aesKey = aesKey;
|
||||
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
||||
printTx("empty btc wallet", sendResult.tx);
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
package bisq.core.offer;
|
||||
|
||||
import bisq.core.btc.TxFeeEstimationService;
|
||||
import bisq.core.btc.wallet.Restrictions;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.CurrencyUtil;
|
||||
|
@ -38,7 +37,6 @@ import bisq.network.p2p.P2PService;
|
|||
|
||||
import bisq.common.app.Version;
|
||||
import bisq.common.crypto.PubKeyRingProvider;
|
||||
import bisq.common.util.Tuple2;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
@ -59,7 +57,6 @@ import static bisq.core.payment.payload.PaymentMethod.HAL_CASH_ID;
|
|||
@Singleton
|
||||
public class CreateOfferService {
|
||||
private final OfferUtil offerUtil;
|
||||
private final TxFeeEstimationService txFeeEstimationService;
|
||||
private final PriceFeedService priceFeedService;
|
||||
private final P2PService p2PService;
|
||||
private final PubKeyRingProvider pubKeyRingProvider;
|
||||
|
@ -75,7 +72,6 @@ public class CreateOfferService {
|
|||
|
||||
@Inject
|
||||
public CreateOfferService(OfferUtil offerUtil,
|
||||
TxFeeEstimationService txFeeEstimationService,
|
||||
PriceFeedService priceFeedService,
|
||||
P2PService p2PService,
|
||||
PubKeyRingProvider pubKeyRingProvider,
|
||||
|
@ -84,7 +80,6 @@ public class CreateOfferService {
|
|||
TradeStatisticsManager tradeStatisticsManager,
|
||||
ArbitratorManager arbitratorManager) {
|
||||
this.offerUtil = offerUtil;
|
||||
this.txFeeEstimationService = txFeeEstimationService;
|
||||
this.priceFeedService = priceFeedService;
|
||||
this.p2PService = p2PService;
|
||||
this.pubKeyRingProvider = pubKeyRingProvider;
|
||||
|
@ -227,18 +222,6 @@ public class CreateOfferService {
|
|||
return offer;
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsize(Coin amount,
|
||||
OfferDirection direction,
|
||||
double buyerSecurityDeposit,
|
||||
double sellerSecurityDeposit) {
|
||||
Coin reservedFundsForOffer = getReservedFundsForOffer(direction,
|
||||
amount,
|
||||
buyerSecurityDeposit,
|
||||
sellerSecurityDeposit);
|
||||
return txFeeEstimationService.getEstimatedFeeAndTxVsizeForMaker(reservedFundsForOffer,
|
||||
offerUtil.getMakerFee(amount));
|
||||
}
|
||||
|
||||
public Coin getReservedFundsForOffer(OfferDirection direction,
|
||||
Coin amount,
|
||||
double buyerSecurityDeposit,
|
||||
|
|
|
@ -27,9 +27,9 @@ import bisq.core.monetary.Volume;
|
|||
import bisq.core.payment.CashByMailAccount;
|
||||
import bisq.core.payment.F2FAccount;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.provider.price.MarketPrice;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
import bisq.core.trade.HavenoUtils;
|
||||
import bisq.core.trade.statistics.ReferralIdService;
|
||||
import bisq.core.user.AutoConfirmSettings;
|
||||
import bisq.core.user.Preferences;
|
||||
|
@ -191,8 +191,8 @@ public class OfferUtil {
|
|||
@Nullable
|
||||
public Coin getTakerFee(@Nullable Coin amount) {
|
||||
if (amount != null) {
|
||||
Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getTakerFeePerBtc(), amount);
|
||||
return CoinUtil.maxCoin(feePerBtc, FeeService.getMinTakerFee());
|
||||
Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), amount);
|
||||
return CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import bisq.core.offer.Offer;
|
|||
import bisq.core.offer.OfferUtil;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.payload.PaymentMethod;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
|
||||
import bisq.common.taskrunner.Model;
|
||||
|
@ -37,8 +36,6 @@ import org.bitcoinj.core.Coin;
|
|||
import javax.inject.Inject;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -60,7 +57,6 @@ public class TakeOfferModel implements Model {
|
|||
// Immutable
|
||||
private final AccountAgeWitnessService accountAgeWitnessService;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final FeeService feeService;
|
||||
private final OfferUtil offerUtil;
|
||||
private final PriceFeedService priceFeedService;
|
||||
|
||||
|
@ -75,11 +71,6 @@ public class TakeOfferModel implements Model {
|
|||
private Coin securityDeposit;
|
||||
private boolean useSavingsWallet;
|
||||
|
||||
// Use an average of a typical trade fee tx with 1 input, deposit tx and payout tx.
|
||||
private final int feeTxVsize = 192; // (175+233+169)/3
|
||||
private Coin txFeePerVbyteFromFeeService;
|
||||
@Getter
|
||||
private Coin txFeeFromFeeService;
|
||||
@Getter
|
||||
private Coin takerFee;
|
||||
@Getter
|
||||
|
@ -98,12 +89,10 @@ public class TakeOfferModel implements Model {
|
|||
@Inject
|
||||
public TakeOfferModel(AccountAgeWitnessService accountAgeWitnessService,
|
||||
XmrWalletService xmrWalletService,
|
||||
FeeService feeService,
|
||||
OfferUtil offerUtil,
|
||||
PriceFeedService priceFeedService) {
|
||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.feeService = feeService;
|
||||
this.offerUtil = offerUtil;
|
||||
this.priceFeedService = priceFeedService;
|
||||
}
|
||||
|
@ -124,7 +113,6 @@ public class TakeOfferModel implements Model {
|
|||
: offer.getSellerSecurityDeposit();
|
||||
this.takerFee = offerUtil.getTakerFee(amount);
|
||||
|
||||
calculateTxFees();
|
||||
calculateVolume();
|
||||
calculateTotalToPay();
|
||||
offer.resetState();
|
||||
|
@ -137,46 +125,12 @@ public class TakeOfferModel implements Model {
|
|||
// empty
|
||||
}
|
||||
|
||||
private void calculateTxFees() {
|
||||
// Taker pays 3 times the tx fee (taker fee, deposit, payout) because the mining
|
||||
// fee might be different when maker created the offer and reserved his funds.
|
||||
// Taker creates at least taker fee and deposit tx at nearly the same moment.
|
||||
// Just the payout will be later and still could lead to issues if the required
|
||||
// fee changed a lot in the meantime. using RBF and/or multiple batch-signed
|
||||
// payout tx with different fees might be an option but RBF is not supported yet
|
||||
// in BitcoinJ and batched txs would add more complexity to the trade protocol.
|
||||
|
||||
// A typical trade fee tx has about 175 vbytes (if one input). The trade txs has
|
||||
// about 169-263 vbytes. We use 192 as a average value.
|
||||
|
||||
// Fee calculations:
|
||||
// Trade fee tx: 175 vbytes (1 input)
|
||||
// Deposit tx: 233 vbytes (1 MS output+ OP_RETURN) - 263 vbytes
|
||||
// (1 MS output + OP_RETURN + change in case of smaller trade amount)
|
||||
// Payout tx: 169 vbytes
|
||||
// Disputed payout tx: 139 vbytes
|
||||
|
||||
txFeePerVbyteFromFeeService = getTxFeePerVbyte();
|
||||
txFeeFromFeeService = offerUtil.getTxFeeByVsize(txFeePerVbyteFromFeeService, feeTxVsize);
|
||||
log.info("{} txFeePerVbyte = {}", feeService.getClass().getSimpleName(), txFeePerVbyteFromFeeService);
|
||||
}
|
||||
|
||||
private Coin getTxFeePerVbyte() {
|
||||
try {
|
||||
CompletableFuture<Void> feeRequestFuture = CompletableFuture.runAsync(feeService::requestFees);
|
||||
feeRequestFuture.get(); // Block until async fee request is complete.
|
||||
return feeService.getTxFeePerVbyte();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new IllegalStateException("Could not request fees from fee service.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateTotalToPay() {
|
||||
// Taker pays 2 times the tx fee because the mining fee might be different when
|
||||
// maker created the offer and reserved his funds, so that would not work well
|
||||
// with dynamic fees. The mining fee for the takeOfferFee tx is deducted from
|
||||
// the createOfferFee and not visible to the trader.
|
||||
Coin feeAndSecDeposit = getTotalTxFee().add(securityDeposit).add(takerFee);
|
||||
Coin feeAndSecDeposit = securityDeposit.add(takerFee);
|
||||
|
||||
totalToPayAsCoin = offer.isBuyOffer()
|
||||
? feeAndSecDeposit.add(amount)
|
||||
|
@ -212,35 +166,10 @@ public class TakeOfferModel implements Model {
|
|||
offer.getMirroredDirection());
|
||||
}
|
||||
|
||||
public Coin getTotalTxFee() {
|
||||
return txFeeFromFeeService.add(getTxFeeForDepositTx()).add(getTxFeeForPayoutTx());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Coin getFundsNeededForTrade() {
|
||||
// If taking a buy offer, taker needs to reserve the offer.amt too.
|
||||
return securityDeposit
|
||||
.add(getTxFeeForDepositTx())
|
||||
.add(getTxFeeForPayoutTx())
|
||||
.add(offer.isBuyOffer() ? amount : ZERO);
|
||||
}
|
||||
|
||||
private Coin getTxFeeForDepositTx() {
|
||||
// TODO fix with new trade protocol!
|
||||
// Unfortunately we cannot change that to the correct fees as it would break
|
||||
// backward compatibility. We still might find a way with offer version or app
|
||||
// version checks so lets keep that commented out code as that shows how it
|
||||
// should be.
|
||||
return txFeeFromFeeService;
|
||||
}
|
||||
|
||||
private Coin getTxFeeForPayoutTx() {
|
||||
// TODO fix with new trade protocol!
|
||||
// Unfortunately we cannot change that to the correct fees as it would break
|
||||
// backward compatibility. We still might find a way with offer version or app
|
||||
// version checks so lets keep that commented out code as that shows how it
|
||||
// should be.
|
||||
return txFeeFromFeeService;
|
||||
return securityDeposit.add(offer.isBuyOffer() ? amount : ZERO);
|
||||
}
|
||||
|
||||
private void validateModelInputs() {
|
||||
|
@ -264,8 +193,6 @@ public class TakeOfferModel implements Model {
|
|||
this.takerFee = null;
|
||||
this.totalAvailableBalance = null;
|
||||
this.totalToPayAsCoin = null;
|
||||
this.txFeeFromFeeService = null;
|
||||
this.txFeePerVbyteFromFeeService = null;
|
||||
this.useSavingsWallet = true;
|
||||
this.volume = null;
|
||||
}
|
||||
|
@ -281,9 +208,6 @@ public class TakeOfferModel implements Model {
|
|||
", addressEntry=" + addressEntry + "\n" +
|
||||
", amount=" + amount + "\n" +
|
||||
", securityDeposit=" + securityDeposit + "\n" +
|
||||
", feeTxVsize=" + feeTxVsize + "\n" +
|
||||
", txFeePerVbyteFromFeeService=" + txFeePerVbyteFromFeeService + "\n" +
|
||||
", txFeeFromFeeService=" + txFeeFromFeeService + "\n" +
|
||||
", takerFee=" + takerFee + "\n" +
|
||||
", totalToPayAsCoin=" + totalToPayAsCoin + "\n" +
|
||||
", missingCoin=" + missingCoin + "\n" +
|
||||
|
|
|
@ -1,191 +0,0 @@
|
|||
/*
|
||||
* 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 bisq.core.provider.fee;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.config.Config;
|
||||
import bisq.common.handlers.FaultHandler;
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import org.bitcoinj.utils.MonetaryFormat;
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import bisq.core.util.ParsingUtils;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@Slf4j
|
||||
public class FeeService {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Static
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Miner fees are between 1-600 sat/vbyte. We try to stay on the safe side. BTC_DEFAULT_TX_FEE is only used if our
|
||||
// fee service would not deliver data.
|
||||
private static final long BTC_DEFAULT_TX_FEE = 50;
|
||||
private static final long MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN = 2;
|
||||
private static final MonetaryFormat btcCoinFormat = Config.baseCurrencyNetworkParameters().getMonetaryFormat();
|
||||
|
||||
|
||||
public static Coin getMakerFeePerBtc() {
|
||||
return ParsingUtils.parseToCoin("0.001", btcCoinFormat);
|
||||
}
|
||||
|
||||
public static Coin getMinMakerFee() {
|
||||
return ParsingUtils.parseToCoin("0.00005", btcCoinFormat);
|
||||
}
|
||||
|
||||
public static Coin getTakerFeePerBtc() {
|
||||
return ParsingUtils.parseToCoin("0.003", btcCoinFormat);
|
||||
}
|
||||
|
||||
public static Coin getMinTakerFee() {
|
||||
return ParsingUtils.parseToCoin("0.00005", btcCoinFormat);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Class fields
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
private final FeeProvider feeProvider;
|
||||
private final IntegerProperty feeUpdateCounter = new SimpleIntegerProperty(0);
|
||||
private long txFeePerVbyte = BTC_DEFAULT_TX_FEE;
|
||||
private Map<String, Long> timeStampMap;
|
||||
@Getter
|
||||
private long lastRequest;
|
||||
@Getter
|
||||
private long minFeePerVByte;
|
||||
private long epochInSecondAtLastRequest;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public FeeService(FeeProvider feeProvider) {
|
||||
this.feeProvider = feeProvider;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onAllServicesInitialized() {
|
||||
minFeePerVByte = Config.baseCurrencyNetwork().getDefaultMinFeePerVbyte();
|
||||
|
||||
requestFees();
|
||||
|
||||
// We update all 5 min.
|
||||
UserThread.runPeriodically(this::requestFees, 5, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
|
||||
public void requestFees() {
|
||||
requestFees(null, null);
|
||||
}
|
||||
|
||||
public void requestFees(Runnable resultHandler) {
|
||||
requestFees(resultHandler, null);
|
||||
}
|
||||
|
||||
public void requestFees(@Nullable Runnable resultHandler, @Nullable FaultHandler faultHandler) {
|
||||
long now = Instant.now().getEpochSecond();
|
||||
// We all requests only each 2 minutes
|
||||
if (now - lastRequest > MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN * 60) {
|
||||
lastRequest = now;
|
||||
FeeRequest feeRequest = new FeeRequest();
|
||||
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>, Map<String, Long>> result) {
|
||||
UserThread.execute(() -> {
|
||||
checkNotNull(result, "Result must not be null at getFees");
|
||||
timeStampMap = result.first;
|
||||
epochInSecondAtLastRequest = timeStampMap.get(Config.BTC_FEES_TS);
|
||||
final Map<String, Long> map = result.second;
|
||||
txFeePerVbyte = map.get(Config.BTC_TX_FEE);
|
||||
minFeePerVByte = map.get(Config.BTC_MIN_TX_FEE);
|
||||
|
||||
if (txFeePerVbyte < minFeePerVByte) {
|
||||
log.warn("The delivered fee of {} sat/vbyte is smaller than the min. default fee of {} sat/vbyte", txFeePerVbyte, minFeePerVByte);
|
||||
txFeePerVbyte = minFeePerVByte;
|
||||
}
|
||||
|
||||
feeUpdateCounter.set(feeUpdateCounter.get() + 1);
|
||||
log.info("BTC tx fee: txFeePerVbyte={} minFeePerVbyte={}", txFeePerVbyte, minFeePerVByte);
|
||||
if (resultHandler != null)
|
||||
resultHandler.run();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
log.warn("Could not load fees. feeProvider={}, error={}", feeProvider.toString(), throwable.toString());
|
||||
if (faultHandler != null)
|
||||
UserThread.execute(() -> faultHandler.handleFault("Could not load fees", throwable));
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
} else {
|
||||
log.debug("We got a requestFees called again before min pause of {} minutes has passed.", MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN);
|
||||
UserThread.execute(() -> {
|
||||
if (resultHandler != null)
|
||||
resultHandler.run();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Coin getTxFee(int vsizeInVbytes) {
|
||||
return getTxFeePerVbyte().multiply(vsizeInVbytes);
|
||||
}
|
||||
|
||||
public Coin getTxFeePerVbyte() {
|
||||
return Coin.valueOf(txFeePerVbyte);
|
||||
}
|
||||
|
||||
public ReadOnlyIntegerProperty feeUpdateCounterProperty() {
|
||||
return feeUpdateCounter;
|
||||
}
|
||||
|
||||
public boolean isFeeAvailable() {
|
||||
return feeUpdateCounter.get() > 0;
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import bisq.core.trade.messages.InitTradeRequest;
|
|||
import bisq.core.trade.messages.PaymentReceivedMessage;
|
||||
import bisq.core.trade.messages.PaymentSentMessage;
|
||||
import bisq.core.util.JsonUtil;
|
||||
import bisq.core.util.ParsingUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
@ -39,7 +40,7 @@ import java.util.concurrent.Executors;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import org.bitcoinj.utils.MonetaryFormat;
|
||||
import com.google.common.base.CaseFormat;
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
|
@ -95,12 +96,29 @@ public class HavenoUtils {
|
|||
public static long xmrToCentineros(double xmr) {
|
||||
return atomicUnitsToCentineros(xmrToAtomicUnits(xmr));
|
||||
}
|
||||
|
||||
private static final MonetaryFormat xmrCoinFormat = Config.baseCurrencyNetworkParameters().getMonetaryFormat();
|
||||
|
||||
|
||||
public static Coin getMakerFeePerBtc() {
|
||||
return ParsingUtils.parseToCoin("0.001", xmrCoinFormat);
|
||||
}
|
||||
|
||||
public static Coin getMinMakerFee() {
|
||||
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
|
||||
}
|
||||
|
||||
public static Coin getTakerFeePerBtc() {
|
||||
return ParsingUtils.parseToCoin("0.003", xmrCoinFormat);
|
||||
}
|
||||
|
||||
public static Coin getMinTakerFee() {
|
||||
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get address to collect trade fees.
|
||||
*
|
||||
* TODO: move to config constants?
|
||||
*
|
||||
* @return the address which collects trade fees
|
||||
*/
|
||||
public static String getTradeFeeAddress() {
|
||||
|
|
|
@ -29,7 +29,6 @@ import bisq.core.offer.OpenOffer;
|
|||
import bisq.core.offer.OpenOfferManager;
|
||||
import bisq.core.offer.SignedOffer;
|
||||
import bisq.core.offer.availability.OfferAvailabilityModel;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||
|
@ -458,8 +457,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
}
|
||||
|
||||
// compute expected taker fee
|
||||
Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getTakerFeePerBtc(), Coin.valueOf(offer.getOfferPayload().getAmount()));
|
||||
Coin takerFee = CoinUtil.maxCoin(feePerBtc, FeeService.getMinTakerFee());
|
||||
Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), Coin.valueOf(offer.getOfferPayload().getAmount()));
|
||||
Coin takerFee = CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee());
|
||||
|
||||
// create arbitrator trade
|
||||
trade = new ArbitratorTrade(offer,
|
||||
|
@ -691,7 +690,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
|
||||
// First we check if offer is still available then we create the trade with the protocol
|
||||
public void onTakeOffer(Coin amount,
|
||||
Coin txFee,
|
||||
Coin takerFee,
|
||||
Coin fundsNeededForTrade,
|
||||
Offer offer,
|
||||
|
|
|
@ -29,7 +29,6 @@ import bisq.core.locale.GlobalSettings;
|
|||
import bisq.core.locale.TradeCurrency;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.PaymentAccountUtil;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.xmr.MoneroNodeSettings;
|
||||
|
||||
import bisq.network.p2p.network.BridgeAddressProvider;
|
||||
|
@ -162,7 +161,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
|
||||
private final PersistenceManager<PreferencesPayload> persistenceManager;
|
||||
private final Config config;
|
||||
private final FeeService feeService;
|
||||
private final LocalBitcoinNode localBitcoinNode;
|
||||
private final String btcNodesFromOptions;
|
||||
@Getter
|
||||
|
@ -175,13 +173,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
@Inject
|
||||
public Preferences(PersistenceManager<PreferencesPayload> persistenceManager,
|
||||
Config config,
|
||||
FeeService feeService,
|
||||
LocalBitcoinNode localBitcoinNode,
|
||||
@Named(Config.BTC_NODES) String btcNodesFromOptions) {
|
||||
|
||||
this.persistenceManager = persistenceManager;
|
||||
this.config = config;
|
||||
this.feeService = feeService;
|
||||
this.localBitcoinNode = localBitcoinNode;
|
||||
this.btcNodesFromOptions = btcNodesFromOptions;
|
||||
|
||||
|
@ -837,11 +833,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
return prefPayload.getBridgeAddresses();
|
||||
}
|
||||
|
||||
public long getWithdrawalTxFeeInVbytes() {
|
||||
return Math.max(prefPayload.getWithdrawalTxFeeInVbytes(),
|
||||
feeService.getMinFeePerVByte());
|
||||
}
|
||||
|
||||
public List<String> getDefaultXmrTxProofServices() {
|
||||
if (config.useLocalhostForP2P) {
|
||||
return XMR_TX_PROOF_SERVICES_CLEAR_NET;
|
||||
|
|
|
@ -20,8 +20,7 @@ package bisq.core.util.coin;
|
|||
import bisq.core.btc.wallet.Restrictions;
|
||||
import bisq.core.monetary.Price;
|
||||
import bisq.core.monetary.Volume;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
|
||||
import bisq.core.trade.HavenoUtils;
|
||||
import bisq.common.util.MathUtils;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
@ -98,8 +97,8 @@ public class CoinUtil {
|
|||
@Nullable
|
||||
public static Coin getMakerFee(@Nullable Coin amount) {
|
||||
if (amount != null) {
|
||||
Coin feePerBtc = getFeePerBtc(FeeService.getMakerFeePerBtc(), amount);
|
||||
return maxCoin(feePerBtc, FeeService.getMinMakerFee());
|
||||
Coin feePerBtc = getFeePerBtc(HavenoUtils.getMakerFeePerBtc(), amount);
|
||||
return maxCoin(feePerBtc, HavenoUtils.getMinMakerFee());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* 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 bisq.core.btc;
|
||||
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class TxFeeEstimationServiceTest {
|
||||
|
||||
@Test
|
||||
public void testGetEstimatedTxVsize_withDefaultTxVsize() throws InsufficientMoneyException {
|
||||
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
|
||||
int initialEstimatedTxVsize;
|
||||
Coin txFeePerVbyte;
|
||||
BtcWalletService btcWalletService = mock(BtcWalletService.class);
|
||||
int result;
|
||||
int realTxVsize;
|
||||
Coin txFee;
|
||||
|
||||
initialEstimatedTxVsize = 175;
|
||||
txFeePerVbyte = Coin.valueOf(10);
|
||||
realTxVsize = 175;
|
||||
|
||||
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
|
||||
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
|
||||
result = TxFeeEstimationService.getEstimatedTxVsize(outputValues, initialEstimatedTxVsize, txFeePerVbyte, btcWalletService);
|
||||
assertEquals(175, result);
|
||||
}
|
||||
|
||||
// FIXME @Bernard could you have a look?
|
||||
@Test
|
||||
@Ignore
|
||||
public void testGetEstimatedTxVsize_withLargeTx() throws InsufficientMoneyException {
|
||||
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
|
||||
int initialEstimatedTxVsize;
|
||||
Coin txFeePerVbyte;
|
||||
BtcWalletService btcWalletService = mock(BtcWalletService.class);
|
||||
int result;
|
||||
int realTxVsize;
|
||||
Coin txFee;
|
||||
|
||||
initialEstimatedTxVsize = 175;
|
||||
txFeePerVbyte = Coin.valueOf(10);
|
||||
realTxVsize = 1750;
|
||||
|
||||
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
|
||||
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
|
||||
|
||||
// repeated calls to getEstimatedFeeTxVsize do not work (returns 0 at second call in loop which cause test to fail)
|
||||
result = TxFeeEstimationService.getEstimatedTxVsize(outputValues, initialEstimatedTxVsize, txFeePerVbyte, btcWalletService);
|
||||
assertEquals(1750, result);
|
||||
}
|
||||
|
||||
// FIXME @Bernard could you have a look?
|
||||
@Test
|
||||
@Ignore
|
||||
public void testGetEstimatedTxVsize_withSmallTx() throws InsufficientMoneyException {
|
||||
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
|
||||
int initialEstimatedTxVsize;
|
||||
Coin txFeePerVbyte;
|
||||
BtcWalletService btcWalletService = mock(BtcWalletService.class);
|
||||
int result;
|
||||
int realTxVsize;
|
||||
Coin txFee;
|
||||
|
||||
initialEstimatedTxVsize = 1750;
|
||||
txFeePerVbyte = Coin.valueOf(10);
|
||||
realTxVsize = 175;
|
||||
|
||||
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
|
||||
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
|
||||
result = TxFeeEstimationService.getEstimatedTxVsize(outputValues, initialEstimatedTxVsize, txFeePerVbyte, btcWalletService);
|
||||
assertEquals(175, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInTolerance() {
|
||||
int estimatedSize;
|
||||
int txVsize;
|
||||
double tolerance;
|
||||
boolean result;
|
||||
|
||||
estimatedSize = 100;
|
||||
txVsize = 100;
|
||||
tolerance = 0.0001;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertTrue(result);
|
||||
|
||||
estimatedSize = 100;
|
||||
txVsize = 200;
|
||||
tolerance = 0.2;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertFalse(result);
|
||||
|
||||
estimatedSize = 120;
|
||||
txVsize = 100;
|
||||
tolerance = 0.2;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertTrue(result);
|
||||
|
||||
estimatedSize = 200;
|
||||
txVsize = 100;
|
||||
tolerance = 1;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertTrue(result);
|
||||
|
||||
estimatedSize = 201;
|
||||
txVsize = 100;
|
||||
tolerance = 1;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertFalse(result);
|
||||
}
|
||||
}
|
|
@ -61,7 +61,7 @@ public class PreferencesTest {
|
|||
Config config = new Config();
|
||||
LocalBitcoinNode localBitcoinNode = new LocalBitcoinNode(config);
|
||||
preferences = new Preferences(
|
||||
persistenceManager, config, null, localBitcoinNode, null);
|
||||
persistenceManager, config, localBitcoinNode, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue