mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-22 19:49:32 +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 tradeFee trade fee
|
||||
* @param peerAmount amount to give peer
|
||||
* @param sendAmount amount to give peer
|
||||
* @param securityDeposit security deposit amount
|
||||
* @return a transaction to reserve a trade
|
||||
*/
|
||||
public MoneroTxWallet createReserveTx(BigInteger tradeFee, BigInteger peerAmount, BigInteger securityDeposit, String returnAddress) {
|
||||
log.info("Creating reserve tx with fee={}, peerAmount={}, securityDeposit={}", tradeFee, peerAmount, securityDeposit);
|
||||
return createTradeTx(tradeFee, peerAmount, securityDeposit, returnAddress);
|
||||
public MoneroTxWallet createReserveTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String returnAddress) {
|
||||
log.info("Creating reserve tx with fee={}, sendAmount={}, securityDeposit={}", tradeFee, sendAmount, securityDeposit);
|
||||
return createTradeTx(tradeFee, sendAmount, securityDeposit, returnAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -324,7 +324,7 @@ public class XmrWalletService {
|
|||
Offer offer = trade.getProcessModel().getOffer();
|
||||
String multisigAddress = trade.getProcessModel().getMultisigAddress();
|
||||
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());
|
||||
|
||||
// thaw reserved outputs then create deposit tx
|
||||
|
@ -336,12 +336,12 @@ public class XmrWalletService {
|
|||
thawOutputs(trade.getSelf().getReserveTxKeyImages());
|
||||
}
|
||||
|
||||
log.info("Creating deposit tx with fee={}, peerAmount={}, securityDeposit={}", tradeFee, peerAmount, securityDeposit);
|
||||
return createTradeTx(tradeFee, peerAmount, securityDeposit, multisigAddress);
|
||||
log.info("Creating deposit tx with fee={}, sendAmount={}, securityDeposit={}", tradeFee, sendAmount, securityDeposit);
|
||||
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();
|
||||
synchronized (wallet) {
|
||||
|
||||
|
@ -349,7 +349,7 @@ public class XmrWalletService {
|
|||
MoneroTxWallet tradeTx = null;
|
||||
double appliedTolerance = 0.0; // percent of tolerance to apply, thereby decreasing security deposit
|
||||
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++) {
|
||||
try {
|
||||
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.
|
||||
*
|
||||
* @param tradeFee trade fee
|
||||
* @param peerAmount amount to give peer
|
||||
* @param sendAmount amount to give peer
|
||||
* @param securityDeposit security deposit amount
|
||||
* @param address expected destination address for the deposit amount
|
||||
* @param txHash transaction hash
|
||||
|
@ -387,7 +387,7 @@ public class XmrWalletService {
|
|||
* @param txKey transaction key
|
||||
* @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();
|
||||
MoneroWallet wallet = getWallet();
|
||||
try {
|
||||
|
@ -426,7 +426,7 @@ public class XmrWalletService {
|
|||
// verify deposit amount
|
||||
check = wallet.checkTxKey(txHash, txKey, address);
|
||||
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());
|
||||
} finally {
|
||||
try {
|
||||
|
|
|
@ -27,6 +27,7 @@ import bisq.core.payment.PaymentAccountUtil;
|
|||
import bisq.core.provider.price.MarketPrice;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||
import bisq.core.trade.HavenoUtils;
|
||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.user.User;
|
||||
|
@ -155,7 +156,7 @@ public class CreateOfferService {
|
|||
String bankId = PaymentAccountUtil.getBankId(paymentAccount);
|
||||
List<String> acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount);
|
||||
double sellerSecurityDeposit = getSellerSecurityDepositAsDouble(buyerSecurityDepositAsDouble);
|
||||
Coin makerFeeAsCoin = offerUtil.getMakerFee(amount);
|
||||
Coin makerFeeAsCoin = HavenoUtils.getMakerFee(amount);
|
||||
Coin buyerSecurityDepositAsCoin = getBuyerSecurityDeposit(amount, buyerSecurityDepositAsDouble);
|
||||
Coin sellerSecurityDepositAsCoin = getSellerSecurityDeposit(amount, sellerSecurityDeposit);
|
||||
long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction);
|
||||
|
|
|
@ -29,12 +29,10 @@ import bisq.core.payment.F2FAccount;
|
|||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.provider.price.MarketPrice;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
import bisq.core.trade.HavenoUtils;
|
||||
import bisq.core.trade.statistics.ReferralIdService;
|
||||
import bisq.core.user.AutoConfirmSettings;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
import bisq.core.util.coin.CoinUtil;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
|
@ -58,8 +56,6 @@ import java.util.UUID;
|
|||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static bisq.common.util.MathUtils.roundDoubleToLong;
|
||||
import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
|
||||
import static bisq.core.btc.wallet.Restrictions.getMaxBuyerSecurityDepositAsPercent;
|
||||
|
@ -163,41 +159,6 @@ public class OfferUtil {
|
|||
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) {
|
||||
return offer != null && offer.getPaymentMethod().isBlockchain();
|
||||
}
|
||||
|
|
|
@ -857,14 +857,22 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
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());
|
||||
BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(offer.getMakerFee());
|
||||
BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(offer.getDirection() == OfferDirection.BUY ? Coin.ZERO : offer.getAmount());
|
||||
BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(HavenoUtils.getMakerFee(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());
|
||||
xmrWalletService.verifyTradeTx(
|
||||
tradeFee,
|
||||
peerAmount,
|
||||
sendAmount,
|
||||
securityDeposit,
|
||||
request.getPayoutAddress(),
|
||||
request.getReserveTxHash(),
|
||||
|
|
|
@ -24,6 +24,7 @@ import bisq.core.offer.Offer;
|
|||
import bisq.core.offer.OfferUtil;
|
||||
import bisq.core.offer.availability.OfferAvailabilityModel;
|
||||
import bisq.core.offer.messages.OfferAvailabilityRequest;
|
||||
import bisq.core.trade.HavenoUtils;
|
||||
import bisq.core.trade.messages.InitTradeRequest;
|
||||
import bisq.core.user.User;
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
@ -55,7 +56,6 @@ public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
|
|||
User user = model.getUser();
|
||||
P2PService p2PService = model.getP2PService();
|
||||
XmrWalletService walletService = model.getXmrWalletService();
|
||||
OfferUtil offerUtil = model.getOfferUtil();
|
||||
String paymentAccountId = model.getPaymentAccountId();
|
||||
String paymentMethodId = user.getPaymentAccount(paymentAccountId).getPaymentAccountPayload().getPaymentMethodId();
|
||||
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(),
|
||||
offer.getAmount().value,
|
||||
price.getValue(),
|
||||
offerUtil.getTakerFee(offer.getAmount()).value,
|
||||
HavenoUtils.getTakerFee(offer.getAmount()).value,
|
||||
user.getAccountId(),
|
||||
paymentAccountId,
|
||||
paymentMethodId,
|
||||
|
|
|
@ -52,10 +52,10 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
|||
|
||||
// create reserve tx
|
||||
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());
|
||||
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
|
||||
List<String> reservedKeyImages = new ArrayList<String>();
|
||||
|
|
|
@ -28,7 +28,7 @@ import bisq.core.offer.OfferUtil;
|
|||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.payload.PaymentMethod;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
|
||||
import bisq.core.trade.HavenoUtils;
|
||||
import bisq.common.taskrunner.Model;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
@ -111,7 +111,7 @@ public class TakeOfferModel implements Model {
|
|||
this.securityDeposit = offer.getDirection() == SELL
|
||||
? offer.getBuyerSecurityDeposit()
|
||||
: offer.getSellerSecurityDeposit();
|
||||
this.takerFee = offerUtil.getTakerFee(amount);
|
||||
this.takerFee = HavenoUtils.getTakerFee(amount);
|
||||
|
||||
calculateVolume();
|
||||
calculateTotalToPay();
|
||||
|
|
|
@ -32,8 +32,6 @@ import java.util.UUID;
|
|||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Slf4j
|
||||
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.util.JsonUtil;
|
||||
import bisq.core.util.ParsingUtils;
|
||||
import bisq.core.util.coin.CoinUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
@ -39,6 +40,8 @@ import java.util.concurrent.ExecutorService;
|
|||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.utils.MonetaryFormat;
|
||||
import com.google.common.base.CaseFormat;
|
||||
|
@ -99,8 +102,27 @@ public class HavenoUtils {
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -108,7 +130,7 @@ public class HavenoUtils {
|
|||
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
|
||||
}
|
||||
|
||||
public static Coin getTakerFeePerBtc() {
|
||||
private static Coin getTakerFeePerXmr() {
|
||||
return ParsingUtils.parseToCoin("0.003", xmrCoinFormat);
|
||||
}
|
||||
|
||||
|
@ -116,6 +138,14 @@ public class HavenoUtils {
|
|||
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.
|
||||
*
|
||||
|
|
|
@ -55,7 +55,6 @@ import bisq.core.trade.statistics.ReferralIdService;
|
|||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
import bisq.core.user.User;
|
||||
import bisq.core.util.Validator;
|
||||
import bisq.core.util.coin.CoinUtil;
|
||||
import bisq.network.p2p.BootstrapListener;
|
||||
import bisq.network.p2p.DecryptedDirectMessageListener;
|
||||
import bisq.network.p2p.DecryptedMessageWithPubKey;
|
||||
|
@ -456,9 +455,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
return;
|
||||
}
|
||||
|
||||
// compute expected taker fee
|
||||
Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), Coin.valueOf(offer.getOfferPayload().getAmount()));
|
||||
Coin takerFee = CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee());
|
||||
// get expected taker fee
|
||||
Coin takerFee = HavenoUtils.getTakerFee(Coin.valueOf(offer.getOfferPayload().getAmount()));
|
||||
|
||||
// create arbitrator trade
|
||||
trade = new ArbitratorTrade(offer,
|
||||
|
@ -522,13 +520,17 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
return;
|
||||
}
|
||||
|
||||
// reserve 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;
|
||||
if (offer.isBuyOffer())
|
||||
trade = new BuyerAsMakerTrade(offer,
|
||||
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(),
|
||||
xmrWalletService,
|
||||
getNewProcessModel(offer),
|
||||
|
@ -539,7 +541,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
else
|
||||
trade = new SellerAsMakerTrade(offer,
|
||||
Coin.valueOf(offer.getOfferPayload().getAmount()),
|
||||
Coin.valueOf(offer.getOfferPayload().getMakerFee()),
|
||||
takerFee,
|
||||
offer.getOfferPayload().getPrice(),
|
||||
xmrWalletService,
|
||||
getNewProcessModel(offer),
|
||||
|
|
|
@ -75,22 +75,19 @@ public class ArbitratorProcessDepositRequest extends TradeTask {
|
|||
|
||||
// collect expected values
|
||||
Offer offer = trade.getOffer();
|
||||
boolean isFromTaker = request.getSenderNodeAddress().equals(trade.getTaker().getNodeAddress());
|
||||
boolean isFromBuyer = isFromTaker ? offer.getDirection() == OfferDirection.SELL : offer.getDirection() == OfferDirection.BUY;
|
||||
BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(isFromBuyer ? Coin.ZERO : offer.getAmount());
|
||||
TradingPeer trader = trade.getTradingPeer(request.getSenderNodeAddress());
|
||||
boolean isFromTaker = trader == trade.getTaker();
|
||||
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());
|
||||
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
|
||||
try {
|
||||
trade.getXmrWalletService().verifyTradeTx(
|
||||
tradeFee,
|
||||
peerAmount,
|
||||
sendAmount,
|
||||
securityDeposit,
|
||||
depositAddress,
|
||||
trader.getDepositTxHash(),
|
||||
|
|
|
@ -54,14 +54,14 @@ public class ArbitratorProcessReserveTx extends TradeTask {
|
|||
|
||||
// TODO (woodser): if signer online, should never be called by maker
|
||||
|
||||
// process reserve tx with expected terms
|
||||
BigInteger tradeFee = HavenoUtils.coinToAtomicUnits(isFromTaker ? trade.getTakerFee() : offer.getMakerFee());
|
||||
BigInteger peerAmount = HavenoUtils.coinToAtomicUnits(isFromBuyer ? Coin.ZERO : offer.getAmount());
|
||||
// process reserve tx with expected values
|
||||
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());
|
||||
try {
|
||||
trade.getXmrWalletService().verifyTradeTx(
|
||||
tradeFee,
|
||||
peerAmount,
|
||||
sendAmount,
|
||||
securityDeposit,
|
||||
request.getPayoutAddress(),
|
||||
request.getReserveTxHash(),
|
||||
|
|
|
@ -44,10 +44,10 @@ public class TakerReserveTradeFunds extends TradeTask {
|
|||
|
||||
// create reserve tx
|
||||
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());
|
||||
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
|
||||
List<String> reservedKeyImages = new ArrayList<String>();
|
||||
|
|
|
@ -20,28 +20,17 @@ package bisq.core.util.coin;
|
|||
import bisq.core.btc.wallet.Restrictions;
|
||||
import bisq.core.monetary.Price;
|
||||
import bisq.core.monetary.Volume;
|
||||
import bisq.core.trade.HavenoUtils;
|
||||
import bisq.common.util.MathUtils;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static bisq.core.util.VolumeUtil.getAdjustedFiatVolume;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
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) {
|
||||
return a.compareTo(b) <= 0 ? a : b;
|
||||
|
@ -87,23 +76,6 @@ public class CoinUtil {
|
|||
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
|
||||
* {@code price} and {@code maxTradeLimit} and {@code factor}.
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package bisq.core.util.coin;
|
||||
|
||||
import bisq.core.monetary.Price;
|
||||
import bisq.core.trade.HavenoUtils;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
|
@ -30,10 +31,10 @@ public class CoinUtilTest {
|
|||
|
||||
@Test
|
||||
public void testGetFeePerBtc() {
|
||||
assertEquals(Coin.parseCoin("1"), CoinUtil.getFeePerBtc(Coin.parseCoin("1"), Coin.parseCoin("1")));
|
||||
assertEquals(Coin.parseCoin("0.1"), CoinUtil.getFeePerBtc(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.015"), CoinUtil.getFeePerBtc(Coin.parseCoin("0.3"), Coin.parseCoin("0.05")));
|
||||
assertEquals(Coin.parseCoin("1"), HavenoUtils.getFeePerXmr(Coin.parseCoin("1"), Coin.parseCoin("1")));
|
||||
assertEquals(Coin.parseCoin("0.1"), HavenoUtils.getFeePerXmr(Coin.parseCoin("0.1"), Coin.parseCoin("1")));
|
||||
assertEquals(Coin.parseCoin("0.01"), HavenoUtils.getFeePerXmr(Coin.parseCoin("0.1"), Coin.parseCoin("0.1")));
|
||||
assertEquals(Coin.parseCoin("0.015"), HavenoUtils.getFeePerXmr(Coin.parseCoin("0.3"), Coin.parseCoin("0.05")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -37,6 +37,7 @@ import bisq.core.offer.OfferUtil;
|
|||
import bisq.core.offer.OpenOfferManager;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
import bisq.core.trade.HavenoUtils;
|
||||
import bisq.core.trade.handlers.TransactionResultHandler;
|
||||
import bisq.core.trade.statistics.TradeStatistics3;
|
||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
|
@ -674,11 +675,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
|||
}
|
||||
|
||||
public Coin getMakerFee() {
|
||||
return offerUtil.getMakerFee(amount.get());
|
||||
}
|
||||
|
||||
public Coin getMakerFeeInBtc() {
|
||||
return CoinUtil.getMakerFee(amount.get());
|
||||
return HavenoUtils.getMakerFee(amount.get());
|
||||
}
|
||||
|
||||
boolean canPlaceOffer() {
|
||||
|
|
|
@ -506,7 +506,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
|||
isTradeFeeVisible.setValue(true);
|
||||
tradeFee.set(getFormatterForMakerFee().formatCoin(makerFeeAsCoin));
|
||||
tradeFeeInBtcWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil,
|
||||
dataModel.getMakerFeeInBtc(),
|
||||
dataModel.getMakerFee(),
|
||||
btcFormatter));
|
||||
}
|
||||
|
||||
|
@ -996,7 +996,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
|||
|
||||
public String getTradeFee() {
|
||||
return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil,
|
||||
dataModel.getMakerFeeInBtc(),
|
||||
dataModel.getMakerFee(),
|
||||
dataModel.getAmount().get(),
|
||||
btcFormatter,
|
||||
HavenoUtils.getMinMakerFee());
|
||||
|
|
|
@ -434,14 +434,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
|
||||
@Nullable
|
||||
Coin getTakerFee() {
|
||||
Coin amount = 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;
|
||||
}
|
||||
return HavenoUtils.getTakerFee(this.amount.get());
|
||||
}
|
||||
|
||||
public void swapTradeToSavings() {
|
||||
|
@ -523,8 +516,4 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
public boolean isUsingHalCashAccount() {
|
||||
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())));
|
||||
priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty());
|
||||
nextButton.disableProperty().bind(model.isNextButtonDisabled);
|
||||
tradeFeeInBtcLabel.textProperty().bind(model.tradeFeeInBtcWithFiat);
|
||||
tradeFeeInBtcLabel.textProperty().bind(model.tradeFeeInXmrWithFiat);
|
||||
tradeFeeDescriptionLabel.textProperty().bind(model.tradeFeeDescription);
|
||||
tradeFeeInBtcLabel.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 spinnerInfoText = new SimpleStringProperty("");
|
||||
final StringProperty tradeFee = new SimpleStringProperty();
|
||||
final StringProperty tradeFeeInBtcWithFiat = new SimpleStringProperty();
|
||||
final StringProperty tradeFeeInXmrWithFiat = new SimpleStringProperty();
|
||||
final StringProperty tradeFeeDescription = new SimpleStringProperty();
|
||||
final BooleanProperty isTradeFeeVisible = new SimpleBooleanProperty(false);
|
||||
|
||||
|
@ -283,8 +283,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
|
||||
isTradeFeeVisible.setValue(true);
|
||||
tradeFee.set(getFormatterForTakerFee().formatCoin(takerFeeAsCoin));
|
||||
tradeFeeInBtcWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil,
|
||||
dataModel.getTakerFeeInBtc(),
|
||||
tradeFeeInXmrWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil,
|
||||
dataModel.getTakerFee(),
|
||||
xmrFormatter));
|
||||
}
|
||||
|
||||
|
@ -689,7 +689,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
|
||||
public String getTradeFee() {
|
||||
return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil,
|
||||
dataModel.getTakerFeeInBtc(),
|
||||
dataModel.getTakerFee(),
|
||||
dataModel.getAmount().get(),
|
||||
xmrFormatter,
|
||||
HavenoUtils.getMinMakerFee());
|
||||
|
|
|
@ -17,8 +17,6 @@ import bisq.core.trade.statistics.TradeStatisticsManager;
|
|||
import bisq.core.user.Preferences;
|
||||
import bisq.core.user.User;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
@ -47,7 +45,7 @@ public class CreateOfferDataModelTest {
|
|||
Res.setup();
|
||||
|
||||
XmrAddressEntry addressEntry = mock(XmrAddressEntry.class);
|
||||
XmrWalletService btcWalletService = mock(XmrWalletService.class);
|
||||
XmrWalletService xmrWalletService = mock(XmrWalletService.class);
|
||||
PriceFeedService priceFeedService = mock(PriceFeedService.class);
|
||||
CreateOfferService createOfferService = mock(CreateOfferService.class);
|
||||
preferences = mock(Preferences.class);
|
||||
|
@ -55,7 +53,7 @@ public class CreateOfferDataModelTest {
|
|||
user = mock(User.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.getBuyerSecurityDepositAsPercent(null)).thenReturn(0.01);
|
||||
when(createOfferService.getRandomOfferId()).thenReturn(UUID.randomUUID().toString());
|
||||
|
@ -64,7 +62,7 @@ public class CreateOfferDataModelTest {
|
|||
model = new CreateOfferDataModel(createOfferService,
|
||||
null,
|
||||
offerUtil,
|
||||
btcWalletService,
|
||||
xmrWalletService,
|
||||
preferences,
|
||||
user,
|
||||
null,
|
||||
|
@ -91,7 +89,6 @@ public class CreateOfferDataModelTest {
|
|||
|
||||
when(user.getPaymentAccounts()).thenReturn(paymentAccounts);
|
||||
when(preferences.getSelectedPaymentAccountForCreateOffer()).thenReturn(revolutAccount);
|
||||
when(offerUtil.getMakerFee(any())).thenReturn(Coin.ZERO);
|
||||
|
||||
model.initWithData(OfferDirection.BUY, new FiatCurrency("USD"));
|
||||
assertEquals("USD", model.getTradeCurrencyCode().get());
|
||||
|
@ -113,7 +110,6 @@ public class CreateOfferDataModelTest {
|
|||
when(user.getPaymentAccounts()).thenReturn(paymentAccounts);
|
||||
when(user.findFirstPaymentAccountWithCurrency(new FiatCurrency("USD"))).thenReturn(zelleAccount);
|
||||
when(preferences.getSelectedPaymentAccountForCreateOffer()).thenReturn(revolutAccount);
|
||||
when(offerUtil.getMakerFee(any())).thenReturn(Coin.ZERO);
|
||||
|
||||
model.initWithData(OfferDirection.BUY, new FiatCurrency("USD"));
|
||||
assertEquals("USD", model.getTradeCurrencyCode().get());
|
||||
|
|
Loading…
Reference in a new issue