mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-23 03:59:36 +00:00
verify maker and taker fees
This commit is contained in:
parent
eae3060c63
commit
2d7654b8d7
21 changed files with 102 additions and 150 deletions
|
@ -305,13 +305,13 @@ public class XmrWalletService {
|
||||||
*
|
*
|
||||||
* @param returnAddress return address for reserved funds
|
* @param returnAddress return address for reserved funds
|
||||||
* @param tradeFee trade fee
|
* @param tradeFee trade fee
|
||||||
* @param peerAmount amount to give peer
|
* @param sendAmount amount to give peer
|
||||||
* @param securityDeposit security deposit amount
|
* @param securityDeposit security deposit amount
|
||||||
* @return a transaction to reserve a trade
|
* @return a transaction to reserve a trade
|
||||||
*/
|
*/
|
||||||
public MoneroTxWallet createReserveTx(BigInteger tradeFee, BigInteger peerAmount, BigInteger securityDeposit, String returnAddress) {
|
public MoneroTxWallet createReserveTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String returnAddress) {
|
||||||
log.info("Creating reserve tx with fee={}, peerAmount={}, securityDeposit={}", tradeFee, peerAmount, securityDeposit);
|
log.info("Creating reserve tx with fee={}, sendAmount={}, securityDeposit={}", tradeFee, sendAmount, securityDeposit);
|
||||||
return createTradeTx(tradeFee, peerAmount, securityDeposit, returnAddress);
|
return createTradeTx(tradeFee, sendAmount, securityDeposit, returnAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -324,7 +324,7 @@ public class XmrWalletService {
|
||||||
Offer offer = trade.getProcessModel().getOffer();
|
Offer offer = trade.getProcessModel().getOffer();
|
||||||
String multisigAddress = trade.getProcessModel().getMultisigAddress();
|
String multisigAddress = trade.getProcessModel().getMultisigAddress();
|
||||||
BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(trade instanceof MakerTrade ? trade.getOffer().getMakerFee() : trade.getTakerFee());
|
BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(trade instanceof MakerTrade ? trade.getOffer().getMakerFee() : trade.getTakerFee());
|
||||||
BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(trade instanceof BuyerTrade ? Coin.ZERO : offer.getAmount());
|
BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(trade instanceof BuyerTrade ? Coin.ZERO : offer.getAmount());
|
||||||
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(trade instanceof BuyerTrade ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
|
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(trade instanceof BuyerTrade ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
|
||||||
|
|
||||||
// thaw reserved outputs then create deposit tx
|
// thaw reserved outputs then create deposit tx
|
||||||
|
@ -336,12 +336,12 @@ public class XmrWalletService {
|
||||||
thawOutputs(trade.getSelf().getReserveTxKeyImages());
|
thawOutputs(trade.getSelf().getReserveTxKeyImages());
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Creating deposit tx with fee={}, peerAmount={}, securityDeposit={}", tradeFee, peerAmount, securityDeposit);
|
log.info("Creating deposit tx with fee={}, sendAmount={}, securityDeposit={}", tradeFee, sendAmount, securityDeposit);
|
||||||
return createTradeTx(tradeFee, peerAmount, securityDeposit, multisigAddress);
|
return createTradeTx(tradeFee, sendAmount, securityDeposit, multisigAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MoneroTxWallet createTradeTx(BigInteger tradeFee, BigInteger peerAmount, BigInteger securityDeposit, String address) {
|
private MoneroTxWallet createTradeTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address) {
|
||||||
MoneroWallet wallet = getWallet();
|
MoneroWallet wallet = getWallet();
|
||||||
synchronized (wallet) {
|
synchronized (wallet) {
|
||||||
|
|
||||||
|
@ -349,7 +349,7 @@ public class XmrWalletService {
|
||||||
MoneroTxWallet tradeTx = null;
|
MoneroTxWallet tradeTx = null;
|
||||||
double appliedTolerance = 0.0; // percent of tolerance to apply, thereby decreasing security deposit
|
double appliedTolerance = 0.0; // percent of tolerance to apply, thereby decreasing security deposit
|
||||||
double searchDiff = 1.0; // difference for next binary search
|
double searchDiff = 1.0; // difference for next binary search
|
||||||
BigInteger maxAmount = peerAmount.add(securityDeposit);
|
BigInteger maxAmount = sendAmount.add(securityDeposit);
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
try {
|
try {
|
||||||
BigInteger amount = new BigDecimal(maxAmount).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE * appliedTolerance)).toBigInteger();
|
BigInteger amount = new BigDecimal(maxAmount).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE * appliedTolerance)).toBigInteger();
|
||||||
|
@ -379,7 +379,7 @@ public class XmrWalletService {
|
||||||
* The transaction is submitted to the pool then flushed without relaying.
|
* The transaction is submitted to the pool then flushed without relaying.
|
||||||
*
|
*
|
||||||
* @param tradeFee trade fee
|
* @param tradeFee trade fee
|
||||||
* @param peerAmount amount to give peer
|
* @param sendAmount amount to give peer
|
||||||
* @param securityDeposit security deposit amount
|
* @param securityDeposit security deposit amount
|
||||||
* @param address expected destination address for the deposit amount
|
* @param address expected destination address for the deposit amount
|
||||||
* @param txHash transaction hash
|
* @param txHash transaction hash
|
||||||
|
@ -387,7 +387,7 @@ public class XmrWalletService {
|
||||||
* @param txKey transaction key
|
* @param txKey transaction key
|
||||||
* @param keyImages expected key images of inputs, ignored if null
|
* @param keyImages expected key images of inputs, ignored if null
|
||||||
*/
|
*/
|
||||||
public void verifyTradeTx(BigInteger tradeFee, BigInteger peerAmount, BigInteger securityDeposit, String address, String txHash, String txHex, String txKey, List<String> keyImages) {
|
public void verifyTradeTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, String txHash, String txHex, String txKey, List<String> keyImages) {
|
||||||
MoneroDaemonRpc daemon = getDaemon();
|
MoneroDaemonRpc daemon = getDaemon();
|
||||||
MoneroWallet wallet = getWallet();
|
MoneroWallet wallet = getWallet();
|
||||||
try {
|
try {
|
||||||
|
@ -426,7 +426,7 @@ public class XmrWalletService {
|
||||||
// verify deposit amount
|
// verify deposit amount
|
||||||
check = wallet.checkTxKey(txHash, txKey, address);
|
check = wallet.checkTxKey(txHash, txKey, address);
|
||||||
if (!check.isGood()) throw new RuntimeException("Invalid proof of deposit amount");
|
if (!check.isGood()) throw new RuntimeException("Invalid proof of deposit amount");
|
||||||
BigInteger minAmount = new BigDecimal(peerAmount.add(securityDeposit)).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE)).toBigInteger();
|
BigInteger minAmount = new BigDecimal(sendAmount.add(securityDeposit)).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE)).toBigInteger();
|
||||||
if (check.getReceivedAmount().compareTo(minAmount) < 0) throw new RuntimeException("Deposit amount is not enough, needed " + minAmount + " but was " + check.getReceivedAmount());
|
if (check.getReceivedAmount().compareTo(minAmount) < 0) throw new RuntimeException("Deposit amount is not enough, needed " + minAmount + " but was " + check.getReceivedAmount());
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import bisq.core.payment.PaymentAccountUtil;
|
||||||
import bisq.core.provider.price.MarketPrice;
|
import bisq.core.provider.price.MarketPrice;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||||
|
import bisq.core.trade.HavenoUtils;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.user.User;
|
import bisq.core.user.User;
|
||||||
|
@ -155,7 +156,7 @@ public class CreateOfferService {
|
||||||
String bankId = PaymentAccountUtil.getBankId(paymentAccount);
|
String bankId = PaymentAccountUtil.getBankId(paymentAccount);
|
||||||
List<String> acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount);
|
List<String> acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount);
|
||||||
double sellerSecurityDeposit = getSellerSecurityDepositAsDouble(buyerSecurityDepositAsDouble);
|
double sellerSecurityDeposit = getSellerSecurityDepositAsDouble(buyerSecurityDepositAsDouble);
|
||||||
Coin makerFeeAsCoin = offerUtil.getMakerFee(amount);
|
Coin makerFeeAsCoin = HavenoUtils.getMakerFee(amount);
|
||||||
Coin buyerSecurityDepositAsCoin = getBuyerSecurityDeposit(amount, buyerSecurityDepositAsDouble);
|
Coin buyerSecurityDepositAsCoin = getBuyerSecurityDeposit(amount, buyerSecurityDepositAsDouble);
|
||||||
Coin sellerSecurityDepositAsCoin = getSellerSecurityDeposit(amount, sellerSecurityDeposit);
|
Coin sellerSecurityDepositAsCoin = getSellerSecurityDeposit(amount, sellerSecurityDeposit);
|
||||||
long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction);
|
long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction);
|
||||||
|
|
|
@ -29,12 +29,10 @@ import bisq.core.payment.F2FAccount;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.provider.price.MarketPrice;
|
import bisq.core.provider.price.MarketPrice;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.HavenoUtils;
|
|
||||||
import bisq.core.trade.statistics.ReferralIdService;
|
import bisq.core.trade.statistics.ReferralIdService;
|
||||||
import bisq.core.user.AutoConfirmSettings;
|
import bisq.core.user.AutoConfirmSettings;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.util.coin.CoinFormatter;
|
import bisq.core.util.coin.CoinFormatter;
|
||||||
import bisq.core.util.coin.CoinUtil;
|
|
||||||
|
|
||||||
import bisq.network.p2p.P2PService;
|
import bisq.network.p2p.P2PService;
|
||||||
|
|
||||||
|
@ -58,8 +56,6 @@ import java.util.UUID;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static bisq.common.util.MathUtils.roundDoubleToLong;
|
import static bisq.common.util.MathUtils.roundDoubleToLong;
|
||||||
import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
|
import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
|
||||||
import static bisq.core.btc.wallet.Restrictions.getMaxBuyerSecurityDepositAsPercent;
|
import static bisq.core.btc.wallet.Restrictions.getMaxBuyerSecurityDepositAsPercent;
|
||||||
|
@ -163,41 +159,6 @@ public class OfferUtil {
|
||||||
return MathUtils.roundDouble(manualPrice / marketPrice, 4);
|
return MathUtils.roundDouble(manualPrice / marketPrice, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the makerFee as Coin, this can be priced in BTC.
|
|
||||||
*
|
|
||||||
* @param amount the amount of BTC to trade
|
|
||||||
* @return the maker fee for the given trade amount, or {@code null} if the amount
|
|
||||||
* is {@code null}
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public Coin getMakerFee(@Nullable Coin amount) {
|
|
||||||
return CoinUtil.getMakerFee(amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Coin getTxFeeByVsize(Coin txFeePerVbyteFromFeeService, int vsizeInVbytes) {
|
|
||||||
return txFeePerVbyteFromFeeService.multiply(getAverageTakerFeeTxVsize(vsizeInVbytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
// We use the sum of the size of the trade fee and the deposit tx to get an average.
|
|
||||||
// Miners will take the trade fee tx if the total fee of both dependent txs are good
|
|
||||||
// enough. With that we avoid that we overpay in case that the trade fee has many
|
|
||||||
// inputs and we would apply that fee for the other 2 txs as well. We still might
|
|
||||||
// overpay a bit for the payout tx.
|
|
||||||
public int getAverageTakerFeeTxVsize(int txVsize) {
|
|
||||||
return (txVsize + 233) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Coin getTakerFee(@Nullable Coin amount) {
|
|
||||||
if (amount != null) {
|
|
||||||
Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), amount);
|
|
||||||
return CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee());
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBlockChainPaymentMethod(Offer offer) {
|
public boolean isBlockChainPaymentMethod(Offer offer) {
|
||||||
return offer != null && offer.getPaymentMethod().isBlockchain();
|
return offer != null && offer.getPaymentMethod().isBlockchain();
|
||||||
}
|
}
|
||||||
|
|
|
@ -857,14 +857,22 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify maker's reserve tx (double spend, trade fee, trade amount, mining fee)
|
// verify maker's trade fee
|
||||||
Offer offer = new Offer(request.getOfferPayload());
|
Offer offer = new Offer(request.getOfferPayload());
|
||||||
BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(offer.getMakerFee());
|
BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(HavenoUtils.getMakerFee(offer.getAmount()));
|
||||||
BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? Coin.ZERO : offer.getAmount());
|
if (!tradeFee.equals(HavenoUtils.coinToAtomicUnits(offer.getMakerFee()))) {
|
||||||
|
errorMessage = "Wrong trade fee for offer " + request.offerId;
|
||||||
|
log.info(errorMessage);
|
||||||
|
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify maker's reserve tx (double spend, trade fee, trade amount, mining fee)
|
||||||
|
BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? Coin.ZERO : offer.getAmount());
|
||||||
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
|
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
|
||||||
xmrWalletService.verifyTradeTx(
|
xmrWalletService.verifyTradeTx(
|
||||||
tradeFee,
|
tradeFee,
|
||||||
peerAmount,
|
sendAmount,
|
||||||
securityDeposit,
|
securityDeposit,
|
||||||
request.getPayoutAddress(),
|
request.getPayoutAddress(),
|
||||||
request.getReserveTxHash(),
|
request.getReserveTxHash(),
|
||||||
|
|
|
@ -24,6 +24,7 @@ import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferUtil;
|
import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.offer.availability.OfferAvailabilityModel;
|
import bisq.core.offer.availability.OfferAvailabilityModel;
|
||||||
import bisq.core.offer.messages.OfferAvailabilityRequest;
|
import bisq.core.offer.messages.OfferAvailabilityRequest;
|
||||||
|
import bisq.core.trade.HavenoUtils;
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.user.User;
|
import bisq.core.user.User;
|
||||||
import bisq.network.p2p.P2PService;
|
import bisq.network.p2p.P2PService;
|
||||||
|
@ -55,7 +56,6 @@ public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
|
||||||
User user = model.getUser();
|
User user = model.getUser();
|
||||||
P2PService p2PService = model.getP2PService();
|
P2PService p2PService = model.getP2PService();
|
||||||
XmrWalletService walletService = model.getXmrWalletService();
|
XmrWalletService walletService = model.getXmrWalletService();
|
||||||
OfferUtil offerUtil = model.getOfferUtil();
|
|
||||||
String paymentAccountId = model.getPaymentAccountId();
|
String paymentAccountId = model.getPaymentAccountId();
|
||||||
String paymentMethodId = user.getPaymentAccount(paymentAccountId).getPaymentAccountPayload().getPaymentMethodId();
|
String paymentMethodId = user.getPaymentAccount(paymentAccountId).getPaymentAccountPayload().getPaymentMethodId();
|
||||||
String payoutAddress = walletService.getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(); // reserve new payout address
|
String payoutAddress = walletService.getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(); // reserve new payout address
|
||||||
|
@ -74,7 +74,7 @@ public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
|
||||||
p2PService.getKeyRing().getPubKeyRing(),
|
p2PService.getKeyRing().getPubKeyRing(),
|
||||||
offer.getAmount().value,
|
offer.getAmount().value,
|
||||||
price.getValue(),
|
price.getValue(),
|
||||||
offerUtil.getTakerFee(offer.getAmount()).value,
|
HavenoUtils.getTakerFee(offer.getAmount()).value,
|
||||||
user.getAccountId(),
|
user.getAccountId(),
|
||||||
paymentAccountId,
|
paymentAccountId,
|
||||||
paymentMethodId,
|
paymentMethodId,
|
||||||
|
|
|
@ -52,10 +52,10 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
||||||
|
|
||||||
// create reserve tx
|
// create reserve tx
|
||||||
BigInteger makerFee = HavenoUtils.coinToAtomicUnits(offer.getMakerFee());
|
BigInteger makerFee = HavenoUtils.coinToAtomicUnits(offer.getMakerFee());
|
||||||
BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? Coin.ZERO : offer.getAmount());
|
BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? Coin.ZERO : offer.getAmount());
|
||||||
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
|
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
|
||||||
String returnAddress = model.getXmrWalletService().getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString();
|
String returnAddress = model.getXmrWalletService().getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString();
|
||||||
MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(makerFee, peerAmount, securityDeposit, returnAddress);
|
MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(makerFee, sendAmount, securityDeposit, returnAddress);
|
||||||
|
|
||||||
// collect reserved key images
|
// collect reserved key images
|
||||||
List<String> reservedKeyImages = new ArrayList<String>();
|
List<String> reservedKeyImages = new ArrayList<String>();
|
||||||
|
|
|
@ -28,7 +28,7 @@ import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.payload.PaymentMethod;
|
import bisq.core.payment.payload.PaymentMethod;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
|
import bisq.core.trade.HavenoUtils;
|
||||||
import bisq.common.taskrunner.Model;
|
import bisq.common.taskrunner.Model;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
@ -111,7 +111,7 @@ public class TakeOfferModel implements Model {
|
||||||
this.securityDeposit = offer.getDirection() == SELL
|
this.securityDeposit = offer.getDirection() == SELL
|
||||||
? offer.getBuyerSecurityDeposit()
|
? offer.getBuyerSecurityDeposit()
|
||||||
: offer.getSellerSecurityDeposit();
|
: offer.getSellerSecurityDeposit();
|
||||||
this.takerFee = offerUtil.getTakerFee(amount);
|
this.takerFee = HavenoUtils.getTakerFee(amount);
|
||||||
|
|
||||||
calculateVolume();
|
calculateVolume();
|
||||||
calculateTotalToPay();
|
calculateTotalToPay();
|
||||||
|
|
|
@ -32,8 +32,6 @@ import java.util.UUID;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade {
|
public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade {
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import bisq.core.trade.messages.PaymentReceivedMessage;
|
||||||
import bisq.core.trade.messages.PaymentSentMessage;
|
import bisq.core.trade.messages.PaymentSentMessage;
|
||||||
import bisq.core.util.JsonUtil;
|
import bisq.core.util.JsonUtil;
|
||||||
import bisq.core.util.ParsingUtils;
|
import bisq.core.util.ParsingUtils;
|
||||||
|
import bisq.core.util.coin.CoinUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
@ -39,6 +40,8 @@ import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.utils.MonetaryFormat;
|
import org.bitcoinj.utils.MonetaryFormat;
|
||||||
import com.google.common.base.CaseFormat;
|
import com.google.common.base.CaseFormat;
|
||||||
|
@ -99,8 +102,27 @@ public class HavenoUtils {
|
||||||
|
|
||||||
private static final MonetaryFormat xmrCoinFormat = Config.baseCurrencyNetworkParameters().getMonetaryFormat();
|
private static final MonetaryFormat xmrCoinFormat = Config.baseCurrencyNetworkParameters().getMonetaryFormat();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Coin getMakerFee(@Nullable Coin amount) {
|
||||||
|
if (amount != null) {
|
||||||
|
Coin feePerXmr = getFeePerXmr(HavenoUtils.getMakerFeePerXmr(), amount);
|
||||||
|
return CoinUtil.maxCoin(feePerXmr, HavenoUtils.getMinMakerFee());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Coin getMakerFeePerBtc() {
|
@Nullable
|
||||||
|
public static Coin getTakerFee(@Nullable Coin amount) {
|
||||||
|
if (amount != null) {
|
||||||
|
Coin feePerXmr = HavenoUtils.getFeePerXmr(HavenoUtils.getTakerFeePerXmr(), amount);
|
||||||
|
return CoinUtil.maxCoin(feePerXmr, HavenoUtils.getMinTakerFee());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Coin getMakerFeePerXmr() {
|
||||||
return ParsingUtils.parseToCoin("0.001", xmrCoinFormat);
|
return ParsingUtils.parseToCoin("0.001", xmrCoinFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +130,7 @@ public class HavenoUtils {
|
||||||
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
|
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Coin getTakerFeePerBtc() {
|
private static Coin getTakerFeePerXmr() {
|
||||||
return ParsingUtils.parseToCoin("0.003", xmrCoinFormat);
|
return ParsingUtils.parseToCoin("0.003", xmrCoinFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +138,14 @@ public class HavenoUtils {
|
||||||
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
|
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Coin getFeePerXmr(Coin feePerXmr, Coin amount) {
|
||||||
|
double feePerBtcAsDouble = feePerXmr != null ? (double) feePerXmr.value : 0;
|
||||||
|
double amountAsDouble = amount != null ? (double) amount.value : 0;
|
||||||
|
double btcAsDouble = (double) Coin.COIN.value;
|
||||||
|
double fact = amountAsDouble / btcAsDouble;
|
||||||
|
return Coin.valueOf(Math.round(feePerBtcAsDouble * fact));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get address to collect trade fees.
|
* Get address to collect trade fees.
|
||||||
*
|
*
|
||||||
|
|
|
@ -55,7 +55,6 @@ import bisq.core.trade.statistics.ReferralIdService;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.core.user.User;
|
import bisq.core.user.User;
|
||||||
import bisq.core.util.Validator;
|
import bisq.core.util.Validator;
|
||||||
import bisq.core.util.coin.CoinUtil;
|
|
||||||
import bisq.network.p2p.BootstrapListener;
|
import bisq.network.p2p.BootstrapListener;
|
||||||
import bisq.network.p2p.DecryptedDirectMessageListener;
|
import bisq.network.p2p.DecryptedDirectMessageListener;
|
||||||
import bisq.network.p2p.DecryptedMessageWithPubKey;
|
import bisq.network.p2p.DecryptedMessageWithPubKey;
|
||||||
|
@ -456,9 +455,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute expected taker fee
|
// get expected taker fee
|
||||||
Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), Coin.valueOf(offer.getOfferPayload().getAmount()));
|
Coin takerFee = HavenoUtils.getTakerFee(Coin.valueOf(offer.getOfferPayload().getAmount()));
|
||||||
Coin takerFee = CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee());
|
|
||||||
|
|
||||||
// create arbitrator trade
|
// create arbitrator trade
|
||||||
trade = new ArbitratorTrade(offer,
|
trade = new ArbitratorTrade(offer,
|
||||||
|
@ -522,13 +520,17 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reserve open offer
|
||||||
openOfferManager.reserveOpenOffer(openOffer); // TODO (woodser): reserve offer if arbitrator? probably. or, arbitrator does not have open offer?
|
openOfferManager.reserveOpenOffer(openOffer); // TODO (woodser): reserve offer if arbitrator? probably. or, arbitrator does not have open offer?
|
||||||
|
|
||||||
|
// get expected taker fee
|
||||||
|
Coin takerFee = HavenoUtils.getTakerFee(Coin.valueOf(offer.getOfferPayload().getAmount()));
|
||||||
|
|
||||||
Trade trade;
|
Trade trade;
|
||||||
if (offer.isBuyOffer())
|
if (offer.isBuyOffer())
|
||||||
trade = new BuyerAsMakerTrade(offer,
|
trade = new BuyerAsMakerTrade(offer,
|
||||||
Coin.valueOf(offer.getOfferPayload().getAmount()),
|
Coin.valueOf(offer.getOfferPayload().getAmount()),
|
||||||
Coin.valueOf(offer.getOfferPayload().getMakerFee()), // TODO (woodser): this is maker fee, but Trade calls it taker fee, why not have both?
|
takerFee,
|
||||||
offer.getOfferPayload().getPrice(),
|
offer.getOfferPayload().getPrice(),
|
||||||
xmrWalletService,
|
xmrWalletService,
|
||||||
getNewProcessModel(offer),
|
getNewProcessModel(offer),
|
||||||
|
@ -539,7 +541,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
else
|
else
|
||||||
trade = new SellerAsMakerTrade(offer,
|
trade = new SellerAsMakerTrade(offer,
|
||||||
Coin.valueOf(offer.getOfferPayload().getAmount()),
|
Coin.valueOf(offer.getOfferPayload().getAmount()),
|
||||||
Coin.valueOf(offer.getOfferPayload().getMakerFee()),
|
takerFee,
|
||||||
offer.getOfferPayload().getPrice(),
|
offer.getOfferPayload().getPrice(),
|
||||||
xmrWalletService,
|
xmrWalletService,
|
||||||
getNewProcessModel(offer),
|
getNewProcessModel(offer),
|
||||||
|
|
|
@ -75,22 +75,19 @@ public class ArbitratorProcessDepositRequest extends TradeTask {
|
||||||
|
|
||||||
// collect expected values
|
// collect expected values
|
||||||
Offer offer = trade.getOffer();
|
Offer offer = trade.getOffer();
|
||||||
boolean isFromTaker = request.getSenderNodeAddress().equals(trade.getTaker().getNodeAddress());
|
TradingPeer trader = trade.getTradingPeer(request.getSenderNodeAddress());
|
||||||
boolean isFromBuyer = isFromTaker ? offer.getDirection() == OfferDirection.SELL : offer.getDirection() == OfferDirection.BUY;
|
boolean isFromTaker = trader == trade.getTaker();
|
||||||
BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(isFromBuyer ? Coin.ZERO : offer.getAmount());
|
boolean isFromBuyer = trader == trade.getBuyer();
|
||||||
|
BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(isFromTaker ? trade.getTakerFee() : trade.getMakerFee());
|
||||||
|
BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(isFromBuyer ? Coin.ZERO : offer.getAmount());
|
||||||
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(isFromBuyer ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
|
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(isFromBuyer ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
|
||||||
String depositAddress = processModel.getMultisigAddress();
|
String depositAddress = processModel.getMultisigAddress();
|
||||||
BigInteger tradeFee;
|
|
||||||
TradingPeer trader = trade.getTradingPeer(request.getSenderNodeAddress());
|
|
||||||
if (trader == processModel.getMaker()) tradeFee = HavenoUtils.coinToAtomicUnits(trade.getOffer().getMakerFee());
|
|
||||||
else if (trader == processModel.getTaker()) tradeFee = HavenoUtils.coinToAtomicUnits(trade.getTakerFee());
|
|
||||||
else throw new RuntimeException("DepositRequest is not from maker or taker");
|
|
||||||
|
|
||||||
// verify deposit tx
|
// verify deposit tx
|
||||||
try {
|
try {
|
||||||
trade.getXmrWalletService().verifyTradeTx(
|
trade.getXmrWalletService().verifyTradeTx(
|
||||||
tradeFee,
|
tradeFee,
|
||||||
peerAmount,
|
sendAmount,
|
||||||
securityDeposit,
|
securityDeposit,
|
||||||
depositAddress,
|
depositAddress,
|
||||||
trader.getDepositTxHash(),
|
trader.getDepositTxHash(),
|
||||||
|
|
|
@ -54,14 +54,14 @@ public class ArbitratorProcessReserveTx extends TradeTask {
|
||||||
|
|
||||||
// TODO (woodser): if signer online, should never be called by maker
|
// TODO (woodser): if signer online, should never be called by maker
|
||||||
|
|
||||||
// process reserve tx with expected terms
|
// process reserve tx with expected values
|
||||||
BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(isFromTaker ? trade.getTakerFee() : offer.getMakerFee());
|
BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(isFromTaker ? trade.getTakerFee() : trade.getMakerFee());
|
||||||
BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(isFromBuyer ? Coin.ZERO : offer.getAmount());
|
BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(isFromBuyer ? Coin.ZERO : offer.getAmount());
|
||||||
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(isFromBuyer ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
|
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(isFromBuyer ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit());
|
||||||
try {
|
try {
|
||||||
trade.getXmrWalletService().verifyTradeTx(
|
trade.getXmrWalletService().verifyTradeTx(
|
||||||
tradeFee,
|
tradeFee,
|
||||||
peerAmount,
|
sendAmount,
|
||||||
securityDeposit,
|
securityDeposit,
|
||||||
request.getPayoutAddress(),
|
request.getPayoutAddress(),
|
||||||
request.getReserveTxHash(),
|
request.getReserveTxHash(),
|
||||||
|
|
|
@ -44,10 +44,10 @@ public class TakerReserveTradeFunds extends TradeTask {
|
||||||
|
|
||||||
// create reserve tx
|
// create reserve tx
|
||||||
BigInteger takerFee = HavenoUtils.coinToAtomicUnits(trade.getTakerFee());
|
BigInteger takerFee = HavenoUtils.coinToAtomicUnits(trade.getTakerFee());
|
||||||
BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getOffer().getAmount() : Coin.ZERO);
|
BigInteger sendAmount = HavenoUtils.coinToAtomicUnits(trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getOffer().getAmount() : Coin.ZERO);
|
||||||
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getOffer().getSellerSecurityDeposit() : trade.getOffer().getBuyerSecurityDeposit());
|
BigInteger securityDeposit = HavenoUtils.coinToAtomicUnits(trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getOffer().getSellerSecurityDeposit() : trade.getOffer().getBuyerSecurityDeposit());
|
||||||
String returnAddress = model.getXmrWalletService().getOrCreateAddressEntry(trade.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString();
|
String returnAddress = model.getXmrWalletService().getOrCreateAddressEntry(trade.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString();
|
||||||
MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(takerFee, peerAmount, securityDeposit, returnAddress);
|
MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(takerFee, sendAmount, securityDeposit, returnAddress);
|
||||||
|
|
||||||
// collect reserved key images
|
// collect reserved key images
|
||||||
List<String> reservedKeyImages = new ArrayList<String>();
|
List<String> reservedKeyImages = new ArrayList<String>();
|
||||||
|
|
|
@ -20,28 +20,17 @@ package bisq.core.util.coin;
|
||||||
import bisq.core.btc.wallet.Restrictions;
|
import bisq.core.btc.wallet.Restrictions;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.monetary.Volume;
|
import bisq.core.monetary.Volume;
|
||||||
import bisq.core.trade.HavenoUtils;
|
|
||||||
import bisq.common.util.MathUtils;
|
import bisq.common.util.MathUtils;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static bisq.core.util.VolumeUtil.getAdjustedFiatVolume;
|
import static bisq.core.util.VolumeUtil.getAdjustedFiatVolume;
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
public class CoinUtil {
|
public class CoinUtil {
|
||||||
|
|
||||||
// Get the fee per amount
|
|
||||||
public static Coin getFeePerBtc(Coin feePerBtc, Coin amount) {
|
|
||||||
double feePerBtcAsDouble = feePerBtc != null ? (double) feePerBtc.value : 0;
|
|
||||||
double amountAsDouble = amount != null ? (double) amount.value : 0;
|
|
||||||
double btcAsDouble = (double) Coin.COIN.value;
|
|
||||||
double fact = amountAsDouble / btcAsDouble;
|
|
||||||
return Coin.valueOf(Math.round(feePerBtcAsDouble * fact));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Coin minCoin(Coin a, Coin b) {
|
public static Coin minCoin(Coin a, Coin b) {
|
||||||
return a.compareTo(b) <= 0 ? a : b;
|
return a.compareTo(b) <= 0 ? a : b;
|
||||||
|
@ -87,23 +76,6 @@ public class CoinUtil {
|
||||||
return Coin.valueOf(Math.round(percent * amountAsDouble));
|
return Coin.valueOf(Math.round(percent * amountAsDouble));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the maker fee for the given amount, marketPrice and marketPriceMargin.
|
|
||||||
*
|
|
||||||
* @param amount the amount of BTC to trade
|
|
||||||
* @return the maker fee for the given trade amount, or {@code null} if the amount is {@code null}
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static Coin getMakerFee(@Nullable Coin amount) {
|
|
||||||
if (amount != null) {
|
|
||||||
Coin feePerBtc = getFeePerBtc(HavenoUtils.getMakerFeePerBtc(), amount);
|
|
||||||
return maxCoin(feePerBtc, HavenoUtils.getMinMakerFee());
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the possibly adjusted amount for {@code amount}, taking into account the
|
* Calculate the possibly adjusted amount for {@code amount}, taking into account the
|
||||||
* {@code price} and {@code maxTradeLimit} and {@code factor}.
|
* {@code price} and {@code maxTradeLimit} and {@code factor}.
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package bisq.core.util.coin;
|
package bisq.core.util.coin;
|
||||||
|
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
|
import bisq.core.trade.HavenoUtils;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
|
@ -30,10 +31,10 @@ public class CoinUtilTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetFeePerBtc() {
|
public void testGetFeePerBtc() {
|
||||||
assertEquals(Coin.parseCoin("1"), CoinUtil.getFeePerBtc(Coin.parseCoin("1"), Coin.parseCoin("1")));
|
assertEquals(Coin.parseCoin("1"), HavenoUtils.getFeePerXmr(Coin.parseCoin("1"), Coin.parseCoin("1")));
|
||||||
assertEquals(Coin.parseCoin("0.1"), CoinUtil.getFeePerBtc(Coin.parseCoin("0.1"), Coin.parseCoin("1")));
|
assertEquals(Coin.parseCoin("0.1"), HavenoUtils.getFeePerXmr(Coin.parseCoin("0.1"), Coin.parseCoin("1")));
|
||||||
assertEquals(Coin.parseCoin("0.01"), CoinUtil.getFeePerBtc(Coin.parseCoin("0.1"), Coin.parseCoin("0.1")));
|
assertEquals(Coin.parseCoin("0.01"), HavenoUtils.getFeePerXmr(Coin.parseCoin("0.1"), Coin.parseCoin("0.1")));
|
||||||
assertEquals(Coin.parseCoin("0.015"), CoinUtil.getFeePerBtc(Coin.parseCoin("0.3"), Coin.parseCoin("0.05")));
|
assertEquals(Coin.parseCoin("0.015"), HavenoUtils.getFeePerXmr(Coin.parseCoin("0.3"), Coin.parseCoin("0.05")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -37,6 +37,7 @@ import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.offer.OpenOfferManager;
|
import bisq.core.offer.OpenOfferManager;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
|
import bisq.core.trade.HavenoUtils;
|
||||||
import bisq.core.trade.handlers.TransactionResultHandler;
|
import bisq.core.trade.handlers.TransactionResultHandler;
|
||||||
import bisq.core.trade.statistics.TradeStatistics3;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
|
@ -674,11 +675,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Coin getMakerFee() {
|
public Coin getMakerFee() {
|
||||||
return offerUtil.getMakerFee(amount.get());
|
return HavenoUtils.getMakerFee(amount.get());
|
||||||
}
|
|
||||||
|
|
||||||
public Coin getMakerFeeInBtc() {
|
|
||||||
return CoinUtil.getMakerFee(amount.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean canPlaceOffer() {
|
boolean canPlaceOffer() {
|
||||||
|
|
|
@ -506,7 +506,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
isTradeFeeVisible.setValue(true);
|
isTradeFeeVisible.setValue(true);
|
||||||
tradeFee.set(getFormatterForMakerFee().formatCoin(makerFeeAsCoin));
|
tradeFee.set(getFormatterForMakerFee().formatCoin(makerFeeAsCoin));
|
||||||
tradeFeeInBtcWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil,
|
tradeFeeInBtcWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil,
|
||||||
dataModel.getMakerFeeInBtc(),
|
dataModel.getMakerFee(),
|
||||||
btcFormatter));
|
btcFormatter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -996,7 +996,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
|
|
||||||
public String getTradeFee() {
|
public String getTradeFee() {
|
||||||
return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil,
|
return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil,
|
||||||
dataModel.getMakerFeeInBtc(),
|
dataModel.getMakerFee(),
|
||||||
dataModel.getAmount().get(),
|
dataModel.getAmount().get(),
|
||||||
btcFormatter,
|
btcFormatter,
|
||||||
HavenoUtils.getMinMakerFee());
|
HavenoUtils.getMinMakerFee());
|
||||||
|
|
|
@ -434,14 +434,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Coin getTakerFee() {
|
Coin getTakerFee() {
|
||||||
Coin amount = this.amount.get();
|
return HavenoUtils.getTakerFee(this.amount.get());
|
||||||
if (amount != null) {
|
|
||||||
// TODO write unit test for that
|
|
||||||
Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), amount);
|
|
||||||
return CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee());
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void swapTradeToSavings() {
|
public void swapTradeToSavings() {
|
||||||
|
@ -523,8 +516,4 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
public boolean isUsingHalCashAccount() {
|
public boolean isUsingHalCashAccount() {
|
||||||
return paymentAccount.hasPaymentMethodWithId(HAL_CASH_ID);
|
return paymentAccount.hasPaymentMethodWithId(HAL_CASH_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Coin getTakerFeeInBtc() {
|
|
||||||
return offerUtil.getTakerFee(amount.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -547,7 +547,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
priceCurrencyLabel.textProperty().bind(createStringBinding(() -> CurrencyUtil.getCounterCurrency(model.dataModel.getCurrencyCode())));
|
priceCurrencyLabel.textProperty().bind(createStringBinding(() -> CurrencyUtil.getCounterCurrency(model.dataModel.getCurrencyCode())));
|
||||||
priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty());
|
priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty());
|
||||||
nextButton.disableProperty().bind(model.isNextButtonDisabled);
|
nextButton.disableProperty().bind(model.isNextButtonDisabled);
|
||||||
tradeFeeInBtcLabel.textProperty().bind(model.tradeFeeInBtcWithFiat);
|
tradeFeeInBtcLabel.textProperty().bind(model.tradeFeeInXmrWithFiat);
|
||||||
tradeFeeDescriptionLabel.textProperty().bind(model.tradeFeeDescription);
|
tradeFeeDescriptionLabel.textProperty().bind(model.tradeFeeDescription);
|
||||||
tradeFeeInBtcLabel.visibleProperty().bind(model.isTradeFeeVisible);
|
tradeFeeInBtcLabel.visibleProperty().bind(model.isTradeFeeVisible);
|
||||||
tradeFeeDescriptionLabel.visibleProperty().bind(model.isTradeFeeVisible);
|
tradeFeeDescriptionLabel.visibleProperty().bind(model.isTradeFeeVisible);
|
||||||
|
|
|
@ -103,7 +103,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||||
final StringProperty offerWarning = new SimpleStringProperty();
|
final StringProperty offerWarning = new SimpleStringProperty();
|
||||||
final StringProperty spinnerInfoText = new SimpleStringProperty("");
|
final StringProperty spinnerInfoText = new SimpleStringProperty("");
|
||||||
final StringProperty tradeFee = new SimpleStringProperty();
|
final StringProperty tradeFee = new SimpleStringProperty();
|
||||||
final StringProperty tradeFeeInBtcWithFiat = new SimpleStringProperty();
|
final StringProperty tradeFeeInXmrWithFiat = new SimpleStringProperty();
|
||||||
final StringProperty tradeFeeDescription = new SimpleStringProperty();
|
final StringProperty tradeFeeDescription = new SimpleStringProperty();
|
||||||
final BooleanProperty isTradeFeeVisible = new SimpleBooleanProperty(false);
|
final BooleanProperty isTradeFeeVisible = new SimpleBooleanProperty(false);
|
||||||
|
|
||||||
|
@ -283,8 +283,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||||
|
|
||||||
isTradeFeeVisible.setValue(true);
|
isTradeFeeVisible.setValue(true);
|
||||||
tradeFee.set(getFormatterForTakerFee().formatCoin(takerFeeAsCoin));
|
tradeFee.set(getFormatterForTakerFee().formatCoin(takerFeeAsCoin));
|
||||||
tradeFeeInBtcWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil,
|
tradeFeeInXmrWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil,
|
||||||
dataModel.getTakerFeeInBtc(),
|
dataModel.getTakerFee(),
|
||||||
xmrFormatter));
|
xmrFormatter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -689,7 +689,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||||
|
|
||||||
public String getTradeFee() {
|
public String getTradeFee() {
|
||||||
return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil,
|
return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil,
|
||||||
dataModel.getTakerFeeInBtc(),
|
dataModel.getTakerFee(),
|
||||||
dataModel.getAmount().get(),
|
dataModel.getAmount().get(),
|
||||||
xmrFormatter,
|
xmrFormatter,
|
||||||
HavenoUtils.getMinMakerFee());
|
HavenoUtils.getMinMakerFee());
|
||||||
|
|
|
@ -17,8 +17,6 @@ import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.user.User;
|
import bisq.core.user.User;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
|
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -47,7 +45,7 @@ public class CreateOfferDataModelTest {
|
||||||
Res.setup();
|
Res.setup();
|
||||||
|
|
||||||
XmrAddressEntry addressEntry = mock(XmrAddressEntry.class);
|
XmrAddressEntry addressEntry = mock(XmrAddressEntry.class);
|
||||||
XmrWalletService btcWalletService = mock(XmrWalletService.class);
|
XmrWalletService xmrWalletService = mock(XmrWalletService.class);
|
||||||
PriceFeedService priceFeedService = mock(PriceFeedService.class);
|
PriceFeedService priceFeedService = mock(PriceFeedService.class);
|
||||||
CreateOfferService createOfferService = mock(CreateOfferService.class);
|
CreateOfferService createOfferService = mock(CreateOfferService.class);
|
||||||
preferences = mock(Preferences.class);
|
preferences = mock(Preferences.class);
|
||||||
|
@ -55,7 +53,7 @@ public class CreateOfferDataModelTest {
|
||||||
user = mock(User.class);
|
user = mock(User.class);
|
||||||
var tradeStats = mock(TradeStatisticsManager.class);
|
var tradeStats = mock(TradeStatisticsManager.class);
|
||||||
|
|
||||||
when(btcWalletService.getOrCreateAddressEntry(anyString(), any())).thenReturn(addressEntry);
|
when(xmrWalletService.getOrCreateAddressEntry(anyString(), any())).thenReturn(addressEntry);
|
||||||
when(preferences.isUsePercentageBasedPrice()).thenReturn(true);
|
when(preferences.isUsePercentageBasedPrice()).thenReturn(true);
|
||||||
when(preferences.getBuyerSecurityDepositAsPercent(null)).thenReturn(0.01);
|
when(preferences.getBuyerSecurityDepositAsPercent(null)).thenReturn(0.01);
|
||||||
when(createOfferService.getRandomOfferId()).thenReturn(UUID.randomUUID().toString());
|
when(createOfferService.getRandomOfferId()).thenReturn(UUID.randomUUID().toString());
|
||||||
|
@ -64,7 +62,7 @@ public class CreateOfferDataModelTest {
|
||||||
model = new CreateOfferDataModel(createOfferService,
|
model = new CreateOfferDataModel(createOfferService,
|
||||||
null,
|
null,
|
||||||
offerUtil,
|
offerUtil,
|
||||||
btcWalletService,
|
xmrWalletService,
|
||||||
preferences,
|
preferences,
|
||||||
user,
|
user,
|
||||||
null,
|
null,
|
||||||
|
@ -91,7 +89,6 @@ public class CreateOfferDataModelTest {
|
||||||
|
|
||||||
when(user.getPaymentAccounts()).thenReturn(paymentAccounts);
|
when(user.getPaymentAccounts()).thenReturn(paymentAccounts);
|
||||||
when(preferences.getSelectedPaymentAccountForCreateOffer()).thenReturn(revolutAccount);
|
when(preferences.getSelectedPaymentAccountForCreateOffer()).thenReturn(revolutAccount);
|
||||||
when(offerUtil.getMakerFee(any())).thenReturn(Coin.ZERO);
|
|
||||||
|
|
||||||
model.initWithData(OfferDirection.BUY, new FiatCurrency("USD"));
|
model.initWithData(OfferDirection.BUY, new FiatCurrency("USD"));
|
||||||
assertEquals("USD", model.getTradeCurrencyCode().get());
|
assertEquals("USD", model.getTradeCurrencyCode().get());
|
||||||
|
@ -113,7 +110,6 @@ public class CreateOfferDataModelTest {
|
||||||
when(user.getPaymentAccounts()).thenReturn(paymentAccounts);
|
when(user.getPaymentAccounts()).thenReturn(paymentAccounts);
|
||||||
when(user.findFirstPaymentAccountWithCurrency(new FiatCurrency("USD"))).thenReturn(zelleAccount);
|
when(user.findFirstPaymentAccountWithCurrency(new FiatCurrency("USD"))).thenReturn(zelleAccount);
|
||||||
when(preferences.getSelectedPaymentAccountForCreateOffer()).thenReturn(revolutAccount);
|
when(preferences.getSelectedPaymentAccountForCreateOffer()).thenReturn(revolutAccount);
|
||||||
when(offerUtil.getMakerFee(any())).thenReturn(Coin.ZERO);
|
|
||||||
|
|
||||||
model.initWithData(OfferDirection.BUY, new FiatCurrency("USD"));
|
model.initWithData(OfferDirection.BUY, new FiatCurrency("USD"));
|
||||||
assertEquals("USD", model.getTradeCurrencyCode().get());
|
assertEquals("USD", model.getTradeCurrencyCode().get());
|
||||||
|
|
Loading…
Reference in a new issue