mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-07-24 15:35:34 -04:00
Refactor fee handling, add user defined fee to settings, UI fixes
This commit is contained in:
parent
35d6612820
commit
f723bf5737
36 changed files with 255 additions and 143 deletions
|
@ -18,39 +18,67 @@
|
|||
package io.bitsquare.btc;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Wallet;
|
||||
|
||||
public class FeePolicy {
|
||||
|
||||
// Official min. fee and fee per kiloByte dropped down to 0.00001 BTC / Coin.valueOf(1000) / 1000 satoshis, but as there are reported problems with
|
||||
// confirmation we use a hgher value.
|
||||
// The should also help to avoid problems when the tx size is larger as the standard (e.g. if the user does not pay
|
||||
// in with one transaction but several tx). We don't do a dynamically fee calculation as we need predictable amounts, so that should help to get a larger
|
||||
// headroom.
|
||||
// Andreas Schildbach reported problems with confirmation and increased the fee/offered UI side fee setting.
|
||||
|
||||
// http://www.cointape.com/
|
||||
// The fastest and cheapest transaction fee is currently 50 satoshis/byte, shown in green at the top.
|
||||
// For the average transaction size of 597 bytes, this results in a fee of 298 bits (0.298 mBTC). -> 0.0003 BTC or Coin.valueOf(30000);
|
||||
|
||||
// With block getting filled up the needed fee to get fast into a black has become more expensive and less predictable.
|
||||
// To see current fees check out:
|
||||
// https://tradeblock.com/blockchain
|
||||
// http://www.cointape.com
|
||||
// Average values are 10-100 satoshis/byte in january 2016
|
||||
//
|
||||
// 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
|
||||
// deposit tx: 336 bytes
|
||||
// payout tx: 371 bytes
|
||||
// disputed payout tx: 408 bytes -> 20400 satoshis with 50 satoshis/byte
|
||||
// disputed payout tx: 408 bytes
|
||||
|
||||
// Other good source is: https://tradeblock.com/blockchain 15-100 satoshis/byte
|
||||
|
||||
public static final Coin TX_FEE = Coin.valueOf(30000); // 0.0003 BTC about 1.2 EUR @ 400 EUR/BTC: about 100 satoshi /byte
|
||||
|
||||
static {
|
||||
Wallet.SendRequest.DEFAULT_FEE_PER_KB = TX_FEE;
|
||||
// We set a fixed fee to make the needed amounts in the trade predictable.
|
||||
// We use 0.0003 BTC (0.12 EUR @ 400 EUR/BTC) which is for our tx sizes about 75-150 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
|
||||
public static Coin getFixedTxFeeForTrades() {
|
||||
return Coin.valueOf(30_000);
|
||||
}
|
||||
|
||||
public static final Coin CREATE_OFFER_FEE = Coin.valueOf(100000); // 0.001 BTC 0.1% of 1 BTC about 0.4 EUR @ 400 EUR/BTC
|
||||
public static final Coin TAKE_OFFER_FEE = CREATE_OFFER_FEE;
|
||||
// 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 FEE_PER_KB = Coin.valueOf(30_000); // 0.0003 BTC about 0.12 EUR @ 400 EUR/BTC
|
||||
|
||||
// TODO make final again later
|
||||
public static Coin SECURITY_DEPOSIT = Coin.valueOf(10000000); // 0.1 BTC; about 4 EUR @ 400 EUR/BTC
|
||||
public static void setFeePerKb(Coin feePerKb) {
|
||||
FEE_PER_KB = feePerKb;
|
||||
}
|
||||
|
||||
public static Coin getFeePerKb() {
|
||||
return FEE_PER_KB;
|
||||
}
|
||||
|
||||
|
||||
// 0.001 BTC 0.1% of 1 BTC about 0.4 EUR @ 400 EUR/BTC
|
||||
public static Coin getCreateOfferFee() {
|
||||
return Coin.valueOf(100_000);
|
||||
}
|
||||
|
||||
// Currently we use the same fee for both offerer and taker
|
||||
public static Coin getTakeOfferFee() {
|
||||
return getCreateOfferFee();
|
||||
}
|
||||
|
||||
|
||||
// TODO make final again later 100_000_000
|
||||
// 0.1 BTC; about 4 EUR @ 400 EUR/BTC
|
||||
private static Coin SECURITY_DEPOSIT = Coin.valueOf(10_000_000);
|
||||
|
||||
public static Coin getSecurityDeposit() {
|
||||
return SECURITY_DEPOSIT;
|
||||
}
|
||||
|
||||
// Called from WalletService to reduce SECURITY_DEPOSIT for mainnet to 0.01 btc
|
||||
// TODO remove later when tested enough
|
||||
|
|
|
@ -34,7 +34,11 @@ public class Restrictions {
|
|||
MAX_TRADE_AMOUNT = maxTradeAmount;
|
||||
}
|
||||
|
||||
public static boolean isMinSpendableAmount(Coin amount) {
|
||||
return amount != null && amount.compareTo(FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT)) > 0;
|
||||
public static boolean isAboveFixedTxFeeAndDust(Coin amount) {
|
||||
return amount != null && amount.compareTo(FeePolicy.getFixedTxFeeForTrades().add(Transaction.MIN_NONDUST_OUTPUT)) > 0;
|
||||
}
|
||||
|
||||
public static boolean isAboveDust(Coin amount) {
|
||||
return amount != null && amount.compareTo(Transaction.MIN_NONDUST_OUTPUT) > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,9 +142,9 @@ public class TradeWalletService {
|
|||
public Transaction createTradingFeeTx(AddressEntry addressEntry, Coin tradingFee, String feeReceiverAddresses)
|
||||
throws InsufficientMoneyException, AddressFormatException {
|
||||
Transaction tradingFeeTx = new Transaction(params);
|
||||
Preconditions.checkArgument(Restrictions.isMinSpendableAmount(tradingFee),
|
||||
Preconditions.checkArgument(Restrictions.isAboveFixedTxFeeAndDust(tradingFee),
|
||||
"You cannot send an amount which are smaller than the fee + dust output.");
|
||||
Coin outPutAmount = tradingFee.subtract(FeePolicy.TX_FEE);
|
||||
Coin outPutAmount = tradingFee.subtract(FeePolicy.getFixedTxFeeForTrades());
|
||||
tradingFeeTx.addOutput(outPutAmount, new Address(params, feeReceiverAddresses));
|
||||
|
||||
// we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to
|
||||
|
@ -154,14 +154,13 @@ public class TradeWalletService {
|
|||
sendRequest.shuffleOutputs = false;
|
||||
sendRequest.aesKey = aesKey;
|
||||
sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry);
|
||||
|
||||
// We use a fixed fee
|
||||
sendRequest.feePerKb = Coin.ZERO;
|
||||
sendRequest.fee = FeePolicy.getFixedTxFeeForTrades();
|
||||
// We use always the same address for all transactions in a trade to keep things simple.
|
||||
// To be discussed if that introduce any privacy issues.
|
||||
sendRequest.changeAddress = addressEntry.getAddress();
|
||||
|
||||
// Wallet.SendRequest.DEFAULT_FEE_PER_KB is set in FeePolicy to our defined amount
|
||||
// We don't want to risk delayed transactions so we set the fee rather high.
|
||||
// Delayed tx will lead to a broken chain of the deposit transaction.
|
||||
wallet.completeTx(sendRequest);
|
||||
printTxWithInputs("tradingFeeTx", tradingFeeTx);
|
||||
|
||||
|
@ -212,7 +211,7 @@ public class TradeWalletService {
|
|||
*/
|
||||
|
||||
// inputAmount includes the tx fee. So we subtract the fee to get the dummyOutputAmount.
|
||||
Coin dummyOutputAmount = inputAmount.subtract(FeePolicy.TX_FEE);
|
||||
Coin dummyOutputAmount = inputAmount.subtract(FeePolicy.getFixedTxFeeForTrades());
|
||||
|
||||
Transaction dummyTX = new Transaction(params);
|
||||
// The output is just used to get the right inputs and change outputs, so we use an anonymous ECKey, as it will never be used for anything.
|
||||
|
@ -303,7 +302,7 @@ public class TradeWalletService {
|
|||
// First we construct a dummy TX to get the inputs and outputs we want to use for the real deposit tx.
|
||||
// Similar to the way we did in the createTakerDepositTxInputs method.
|
||||
Transaction dummyTx = new Transaction(params);
|
||||
Coin dummyOutputAmount = offererInputAmount.subtract(FeePolicy.TX_FEE);
|
||||
Coin dummyOutputAmount = offererInputAmount.subtract(FeePolicy.getFixedTxFeeForTrades());
|
||||
TransactionOutput dummyOutput = new TransactionOutput(params, dummyTx, dummyOutputAmount, new ECKey().toAddress(params));
|
||||
dummyTx.addOutput(dummyOutput);
|
||||
addAvailableInputsAndChangeOutputs(dummyTx, offererAddressInfo);
|
||||
|
@ -640,7 +639,6 @@ public class TradeWalletService {
|
|||
return payoutTx;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Dispute
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -694,12 +692,15 @@ public class TradeWalletService {
|
|||
preparedPayoutTx.addOutput(buyerPayoutAmount, new Address(params, buyerAddressString));
|
||||
if (sellerPayoutAmount.isGreaterThan(Coin.ZERO))
|
||||
preparedPayoutTx.addOutput(sellerPayoutAmount, new Address(params, sellerAddressString));
|
||||
if (arbitratorPayoutAmount.isGreaterThan(Coin.ZERO))
|
||||
if (arbitratorPayoutAmount.isGreaterThan(Coin.ZERO) && arbitratorAddressEntry.getAddressString() != null)
|
||||
preparedPayoutTx.addOutput(arbitratorPayoutAmount, new Address(params, arbitratorAddressEntry.getAddressString()));
|
||||
|
||||
// take care of sorting!
|
||||
Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
|
||||
Sha256Hash sigHash = preparedPayoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false);
|
||||
if (arbitratorAddressEntry.getKeyPair() == null)
|
||||
throw new RuntimeException("Unexpected null value: arbitratorAddressEntry.getKeyPair() must not be null");
|
||||
|
||||
ECKey.ECDSASignature arbitratorSignature = arbitratorAddressEntry.getKeyPair().sign(sigHash, aesKey).toCanonicalised();
|
||||
|
||||
verifyTransaction(preparedPayoutTx);
|
||||
|
@ -1026,6 +1027,9 @@ public class TradeWalletService {
|
|||
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(transaction);
|
||||
sendRequest.shuffleOutputs = false;
|
||||
sendRequest.aesKey = aesKey;
|
||||
// We use a fixed fee
|
||||
sendRequest.feePerKb = Coin.ZERO;
|
||||
sendRequest.fee = FeePolicy.getFixedTxFeeForTrades();
|
||||
// we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to wait for 1 confirmation)
|
||||
sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry);
|
||||
// We use always the same address in a trade for all transactions
|
||||
|
@ -1052,4 +1056,5 @@ public class TradeWalletService {
|
|||
}
|
||||
return balance;
|
||||
}*/
|
||||
|
||||
}
|
||||
|
|
|
@ -438,15 +438,29 @@ public class WalletService {
|
|||
// Withdrawal
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public String sendFunds(String fromAddress,
|
||||
String toAddress,
|
||||
Coin amount,
|
||||
KeyParameter aesKey,
|
||||
FutureCallback<Transaction> callback) throws AddressFormatException, IllegalArgumentException, InsufficientMoneyException {
|
||||
public Coin getRequiredFee(String fromAddress,
|
||||
String toAddress,
|
||||
Coin amount) throws AddressFormatException, IllegalArgumentException, InsufficientMoneyException {
|
||||
Coin fee;
|
||||
try {
|
||||
wallet.completeTx(getSendRequest(fromAddress, toAddress, amount, null));
|
||||
fee = Coin.ZERO;
|
||||
} catch (InsufficientMoneyException e) {
|
||||
log.info("The amount to be transferred is not enough to pay the transaction fees of {}. " +
|
||||
"We subtract that fee from the receivers amount to make the transaction possible.");
|
||||
fee = e.missing;
|
||||
}
|
||||
return fee;
|
||||
}
|
||||
|
||||
public Wallet.SendRequest getSendRequest(String fromAddress,
|
||||
String toAddress,
|
||||
Coin amount,
|
||||
@Nullable KeyParameter aesKey) throws AddressFormatException, IllegalArgumentException, InsufficientMoneyException {
|
||||
Transaction tx = new Transaction(params);
|
||||
Preconditions.checkArgument(Restrictions.isMinSpendableAmount(amount),
|
||||
"You cannot send an amount which are smaller than the fee + dust output.");
|
||||
tx.addOutput(amount.subtract(FeePolicy.TX_FEE), new Address(params, toAddress));
|
||||
Preconditions.checkArgument(Restrictions.isAboveDust(amount),
|
||||
"You cannot send an amount which are smaller than 546 satoshis.");
|
||||
tx.addOutput(amount, new Address(params, toAddress));
|
||||
|
||||
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
|
||||
sendRequest.aesKey = aesKey;
|
||||
|
@ -457,13 +471,21 @@ public class WalletService {
|
|||
|
||||
sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry.get());
|
||||
sendRequest.changeAddress = addressEntry.get().getAddress();
|
||||
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
||||
sendRequest.feePerKb = FeePolicy.getFeePerKb();
|
||||
return sendRequest;
|
||||
}
|
||||
|
||||
public String sendFunds(String fromAddress,
|
||||
String toAddress,
|
||||
Coin amount,
|
||||
KeyParameter aesKey,
|
||||
FutureCallback<Transaction> callback) throws AddressFormatException, IllegalArgumentException, InsufficientMoneyException {
|
||||
Coin fee = getRequiredFee(fromAddress, toAddress, amount);
|
||||
Wallet.SendResult sendResult = wallet.sendCoins(getSendRequest(fromAddress, toAddress, amount.subtract(fee), aesKey));
|
||||
Futures.addCallback(sendResult.broadcastComplete, callback);
|
||||
|
||||
printTxWithInputs("sendFunds", tx);
|
||||
log.debug("tx=" + tx);
|
||||
|
||||
return tx.getHashAsString();
|
||||
printTxWithInputs("sendFunds", sendResult.tx);
|
||||
return sendResult.tx.getHashAsString();
|
||||
}
|
||||
|
||||
public void emptyWallet(String toAddress, KeyParameter aesKey, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler)
|
||||
|
@ -471,6 +493,7 @@ public class WalletService {
|
|||
Wallet.SendRequest sendRequest = Wallet.SendRequest.emptyWallet(new Address(params, toAddress));
|
||||
sendRequest.aesKey = aesKey;
|
||||
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
||||
sendRequest.feePerKb = FeePolicy.getFeePerKb();
|
||||
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>() {
|
||||
@Override
|
||||
public void onSuccess(Transaction result) {
|
||||
|
|
|
@ -79,7 +79,7 @@ public class BuyerAsOffererTrade extends BuyerTrade implements OffererTrade, Ser
|
|||
public Coin getPayoutAmount() {
|
||||
checkNotNull(getTradeAmount(), "Invalid state: getTradeAmount() = null");
|
||||
|
||||
return FeePolicy.SECURITY_DEPOSIT.add(getTradeAmount());
|
||||
return FeePolicy.getSecurityDeposit().add(getTradeAmount());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -80,6 +80,6 @@ public class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade, Seriali
|
|||
public Coin getPayoutAmount() {
|
||||
checkNotNull(getTradeAmount(), "Invalid state: getTradeAmount() = null");
|
||||
|
||||
return FeePolicy.SECURITY_DEPOSIT.add(getTradeAmount());
|
||||
return FeePolicy.getSecurityDeposit().add(getTradeAmount());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -368,7 +368,7 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||
}
|
||||
|
||||
public Coin getPayoutAmount() {
|
||||
return FeePolicy.SECURITY_DEPOSIT;
|
||||
return FeePolicy.getSecurityDeposit();
|
||||
}
|
||||
|
||||
public ProcessModel getProcessModel() {
|
||||
|
|
|
@ -131,7 +131,6 @@ public class TradeManager {
|
|||
log.trace("onMailboxMessageAdded senderAddress: " + senderAddress);
|
||||
Message message = decryptedMsgWithPubKey.message;
|
||||
if (message instanceof PayDepositRequest) {
|
||||
//TODO is that used????
|
||||
PayDepositRequest payDepositRequest = (PayDepositRequest) message;
|
||||
log.trace("Received payDepositRequest: " + payDepositRequest);
|
||||
if (payDepositRequest.getSenderAddress().equals(senderAddress))
|
||||
|
@ -163,12 +162,6 @@ public class TradeManager {
|
|||
// Lifecycle
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// When all services are initialized we create the protocols for our open offers and persisted pendingTrades
|
||||
// OffererAsBuyerProtocol listens for take offer requests, so we need to instantiate it early.
|
||||
public void onAllServicesInitialized() {
|
||||
log.trace("onAllServicesInitialized");
|
||||
}
|
||||
|
||||
private void initPendingTrades() {
|
||||
if (firstPeerAuthenticatedListener != null) p2PService.removeP2PServiceListener(firstPeerAuthenticatedListener);
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ public class BroadcastCreateOfferFeeTx extends Task<PlaceOfferModel> {
|
|||
protected void run() {
|
||||
try {
|
||||
runInterceptHook();
|
||||
Coin totalsNeeded = FeePolicy.SECURITY_DEPOSIT.add(FeePolicy.CREATE_OFFER_FEE).add(FeePolicy.TX_FEE);
|
||||
Coin totalsNeeded = FeePolicy.getSecurityDeposit().add(FeePolicy.getCreateOfferFee()).add(FeePolicy.getFixedTxFeeForTrades());
|
||||
AddressEntry addressEntry = model.walletService.getAddressEntryByOfferId(model.offer.getId());
|
||||
Coin balance = model.walletService.getBalanceForAddress(addressEntry.getAddress());
|
||||
if (balance.compareTo(totalsNeeded) >= 0) {
|
||||
|
|
|
@ -45,7 +45,7 @@ public class CreateOfferFeeTx extends Task<PlaceOfferModel> {
|
|||
Arbitrator selectedArbitrator = model.user.getAcceptedArbitratorByAddress(selectedArbitratorAddress);
|
||||
Transaction transaction = model.tradeWalletService.createTradingFeeTx(
|
||||
model.walletService.getAddressEntryByOfferId(model.offer.getId()),
|
||||
FeePolicy.CREATE_OFFER_FEE,
|
||||
FeePolicy.getCreateOfferFee(),
|
||||
selectedArbitrator.getBtcAddress());
|
||||
|
||||
// We assume there will be no tx malleability. We add a check later in case the published offer has a different hash.
|
||||
|
|
|
@ -41,8 +41,8 @@ public class CreateAndSignDepositTxAsBuyer extends TradeTask {
|
|||
try {
|
||||
runInterceptHook();
|
||||
checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null");
|
||||
Coin buyerInputAmount = FeePolicy.SECURITY_DEPOSIT.add(FeePolicy.TX_FEE);
|
||||
Coin msOutputAmount = buyerInputAmount.add(FeePolicy.SECURITY_DEPOSIT).add(trade.getTradeAmount());
|
||||
Coin buyerInputAmount = FeePolicy.getSecurityDeposit().add(FeePolicy.getFixedTxFeeForTrades());
|
||||
Coin msOutputAmount = buyerInputAmount.add(FeePolicy.getSecurityDeposit()).add(trade.getTradeAmount());
|
||||
|
||||
log.info("\n\n------------------------------------------------------------\n"
|
||||
+ "Contract as json\n"
|
||||
|
|
|
@ -37,7 +37,7 @@ public class CreateDepositTxInputsAsBuyer extends TradeTask {
|
|||
protected void run() {
|
||||
try {
|
||||
runInterceptHook();
|
||||
Coin takerInputAmount = FeePolicy.SECURITY_DEPOSIT.add(FeePolicy.TX_FEE);
|
||||
Coin takerInputAmount = FeePolicy.getSecurityDeposit().add(FeePolicy.getFixedTxFeeForTrades());
|
||||
InputsAndChangeOutput result = processModel.getTradeWalletService().takerCreatesDepositsTxInputs(takerInputAmount, processModel.getAddressEntry());
|
||||
processModel.setRawInputs(result.rawInputs);
|
||||
processModel.setChangeOutputValue(result.changeOutputValue);
|
||||
|
|
|
@ -40,7 +40,7 @@ public class SignAndFinalizePayoutTx extends TradeTask {
|
|||
try {
|
||||
runInterceptHook();
|
||||
checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null");
|
||||
Coin sellerPayoutAmount = FeePolicy.SECURITY_DEPOSIT;
|
||||
Coin sellerPayoutAmount = FeePolicy.getSecurityDeposit();
|
||||
Coin buyerPayoutAmount = sellerPayoutAmount.add(trade.getTradeAmount());
|
||||
|
||||
Transaction transaction = processModel.getTradeWalletService().buyerSignsAndFinalizesPayoutTx(
|
||||
|
|
|
@ -41,8 +41,8 @@ public class CreateAndSignDepositTxAsSeller extends TradeTask {
|
|||
try {
|
||||
runInterceptHook();
|
||||
checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null");
|
||||
Coin sellerInputAmount = FeePolicy.SECURITY_DEPOSIT.add(FeePolicy.TX_FEE).add(trade.getTradeAmount());
|
||||
Coin msOutputAmount = sellerInputAmount.add(FeePolicy.SECURITY_DEPOSIT);
|
||||
Coin sellerInputAmount = FeePolicy.getSecurityDeposit().add(FeePolicy.getFixedTxFeeForTrades()).add(trade.getTradeAmount());
|
||||
Coin msOutputAmount = sellerInputAmount.add(FeePolicy.getSecurityDeposit());
|
||||
|
||||
log.info("\n\n------------------------------------------------------------\n"
|
||||
+ "Contract as json\n"
|
||||
|
|
|
@ -38,7 +38,7 @@ public class CreateDepositTxInputsAsSeller extends TradeTask {
|
|||
try {
|
||||
runInterceptHook();
|
||||
if (trade.getTradeAmount() != null) {
|
||||
Coin takerInputAmount = FeePolicy.SECURITY_DEPOSIT.add(FeePolicy.TX_FEE).add(trade.getTradeAmount());
|
||||
Coin takerInputAmount = FeePolicy.getSecurityDeposit().add(FeePolicy.getFixedTxFeeForTrades()).add(trade.getTradeAmount());
|
||||
|
||||
InputsAndChangeOutput result = processModel.getTradeWalletService().takerCreatesDepositsTxInputs(takerInputAmount, processModel
|
||||
.getAddressEntry());
|
||||
|
|
|
@ -39,7 +39,7 @@ public class SignPayoutTx extends TradeTask {
|
|||
try {
|
||||
runInterceptHook();
|
||||
checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null");
|
||||
Coin sellerPayoutAmount = FeePolicy.SECURITY_DEPOSIT;
|
||||
Coin sellerPayoutAmount = FeePolicy.getSecurityDeposit();
|
||||
Coin buyerPayoutAmount = sellerPayoutAmount.add(trade.getTradeAmount());
|
||||
|
||||
// We use the sellers LastBlockSeenHeight, which might be different to the buyers one.
|
||||
|
|
|
@ -47,7 +47,7 @@ public class CreateTakeOfferFeeTx extends TradeTask {
|
|||
Arbitrator selectedArbitrator = user.getAcceptedArbitratorByAddress(selectedArbitratorAddress);
|
||||
Transaction createTakeOfferFeeTx = processModel.getTradeWalletService().createTradingFeeTx(
|
||||
processModel.getAddressEntry(),
|
||||
FeePolicy.TAKE_OFFER_FEE,
|
||||
FeePolicy.getTakeOfferFee(),
|
||||
selectedArbitrator.getBtcAddress());
|
||||
|
||||
processModel.setTakeOfferFeeTx(createTakeOfferFeeTx);
|
||||
|
|
|
@ -20,6 +20,7 @@ package io.bitsquare.user;
|
|||
import io.bitsquare.app.BitsquareEnvironment;
|
||||
import io.bitsquare.app.Version;
|
||||
import io.bitsquare.btc.BitcoinNetwork;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.locale.CountryUtil;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
|
@ -31,6 +32,8 @@ import javafx.beans.property.SimpleStringProperty;
|
|||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.utils.MonetaryFormat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -100,6 +103,7 @@ public class Preferences implements Serializable {
|
|||
private boolean tacAccepted;
|
||||
private Locale preferredLocale;
|
||||
private TradeCurrency preferredTradeCurrency;
|
||||
private long txFeePerKB = FeePolicy.getFeePerKb().value;
|
||||
|
||||
// Observable wrappers
|
||||
transient private final StringProperty btcDenominationProperty = new SimpleStringProperty(btcDenomination);
|
||||
|
@ -146,6 +150,11 @@ public class Preferences implements Serializable {
|
|||
defaultLocale = preferredLocale;
|
||||
preferredTradeCurrency = persisted.getPreferredTradeCurrency();
|
||||
defaultTradeCurrency = preferredTradeCurrency;
|
||||
try {
|
||||
setTxFeePerKB(persisted.getTxFeePerKB());
|
||||
} catch (Exception e) {
|
||||
// leave default value
|
||||
}
|
||||
} else {
|
||||
setTradeCurrencies(CurrencyUtil.getAllSortedCurrencies());
|
||||
tradeCurrencies = new ArrayList<>(tradeCurrenciesAsObservable);
|
||||
|
@ -162,6 +171,7 @@ public class Preferences implements Serializable {
|
|||
|
||||
preferredLocale = getDefaultLocale();
|
||||
preferredTradeCurrency = getDefaultTradeCurrency();
|
||||
|
||||
storage.queueUpForSave();
|
||||
}
|
||||
|
||||
|
@ -272,6 +282,14 @@ public class Preferences implements Serializable {
|
|||
storage.queueUpForSave();
|
||||
}
|
||||
|
||||
public void setTxFeePerKB(long txFeePerKB) throws Exception {
|
||||
if (txFeePerKB < Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.value)
|
||||
throw new Exception("Transaction fee must be at least 5 satoshi/byte");
|
||||
|
||||
this.txFeePerKB = txFeePerKB;
|
||||
FeePolicy.setFeePerKb(Coin.valueOf(txFeePerKB));
|
||||
storage.queueUpForSave();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getter
|
||||
|
@ -387,4 +405,8 @@ public class Preferences implements Serializable {
|
|||
public TradeCurrency getPreferredTradeCurrency() {
|
||||
return preferredTradeCurrency;
|
||||
}
|
||||
|
||||
public long getTxFeePerKB() {
|
||||
return Math.max(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.value, txFeePerKB);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,21 +28,21 @@ public class RestrictionsTest {
|
|||
@Test
|
||||
public void testIsMinSpendableAmount() {
|
||||
Coin amount = null;
|
||||
assertFalse("tx unfunded, pending", Restrictions.isMinSpendableAmount(amount));
|
||||
assertFalse("tx unfunded, pending", Restrictions.isAboveFixedTxFeeAndDust(amount));
|
||||
|
||||
amount = Coin.ZERO;
|
||||
assertFalse("tx unfunded, pending", Restrictions.isMinSpendableAmount(amount));
|
||||
assertFalse("tx unfunded, pending", Restrictions.isAboveFixedTxFeeAndDust(amount));
|
||||
|
||||
amount = FeePolicy.TX_FEE;
|
||||
assertFalse("tx unfunded, pending", Restrictions.isMinSpendableAmount(amount));
|
||||
amount = FeePolicy.getFixedTxFeeForTrades();
|
||||
assertFalse("tx unfunded, pending", Restrictions.isAboveFixedTxFeeAndDust(amount));
|
||||
|
||||
amount = Transaction.MIN_NONDUST_OUTPUT;
|
||||
assertFalse("tx unfunded, pending", Restrictions.isMinSpendableAmount(amount));
|
||||
assertFalse("tx unfunded, pending", Restrictions.isAboveFixedTxFeeAndDust(amount));
|
||||
|
||||
amount = FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT);
|
||||
assertFalse("tx unfunded, pending", Restrictions.isMinSpendableAmount(amount));
|
||||
amount = FeePolicy.getFixedTxFeeForTrades().add(Transaction.MIN_NONDUST_OUTPUT);
|
||||
assertFalse("tx unfunded, pending", Restrictions.isAboveFixedTxFeeAndDust(amount));
|
||||
|
||||
amount = FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT).add(Coin.valueOf(1));
|
||||
assertTrue("tx unfunded, pending", Restrictions.isMinSpendableAmount(amount));
|
||||
amount = FeePolicy.getFixedTxFeeForTrades().add(Transaction.MIN_NONDUST_OUTPUT).add(Coin.valueOf(1));
|
||||
assertTrue("tx unfunded, pending", Restrictions.isAboveFixedTxFeeAndDust(amount));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue