mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-22 14:42:37 +01:00
Rename "tx size" to "tx vsize"
This commit is contained in:
parent
52f1d37863
commit
c4131c7398
35 changed files with 344 additions and 346 deletions
|
@ -72,7 +72,7 @@ public enum BaseCurrencyNetwork {
|
|||
return "BTC_REGTEST".equals(name());
|
||||
}
|
||||
|
||||
public long getDefaultMinFeePerByte() {
|
||||
public long getDefaultMinFeePerVbyte() {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ public class WalletAppSetup {
|
|||
String result;
|
||||
if (exception == null) {
|
||||
double percentage = (double) downloadPercentage;
|
||||
long fees = feeService.getTxFeePerByte().longValue();
|
||||
long fees = feeService.getTxFeePerVbyte().longValue();
|
||||
btcSyncProgress.set(percentage);
|
||||
if (percentage == 1) {
|
||||
String feeRate = Res.get("mainView.footer.btcFeeRate", fees);
|
||||
|
|
|
@ -55,8 +55,8 @@ public class TxFeeEstimationService {
|
|||
// 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_SIZE = 175;
|
||||
private static int DEPOSIT_TX_SIZE = 233;
|
||||
public static int TYPICAL_TX_WITH_1_INPUT_VSIZE = 175;
|
||||
private static int DEPOSIT_TX_VSIZE = 233;
|
||||
|
||||
private static int BSQ_INPUT_INCREASE = 150;
|
||||
private static int MAX_ITERATIONS = 10;
|
||||
|
@ -75,8 +75,8 @@ public class TxFeeEstimationService {
|
|||
this.preferences = preferences;
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxSizeForTaker(Coin fundsNeededForTrade, Coin tradeFee) {
|
||||
return getEstimatedFeeAndTxSize(true,
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsizeForTaker(Coin fundsNeededForTrade, Coin tradeFee) {
|
||||
return getEstimatedFeeAndTxVsize(true,
|
||||
fundsNeededForTrade,
|
||||
tradeFee,
|
||||
feeService,
|
||||
|
@ -84,9 +84,9 @@ public class TxFeeEstimationService {
|
|||
preferences);
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxSizeForMaker(Coin reservedFundsForOffer,
|
||||
Coin tradeFee) {
|
||||
return getEstimatedFeeAndTxSize(false,
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsizeForMaker(Coin reservedFundsForOffer,
|
||||
Coin tradeFee) {
|
||||
return getEstimatedFeeAndTxVsize(false,
|
||||
reservedFundsForOffer,
|
||||
tradeFee,
|
||||
feeService,
|
||||
|
@ -94,120 +94,120 @@ public class TxFeeEstimationService {
|
|||
preferences);
|
||||
}
|
||||
|
||||
private Tuple2<Coin, Integer> getEstimatedFeeAndTxSize(boolean isTaker,
|
||||
Coin amount,
|
||||
Coin tradeFee,
|
||||
FeeService feeService,
|
||||
BtcWalletService btcWalletService,
|
||||
Preferences preferences) {
|
||||
Coin txFeePerByte = feeService.getTxFeePerByte();
|
||||
// We start with min taker fee size of 175
|
||||
int estimatedTxSize = TYPICAL_TX_WITH_1_INPUT_SIZE;
|
||||
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 {
|
||||
estimatedTxSize = getEstimatedTxSize(List.of(tradeFee, amount), estimatedTxSize, txFeePerByte, btcWalletService);
|
||||
estimatedTxVsize = getEstimatedTxVsize(List.of(tradeFee, amount), estimatedTxVsize, txFeePerVbyte, btcWalletService);
|
||||
} catch (InsufficientMoneyException e) {
|
||||
if (isTaker) {
|
||||
// If we cannot do the estimation, we use the size o the largest of our txs which is the deposit tx.
|
||||
estimatedTxSize = DEPOSIT_TX_SIZE;
|
||||
// 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 size of {} bytes.", estimatedTxSize);
|
||||
"if the user pays from an external wallet. In that case we use an estimated tx vsize of {} vbytes.", estimatedTxVsize);
|
||||
}
|
||||
|
||||
if (!preferences.isPayFeeInBtc()) {
|
||||
// If we pay the fee in BSQ we have one input more which adds about 150 bytes
|
||||
// TODO: Clarify if there is always just one additional input or if there can be more.
|
||||
estimatedTxSize += BSQ_INPUT_INCREASE;
|
||||
estimatedTxVsize += BSQ_INPUT_INCREASE;
|
||||
}
|
||||
|
||||
Coin txFee;
|
||||
int size;
|
||||
int vsize;
|
||||
if (isTaker) {
|
||||
int averageSize = (estimatedTxSize + DEPOSIT_TX_SIZE) / 2; // deposit tx has about 233 bytes
|
||||
// We use at least the size of the deposit tx to not underpay it.
|
||||
size = Math.max(DEPOSIT_TX_SIZE, averageSize);
|
||||
txFee = txFeePerByte.multiply(size);
|
||||
log.info("Fee estimation resulted in a tx size of {} bytes.\n" +
|
||||
"We use an average between the taker fee tx and the deposit tx (233 bytes) which results in {} bytes.\n" +
|
||||
"The deposit tx has 233 bytes, we use that as our min value. Size for fee calculation is {} bytes.\n" +
|
||||
"The tx fee of {} Sat", estimatedTxSize, averageSize, size, txFee.value);
|
||||
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 {
|
||||
size = estimatedTxSize;
|
||||
txFee = txFeePerByte.multiply(size);
|
||||
log.info("Fee estimation resulted in a tx size of {} bytes and a tx fee of {} Sat.", size, txFee.value);
|
||||
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, size);
|
||||
return new Tuple2<>(txFee, vsize);
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxSize(Coin amount,
|
||||
FeeService feeService,
|
||||
BtcWalletService btcWalletService) {
|
||||
Coin txFeePerByte = feeService.getTxFeePerByte();
|
||||
// We start with min taker fee size of 175
|
||||
int estimatedTxSize = TYPICAL_TX_WITH_1_INPUT_SIZE;
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsize(Coin amount,
|
||||
FeeService feeService,
|
||||
BtcWalletService btcWalletService) {
|
||||
Coin txFeePerVbyte = feeService.getTxFeePerVbyte();
|
||||
// We start with min taker fee vsize of 175
|
||||
int estimatedTxVsize = TYPICAL_TX_WITH_1_INPUT_VSIZE;
|
||||
try {
|
||||
estimatedTxSize = getEstimatedTxSize(List.of(amount), estimatedTxSize, txFeePerByte, btcWalletService);
|
||||
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 size of {} bytes.", estimatedTxSize);
|
||||
"if the user pays from an external wallet. In that case we use an estimated tx vsize of {} vbytes.", estimatedTxVsize);
|
||||
}
|
||||
|
||||
Coin txFee = txFeePerByte.multiply(estimatedTxSize);
|
||||
log.info("Fee estimation resulted in a tx size of {} bytes and a tx fee of {} Sat.", estimatedTxSize, txFee.value);
|
||||
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, estimatedTxSize);
|
||||
return new Tuple2<>(txFee, estimatedTxVsize);
|
||||
}
|
||||
|
||||
// We start with the initialEstimatedTxSize for a tx with 1 input (175) bytes and get from BitcoinJ a tx back which
|
||||
// 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 size from the tx BitcoinJ has created
|
||||
// with the required inputs we compare if the size is not more then 20% different to our assumed tx size. If we are inside
|
||||
// that tolerance we use that tx size for our fee estimation, if not (if there has been more then 1 inputs) we
|
||||
// apply the new fee based on the reported tx size and request again from BitcoinJ to fill that tx with the inputs
|
||||
// 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 size). As the increased fee might
|
||||
// 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 size we got reported is > 20% off to
|
||||
// the real tx size but as fee estimation is anyway a educated guess in the best case we don't worry too much.
|
||||
// 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 getEstimatedTxSize(List<Coin> outputValues,
|
||||
int initialEstimatedTxSize,
|
||||
Coin txFeePerByte,
|
||||
BtcWalletService btcWalletService)
|
||||
static int getEstimatedTxVsize(List<Coin> outputValues,
|
||||
int initialEstimatedTxVsize,
|
||||
Coin txFeePerVbyte,
|
||||
BtcWalletService btcWalletService)
|
||||
throws InsufficientMoneyException {
|
||||
boolean isInTolerance;
|
||||
int estimatedTxSize = initialEstimatedTxSize;
|
||||
int realTxSize;
|
||||
int estimatedTxVsize = initialEstimatedTxVsize;
|
||||
int realTxVsize;
|
||||
int counter = 0;
|
||||
do {
|
||||
Coin txFee = txFeePerByte.multiply(estimatedTxSize);
|
||||
realTxSize = btcWalletService.getEstimatedFeeTxSize(outputValues, txFee);
|
||||
isInTolerance = isInTolerance(estimatedTxSize, realTxSize, 0.2);
|
||||
Coin txFee = txFeePerVbyte.multiply(estimatedTxVsize);
|
||||
realTxVsize = btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee);
|
||||
isInTolerance = isInTolerance(estimatedTxVsize, realTxVsize, 0.2);
|
||||
if (!isInTolerance) {
|
||||
estimatedTxSize = realTxSize;
|
||||
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%. " +
|
||||
"realTxSize={}, estimatedTxSize={}",
|
||||
realTxSize, estimatedTxSize);
|
||||
"realTxVsize={}, estimatedTxVsize={}",
|
||||
realTxVsize, estimatedTxVsize);
|
||||
}
|
||||
return estimatedTxSize;
|
||||
return estimatedTxVsize;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static boolean isInTolerance(int estimatedSize, int txSize, double tolerance) {
|
||||
checkArgument(estimatedSize > 0, "estimatedSize must be positive");
|
||||
checkArgument(txSize > 0, "txSize must be positive");
|
||||
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) estimatedSize / (double) txSize));
|
||||
double deviation = Math.abs(1 - ((double) estimatedVsize / (double) txVsize));
|
||||
return deviation <= tolerance;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,8 +220,8 @@ public class BtcWalletService extends WalletService {
|
|||
// estimated size of input sig
|
||||
int sigSizePerInput = 106;
|
||||
// typical size for a tx with 3 inputs
|
||||
int txSizeWithUnsignedInputs = 300;
|
||||
Coin txFeePerByte = feeService.getTxFeePerByte();
|
||||
int txVsizeWithUnsignedInputs = 300;
|
||||
Coin txFeePerVbyte = feeService.getTxFeePerVbyte();
|
||||
|
||||
Address changeAddress = getFreshAddressEntry().getAddress();
|
||||
checkNotNull(changeAddress, "changeAddress must not be null");
|
||||
|
@ -253,7 +253,7 @@ public class BtcWalletService extends WalletService {
|
|||
// signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet)
|
||||
sendRequest.signInputs = false;
|
||||
|
||||
sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs +
|
||||
sendRequest.fee = txFeePerVbyte.multiply(txVsizeWithUnsignedInputs +
|
||||
sigSizePerInput * numLegacyInputs +
|
||||
sigSizePerInput * numSegwitInputs / 4);
|
||||
|
||||
|
@ -272,8 +272,8 @@ public class BtcWalletService extends WalletService {
|
|||
numInputs = getNumInputs(resultTx);
|
||||
numLegacyInputs = numInputs.first;
|
||||
numSegwitInputs = numInputs.second;
|
||||
txSizeWithUnsignedInputs = resultTx.getVsize();
|
||||
long estimatedFeeAsLong = txFeePerByte.multiply(txSizeWithUnsignedInputs +
|
||||
txVsizeWithUnsignedInputs = resultTx.getVsize();
|
||||
long estimatedFeeAsLong = txFeePerVbyte.multiply(txVsizeWithUnsignedInputs +
|
||||
sigSizePerInput * numLegacyInputs +
|
||||
sigSizePerInput * numSegwitInputs / 4).value;
|
||||
|
||||
|
@ -340,8 +340,8 @@ public class BtcWalletService extends WalletService {
|
|||
// estimated size of input sig
|
||||
int sigSizePerInput = 106;
|
||||
// typical size for a tx with 3 inputs
|
||||
int txSizeWithUnsignedInputs = 300;
|
||||
Coin txFeePerByte = feeService.getTxFeePerByte();
|
||||
int txVsizeWithUnsignedInputs = 300;
|
||||
Coin txFeePerVbyte = feeService.getTxFeePerVbyte();
|
||||
|
||||
Address changeAddress = getFreshAddressEntry().getAddress();
|
||||
checkNotNull(changeAddress, "changeAddress must not be null");
|
||||
|
@ -373,7 +373,7 @@ public class BtcWalletService extends WalletService {
|
|||
// signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet)
|
||||
sendRequest.signInputs = false;
|
||||
|
||||
sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs +
|
||||
sendRequest.fee = txFeePerVbyte.multiply(txVsizeWithUnsignedInputs +
|
||||
sigSizePerInput * numLegacyInputs +
|
||||
sigSizePerInput * numSegwitInputs / 4);
|
||||
sendRequest.feePerKb = Coin.ZERO;
|
||||
|
@ -391,8 +391,8 @@ public class BtcWalletService extends WalletService {
|
|||
numInputs = getNumInputs(resultTx);
|
||||
numLegacyInputs = numInputs.first;
|
||||
numSegwitInputs = numInputs.second;
|
||||
txSizeWithUnsignedInputs = resultTx.getVsize();
|
||||
final long estimatedFeeAsLong = txFeePerByte.multiply(txSizeWithUnsignedInputs +
|
||||
txVsizeWithUnsignedInputs = resultTx.getVsize();
|
||||
final long estimatedFeeAsLong = txFeePerVbyte.multiply(txVsizeWithUnsignedInputs +
|
||||
sigSizePerInput * numLegacyInputs +
|
||||
sigSizePerInput * numSegwitInputs / 4).value;
|
||||
// calculated fee must be inside of a tolerance range with tx fee
|
||||
|
@ -486,9 +486,9 @@ public class BtcWalletService extends WalletService {
|
|||
// estimated size of input sig
|
||||
int sigSizePerInput = 106;
|
||||
// typical size for a tx with 2 inputs
|
||||
int txSizeWithUnsignedInputs = 203;
|
||||
int txVsizeWithUnsignedInputs = 203;
|
||||
// If useCustomTxFee we allow overriding the estimated fee from preferences
|
||||
Coin txFeePerByte = useCustomTxFee ? getTxFeeForWithdrawalPerByte() : feeService.getTxFeePerByte();
|
||||
Coin txFeePerVbyte = useCustomTxFee ? getTxFeeForWithdrawalPerVbyte() : feeService.getTxFeePerVbyte();
|
||||
// In case there are no change outputs we force a change by adding min dust to the BTC input
|
||||
Coin forcedChangeValue = Coin.ZERO;
|
||||
|
||||
|
@ -531,7 +531,7 @@ public class BtcWalletService extends WalletService {
|
|||
// signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet)
|
||||
sendRequest.signInputs = false;
|
||||
|
||||
sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs +
|
||||
sendRequest.fee = txFeePerVbyte.multiply(txVsizeWithUnsignedInputs +
|
||||
sigSizePerInput * numLegacyInputs +
|
||||
sigSizePerInput * numSegwitInputs / 4);
|
||||
sendRequest.feePerKb = Coin.ZERO;
|
||||
|
@ -556,8 +556,8 @@ public class BtcWalletService extends WalletService {
|
|||
Tuple2<Integer, Integer> numInputs = getNumInputs(resultTx);
|
||||
numLegacyInputs = numInputs.first;
|
||||
numSegwitInputs = numInputs.second;
|
||||
txSizeWithUnsignedInputs = resultTx.getVsize();
|
||||
final long estimatedFeeAsLong = txFeePerByte.multiply(txSizeWithUnsignedInputs +
|
||||
txVsizeWithUnsignedInputs = resultTx.getVsize();
|
||||
final long estimatedFeeAsLong = txFeePerVbyte.multiply(txVsizeWithUnsignedInputs +
|
||||
sigSizePerInput * numLegacyInputs +
|
||||
sigSizePerInput * numSegwitInputs / 4).value;
|
||||
// calculated fee must be inside of a tolerance range with tx fee
|
||||
|
@ -565,7 +565,7 @@ public class BtcWalletService extends WalletService {
|
|||
}
|
||||
while (opReturnIsOnlyOutput ||
|
||||
isFeeOutsideTolerance ||
|
||||
resultTx.getFee().value < txFeePerByte.multiply(resultTx.getVsize()).value);
|
||||
resultTx.getFee().value < txFeePerVbyte.multiply(resultTx.getVsize()).value);
|
||||
|
||||
// Sign all BTC inputs
|
||||
signAllBtcInputs(preparedBsqTxInputs.size(), resultTx);
|
||||
|
@ -857,7 +857,7 @@ public class BtcWalletService extends WalletService {
|
|||
);
|
||||
|
||||
log.info("newTransaction no. of inputs " + newTransaction.getInputs().size());
|
||||
log.info("newTransaction size in kB " + newTransaction.getVsize() / 1024);
|
||||
log.info("newTransaction vsize in vkB " + newTransaction.getVsize() / 1024);
|
||||
|
||||
if (!newTransaction.getInputs().isEmpty()) {
|
||||
Coin amount = Coin.valueOf(newTransaction.getInputs().stream()
|
||||
|
@ -868,13 +868,13 @@ public class BtcWalletService extends WalletService {
|
|||
try {
|
||||
Coin fee;
|
||||
int counter = 0;
|
||||
int txSize = 0;
|
||||
int txVsize = 0;
|
||||
Transaction tx;
|
||||
SendRequest sendRequest;
|
||||
Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte();
|
||||
Coin txFeeForWithdrawalPerVbyte = getTxFeeForWithdrawalPerVbyte();
|
||||
do {
|
||||
counter++;
|
||||
fee = txFeeForWithdrawalPerByte.multiply(txSize);
|
||||
fee = txFeeForWithdrawalPerVbyte.multiply(txVsize);
|
||||
newTransaction.clearOutputs();
|
||||
newTransaction.addOutput(amount.subtract(fee), toAddress);
|
||||
|
||||
|
@ -887,7 +887,7 @@ public class BtcWalletService extends WalletService {
|
|||
sendRequest.changeAddress = toAddress;
|
||||
wallet.completeTx(sendRequest);
|
||||
tx = sendRequest.tx;
|
||||
txSize = tx.getVsize();
|
||||
txVsize = tx.getVsize();
|
||||
printTx("FeeEstimationTransaction", tx);
|
||||
sendRequest.tx.getOutputs().forEach(o -> log.debug("Output value " + o.getValue().toFriendlyString()));
|
||||
}
|
||||
|
@ -986,16 +986,16 @@ public class BtcWalletService extends WalletService {
|
|||
try {
|
||||
Coin fee;
|
||||
int counter = 0;
|
||||
int txSize = 0;
|
||||
int txVsize = 0;
|
||||
Transaction tx;
|
||||
Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte();
|
||||
Coin txFeeForWithdrawalPerVbyte = getTxFeeForWithdrawalPerVbyte();
|
||||
do {
|
||||
counter++;
|
||||
fee = txFeeForWithdrawalPerByte.multiply(txSize);
|
||||
fee = txFeeForWithdrawalPerVbyte.multiply(txVsize);
|
||||
SendRequest sendRequest = getSendRequest(fromAddress, toAddress, amount, fee, aesKey, context);
|
||||
wallet.completeTx(sendRequest);
|
||||
tx = sendRequest.tx;
|
||||
txSize = tx.getVsize();
|
||||
txVsize = tx.getVsize();
|
||||
printTx("FeeEstimationTransaction", tx);
|
||||
}
|
||||
while (feeEstimationNotSatisfied(counter, tx));
|
||||
|
@ -1033,12 +1033,12 @@ public class BtcWalletService extends WalletService {
|
|||
try {
|
||||
Coin fee;
|
||||
int counter = 0;
|
||||
int txSize = 0;
|
||||
int txVsize = 0;
|
||||
Transaction tx;
|
||||
Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte();
|
||||
Coin txFeeForWithdrawalPerVbyte = getTxFeeForWithdrawalPerVbyte();
|
||||
do {
|
||||
counter++;
|
||||
fee = txFeeForWithdrawalPerByte.multiply(txSize);
|
||||
fee = txFeeForWithdrawalPerVbyte.multiply(txVsize);
|
||||
// We use a dummy address for the output
|
||||
// We don't know here whether the output is segwit or not but we don't care too much because the size of
|
||||
// a segwit ouput is just 3 byte smaller than the size of a legacy ouput.
|
||||
|
@ -1046,7 +1046,7 @@ public class BtcWalletService extends WalletService {
|
|||
SendRequest sendRequest = getSendRequestForMultipleAddresses(fromAddresses, dummyReceiver, amount, fee, null, aesKey);
|
||||
wallet.completeTx(sendRequest);
|
||||
tx = sendRequest.tx;
|
||||
txSize = tx.getVsize();
|
||||
txVsize = tx.getVsize();
|
||||
printTx("FeeEstimationTransactionForMultipleAddresses", tx);
|
||||
}
|
||||
while (feeEstimationNotSatisfied(counter, tx));
|
||||
|
@ -1062,13 +1062,13 @@ public class BtcWalletService extends WalletService {
|
|||
}
|
||||
|
||||
private boolean feeEstimationNotSatisfied(int counter, Transaction tx) {
|
||||
long targetFee = getTxFeeForWithdrawalPerByte().multiply(tx.getVsize()).value;
|
||||
long targetFee = getTxFeeForWithdrawalPerVbyte().multiply(tx.getVsize()).value;
|
||||
return counter < 10 &&
|
||||
(tx.getFee().value < targetFee ||
|
||||
tx.getFee().value - targetFee > 1000);
|
||||
}
|
||||
|
||||
public int getEstimatedFeeTxSize(List<Coin> outputValues, Coin txFee)
|
||||
public int getEstimatedFeeTxVsize(List<Coin> outputValues, Coin txFee)
|
||||
throws InsufficientMoneyException, AddressFormatException {
|
||||
Transaction transaction = new Transaction(params);
|
||||
// In reality txs have a mix of segwit/legacy ouputs, but we don't care too much because the size of
|
||||
|
|
|
@ -475,10 +475,10 @@ public abstract class WalletService {
|
|||
return getBalanceForAddress(getAddressFromOutput(output));
|
||||
}
|
||||
|
||||
public Coin getTxFeeForWithdrawalPerByte() {
|
||||
public Coin getTxFeeForWithdrawalPerVbyte() {
|
||||
Coin fee = (preferences.isUseCustomWithdrawalTxFee()) ?
|
||||
Coin.valueOf(preferences.getWithdrawalTxFeeInBytes()) :
|
||||
feeService.getTxFeePerByte();
|
||||
Coin.valueOf(preferences.getWithdrawalTxFeeInVbytes()) :
|
||||
feeService.getTxFeePerVbyte();
|
||||
log.info("tx fee = " + fee.toFriendlyString());
|
||||
return fee;
|
||||
}
|
||||
|
@ -517,7 +517,7 @@ public abstract class WalletService {
|
|||
throws InsufficientMoneyException, AddressFormatException {
|
||||
SendRequest sendRequest = SendRequest.emptyWallet(Address.fromString(params, toAddress));
|
||||
sendRequest.fee = Coin.ZERO;
|
||||
sendRequest.feePerKb = getTxFeeForWithdrawalPerByte().multiply(1000);
|
||||
sendRequest.feePerKb = getTxFeeForWithdrawalPerVbyte().multiply(1000);
|
||||
sendRequest.aesKey = aesKey;
|
||||
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
||||
printTx("empty btc wallet", sendResult.tx);
|
||||
|
|
|
@ -382,9 +382,9 @@ public class DaoFacade implements DaoSetupService {
|
|||
return BlindVoteConsensus.getFee(daoStateService, daoStateService.getChainHeight());
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getBlindVoteMiningFeeAndTxSize(Coin stake)
|
||||
public Tuple2<Coin, Integer> getBlindVoteMiningFeeAndTxVsize(Coin stake)
|
||||
throws WalletException, InsufficientMoneyException, TransactionVerificationException {
|
||||
return myBlindVoteListService.getMiningFeeAndTxSize(stake);
|
||||
return myBlindVoteListService.getMiningFeeAndTxVsize(stake);
|
||||
}
|
||||
|
||||
// Publish blindVote tx and broadcast blindVote to p2p network and store to blindVoteList.
|
||||
|
@ -532,12 +532,12 @@ public class DaoFacade implements DaoSetupService {
|
|||
lockupTxService.publishLockupTx(lockupAmount, lockTime, lockupReason, hash, resultHandler, exceptionHandler);
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getLockupTxMiningFeeAndTxSize(Coin lockupAmount,
|
||||
int lockTime,
|
||||
LockupReason lockupReason,
|
||||
byte[] hash)
|
||||
public Tuple2<Coin, Integer> getLockupTxMiningFeeAndTxVsize(Coin lockupAmount,
|
||||
int lockTime,
|
||||
LockupReason lockupReason,
|
||||
byte[] hash)
|
||||
throws InsufficientMoneyException, IOException, TransactionVerificationException, WalletException {
|
||||
return lockupTxService.getMiningFeeAndTxSize(lockupAmount, lockTime, lockupReason, hash);
|
||||
return lockupTxService.getMiningFeeAndTxVsize(lockupAmount, lockTime, lockupReason, hash);
|
||||
}
|
||||
|
||||
public void publishUnlockTx(String lockupTxId, Consumer<String> resultHandler,
|
||||
|
@ -545,9 +545,9 @@ public class DaoFacade implements DaoSetupService {
|
|||
unlockTxService.publishUnlockTx(lockupTxId, resultHandler, exceptionHandler);
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getUnlockTxMiningFeeAndTxSize(String lockupTxId)
|
||||
public Tuple2<Coin, Integer> getUnlockTxMiningFeeAndTxVsize(String lockupTxId)
|
||||
throws InsufficientMoneyException, TransactionVerificationException, WalletException {
|
||||
return unlockTxService.getMiningFeeAndTxSize(lockupTxId);
|
||||
return unlockTxService.getMiningFeeAndTxVsize(lockupTxId);
|
||||
}
|
||||
|
||||
public long getTotalLockupAmount() {
|
||||
|
|
|
@ -189,14 +189,14 @@ public class MyBlindVoteListService implements PersistedDataHost, DaoStateListen
|
|||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Tuple2<Coin, Integer> getMiningFeeAndTxSize(Coin stake)
|
||||
public Tuple2<Coin, Integer> getMiningFeeAndTxVsize(Coin stake)
|
||||
throws InsufficientMoneyException, WalletException, TransactionVerificationException {
|
||||
// We set dummy opReturn data
|
||||
Coin blindVoteFee = BlindVoteConsensus.getFee(daoStateService, daoStateService.getChainHeight());
|
||||
Transaction dummyTx = getBlindVoteTx(stake, blindVoteFee, new byte[22]);
|
||||
Coin miningFee = dummyTx.getFee();
|
||||
int txSize = dummyTx.getVsize();
|
||||
return new Tuple2<>(miningFee, txSize);
|
||||
int txVsize = dummyTx.getVsize();
|
||||
return new Tuple2<>(miningFee, txVsize);
|
||||
}
|
||||
|
||||
public void publishBlindVote(Coin stake, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
|
||||
|
|
|
@ -91,12 +91,12 @@ public class LockupTxService {
|
|||
}
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getMiningFeeAndTxSize(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash)
|
||||
public Tuple2<Coin, Integer> getMiningFeeAndTxVsize(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash)
|
||||
throws InsufficientMoneyException, WalletException, TransactionVerificationException, IOException {
|
||||
Transaction tx = getLockupTx(lockupAmount, lockTime, lockupReason, hash);
|
||||
Coin miningFee = tx.getFee();
|
||||
int txSize = tx.getVsize();
|
||||
return new Tuple2<>(miningFee, txSize);
|
||||
int txVsize = tx.getVsize();
|
||||
return new Tuple2<>(miningFee, txVsize);
|
||||
}
|
||||
|
||||
private Transaction getLockupTx(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash)
|
||||
|
|
|
@ -89,12 +89,12 @@ public class UnlockTxService {
|
|||
}
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getMiningFeeAndTxSize(String lockupTxId)
|
||||
public Tuple2<Coin, Integer> getMiningFeeAndTxVsize(String lockupTxId)
|
||||
throws InsufficientMoneyException, WalletException, TransactionVerificationException {
|
||||
Transaction tx = getUnlockTx(lockupTxId);
|
||||
Coin miningFee = tx.getFee();
|
||||
int txSize = tx.getVsize();
|
||||
return new Tuple2<>(miningFee, txSize);
|
||||
int txVsize = tx.getVsize();
|
||||
return new Tuple2<>(miningFee, txVsize);
|
||||
}
|
||||
|
||||
private Transaction getUnlockTx(String lockupTxId)
|
||||
|
|
|
@ -163,7 +163,7 @@ public class CreateOfferService {
|
|||
String bankId = PaymentAccountUtil.getBankId(paymentAccount);
|
||||
List<String> acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount);
|
||||
double sellerSecurityDeposit = getSellerSecurityDepositAsDouble(buyerSecurityDepositAsDouble);
|
||||
Coin txFeeFromFeeService = getEstimatedFeeAndTxSize(amount, direction, buyerSecurityDepositAsDouble, sellerSecurityDeposit).first;
|
||||
Coin txFeeFromFeeService = getEstimatedFeeAndTxVsize(amount, direction, buyerSecurityDepositAsDouble, sellerSecurityDeposit).first;
|
||||
Coin txFeeToUse = txFee.isPositive() ? txFee : txFeeFromFeeService;
|
||||
Coin makerFeeAsCoin = offerUtil.getMakerFee(amount);
|
||||
boolean isCurrencyForMakerFeeBtc = offerUtil.isCurrencyForMakerFeeBtc(amount);
|
||||
|
@ -233,15 +233,15 @@ public class CreateOfferService {
|
|||
return offer;
|
||||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxSize(Coin amount,
|
||||
OfferPayload.Direction direction,
|
||||
double buyerSecurityDeposit,
|
||||
double sellerSecurityDeposit) {
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsize(Coin amount,
|
||||
OfferPayload.Direction direction,
|
||||
double buyerSecurityDeposit,
|
||||
double sellerSecurityDeposit) {
|
||||
Coin reservedFundsForOffer = getReservedFundsForOffer(direction,
|
||||
amount,
|
||||
buyerSecurityDeposit,
|
||||
sellerSecurityDeposit);
|
||||
return txFeeEstimationService.getEstimatedFeeAndTxSizeForMaker(reservedFundsForOffer,
|
||||
return txFeeEstimationService.getEstimatedFeeAndTxVsizeForMaker(reservedFundsForOffer,
|
||||
offerUtil.getMakerFee(amount));
|
||||
}
|
||||
|
||||
|
|
|
@ -177,8 +177,8 @@ public class OfferUtil {
|
|||
return CoinUtil.getMakerFee(isCurrencyForMakerFeeBtc, amount);
|
||||
}
|
||||
|
||||
public Coin getTxFeeBySize(Coin txFeePerByteFromFeeService, int sizeInBytes) {
|
||||
return txFeePerByteFromFeeService.multiply(getAverageTakerFeeTxSize(sizeInBytes));
|
||||
public Coin getTxFeeByVsize(Coin txFeePerVbyteFromFeeService, int vsizeInVbytes) {
|
||||
return txFeePerVbyteFromFeeService.multiply(getAverageTakerFeeTxVsize(vsizeInVbytes));
|
||||
}
|
||||
|
||||
// We use the sum of the size of the trade fee and the deposit tx to get an average.
|
||||
|
@ -186,8 +186,8 @@ public class OfferUtil {
|
|||
// enough. With that we avoid that we overpay in case that the trade fee has many
|
||||
// inputs and we would apply that fee for the other 2 txs as well. We still might
|
||||
// overpay a bit for the payout tx.
|
||||
public int getAverageTakerFeeTxSize(int txSize) {
|
||||
return (txSize + 233) / 2;
|
||||
public int getAverageTakerFeeTxVsize(int txVsize) {
|
||||
return (txVsize + 233) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -78,8 +78,8 @@ public class TakeOfferModel implements Model {
|
|||
private boolean useSavingsWallet;
|
||||
|
||||
// Use an average of a typical trade fee tx with 1 input, deposit tx and payout tx.
|
||||
private final int feeTxSize = 192; // (175+233+169)/3
|
||||
private Coin txFeePerByteFromFeeService;
|
||||
private final int feeTxVsize = 192; // (175+233+169)/3
|
||||
private Coin txFeePerVbyteFromFeeService;
|
||||
@Getter
|
||||
private Coin txFeeFromFeeService;
|
||||
@Getter
|
||||
|
@ -149,26 +149,26 @@ public class TakeOfferModel implements Model {
|
|||
// 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 bytes (if one input). The trade txs has
|
||||
// about 169-263 bytes. We use 192 as a average value.
|
||||
// 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 bytes (1 input)
|
||||
// Deposit tx: 233 bytes (1 MS output+ OP_RETURN) - 263 bytes
|
||||
// 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 bytes
|
||||
// Disputed payout tx: 139 bytes
|
||||
// Payout tx: 169 vbytes
|
||||
// Disputed payout tx: 139 vbytes
|
||||
|
||||
txFeePerByteFromFeeService = getTxFeePerByte();
|
||||
txFeeFromFeeService = offerUtil.getTxFeeBySize(txFeePerByteFromFeeService, feeTxSize);
|
||||
log.info("{} txFeePerByte = {}", feeService.getClass().getSimpleName(), txFeePerByteFromFeeService);
|
||||
txFeePerVbyteFromFeeService = getTxFeePerVbyte();
|
||||
txFeeFromFeeService = offerUtil.getTxFeeByVsize(txFeePerVbyteFromFeeService, feeTxVsize);
|
||||
log.info("{} txFeePerVbyte = {}", feeService.getClass().getSimpleName(), txFeePerVbyteFromFeeService);
|
||||
}
|
||||
|
||||
private Coin getTxFeePerByte() {
|
||||
private Coin getTxFeePerVbyte() {
|
||||
try {
|
||||
CompletableFuture<Void> feeRequestFuture = CompletableFuture.runAsync(feeService::requestFees);
|
||||
feeRequestFuture.get(); // Block until async fee request is complete.
|
||||
return feeService.getTxFeePerByte();
|
||||
return feeService.getTxFeePerVbyte();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new IllegalStateException("Could not request fees from fee service.", e);
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ public class TakeOfferModel implements Model {
|
|||
this.totalAvailableBalance = null;
|
||||
this.totalToPayAsCoin = null;
|
||||
this.txFeeFromFeeService = null;
|
||||
this.txFeePerByteFromFeeService = null;
|
||||
this.txFeePerVbyteFromFeeService = null;
|
||||
this.useSavingsWallet = true;
|
||||
this.volume = null;
|
||||
}
|
||||
|
@ -299,8 +299,8 @@ public class TakeOfferModel implements Model {
|
|||
", addressEntry=" + addressEntry + "\n" +
|
||||
", amount=" + amount + "\n" +
|
||||
", securityDeposit=" + securityDeposit + "\n" +
|
||||
", feeTxSize=" + feeTxSize + "\n" +
|
||||
", txFeePerByteFromFeeService=" + txFeePerByteFromFeeService + "\n" +
|
||||
", feeTxVsize=" + feeTxVsize + "\n" +
|
||||
", txFeePerVbyteFromFeeService=" + txFeePerVbyteFromFeeService + "\n" +
|
||||
", txFeeFromFeeService=" + txFeeFromFeeService + "\n" +
|
||||
", takerFee=" + takerFee + "\n" +
|
||||
", totalToPayAsCoin=" + totalToPayAsCoin + "\n" +
|
||||
|
|
|
@ -59,7 +59,7 @@ public class FeeService {
|
|||
// Static
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Miner fees are between 1-600 sat/byte. We try to stay on the safe side. BTC_DEFAULT_TX_FEE is only used if our
|
||||
// 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;
|
||||
|
@ -93,10 +93,10 @@ public class FeeService {
|
|||
|
||||
private final FeeProvider feeProvider;
|
||||
private final IntegerProperty feeUpdateCounter = new SimpleIntegerProperty(0);
|
||||
private long txFeePerByte = BTC_DEFAULT_TX_FEE;
|
||||
private long txFeePerVbyte = BTC_DEFAULT_TX_FEE;
|
||||
private Map<String, Long> timeStampMap;
|
||||
private long lastRequest;
|
||||
private long minFeePerByte;
|
||||
private long minFeePerVByte;
|
||||
private long epochInSecondAtLastRequest;
|
||||
|
||||
|
||||
|
@ -117,7 +117,7 @@ public class FeeService {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onAllServicesInitialized() {
|
||||
minFeePerByte = Config.baseCurrencyNetwork().getDefaultMinFeePerByte();
|
||||
minFeePerVByte = Config.baseCurrencyNetwork().getDefaultMinFeePerVbyte();
|
||||
|
||||
requestFees();
|
||||
|
||||
|
@ -149,15 +149,15 @@ public class FeeService {
|
|||
timeStampMap = result.first;
|
||||
epochInSecondAtLastRequest = timeStampMap.get("bitcoinFeesTs");
|
||||
final Map<String, Long> map = result.second;
|
||||
txFeePerByte = map.get("BTC");
|
||||
txFeePerVbyte = map.get("BTC");
|
||||
|
||||
if (txFeePerByte < minFeePerByte) {
|
||||
log.warn("The delivered fee per byte is smaller than the min. default fee of 5 sat/byte");
|
||||
txFeePerByte = minFeePerByte;
|
||||
if (txFeePerVbyte < minFeePerVByte) {
|
||||
log.warn("The delivered fee per vbyte is smaller than the min. default fee of 5 sat/vbyte");
|
||||
txFeePerVbyte = minFeePerVByte;
|
||||
}
|
||||
|
||||
feeUpdateCounter.set(feeUpdateCounter.get() + 1);
|
||||
log.info("BTC tx fee: txFeePerByte={}", txFeePerByte);
|
||||
log.info("BTC tx fee: txFeePerVbyte={}", txFeePerVbyte);
|
||||
if (resultHandler != null)
|
||||
resultHandler.run();
|
||||
});
|
||||
|
@ -179,12 +179,12 @@ public class FeeService {
|
|||
}
|
||||
}
|
||||
|
||||
public Coin getTxFee(int sizeInBytes) {
|
||||
return getTxFeePerByte().multiply(sizeInBytes);
|
||||
public Coin getTxFee(int vsizeInVbytes) {
|
||||
return getTxFeePerVbyte().multiply(vsizeInVbytes);
|
||||
}
|
||||
|
||||
public Coin getTxFeePerByte() {
|
||||
return Coin.valueOf(txFeePerByte);
|
||||
public Coin getTxFeePerVbyte() {
|
||||
return Coin.valueOf(txFeePerVbyte);
|
||||
}
|
||||
|
||||
public ReadOnlyIntegerProperty feeUpdateCounterProperty() {
|
||||
|
|
|
@ -589,8 +589,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
requestPersistence();
|
||||
}
|
||||
|
||||
public void setWithdrawalTxFeeInBytes(long withdrawalTxFeeInBytes) {
|
||||
prefPayload.setWithdrawalTxFeeInBytes(withdrawalTxFeeInBytes);
|
||||
public void setWithdrawalTxFeeInVbytes(long withdrawalTxFeeInVbytes) {
|
||||
prefPayload.setWithdrawalTxFeeInVbytes(withdrawalTxFeeInVbytes);
|
||||
requestPersistence();
|
||||
}
|
||||
|
||||
|
@ -865,8 +865,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
return prefPayload.getBridgeAddresses();
|
||||
}
|
||||
|
||||
public long getWithdrawalTxFeeInBytes() {
|
||||
return Math.max(prefPayload.getWithdrawalTxFeeInBytes(), Config.baseCurrencyNetwork().getDefaultMinFeePerByte());
|
||||
public long getWithdrawalTxFeeInVbytes() {
|
||||
return Math.max(prefPayload.getWithdrawalTxFeeInVbytes(), Config.baseCurrencyNetwork().getDefaultMinFeePerVbyte());
|
||||
}
|
||||
|
||||
public boolean isDaoFullNode() {
|
||||
|
@ -976,7 +976,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
|
||||
void setUseCustomWithdrawalTxFee(boolean useCustomWithdrawalTxFee);
|
||||
|
||||
void setWithdrawalTxFeeInBytes(long withdrawalTxFeeInBytes);
|
||||
void setWithdrawalTxFeeInVbytes(long withdrawalTxFeeInVbytes);
|
||||
|
||||
void setSelectedPaymentAccountForCreateOffer(@Nullable PaymentAccount paymentAccount);
|
||||
|
||||
|
@ -1022,7 +1022,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
|
||||
List<String> getBridgeAddresses();
|
||||
|
||||
long getWithdrawalTxFeeInBytes();
|
||||
long getWithdrawalTxFeeInVbytes();
|
||||
|
||||
void setUseStandbyMode(boolean useStandbyMode);
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
private boolean showOwnOffersInOfferBook = true;
|
||||
@Nullable
|
||||
private TradeCurrency preferredTradeCurrency;
|
||||
private long withdrawalTxFeeInBytes = 100;
|
||||
private long withdrawalTxFeeInVbytes = 100;
|
||||
private boolean useCustomWithdrawalTxFee = false;
|
||||
private double maxPriceDistanceInPercent = 0.3;
|
||||
@Nullable
|
||||
|
@ -160,7 +160,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
.setTacAccepted(tacAccepted)
|
||||
.setUseTorForBitcoinJ(useTorForBitcoinJ)
|
||||
.setShowOwnOffersInOfferBook(showOwnOffersInOfferBook)
|
||||
.setWithdrawalTxFeeInBytes(withdrawalTxFeeInBytes)
|
||||
.setWithdrawalTxFeeInVbytes(withdrawalTxFeeInVbytes)
|
||||
.setUseCustomWithdrawalTxFee(useCustomWithdrawalTxFee)
|
||||
.setMaxPriceDistanceInPercent(maxPriceDistanceInPercent)
|
||||
.setTradeStatisticsTickUnitIndex(tradeStatisticsTickUnitIndex)
|
||||
|
@ -241,7 +241,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
proto.getUseTorForBitcoinJ(),
|
||||
proto.getShowOwnOffersInOfferBook(),
|
||||
proto.hasPreferredTradeCurrency() ? TradeCurrency.fromProto(proto.getPreferredTradeCurrency()) : null,
|
||||
proto.getWithdrawalTxFeeInBytes(),
|
||||
proto.getWithdrawalTxFeeInVbytes(),
|
||||
proto.getUseCustomWithdrawalTxFee(),
|
||||
proto.getMaxPriceDistanceInPercent(),
|
||||
ProtoUtil.stringOrNullFromProto(proto.getOfferBookChartScreenCurrencyCode()),
|
||||
|
|
|
@ -52,9 +52,9 @@ public class CoinUtil {
|
|||
return a.compareTo(b) >= 0 ? a : b;
|
||||
}
|
||||
|
||||
public static double getFeePerByte(Coin miningFee, int txSize) {
|
||||
public static double getFeePerVbyte(Coin miningFee, int txVsize) {
|
||||
double value = miningFee != null ? miningFee.value : 0;
|
||||
return MathUtils.roundDouble((value / (double) txSize), 2);
|
||||
return MathUtils.roundDouble((value / (double) txVsize), 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -122,7 +122,7 @@ shared.noDateAvailable=No date available
|
|||
shared.noDetailsAvailable=No details available
|
||||
shared.notUsedYet=Not used yet
|
||||
shared.date=Date
|
||||
shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount?
|
||||
shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount?
|
||||
shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n
|
||||
shared.copyToClipboard=Copy to clipboard
|
||||
shared.language=Language
|
||||
|
@ -1192,10 +1192,10 @@ setting.preferences.autoConfirmRequiredConfirmations=Required confirmations
|
|||
setting.preferences.autoConfirmMaxTradeSize=Max. trade amount (BTC)
|
||||
setting.preferences.autoConfirmServiceAddresses=Monero Explorer URLs (uses Tor, except for localhost, LAN IP addresses, and *.local hostnames)
|
||||
setting.preferences.deviationToLarge=Values higher than {0}% are not allowed.
|
||||
setting.preferences.txFee=Withdrawal transaction fee (satoshis/byte)
|
||||
setting.preferences.txFee=Withdrawal transaction fee (satoshis/vbyte)
|
||||
setting.preferences.useCustomValue=Use custom value
|
||||
setting.preferences.txFeeMin=Transaction fee must be at least {0} satoshis/byte
|
||||
setting.preferences.txFeeTooLarge=Your input is above any reasonable value (>5000 satoshis/byte). Transaction fee is usually in the range of 50-400 satoshis/byte.
|
||||
setting.preferences.txFeeMin=Transaction fee must be at least {0} satoshis/vbyte
|
||||
setting.preferences.txFeeTooLarge=Your input is above any reasonable value (>5000 satoshis/vbyte). Transaction fee is usually in the range of 50-400 satoshis/vbyte.
|
||||
setting.preferences.ignorePeers=Ignored peers [onion address:port]
|
||||
setting.preferences.refererId=Referral ID
|
||||
setting.preferences.ignoreDustThreshold=Min. non-dust output value
|
||||
|
@ -1944,10 +1944,10 @@ dao.bond.reputation.hash=Hash
|
|||
dao.bond.reputation.lockupButton=Lockup
|
||||
dao.bond.reputation.lockup.headline=Confirm lockup transaction
|
||||
dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\n\
|
||||
Mining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed?
|
||||
Mining fee: {3} ({4} Satoshis/vbyte)\nTransaction vsize: {5} Kb\n\nAre you sure you want to proceed?
|
||||
dao.bond.reputation.unlock.headline=Confirm unlock transaction
|
||||
dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\n\
|
||||
Mining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed?
|
||||
Mining fee: {3} ({4} Satoshis/vbyte)\nTransaction vsize: {5} Kb\n\nAre you sure you want to proceed?
|
||||
|
||||
dao.bond.allBonds.header=All bonds
|
||||
|
||||
|
@ -2262,7 +2262,7 @@ dao.wallet.send.setDestinationAddress=Fill in your destination address
|
|||
dao.wallet.send.send=Send BSQ funds
|
||||
dao.wallet.send.sendBtc=Send BTC funds
|
||||
dao.wallet.send.sendFunds.headline=Confirm withdrawal request
|
||||
dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired transaction fee is: {2} ({3} satoshis/byte)\nTransaction size: {4} Kb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount?
|
||||
dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired transaction fee is: {2} ({3} satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount?
|
||||
dao.wallet.chainHeightSynced=Latest verified block: {0}
|
||||
dao.wallet.chainHeightSyncing=Awaiting blocks... Verified {0} blocks out of {1}
|
||||
dao.wallet.tx.type=Type
|
||||
|
@ -2333,14 +2333,14 @@ dao.proposal.create.missingIssuanceFunds=You don''t have sufficient BTC funds fo
|
|||
|
||||
dao.feeTx.confirm=Confirm {0} transaction
|
||||
dao.feeTx.confirm.details={0} fee: {1}\n\
|
||||
Mining fee: {2} ({3} Satoshis/byte)\n\
|
||||
Transaction size: {4} Kb\n\n\
|
||||
Mining fee: {2} ({3} Satoshis/vbyte)\n\
|
||||
Transaction vsize: {4} vKb\n\n\
|
||||
Are you sure you want to publish the {5} transaction?
|
||||
|
||||
dao.feeTx.issuanceProposal.confirm.details={0} fee: {1}\n\
|
||||
BTC needed for BSQ issuance: {2} ({3} Satoshis/BSQ)\n\
|
||||
Mining fee: {4} ({5} Satoshis/byte)\n\
|
||||
Transaction size: {6} Kb\n\n\
|
||||
Mining fee: {4} ({5} Satoshis/vbyte)\n\
|
||||
Transaction vsize: {6} vKb\n\n\
|
||||
If your request is approved, you will receive the amount you requested net of the 2 BSQ proposal fee.\n\n\
|
||||
Are you sure you want to publish the {7} transaction?
|
||||
|
||||
|
@ -2562,8 +2562,8 @@ disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n
|
|||
disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n
|
||||
disputeSummaryWindow.close.txDetails=Spending: {0}\n\
|
||||
{1}{2}\
|
||||
Transaction fee: {3} ({4} satoshis/byte)\n\
|
||||
Transaction size: {5} Kb\n\n\
|
||||
Transaction fee: {3} ({4} satoshis/vbyte)\n\
|
||||
Transaction vsize: {5} vKb\n\n\
|
||||
Are you sure you want to publish this transaction?
|
||||
|
||||
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
||||
|
@ -2960,7 +2960,7 @@ systemTray.tooltip=Bisq: A decentralized bitcoin exchange network
|
|||
####################################################################
|
||||
|
||||
guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is \
|
||||
at least {0} satoshis/byte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute.
|
||||
at least {0} satoshis/vbyte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute.
|
||||
|
||||
guiUtil.accountExport.savedToPath=Trading accounts saved to path:\n{0}
|
||||
guiUtil.accountExport.noAccountSetup=You don't have trading accounts set up for exporting.
|
||||
|
|
|
@ -36,106 +36,106 @@ import static org.mockito.Mockito.when;
|
|||
public class TxFeeEstimationServiceTest {
|
||||
|
||||
@Test
|
||||
public void testGetEstimatedTxSize_withDefaultTxSize() throws InsufficientMoneyException {
|
||||
public void testGetEstimatedTxVsize_withDefaultTxVsize() throws InsufficientMoneyException {
|
||||
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
|
||||
int initialEstimatedTxSize;
|
||||
Coin txFeePerByte;
|
||||
int initialEstimatedTxVsize;
|
||||
Coin txFeePerVbyte;
|
||||
BtcWalletService btcWalletService = mock(BtcWalletService.class);
|
||||
int result;
|
||||
int realTxSize;
|
||||
int realTxVsize;
|
||||
Coin txFee;
|
||||
|
||||
initialEstimatedTxSize = 175;
|
||||
txFeePerByte = Coin.valueOf(10);
|
||||
realTxSize = 175;
|
||||
initialEstimatedTxVsize = 175;
|
||||
txFeePerVbyte = Coin.valueOf(10);
|
||||
realTxVsize = 175;
|
||||
|
||||
txFee = txFeePerByte.multiply(initialEstimatedTxSize);
|
||||
when(btcWalletService.getEstimatedFeeTxSize(outputValues, txFee)).thenReturn(realTxSize);
|
||||
result = TxFeeEstimationService.getEstimatedTxSize(outputValues, initialEstimatedTxSize, txFeePerByte, btcWalletService);
|
||||
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 testGetEstimatedTxSize_withLargeTx() throws InsufficientMoneyException {
|
||||
public void testGetEstimatedTxVsize_withLargeTx() throws InsufficientMoneyException {
|
||||
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
|
||||
int initialEstimatedTxSize;
|
||||
Coin txFeePerByte;
|
||||
int initialEstimatedTxVsize;
|
||||
Coin txFeePerVbyte;
|
||||
BtcWalletService btcWalletService = mock(BtcWalletService.class);
|
||||
int result;
|
||||
int realTxSize;
|
||||
int realTxVsize;
|
||||
Coin txFee;
|
||||
|
||||
initialEstimatedTxSize = 175;
|
||||
txFeePerByte = Coin.valueOf(10);
|
||||
realTxSize = 1750;
|
||||
initialEstimatedTxVsize = 175;
|
||||
txFeePerVbyte = Coin.valueOf(10);
|
||||
realTxVsize = 1750;
|
||||
|
||||
txFee = txFeePerByte.multiply(initialEstimatedTxSize);
|
||||
when(btcWalletService.getEstimatedFeeTxSize(outputValues, txFee)).thenReturn(realTxSize);
|
||||
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
|
||||
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
|
||||
|
||||
// repeated calls to getEstimatedFeeTxSize do not work (returns 0 at second call in loop which cause test to fail)
|
||||
result = TxFeeEstimationService.getEstimatedTxSize(outputValues, initialEstimatedTxSize, txFeePerByte, btcWalletService);
|
||||
// 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 testGetEstimatedTxSize_withSmallTx() throws InsufficientMoneyException {
|
||||
public void testGetEstimatedTxVsize_withSmallTx() throws InsufficientMoneyException {
|
||||
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
|
||||
int initialEstimatedTxSize;
|
||||
Coin txFeePerByte;
|
||||
int initialEstimatedTxVsize;
|
||||
Coin txFeePerVbyte;
|
||||
BtcWalletService btcWalletService = mock(BtcWalletService.class);
|
||||
int result;
|
||||
int realTxSize;
|
||||
int realTxVsize;
|
||||
Coin txFee;
|
||||
|
||||
initialEstimatedTxSize = 1750;
|
||||
txFeePerByte = Coin.valueOf(10);
|
||||
realTxSize = 175;
|
||||
initialEstimatedTxVsize = 1750;
|
||||
txFeePerVbyte = Coin.valueOf(10);
|
||||
realTxVsize = 175;
|
||||
|
||||
txFee = txFeePerByte.multiply(initialEstimatedTxSize);
|
||||
when(btcWalletService.getEstimatedFeeTxSize(outputValues, txFee)).thenReturn(realTxSize);
|
||||
result = TxFeeEstimationService.getEstimatedTxSize(outputValues, initialEstimatedTxSize, txFeePerByte, btcWalletService);
|
||||
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 txSize;
|
||||
int txVsize;
|
||||
double tolerance;
|
||||
boolean result;
|
||||
|
||||
estimatedSize = 100;
|
||||
txSize = 100;
|
||||
txVsize = 100;
|
||||
tolerance = 0.0001;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txSize, tolerance);
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertTrue(result);
|
||||
|
||||
estimatedSize = 100;
|
||||
txSize = 200;
|
||||
txVsize = 200;
|
||||
tolerance = 0.2;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txSize, tolerance);
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertFalse(result);
|
||||
|
||||
estimatedSize = 120;
|
||||
txSize = 100;
|
||||
txVsize = 100;
|
||||
tolerance = 0.2;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txSize, tolerance);
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertTrue(result);
|
||||
|
||||
estimatedSize = 200;
|
||||
txSize = 100;
|
||||
txVsize = 100;
|
||||
tolerance = 1;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txSize, tolerance);
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertTrue(result);
|
||||
|
||||
estimatedSize = 201;
|
||||
txSize = 100;
|
||||
txVsize = 100;
|
||||
tolerance = 1;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txSize, tolerance);
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertFalse(result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,9 +34,7 @@ import bisq.core.dao.state.model.blockchain.TxOutput;
|
|||
import bisq.core.dao.state.model.governance.Role;
|
||||
import bisq.core.dao.state.model.governance.RoleProposal;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.util.coin.ImmutableCoinFormatter;
|
||||
import bisq.core.util.coin.BsqFormatter;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
import bisq.core.util.coin.CoinUtil;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
|
||||
|
@ -111,9 +109,9 @@ public class BondingViewUtils {
|
|||
if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, walletsSetup)) {
|
||||
if (!DevEnv.isDevMode()) {
|
||||
try {
|
||||
Tuple2<Coin, Integer> miningFeeAndTxSize = daoFacade.getLockupTxMiningFeeAndTxSize(lockupAmount, lockupTime, lockupReason, hash);
|
||||
Coin miningFee = miningFeeAndTxSize.first;
|
||||
int txSize = miningFeeAndTxSize.second;
|
||||
Tuple2<Coin, Integer> miningFeeAndTxVsize = daoFacade.getLockupTxMiningFeeAndTxVsize(lockupAmount, lockupTime, lockupReason, hash);
|
||||
Coin miningFee = miningFeeAndTxVsize.first;
|
||||
int txVsize = miningFeeAndTxVsize.second;
|
||||
String duration = FormattingUtils.formatDurationAsWords(lockupTime * 10 * 60 * 1000L, false, false);
|
||||
new Popup().headLine(Res.get("dao.bond.reputation.lockup.headline"))
|
||||
.confirmation(Res.get("dao.bond.reputation.lockup.details",
|
||||
|
@ -121,8 +119,8 @@ public class BondingViewUtils {
|
|||
lockupTime,
|
||||
duration,
|
||||
bsqFormatter.formatBTCWithCode(miningFee),
|
||||
CoinUtil.getFeePerByte(miningFee, txSize),
|
||||
txSize / 1000d
|
||||
CoinUtil.getFeePerVbyte(miningFee, txVsize),
|
||||
txVsize / 1000d
|
||||
))
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.onAction(() -> publishLockupTx(lockupAmount, lockupTime, lockupReason, hash, resultHandler))
|
||||
|
@ -169,9 +167,9 @@ public class BondingViewUtils {
|
|||
|
||||
try {
|
||||
if (!DevEnv.isDevMode()) {
|
||||
Tuple2<Coin, Integer> miningFeeAndTxSize = daoFacade.getUnlockTxMiningFeeAndTxSize(lockupTxId);
|
||||
Coin miningFee = miningFeeAndTxSize.first;
|
||||
int txSize = miningFeeAndTxSize.second;
|
||||
Tuple2<Coin, Integer> miningFeeAndTxVsize = daoFacade.getUnlockTxMiningFeeAndTxVsize(lockupTxId);
|
||||
Coin miningFee = miningFeeAndTxVsize.first;
|
||||
int txVsize = miningFeeAndTxVsize.second;
|
||||
String duration = FormattingUtils.formatDurationAsWords(lockTime * 10 * 60 * 1000L, false, false);
|
||||
new Popup().headLine(Res.get("dao.bond.reputation.unlock.headline"))
|
||||
.confirmation(Res.get("dao.bond.reputation.unlock.details",
|
||||
|
@ -179,8 +177,8 @@ public class BondingViewUtils {
|
|||
lockTime,
|
||||
duration,
|
||||
bsqFormatter.formatBTCWithCode(miningFee),
|
||||
CoinUtil.getFeePerByte(miningFee, txSize),
|
||||
txSize / 1000d
|
||||
CoinUtil.getFeePerVbyte(miningFee, txVsize),
|
||||
txVsize / 1000d
|
||||
))
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.onAction(() -> publishUnlockTx(lockupTxId, resultHandler))
|
||||
|
|
|
@ -189,10 +189,10 @@ public class AssetFeeView extends ActivatableView<GridPane, Void> implements Bsq
|
|||
try {
|
||||
Transaction transaction = assetService.payFee(selectedAsset, listingFee.value);
|
||||
Coin miningFee = transaction.getFee();
|
||||
int txSize = transaction.getVsize();
|
||||
int txVsize = transaction.getVsize();
|
||||
|
||||
if (!DevEnv.isDevMode()) {
|
||||
GUIUtil.showBsqFeeInfoPopup(listingFee, miningFee, txSize, bsqFormatter, btcFormatter,
|
||||
GUIUtil.showBsqFeeInfoPopup(listingFee, miningFee, txVsize, bsqFormatter, btcFormatter,
|
||||
Res.get("dao.burnBsq.assetFee"), () -> doPublishFeeTx(transaction));
|
||||
} else {
|
||||
doPublishFeeTx(transaction);
|
||||
|
|
|
@ -172,10 +172,10 @@ public class ProofOfBurnView extends ActivatableView<GridPane, Void> implements
|
|||
String preImageAsString = preImageTextField.getText();
|
||||
Transaction transaction = proofOfBurnService.burn(preImageAsString, amount.value);
|
||||
Coin miningFee = transaction.getFee();
|
||||
int txSize = transaction.getVsize();
|
||||
int txVsize = transaction.getVsize();
|
||||
|
||||
if (!DevEnv.isDevMode()) {
|
||||
GUIUtil.showBsqFeeInfoPopup(amount, miningFee, txSize, bsqFormatter, btcFormatter,
|
||||
GUIUtil.showBsqFeeInfoPopup(amount, miningFee, txVsize, bsqFormatter, btcFormatter,
|
||||
Res.get("dao.proofOfBurn.header"), () -> doPublishFeeTx(transaction, preImageAsString));
|
||||
} else {
|
||||
doPublishFeeTx(transaction, preImageAsString);
|
||||
|
|
|
@ -283,7 +283,7 @@ public class MakeProposalView extends ActivatableView<GridPane, Void> implements
|
|||
Proposal proposal = proposalWithTransaction.getProposal();
|
||||
Transaction transaction = proposalWithTransaction.getTransaction();
|
||||
Coin miningFee = transaction.getFee();
|
||||
int txSize = transaction.getVsize();
|
||||
int txVsize = transaction.getVsize();
|
||||
Coin fee = daoFacade.getProposalFee(daoFacade.getChainHeight());
|
||||
|
||||
if (type.equals(ProposalType.BONDED_ROLE)) {
|
||||
|
@ -298,13 +298,13 @@ public class MakeProposalView extends ActivatableView<GridPane, Void> implements
|
|||
new Popup().warning(Res.get("dao.proposal.create.missingBsqFundsForBond",
|
||||
bsqFormatter.formatCoinWithCode(missing)))
|
||||
.actionButtonText(Res.get("dao.proposal.create.publish"))
|
||||
.onAction(() -> showFeeInfoAndPublishMyProposal(proposal, transaction, miningFee, txSize, fee))
|
||||
.onAction(() -> showFeeInfoAndPublishMyProposal(proposal, transaction, miningFee, txVsize, fee))
|
||||
.show();
|
||||
} else {
|
||||
showFeeInfoAndPublishMyProposal(proposal, transaction, miningFee, txSize, fee);
|
||||
showFeeInfoAndPublishMyProposal(proposal, transaction, miningFee, txVsize, fee);
|
||||
}
|
||||
} else {
|
||||
showFeeInfoAndPublishMyProposal(proposal, transaction, miningFee, txSize, fee);
|
||||
showFeeInfoAndPublishMyProposal(proposal, transaction, miningFee, txVsize, fee);
|
||||
}
|
||||
} catch (InsufficientMoneyException e) {
|
||||
if (e instanceof InsufficientBsqException) {
|
||||
|
@ -340,13 +340,13 @@ public class MakeProposalView extends ActivatableView<GridPane, Void> implements
|
|||
}
|
||||
}
|
||||
|
||||
private void showFeeInfoAndPublishMyProposal(Proposal proposal, Transaction transaction, Coin miningFee, int txSize, Coin fee) {
|
||||
private void showFeeInfoAndPublishMyProposal(Proposal proposal, Transaction transaction, Coin miningFee, int txVsize, Coin fee) {
|
||||
if (!DevEnv.isDevMode()) {
|
||||
Coin btcForIssuance = null;
|
||||
|
||||
if (proposal instanceof IssuanceProposal) btcForIssuance = ((IssuanceProposal) proposal).getRequestedBsq();
|
||||
|
||||
GUIUtil.showBsqFeeInfoPopup(fee, miningFee, btcForIssuance, txSize, bsqFormatter, btcFormatter,
|
||||
GUIUtil.showBsqFeeInfoPopup(fee, miningFee, btcForIssuance, txVsize, bsqFormatter, btcFormatter,
|
||||
Res.get("dao.proposal"), () -> doPublishMyProposal(proposal, transaction));
|
||||
} else {
|
||||
doPublishMyProposal(proposal, transaction);
|
||||
|
|
|
@ -475,12 +475,12 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
|
|||
Coin stake = ParsingUtils.parseToCoin(stakeInputTextField.getText(), bsqFormatter);
|
||||
try {
|
||||
// We create a dummy tx to get the miningFee for displaying it at the confirmation popup
|
||||
Tuple2<Coin, Integer> miningFeeAndTxSize = daoFacade.getBlindVoteMiningFeeAndTxSize(stake);
|
||||
Coin miningFee = miningFeeAndTxSize.first;
|
||||
int txSize = miningFeeAndTxSize.second;
|
||||
Tuple2<Coin, Integer> miningFeeAndTxVsize = daoFacade.getBlindVoteMiningFeeAndTxVsize(stake);
|
||||
Coin miningFee = miningFeeAndTxVsize.first;
|
||||
int txVsize = miningFeeAndTxVsize.second;
|
||||
Coin blindVoteFee = daoFacade.getBlindVoteFeeForCycle();
|
||||
if (!DevEnv.isDevMode()) {
|
||||
GUIUtil.showBsqFeeInfoPopup(blindVoteFee, miningFee, txSize, bsqFormatter, btcFormatter,
|
||||
GUIUtil.showBsqFeeInfoPopup(blindVoteFee, miningFee, txVsize, bsqFormatter, btcFormatter,
|
||||
Res.get("dao.blindVote"), () -> publishBlindVote(stake));
|
||||
} else {
|
||||
publishBlindVote(stake);
|
||||
|
|
|
@ -243,12 +243,12 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||
Transaction txWithBtcFee = btcWalletService.completePreparedSendBsqTx(preparedSendTx, true);
|
||||
Transaction signedTx = bsqWalletService.signTx(txWithBtcFee);
|
||||
Coin miningFee = signedTx.getFee();
|
||||
int txSize = signedTx.getVsize();
|
||||
int txVsize = signedTx.getVsize();
|
||||
showPublishTxPopup(receiverAmount,
|
||||
txWithBtcFee,
|
||||
TxType.TRANSFER_BSQ,
|
||||
miningFee,
|
||||
txSize,
|
||||
txVsize,
|
||||
receiversAddressInputTextField.getText(),
|
||||
bsqFormatter,
|
||||
btcFormatter,
|
||||
|
@ -305,12 +305,12 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||
if (miningFee.getValue() >= receiverAmount.getValue())
|
||||
GUIUtil.showWantToBurnBTCPopup(miningFee, receiverAmount, btcFormatter);
|
||||
else {
|
||||
int txSize = signedTx.getVsize();
|
||||
int txVsize = signedTx.getVsize();
|
||||
showPublishTxPopup(receiverAmount,
|
||||
txWithBtcFee,
|
||||
TxType.INVALID,
|
||||
miningFee,
|
||||
txSize, receiversBtcAddressInputTextField.getText(),
|
||||
txVsize, receiversBtcAddressInputTextField.getText(),
|
||||
btcFormatter,
|
||||
btcFormatter,
|
||||
() -> {
|
||||
|
@ -348,7 +348,7 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||
Transaction txWithBtcFee,
|
||||
TxType txType,
|
||||
Coin miningFee,
|
||||
int txSize, String address,
|
||||
int txVsize, String address,
|
||||
CoinFormatter amountFormatter, // can be BSQ or BTC formatter
|
||||
CoinFormatter feeFormatter,
|
||||
ResultHandler resultHandler) {
|
||||
|
@ -357,8 +357,8 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||
amountFormatter.formatCoinWithCode(receiverAmount),
|
||||
address,
|
||||
feeFormatter.formatCoinWithCode(miningFee),
|
||||
CoinUtil.getFeePerByte(miningFee, txSize),
|
||||
txSize / 1000d,
|
||||
CoinUtil.getFeePerVbyte(miningFee, txVsize),
|
||||
txVsize / 1000d,
|
||||
amountFormatter.formatCoinWithCode(receiverAmount)))
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.onAction(() -> {
|
||||
|
|
|
@ -356,20 +356,20 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
}
|
||||
|
||||
if (areInputsValid()) {
|
||||
int txSize = feeEstimationTransaction.getVsize();
|
||||
log.info("Fee for tx with size {}: {} " + Res.getBaseCurrencyCode() + "", txSize, fee.toPlainString());
|
||||
int txVsize = feeEstimationTransaction.getVsize();
|
||||
log.info("Fee for tx with size {}: {} " + Res.getBaseCurrencyCode() + "", txVsize, fee.toPlainString());
|
||||
|
||||
if (receiverAmount.isPositive()) {
|
||||
double feePerByte = CoinUtil.getFeePerByte(fee, txSize);
|
||||
double kb = txSize / 1000d;
|
||||
double feePerVbyte = CoinUtil.getFeePerVbyte(fee, txVsize);
|
||||
double vkb = txVsize / 1000d;
|
||||
|
||||
String messageText = Res.get("shared.sendFundsDetailsWithFee",
|
||||
formatter.formatCoinWithCode(sendersAmount),
|
||||
withdrawFromTextField.getText(),
|
||||
withdrawToTextField.getText(),
|
||||
formatter.formatCoinWithCode(fee),
|
||||
feePerByte,
|
||||
kb,
|
||||
feePerVbyte,
|
||||
vkb,
|
||||
formatter.formatCoinWithCode(receiverAmount));
|
||||
if (dust.isPositive()) {
|
||||
messageText = Res.get("shared.sendFundsDetailsDust",
|
||||
|
|
|
@ -129,7 +129,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
protected double marketPriceMargin = 0;
|
||||
private Coin txFeeFromFeeService = Coin.ZERO;
|
||||
private boolean marketPriceAvailable;
|
||||
private int feeTxSize = TxFeeEstimationService.TYPICAL_TX_WITH_1_INPUT_SIZE;
|
||||
private int feeTxVsize = TxFeeEstimationService.TYPICAL_TX_WITH_1_INPUT_VSIZE;
|
||||
protected boolean allowAmountUpdate = true;
|
||||
private final TradeStatisticsManager tradeStatisticsManager;
|
||||
|
||||
|
@ -262,7 +262,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
|
||||
// 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.
|
||||
txFeeFromFeeService = feeService.getTxFee(feeTxSize);
|
||||
txFeeFromFeeService = feeService.getTxFee(feeTxVsize);
|
||||
|
||||
calculateVolume();
|
||||
calculateTotalToPay();
|
||||
|
@ -301,13 +301,13 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
}
|
||||
|
||||
// This works only if we have already funds in the wallet
|
||||
public void updateEstimatedFeeAndTxSize() {
|
||||
Tuple2<Coin, Integer> estimatedFeeAndTxSize = createOfferService.getEstimatedFeeAndTxSize(amount.get(),
|
||||
public void updateEstimatedFeeAndTxVsize() {
|
||||
Tuple2<Coin, Integer> estimatedFeeAndTxVsize = createOfferService.getEstimatedFeeAndTxVsize(amount.get(),
|
||||
direction,
|
||||
buyerSecurityDeposit.get(),
|
||||
createOfferService.getSellerSecurityDepositAsDouble(buyerSecurityDeposit.get()));
|
||||
txFeeFromFeeService = estimatedFeeAndTxSize.first;
|
||||
feeTxSize = estimatedFeeAndTxSize.second;
|
||||
txFeeFromFeeService = estimatedFeeAndTxVsize.first;
|
||||
feeTxVsize = estimatedFeeAndTxVsize.second;
|
||||
}
|
||||
|
||||
void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler) {
|
||||
|
@ -439,7 +439,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
|
||||
void requestTxFee(@Nullable Runnable actionHandler) {
|
||||
feeService.requestFees(() -> {
|
||||
txFeeFromFeeService = feeService.getTxFee(feeTxSize);
|
||||
txFeeFromFeeService = feeService.getTxFee(feeTxVsize);
|
||||
calculateTotalToPay();
|
||||
if (actionHandler != null)
|
||||
actionHandler.run();
|
||||
|
|
|
@ -688,7 +688,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
|||
}
|
||||
|
||||
void onShowPayFundsScreen(Runnable actionHandler) {
|
||||
dataModel.updateEstimatedFeeAndTxSize();
|
||||
dataModel.updateEstimatedFeeAndTxVsize();
|
||||
dataModel.requestTxFee(actionHandler);
|
||||
showPayFundsScreenDisplayed.set(true);
|
||||
updateSpinnerInfo();
|
||||
|
|
|
@ -110,9 +110,9 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
private boolean isTabSelected;
|
||||
Price tradePrice;
|
||||
// Use an average of a typical trade fee tx with 1 input, deposit tx and payout tx.
|
||||
private int feeTxSize = 192; // (175+233+169)/3
|
||||
private int feeTxVsize = 192; // (175+233+169)/3
|
||||
private boolean freezeFee;
|
||||
private Coin txFeePerByteFromFeeService;
|
||||
private Coin txFeePerVbyteFromFeeService;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -213,25 +213,25 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
// 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 bytes (if one input). The trade txs has about 169-263 bytes.
|
||||
// A typical trade fee tx has about 175 vbytes (if one input). The trade txs has about 169-263 vbytes.
|
||||
// We use 192 as average value.
|
||||
|
||||
// trade fee tx: 175 bytes (1 input)
|
||||
// deposit tx: 233 bytes (1 MS output+ OP_RETURN) - 263 bytes (1 MS output + OP_RETURN + change in case of smaller trade amount)
|
||||
// payout tx: 169 bytes
|
||||
// disputed payout tx: 139 bytes
|
||||
// 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
|
||||
|
||||
// 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.
|
||||
txFeePerByteFromFeeService = feeService.getTxFeePerByte();
|
||||
txFeeFromFeeService = getTxFeeBySize(feeTxSize);
|
||||
txFeePerVbyteFromFeeService = feeService.getTxFeePerVbyte();
|
||||
txFeeFromFeeService = getTxFeeByVsize(feeTxVsize);
|
||||
|
||||
// We request to get the actual estimated fee
|
||||
log.info("Start requestTxFee: txFeeFromFeeService={}", txFeeFromFeeService);
|
||||
feeService.requestFees(() -> {
|
||||
if (!freezeFee) {
|
||||
txFeePerByteFromFeeService = feeService.getTxFeePerByte();
|
||||
txFeeFromFeeService = getTxFeeBySize(feeTxSize);
|
||||
txFeePerVbyteFromFeeService = feeService.getTxFeePerVbyte();
|
||||
txFeeFromFeeService = getTxFeeByVsize(feeTxVsize);
|
||||
calculateTotalToPay();
|
||||
log.info("Completed requestTxFee: txFeeFromFeeService={}", txFeeFromFeeService);
|
||||
} else {
|
||||
|
@ -257,7 +257,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
|
||||
// We don't want that the fee gets updated anymore after we show the funding screen.
|
||||
void onShowPayFundsScreen() {
|
||||
estimateTxSize();
|
||||
estimateTxVsize();
|
||||
freezeFee = true;
|
||||
calculateTotalToPay();
|
||||
}
|
||||
|
@ -338,8 +338,8 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
// leading to a smaller tx and too high fees. Simply updating the fee estimation would lead to changed required funds
|
||||
// and if funds get higher (if tx get larger) the user would get confused (adding small inputs would increase total required funds).
|
||||
// So that would require more thoughts how to deal with all those cases.
|
||||
public void estimateTxSize() {
|
||||
int txSize = 0;
|
||||
public void estimateTxVsize() {
|
||||
int txVsize = 0;
|
||||
if (btcWalletService.getBalance(Wallet.BalanceType.AVAILABLE).isPositive()) {
|
||||
Coin fundsNeededForTrade = getFundsNeededForTrade();
|
||||
if (isBuyOffer())
|
||||
|
@ -347,28 +347,28 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
|
||||
// As taker we pay 3 times the fee and currently the fee is the same for all 3 txs (trade fee tx, deposit
|
||||
// tx and payout tx).
|
||||
// We should try to change that in future to have the deposit and payout tx with a fixed fee as the size is
|
||||
// We should try to change that in future to have the deposit and payout tx with a fixed fee as the vsize is
|
||||
// there more deterministic.
|
||||
// The trade fee tx can be in the worst case very large if there are many inputs so if we take that tx alone
|
||||
// for the fee estimation we would overpay a lot.
|
||||
// On the other side if we have the best case of a 1 input tx fee tx then it is only 175 bytes but the
|
||||
// other 2 txs are different (233 and 169 bytes) and may get a lower fee/byte as intended.
|
||||
// On the other side if we have the best case of a 1 input tx fee tx then it is only 175 vbytes but the
|
||||
// other 2 txs are different (233 and 169 vbytes) and may get a lower fee/vbyte as intended.
|
||||
// We apply following model to not overpay too much but be on the safe side as well.
|
||||
// We sum the taker fee tx and the deposit tx together as it can be assumed that both be in the same block and
|
||||
// as they are dependent txs the miner will pick both if the fee in total is good enough.
|
||||
// We make sure that the fee is sufficient to meet our intended fee/byte for the larger deposit tx with 233 bytes.
|
||||
Tuple2<Coin, Integer> estimatedFeeAndTxSize = txFeeEstimationService.getEstimatedFeeAndTxSizeForTaker(fundsNeededForTrade,
|
||||
// We make sure that the fee is sufficient to meet our intended fee/vbyte for the larger deposit tx with 233 vbytes.
|
||||
Tuple2<Coin, Integer> estimatedFeeAndTxVsize = txFeeEstimationService.getEstimatedFeeAndTxVsizeForTaker(fundsNeededForTrade,
|
||||
getTakerFee());
|
||||
txFeeFromFeeService = estimatedFeeAndTxSize.first;
|
||||
feeTxSize = estimatedFeeAndTxSize.second;
|
||||
txFeeFromFeeService = estimatedFeeAndTxVsize.first;
|
||||
feeTxVsize = estimatedFeeAndTxVsize.second;
|
||||
} else {
|
||||
feeTxSize = 233;
|
||||
txFeeFromFeeService = txFeePerByteFromFeeService.multiply(feeTxSize);
|
||||
feeTxVsize = 233;
|
||||
txFeeFromFeeService = txFeePerVbyteFromFeeService.multiply(feeTxVsize);
|
||||
log.info("We cannot do the fee estimation because there are no funds in the wallet.\nThis is expected " +
|
||||
"if the user has not funded their wallet yet.\n" +
|
||||
"In that case we use an estimated tx size of 233 bytes.\n" +
|
||||
"txFee based on estimated size of {} bytes. feeTxSize = {} bytes. Actual tx size = {} bytes. TxFee is {} ({} sat/byte)",
|
||||
feeTxSize, feeTxSize, txSize, txFeeFromFeeService.toFriendlyString(), feeService.getTxFeePerByte());
|
||||
"In that case we use an estimated tx vsize of 233 vbytes.\n" +
|
||||
"txFee based on estimated vsize of {} vbytes. feeTxVsize = {} vbytes. Actual tx vsize = {} vbytes. TxFee is {} ({} sat/vbyte)",
|
||||
feeTxVsize, feeTxVsize, txVsize, txFeeFromFeeService.toFriendlyString(), feeService.getTxFeePerVbyte());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -526,16 +526,16 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
btcWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
||||
}
|
||||
|
||||
// We use the sum of the size of the trade fee and the deposit tx to get an average.
|
||||
// We use the sum of the vsize of the trade fee and the deposit tx to get an average.
|
||||
// Miners will take the trade fee tx if the total fee of both dependent txs are good enough.
|
||||
// With that we avoid that we overpay in case that the trade fee has many inputs and we would apply that fee for the
|
||||
// other 2 txs as well. We still might overpay a bit for the payout tx.
|
||||
private int getAverageSize(int txSize) {
|
||||
return (txSize + 233) / 2;
|
||||
private int getAverageVsize(int txVsize) {
|
||||
return (txVsize + 233) / 2;
|
||||
}
|
||||
|
||||
private Coin getTxFeeBySize(int sizeInBytes) {
|
||||
return txFeePerByteFromFeeService.multiply(getAverageSize(sizeInBytes));
|
||||
private Coin getTxFeeByVsize(int vsizeInVbytes) {
|
||||
return txFeePerVbyteFromFeeService.multiply(getAverageVsize(vsizeInVbytes));
|
||||
}
|
||||
|
||||
/* private void setFeeFromFundingTx(Coin fee) {
|
||||
|
|
|
@ -672,11 +672,11 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
Coin sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
|
||||
String sellerPayoutAddressString = contract.getSellerPayoutAddressString();
|
||||
Coin outputAmount = buyerPayoutAmount.add(sellerPayoutAmount);
|
||||
Tuple2<Coin, Integer> feeTuple = txFeeEstimationService.getEstimatedFeeAndTxSize(outputAmount, feeService, btcWalletService);
|
||||
Tuple2<Coin, Integer> feeTuple = txFeeEstimationService.getEstimatedFeeAndTxVsize(outputAmount, feeService, btcWalletService);
|
||||
Coin fee = feeTuple.first;
|
||||
Integer txSize = feeTuple.second;
|
||||
double feePerByte = CoinUtil.getFeePerByte(fee, txSize);
|
||||
double kb = txSize / 1000d;
|
||||
Integer txVsize = feeTuple.second;
|
||||
double feePerVbyte = CoinUtil.getFeePerVbyte(fee, txVsize);
|
||||
double vkb = txVsize / 1000d;
|
||||
Coin inputAmount = outputAmount.add(fee);
|
||||
String buyerDetails = "";
|
||||
if (buyerPayoutAmount.isPositive()) {
|
||||
|
@ -698,8 +698,8 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
buyerDetails,
|
||||
sellerDetails,
|
||||
formatter.formatCoinWithCode(fee),
|
||||
feePerByte,
|
||||
kb))
|
||||
feePerVbyte,
|
||||
vkb))
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.onAction(() -> {
|
||||
doPayout(buyerPayoutAmount,
|
||||
|
|
|
@ -205,9 +205,9 @@ public class BuyerStep4View extends TradeStepView {
|
|||
validateWithdrawAddress();
|
||||
} else if (Restrictions.isAboveDust(receiverAmount)) {
|
||||
CoinFormatter formatter = model.btcFormatter;
|
||||
int txSize = feeEstimationTransaction.getVsize();
|
||||
double feePerByte = CoinUtil.getFeePerByte(fee, txSize);
|
||||
double kb = txSize / 1000d;
|
||||
int txVsize = feeEstimationTransaction.getVsize();
|
||||
double feePerVbyte = CoinUtil.getFeePerVbyte(fee, txVsize);
|
||||
double vkb = txVsize / 1000d;
|
||||
String recAmount = formatter.formatCoinWithCode(receiverAmount);
|
||||
new Popup().headLine(Res.get("portfolio.pending.step5_buyer.confirmWithdrawal"))
|
||||
.confirmation(Res.get("shared.sendFundsDetailsWithFee",
|
||||
|
@ -215,8 +215,8 @@ public class BuyerStep4View extends TradeStepView {
|
|||
fromAddresses,
|
||||
toAddresses,
|
||||
formatter.formatCoinWithCode(fee),
|
||||
feePerByte,
|
||||
kb,
|
||||
feePerVbyte,
|
||||
vkb,
|
||||
recAmount))
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.onAction(() -> doWithdrawal(amount, fee))
|
||||
|
|
|
@ -270,9 +270,9 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
preferences.setUseCustomWithdrawalTxFee(newValue);
|
||||
transactionFeeInputTextField.setEditable(newValue);
|
||||
if (!newValue) {
|
||||
transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerByte().value));
|
||||
transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerVbyte().value));
|
||||
try {
|
||||
preferences.setWithdrawalTxFeeInBytes(feeService.getTxFeePerByte().value);
|
||||
preferences.setWithdrawalTxFeeInVbytes(feeService.getTxFeePerVbyte().value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
@ -283,18 +283,18 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
|
||||
transactionFeeFocusedListener = (o, oldValue, newValue) -> {
|
||||
if (oldValue && !newValue) {
|
||||
String estimatedFee = String.valueOf(feeService.getTxFeePerByte().value);
|
||||
String estimatedFee = String.valueOf(feeService.getTxFeePerVbyte().value);
|
||||
try {
|
||||
int withdrawalTxFeePerByte = Integer.parseInt(transactionFeeInputTextField.getText());
|
||||
final long minFeePerByte = Config.baseCurrencyNetwork().getDefaultMinFeePerByte();
|
||||
if (withdrawalTxFeePerByte < minFeePerByte) {
|
||||
new Popup().warning(Res.get("setting.preferences.txFeeMin", minFeePerByte)).show();
|
||||
int withdrawalTxFeePerVbyte = Integer.parseInt(transactionFeeInputTextField.getText());
|
||||
final long minFeePerVbyte = Config.baseCurrencyNetwork().getDefaultMinFeePerVbyte();
|
||||
if (withdrawalTxFeePerVbyte < minFeePerVbyte) {
|
||||
new Popup().warning(Res.get("setting.preferences.txFeeMin", minFeePerVbyte)).show();
|
||||
transactionFeeInputTextField.setText(estimatedFee);
|
||||
} else if (withdrawalTxFeePerByte > 5000) {
|
||||
} else if (withdrawalTxFeePerVbyte > 5000) {
|
||||
new Popup().warning(Res.get("setting.preferences.txFeeTooLarge")).show();
|
||||
transactionFeeInputTextField.setText(estimatedFee);
|
||||
} else {
|
||||
preferences.setWithdrawalTxFeeInBytes(withdrawalTxFeePerByte);
|
||||
preferences.setWithdrawalTxFeeInVbytes(withdrawalTxFeePerVbyte);
|
||||
}
|
||||
} catch (NumberFormatException t) {
|
||||
log.error(t.toString());
|
||||
|
@ -309,7 +309,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
}
|
||||
}
|
||||
};
|
||||
transactionFeeChangeListener = (observable, oldValue, newValue) -> transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerByte().value));
|
||||
transactionFeeChangeListener = (observable, oldValue, newValue) -> transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerVbyte().value));
|
||||
|
||||
// deviation
|
||||
deviationInputTextField = addInputTextField(root, ++gridRow,
|
||||
|
@ -795,11 +795,11 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
|
||||
transactionFeeInputTextField.setEditable(useCustomWithdrawalTxFee);
|
||||
if (!useCustomWithdrawalTxFee) {
|
||||
transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerByte().value));
|
||||
transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerVbyte().value));
|
||||
feeService.feeUpdateCounterProperty().addListener(transactionFeeChangeListener);
|
||||
}
|
||||
|
||||
transactionFeeInputTextField.setText(String.valueOf(getTxFeeForWithdrawalPerByte()));
|
||||
transactionFeeInputTextField.setText(String.valueOf(getTxFeeForWithdrawalPerVbyte()));
|
||||
ignoreTradersListInputTextField.setText(String.join(", ", preferences.getIgnoreTradersList()));
|
||||
/* referralIdService.getOptionalReferralId().ifPresent(referralId -> referralIdInputTextField.setText(referralId));
|
||||
referralIdInputTextField.setPromptText(Res.get("setting.preferences.refererId.prompt"));*/
|
||||
|
@ -870,10 +870,10 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
ignoreDustThresholdInputTextField.textProperty().addListener(ignoreDustThresholdListener);
|
||||
}
|
||||
|
||||
private Coin getTxFeeForWithdrawalPerByte() {
|
||||
private Coin getTxFeeForWithdrawalPerVbyte() {
|
||||
Coin fee = (preferences.isUseCustomWithdrawalTxFee()) ?
|
||||
Coin.valueOf(preferences.getWithdrawalTxFeeInBytes()) :
|
||||
feeService.getTxFeePerByte();
|
||||
Coin.valueOf(preferences.getWithdrawalTxFeeInVbytes()) :
|
||||
feeService.getTxFeePerVbyte();
|
||||
log.info("tx fee = " + fee.toFriendlyString());
|
||||
return fee;
|
||||
}
|
||||
|
|
|
@ -194,7 +194,7 @@ public class GUIUtil {
|
|||
public static void showFeeInfoBeforeExecute(Runnable runnable) {
|
||||
String key = "miningFeeInfo";
|
||||
if (!DevEnv.isDevMode() && DontShowAgainLookup.showAgain(key)) {
|
||||
new Popup().attention(Res.get("guiUtil.miningFeeInfo", String.valueOf(GUIUtil.feeService.getTxFeePerByte().value)))
|
||||
new Popup().attention(Res.get("guiUtil.miningFeeInfo", String.valueOf(GUIUtil.feeService.getTxFeePerVbyte().value)))
|
||||
.onClose(runnable)
|
||||
.useIUnderstandButton()
|
||||
.show();
|
||||
|
@ -911,7 +911,7 @@ public class GUIUtil {
|
|||
public static void showBsqFeeInfoPopup(Coin fee,
|
||||
Coin miningFee,
|
||||
Coin btcForIssuance,
|
||||
int txSize,
|
||||
int txVsize,
|
||||
BsqFormatter bsqFormatter,
|
||||
CoinFormatter btcFormatter,
|
||||
String type,
|
||||
|
@ -925,16 +925,16 @@ public class GUIUtil {
|
|||
bsqFormatter.formatBTCWithCode(btcForIssuance),
|
||||
100,
|
||||
btcFormatter.formatCoinWithCode(miningFee),
|
||||
CoinUtil.getFeePerByte(miningFee, txSize),
|
||||
txSize / 1000d,
|
||||
CoinUtil.getFeePerVbyte(miningFee, txVsize),
|
||||
txVsize / 1000d,
|
||||
type);
|
||||
} else {
|
||||
confirmationMessage = Res.get("dao.feeTx.confirm.details",
|
||||
StringUtils.capitalize(type),
|
||||
bsqFormatter.formatCoinWithCode(fee),
|
||||
btcFormatter.formatCoinWithCode(miningFee),
|
||||
CoinUtil.getFeePerByte(miningFee, txSize),
|
||||
txSize / 1000d,
|
||||
CoinUtil.getFeePerVbyte(miningFee, txVsize),
|
||||
txVsize / 1000d,
|
||||
type);
|
||||
}
|
||||
new Popup().headLine(Res.get("dao.feeTx.confirm", type))
|
||||
|
@ -945,10 +945,10 @@ public class GUIUtil {
|
|||
.show();
|
||||
}
|
||||
|
||||
public static void showBsqFeeInfoPopup(Coin fee, Coin miningFee, int txSize, BsqFormatter bsqFormatter,
|
||||
public static void showBsqFeeInfoPopup(Coin fee, Coin miningFee, int txVsize, BsqFormatter bsqFormatter,
|
||||
CoinFormatter btcFormatter, String type,
|
||||
Runnable actionHandler) {
|
||||
showBsqFeeInfoPopup(fee, miningFee, null, txSize, bsqFormatter, btcFormatter, type, actionHandler);
|
||||
showBsqFeeInfoPopup(fee, miningFee, null, txVsize, bsqFormatter, btcFormatter, type, actionHandler);
|
||||
}
|
||||
|
||||
public static void setFitToRowsForTableView(TableView<?> tableView,
|
||||
|
|
|
@ -26,7 +26,7 @@ import java.time.Duration;
|
|||
*/
|
||||
public abstract class FeeRateProvider extends PriceProvider<FeeRate> {
|
||||
|
||||
public static final long MIN_FEE_RATE = 10; // satoshi/byte
|
||||
public static final long MIN_FEE_RATE = 10; // satoshi/vbyte
|
||||
public static final long MAX_FEE_RATE = 1000;
|
||||
|
||||
public FeeRateProvider(Duration refreshInterval) {
|
||||
|
|
|
@ -95,7 +95,7 @@ abstract class MempoolFeeRateProvider extends FeeRateProvider {
|
|||
.map(Map.Entry::getValue)
|
||||
.findFirst()
|
||||
.map(r -> {
|
||||
log.info("Retrieved estimated mining fee of {} sat/byte from {}", r, getMempoolApiHostname());
|
||||
log.info("Retrieved estimated mining fee of {} sat/vbyte from {}", r, getMempoolApiHostname());
|
||||
return r;
|
||||
})
|
||||
.map(r -> Math.max(r, MIN_FEE_RATE))
|
||||
|
|
|
@ -1546,7 +1546,7 @@ message PreferencesPayload {
|
|||
bool use_tor_for_bitcoin_j = 12;
|
||||
bool show_own_offers_in_offer_book = 13;
|
||||
TradeCurrency preferred_trade_currency = 14;
|
||||
int64 withdrawal_tx_fee_in_bytes = 15;
|
||||
int64 withdrawal_tx_fee_in_vbytes = 15;
|
||||
bool use_custom_withdrawal_tx_fee = 16;
|
||||
double max_price_distance_in_percent = 17;
|
||||
string offer_book_chart_screen_currency_code = 18;
|
||||
|
|
Loading…
Add table
Reference in a new issue