diff --git a/common/src/main/java/io/bitsquare/app/DevFlags.java b/common/src/main/java/io/bitsquare/app/DevFlags.java index e095f89277..ba92c951de 100644 --- a/common/src/main/java/io/bitsquare/app/DevFlags.java +++ b/common/src/main/java/io/bitsquare/app/DevFlags.java @@ -2,5 +2,5 @@ package io.bitsquare.app; public class DevFlags { public static final boolean STRESS_TEST_MODE = false; - public static final boolean DEV_MODE = STRESS_TEST_MODE || false; + public static final boolean DEV_MODE = STRESS_TEST_MODE || true; } diff --git a/core/src/main/java/io/bitsquare/btc/FeePolicy.java b/core/src/main/java/io/bitsquare/btc/FeePolicy.java index 15115abe55..a7aa5e1d40 100644 --- a/core/src/main/java/io/bitsquare/btc/FeePolicy.java +++ b/core/src/main/java/io/bitsquare/btc/FeePolicy.java @@ -17,7 +17,6 @@ package io.bitsquare.btc; -import io.bitsquare.app.DevFlags; import org.bitcoinj.core.Coin; public class FeePolicy { @@ -60,10 +59,4 @@ public class FeePolicy { return NON_TRADE_FEE_PER_KB; } - - // TODO will be increased once we get higher limits - // 0.01 BTC; about 4 EUR @ 400 EUR/BTC - public static Coin getSecurityDeposit() { - return DevFlags.STRESS_TEST_MODE ? Coin.valueOf(5_000) : Coin.valueOf(1_000_000); - } } diff --git a/core/src/main/java/io/bitsquare/btc/TradeWalletService.java b/core/src/main/java/io/bitsquare/btc/TradeWalletService.java index b3bdabcfd7..5220d5e5be 100644 --- a/core/src/main/java/io/bitsquare/btc/TradeWalletService.java +++ b/core/src/main/java/io/bitsquare/btc/TradeWalletService.java @@ -1120,13 +1120,16 @@ public class TradeWalletService { } private static void printTxWithInputs(String tracePrefix, Transaction tx) { - log.info(tracePrefix + ": " + tx.toString()); + StringBuilder sb = new StringBuilder(); + sb.append(tracePrefix).append(": ").append(tx.toString()).append("\n").append(tracePrefix); for (TransactionInput input : tx.getInputs()) { if (input.getConnectedOutput() != null) - log.info(tracePrefix + " input value: " + input.getConnectedOutput().getValue().toFriendlyString()); + sb.append(" input value: ").append(input.getConnectedOutput().getValue().toFriendlyString()); else - log.info(tracePrefix + ": Transaction already has inputs but we don't have the connected outputs, so we don't know the value."); + sb.append(": Transaction already has inputs but we don't have the connected outputs, so we don't know the value."); } + sb.append("\n").append("Size: " + tx.bitcoinSerialize().length); + log.info(sb.toString()); } private void signInput(Transaction transaction, TransactionInput input, int inputIndex) throws SigningException { diff --git a/core/src/main/java/io/bitsquare/btc/WalletService.java b/core/src/main/java/io/bitsquare/btc/WalletService.java index c5d3391188..2448f84ef2 100644 --- a/core/src/main/java/io/bitsquare/btc/WalletService.java +++ b/core/src/main/java/io/bitsquare/btc/WalletService.java @@ -113,6 +113,7 @@ public class WalletService { AddressEntryList addressEntryList, UserAgent userAgent, Preferences preferences, + Socks5ProxyProvider socks5ProxyProvider, @Named(BtcOptionKeys.WALLET_DIR) File appDir) { this.regTestHost = regTestHost; diff --git a/core/src/main/java/io/bitsquare/btc/provider/fee/FeeData.java b/core/src/main/java/io/bitsquare/btc/provider/fee/FeeData.java index 3e9f8c4bc9..4b09b9f7f0 100644 --- a/core/src/main/java/io/bitsquare/btc/provider/fee/FeeData.java +++ b/core/src/main/java/io/bitsquare/btc/provider/fee/FeeData.java @@ -6,12 +6,12 @@ import org.slf4j.LoggerFactory; public class FeeData { private static final Logger log = LoggerFactory.getLogger(FeeData.class); - public final long txFee; + public final long txFeePerByte; public final long createOfferFee; public final long takeOfferFee; - public FeeData(long txFee, long createOfferFee, long takeOfferFee) { - this.txFee = txFee; + public FeeData(long txFeePerByte, long createOfferFee, long takeOfferFee) { + this.txFeePerByte = txFeePerByte; this.createOfferFee = createOfferFee; this.takeOfferFee = takeOfferFee; } diff --git a/core/src/main/java/io/bitsquare/btc/provider/fee/FeeService.java b/core/src/main/java/io/bitsquare/btc/provider/fee/FeeService.java index bf61e6cda3..619ed7633c 100644 --- a/core/src/main/java/io/bitsquare/btc/provider/fee/FeeService.java +++ b/core/src/main/java/io/bitsquare/btc/provider/fee/FeeService.java @@ -44,13 +44,13 @@ public class FeeService { public static final long MAX_TX_FEE = 200; public static final long DEFAULT_TX_FEE = 60; - public static final long MIN_CREATE_OFFER_FEE = 50_000; + public static final long MIN_CREATE_OFFER_FEE = 10_000; public static final long MAX_CREATE_OFFER_FEE = 500_000; - public static final long DEFAULT_CREATE_OFFER_FEE = 50_000; + public static final long DEFAULT_CREATE_OFFER_FEE = 30_000; - public static final long MIN_TAKE_OFFER_FEE = 100_000; + public static final long MIN_TAKE_OFFER_FEE = 10_000; public static final long MAX_TAKE_OFFER_FEE = 1000_000; - public static final long DEFAULT_TAKE_OFFER_FEE = 100_000; + public static final long DEFAULT_TAKE_OFFER_FEE = 80_000; private final FeeProvider feeProvider; private final ProvidersRepository providersRepository; @@ -104,15 +104,18 @@ public class FeeService { }); } - public Coin getTxFee() { - // feeData.txFee is sat/byte but we want satoshi / kb - log.debug("getTxFee " + (feeData.txFee * 1000)); - return Coin.valueOf(feeData.txFee * 1000); + public Coin getTxFee(int sizeInBytes) { + return getTxFeePerByte().multiply(sizeInBytes); + } + + public Coin getTxFeePerByte() { + log.debug("getTxFee " + (feeData.txFeePerByte)); + return Coin.valueOf(feeData.txFeePerByte); } // TODO needed? public Coin getTxFeeForWithdrawal() { - return getTxFee(); + return getTxFeePerByte(); } public Coin getCreateOfferFee() { diff --git a/core/src/main/java/io/bitsquare/trade/BuyerTrade.java b/core/src/main/java/io/bitsquare/trade/BuyerTrade.java index f30f09d0ec..b6afad6494 100644 --- a/core/src/main/java/io/bitsquare/trade/BuyerTrade.java +++ b/core/src/main/java/io/bitsquare/trade/BuyerTrade.java @@ -18,7 +18,6 @@ package io.bitsquare.trade; import io.bitsquare.app.Version; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.p2p.NodeAddress; @@ -61,7 +60,7 @@ public abstract class BuyerTrade extends Trade { public Coin getPayoutAmount() { checkNotNull(getTradeAmount(), "Invalid state: getTradeAmount() = null"); - return FeePolicy.getSecurityDeposit().add(getTradeAmount()); + return getOffer().getSecurityDeposit().add(getTradeAmount()); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/io/bitsquare/trade/SellerTrade.java b/core/src/main/java/io/bitsquare/trade/SellerTrade.java index edfab7c75e..f2e35d8649 100644 --- a/core/src/main/java/io/bitsquare/trade/SellerTrade.java +++ b/core/src/main/java/io/bitsquare/trade/SellerTrade.java @@ -18,7 +18,6 @@ package io.bitsquare.trade; import io.bitsquare.app.Version; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.p2p.NodeAddress; @@ -58,7 +57,7 @@ public abstract class SellerTrade extends Trade { @Override public Coin getPayoutAmount() { - return FeePolicy.getSecurityDeposit(); + return getOffer().getSecurityDeposit(); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/io/bitsquare/trade/offer/Offer.java b/core/src/main/java/io/bitsquare/trade/offer/Offer.java index f2ca17a23c..09b04f8585 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/Offer.java +++ b/core/src/main/java/io/bitsquare/trade/offer/Offer.java @@ -120,7 +120,10 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload // We use 2 type of prices: fixed price or price based on distance from market price private final boolean useMarketBasedPrice; // fiatPrice if fixed price is used (usePercentageBasedPrice = false), otherwise 0 + + //TODO add support for altcoin price or fix precision issue private final long fiatPrice; + // Distance form market price if percentage based price is used (usePercentageBasedPrice = true), otherwise 0. // E.g. 0.1 -> 10%. Can be negative as well. Depending on direction the marketPriceMargin is above or below the market price. // Positive values is always the usual case where you want a better price as the market. @@ -140,12 +143,16 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload // New properties from v. 0.5.0.0 private final String versionNr; + private final long blockHeightAtOfferCreation; private final long txFee; private final long createOfferFee; - private final long takerFee; private final long securityDeposit; private final long maxTradeLimit; private final long maxTradePeriod; + private final boolean useAutoClose; + private final boolean useReOpenAfterAutoClose; + private final long lowerClosePrice; + private final long upperClosePrice; // Reserved for possible future use to support private trades where the taker need to have an accessKey private final boolean isPrivateOffer; @@ -196,15 +203,20 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload @Nullable ArrayList acceptedBankIds, PriceFeedService priceFeedService, String versionNr, + long blockHeightAtOfferCreation, long txFee, long createOfferFee, - long takerFee, long securityDeposit, long maxTradeLimit, long maxTradePeriod, + boolean useAutoClose, + boolean useReOpenAfterAutoClose, + long lowerClosePrice, + long upperClosePrice, boolean isPrivateOffer, @Nullable String hashOfChallenge, @Nullable HashMap extraDataMap) { + this.id = id; this.offererNodeAddress = offererNodeAddress; this.pubKeyRing = pubKeyRing; @@ -224,12 +236,16 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload this.acceptedBankIds = acceptedBankIds; this.priceFeedService = priceFeedService; this.versionNr = versionNr; + this.blockHeightAtOfferCreation = blockHeightAtOfferCreation; this.txFee = txFee; this.createOfferFee = createOfferFee; - this.takerFee = takerFee; this.securityDeposit = securityDeposit; this.maxTradeLimit = maxTradeLimit; this.maxTradePeriod = maxTradePeriod; + this.useAutoClose = useAutoClose; + this.useReOpenAfterAutoClose = useReOpenAfterAutoClose; + this.lowerClosePrice = lowerClosePrice; + this.upperClosePrice = upperClosePrice; this.isPrivateOffer = isPrivateOffer; this.hashOfChallenge = hashOfChallenge; this.extraDataMap = extraDataMap; @@ -261,6 +277,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return offererNodeAddress; } + //TODO update with new properties public void validate() { checkNotNull(getAmount(), "Amount is null"); checkNotNull(getArbitratorNodeAddresses(), "Arbitrator is null"); @@ -273,7 +290,6 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload checkNotNull(getPrice(), "Price is null"); checkNotNull(getTxFee(), "txFee is null"); checkNotNull(getCreateOfferFee(), "CreateOfferFee is null"); - checkNotNull(getTakerFee(), "TakerFee is null"); checkNotNull(getVersionNr(), "VersionNr is null"); checkNotNull(getSecurityDeposit(), "SecurityDeposit is null"); checkNotNull(getMaxTradeLimit(), "MaxTradeLimit is null"); @@ -560,10 +576,6 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return Coin.valueOf(createOfferFee); } - public Coin getTakerFee() { - return Coin.valueOf(takerFee); - } - public Coin getSecurityDeposit() { return Coin.valueOf(securityDeposit); } @@ -576,6 +588,26 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return maxTradePeriod; } + public long getBlockHeightAtOfferCreation() { + return blockHeightAtOfferCreation; + } + + public boolean isUseAutoClose() { + return useAutoClose; + } + + public boolean isUseReOpenAfterAutoClose() { + return useReOpenAfterAutoClose; + } + + public long getLowerClosePrice() { + return lowerClosePrice; + } + + public long getUpperClosePrice() { + return upperClosePrice; + } + public boolean isPrivateOffer() { return isPrivateOffer; } @@ -590,6 +622,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return extraDataMap; } + //TODO update with new properties @Override public boolean equals(Object o) { if (this == o) return true; @@ -606,7 +639,6 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload if (minAmount != offer.minAmount) return false; if (txFee != offer.txFee) return false; if (createOfferFee != offer.createOfferFee) return false; - if (takerFee != offer.takerFee) return false; if (securityDeposit != offer.securityDeposit) return false; if (maxTradePeriod != offer.maxTradePeriod) return false; if (maxTradeLimit != offer.maxTradeLimit) return false; @@ -638,6 +670,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload } + //TODO update with new properties @Override public int hashCode() { int result; @@ -666,7 +699,6 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload result = 31 * result + (versionNr != null ? versionNr.hashCode() : 0); result = 31 * result + (int) (txFee ^ (txFee >>> 32)); result = 31 * result + (int) (createOfferFee ^ (createOfferFee >>> 32)); - result = 31 * result + (int) (takerFee ^ (takerFee >>> 32)); result = 31 * result + (int) (securityDeposit ^ (securityDeposit >>> 32)); result = 31 * result + (int) (maxTradePeriod ^ (maxTradePeriod >>> 32)); result = 31 * result + (int) (maxTradeLimit ^ (maxTradeLimit >>> 32)); @@ -676,6 +708,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return result; } + //TODO update with new properties @Override public String toString() { return "Offer{" + @@ -693,7 +726,6 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload "\n\ttxFee=" + txFee + "\n\tcreateOfferFee=" + createOfferFee + - "\n\ttakerFee=" + takerFee + "\n\tsecurityDeposit=" + securityDeposit + "\n\tmaxTradePeriod=" + maxTradePeriod + "\n\tmaxTradeLimit=" + maxTradeLimit + diff --git a/core/src/main/java/io/bitsquare/trade/protocol/placeoffer/tasks/CreateOfferFeeTx.java b/core/src/main/java/io/bitsquare/trade/protocol/placeoffer/tasks/CreateOfferFeeTx.java index 59172e7697..edf6fbb7b4 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/placeoffer/tasks/CreateOfferFeeTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/placeoffer/tasks/CreateOfferFeeTx.java @@ -23,6 +23,7 @@ import io.bitsquare.btc.WalletService; import io.bitsquare.common.taskrunner.Task; import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.p2p.NodeAddress; +import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.protocol.placeoffer.PlaceOfferModel; import io.bitsquare.trade.protocol.trade.ArbitrationSelectionRule; import org.bitcoinj.core.Transaction; @@ -40,6 +41,7 @@ public class CreateOfferFeeTx extends Task { @Override protected void run() { + Offer offer = model.offer; try { runInterceptHook(); @@ -48,26 +50,26 @@ public class CreateOfferFeeTx extends Task { Arbitrator selectedArbitrator = model.user.getAcceptedArbitratorByAddress(selectedArbitratorNodeAddress); checkNotNull(selectedArbitrator, "selectedArbitrator must not be null at CreateOfferFeeTx"); WalletService walletService = model.walletService; - String id = model.offer.getId(); + String id = offer.getId(); Transaction transaction = model.tradeWalletService.createTradingFeeTx( walletService.getOrCreateAddressEntry(id, AddressEntry.Context.OFFER_FUNDING).getAddress(), walletService.getOrCreateAddressEntry(id, AddressEntry.Context.RESERVED_FOR_TRADE).getAddress(), walletService.getOrCreateAddressEntry(AddressEntry.Context.AVAILABLE).getAddress(), model.reservedFundsForOffer, model.useSavingsWallet, - model.offer.getCreateOfferFee(), - model.offer.getTxFee(), + offer.getCreateOfferFee(), + offer.getTxFee(), selectedArbitrator.getBtcAddress()); // We assume there will be no tx malleability. We add a check later in case the published offer has a different hash. // As the txId is part of the offer and therefore change the hash data we need to be sure to have no // tx malleability - model.offer.setOfferFeePaymentTxID(transaction.getHashAsString()); + offer.setOfferFeePaymentTxID(transaction.getHashAsString()); model.setTransaction(transaction); complete(); } catch (Throwable t) { - model.offer.setErrorMessage("An error occurred.\n" + + offer.setErrorMessage("An error occurred.\n" + "Error message:\n" + t.getMessage()); failed(t); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SignAndFinalizePayoutTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SignAndFinalizePayoutTx.java index b9b2a78bb8..7488a5d994 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SignAndFinalizePayoutTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SignAndFinalizePayoutTx.java @@ -18,7 +18,6 @@ package io.bitsquare.trade.protocol.trade.tasks.buyer; import io.bitsquare.btc.AddressEntry; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.trade.Trade; import io.bitsquare.trade.protocol.trade.tasks.TradeTask; @@ -41,7 +40,7 @@ public class SignAndFinalizePayoutTx extends TradeTask { try { runInterceptHook(); checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null"); - Coin sellerPayoutAmount = FeePolicy.getSecurityDeposit(); + Coin sellerPayoutAmount = trade.getOffer().getSecurityDeposit(); Coin buyerPayoutAmount = sellerPayoutAmount.add(trade.getTradeAmount()); Transaction transaction = processModel.getTradeWalletService().buyerSignsAndFinalizesPayoutTx( diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/TakerCreatesDepositTxInputsAsBuyer.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/TakerCreatesDepositTxInputsAsBuyer.java index 40af43509e..176ee86ba6 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/TakerCreatesDepositTxInputsAsBuyer.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/TakerCreatesDepositTxInputsAsBuyer.java @@ -18,7 +18,6 @@ package io.bitsquare.trade.protocol.trade.tasks.buyer; import io.bitsquare.btc.AddressEntry; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.data.InputsAndChangeOutput; import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.trade.Trade; @@ -40,7 +39,7 @@ public class TakerCreatesDepositTxInputsAsBuyer extends TradeTask { runInterceptHook(); Coin txFee = trade.getTxFee(); Coin doubleTxFee = txFee.add(txFee); - Coin takerInputAmount = FeePolicy.getSecurityDeposit().add(doubleTxFee); + Coin takerInputAmount = trade.getOffer().getSecurityDeposit().add(doubleTxFee); InputsAndChangeOutput result = processModel.getTradeWalletService().takerCreatesDepositsTxInputs( takerInputAmount, txFee, diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignPayoutTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignPayoutTx.java index 486bf1c613..d5dff9c0ea 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignPayoutTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignPayoutTx.java @@ -18,7 +18,6 @@ package io.bitsquare.trade.protocol.trade.tasks.seller; import io.bitsquare.btc.AddressEntry; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.WalletService; import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.trade.Trade; @@ -42,7 +41,7 @@ public class SignPayoutTx extends TradeTask { runInterceptHook(); checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null"); checkNotNull(trade.getDepositTx(), "trade.getDepositTx() must not be null"); - Coin sellerPayoutAmount = FeePolicy.getSecurityDeposit(); + Coin sellerPayoutAmount = trade.getOffer().getSecurityDeposit(); Coin buyerPayoutAmount = sellerPayoutAmount.add(trade.getTradeAmount()); // We use the sellers LastBlockSeenHeight, which might be different to the buyers one. diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/TakerCreatesDepositTxInputsAsSeller.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/TakerCreatesDepositTxInputsAsSeller.java index 21e82462f1..84d5a8c6af 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/TakerCreatesDepositTxInputsAsSeller.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/TakerCreatesDepositTxInputsAsSeller.java @@ -18,7 +18,6 @@ package io.bitsquare.trade.protocol.trade.tasks.seller; import io.bitsquare.btc.AddressEntry; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.data.InputsAndChangeOutput; import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.trade.Trade; @@ -41,7 +40,7 @@ public class TakerCreatesDepositTxInputsAsSeller extends TradeTask { if (trade.getTradeAmount() != null) { Coin txFee = trade.getTxFee(); Coin doubleTxFee = txFee.add(txFee); - Coin takerInputAmount = FeePolicy.getSecurityDeposit().add(doubleTxFee).add(trade.getTradeAmount()); + Coin takerInputAmount = trade.getOffer().getSecurityDeposit().add(doubleTxFee).add(trade.getTradeAmount()); InputsAndChangeOutput result = processModel.getTradeWalletService().takerCreatesDepositsTxInputs(takerInputAmount, txFee, diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java index 740a28edc2..c8fc209217 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java @@ -22,7 +22,6 @@ import io.bitsquare.app.DevFlags; import io.bitsquare.app.Version; import io.bitsquare.arbitration.Arbitrator; import io.bitsquare.btc.AddressEntry; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; import io.bitsquare.btc.blockchain.BlockchainService; @@ -35,7 +34,6 @@ import io.bitsquare.gui.common.model.ActivatableDataModel; import io.bitsquare.gui.main.offer.createoffer.monetary.Price; import io.bitsquare.gui.main.offer.createoffer.monetary.Volume; import io.bitsquare.gui.main.overlays.notifications.Notification; -import io.bitsquare.gui.main.overlays.popups.Popup; import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.locale.CurrencyUtil; import io.bitsquare.locale.TradeCurrency; @@ -79,7 +77,7 @@ class CreateOfferDataModel extends ActivatableDataModel { private final BSFormatter formatter; private final String offerId; private final AddressEntry addressEntry; - private Coin createOfferFeeAsCoin, takerFeeAsCoin; + private Coin createOfferFeeAsCoin; private Coin txFeeAsCoin; private Coin securityDepositAsCoin; private final BalanceListener balanceListener; @@ -147,6 +145,9 @@ class CreateOfferDataModel extends ActivatableDataModel { useMarketBasedPrice.set(preferences.getUsePercentageBasedPrice()); + // TODO add ui for editing + securityDepositAsCoin = Coin.valueOf(1_000_000); + balanceListener = new BalanceListener(getAddressEntry().getAddress()) { @Override public void onBalanceChanged(Coin balance, Transaction tx) { @@ -184,21 +185,6 @@ class CreateOfferDataModel extends ActivatableDataModel { addBindings(); addListeners(); - feeService.requestFees(() -> { - //TODO update doubleTxFeeAsCoin and txFeeAsCoin in view with binding - createOfferFeeAsCoin = feeService.getCreateOfferFee(); - takerFeeAsCoin = feeService.getTakeOfferFee(); - txFeeAsCoin = feeService.getTxFee(); - calculateTotalToPay(); - }, (errorMessage, throwable) -> new Popup<>().warning(errorMessage).show()); - - createOfferFeeAsCoin = feeService.getCreateOfferFee(); - takerFeeAsCoin = feeService.getTakeOfferFee(); - txFeeAsCoin = feeService.getTxFee(); - - // TODO - securityDepositAsCoin = FeePolicy.getSecurityDeposit(); - if (!preferences.getUseStickyMarketPrice() && isTabSelected) priceFeedService.setCurrencyCode(tradeCurrencyCode.get()); @@ -235,6 +221,7 @@ class CreateOfferDataModel extends ActivatableDataModel { // API /////////////////////////////////////////////////////////////////////////////////////////// + // called before activate() boolean initWithData(Offer.Direction direction, TradeCurrency tradeCurrency) { this.direction = direction; @@ -260,6 +247,25 @@ class CreateOfferDataModel extends ActivatableDataModel { if (!preferences.getUseStickyMarketPrice()) priceFeedService.setCurrencyCode(tradeCurrencyCode.get()); + // The offerer only pays the mining fee for the trade fee tx (not the mining fee for other trade txs). + // A typical trade fee tx has about 226 bytes (if one input). We use 400 as a safe value. + // We cannot use tx size calculation as we do not know initially how the input is funded. And we require the + // fee for getting the funds needed. + // So we use an estimated average size and risk that in some cases we might get a bit of delay if the actual required + // fee would be larger. + // As we use the best fee estimation (for 1 confirmation) that risk should not be too critical as long there are + // not too many inputs. + + // trade fee tx: 226 bytes (1 input) - 374 bytes (2 inputs) + feeService.requestFees(() -> { + createOfferFeeAsCoin = feeService.getCreateOfferFee(); + txFeeAsCoin = feeService.getTxFee(400); + calculateTotalToPay(); + }, null); + + createOfferFeeAsCoin = feeService.getCreateOfferFee(); + txFeeAsCoin = feeService.getTxFee(400); + calculateVolume(); calculateTotalToPay(); return true; @@ -278,6 +284,7 @@ class CreateOfferDataModel extends ActivatableDataModel { Offer createAndGetOffer() { long priceAsLong = price.get() != null && !useMarketBasedPrice.get() ? price.get().getValue() : 0L; // We use precision 8 in AltcoinPrice but in Offer we use Fiat with precision 4. Will be refactored once in a bigger update.... + // TODO use same precision for both in next release if (CurrencyUtil.isCryptoCurrency(tradeCurrencyCode.get())) priceAsLong = priceAsLong / 10000; @@ -312,10 +319,14 @@ class CreateOfferDataModel extends ActivatableDataModel { long maxTradeLimit = paymentAccount.getPaymentMethod().getMaxTradeLimit().value; long maxTradePeriod = paymentAccount.getPaymentMethod().getMaxTradePeriod(); + // reserved for future use cases boolean isPrivateOffer = false; String hashOfChallenge = null; HashMap extraDataMap = null; - + boolean useAutoClose = false; + boolean useReOpenAfterAutoClose = false; + long lowerClosePrice = 0; + long upperClosePrice = 0; return new Offer(offerId, p2PService.getAddress(), @@ -337,12 +348,16 @@ class CreateOfferDataModel extends ActivatableDataModel { priceFeedService, Version.VERSION, + walletService.getWallet().getLastBlockSeenHeight(), txFeeAsCoin.value, createOfferFeeAsCoin.value, - takerFeeAsCoin.value, securityDepositAsCoin.value, maxTradeLimit, maxTradePeriod, + useAutoClose, + useReOpenAfterAutoClose, + upperClosePrice, + lowerClosePrice, isPrivateOffer, hashOfChallenge, extraDataMap); diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java index a707f46485..6a8b322988 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java @@ -21,7 +21,6 @@ import com.google.inject.Inject; import io.bitsquare.app.DevFlags; import io.bitsquare.arbitration.Arbitrator; import io.bitsquare.btc.AddressEntry; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; import io.bitsquare.btc.blockchain.BlockchainService; @@ -162,7 +161,6 @@ class TakeOfferDataModel extends ActivatableDataModel { void initWithData(Offer offer) { this.offer = offer; tradePrice = offer.getPrice(); - takerFeeAsCoin = offer.getTakerFee(); addressEntry = walletService.getOrCreateAddressEntry(offer.getId(), AddressEntry.Context.OFFER_FUNDING); checkNotNull(addressEntry, "addressEntry must not be null"); @@ -175,24 +173,39 @@ class TakeOfferDataModel extends ActivatableDataModel { if (DevFlags.DEV_MODE) amountAsCoin.set(offer.getAmount()); + securityDepositAsCoin = offer.getSecurityDeposit(); + // Taker pays 2 times the tx fee because the mining fee might be different when offerer created the offer // and reserved his funds, so that would not work well with dynamic fees. // The mining fee for the takeOfferFee tx is deducted from the createOfferFee and not visible to the trader + + // The taker pays the mining fee for the trade fee tx and the trade txs. + // A typical trade fee tx has about 226 bytes (if one input). The trade txs has about 336-414 bytes. + // We use 400 as a safe value. + // We cannot use tx size calculation as we do not know initially how the input is funded. And we require the + // fee for getting the funds needed. + // So we use an estimated average size and risk that in some cases we might get a bit of delay if the actual required + // fee would be larger. + // As we use the best fee estimation (for 1 confirmation) that risk should not be too critical as long there are + // not too many inputs. The trade txs have no risks as there cannot be more than about 414 bytes. + // Only the trade fee tx carries a risk that it might be larger. + + // trade fee tx: 226 bytes (1 input) - 374 bytes (2 inputs) + // deposit tx: 336 bytes (1 MS output+ OP_RETURN) - 414 bytes (1 MS output + OP_RETURN + change in case of smaller trade amount) + // payout tx: 371 bytes + // disputed payout tx: 408 bytes feeService.requestFees(() -> { //TODO update doubleTxFeeAsCoin and txFeeAsCoin in view with binding takerFeeAsCoin = feeService.getTakeOfferFee(); - txFeeAsCoin = feeService.getTxFee(); + txFeeAsCoin = feeService.getTxFee(400); totalTxFeeAsCoin = txFeeAsCoin.multiply(3); calculateTotalToPay(); - }, (errorMessage, throwable) -> new Popup<>().warning(errorMessage).show()); + }, null); takerFeeAsCoin = feeService.getTakeOfferFee(); - txFeeAsCoin = feeService.getTxFee(); + txFeeAsCoin = feeService.getTxFee(400); totalTxFeeAsCoin = txFeeAsCoin.multiply(3); - //TODO - securityDepositAsCoin = FeePolicy.getSecurityDeposit(); - calculateVolume(); calculateTotalToPay(); diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/DisputeSummaryWindow.java b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/DisputeSummaryWindow.java index a8b21669fc..170319de20 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/DisputeSummaryWindow.java +++ b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/DisputeSummaryWindow.java @@ -21,7 +21,6 @@ import io.bitsquare.arbitration.Dispute; import io.bitsquare.arbitration.DisputeManager; import io.bitsquare.arbitration.DisputeResult; import io.bitsquare.btc.AddressEntry; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; import io.bitsquare.btc.exceptions.TransactionVerificationException; @@ -370,16 +369,18 @@ public class DisputeSummaryWindow extends Overlay { Coin buyerAmount = formatter.parseToCoin(buyerPayoutAmountInputTextField.getText()); Coin sellerAmount = formatter.parseToCoin(sellerPayoutAmountInputTextField.getText()); Coin arbitratorAmount = formatter.parseToCoin(arbitratorPayoutAmountInputTextField.getText()); - Coin securityDeposit = FeePolicy.getSecurityDeposit(); - Coin tradeAmount = dispute.getContract().getTradeAmount(); + Contract contract = dispute.getContract(); + Coin securityDeposit = contract.offer.getSecurityDeposit(); + Coin tradeAmount = contract.getTradeAmount(); Coin available = tradeAmount.add(securityDeposit).add(securityDeposit); Coin totalAmount = buyerAmount.add(sellerAmount).add(arbitratorAmount); return (totalAmount.compareTo(available) == 0); } private void applyCustomAmounts(InputTextField inputTextField) { - Coin securityDeposit = FeePolicy.getSecurityDeposit(); - Coin tradeAmount = dispute.getContract().getTradeAmount(); + Contract contract = dispute.getContract(); + Coin securityDeposit = contract.offer.getSecurityDeposit(); + Coin tradeAmount = contract.getTradeAmount(); Coin buyerAmount = formatter.parseToCoin(buyerPayoutAmountInputTextField.getText()); Coin sellerAmount = formatter.parseToCoin(sellerPayoutAmountInputTextField.getText()); @@ -669,7 +670,7 @@ public class DisputeSummaryWindow extends Overlay { private void calculatePayoutAmounts(DisputeResult.DisputeFeePolicy feePayment) { Contract contract = dispute.getContract(); - Coin refund = FeePolicy.getSecurityDeposit(); + Coin refund = contract.offer.getSecurityDeposit(); Coin winnerRefund; Coin loserRefund; switch (feePayment) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java index 1094e2e366..bba88d9f77 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java +++ b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java @@ -18,7 +18,6 @@ package io.bitsquare.gui.main.overlays.windows; import com.google.common.base.Joiner; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.util.Tuple3; import io.bitsquare.gui.Navigation; @@ -264,7 +263,7 @@ public class OfferDetailsWindow extends Overlay { addLabelTextFieldWithCopyIcon(gridPane, rowIndex, "Offer ID:", offer.getId(), Layout.FIRST_ROW_AND_GROUP_DISTANCE); addLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, "Offerer's onion address:", offer.getOffererNodeAddress().getFullAddress()); addLabelTextField(gridPane, ++rowIndex, "Creation date:", formatter.formatDateTime(offer.getDate())); - addLabelTextField(gridPane, ++rowIndex, "Security deposit:", formatter.formatCoinWithCode(FeePolicy.getSecurityDeposit())); + addLabelTextField(gridPane, ++rowIndex, "Security deposit:", formatter.formatCoinWithCode(offer.getSecurityDeposit())); if (paymentMethodCountryCode != null) addLabelTextField(gridPane, ++rowIndex, "Offerer's country of bank:", diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/TradeDetailsWindow.java b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/TradeDetailsWindow.java index d80a4911bb..f0303c7739 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/TradeDetailsWindow.java +++ b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/TradeDetailsWindow.java @@ -18,7 +18,6 @@ package io.bitsquare.gui.main.overlays.windows; import io.bitsquare.arbitration.DisputeManager; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.gui.components.TextFieldWithCopyIcon; import io.bitsquare.gui.main.MainView; import io.bitsquare.gui.main.overlays.Overlay; @@ -172,7 +171,7 @@ public class TradeDetailsWindow extends Overlay { addTitledGroupBg(gridPane, ++rowIndex, rows, "Details", Layout.GROUP_DISTANCE); addLabelTextFieldWithCopyIcon(gridPane, rowIndex, "Trade ID:", trade.getId(), Layout.FIRST_ROW_AND_GROUP_DISTANCE); addLabelTextField(gridPane, ++rowIndex, "Trade date:", formatter.formatDateTime(trade.getDate())); - addLabelTextField(gridPane, ++rowIndex, "Security deposit:", formatter.formatCoinWithCode(FeePolicy.getSecurityDeposit())); + addLabelTextField(gridPane, ++rowIndex, "Security deposit:", formatter.formatCoinWithCode(offer.getSecurityDeposit())); addLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, "Selected arbitrator:", trade.getArbitratorNodeAddress().getFullAddress()); if (trade.getTradingPeerNodeAddress() != null) diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java index 238a199d1b..ca524a2e84 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java @@ -19,7 +19,6 @@ package io.bitsquare.gui.main.portfolio.pendingtrades; import com.google.inject.Inject; import io.bitsquare.app.Log; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.common.Clock; import io.bitsquare.gui.common.model.ActivatableWithDataModel; import io.bitsquare.gui.common.model.ViewModel; @@ -268,7 +267,10 @@ public class PendingTradesViewModel extends ActivatableWithDataModel