mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 07:07:43 +01:00
Use new tx and trade fees. UI improvements, remove trade fee from provider data
This commit is contained in:
parent
e31dac1e49
commit
0580e73179
16 changed files with 164 additions and 124 deletions
|
@ -31,28 +31,8 @@ public class FeePolicy {
|
|||
// https://estimatefee.appspot.com/
|
||||
// Average values are 10-100 satoshis/byte in january 2016
|
||||
// Average values are 60-140 satoshis/byte in february 2017
|
||||
//
|
||||
// Our trade transactions have a fixed set of inputs and outputs making the size very predictable
|
||||
// (as long the user does not do multiple funding transactions)
|
||||
//
|
||||
// trade fee tx: 226 bytes // 221 satoshi/byte
|
||||
// deposit tx: 336 bytes // 148 satoshi/byte
|
||||
// payout tx: 371 bytes // 134 satoshi/byte
|
||||
// disputed payout tx: 408 bytes // 122 satoshi/byte
|
||||
|
||||
// We set a fixed fee to make the needed amounts in the trade predictable.
|
||||
// We use 0.0005 BTC (0.5 EUR @ 1000 EUR/BTC) which is for our tx sizes about 120-220 satoshi/byte
|
||||
// We cannot make that user defined as it need to be the same for both users, so we can only change that in
|
||||
// software updates
|
||||
// TODO before Beta we should get a good future proof guess as a change causes incompatible versions
|
||||
|
||||
// For non trade transactions (withdrawal) we use the default fee calculation
|
||||
// To avoid issues with not getting into full blocks, we increase the fee/kb to 30 satoshi/byte
|
||||
// The user can change that in the preferences
|
||||
// The BitcoinJ fee calculation use kb so a tx size < 1kb will still pay the fee for a kb tx.
|
||||
// Our payout tx has about 370 bytes so we get a fee/kb value of about 90 satoshi/byte making it high priority
|
||||
// Other payout transactions (E.g. arbitrators many collected transactions) will go with 30 satoshi/byte if > 1kb
|
||||
private static Coin NON_TRADE_FEE_PER_KB = Coin.valueOf(40_000); // 0.0004 BTC about 0.16 EUR @ 400 EUR/BTC
|
||||
private static Coin NON_TRADE_FEE_PER_KB = Coin.valueOf(150_000);
|
||||
|
||||
public static void setNonTradeFeePerKb(Coin nonTradeFeePerKb) {
|
||||
NON_TRADE_FEE_PER_KB = nonTradeFeePerKb;
|
||||
|
@ -62,4 +42,8 @@ public class FeePolicy {
|
|||
return NON_TRADE_FEE_PER_KB;
|
||||
}
|
||||
|
||||
public static Coin getDefaultSecurityDeposit() {
|
||||
return Coin.valueOf(3_000_000);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,12 +7,8 @@ public class FeeData {
|
|||
private static final Logger log = LoggerFactory.getLogger(FeeData.class);
|
||||
|
||||
public final long txFeePerByte;
|
||||
public final long createOfferFee;
|
||||
public final long takeOfferFee;
|
||||
|
||||
public FeeData(long txFeePerByte, long createOfferFee, long takeOfferFee) {
|
||||
public FeeData(long txFeePerByte) {
|
||||
this.txFeePerByte = txFeePerByte;
|
||||
this.createOfferFee = createOfferFee;
|
||||
this.takeOfferFee = takeOfferFee;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.io.IOException;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
//TODO use protobuffer instead of json
|
||||
public class FeeProvider extends HttpClientProvider {
|
||||
private static final Logger log = LoggerFactory.getLogger(FeeProvider.class);
|
||||
|
||||
|
@ -28,7 +29,7 @@ public class FeeProvider extends HttpClientProvider {
|
|||
tsMap.put("bitcoinFeesTs", ((Double) linkedTreeMap.get("bitcoinFeesTs")).longValue());
|
||||
|
||||
LinkedTreeMap<String, Double> dataMap = (LinkedTreeMap<String, Double>) linkedTreeMap.get("data");
|
||||
FeeData feeData = new FeeData(dataMap.get("txFee").longValue(), dataMap.get("createOfferFee").longValue(), dataMap.get("takeOfferFee").longValue());
|
||||
FeeData feeData = new FeeData(dataMap.get("txFee").longValue());
|
||||
return new Tuple2<>(tsMap, feeData);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import com.google.common.util.concurrent.FutureCallback;
|
|||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.app.Log;
|
||||
import io.bitsquare.btc.provider.ProvidersRepository;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.handlers.FaultHandler;
|
||||
|
@ -33,6 +32,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
@ -40,22 +40,28 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
public class FeeService {
|
||||
private static final Logger log = LoggerFactory.getLogger(FeeService.class);
|
||||
|
||||
public static final long MIN_TX_FEE = 40; // satoshi/byte
|
||||
public static final long MAX_TX_FEE = 200;
|
||||
public static final long DEFAULT_TX_FEE = 100;
|
||||
public static final long MIN_TX_FEE = 60; // satoshi/byte
|
||||
public static final long MAX_TX_FEE = 300;
|
||||
public static final long DEFAULT_TX_FEE = 150;
|
||||
|
||||
public static final long MIN_CREATE_OFFER_FEE = 10_000;
|
||||
public static final long MAX_CREATE_OFFER_FEE = 500_000;
|
||||
public static final long DEFAULT_CREATE_OFFER_FEE = 30_000;
|
||||
public static final long MIN_CREATE_OFFER_FEE_IN_BTC = 10_000;
|
||||
public static final long MAX_CREATE_OFFER_FEE_IN_BTC = 500_000;
|
||||
public static final long DEFAULT_CREATE_OFFER_FEE_IN_BTC = 30_000; // excluded mining fee
|
||||
|
||||
public static final long MIN_TAKE_OFFER_FEE = 10_000;
|
||||
public static final long MAX_TAKE_OFFER_FEE = 1000_000;
|
||||
public static final long DEFAULT_TAKE_OFFER_FEE = 80_000;
|
||||
public static final long MIN_TAKE_OFFER_FEE_IN_BTC = 10_000;
|
||||
public static final long MAX_TAKE_OFFER_FEE_IN_BTC = 1000_000;
|
||||
public static final long DEFAULT_TAKE_OFFER_FEE_IN_BTC = 30_000; // excluded mining fee
|
||||
|
||||
// 0.00216 btc is for 3 x tx fee for taker -> about 2 EUR!
|
||||
|
||||
public static final long MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN = 10;
|
||||
|
||||
private final FeeProvider feeProvider;
|
||||
@Nullable
|
||||
private FeeData feeData;
|
||||
private Map<String, Long> timeStampMap;
|
||||
private long epochInSecondAtLastRequest;
|
||||
private long lastRequest;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
|
@ -65,7 +71,6 @@ public class FeeService {
|
|||
public FeeService(HttpClient httpClient,
|
||||
ProvidersRepository providersRepository) {
|
||||
this.feeProvider = new FeeProvider(httpClient, providersRepository.getBaseUrl());
|
||||
feeData = new FeeData(DEFAULT_TX_FEE, DEFAULT_CREATE_OFFER_FEE, DEFAULT_TAKE_OFFER_FEE);
|
||||
}
|
||||
|
||||
public void onAllServicesInitialized() {
|
||||
|
@ -73,30 +78,39 @@ public class FeeService {
|
|||
}
|
||||
|
||||
public void requestFees(@Nullable Runnable resultHandler, @Nullable FaultHandler faultHandler) {
|
||||
//TODO add throttle
|
||||
Log.traceCall();
|
||||
FeeRequest feeRequest = new FeeRequest();
|
||||
SettableFuture<Tuple2<Map<String, Long>, FeeData>> future = feeRequest.getFees(feeProvider);
|
||||
Futures.addCallback(future, new FutureCallback<Tuple2<Map<String, Long>, FeeData>>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Tuple2<Map<String, Long>, FeeData> result) {
|
||||
UserThread.execute(() -> {
|
||||
checkNotNull(result, "Result must not be null at getFees");
|
||||
timeStampMap = result.first;
|
||||
epochInSecondAtLastRequest = timeStampMap.get("bitcoinFeesTs");
|
||||
feeData = result.second;
|
||||
if (resultHandler != null)
|
||||
resultHandler.run();
|
||||
});
|
||||
}
|
||||
long now = Instant.now().getEpochSecond();
|
||||
if (feeData == null || 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>>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Tuple2<Map<String, Long>, FeeData> 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);
|
||||
if (resultHandler != null)
|
||||
resultHandler.run();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
log.warn("Could not load fees. " + throwable.toString());
|
||||
if (faultHandler != null)
|
||||
UserThread.execute(() -> faultHandler.handleFault("Could not load fees", throwable));
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
log.warn("Could not load fees. " + throwable.toString());
|
||||
if (faultHandler != null)
|
||||
UserThread.execute(() -> faultHandler.handleFault("Could not load fees", throwable));
|
||||
}
|
||||
});
|
||||
} 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 sizeInBytes) {
|
||||
|
@ -104,16 +118,20 @@ public class FeeService {
|
|||
}
|
||||
|
||||
public Coin getTxFeePerByte() {
|
||||
log.info("txFeePerByte = " + feeData.txFeePerByte);
|
||||
return Coin.valueOf(feeData.txFeePerByte);
|
||||
if (feeData != null)
|
||||
return Coin.valueOf(feeData.txFeePerByte);
|
||||
else
|
||||
return Coin.valueOf(DEFAULT_TX_FEE);
|
||||
}
|
||||
|
||||
public Coin getCreateOfferFee() {
|
||||
return Coin.valueOf(feeData.createOfferFee);
|
||||
// TODO we will get that from the DAO voting
|
||||
public Coin getCreateOfferFeeInBtc() {
|
||||
return Coin.valueOf(DEFAULT_CREATE_OFFER_FEE_IN_BTC);
|
||||
}
|
||||
|
||||
public Coin getTakeOfferFee() {
|
||||
return Coin.valueOf(feeData.takeOfferFee);
|
||||
// TODO we will get that from the DAO voting
|
||||
public Coin getTakeOfferFeeInBtc() {
|
||||
return Coin.valueOf(DEFAULT_TAKE_OFFER_FEE_IN_BTC);
|
||||
}
|
||||
|
||||
public Coin getCreateCompensationRequestFee() {
|
||||
|
|
|
@ -73,18 +73,18 @@ abstract public class SquBlockchainService {
|
|||
String genesisTxId)
|
||||
throws SquBlockchainException {
|
||||
try {
|
||||
log.info("blockCount=" + chainHeadHeight);
|
||||
//log.info("blockCount=" + chainHeadHeight);
|
||||
long startTs = System.currentTimeMillis();
|
||||
for (int blockHeight = genesisBlockHeight; blockHeight <= chainHeadHeight; blockHeight++) {
|
||||
Block block = requestBlock(blockHeight);
|
||||
log.info("blockHeight=" + blockHeight);
|
||||
//log.info("blockHeight=" + blockHeight);
|
||||
parseBlock(new SquBlock(block.getTx(), block.getHeight()),
|
||||
genesisBlockHeight,
|
||||
genesisTxId,
|
||||
utxoByTxIdMap);
|
||||
}
|
||||
printUtxoMap(utxoByTxIdMap);
|
||||
log.info("Took {} ms", System.currentTimeMillis() - startTs);
|
||||
// log.info("Took {} ms", System.currentTimeMillis() - startTs);
|
||||
} catch (BitcoindException | CommunicationException e) {
|
||||
throw new SquBlockchainException(e.getMessage(), e);
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ abstract public class SquBlockchainService {
|
|||
for (SquTransaction transaction : connectedTxs) {
|
||||
verifyTransaction(transaction, blockHeight, utxoByTxIdMap);
|
||||
}
|
||||
log.info("orphanTxs " + orphanTxs);
|
||||
//log.info("orphanTxs " + orphanTxs);
|
||||
if (!orphanTxs.isEmpty() && recursions < maxRecursions)
|
||||
resolveConnectedTxs(orphanTxs, utxoByTxIdMap, blockHeight, ++recursions, maxRecursions);
|
||||
}
|
||||
|
@ -286,6 +286,6 @@ abstract public class SquBlockchainService {
|
|||
.append(a.getValue().toString()).append("}\n");
|
||||
});
|
||||
});
|
||||
log.info(sb.toString());
|
||||
//log.info(sb.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import io.bitsquare.app.DevFlags;
|
|||
import io.bitsquare.app.Version;
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.btc.provider.fee.FeeService;
|
||||
import io.bitsquare.btc.provider.price.PriceFeedService;
|
||||
|
@ -143,8 +144,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
|||
|
||||
useMarketBasedPrice.set(preferences.getUsePercentageBasedPrice());
|
||||
|
||||
// TODO add ui for editing
|
||||
securityDepositAsCoin = Coin.valueOf(1_000_000);
|
||||
// TODO add ui for editing, use preferences
|
||||
securityDepositAsCoin = FeePolicy.getDefaultSecurityDeposit();
|
||||
|
||||
balanceListener = new BalanceListener(getAddressEntry().getAddress()) {
|
||||
@Override
|
||||
|
@ -255,14 +256,14 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
|||
// not too many inputs.
|
||||
|
||||
// trade fee tx: 226 bytes (1 input) - 374 bytes (2 inputs)
|
||||
feeService.requestFees(() -> {
|
||||
createOfferFeeAsCoin = feeService.getCreateOfferFee();
|
||||
txFeeAsCoin = feeService.getTxFee(400);
|
||||
calculateTotalToPay();
|
||||
}, null);
|
||||
|
||||
createOfferFeeAsCoin = feeService.getCreateOfferFee();
|
||||
// Set the default values (in rare cases if the fee request was not done yet we get the hard coded default values)
|
||||
// But offer creation happens usually after that so we should have already the value from the estimation service.
|
||||
txFeeAsCoin = feeService.getTxFee(400);
|
||||
createOfferFeeAsCoin = feeService.getCreateOfferFeeInBtc();
|
||||
|
||||
// We request to get the actual estimated fee
|
||||
requestTxFee();
|
||||
|
||||
calculateVolume();
|
||||
calculateTotalToPay();
|
||||
|
@ -419,6 +420,13 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
|||
this.marketPriceMargin = marketPriceMargin;
|
||||
}
|
||||
|
||||
void requestTxFee() {
|
||||
feeService.requestFees(() -> {
|
||||
txFeeAsCoin = feeService.getTxFee(400);
|
||||
createOfferFeeAsCoin = feeService.getCreateOfferFeeInBtc();
|
||||
calculateTotalToPay();
|
||||
}, null);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
|
|
|
@ -334,7 +334,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
.show();
|
||||
|
||||
key = "createOfferFundWalletInfo";
|
||||
String tradeAmountText = model.isSellOffer() ? "- Trade amount: " + model.tradeAmount.get() + "\n" : "";
|
||||
String tradeAmountText = model.isSellOffer() ? "- Trade amount: " + model.getTradeAmount() + "\n" : "";
|
||||
|
||||
new Popup().headLine("Fund your offer").instruction("You need to deposit " +
|
||||
model.totalToPay.get() + " to this offer.\n\n" +
|
||||
|
@ -345,7 +345,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
"The amount is the sum of:\n" +
|
||||
tradeAmountText +
|
||||
"- Security deposit: " + model.getSecurityDeposit() + "\n" +
|
||||
"- Trading fee: " + model.getOfferFee() + "\n" +
|
||||
"- Trading fee: " + model.getCreateOfferFee() + "\n" +
|
||||
"- Mining fee: " + model.getTxFee() + "\n\n" +
|
||||
|
||||
"You can choose between two options when funding your trade:\n" +
|
||||
|
@ -1078,7 +1078,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
addPayInfoEntry(infoGridPane, i++, BSResources.get("createOffer.fundsBox.tradeAmount"), model.tradeAmount.get());
|
||||
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("createOffer.fundsBox.securityDeposit"), model.getSecurityDeposit());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("createOffer.fundsBox.offerFee"), model.getOfferFee());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("createOffer.fundsBox.offerFee"), model.getCreateOfferFee());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("createOffer.fundsBox.networkFee"), model.getTxFee());
|
||||
Separator separator = new Separator();
|
||||
separator.setOrientation(Orientation.HORIZONTAL);
|
||||
|
@ -1099,6 +1099,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
private void addPayInfoEntry(GridPane infoGridPane, int row, String labelText, String value) {
|
||||
Label label = new Label(labelText);
|
||||
TextField textField = new TextField(value);
|
||||
textField.setMinWidth(300);
|
||||
textField.setEditable(false);
|
||||
textField.setFocusTraversable(false);
|
||||
textField.setId("payment-info");
|
||||
|
|
|
@ -36,6 +36,7 @@ import io.bitsquare.gui.main.overlays.popups.Popup;
|
|||
import io.bitsquare.gui.main.settings.SettingsView;
|
||||
import io.bitsquare.gui.main.settings.preferences.PreferencesView;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.GUIUtil;
|
||||
import io.bitsquare.gui.util.validation.BtcValidator;
|
||||
import io.bitsquare.gui.util.validation.FiatValidator;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
|
@ -495,6 +496,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
}
|
||||
|
||||
void onShowPayFundsScreen() {
|
||||
dataModel.requestTxFee();
|
||||
showPayFundsScreenDisplayed.set(true);
|
||||
updateSpinnerInfo();
|
||||
}
|
||||
|
@ -659,16 +661,23 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
return dataModel.getTradeCurrency();
|
||||
}
|
||||
|
||||
public String getOfferFee() {
|
||||
return formatter.formatCoinWithCode(dataModel.getCreateOfferFeeAsCoin());
|
||||
}
|
||||
|
||||
public String getTxFee() {
|
||||
return formatter.formatCoinWithCode(dataModel.getTxFeeAsCoin());
|
||||
public String getTradeAmount() {
|
||||
return formatter.formatCoinWithCode(dataModel.amount.get());
|
||||
}
|
||||
|
||||
public String getSecurityDeposit() {
|
||||
return formatter.formatCoinWithCode(dataModel.getSecurityDepositAsCoin());
|
||||
return formatter.formatCoinWithCode(dataModel.getSecurityDepositAsCoin()) +
|
||||
GUIUtil.getPercentageOfTradeAmount(dataModel.getSecurityDepositAsCoin(), dataModel.amount.get(), formatter);
|
||||
}
|
||||
|
||||
public String getCreateOfferFee() {
|
||||
return formatter.formatCoinWithCode(dataModel.getCreateOfferFeeAsCoin()) +
|
||||
GUIUtil.getPercentageOfTradeAmount(dataModel.getCreateOfferFeeAsCoin(), dataModel.amount.get(), formatter);
|
||||
}
|
||||
|
||||
public String getTxFee() {
|
||||
return formatter.formatCoinWithCode(dataModel.getTxFeeAsCoin()) +
|
||||
GUIUtil.getPercentageOfTradeAmount(dataModel.getTxFeeAsCoin(), dataModel.amount.get(), formatter);
|
||||
}
|
||||
|
||||
public PaymentAccount getPaymentAccount() {
|
||||
|
|
|
@ -174,7 +174,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
|
||||
// Taker pays 2 times the tx fee because the mining fee might be different when offerer 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
|
||||
// The mining fee for the takeOfferFee tx is deducted from the takeOfferFee and not visible to the trader
|
||||
|
||||
// The taker pays the mining fee for the trade fee tx and the trade txs.
|
||||
// A typical trade fee tx has about 226 bytes (if one input). The trade txs has about 336-414 bytes.
|
||||
|
@ -190,19 +190,17 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
// trade fee tx: 226 bytes (1 input) - 374 bytes (2 inputs)
|
||||
// deposit tx: 336 bytes (1 MS output+ OP_RETURN) - 414 bytes (1 MS output + OP_RETURN + change in case of smaller trade amount)
|
||||
// payout tx: 371 bytes
|
||||
// disputed payout tx: 408 bytes
|
||||
feeService.requestFees(() -> {
|
||||
//TODO update doubleTxFeeAsCoin and txFeeAsCoin in view with binding
|
||||
takerFeeAsCoin = feeService.getTakeOfferFee();
|
||||
txFeeAsCoin = feeService.getTxFee(400);
|
||||
totalTxFeeAsCoin = txFeeAsCoin.multiply(3);
|
||||
calculateTotalToPay();
|
||||
}, null);
|
||||
// disputed payout tx: 408 bytes
|
||||
|
||||
takerFeeAsCoin = feeService.getTakeOfferFee();
|
||||
// Set the default values (in rare cases if the fee request was not done yet we get the hard coded default values)
|
||||
// But the "take offer" happens usually after that so we should have already the value from the estimation service.
|
||||
takerFeeAsCoin = feeService.getTakeOfferFeeInBtc();
|
||||
txFeeAsCoin = feeService.getTxFee(400);
|
||||
totalTxFeeAsCoin = txFeeAsCoin.multiply(3);
|
||||
|
||||
// We request to get the actual estimated fee
|
||||
requestTxFee();
|
||||
|
||||
calculateVolume();
|
||||
calculateTotalToPay();
|
||||
|
||||
|
@ -244,6 +242,15 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
priceFeedService.setCurrencyCode(offer.getCurrencyCode());
|
||||
}
|
||||
|
||||
void requestTxFee() {
|
||||
feeService.requestFees(() -> {
|
||||
takerFeeAsCoin = feeService.getTakeOfferFeeInBtc();
|
||||
txFeeAsCoin = feeService.getTxFee(400);
|
||||
totalTxFeeAsCoin = txFeeAsCoin.multiply(3);
|
||||
calculateTotalToPay();
|
||||
}, null);
|
||||
}
|
||||
|
||||
void onTabSelected(boolean isSelected) {
|
||||
this.isTabSelected = isSelected;
|
||||
if (!preferences.getUseStickyMarketPrice() && isTabSelected)
|
||||
|
@ -258,11 +265,12 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
// errorMessageHandler is used only in the check availability phase. As soon we have a trade we write the error msg in the trade object as we want to
|
||||
// have it persisted as well.
|
||||
void onTakeOffer(TradeResultHandler tradeResultHandler) {
|
||||
Coin fundsNeededForTrade = totalToPayAsCoin.get().subtract(takerFeeAsCoin).subtract(txFeeAsCoin);
|
||||
tradeManager.onTakeOffer(amountAsCoin.get(),
|
||||
txFeeAsCoin,
|
||||
takerFeeAsCoin,
|
||||
tradePrice.getValue(),
|
||||
totalToPayAsCoin.get().subtract(takerFeeAsCoin).subtract(txFeeAsCoin),
|
||||
fundsNeededForTrade,
|
||||
offer,
|
||||
paymentAccount.getId(),
|
||||
useSavingsWallet,
|
||||
|
@ -460,6 +468,10 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
return totalTxFeeAsCoin;
|
||||
}
|
||||
|
||||
public Coin getTxFeeAsCoin() {
|
||||
return txFeeAsCoin;
|
||||
}
|
||||
|
||||
public AddressEntry getAddressEntry() {
|
||||
return addressEntry;
|
||||
}
|
||||
|
|
|
@ -340,7 +340,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
.show();
|
||||
|
||||
key = "takeOfferFundWalletInfo";
|
||||
String tradeAmountText = model.isSeller() ? "- Trade amount: " + model.getAmount() + "\n" : "";
|
||||
String tradeAmountText = model.isSeller() ? "- Trade amount: " + model.getTradeAmount() + "\n" : "";
|
||||
new Popup().headLine("Fund your trade").instruction("You need to deposit " +
|
||||
model.totalToPay.get() + " for taking this offer.\n\n" +
|
||||
|
||||
|
@ -348,7 +348,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
tradeAmountText +
|
||||
"- Security deposit: " + model.getSecurityDeposit() + "\n" +
|
||||
"- Trading fee: " + model.getTakerFee() + "\n" +
|
||||
"- Bitcoin mining fee: " + model.getNetworkFee() + "\n\n" +
|
||||
"- Mining fee (3x): " + model.getTxFee() + "\n\n" +
|
||||
|
||||
"You can choose between two options when funding your trade:\n" +
|
||||
"- Use your Bitsquare wallet (convenient, but transactions may be linkable) OR\n" +
|
||||
|
@ -977,11 +977,11 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
|
||||
int i = 0;
|
||||
if (model.isSeller())
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.tradeAmount"), model.getAmount());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.tradeAmount"), model.getTradeAmount());
|
||||
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.securityDeposit"), model.getSecurityDeposit());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.offerFee"), model.getTakerFee());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.networkFee"), model.getNetworkFee());
|
||||
addPayInfoEntry(infoGridPane, i++, BSResources.get("takeOffer.fundsBox.networkFee"), model.getTxFee());
|
||||
Separator separator = new Separator();
|
||||
separator.setOrientation(Orientation.HORIZONTAL);
|
||||
separator.setStyle("-fx-background: #666;");
|
||||
|
@ -1002,6 +1002,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
private void addPayInfoEntry(GridPane infoGridPane, int row, String labelText, String value) {
|
||||
Label label = new Label(labelText);
|
||||
TextField textField = new TextField(value);
|
||||
textField.setMinWidth(300);
|
||||
textField.setEditable(false);
|
||||
textField.setFocusTraversable(false);
|
||||
textField.setId("payment-info");
|
||||
|
|
|
@ -27,6 +27,7 @@ import io.bitsquare.gui.main.funds.FundsView;
|
|||
import io.bitsquare.gui.main.funds.deposit.DepositView;
|
||||
import io.bitsquare.gui.main.overlays.popups.Popup;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.GUIUtil;
|
||||
import io.bitsquare.gui.util.validation.BtcValidator;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
|
@ -208,6 +209,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
}
|
||||
|
||||
public void onShowPayFundsScreen() {
|
||||
dataModel.requestTxFee();
|
||||
showPayFundsScreenDisplayed.set(true);
|
||||
updateSpinnerInfo();
|
||||
}
|
||||
|
@ -569,22 +571,26 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
return amountDescription;
|
||||
}
|
||||
|
||||
String getAmount() {
|
||||
String getTradeAmount() {
|
||||
return formatter.formatCoinWithCode(dataModel.amountAsCoin.get());
|
||||
}
|
||||
|
||||
String getTakerFee() {
|
||||
return formatter.formatCoinWithCode(dataModel.getTakerFeeAsCoin());
|
||||
}
|
||||
|
||||
String getNetworkFee() {
|
||||
return formatter.formatCoinWithCode(dataModel.getTotalTxFeeAsCoin());
|
||||
}
|
||||
|
||||
public String getSecurityDeposit() {
|
||||
return formatter.formatCoinWithCode(dataModel.getSecurityDepositAsCoin());
|
||||
return formatter.formatCoinWithCode(dataModel.getSecurityDepositAsCoin()) +
|
||||
GUIUtil.getPercentageOfTradeAmount(dataModel.getSecurityDepositAsCoin(), dataModel.amountAsCoin.get(), formatter);
|
||||
}
|
||||
|
||||
public String getTakerFee() {
|
||||
return formatter.formatCoinWithCode(dataModel.getTakerFeeAsCoin()) +
|
||||
GUIUtil.getPercentageOfTradeAmount(dataModel.getTakerFeeAsCoin(), dataModel.amountAsCoin.get(), formatter);
|
||||
}
|
||||
|
||||
public String getTxFee() {
|
||||
return formatter.formatCoinWithCode(dataModel.getTotalTxFeeAsCoin()) +
|
||||
GUIUtil.getPercentageOfTradeAmount(dataModel.getTotalTxFeeAsCoin(), dataModel.amountAsCoin.get(), formatter);
|
||||
}
|
||||
|
||||
|
||||
public PaymentMethod getPaymentMethod() {
|
||||
return dataModel.getPaymentMethod();
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package io.bitsquare.gui.main.overlays.windows;
|
||||
|
||||
import io.bitsquare.arbitration.DisputeManager;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.gui.components.TextFieldWithCopyIcon;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.overlays.Overlay;
|
||||
|
@ -131,7 +130,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
|
|||
addLabelTextField(gridPane, ++rowIndex, "Payment method:", BSResources.get(offer.getPaymentMethod().getId()));
|
||||
|
||||
// second group
|
||||
rows = 5;
|
||||
rows = 6;
|
||||
PaymentAccountContractData buyerPaymentAccountContractData = null;
|
||||
PaymentAccountContractData sellerPaymentAccountContractData = null;
|
||||
|
||||
|
@ -173,6 +172,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
|
|||
addLabelTextFieldWithCopyIcon(gridPane, rowIndex, "Trade ID:", trade.getId(), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
|
||||
addLabelTextField(gridPane, ++rowIndex, "Trade date:", formatter.formatDateTime(trade.getDate()));
|
||||
addLabelTextField(gridPane, ++rowIndex, "Security deposit:", formatter.formatCoinWithCode(offer.getSecurityDeposit()));
|
||||
addLabelTextField(gridPane, ++rowIndex, "Tx fee:", formatter.formatCoinWithCode(trade.getTxFee()));
|
||||
addLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, "Selected arbitrator:", trade.getArbitratorNodeAddress().getFullAddress());
|
||||
|
||||
if (trade.getTradingPeerNodeAddress() != null)
|
||||
|
|
|
@ -39,6 +39,7 @@ import javafx.stage.DirectoryChooser;
|
|||
import javafx.stage.FileChooser;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.StringConverter;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -300,4 +301,9 @@ public class GUIUtil {
|
|||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getPercentageOfTradeAmount(Coin fee, Coin tradeAmount, BSFormatter formatter) {
|
||||
return " (" + formatter.formatToPercentWithSymbol((double) fee.value / (double) tradeAmount.value) +
|
||||
" of trade amount)";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ takeOffer.fundsBox.sell.info=For every offer there is a dedicated trade wallet.
|
|||
takeOffer.fundsBox.tradeAmount=Amount to sell:
|
||||
takeOffer.fundsBox.securityDeposit=Security deposit:
|
||||
takeOffer.fundsBox.offerFee=Take offer fee:
|
||||
takeOffer.fundsBox.networkFee=Mining fee:
|
||||
takeOffer.fundsBox.networkFee=Mining fees (3x):
|
||||
takeOffer.fundsBox.total=Total:
|
||||
takeOffer.fundsBox.showAdvanced=Show advanced settings
|
||||
takeOffer.fundsBox.hideAdvanced=Hide advanced settings
|
||||
|
|
|
@ -32,6 +32,7 @@ import java.util.Timer;
|
|||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
//TODO use protobuffer instead of json
|
||||
public class FeeRequestService {
|
||||
private static final Logger log = LoggerFactory.getLogger(FeeRequestService.class);
|
||||
|
||||
|
@ -47,10 +48,7 @@ public class FeeRequestService {
|
|||
public FeeRequestService() throws IOException {
|
||||
btcFeesProvider = new BtcFeesProvider();
|
||||
|
||||
allFeesMap.put("txFee", FeeService.DEFAULT_TX_FEE);
|
||||
allFeesMap.put("createOfferFee", FeeService.DEFAULT_CREATE_OFFER_FEE);
|
||||
allFeesMap.put("takeOfferFee", FeeService.DEFAULT_TAKE_OFFER_FEE);
|
||||
|
||||
writeToJson();
|
||||
startRequests();
|
||||
}
|
||||
|
||||
|
@ -101,5 +99,4 @@ public class FeeRequestService {
|
|||
public String getJson() {
|
||||
return json;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.io.IOException;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
//TODO use protobuffer instead of json
|
||||
public class BtcFeesProvider {
|
||||
private static final Logger log = LoggerFactory.getLogger(BtcFeesProvider.class);
|
||||
|
||||
|
@ -23,7 +24,7 @@ public class BtcFeesProvider {
|
|||
|
||||
public Long getFee() throws IOException, HttpException {
|
||||
String response = httpClient.requestWithGET("recommended", "User-Agent", "");
|
||||
log.debug("Get recommended fee response: " + response);
|
||||
log.info("Get recommended fee response: " + response);
|
||||
Map<String, Long> map = new HashMap<>();
|
||||
LinkedTreeMap<String, Double> treeMap = new Gson().fromJson(response, LinkedTreeMap.class);
|
||||
treeMap.entrySet().stream().forEach(e -> map.put(e.getKey(), e.getValue().longValue()));
|
||||
|
|
Loading…
Add table
Reference in a new issue