Use security deposit from offer. Use fixed estimated tx size for fee calculation

This commit is contained in:
Manfred Karrer 2016-12-05 23:39:56 +01:00
parent 5fd2839e24
commit 5beb7e6a1b
22 changed files with 156 additions and 91 deletions

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -113,6 +113,7 @@ public class WalletService {
AddressEntryList addressEntryList,
UserAgent userAgent,
Preferences preferences,
Socks5ProxyProvider socks5ProxyProvider,
@Named(BtcOptionKeys.WALLET_DIR) File appDir) {
this.regTestHost = regTestHost;

View File

@ -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;
}

View File

@ -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() {

View File

@ -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());
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -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();
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -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<String> 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<String, String> 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 +

View File

@ -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<PlaceOfferModel> {
@Override
protected void run() {
Offer offer = model.offer;
try {
runInterceptHook();
@ -48,26 +50,26 @@ public class CreateOfferFeeTx extends Task<PlaceOfferModel> {
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);

View File

@ -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(

View File

@ -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,

View File

@ -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.

View File

@ -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,

View File

@ -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<String, String> 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);

View File

@ -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();

View File

@ -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<DisputeSummaryWindow> {
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<DisputeSummaryWindow> {
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) {

View File

@ -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<OfferDetailsWindow> {
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:",

View File

@ -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<TradeDetailsWindow> {
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)

View File

@ -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<PendingTrad
}
public String getSecurityDeposit() {
return formatter.formatCoinWithCode(FeePolicy.getSecurityDeposit());
if (dataModel.getOffer() != null)
return formatter.formatCoinWithCode(dataModel.getOffer().getSecurityDeposit());
else
return "";
}
public boolean isBlockChainMethod() {

View File

@ -60,6 +60,10 @@ public class TradesChartsViewModelTest {
0,
0,
false,
false,
0,
0,
false,
null,
null
);

View File

@ -288,6 +288,10 @@ public class OfferBookViewModelTest {
0,
0,
false,
false,
0,
0,
false,
null,
null);
}