trade fees are adjustable and persisted in offer payload

This commit is contained in:
woodser 2024-04-07 08:12:23 -04:00
parent 59fbd805a5
commit 7d7660414a
73 changed files with 539 additions and 542 deletions

View file

@ -189,7 +189,7 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
protected final Function<TradeInfo, Long> toTradeFeeBtc = (t) -> { protected final Function<TradeInfo, Long> toTradeFeeBtc = (t) -> {
var isMyOffer = t.getOffer().getIsMyOffer(); var isMyOffer = t.getOffer().getIsMyOffer();
if (isMyOffer) { if (isMyOffer) {
return t.getOffer().getMakerFee(); return t.getMakerFee();
} else { } else {
return t.getTakerFee(); return t.getTakerFee();
} }
@ -198,7 +198,7 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
protected final Function<TradeInfo, Long> toMyMakerOrTakerFee = (t) -> { protected final Function<TradeInfo, Long> toMyMakerOrTakerFee = (t) -> {
return isTaker.test(t) return isTaker.test(t)
? t.getTakerFee() ? t.getTakerFee()
: t.getOffer().getMakerFee(); : t.getMakerFee();
}; };
protected final Function<TradeInfo, String> toOfferType = (t) -> { protected final Function<TradeInfo, String> toOfferType = (t) -> {

View file

@ -143,18 +143,15 @@ class CoreTradesService {
} }
// synchronize access to take offer model // TODO (woodser): to avoid synchronizing, don't use stateful model // synchronize access to take offer model // TODO (woodser): to avoid synchronizing, don't use stateful model
BigInteger takerFee;
BigInteger fundsNeededForTrade; BigInteger fundsNeededForTrade;
synchronized (takeOfferModel) { synchronized (takeOfferModel) {
takeOfferModel.initModel(offer, paymentAccount, amount, useSavingsWallet); takeOfferModel.initModel(offer, paymentAccount, amount, useSavingsWallet);
takerFee = takeOfferModel.getTakerFee();
fundsNeededForTrade = takeOfferModel.getFundsNeededForTrade(); fundsNeededForTrade = takeOfferModel.getFundsNeededForTrade();
log.debug("Initiating take {} offer, {}", offer.isBuyOffer() ? "buy" : "sell", takeOfferModel); log.debug("Initiating take {} offer, {}", offer.isBuyOffer() ? "buy" : "sell", takeOfferModel);
} }
// take offer // take offer
tradeManager.onTakeOffer(amount, tradeManager.onTakeOffer(amount,
takerFee,
fundsNeededForTrade, fundsNeededForTrade,
offer, offer,
paymentAccountId, paymentAccountId,

View file

@ -52,8 +52,9 @@ public class OfferInfo implements Payload {
private final long minAmount; private final long minAmount;
private final String volume; private final String volume;
private final String minVolume; private final String minVolume;
private final long makerFee; private final double makerFeePct;
@Nullable private final double takerFeePct;
private final double penaltyFeePct;
private final double buyerSecurityDepositPct; private final double buyerSecurityDepositPct;
private final double sellerSecurityDepositPct; private final double sellerSecurityDepositPct;
private final String triggerPrice; private final String triggerPrice;
@ -86,11 +87,13 @@ public class OfferInfo implements Payload {
this.marketPriceMarginPct = builder.getMarketPriceMarginPct(); this.marketPriceMarginPct = builder.getMarketPriceMarginPct();
this.amount = builder.getAmount(); this.amount = builder.getAmount();
this.minAmount = builder.getMinAmount(); this.minAmount = builder.getMinAmount();
this.volume = builder.getVolume(); this.makerFeePct = builder.getMakerFeePct();
this.minVolume = builder.getMinVolume(); this.takerFeePct = builder.getTakerFeePct();
this.makerFee = builder.getMakerFee(); this.penaltyFeePct = builder.getPenaltyFeePct();
this.buyerSecurityDepositPct = builder.getBuyerSecurityDepositPct(); this.buyerSecurityDepositPct = builder.getBuyerSecurityDepositPct();
this.sellerSecurityDepositPct = builder.getSellerSecurityDepositPct(); this.sellerSecurityDepositPct = builder.getSellerSecurityDepositPct();
this.volume = builder.getVolume();
this.minVolume = builder.getMinVolume();
this.triggerPrice = builder.getTriggerPrice(); this.triggerPrice = builder.getTriggerPrice();
this.paymentAccountId = builder.getPaymentAccountId(); this.paymentAccountId = builder.getPaymentAccountId();
this.paymentMethodId = builder.getPaymentMethodId(); this.paymentMethodId = builder.getPaymentMethodId();
@ -155,11 +158,14 @@ public class OfferInfo implements Payload {
.withMarketPriceMarginPct(marketPriceMarginAsPctLiteral) .withMarketPriceMarginPct(marketPriceMarginAsPctLiteral)
.withAmount(offer.getAmount().longValueExact()) .withAmount(offer.getAmount().longValueExact())
.withMinAmount(offer.getMinAmount().longValueExact()) .withMinAmount(offer.getMinAmount().longValueExact())
.withVolume(roundedVolume) .withMakerFeePct(offer.getMakerFeePct())
.withMinVolume(roundedMinVolume) .withTakerFeePct(offer.getTakerFeePct())
.withMakerFee(offer.getMakerFee().longValueExact()) .withPenaltyFeePct(offer.getPenaltyFeePct())
.withSellerSecurityDepositPct(offer.getSellerSecurityDepositPct())
.withBuyerSecurityDepositPct(offer.getBuyerSecurityDepositPct()) .withBuyerSecurityDepositPct(offer.getBuyerSecurityDepositPct())
.withSellerSecurityDepositPct(offer.getSellerSecurityDepositPct()) .withSellerSecurityDepositPct(offer.getSellerSecurityDepositPct())
.withVolume(roundedVolume)
.withMinVolume(roundedMinVolume)
.withPaymentAccountId(offer.getMakerPaymentAccountId()) .withPaymentAccountId(offer.getMakerPaymentAccountId())
.withPaymentMethodId(offer.getPaymentMethod().getId()) .withPaymentMethodId(offer.getPaymentMethod().getId())
.withPaymentMethodShortName(offer.getPaymentMethod().getShortName()) .withPaymentMethodShortName(offer.getPaymentMethod().getShortName())
@ -190,7 +196,9 @@ public class OfferInfo implements Payload {
.setMinAmount(minAmount) .setMinAmount(minAmount)
.setVolume(volume) .setVolume(volume)
.setMinVolume(minVolume) .setMinVolume(minVolume)
.setMakerFee(makerFee) .setMakerFeePct(makerFeePct)
.setTakerFeePct(takerFeePct)
.setPenaltyFeePct(penaltyFeePct)
.setBuyerSecurityDepositPct(buyerSecurityDepositPct) .setBuyerSecurityDepositPct(buyerSecurityDepositPct)
.setSellerSecurityDepositPct(sellerSecurityDepositPct) .setSellerSecurityDepositPct(sellerSecurityDepositPct)
.setTriggerPrice(triggerPrice == null ? "0" : triggerPrice) .setTriggerPrice(triggerPrice == null ? "0" : triggerPrice)
@ -225,7 +233,9 @@ public class OfferInfo implements Payload {
.withMinAmount(proto.getMinAmount()) .withMinAmount(proto.getMinAmount())
.withVolume(proto.getVolume()) .withVolume(proto.getVolume())
.withMinVolume(proto.getMinVolume()) .withMinVolume(proto.getMinVolume())
.withMakerFee(proto.getMakerFee()) .withMakerFeePct(proto.getMakerFeePct())
.withTakerFeePct(proto.getTakerFeePct())
.withPenaltyFeePct(proto.getPenaltyFeePct())
.withBuyerSecurityDepositPct(proto.getBuyerSecurityDepositPct()) .withBuyerSecurityDepositPct(proto.getBuyerSecurityDepositPct())
.withSellerSecurityDepositPct(proto.getSellerSecurityDepositPct()) .withSellerSecurityDepositPct(proto.getSellerSecurityDepositPct())
.withTriggerPrice(proto.getTriggerPrice()) .withTriggerPrice(proto.getTriggerPrice())

View file

@ -64,11 +64,12 @@ public class TradeInfo implements Payload {
private final String shortId; private final String shortId;
private final long date; private final long date;
private final String role; private final String role;
private final long takerFee;
private final String makerDepositTxId; private final String makerDepositTxId;
private final String takerDepositTxId; private final String takerDepositTxId;
private final String payoutTxId; private final String payoutTxId;
private final long amount; private final long amount;
private final long makerFee;
private final long takerFee;
private final long buyerSecurityDeposit; private final long buyerSecurityDeposit;
private final long sellerSecurityDeposit; private final long sellerSecurityDeposit;
private final long buyerDepositTxFee; private final long buyerDepositTxFee;
@ -104,11 +105,12 @@ public class TradeInfo implements Payload {
this.shortId = builder.getShortId(); this.shortId = builder.getShortId();
this.date = builder.getDate(); this.date = builder.getDate();
this.role = builder.getRole(); this.role = builder.getRole();
this.takerFee = builder.getTakerFee();
this.makerDepositTxId = builder.getMakerDepositTxId(); this.makerDepositTxId = builder.getMakerDepositTxId();
this.takerDepositTxId = builder.getTakerDepositTxId(); this.takerDepositTxId = builder.getTakerDepositTxId();
this.payoutTxId = builder.getPayoutTxId(); this.payoutTxId = builder.getPayoutTxId();
this.amount = builder.getAmount(); this.amount = builder.getAmount();
this.makerFee = builder.getMakerFee();
this.takerFee = builder.getTakerFee();
this.buyerSecurityDeposit = builder.getBuyerSecurityDeposit(); this.buyerSecurityDeposit = builder.getBuyerSecurityDeposit();
this.sellerSecurityDeposit = builder.getSellerSecurityDeposit(); this.sellerSecurityDeposit = builder.getSellerSecurityDeposit();
this.buyerDepositTxFee = builder.getBuyerDepositTxFee(); this.buyerDepositTxFee = builder.getBuyerDepositTxFee();
@ -166,11 +168,12 @@ public class TradeInfo implements Payload {
.withShortId(trade.getShortId()) .withShortId(trade.getShortId())
.withDate(trade.getDate().getTime()) .withDate(trade.getDate().getTime())
.withRole(role == null ? "" : role) .withRole(role == null ? "" : role)
.withTakerFee(trade.getTakerFee().longValueExact())
.withMakerDepositTxId(trade.getMaker().getDepositTxHash()) .withMakerDepositTxId(trade.getMaker().getDepositTxHash())
.withTakerDepositTxId(trade.getTaker().getDepositTxHash()) .withTakerDepositTxId(trade.getTaker().getDepositTxHash())
.withPayoutTxId(trade.getPayoutTxId()) .withPayoutTxId(trade.getPayoutTxId())
.withAmount(trade.getAmount().longValueExact()) .withAmount(trade.getAmount().longValueExact())
.withMakerFee(trade.getMakerFee().longValueExact())
.withTakerFee(trade.getTakerFee().longValueExact())
.withBuyerSecurityDeposit(trade.getBuyer().getSecurityDeposit() == null ? -1 : trade.getBuyer().getSecurityDeposit().longValueExact()) .withBuyerSecurityDeposit(trade.getBuyer().getSecurityDeposit() == null ? -1 : trade.getBuyer().getSecurityDeposit().longValueExact())
.withSellerSecurityDeposit(trade.getSeller().getSecurityDeposit() == null ? -1 : trade.getSeller().getSecurityDeposit().longValueExact()) .withSellerSecurityDeposit(trade.getSeller().getSecurityDeposit() == null ? -1 : trade.getSeller().getSecurityDeposit().longValueExact())
.withBuyerDepositTxFee(trade.getBuyer().getDepositTxFee() == null ? -1 : trade.getBuyer().getDepositTxFee().longValueExact()) .withBuyerDepositTxFee(trade.getBuyer().getDepositTxFee() == null ? -1 : trade.getBuyer().getDepositTxFee().longValueExact())
@ -216,11 +219,12 @@ public class TradeInfo implements Payload {
.setShortId(shortId) .setShortId(shortId)
.setDate(date) .setDate(date)
.setRole(role) .setRole(role)
.setTakerFee(takerFee)
.setMakerDepositTxId(makerDepositTxId == null ? "" : makerDepositTxId) .setMakerDepositTxId(makerDepositTxId == null ? "" : makerDepositTxId)
.setTakerDepositTxId(takerDepositTxId == null ? "" : takerDepositTxId) .setTakerDepositTxId(takerDepositTxId == null ? "" : takerDepositTxId)
.setPayoutTxId(payoutTxId == null ? "" : payoutTxId) .setPayoutTxId(payoutTxId == null ? "" : payoutTxId)
.setAmount(amount) .setAmount(amount)
.setMakerFee(makerFee)
.setTakerFee(takerFee)
.setBuyerSecurityDeposit(buyerSecurityDeposit) .setBuyerSecurityDeposit(buyerSecurityDeposit)
.setSellerSecurityDeposit(sellerSecurityDeposit) .setSellerSecurityDeposit(sellerSecurityDeposit)
.setBuyerDepositTxFee(buyerDepositTxFee) .setBuyerDepositTxFee(buyerDepositTxFee)
@ -259,11 +263,12 @@ public class TradeInfo implements Payload {
.withShortId(proto.getShortId()) .withShortId(proto.getShortId())
.withDate(proto.getDate()) .withDate(proto.getDate())
.withRole(proto.getRole()) .withRole(proto.getRole())
.withTakerFee(proto.getTakerFee())
.withMakerDepositTxId(proto.getMakerDepositTxId()) .withMakerDepositTxId(proto.getMakerDepositTxId())
.withTakerDepositTxId(proto.getTakerDepositTxId()) .withTakerDepositTxId(proto.getTakerDepositTxId())
.withPayoutTxId(proto.getPayoutTxId()) .withPayoutTxId(proto.getPayoutTxId())
.withAmount(proto.getAmount()) .withAmount(proto.getAmount())
.withMakerFee(proto.getMakerFee())
.withTakerFee(proto.getTakerFee())
.withBuyerSecurityDeposit(proto.getBuyerSecurityDeposit()) .withBuyerSecurityDeposit(proto.getBuyerSecurityDeposit())
.withSellerSecurityDeposit(proto.getSellerSecurityDeposit()) .withSellerSecurityDeposit(proto.getSellerSecurityDeposit())
.withBuyerDepositTxFee(proto.getBuyerDepositTxFee()) .withBuyerDepositTxFee(proto.getBuyerDepositTxFee())
@ -302,11 +307,12 @@ public class TradeInfo implements Payload {
", shortId='" + shortId + '\'' + "\n" + ", shortId='" + shortId + '\'' + "\n" +
", date='" + date + '\'' + "\n" + ", date='" + date + '\'' + "\n" +
", role='" + role + '\'' + "\n" + ", role='" + role + '\'' + "\n" +
", takerFee='" + takerFee + '\'' + "\n" +
", makerDepositTxId='" + makerDepositTxId + '\'' + "\n" + ", makerDepositTxId='" + makerDepositTxId + '\'' + "\n" +
", takerDepositTxId='" + takerDepositTxId + '\'' + "\n" + ", takerDepositTxId='" + takerDepositTxId + '\'' + "\n" +
", payoutTxId='" + payoutTxId + '\'' + "\n" + ", payoutTxId='" + payoutTxId + '\'' + "\n" +
", amount='" + amount + '\'' + "\n" + ", amount='" + amount + '\'' + "\n" +
", makerFee='" + makerFee + '\'' + "\n" +
", takerFee='" + takerFee + '\'' + "\n" +
", buyerSecurityDeposit='" + buyerSecurityDeposit + '\'' + "\n" + ", buyerSecurityDeposit='" + buyerSecurityDeposit + '\'' + "\n" +
", sellerSecurityDeposit='" + sellerSecurityDeposit + '\'' + "\n" + ", sellerSecurityDeposit='" + sellerSecurityDeposit + '\'' + "\n" +
", buyerDepositTxFee='" + buyerDepositTxFee + '\'' + "\n" + ", buyerDepositTxFee='" + buyerDepositTxFee + '\'' + "\n" +

View file

@ -38,7 +38,9 @@ public final class OfferInfoBuilder {
private long minAmount; private long minAmount;
private String volume; private String volume;
private String minVolume; private String minVolume;
private long makerFee; private double makerFeePct;
private double takerFeePct;
private double penaltyFeePct;
private double buyerSecurityDepositPct; private double buyerSecurityDepositPct;
private double sellerSecurityDepositPct; private double sellerSecurityDepositPct;
private String triggerPrice; private String triggerPrice;
@ -97,18 +99,18 @@ public final class OfferInfoBuilder {
return this; return this;
} }
public OfferInfoBuilder withVolume(String volume) { public OfferInfoBuilder withMakerFeePct(double makerFeePct) {
this.volume = volume; this.makerFeePct = makerFeePct;
return this; return this;
} }
public OfferInfoBuilder withMinVolume(String minVolume) { public OfferInfoBuilder withTakerFeePct(double takerFeePct) {
this.minVolume = minVolume; this.takerFeePct = takerFeePct;
return this; return this;
} }
public OfferInfoBuilder withMakerFee(long makerFee) { public OfferInfoBuilder withPenaltyFeePct(double penaltyFeePct) {
this.makerFee = makerFee; this.penaltyFeePct = penaltyFeePct;
return this; return this;
} }
@ -122,6 +124,16 @@ public final class OfferInfoBuilder {
return this; return this;
} }
public OfferInfoBuilder withVolume(String volume) {
this.volume = volume;
return this;
}
public OfferInfoBuilder withMinVolume(String minVolume) {
this.minVolume = minVolume;
return this;
}
public OfferInfoBuilder withTriggerPrice(String triggerPrice) { public OfferInfoBuilder withTriggerPrice(String triggerPrice) {
this.triggerPrice = triggerPrice; this.triggerPrice = triggerPrice;
return this; return this;

View file

@ -38,6 +38,7 @@ public final class TradeInfoV1Builder {
private String role; private String role;
private boolean isCurrencyForTakerFeeBtc; private boolean isCurrencyForTakerFeeBtc;
private long totalTxFee; private long totalTxFee;
private long makerFee;
private long takerFee; private long takerFee;
private long buyerSecurityDeposit; private long buyerSecurityDeposit;
private long sellerSecurityDeposit; private long sellerSecurityDeposit;
@ -108,6 +109,11 @@ public final class TradeInfoV1Builder {
return this; return this;
} }
public TradeInfoV1Builder withMakerFee(long makerFee) {
this.makerFee = makerFee;
return this;
}
public TradeInfoV1Builder withTakerFee(long takerFee) { public TradeInfoV1Builder withTakerFee(long takerFee) {
this.takerFee = takerFee; this.takerFee = takerFee;
return this; return this;

View file

@ -160,7 +160,6 @@ public class CreateOfferService {
List<String> acceptedCountryCodes = PaymentAccountUtil.getAcceptedCountryCodes(paymentAccount); List<String> acceptedCountryCodes = PaymentAccountUtil.getAcceptedCountryCodes(paymentAccount);
String bankId = PaymentAccountUtil.getBankId(paymentAccount); String bankId = PaymentAccountUtil.getBankId(paymentAccount);
List<String> acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount); List<String> acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount);
BigInteger makerFee = HavenoUtils.getMakerFee(amount);
long maxTradePeriod = paymentAccount.getMaxTradePeriod(); long maxTradePeriod = paymentAccount.getMaxTradePeriod();
// reserved for future use cases // reserved for future use cases
@ -178,8 +177,7 @@ public class CreateOfferService {
offerUtil.validateOfferData( offerUtil.validateOfferData(
securityDepositAsDouble, securityDepositAsDouble,
paymentAccount, paymentAccount,
currencyCode, currencyCode);
makerFee);
OfferPayload offerPayload = new OfferPayload(offerId, OfferPayload offerPayload = new OfferPayload(offerId,
creationTime, creationTime,
@ -191,6 +189,11 @@ public class CreateOfferService {
useMarketBasedPriceValue, useMarketBasedPriceValue,
amountAsLong, amountAsLong,
minAmountAsLong, minAmountAsLong,
HavenoUtils.MAKER_FEE_PCT,
HavenoUtils.TAKER_FEE_PCT,
HavenoUtils.PENALTY_FEE_PCT,
securityDepositAsDouble,
securityDepositAsDouble,
baseCurrencyCode, baseCurrencyCode,
counterCurrencyCode, counterCurrencyCode,
paymentAccount.getPaymentMethod().getId(), paymentAccount.getPaymentMethod().getId(),
@ -201,9 +204,6 @@ public class CreateOfferService {
acceptedBanks, acceptedBanks,
Version.VERSION, Version.VERSION,
xmrWalletService.getWallet().getHeight(), xmrWalletService.getWallet().getHeight(),
makerFee.longValueExact(),
securityDepositAsDouble,
securityDepositAsDouble,
maxTradeLimit, maxTradeLimit,
maxTradePeriod, maxTradePeriod,
useAutoClose, useAutoClose,

View file

@ -39,6 +39,7 @@ import haveno.core.offer.availability.OfferAvailabilityProtocol;
import haveno.core.payment.payload.PaymentMethod; import haveno.core.payment.payload.PaymentMethod;
import haveno.core.provider.price.MarketPrice; import haveno.core.provider.price.MarketPrice;
import haveno.core.provider.price.PriceFeedService; import haveno.core.provider.price.PriceFeedService;
import haveno.core.trade.HavenoUtils;
import haveno.core.util.VolumeUtil; import haveno.core.util.VolumeUtil;
import haveno.network.p2p.NodeAddress; import haveno.network.p2p.NodeAddress;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
@ -285,12 +286,12 @@ public class Offer implements NetworkPayload, PersistablePayload {
public BigInteger getReserveAmount() { public BigInteger getReserveAmount() {
BigInteger reserveAmount = getDirection() == OfferDirection.BUY ? getMaxBuyerSecurityDeposit() : getMaxSellerSecurityDeposit(); BigInteger reserveAmount = getDirection() == OfferDirection.BUY ? getMaxBuyerSecurityDeposit() : getMaxSellerSecurityDeposit();
if (getDirection() == OfferDirection.SELL) reserveAmount = reserveAmount.add(getAmount()); if (getDirection() == OfferDirection.SELL) reserveAmount = reserveAmount.add(getAmount());
reserveAmount = reserveAmount.add(getMakerFee()); reserveAmount = reserveAmount.add(getMaxMakerFee());
return reserveAmount; return reserveAmount;
} }
public BigInteger getMakerFee() { public BigInteger getMaxMakerFee() {
return BigInteger.valueOf(offerPayload.getMakerFee()); return offerPayload.getMaxMakerFee();
} }
public BigInteger getMaxBuyerSecurityDeposit() { public BigInteger getMaxBuyerSecurityDeposit() {
@ -301,6 +302,26 @@ public class Offer implements NetworkPayload, PersistablePayload {
return offerPayload.getMaxSellerSecurityDeposit(); return offerPayload.getMaxSellerSecurityDeposit();
} }
public double getMakerFeePct() {
return offerPayload.getMakerFeePct();
}
public double getTakerFeePct() {
return offerPayload.getTakerFeePct();
}
public double getPenaltyFeePct() {
return offerPayload.getPenaltyFeePct();
}
public BigInteger getMakerFee(BigInteger tradeAmount) {
return HavenoUtils.multiply(tradeAmount, getMakerFeePct());
}
public BigInteger getTakerFee(BigInteger tradeAmount) {
return HavenoUtils.multiply(tradeAmount, getTakerFeePct());
}
public double getBuyerSecurityDepositPct() { public double getBuyerSecurityDepositPct() {
return offerPayload.getBuyerSecurityDepositPct(); return offerPayload.getBuyerSecurityDepositPct();
} }

View file

@ -132,7 +132,9 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
@Nullable @Nullable
private final List<String> acceptedBankIds; private final List<String> acceptedBankIds;
private final long blockHeightAtOfferCreation; private final long blockHeightAtOfferCreation;
private final long makerFee; private final double makerFeePct;
private final double takerFeePct;
private final double penaltyFeePct;
private final double buyerSecurityDepositPct; private final double buyerSecurityDepositPct;
private final double sellerSecurityDepositPct; private final double sellerSecurityDepositPct;
private final long maxTradeLimit; private final long maxTradeLimit;
@ -168,6 +170,11 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
boolean useMarketBasedPrice, boolean useMarketBasedPrice,
long amount, long amount,
long minAmount, long minAmount,
double makerFeePct,
double takerFeePct,
double penaltyFeePct,
double buyerSecurityDepositPct,
double sellerSecurityDepositPct,
String baseCurrencyCode, String baseCurrencyCode,
String counterCurrencyCode, String counterCurrencyCode,
String paymentMethodId, String paymentMethodId,
@ -178,9 +185,6 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
@Nullable List<String> acceptedBankIds, @Nullable List<String> acceptedBankIds,
String versionNr, String versionNr,
long blockHeightAtOfferCreation, long blockHeightAtOfferCreation,
long makerFee,
double buyerSecurityDepositPct,
double sellerSecurityDepositPct,
long maxTradeLimit, long maxTradeLimit,
long maxTradePeriod, long maxTradePeriod,
boolean useAutoClose, boolean useAutoClose,
@ -204,6 +208,11 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
this.price = price; this.price = price;
this.amount = amount; this.amount = amount;
this.minAmount = minAmount; this.minAmount = minAmount;
this.makerFeePct = makerFeePct;
this.takerFeePct = takerFeePct;
this.penaltyFeePct = penaltyFeePct;
this.buyerSecurityDepositPct = buyerSecurityDepositPct;
this.sellerSecurityDepositPct = sellerSecurityDepositPct;
this.paymentMethodId = paymentMethodId; this.paymentMethodId = paymentMethodId;
this.makerPaymentAccountId = makerPaymentAccountId; this.makerPaymentAccountId = makerPaymentAccountId;
this.extraDataMap = extraDataMap; this.extraDataMap = extraDataMap;
@ -219,9 +228,6 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
this.bankId = bankId; this.bankId = bankId;
this.acceptedBankIds = acceptedBankIds; this.acceptedBankIds = acceptedBankIds;
this.blockHeightAtOfferCreation = blockHeightAtOfferCreation; this.blockHeightAtOfferCreation = blockHeightAtOfferCreation;
this.makerFee = makerFee;
this.buyerSecurityDepositPct = buyerSecurityDepositPct;
this.sellerSecurityDepositPct = sellerSecurityDepositPct;
this.maxTradeLimit = maxTradeLimit; this.maxTradeLimit = maxTradeLimit;
this.maxTradePeriod = maxTradePeriod; this.maxTradePeriod = maxTradePeriod;
this.useAutoClose = useAutoClose; this.useAutoClose = useAutoClose;
@ -253,6 +259,11 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
false, false,
amount, amount,
minAmount, minAmount,
makerFeePct,
takerFeePct,
penaltyFeePct,
buyerSecurityDepositPct,
sellerSecurityDepositPct,
baseCurrencyCode, baseCurrencyCode,
counterCurrencyCode, counterCurrencyCode,
paymentMethodId, paymentMethodId,
@ -263,9 +274,6 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
acceptedBankIds, acceptedBankIds,
versionNr, versionNr,
blockHeightAtOfferCreation, blockHeightAtOfferCreation,
makerFee,
buyerSecurityDepositPct,
sellerSecurityDepositPct,
maxTradeLimit, maxTradeLimit,
maxTradePeriod, maxTradePeriod,
useAutoClose, useAutoClose,
@ -303,6 +311,10 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
return getBaseCurrencyCode().equals("XMR") ? getCounterCurrencyCode() : getBaseCurrencyCode(); return getBaseCurrencyCode().equals("XMR") ? getCounterCurrencyCode() : getBaseCurrencyCode();
} }
public BigInteger getMaxMakerFee() {
return HavenoUtils.multiply(BigInteger.valueOf(getAmount()), getMakerFeePct());
}
public BigInteger getMaxBuyerSecurityDeposit() { public BigInteger getMaxBuyerSecurityDeposit() {
return getBuyerSecurityDepositForTradeAmount(BigInteger.valueOf(getAmount())); return getBuyerSecurityDepositForTradeAmount(BigInteger.valueOf(getAmount()));
} }
@ -312,12 +324,12 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
} }
public BigInteger getBuyerSecurityDepositForTradeAmount(BigInteger tradeAmount) { public BigInteger getBuyerSecurityDepositForTradeAmount(BigInteger tradeAmount) {
BigInteger securityDepositUnadjusted = HavenoUtils.xmrToAtomicUnits(HavenoUtils.atomicUnitsToXmr(tradeAmount) * getBuyerSecurityDepositPct()); BigInteger securityDepositUnadjusted = HavenoUtils.multiply(tradeAmount, getBuyerSecurityDepositPct());
return Restrictions.getMinBuyerSecurityDeposit().max(securityDepositUnadjusted); return Restrictions.getMinBuyerSecurityDeposit().max(securityDepositUnadjusted);
} }
public BigInteger getSellerSecurityDepositForTradeAmount(BigInteger tradeAmount) { public BigInteger getSellerSecurityDepositForTradeAmount(BigInteger tradeAmount) {
BigInteger securityDepositUnadjusted = HavenoUtils.xmrToAtomicUnits(HavenoUtils.atomicUnitsToXmr(tradeAmount) * getSellerSecurityDepositPct()); BigInteger securityDepositUnadjusted = HavenoUtils.multiply(tradeAmount, getSellerSecurityDepositPct());
return Restrictions.getMinSellerSecurityDeposit().max(securityDepositUnadjusted); return Restrictions.getMinSellerSecurityDeposit().max(securityDepositUnadjusted);
} }
@ -337,15 +349,17 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
.setUseMarketBasedPrice(useMarketBasedPrice) .setUseMarketBasedPrice(useMarketBasedPrice)
.setAmount(amount) .setAmount(amount)
.setMinAmount(minAmount) .setMinAmount(minAmount)
.setMakerFeePct(makerFeePct)
.setTakerFeePct(takerFeePct)
.setPenaltyFeePct(penaltyFeePct)
.setBuyerSecurityDepositPct(buyerSecurityDepositPct)
.setSellerSecurityDepositPct(sellerSecurityDepositPct)
.setBaseCurrencyCode(baseCurrencyCode) .setBaseCurrencyCode(baseCurrencyCode)
.setCounterCurrencyCode(counterCurrencyCode) .setCounterCurrencyCode(counterCurrencyCode)
.setPaymentMethodId(paymentMethodId) .setPaymentMethodId(paymentMethodId)
.setMakerPaymentAccountId(makerPaymentAccountId) .setMakerPaymentAccountId(makerPaymentAccountId)
.setVersionNr(versionNr) .setVersionNr(versionNr)
.setBlockHeightAtOfferCreation(blockHeightAtOfferCreation) .setBlockHeightAtOfferCreation(blockHeightAtOfferCreation)
.setMakerFee(makerFee)
.setBuyerSecurityDepositPct(buyerSecurityDepositPct)
.setSellerSecurityDepositPct(sellerSecurityDepositPct)
.setMaxTradeLimit(maxTradeLimit) .setMaxTradeLimit(maxTradeLimit)
.setMaxTradePeriod(maxTradePeriod) .setMaxTradePeriod(maxTradePeriod)
.setUseAutoClose(useAutoClose) .setUseAutoClose(useAutoClose)
@ -387,6 +401,11 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
proto.getUseMarketBasedPrice(), proto.getUseMarketBasedPrice(),
proto.getAmount(), proto.getAmount(),
proto.getMinAmount(), proto.getMinAmount(),
proto.getMakerFeePct(),
proto.getTakerFeePct(),
proto.getPenaltyFeePct(),
proto.getBuyerSecurityDepositPct(),
proto.getSellerSecurityDepositPct(),
proto.getBaseCurrencyCode(), proto.getBaseCurrencyCode(),
proto.getCounterCurrencyCode(), proto.getCounterCurrencyCode(),
proto.getPaymentMethodId(), proto.getPaymentMethodId(),
@ -397,9 +416,6 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
acceptedBankIds, acceptedBankIds,
proto.getVersionNr(), proto.getVersionNr(),
proto.getBlockHeightAtOfferCreation(), proto.getBlockHeightAtOfferCreation(),
proto.getMakerFee(),
proto.getBuyerSecurityDepositPct(),
proto.getSellerSecurityDepositPct(),
proto.getMaxTradeLimit(), proto.getMaxTradeLimit(),
proto.getMaxTradePeriod(), proto.getMaxTradePeriod(),
proto.getUseAutoClose(), proto.getUseAutoClose(),
@ -425,6 +441,11 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
",\r\n price=" + price + ",\r\n price=" + price +
",\r\n amount=" + amount + ",\r\n amount=" + amount +
",\r\n minAmount=" + minAmount + ",\r\n minAmount=" + minAmount +
",\r\n makerFeePct=" + makerFeePct +
",\r\n takerFeePct=" + takerFeePct +
",\r\n penaltyFeePct=" + penaltyFeePct +
",\r\n buyerSecurityDepositPct=" + buyerSecurityDepositPct +
",\r\n sellerSecurityDeposiPct=" + sellerSecurityDepositPct +
",\r\n paymentMethodId='" + paymentMethodId + '\'' + ",\r\n paymentMethodId='" + paymentMethodId + '\'' +
",\r\n makerPaymentAccountId='" + makerPaymentAccountId + '\'' + ",\r\n makerPaymentAccountId='" + makerPaymentAccountId + '\'' +
",\r\n ownerNodeAddress=" + ownerNodeAddress + ",\r\n ownerNodeAddress=" + ownerNodeAddress +
@ -442,9 +463,6 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
",\r\n bankId='" + bankId + '\'' + ",\r\n bankId='" + bankId + '\'' +
",\r\n acceptedBankIds=" + acceptedBankIds + ",\r\n acceptedBankIds=" + acceptedBankIds +
",\r\n blockHeightAtOfferCreation=" + blockHeightAtOfferCreation + ",\r\n blockHeightAtOfferCreation=" + blockHeightAtOfferCreation +
",\r\n makerFee=" + makerFee +
",\r\n buyerSecurityDepositPct=" + buyerSecurityDepositPct +
",\r\n sellerSecurityDeposiPct=" + sellerSecurityDepositPct +
",\r\n maxTradeLimit=" + maxTradeLimit + ",\r\n maxTradeLimit=" + maxTradeLimit +
",\r\n maxTradePeriod=" + maxTradePeriod + ",\r\n maxTradePeriod=" + maxTradePeriod +
",\r\n useAutoClose=" + useAutoClose + ",\r\n useAutoClose=" + useAutoClose +
@ -474,15 +492,17 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
object.add("useMarketBasedPrice", context.serialize(offerPayload.isUseMarketBasedPrice())); object.add("useMarketBasedPrice", context.serialize(offerPayload.isUseMarketBasedPrice()));
object.add("amount", context.serialize(offerPayload.getAmount())); object.add("amount", context.serialize(offerPayload.getAmount()));
object.add("minAmount", context.serialize(offerPayload.getMinAmount())); object.add("minAmount", context.serialize(offerPayload.getMinAmount()));
object.add("makerFeePct", context.serialize(offerPayload.getMakerFeePct()));
object.add("takerFeePct", context.serialize(offerPayload.getTakerFeePct()));
object.add("penaltyFeePct", context.serialize(offerPayload.getPenaltyFeePct()));
object.add("buyerSecurityDepositPct", context.serialize(offerPayload.getBuyerSecurityDepositPct()));
object.add("sellerSecurityDepositPct", context.serialize(offerPayload.getSellerSecurityDepositPct()));
object.add("baseCurrencyCode", context.serialize(offerPayload.getBaseCurrencyCode())); object.add("baseCurrencyCode", context.serialize(offerPayload.getBaseCurrencyCode()));
object.add("counterCurrencyCode", context.serialize(offerPayload.getCounterCurrencyCode())); object.add("counterCurrencyCode", context.serialize(offerPayload.getCounterCurrencyCode()));
object.add("paymentMethodId", context.serialize(offerPayload.getPaymentMethodId())); object.add("paymentMethodId", context.serialize(offerPayload.getPaymentMethodId()));
object.add("makerPaymentAccountId", context.serialize(offerPayload.getMakerPaymentAccountId())); object.add("makerPaymentAccountId", context.serialize(offerPayload.getMakerPaymentAccountId()));
object.add("versionNr", context.serialize(offerPayload.getVersionNr())); object.add("versionNr", context.serialize(offerPayload.getVersionNr()));
object.add("blockHeightAtOfferCreation", context.serialize(offerPayload.getBlockHeightAtOfferCreation())); object.add("blockHeightAtOfferCreation", context.serialize(offerPayload.getBlockHeightAtOfferCreation()));
object.add("makerFee", context.serialize(offerPayload.getMakerFee()));
object.add("buyerSecurityDepositPct", context.serialize(offerPayload.getBuyerSecurityDepositPct()));
object.add("sellerSecurityDepositPct", context.serialize(offerPayload.getSellerSecurityDepositPct()));
object.add("maxTradeLimit", context.serialize(offerPayload.getMaxTradeLimit())); object.add("maxTradeLimit", context.serialize(offerPayload.getMaxTradeLimit()));
object.add("maxTradePeriod", context.serialize(offerPayload.getMaxTradePeriod())); object.add("maxTradePeriod", context.serialize(offerPayload.getMaxTradePeriod()));
object.add("useAutoClose", context.serialize(offerPayload.isUseAutoClose())); object.add("useAutoClose", context.serialize(offerPayload.isUseAutoClose()));

View file

@ -211,9 +211,7 @@ public class OfferUtil {
public void validateOfferData(double buyerSecurityDeposit, public void validateOfferData(double buyerSecurityDeposit,
PaymentAccount paymentAccount, PaymentAccount paymentAccount,
String currencyCode, String currencyCode) {
BigInteger makerFee) {
checkNotNull(makerFee, "makerFee must not be null");
checkNotNull(p2PService.getAddress(), "Address must not be null"); checkNotNull(p2PService.getAddress(), "Address must not be null");
checkArgument(buyerSecurityDeposit <= getMaxBuyerSecurityDepositAsPercent(), checkArgument(buyerSecurityDeposit <= getMaxBuyerSecurityDepositAsPercent(),
"securityDeposit must not exceed " + "securityDeposit must not exceed " +

View file

@ -538,7 +538,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
boolean reserveExactAmount, boolean reserveExactAmount,
TransactionResultHandler resultHandler, TransactionResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler) { ErrorMessageHandler errorMessageHandler) {
checkNotNull(offer.getMakerFee(), "makerFee must not be null");
// create open offer // create open offer
OpenOffer openOffer = new OpenOffer(offer, triggerPrice, reserveExactAmount); OpenOffer openOffer = new OpenOffer(offer, triggerPrice, reserveExactAmount);
@ -1199,9 +1198,24 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
// verify maker's trade fee // verify maker's trade fee
Offer offer = new Offer(request.getOfferPayload()); Offer offer = new Offer(request.getOfferPayload());
BigInteger tradeFee = HavenoUtils.getMakerFee(offer.getAmount()); if (offer.getMakerFeePct() != HavenoUtils.MAKER_FEE_PCT) {
if (!tradeFee.equals(offer.getMakerFee())) { errorMessage = "Wrong maker fee for offer " + request.offerId;
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 taker's trade fee
if (offer.getTakerFeePct() != HavenoUtils.TAKER_FEE_PCT) {
errorMessage = "Wrong taker fee for offer " + request.offerId;
log.info(errorMessage);
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
return;
}
// verify penalty fee
if (offer.getPenaltyFeePct() != HavenoUtils.PENALTY_FEE_PCT) {
errorMessage = "Wrong penalty fee for offer " + request.offerId;
log.info(errorMessage); log.info(errorMessage);
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
return; return;
@ -1216,11 +1230,14 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
} }
// verify maker's reserve tx (double spend, trade fee, trade amount, mining fee) // verify maker's reserve tx (double spend, trade fee, trade amount, mining fee)
BigInteger penaltyFee = HavenoUtils.multiply(offer.getAmount(), HavenoUtils.PENALTY_FEE_PCT);
BigInteger maxTradeFee = HavenoUtils.multiply(offer.getAmount(), HavenoUtils.MAKER_FEE_PCT);
BigInteger sendAmount = offer.getDirection() == OfferDirection.BUY ? BigInteger.ZERO : offer.getAmount(); BigInteger sendAmount = offer.getDirection() == OfferDirection.BUY ? BigInteger.ZERO : offer.getAmount();
BigInteger securityDeposit = offer.getDirection() == OfferDirection.BUY ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit(); BigInteger securityDeposit = offer.getDirection() == OfferDirection.BUY ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit();
Tuple2<MoneroTx, BigInteger> txResult = xmrWalletService.verifyTradeTx( Tuple2<MoneroTx, BigInteger> txResult = xmrWalletService.verifyTradeTx(
offer.getId(), offer.getId(),
tradeFee, penaltyFee,
maxTradeFee,
sendAmount, sendAmount,
securityDeposit, securityDeposit,
request.getPayoutAddress(), request.getPayoutAddress(),
@ -1240,7 +1257,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
signedOfferPayload.getPubKeyRing().hashCode(), // trader id signedOfferPayload.getPubKeyRing().hashCode(), // trader id
signedOfferPayload.getId(), signedOfferPayload.getId(),
offer.getAmount().longValueExact(), offer.getAmount().longValueExact(),
tradeFee.longValueExact(), maxTradeFee.longValueExact(),
request.getReserveTxHash(), request.getReserveTxHash(),
request.getReserveTxHex(), request.getReserveTxHex(),
request.getReserveTxKeyImages(), request.getReserveTxKeyImages(),
@ -1549,6 +1566,11 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
originalOfferPayload.isUseMarketBasedPrice(), originalOfferPayload.isUseMarketBasedPrice(),
originalOfferPayload.getAmount(), originalOfferPayload.getAmount(),
originalOfferPayload.getMinAmount(), originalOfferPayload.getMinAmount(),
originalOfferPayload.getMakerFeePct(),
originalOfferPayload.getTakerFeePct(),
originalOfferPayload.getPenaltyFeePct(),
originalOfferPayload.getBuyerSecurityDepositPct(),
originalOfferPayload.getSellerSecurityDepositPct(),
originalOfferPayload.getBaseCurrencyCode(), originalOfferPayload.getBaseCurrencyCode(),
originalOfferPayload.getCounterCurrencyCode(), originalOfferPayload.getCounterCurrencyCode(),
originalOfferPayload.getPaymentMethodId(), originalOfferPayload.getPaymentMethodId(),
@ -1559,9 +1581,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
originalOfferPayload.getAcceptedBankIds(), originalOfferPayload.getAcceptedBankIds(),
originalOfferPayload.getVersionNr(), originalOfferPayload.getVersionNr(),
originalOfferPayload.getBlockHeightAtOfferCreation(), originalOfferPayload.getBlockHeightAtOfferCreation(),
originalOfferPayload.getMakerFee(),
originalOfferPayload.getBuyerSecurityDepositPct(),
originalOfferPayload.getSellerSecurityDepositPct(),
originalOfferPayload.getMaxTradeLimit(), originalOfferPayload.getMaxTradeLimit(),
originalOfferPayload.getMaxTradePeriod(), originalOfferPayload.getMaxTradePeriod(),
originalOfferPayload.isUseAutoClose(), originalOfferPayload.isUseAutoClose(),

View file

@ -71,7 +71,6 @@ public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
p2PService.getKeyRing().getPubKeyRing(), p2PService.getKeyRing().getPubKeyRing(),
model.getTradeAmount().longValueExact(), model.getTradeAmount().longValueExact(),
price.getValue(), price.getValue(),
HavenoUtils.getTakerFee(model.getTradeAmount()).longValueExact(),
user.getAccountId(), user.getAccountId(),
paymentAccountId, paymentAccountId,
paymentMethodId, paymentMethodId,

View file

@ -22,6 +22,7 @@ import haveno.common.taskrunner.TaskRunner;
import haveno.core.offer.Offer; import haveno.core.offer.Offer;
import haveno.core.offer.OfferDirection; import haveno.core.offer.OfferDirection;
import haveno.core.offer.placeoffer.PlaceOfferModel; import haveno.core.offer.placeoffer.PlaceOfferModel;
import haveno.core.trade.HavenoUtils;
import haveno.core.xmr.model.XmrAddressEntry; import haveno.core.xmr.model.XmrAddressEntry;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import monero.daemon.model.MoneroOutput; import monero.daemon.model.MoneroOutput;
@ -50,13 +51,14 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
model.getXmrWalletService().getConnectionService().verifyConnection(); model.getXmrWalletService().getConnectionService().verifyConnection();
// create reserve tx // create reserve tx
BigInteger makerFee = offer.getMakerFee(); BigInteger penaltyFee = HavenoUtils.multiply(offer.getAmount(), offer.getPenaltyFeePct());
BigInteger makerFee = offer.getMaxMakerFee();
BigInteger sendAmount = offer.getDirection() == OfferDirection.BUY ? BigInteger.ZERO : offer.getAmount(); BigInteger sendAmount = offer.getDirection() == OfferDirection.BUY ? BigInteger.ZERO : offer.getAmount();
BigInteger securityDeposit = offer.getDirection() == OfferDirection.BUY ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit(); BigInteger securityDeposit = offer.getDirection() == OfferDirection.BUY ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit();
String returnAddress = model.getXmrWalletService().getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(); String returnAddress = model.getXmrWalletService().getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString();
XmrAddressEntry fundingEntry = model.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.OFFER_FUNDING).orElse(null); XmrAddressEntry fundingEntry = model.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.OFFER_FUNDING).orElse(null);
Integer preferredSubaddressIndex = fundingEntry == null ? null : fundingEntry.getSubaddressIndex(); Integer preferredSubaddressIndex = fundingEntry == null ? null : fundingEntry.getSubaddressIndex();
MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(makerFee, sendAmount, securityDeposit, returnAddress, model.getOpenOffer().isReserveExactAmount(), preferredSubaddressIndex); MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, makerFee, sendAmount, securityDeposit, returnAddress, model.getOpenOffer().isReserveExactAmount(), preferredSubaddressIndex);
// check for error in case creating reserve tx exceeded timeout // TODO: better way? // check for error in case creating reserve tx exceeded timeout // TODO: better way?
if (!model.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).isPresent()) { if (!model.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).isPresent()) {

View file

@ -44,11 +44,12 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
// Coins // Coins
checkBINotNullOrZero(offer.getAmount(), "Amount"); checkBINotNullOrZero(offer.getAmount(), "Amount");
checkBINotNullOrZero(offer.getMinAmount(), "MinAmount"); checkBINotNullOrZero(offer.getMinAmount(), "MinAmount");
checkBINotNullOrZero(offer.getMakerFee(), "MakerFee");
//checkCoinNotNullOrZero(offer.getTxFee(), "txFee"); // TODO: remove from data model //checkCoinNotNullOrZero(offer.getTxFee(), "txFee"); // TODO: remove from data model
checkBINotNullOrZero(offer.getMaxTradeLimit(), "MaxTradeLimit"); checkBINotNullOrZero(offer.getMaxTradeLimit(), "MaxTradeLimit");
if (offer.getBuyerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Buyer security deposit must be positive but was " + offer.getBuyerSecurityDepositPct()); if (offer.getMakerFeePct() < 0) throw new IllegalArgumentException("Maker fee must be >= 0% but was " + offer.getMakerFeePct());
if (offer.getSellerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Seller security deposit must be positive but was " + offer.getSellerSecurityDepositPct()); if (offer.getTakerFeePct() < 0) throw new IllegalArgumentException("Taker fee must be >= 0% but was " + offer.getTakerFeePct());
if (offer.getBuyerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Buyer security deposit percent must be positive but was " + offer.getBuyerSecurityDepositPct());
if (offer.getSellerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Seller security deposit percent must be positive but was " + offer.getSellerSecurityDepositPct());
// We remove those checks to be more flexible with future changes. // We remove those checks to be more flexible with future changes.
/*checkArgument(offer.getMakerFee().value >= FeeService.getMinMakerFee(offer.isCurrencyForMakerFeeBtc()).value, /*checkArgument(offer.getMakerFee().value >= FeeService.getMinMakerFee(offer.isCurrencyForMakerFeeBtc()).value,
@ -84,7 +85,6 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
checkNotNull(offer.getPubKeyRing(), "pubKeyRing is null"); checkNotNull(offer.getPubKeyRing(), "pubKeyRing is null");
checkNotNull(offer.getMinAmount(), "MinAmount is null"); checkNotNull(offer.getMinAmount(), "MinAmount is null");
checkNotNull(offer.getPrice(), "Price is null"); checkNotNull(offer.getPrice(), "Price is null");
checkNotNull(offer.getMakerFee(), "MakerFee is null");
checkNotNull(offer.getVersionNr(), "VersionNr is null"); checkNotNull(offer.getVersionNr(), "VersionNr is null");
checkArgument(offer.getMaxTradePeriod() > 0, checkArgument(offer.getMaxTradePeriod() > 0,
"maxTradePeriod must be positive. maxTradePeriod=" + offer.getMaxTradePeriod()); "maxTradePeriod must be positive. maxTradePeriod=" + offer.getMaxTradePeriod());
@ -136,6 +136,6 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
} }
public static void checkTradeId(String tradeId, TradeMessage tradeMessage) { public static void checkTradeId(String tradeId, TradeMessage tradeMessage) {
checkArgument(tradeId.equals(tradeMessage.getTradeId())); checkArgument(tradeId.equals(tradeMessage.getOfferId()));
} }
} }

View file

@ -100,7 +100,7 @@ public class TakeOfferModel implements Model {
this.securityDeposit = offer.getDirection() == SELL this.securityDeposit = offer.getDirection() == SELL
? offer.getOfferPayload().getBuyerSecurityDepositForTradeAmount(amount) ? offer.getOfferPayload().getBuyerSecurityDepositForTradeAmount(amount)
: offer.getOfferPayload().getSellerSecurityDepositForTradeAmount(amount); : offer.getOfferPayload().getSellerSecurityDepositForTradeAmount(amount);
this.takerFee = HavenoUtils.getTakerFee(amount); this.takerFee = HavenoUtils.multiply(amount, offer.getTakerFeePct());
calculateVolume(); calculateVolume();
calculateTotalToPay(); calculateTotalToPay();

View file

@ -36,7 +36,6 @@ public class ArbitratorTrade extends Trade {
public ArbitratorTrade(Offer offer, public ArbitratorTrade(Offer offer,
BigInteger tradeAmount, BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice, long tradePrice,
XmrWalletService xmrWalletService, XmrWalletService xmrWalletService,
ProcessModel processModel, ProcessModel processModel,
@ -44,7 +43,7 @@ public class ArbitratorTrade extends Trade {
NodeAddress makerNodeAddress, NodeAddress makerNodeAddress,
NodeAddress takerNodeAddress, NodeAddress takerNodeAddress,
NodeAddress arbitratorNodeAddress) { NodeAddress arbitratorNodeAddress) {
super(offer, tradeAmount, takerFee, tradePrice, xmrWalletService, processModel, uid, makerNodeAddress, takerNodeAddress, arbitratorNodeAddress); super(offer, tradeAmount, tradePrice, xmrWalletService, processModel, uid, makerNodeAddress, takerNodeAddress, arbitratorNodeAddress);
} }
@Override @Override
@ -76,7 +75,6 @@ public class ArbitratorTrade extends Trade {
return fromProto(new ArbitratorTrade( return fromProto(new ArbitratorTrade(
Offer.fromProto(proto.getOffer()), Offer.fromProto(proto.getOffer()),
BigInteger.valueOf(proto.getAmount()), BigInteger.valueOf(proto.getAmount()),
BigInteger.valueOf(proto.getTakerFee()),
proto.getPrice(), proto.getPrice(),
xmrWalletService, xmrWalletService,
processModel, processModel,

View file

@ -37,7 +37,6 @@ public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade {
public BuyerAsMakerTrade(Offer offer, public BuyerAsMakerTrade(Offer offer,
BigInteger tradeAmount, BigInteger tradeAmount,
BigInteger takeOfferFee,
long tradePrice, long tradePrice,
XmrWalletService xmrWalletService, XmrWalletService xmrWalletService,
ProcessModel processModel, ProcessModel processModel,
@ -47,7 +46,6 @@ public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade {
NodeAddress arbitratorNodeAddress) { NodeAddress arbitratorNodeAddress) {
super(offer, super(offer,
tradeAmount, tradeAmount,
takeOfferFee,
tradePrice, tradePrice,
xmrWalletService, xmrWalletService,
processModel, processModel,
@ -81,7 +79,6 @@ public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade {
BuyerAsMakerTrade trade = new BuyerAsMakerTrade( BuyerAsMakerTrade trade = new BuyerAsMakerTrade(
Offer.fromProto(proto.getOffer()), Offer.fromProto(proto.getOffer()),
BigInteger.valueOf(proto.getAmount()), BigInteger.valueOf(proto.getAmount()),
BigInteger.valueOf(proto.getTakerFee()),
proto.getPrice(), proto.getPrice(),
xmrWalletService, xmrWalletService,
processModel, processModel,

View file

@ -38,7 +38,6 @@ public final class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade {
public BuyerAsTakerTrade(Offer offer, public BuyerAsTakerTrade(Offer offer,
BigInteger tradeAmount, BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice, long tradePrice,
XmrWalletService xmrWalletService, XmrWalletService xmrWalletService,
ProcessModel processModel, ProcessModel processModel,
@ -48,7 +47,6 @@ public final class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade {
@Nullable NodeAddress arbitratorNodeAddress) { @Nullable NodeAddress arbitratorNodeAddress) {
super(offer, super(offer,
tradeAmount, tradeAmount,
takerFee,
tradePrice, tradePrice,
xmrWalletService, xmrWalletService,
processModel, processModel,
@ -83,7 +81,6 @@ public final class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade {
return fromProto(new BuyerAsTakerTrade( return fromProto(new BuyerAsTakerTrade(
Offer.fromProto(proto.getOffer()), Offer.fromProto(proto.getOffer()),
BigInteger.valueOf(proto.getAmount()), BigInteger.valueOf(proto.getAmount()),
BigInteger.valueOf(proto.getTakerFee()),
proto.getPrice(), proto.getPrice(),
xmrWalletService, xmrWalletService,
processModel, processModel,

View file

@ -32,7 +32,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
public abstract class BuyerTrade extends Trade { public abstract class BuyerTrade extends Trade {
BuyerTrade(Offer offer, BuyerTrade(Offer offer,
BigInteger tradeAmount, BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice, long tradePrice,
XmrWalletService xmrWalletService, XmrWalletService xmrWalletService,
ProcessModel processModel, ProcessModel processModel,
@ -42,7 +41,6 @@ public abstract class BuyerTrade extends Trade {
@Nullable NodeAddress arbitratorNodeAddress) { @Nullable NodeAddress arbitratorNodeAddress) {
super(offer, super(offer,
tradeAmount, tradeAmount,
takerFee,
tradePrice, tradePrice,
xmrWalletService, xmrWalletService,
processModel, processModel,

View file

@ -106,7 +106,7 @@ public class CleanupMailboxMessages {
} }
private boolean isMyMessage(TradeMessage message, Trade trade) { private boolean isMyMessage(TradeMessage message, Trade trade) {
return message.getTradeId().equals(trade.getId()); return message.getOfferId().equals(trade.getId());
} }
private boolean isMyMessage(AckMessage ackMessage, Trade trade) { private boolean isMyMessage(AckMessage ackMessage, Trade trade) {

View file

@ -105,7 +105,7 @@ public class CleanupMailboxMessagesService {
} }
private boolean isMyMessage(TradeMessage message, Trade trade) { private boolean isMyMessage(TradeMessage message, Trade trade) {
return message.getTradeId().equals(trade.getId()); return message.getOfferId().equals(trade.getId());
} }
private boolean isMyMessage(AckMessage ackMessage, Trade trade) { private boolean isMyMessage(AckMessage ackMessage, Trade trade) {

View file

@ -34,11 +34,9 @@ import haveno.core.trade.messages.InitTradeRequest;
import haveno.core.trade.messages.PaymentReceivedMessage; import haveno.core.trade.messages.PaymentReceivedMessage;
import haveno.core.trade.messages.PaymentSentMessage; import haveno.core.trade.messages.PaymentSentMessage;
import haveno.core.util.JsonUtil; import haveno.core.util.JsonUtil;
import haveno.core.util.ParsingUtils;
import haveno.network.p2p.NodeAddress; import haveno.network.p2p.NodeAddress;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.URI; import java.net.URI;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.text.DecimalFormat; import java.text.DecimalFormat;
@ -48,7 +46,6 @@ import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import javax.annotation.Nullable;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import monero.common.MoneroRpcConnection; import monero.common.MoneroRpcConnection;
import monero.wallet.model.MoneroDestination; import monero.wallet.model.MoneroDestination;
@ -62,12 +59,17 @@ import org.bitcoinj.core.Coin;
@Slf4j @Slf4j
public class HavenoUtils { public class HavenoUtils {
// configurable // configure release date
private static final String RELEASE_DATE = "01-03-2024 00:00:00"; // optionally set to release date of the network in format dd-mm-yyyy to impose temporary limits, etc. e.g. "01-03-2024 00:00:00" private static final String RELEASE_DATE = "01-03-2024 00:00:00"; // optionally set to release date of the network in format dd-mm-yyyy to impose temporary limits, etc. e.g. "01-03-2024 00:00:00"
public static final int RELEASE_LIMIT_DAYS = 60; // number of days to limit sell offers to max buy limit for new accounts public static final int RELEASE_LIMIT_DAYS = 60; // number of days to limit sell offers to max buy limit for new accounts
public static final int WARN_ON_OFFER_EXCEEDS_UNSIGNED_BUY_LIMIT_DAYS = 182; // number of days to warn if sell offer exceeds unsigned buy limit public static final int WARN_ON_OFFER_EXCEEDS_UNSIGNED_BUY_LIMIT_DAYS = 182; // number of days to warn if sell offer exceeds unsigned buy limit
public static final int ARBITRATOR_ACK_TIMEOUT_SECONDS = 15; public static final int ARBITRATOR_ACK_TIMEOUT_SECONDS = 15;
// configure fees
public static final double MAKER_FEE_PCT = 0.0015; // 0.15%
public static final double TAKER_FEE_PCT = 0.0075; // 0.75%
public static final double PENALTY_FEE_PCT = 0.02; // 2%
// non-configurable // non-configurable
public static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = DecimalFormatSymbols.getInstance(Locale.US); // use the US locale as a base for all DecimalFormats (commas should be omitted from number strings) public static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = DecimalFormatSymbols.getInstance(Locale.US); // use the US locale as a base for all DecimalFormats (commas should be omitted from number strings)
public static int XMR_SMALLEST_UNIT_EXPONENT = 12; public static int XMR_SMALLEST_UNIT_EXPONENT = 12;
@ -125,7 +127,7 @@ public class HavenoUtils {
} }
public static long atomicUnitsToCentineros(long atomicUnits) { public static long atomicUnitsToCentineros(long atomicUnits) {
return atomicUnits / CENTINEROS_AU_MULTIPLIER; return atomicUnitsToCentineros(BigInteger.valueOf(atomicUnits));
} }
public static long atomicUnitsToCentineros(BigInteger atomicUnits) { public static long atomicUnitsToCentineros(BigInteger atomicUnits) {
@ -149,7 +151,7 @@ public class HavenoUtils {
} }
public static BigInteger xmrToAtomicUnits(double xmr) { public static BigInteger xmrToAtomicUnits(double xmr) {
return BigDecimal.valueOf(xmr).setScale(8, RoundingMode.DOWN).multiply(new BigDecimal(XMR_AU_MULTIPLIER)).toBigInteger(); return new BigDecimal(xmr).multiply(new BigDecimal(XMR_AU_MULTIPLIER)).toBigInteger();
} }
public static long xmrToCentineros(double xmr) { public static long xmrToCentineros(double xmr) {
@ -161,7 +163,11 @@ public class HavenoUtils {
} }
public static double divide(BigInteger auDividend, BigInteger auDivisor) { public static double divide(BigInteger auDividend, BigInteger auDivisor) {
return (double) atomicUnitsToCentineros(auDividend) / (double) atomicUnitsToCentineros(auDivisor); return atomicUnitsToXmr(auDividend) / atomicUnitsToXmr(auDivisor);
}
public static BigInteger multiply(BigInteger amount1, double amount2) {
return amount1 == null ? null : new BigDecimal(amount1).multiply(BigDecimal.valueOf(amount2)).toBigInteger();
} }
// ------------------------- FORMAT UTILS --------------------------------- // ------------------------- FORMAT UTILS ---------------------------------
@ -221,56 +227,12 @@ public class HavenoUtils {
public static BigInteger parseXmr(String input) { public static BigInteger parseXmr(String input) {
if (input == null || input.length() == 0) return BigInteger.ZERO; if (input == null || input.length() == 0) return BigInteger.ZERO;
try { try {
return xmrToAtomicUnits(new BigDecimal(ParsingUtils.parseNumberStringToDouble(input)).doubleValue()); return new BigDecimal(input).multiply(new BigDecimal(XMR_AU_MULTIPLIER)).toBigInteger();
} catch (Exception e) { } catch (Exception e) {
return BigInteger.ZERO; return BigInteger.ZERO;
} }
} }
// ------------------------------ FEE UTILS -------------------------------
@Nullable
public static BigInteger getMakerFee(@Nullable BigInteger amount) {
if (amount != null) {
BigInteger feePerXmr = getFeePerXmr(HavenoUtils.getMakerFeePerXmr(), amount);
return feePerXmr.max(HavenoUtils.getMinMakerFee());
} else {
return null;
}
}
@Nullable
public static BigInteger getTakerFee(@Nullable BigInteger amount) {
if (amount != null) {
BigInteger feePerXmr = HavenoUtils.getFeePerXmr(HavenoUtils.getTakerFeePerXmr(), amount);
return feePerXmr.max(HavenoUtils.getMinTakerFee());
} else {
return null;
}
}
private static BigInteger getMakerFeePerXmr() {
return HavenoUtils.xmrToAtomicUnits(0.0015);
}
public static BigInteger getMinMakerFee() {
return HavenoUtils.xmrToAtomicUnits(0.00005);
}
private static BigInteger getTakerFeePerXmr() {
return HavenoUtils.xmrToAtomicUnits(0.0075);
}
public static BigInteger getMinTakerFee() {
return HavenoUtils.xmrToAtomicUnits(0.00005);
}
public static BigInteger getFeePerXmr(BigInteger feePerXmr, BigInteger amount) {
BigDecimal feePerXmrAsDecimal = feePerXmr == null ? BigDecimal.valueOf(0) : new BigDecimal(feePerXmr);
BigDecimal amountMultiplier = BigDecimal.valueOf(divide(amount == null ? BigInteger.ZERO : amount, HavenoUtils.xmrToAtomicUnits(1.0)));
return feePerXmrAsDecimal.multiply(amountMultiplier).toBigInteger();
}
// ------------------------ SIGNING AND VERIFYING ------------------------- // ------------------------ SIGNING AND VERIFYING -------------------------
public static byte[] sign(KeyRing keyRing, String message) { public static byte[] sign(KeyRing keyRing, String message) {
@ -351,12 +313,11 @@ public class HavenoUtils {
// re-create trade request with signed fields // re-create trade request with signed fields
InitTradeRequest signedRequest = new InitTradeRequest( InitTradeRequest signedRequest = new InitTradeRequest(
request.getTradeId(), request.getOfferId(),
request.getSenderNodeAddress(), request.getSenderNodeAddress(),
request.getPubKeyRing(), request.getPubKeyRing(),
request.getTradeAmount(), request.getTradeAmount(),
request.getTradePrice(), request.getTradePrice(),
request.getTradeFee(),
request.getAccountId(), request.getAccountId(),
request.getPaymentAccountId(), request.getPaymentAccountId(),
request.getPaymentMethodId(), request.getPaymentMethodId(),
@ -380,7 +341,7 @@ public class HavenoUtils {
// verify maker signature // verify maker signature
boolean isSignatureValid = isSignatureValid(makerPubKeyRing, tradeRequestAsJson, signature); boolean isSignatureValid = isSignatureValid(makerPubKeyRing, tradeRequestAsJson, signature);
if (!isSignatureValid) { if (!isSignatureValid) {
log.warn("Invalid maker signature for trade request: " + request.getTradeId() + " from " + request.getSenderNodeAddress().getAddressForDisplay()); log.warn("Invalid maker signature for trade request: " + request.getOfferId() + " from " + request.getSenderNodeAddress().getAddressForDisplay());
log.warn("Trade request as json: " + tradeRequestAsJson); log.warn("Trade request as json: " + tradeRequestAsJson);
log.warn("Maker pub key ring: " + (makerPubKeyRing == null ? null : "...")); log.warn("Maker pub key ring: " + (makerPubKeyRing == null ? null : "..."));
log.warn("Maker signature: " + (signature == null ? null : Utilities.bytesAsHexString(signature))); log.warn("Maker signature: " + (signature == null ? null : Utilities.bytesAsHexString(signature)));
@ -413,7 +374,7 @@ public class HavenoUtils {
} }
// verify trade id // verify trade id
if (!trade.getId().equals(message.getTradeId())) throw new IllegalArgumentException("The " + message.getClass().getSimpleName() + " has the wrong trade id, expected " + trade.getId() + " but was " + message.getTradeId()); if (!trade.getId().equals(message.getOfferId())) throw new IllegalArgumentException("The " + message.getClass().getSimpleName() + " has the wrong trade id, expected " + trade.getId() + " but was " + message.getOfferId());
} }
/** /**
@ -441,7 +402,7 @@ public class HavenoUtils {
} }
// verify trade id // verify trade id
if (!trade.getId().equals(message.getTradeId())) throw new IllegalArgumentException("The " + message.getClass().getSimpleName() + " has the wrong trade id, expected " + trade.getId() + " but was " + message.getTradeId()); if (!trade.getId().equals(message.getOfferId())) throw new IllegalArgumentException("The " + message.getClass().getSimpleName() + " has the wrong trade id, expected " + trade.getId() + " but was " + message.getOfferId());
// verify buyer signature of payment sent message // verify buyer signature of payment sent message
if (message.getPaymentSentMessage() != null) verifyPaymentSentMessage(trade, message.getPaymentSentMessage()); if (message.getPaymentSentMessage() != null) verifyPaymentSentMessage(trade, message.getPaymentSentMessage());

View file

@ -38,7 +38,6 @@ public final class SellerAsMakerTrade extends SellerTrade implements MakerTrade
public SellerAsMakerTrade(Offer offer, public SellerAsMakerTrade(Offer offer,
BigInteger tradeAmount, BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice, long tradePrice,
XmrWalletService xmrWalletService, XmrWalletService xmrWalletService,
ProcessModel processModel, ProcessModel processModel,
@ -48,7 +47,6 @@ public final class SellerAsMakerTrade extends SellerTrade implements MakerTrade
@Nullable NodeAddress arbitratorNodeAddress) { @Nullable NodeAddress arbitratorNodeAddress) {
super(offer, super(offer,
tradeAmount, tradeAmount,
takerFee,
tradePrice, tradePrice,
xmrWalletService, xmrWalletService,
processModel, processModel,
@ -83,7 +81,6 @@ public final class SellerAsMakerTrade extends SellerTrade implements MakerTrade
SellerAsMakerTrade trade = new SellerAsMakerTrade( SellerAsMakerTrade trade = new SellerAsMakerTrade(
Offer.fromProto(proto.getOffer()), Offer.fromProto(proto.getOffer()),
BigInteger.valueOf(proto.getAmount()), BigInteger.valueOf(proto.getAmount()),
BigInteger.valueOf(proto.getTakerFee()),
proto.getPrice(), proto.getPrice(),
xmrWalletService, xmrWalletService,
processModel, processModel,

View file

@ -38,7 +38,6 @@ public final class SellerAsTakerTrade extends SellerTrade implements TakerTrade
public SellerAsTakerTrade(Offer offer, public SellerAsTakerTrade(Offer offer,
BigInteger tradeAmount, BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice, long tradePrice,
XmrWalletService xmrWalletService, XmrWalletService xmrWalletService,
ProcessModel processModel, ProcessModel processModel,
@ -48,7 +47,6 @@ public final class SellerAsTakerTrade extends SellerTrade implements TakerTrade
@Nullable NodeAddress arbitratorNodeAddress) { @Nullable NodeAddress arbitratorNodeAddress) {
super(offer, super(offer,
tradeAmount, tradeAmount,
takerFee,
tradePrice, tradePrice,
xmrWalletService, xmrWalletService,
processModel, processModel,
@ -83,7 +81,6 @@ public final class SellerAsTakerTrade extends SellerTrade implements TakerTrade
return fromProto(new SellerAsTakerTrade( return fromProto(new SellerAsTakerTrade(
Offer.fromProto(proto.getOffer()), Offer.fromProto(proto.getOffer()),
BigInteger.valueOf(proto.getAmount()), BigInteger.valueOf(proto.getAmount()),
BigInteger.valueOf(proto.getTakerFee()),
proto.getPrice(), proto.getPrice(),
xmrWalletService, xmrWalletService,
processModel, processModel,

View file

@ -30,7 +30,6 @@ import java.math.BigInteger;
public abstract class SellerTrade extends Trade { public abstract class SellerTrade extends Trade {
SellerTrade(Offer offer, SellerTrade(Offer offer,
BigInteger tradeAmount, BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice, long tradePrice,
XmrWalletService xmrWalletService, XmrWalletService xmrWalletService,
ProcessModel processModel, ProcessModel processModel,
@ -40,7 +39,6 @@ public abstract class SellerTrade extends Trade {
@Nullable NodeAddress arbitratorNodeAddress) { @Nullable NodeAddress arbitratorNodeAddress) {
super(offer, super(offer,
tradeAmount, tradeAmount,
takerFee,
tradePrice, tradePrice,
xmrWalletService, xmrWalletService,
processModel, processModel,

View file

@ -65,7 +65,7 @@ public interface Tradable extends PersistablePayload {
} }
default Optional<BigInteger> getOptionalMakerFee() { default Optional<BigInteger> getOptionalMakerFee() {
return asTradeModel().map(Trade::getOffer).map(Offer::getMakerFee).or(() -> Optional.ofNullable(getOffer().getMakerFee())); return asTradeModel().map(Trade::getMakerFee);
} }
default Optional<NodeAddress> getOptionalTradePeerNodeAddress() { default Optional<NodeAddress> getOptionalTradePeerNodeAddress() {

View file

@ -345,7 +345,6 @@ public abstract class Trade implements Tradable, Model {
private final ProcessModel processModel; private final ProcessModel processModel;
@Getter @Getter
private final Offer offer; private final Offer offer;
private final long takerFee;
// Added in 1.5.1 // Added in 1.5.1
@Getter @Getter
@ -490,7 +489,6 @@ public abstract class Trade implements Tradable, Model {
// maker // maker
protected Trade(Offer offer, protected Trade(Offer offer,
BigInteger tradeAmount, BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice, long tradePrice,
XmrWalletService xmrWalletService, XmrWalletService xmrWalletService,
ProcessModel processModel, ProcessModel processModel,
@ -500,7 +498,6 @@ public abstract class Trade implements Tradable, Model {
@Nullable NodeAddress arbitratorNodeAddress) { @Nullable NodeAddress arbitratorNodeAddress) {
this.offer = offer; this.offer = offer;
this.amount = tradeAmount.longValueExact(); this.amount = tradeAmount.longValueExact();
this.takerFee = takerFee.longValueExact();
this.price = tradePrice; this.price = tradePrice;
this.xmrWalletService = xmrWalletService; this.xmrWalletService = xmrWalletService;
this.xmrConnectionService = xmrWalletService.getConnectionService(); this.xmrConnectionService = xmrWalletService.getConnectionService();
@ -523,7 +520,6 @@ public abstract class Trade implements Tradable, Model {
protected Trade(Offer offer, protected Trade(Offer offer,
BigInteger tradeAmount, BigInteger tradeAmount,
BigInteger txFee, BigInteger txFee,
BigInteger takerFee,
long tradePrice, long tradePrice,
@Nullable NodeAddress mediatorNodeAddress, // TODO (woodser): remove mediator, refund agent from trade @Nullable NodeAddress mediatorNodeAddress, // TODO (woodser): remove mediator, refund agent from trade
@Nullable NodeAddress refundAgentNodeAddress, @Nullable NodeAddress refundAgentNodeAddress,
@ -536,7 +532,6 @@ public abstract class Trade implements Tradable, Model {
this(offer, this(offer,
tradeAmount, tradeAmount,
takerFee,
tradePrice, tradePrice,
xmrWalletService, xmrWalletService,
processModel, processModel,
@ -552,7 +547,6 @@ public abstract class Trade implements Tradable, Model {
protected Trade(Offer offer, protected Trade(Offer offer,
BigInteger tradeAmount, BigInteger tradeAmount,
Coin txFee, Coin txFee,
BigInteger takerFee,
long tradePrice, long tradePrice,
NodeAddress makerNodeAddress, NodeAddress makerNodeAddress,
NodeAddress takerNodeAddress, NodeAddress takerNodeAddress,
@ -563,7 +557,6 @@ public abstract class Trade implements Tradable, Model {
this(offer, this(offer,
tradeAmount, tradeAmount,
takerFee,
tradePrice, tradePrice,
xmrWalletService, xmrWalletService,
processModel, processModel,
@ -926,7 +919,6 @@ public abstract class Trade implements Tradable, Model {
private void forceCloseWallet() { private void forceCloseWallet() {
if (wallet != null) { if (wallet != null) {
log.warn("Force closing wallet for {} {}", getClass().getSimpleName(), getId());
xmrWalletService.forceCloseWallet(wallet, wallet.getPath()); xmrWalletService.forceCloseWallet(wallet, wallet.getPath());
wallet = null; wallet = null;
} }
@ -960,7 +952,7 @@ public abstract class Trade implements Tradable, Model {
throw new IllegalStateException("Refusing to delete wallet for " + getClass().getSimpleName() + " " + getId() + " because it has a balance"); throw new IllegalStateException("Refusing to delete wallet for " + getClass().getSimpleName() + " " + getId() + " because it has a balance");
} }
// force close wallet // force close wallet without warning
forceCloseWallet(); forceCloseWallet();
// delete wallet // delete wallet
@ -1138,7 +1130,7 @@ public abstract class Trade implements Tradable, Model {
// verify fee is within tolerance by recreating payout tx // verify fee is within tolerance by recreating payout tx
// TODO (monero-project): creating tx will require exchanging updated multisig hex if message needs reprocessed. provide weight with describe_transfer so fee can be estimated? // TODO (monero-project): creating tx will require exchanging updated multisig hex if message needs reprocessed. provide weight with describe_transfer so fee can be estimated?
MoneroTxWallet feeEstimateTx = createPayoutTx();; MoneroTxWallet feeEstimateTx = createPayoutTx();
BigInteger feeEstimate = feeEstimateTx.getFee(); BigInteger feeEstimate = feeEstimateTx.getFee();
double feeDiff = payoutTx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); // TODO: use BigDecimal? double feeDiff = payoutTx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); // TODO: use BigDecimal?
if (feeDiff > XmrWalletService.MINER_FEE_TOLERANCE) throw new IllegalArgumentException("Miner fee is not within " + (XmrWalletService.MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + payoutTx.getFee()); if (feeDiff > XmrWalletService.MINER_FEE_TOLERANCE) throw new IllegalArgumentException("Miner fee is not within " + (XmrWalletService.MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + payoutTx.getFee());
@ -1912,11 +1904,11 @@ public abstract class Trade implements Tradable, Model {
} }
public BigInteger getMakerFee() { public BigInteger getMakerFee() {
return offer.getMakerFee(); return offer.getMakerFee(getAmount());
} }
public BigInteger getTakerFee() { public BigInteger getTakerFee() {
return BigInteger.valueOf(takerFee); return offer.getTakerFee(getAmount());
} }
public BigInteger getBuyerSecurityDepositBeforeMiningFee() { public BigInteger getBuyerSecurityDepositBeforeMiningFee() {
@ -2308,7 +2300,6 @@ public abstract class Trade implements Tradable, Model {
public Message toProtoMessage() { public Message toProtoMessage() {
protobuf.Trade.Builder builder = protobuf.Trade.newBuilder() protobuf.Trade.Builder builder = protobuf.Trade.newBuilder()
.setOffer(offer.toProtoMessage()) .setOffer(offer.toProtoMessage())
.setTakerFee(takerFee)
.setTakeOfferDate(takeOfferDate) .setTakeOfferDate(takeOfferDate)
.setProcessModel(processModel.toProtoMessage()) .setProcessModel(processModel.toProtoMessage())
.setAmount(amount) .setAmount(amount)
@ -2371,7 +2362,6 @@ public abstract class Trade implements Tradable, Model {
public String toString() { public String toString() {
return "Trade{" + return "Trade{" +
"\n offer=" + offer + "\n offer=" + offer +
",\n takerFee=" + takerFee +
",\n totalTxFee=" + getTotalTxFee() + ",\n totalTxFee=" + getTotalTxFee() +
",\n takeOfferDate=" + takeOfferDate + ",\n takeOfferDate=" + takeOfferDate +
",\n processModel=" + processModel + ",\n processModel=" + processModel +
@ -2389,7 +2379,6 @@ public abstract class Trade implements Tradable, Model {
",\n counterCurrencyTxId='" + counterCurrencyTxId + '\'' + ",\n counterCurrencyTxId='" + counterCurrencyTxId + '\'' +
",\n counterCurrencyExtraData='" + counterCurrencyExtraData + '\'' + ",\n counterCurrencyExtraData='" + counterCurrencyExtraData + '\'' +
",\n chatMessages=" + chatMessages + ",\n chatMessages=" + chatMessages +
",\n takerFee=" + takerFee +
",\n xmrWalletService=" + xmrWalletService + ",\n xmrWalletService=" + xmrWalletService +
",\n stateProperty=" + stateProperty + ",\n stateProperty=" + stateProperty +
",\n statePhaseProperty=" + phaseProperty + ",\n statePhaseProperty=" + phaseProperty +

View file

@ -283,7 +283,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
public void onDirectMessage(DecryptedMessageWithPubKey message, NodeAddress peer) { public void onDirectMessage(DecryptedMessageWithPubKey message, NodeAddress peer) {
NetworkEnvelope networkEnvelope = message.getNetworkEnvelope(); NetworkEnvelope networkEnvelope = message.getNetworkEnvelope();
if (!(networkEnvelope instanceof TradeMessage)) return; if (!(networkEnvelope instanceof TradeMessage)) return;
String tradeId = ((TradeMessage) networkEnvelope).getTradeId(); String tradeId = ((TradeMessage) networkEnvelope).getOfferId();
ThreadUtils.execute(() -> { ThreadUtils.execute(() -> {
if (networkEnvelope instanceof InitTradeRequest) { if (networkEnvelope instanceof InitTradeRequest) {
handleInitTradeRequest((InitTradeRequest) networkEnvelope, peer); handleInitTradeRequest((InitTradeRequest) networkEnvelope, peer);
@ -532,10 +532,10 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
} }
private void handleInitTradeRequest(InitTradeRequest request, NodeAddress sender) { private void handleInitTradeRequest(InitTradeRequest request, NodeAddress sender) {
log.info("Received InitTradeRequest from {} with tradeId {} and uid {}", sender, request.getTradeId(), request.getUid()); log.info("Received InitTradeRequest from {} with tradeId {} and uid {}", sender, request.getOfferId(), request.getUid());
try { try {
Validator.nonEmptyStringOf(request.getTradeId()); Validator.nonEmptyStringOf(request.getOfferId());
} catch (Throwable t) { } catch (Throwable t) {
log.warn("Invalid InitTradeRequest message " + request.toString()); log.warn("Invalid InitTradeRequest message " + request.toString());
return; return;
@ -549,19 +549,19 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
Arbitrator thisArbitrator = user.getRegisteredArbitrator(); Arbitrator thisArbitrator = user.getRegisteredArbitrator();
NodeAddress thisAddress = p2PService.getNetworkNode().getNodeAddress(); NodeAddress thisAddress = p2PService.getNetworkNode().getNodeAddress();
if (thisArbitrator == null || !thisArbitrator.getNodeAddress().equals(thisAddress)) { if (thisArbitrator == null || !thisArbitrator.getNodeAddress().equals(thisAddress)) {
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because we are not an arbitrator", sender, request.getTradeId()); log.warn("Ignoring InitTradeRequest from {} with tradeId {} because we are not an arbitrator", sender, request.getOfferId());
return; return;
} }
// get offer associated with trade // get offer associated with trade
Offer offer = null; Offer offer = null;
for (Offer anOffer : offerBookService.getOffers()) { for (Offer anOffer : offerBookService.getOffers()) {
if (anOffer.getId().equals(request.getTradeId())) { if (anOffer.getId().equals(request.getOfferId())) {
offer = anOffer; offer = anOffer;
} }
} }
if (offer == null) { if (offer == null) {
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because offer is not on the books", sender, request.getTradeId()); log.warn("Ignoring InitTradeRequest from {} with tradeId {} because offer is not on the books", sender, request.getOfferId());
return; return;
} }
@ -571,7 +571,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// verify maker is offer owner // verify maker is offer owner
// TODO (woodser): maker address might change if they disconnect and reconnect, should allow maker address to differ if pubKeyRing is same? // TODO (woodser): maker address might change if they disconnect and reconnect, should allow maker address to differ if pubKeyRing is same?
if (!offer.getOwnerNodeAddress().equals(request.getMakerNodeAddress())) { if (!offer.getOwnerNodeAddress().equals(request.getMakerNodeAddress())) {
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because maker is not offer owner", sender, request.getTradeId()); log.warn("Ignoring InitTradeRequest from {} with tradeId {} because maker is not offer owner", sender, request.getOfferId());
return; return;
} }
@ -585,7 +585,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
if (!sender.equals(request.getMakerNodeAddress())) { if (!sender.equals(request.getMakerNodeAddress())) {
// send nack if trade already taken // send nack if trade already taken
String errMsg = "Trade is already taken, tradeId=" + request.getTradeId(); String errMsg = "Trade is already taken, tradeId=" + request.getOfferId();
log.warn(errMsg); log.warn(errMsg);
sendAckMessage(sender, request.getPubKeyRing(), request, false, errMsg); sendAckMessage(sender, request.getPubKeyRing(), request, false, errMsg);
return; return;
@ -594,17 +594,13 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// verify request is from taker // verify request is from taker
if (!sender.equals(request.getTakerNodeAddress())) { if (!sender.equals(request.getTakerNodeAddress())) {
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because request must be from taker when trade is not initialized", sender, request.getTradeId()); log.warn("Ignoring InitTradeRequest from {} with tradeId {} because request must be from taker when trade is not initialized", sender, request.getOfferId());
return; return;
} }
// get expected taker fee
BigInteger takerFee = HavenoUtils.getTakerFee(BigInteger.valueOf(request.getTradeAmount()));
// create arbitrator trade // create arbitrator trade
trade = new ArbitratorTrade(offer, trade = new ArbitratorTrade(offer,
BigInteger.valueOf(request.getTradeAmount()), BigInteger.valueOf(request.getTradeAmount()),
takerFee,
offer.getOfferPayload().getPrice(), offer.getOfferPayload().getPrice(),
xmrWalletService, xmrWalletService,
getNewProcessModel(offer), getNewProcessModel(offer),
@ -614,7 +610,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
request.getArbitratorNodeAddress()); request.getArbitratorNodeAddress());
// set reserve tx hash if available // set reserve tx hash if available
Optional<SignedOffer> signedOfferOptional = openOfferManager.getSignedOfferById(request.getTradeId()); Optional<SignedOffer> signedOfferOptional = openOfferManager.getSignedOfferById(request.getOfferId());
if (signedOfferOptional.isPresent()) { if (signedOfferOptional.isPresent()) {
SignedOffer signedOffer = signedOfferOptional.get(); SignedOffer signedOffer = signedOfferOptional.get();
trade.getMaker().setReserveTxHash(signedOffer.getReserveTxHash()); trade.getMaker().setReserveTxHash(signedOffer.getReserveTxHash());
@ -637,7 +633,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// handle request as maker // handle request as maker
else { else {
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(request.getTradeId()); Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(request.getOfferId());
if (!openOfferOptional.isPresent()) { if (!openOfferOptional.isPresent()) {
return; return;
} }
@ -652,28 +648,24 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// verify request is from arbitrator // verify request is from arbitrator
Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(sender); Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(sender);
if (arbitrator == null) { if (arbitrator == null) {
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because request is not from accepted arbitrator", sender, request.getTradeId()); log.warn("Ignoring InitTradeRequest from {} with tradeId {} because request is not from accepted arbitrator", sender, request.getOfferId());
return; return;
} }
Optional<Trade> tradeOptional = getOpenTrade(request.getTradeId()); Optional<Trade> tradeOptional = getOpenTrade(request.getOfferId());
if (tradeOptional.isPresent()) { if (tradeOptional.isPresent()) {
log.warn("Maker trade already exists with id " + request.getTradeId() + ". This should never happen."); log.warn("Maker trade already exists with id " + request.getOfferId() + ". This should never happen.");
return; return;
} }
// reserve open offer // reserve open offer
openOfferManager.reserveOpenOffer(openOffer); openOfferManager.reserveOpenOffer(openOffer);
// get expected taker fee
BigInteger takerFee = HavenoUtils.getTakerFee(BigInteger.valueOf(request.getTradeAmount()));
// initialize trade // initialize trade
Trade trade; Trade trade;
if (offer.isBuyOffer()) if (offer.isBuyOffer())
trade = new BuyerAsMakerTrade(offer, trade = new BuyerAsMakerTrade(offer,
BigInteger.valueOf(request.getTradeAmount()), BigInteger.valueOf(request.getTradeAmount()),
takerFee,
offer.getOfferPayload().getPrice(), offer.getOfferPayload().getPrice(),
xmrWalletService, xmrWalletService,
getNewProcessModel(offer), getNewProcessModel(offer),
@ -684,7 +676,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
else else
trade = new SellerAsMakerTrade(offer, trade = new SellerAsMakerTrade(offer,
BigInteger.valueOf(request.getTradeAmount()), BigInteger.valueOf(request.getTradeAmount()),
takerFee,
offer.getOfferPayload().getPrice(), offer.getOfferPayload().getPrice(),
xmrWalletService, xmrWalletService,
getNewProcessModel(offer), getNewProcessModel(offer),
@ -720,18 +711,18 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
} }
private void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress peer) { private void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress peer) {
log.info("Received {} for trade {} from {} with uid {}", request.getClass().getSimpleName(), request.getTradeId(), peer, request.getUid()); log.info("Received {} for trade {} from {} with uid {}", request.getClass().getSimpleName(), request.getOfferId(), peer, request.getUid());
try { try {
Validator.nonEmptyStringOf(request.getTradeId()); Validator.nonEmptyStringOf(request.getOfferId());
} catch (Throwable t) { } catch (Throwable t) {
log.warn("Invalid InitMultisigRequest " + request.toString()); log.warn("Invalid InitMultisigRequest " + request.toString());
return; return;
} }
Optional<Trade> tradeOptional = getOpenTrade(request.getTradeId()); Optional<Trade> tradeOptional = getOpenTrade(request.getOfferId());
if (!tradeOptional.isPresent()) { if (!tradeOptional.isPresent()) {
log.warn("No trade with id " + request.getTradeId() + " at node " + P2PService.getMyNodeAddress()); log.warn("No trade with id " + request.getOfferId() + " at node " + P2PService.getMyNodeAddress());
return; return;
} }
Trade trade = tradeOptional.get(); Trade trade = tradeOptional.get();
@ -739,18 +730,18 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
} }
private void handleSignContractRequest(SignContractRequest request, NodeAddress peer) { private void handleSignContractRequest(SignContractRequest request, NodeAddress peer) {
log.info("Received {} for trade {} from {} with uid {}", request.getClass().getSimpleName(), request.getTradeId(), peer, request.getUid()); log.info("Received {} for trade {} from {} with uid {}", request.getClass().getSimpleName(), request.getOfferId(), peer, request.getUid());
try { try {
Validator.nonEmptyStringOf(request.getTradeId()); Validator.nonEmptyStringOf(request.getOfferId());
} catch (Throwable t) { } catch (Throwable t) {
log.warn("Invalid SignContractRequest message " + request.toString()); log.warn("Invalid SignContractRequest message " + request.toString());
return; return;
} }
Optional<Trade> tradeOptional = getOpenTrade(request.getTradeId()); Optional<Trade> tradeOptional = getOpenTrade(request.getOfferId());
if (!tradeOptional.isPresent()) { if (!tradeOptional.isPresent()) {
log.warn("No trade with id " + request.getTradeId()); log.warn("No trade with id " + request.getOfferId());
return; return;
} }
Trade trade = tradeOptional.get(); Trade trade = tradeOptional.get();
@ -758,18 +749,18 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
} }
private void handleSignContractResponse(SignContractResponse request, NodeAddress peer) { private void handleSignContractResponse(SignContractResponse request, NodeAddress peer) {
log.info("Received {} for trade {} from {} with uid {}", request.getClass().getSimpleName(), request.getTradeId(), peer, request.getUid()); log.info("Received {} for trade {} from {} with uid {}", request.getClass().getSimpleName(), request.getOfferId(), peer, request.getUid());
try { try {
Validator.nonEmptyStringOf(request.getTradeId()); Validator.nonEmptyStringOf(request.getOfferId());
} catch (Throwable t) { } catch (Throwable t) {
log.warn("Invalid SignContractResponse message " + request.toString()); log.warn("Invalid SignContractResponse message " + request.toString());
return; return;
} }
Optional<Trade> tradeOptional = getOpenTrade(request.getTradeId()); Optional<Trade> tradeOptional = getOpenTrade(request.getOfferId());
if (!tradeOptional.isPresent()) { if (!tradeOptional.isPresent()) {
log.warn("No trade with id " + request.getTradeId()); log.warn("No trade with id " + request.getOfferId());
return; return;
} }
Trade trade = tradeOptional.get(); Trade trade = tradeOptional.get();
@ -777,18 +768,18 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
} }
private void handleDepositRequest(DepositRequest request, NodeAddress peer) { private void handleDepositRequest(DepositRequest request, NodeAddress peer) {
log.info("Received {} for trade {} from {} with uid {}", request.getClass().getSimpleName(), request.getTradeId(), peer, request.getUid()); log.info("Received {} for trade {} from {} with uid {}", request.getClass().getSimpleName(), request.getOfferId(), peer, request.getUid());
try { try {
Validator.nonEmptyStringOf(request.getTradeId()); Validator.nonEmptyStringOf(request.getOfferId());
} catch (Throwable t) { } catch (Throwable t) {
log.warn("Invalid DepositRequest message " + request.toString()); log.warn("Invalid DepositRequest message " + request.toString());
return; return;
} }
Optional<Trade> tradeOptional = getOpenTrade(request.getTradeId()); Optional<Trade> tradeOptional = getOpenTrade(request.getOfferId());
if (!tradeOptional.isPresent()) { if (!tradeOptional.isPresent()) {
log.warn("No trade with id " + request.getTradeId()); log.warn("No trade with id " + request.getOfferId());
return; return;
} }
Trade trade = tradeOptional.get(); Trade trade = tradeOptional.get();
@ -796,18 +787,18 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
} }
private void handleDepositResponse(DepositResponse response, NodeAddress peer) { private void handleDepositResponse(DepositResponse response, NodeAddress peer) {
log.info("Received {} for trade {} from {} with uid {}", response.getClass().getSimpleName(), response.getTradeId(), peer, response.getUid()); log.info("Received {} for trade {} from {} with uid {}", response.getClass().getSimpleName(), response.getOfferId(), peer, response.getUid());
try { try {
Validator.nonEmptyStringOf(response.getTradeId()); Validator.nonEmptyStringOf(response.getOfferId());
} catch (Throwable t) { } catch (Throwable t) {
log.warn("Invalid DepositResponse message " + response.toString()); log.warn("Invalid DepositResponse message " + response.toString());
return; return;
} }
Optional<Trade> tradeOptional = getOpenTrade(response.getTradeId()); Optional<Trade> tradeOptional = getOpenTrade(response.getOfferId());
if (!tradeOptional.isPresent()) { if (!tradeOptional.isPresent()) {
log.warn("No trade with id " + response.getTradeId()); log.warn("No trade with id " + response.getOfferId());
return; return;
} }
Trade trade = tradeOptional.get(); Trade trade = tradeOptional.get();
@ -829,7 +820,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// First we check if offer is still available then we create the trade with the protocol // First we check if offer is still available then we create the trade with the protocol
public void onTakeOffer(BigInteger amount, public void onTakeOffer(BigInteger amount,
BigInteger takerFee,
BigInteger fundsNeededForTrade, BigInteger fundsNeededForTrade,
Offer offer, Offer offer,
String paymentAccountId, String paymentAccountId,
@ -852,7 +842,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
if (offer.isBuyOffer()) { if (offer.isBuyOffer()) {
trade = new SellerAsTakerTrade(offer, trade = new SellerAsTakerTrade(offer,
amount, amount,
takerFee,
model.getTradeRequest().getTradePrice(), model.getTradeRequest().getTradePrice(),
xmrWalletService, xmrWalletService,
getNewProcessModel(offer), getNewProcessModel(offer),
@ -863,7 +852,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
} else { } else {
trade = new BuyerAsTakerTrade(offer, trade = new BuyerAsTakerTrade(offer,
amount, amount,
takerFee,
model.getTradeRequest().getTradePrice(), model.getTradeRequest().getTradePrice(),
xmrWalletService, xmrWalletService,
getNewProcessModel(offer), getNewProcessModel(offer),
@ -1139,7 +1127,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
public void sendAckMessage(NodeAddress peer, PubKeyRing peersPubKeyRing, TradeMessage message, boolean result, @Nullable String errorMessage) { public void sendAckMessage(NodeAddress peer, PubKeyRing peersPubKeyRing, TradeMessage message, boolean result, @Nullable String errorMessage) {
// create ack message // create ack message
String tradeId = message.getTradeId(); String tradeId = message.getOfferId();
String sourceUid = message.getUid(); String sourceUid = message.getUid();
AckMessage ackMessage = new AckMessage(P2PService.getMyNodeAddress(), AckMessage ackMessage = new AckMessage(P2PService.getMyNodeAddress(),
AckMessageSourceType.TRADE_MESSAGE, AckMessageSourceType.TRADE_MESSAGE,

View file

@ -62,7 +62,7 @@ public final class DepositRequest extends TradeMessage implements DirectMessage
@Override @Override
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
protobuf.DepositRequest.Builder builder = protobuf.DepositRequest.newBuilder() protobuf.DepositRequest.Builder builder = protobuf.DepositRequest.newBuilder()
.setTradeId(tradeId) .setTradeId(offerId)
.setUid(uid) .setUid(uid)
.setDepositTxHex(depositTxHex) .setDepositTxHex(depositTxHex)
.setDepositTxKey(depositTxKey); .setDepositTxKey(depositTxKey);

View file

@ -55,7 +55,7 @@ public final class DepositResponse extends TradeMessage implements DirectMessage
@Override @Override
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
protobuf.DepositResponse.Builder builder = protobuf.DepositResponse.newBuilder() protobuf.DepositResponse.Builder builder = protobuf.DepositResponse.newBuilder()
.setTradeId(tradeId) .setTradeId(offerId)
.setUid(uid); .setUid(uid);
builder.setCurrentDate(currentDate); builder.setCurrentDate(currentDate);
builder.setBuyerSecurityDeposit(buyerSecurityDeposit); builder.setBuyerSecurityDeposit(buyerSecurityDeposit);

View file

@ -61,7 +61,7 @@ public final class DepositsConfirmedMessage extends TradeMailboxMessage {
@Override @Override
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
protobuf.DepositsConfirmedMessage.Builder builder = protobuf.DepositsConfirmedMessage.newBuilder() protobuf.DepositsConfirmedMessage.Builder builder = protobuf.DepositsConfirmedMessage.newBuilder()
.setTradeId(tradeId) .setTradeId(offerId)
.setSenderNodeAddress(senderNodeAddress.toProtoMessage()) .setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setPubKeyRing(pubKeyRing.toProtoMessage()) .setPubKeyRing(pubKeyRing.toProtoMessage())
.setUid(uid); .setUid(uid);

View file

@ -59,7 +59,7 @@ public final class InitMultisigRequest extends TradeMessage implements DirectMes
@Override @Override
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
protobuf.InitMultisigRequest.Builder builder = protobuf.InitMultisigRequest.newBuilder() protobuf.InitMultisigRequest.Builder builder = protobuf.InitMultisigRequest.newBuilder()
.setTradeId(tradeId) .setTradeId(offerId)
.setUid(uid); .setUid(uid);
Optional.ofNullable(preparedMultisigHex).ifPresent(e -> builder.setPreparedMultisigHex(preparedMultisigHex)); Optional.ofNullable(preparedMultisigHex).ifPresent(e -> builder.setPreparedMultisigHex(preparedMultisigHex));

View file

@ -36,7 +36,6 @@ public final class InitTradeRequest extends TradeMessage implements DirectMessag
private final NodeAddress senderNodeAddress; private final NodeAddress senderNodeAddress;
private final long tradeAmount; private final long tradeAmount;
private final long tradePrice; private final long tradePrice;
private final long tradeFee;
private final String accountId; private final String accountId;
private final String paymentAccountId; private final String paymentAccountId;
private final String paymentMethodId; private final String paymentMethodId;
@ -63,12 +62,11 @@ public final class InitTradeRequest extends TradeMessage implements DirectMessag
@Nullable @Nullable
private final byte[] makerSignature; private final byte[] makerSignature;
public InitTradeRequest(String tradeId, public InitTradeRequest(String offerId,
NodeAddress senderNodeAddress, NodeAddress senderNodeAddress,
PubKeyRing pubKeyRing, PubKeyRing pubKeyRing,
long tradeAmount, long tradeAmount,
long tradePrice, long tradePrice,
long tradeFee,
String accountId, String accountId,
String paymentAccountId, String paymentAccountId,
String paymentMethodId, String paymentMethodId,
@ -84,12 +82,11 @@ public final class InitTradeRequest extends TradeMessage implements DirectMessag
@Nullable String reserveTxKey, @Nullable String reserveTxKey,
@Nullable String payoutAddress, @Nullable String payoutAddress,
@Nullable byte[] makerSignature) { @Nullable byte[] makerSignature) {
super(messageVersion, tradeId, uid); super(messageVersion, offerId, uid);
this.senderNodeAddress = senderNodeAddress; this.senderNodeAddress = senderNodeAddress;
this.pubKeyRing = pubKeyRing; this.pubKeyRing = pubKeyRing;
this.tradeAmount = tradeAmount; this.tradeAmount = tradeAmount;
this.tradePrice = tradePrice; this.tradePrice = tradePrice;
this.tradeFee = tradeFee;
this.accountId = accountId; this.accountId = accountId;
this.paymentAccountId = paymentAccountId; this.paymentAccountId = paymentAccountId;
this.paymentMethodId = paymentMethodId; this.paymentMethodId = paymentMethodId;
@ -113,13 +110,12 @@ public final class InitTradeRequest extends TradeMessage implements DirectMessag
@Override @Override
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
protobuf.InitTradeRequest.Builder builder = protobuf.InitTradeRequest.newBuilder() protobuf.InitTradeRequest.Builder builder = protobuf.InitTradeRequest.newBuilder()
.setTradeId(tradeId) .setOfferId(offerId)
.setSenderNodeAddress(senderNodeAddress.toProtoMessage()) .setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setTakerNodeAddress(takerNodeAddress.toProtoMessage()) .setTakerNodeAddress(takerNodeAddress.toProtoMessage())
.setMakerNodeAddress(makerNodeAddress.toProtoMessage()) .setMakerNodeAddress(makerNodeAddress.toProtoMessage())
.setTradeAmount(tradeAmount) .setTradeAmount(tradeAmount)
.setTradePrice(tradePrice) .setTradePrice(tradePrice)
.setTradeFee(tradeFee)
.setPubKeyRing(pubKeyRing.toProtoMessage()) .setPubKeyRing(pubKeyRing.toProtoMessage())
.setPaymentAccountId(paymentAccountId) .setPaymentAccountId(paymentAccountId)
.setPaymentMethodId(paymentMethodId) .setPaymentMethodId(paymentMethodId)
@ -141,12 +137,11 @@ public final class InitTradeRequest extends TradeMessage implements DirectMessag
public static InitTradeRequest fromProto(protobuf.InitTradeRequest proto, public static InitTradeRequest fromProto(protobuf.InitTradeRequest proto,
CoreProtoResolver coreProtoResolver, CoreProtoResolver coreProtoResolver,
String messageVersion) { String messageVersion) {
return new InitTradeRequest(proto.getTradeId(), return new InitTradeRequest(proto.getOfferId(),
NodeAddress.fromProto(proto.getSenderNodeAddress()), NodeAddress.fromProto(proto.getSenderNodeAddress()),
PubKeyRing.fromProto(proto.getPubKeyRing()), PubKeyRing.fromProto(proto.getPubKeyRing()),
proto.getTradeAmount(), proto.getTradeAmount(),
proto.getTradePrice(), proto.getTradePrice(),
proto.getTradeFee(),
proto.getAccountId(), proto.getAccountId(),
proto.getPaymentAccountId(), proto.getPaymentAccountId(),
proto.getPaymentMethodId(), proto.getPaymentMethodId(),
@ -168,9 +163,9 @@ public final class InitTradeRequest extends TradeMessage implements DirectMessag
public String toString() { public String toString() {
return "InitTradeRequest{" + return "InitTradeRequest{" +
"\n senderNodeAddress=" + senderNodeAddress + "\n senderNodeAddress=" + senderNodeAddress +
",\n offerId=" + offerId +
",\n tradeAmount=" + tradeAmount + ",\n tradeAmount=" + tradeAmount +
",\n tradePrice=" + tradePrice + ",\n tradePrice=" + tradePrice +
",\n tradeFee=" + tradeFee +
",\n pubKeyRing=" + pubKeyRing + ",\n pubKeyRing=" + pubKeyRing +
",\n accountId='" + accountId + '\'' + ",\n accountId='" + accountId + '\'' +
",\n paymentAccountId=" + paymentAccountId + ",\n paymentAccountId=" + paymentAccountId +

View file

@ -61,7 +61,7 @@ public final class MediatedPayoutTxPublishedMessage extends TradeMailboxMessage
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
return getNetworkEnvelopeBuilder() return getNetworkEnvelopeBuilder()
.setMediatedPayoutTxPublishedMessage(protobuf.MediatedPayoutTxPublishedMessage.newBuilder() .setMediatedPayoutTxPublishedMessage(protobuf.MediatedPayoutTxPublishedMessage.newBuilder()
.setTradeId(tradeId) .setTradeId(offerId)
.setPayoutTx(ByteString.copyFrom(payoutTx)) .setPayoutTx(ByteString.copyFrom(payoutTx))
.setSenderNodeAddress(senderNodeAddress.toProtoMessage()) .setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setUid(uid)) .setUid(uid))

View file

@ -63,7 +63,7 @@ public class MediatedPayoutTxSignatureMessage extends TradeMailboxMessage {
return getNetworkEnvelopeBuilder() return getNetworkEnvelopeBuilder()
.setMediatedPayoutTxSignatureMessage(protobuf.MediatedPayoutTxSignatureMessage.newBuilder() .setMediatedPayoutTxSignatureMessage(protobuf.MediatedPayoutTxSignatureMessage.newBuilder()
.setTxSignature(ByteString.copyFrom(txSignature)) .setTxSignature(ByteString.copyFrom(txSignature))
.setTradeId(tradeId) .setTradeId(offerId)
.setSenderNodeAddress(senderNodeAddress.toProtoMessage()) .setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setUid(uid)) .setUid(uid))
.build(); .build();
@ -79,8 +79,8 @@ public class MediatedPayoutTxSignatureMessage extends TradeMailboxMessage {
} }
@Override @Override
public String getTradeId() { public String getOfferId() {
return tradeId; return offerId;
} }
@ -88,7 +88,7 @@ public class MediatedPayoutTxSignatureMessage extends TradeMailboxMessage {
public String toString() { public String toString() {
return "MediatedPayoutSignatureMessage{" + return "MediatedPayoutSignatureMessage{" +
"\n txSignature=" + Utilities.bytesAsHexString(txSignature) + "\n txSignature=" + Utilities.bytesAsHexString(txSignature) +
",\n tradeId='" + tradeId + '\'' + ",\n tradeId='" + offerId + '\'' +
",\n senderNodeAddress=" + senderNodeAddress + ",\n senderNodeAddress=" + senderNodeAddress +
"\n} " + super.toString(); "\n} " + super.toString();
} }

View file

@ -105,7 +105,7 @@ public final class PaymentReceivedMessage extends TradeMailboxMessage {
@Override @Override
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
protobuf.PaymentReceivedMessage.Builder builder = protobuf.PaymentReceivedMessage.newBuilder() protobuf.PaymentReceivedMessage.Builder builder = protobuf.PaymentReceivedMessage.newBuilder()
.setTradeId(tradeId) .setTradeId(offerId)
.setSenderNodeAddress(senderNodeAddress.toProtoMessage()) .setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setUid(uid) .setUid(uid)
.setDeferPublishPayout(deferPublishPayout); .setDeferPublishPayout(deferPublishPayout);

View file

@ -101,7 +101,7 @@ public final class PaymentSentMessage extends TradeMailboxMessage {
@Override @Override
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
final protobuf.PaymentSentMessage.Builder builder = protobuf.PaymentSentMessage.newBuilder(); final protobuf.PaymentSentMessage.Builder builder = protobuf.PaymentSentMessage.newBuilder();
builder.setTradeId(tradeId) builder.setTradeId(offerId)
.setSenderNodeAddress(senderNodeAddress.toProtoMessage()) .setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setUid(uid); .setUid(uid);
@ -141,7 +141,7 @@ public final class PaymentSentMessage extends TradeMailboxMessage {
@Override @Override
public String toString() { public String toString() {
return "PaymentSentMessage{" + return "PaymentSentMessage{" +
",\n tradeId=" + tradeId + ",\n tradeId=" + offerId +
",\n uid='" + uid + '\'' + ",\n uid='" + uid + '\'' +
",\n senderNodeAddress=" + senderNodeAddress + ",\n senderNodeAddress=" + senderNodeAddress +
",\n counterCurrencyTxId=" + counterCurrencyTxId + ",\n counterCurrencyTxId=" + counterCurrencyTxId +

View file

@ -64,7 +64,7 @@ public final class SignContractRequest extends TradeMessage implements DirectMes
@Override @Override
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
protobuf.SignContractRequest.Builder builder = protobuf.SignContractRequest.newBuilder() protobuf.SignContractRequest.Builder builder = protobuf.SignContractRequest.newBuilder()
.setTradeId(tradeId) .setTradeId(offerId)
.setUid(uid) .setUid(uid)
.setAccountId(accountId) .setAccountId(accountId)
.setPaymentAccountPayloadHash(ByteString.copyFrom(paymentAccountPayloadHash)) .setPaymentAccountPayloadHash(ByteString.copyFrom(paymentAccountPayloadHash))

View file

@ -58,7 +58,7 @@ public final class SignContractResponse extends TradeMessage implements DirectMe
@Override @Override
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
protobuf.SignContractResponse.Builder builder = protobuf.SignContractResponse.newBuilder() protobuf.SignContractResponse.Builder builder = protobuf.SignContractResponse.newBuilder()
.setTradeId(tradeId) .setTradeId(offerId)
.setUid(uid); .setUid(uid);
Optional.ofNullable(contractAsJson).ifPresent(e -> builder.setContractAsJson(contractAsJson)); Optional.ofNullable(contractAsJson).ifPresent(e -> builder.setContractAsJson(contractAsJson));

View file

@ -27,12 +27,12 @@ import lombok.ToString;
@Getter @Getter
@ToString @ToString
public abstract class TradeMessage extends NetworkEnvelope implements UidMessage { public abstract class TradeMessage extends NetworkEnvelope implements UidMessage {
protected final String tradeId; protected final String offerId;
protected final String uid; protected final String uid;
protected TradeMessage(String messageVersion, String tradeId, String uid) { protected TradeMessage(String messageVersion, String offerId, String uid) {
super(messageVersion); super(messageVersion);
this.tradeId = tradeId; this.offerId = offerId;
this.uid = uid; this.uid = uid;
} }
} }

View file

@ -109,7 +109,7 @@ public class ArbitratorProtocol extends DisputeProtocol {
@Override @Override
public void handleDepositResponse(DepositResponse response, NodeAddress sender) { public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
log.warn("Arbitrator ignoring DepositResponse for trade " + response.getTradeId()); log.warn("Arbitrator ignoring DepositResponse for trade " + response.getOfferId());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View file

@ -237,7 +237,7 @@ public class FluentProtocol {
boolean isTradeIdValid = message == null || isTradeIdValid(trade.getId(), message); boolean isTradeIdValid = message == null || isTradeIdValid(trade.getId(), message);
if (!isTradeIdValid) { if (!isTradeIdValid) {
String info = MessageFormat.format("TradeId does not match tradeId in message, TradeId={0}, tradeId in message={1}", String info = MessageFormat.format("TradeId does not match tradeId in message, TradeId={0}, tradeId in message={1}",
trade.getId(), message.getTradeId()); trade.getId(), message.getOfferId());
result = Result.INVALID_TRADE_ID.info(info); result = Result.INVALID_TRADE_ID.info(info);
return result; return result;
} }

View file

@ -121,12 +121,12 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
protected void onTradeMessage(TradeMessage message, NodeAddress peerNodeAddress) { protected void onTradeMessage(TradeMessage message, NodeAddress peerNodeAddress) {
log.info("Received {} as TradeMessage from {} with tradeId {} and uid {}", message.getClass().getSimpleName(), peerNodeAddress, message.getTradeId(), message.getUid()); log.info("Received {} as TradeMessage from {} with tradeId {} and uid {}", message.getClass().getSimpleName(), peerNodeAddress, message.getOfferId(), message.getUid());
ThreadUtils.execute(() -> handle(message, peerNodeAddress), trade.getId()); ThreadUtils.execute(() -> handle(message, peerNodeAddress), trade.getId());
} }
protected void onMailboxMessage(TradeMessage message, NodeAddress peerNodeAddress) { protected void onMailboxMessage(TradeMessage message, NodeAddress peerNodeAddress) {
log.info("Received {} as MailboxMessage from {} with tradeId {} and uid {}", message.getClass().getSimpleName(), peerNodeAddress, message.getTradeId(), message.getUid()); log.info("Received {} as MailboxMessage from {} with tradeId {} and uid {}", message.getClass().getSimpleName(), peerNodeAddress, message.getOfferId(), message.getUid());
ThreadUtils.execute(() -> handle(message, peerNodeAddress), trade.getId()); ThreadUtils.execute(() -> handle(message, peerNodeAddress), trade.getId());
} }
@ -156,7 +156,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
// notify trade listeners // notify trade listeners
// TODO (woodser): better way to register message notifications for trade? // TODO (woodser): better way to register message notifications for trade?
if (((TradeMessage) networkEnvelope).getTradeId().equals(processModel.getOfferId())) { if (((TradeMessage) networkEnvelope).getOfferId().equals(processModel.getOfferId())) {
trade.onVerifiedTradeMessage((TradeMessage) networkEnvelope, peer); trade.onVerifiedTradeMessage((TradeMessage) networkEnvelope, peer);
} }
} else if (networkEnvelope instanceof AckMessage) { } else if (networkEnvelope instanceof AckMessage) {
@ -862,7 +862,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
private boolean isMyMessage(NetworkEnvelope message) { private boolean isMyMessage(NetworkEnvelope message) {
if (message instanceof TradeMessage) { if (message instanceof TradeMessage) {
TradeMessage tradeMessage = (TradeMessage) message; TradeMessage tradeMessage = (TradeMessage) message;
return tradeMessage.getTradeId().equals(trade.getId()); return tradeMessage.getOfferId().equals(trade.getId());
} else if (message instanceof AckMessage) { } else if (message instanceof AckMessage) {
AckMessage ackMessage = (AckMessage) message; AckMessage ackMessage = (AckMessage) message;
return ackMessage.getSourceType() == AckMessageSourceType.TRADE_MESSAGE && ackMessage.getSourceId().equals(trade.getId()); return ackMessage.getSourceType() == AckMessageSourceType.TRADE_MESSAGE && ackMessage.getSourceId().equals(trade.getId());

View file

@ -89,6 +89,7 @@ public class ArbitratorProcessDepositRequest extends TradeTask {
try { try {
txResult = trade.getXmrWalletService().verifyTradeTx( txResult = trade.getXmrWalletService().verifyTradeTx(
offer.getId(), offer.getId(),
null,
tradeFee, tradeFee,
sendAmount, sendAmount,
securityDeposit, securityDeposit,

View file

@ -21,6 +21,7 @@ import haveno.common.taskrunner.TaskRunner;
import haveno.common.util.Tuple2; import haveno.common.util.Tuple2;
import haveno.core.offer.Offer; import haveno.core.offer.Offer;
import haveno.core.offer.OfferDirection; import haveno.core.offer.OfferDirection;
import haveno.core.trade.HavenoUtils;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.messages.InitTradeRequest; import haveno.core.trade.messages.InitTradeRequest;
import haveno.core.trade.protocol.TradePeer; import haveno.core.trade.protocol.TradePeer;
@ -54,13 +55,15 @@ 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 values // process reserve tx with expected values
BigInteger tradeFee = isFromMaker ? trade.getMakerFee() : trade.getTakerFee(); BigInteger penaltyFee = HavenoUtils.multiply(isFromMaker ? offer.getAmount() : trade.getAmount(), offer.getPenaltyFeePct());
BigInteger tradeFee = isFromMaker ? offer.getMaxMakerFee() : trade.getTakerFee();
BigInteger sendAmount = isFromBuyer ? BigInteger.ZERO : isFromMaker ? offer.getAmount() : trade.getAmount(); // maker reserve tx is for offer amount BigInteger sendAmount = isFromBuyer ? BigInteger.ZERO : isFromMaker ? offer.getAmount() : trade.getAmount(); // maker reserve tx is for offer amount
BigInteger securityDeposit = isFromMaker ? isFromBuyer ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit() : isFromBuyer ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee(); BigInteger securityDeposit = isFromMaker ? isFromBuyer ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit() : isFromBuyer ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee();
Tuple2<MoneroTx, BigInteger> txResult; Tuple2<MoneroTx, BigInteger> txResult;
try { try {
txResult = trade.getXmrWalletService().verifyTradeTx( txResult = trade.getXmrWalletService().verifyTradeTx(
offer.getId(), offer.getId(),
penaltyFee,
tradeFee, tradeFee,
sendAmount, sendAmount,
securityDeposit, securityDeposit,

View file

@ -60,7 +60,6 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
request.getPubKeyRing(), request.getPubKeyRing(),
trade.getAmount().longValueExact(), trade.getAmount().longValueExact(),
trade.getPrice().getValue(), trade.getPrice().getValue(),
trade.getTakerFee().longValueExact(),
request.getAccountId(), request.getAccountId(),
request.getPaymentAccountId(), request.getPaymentAccountId(),
request.getPaymentMethodId(), request.getPaymentMethodId(),
@ -78,7 +77,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
null); null);
// send request to maker // send request to maker
log.info("Send {} with offerId {} and uid {} to maker {}", makerRequest.getClass().getSimpleName(), makerRequest.getTradeId(), makerRequest.getUid(), trade.getMaker().getNodeAddress()); log.info("Send {} with offerId {} and uid {} to maker {}", makerRequest.getClass().getSimpleName(), makerRequest.getOfferId(), makerRequest.getUid(), trade.getMaker().getNodeAddress());
processModel.getP2PService().sendEncryptedDirectMessage( processModel.getP2PService().sendEncryptedDirectMessage(
trade.getMaker().getNodeAddress(), // TODO (woodser): maker's address might be different from original owner address if they disconnect and reconnect, need to validate and update address when requests received trade.getMaker().getNodeAddress(), // TODO (woodser): maker's address might be different from original owner address if they disconnect and reconnect, need to validate and update address when requests received
trade.getMaker().getPubKeyRing(), trade.getMaker().getPubKeyRing(),
@ -86,7 +85,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
new SendDirectMessageListener() { new SendDirectMessageListener() {
@Override @Override
public void onArrived() { public void onArrived() {
log.info("{} arrived at maker: offerId={}; uid={}", makerRequest.getClass().getSimpleName(), makerRequest.getTradeId(), makerRequest.getUid()); log.info("{} arrived at maker: offerId={}; uid={}", makerRequest.getClass().getSimpleName(), makerRequest.getOfferId(), makerRequest.getUid());
complete(); complete();
} }
@Override @Override
@ -136,7 +135,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
null); null);
// send request to maker // send request to maker
log.info("Send {} with offerId {} and uid {} to maker {}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getTradeId(), initMultisigRequest.getUid(), trade.getMaker().getNodeAddress()); log.info("Send {} with offerId {} and uid {} to maker {}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getOfferId(), initMultisigRequest.getUid(), trade.getMaker().getNodeAddress());
processModel.getP2PService().sendEncryptedDirectMessage( processModel.getP2PService().sendEncryptedDirectMessage(
trade.getMaker().getNodeAddress(), trade.getMaker().getNodeAddress(),
trade.getMaker().getPubKeyRing(), trade.getMaker().getPubKeyRing(),
@ -144,7 +143,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
new SendDirectMessageListener() { new SendDirectMessageListener() {
@Override @Override
public void onArrived() { public void onArrived() {
log.info("{} arrived at maker: offerId={}; uid={}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getTradeId(), initMultisigRequest.getUid()); log.info("{} arrived at maker: offerId={}; uid={}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getOfferId(), initMultisigRequest.getUid());
} }
@Override @Override
public void onFault(String errorMessage) { public void onFault(String errorMessage) {
@ -154,7 +153,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
); );
// send request to taker // send request to taker
log.info("Send {} with offerId {} and uid {} to taker {}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getTradeId(), initMultisigRequest.getUid(), trade.getTaker().getNodeAddress()); log.info("Send {} with offerId {} and uid {} to taker {}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getOfferId(), initMultisigRequest.getUid(), trade.getTaker().getNodeAddress());
processModel.getP2PService().sendEncryptedDirectMessage( processModel.getP2PService().sendEncryptedDirectMessage(
trade.getTaker().getNodeAddress(), trade.getTaker().getNodeAddress(),
trade.getTaker().getPubKeyRing(), trade.getTaker().getPubKeyRing(),
@ -162,7 +161,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
new SendDirectMessageListener() { new SendDirectMessageListener() {
@Override @Override
public void onArrived() { public void onArrived() {
log.info("{} arrived at taker: offerId={}; uid={}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getTradeId(), initMultisigRequest.getUid()); log.info("{} arrived at taker: offerId={}; uid={}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getOfferId(), initMultisigRequest.getUid());
} }
@Override @Override
public void onFault(String errorMessage) { public void onFault(String errorMessage) {

View file

@ -57,7 +57,6 @@ public class MakerSendInitTradeRequest extends TradeTask {
processModel.getPubKeyRing(), processModel.getPubKeyRing(),
trade.getAmount().longValueExact(), trade.getAmount().longValueExact(),
trade.getPrice().getValue(), trade.getPrice().getValue(),
offer.getMakerFee().longValueExact(),
trade.getProcessModel().getAccountId(), trade.getProcessModel().getAccountId(),
offer.getMakerPaymentAccountId(), offer.getMakerPaymentAccountId(),
offer.getOfferPayload().getPaymentMethodId(), offer.getOfferPayload().getPaymentMethodId(),
@ -75,7 +74,7 @@ public class MakerSendInitTradeRequest extends TradeTask {
null); null);
// send request to arbitrator // send request to arbitrator
log.info("Sending {} with offerId {} and uid {} to arbitrator {}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getTradeId(), arbitratorRequest.getUid(), trade.getArbitrator().getNodeAddress()); log.info("Sending {} with offerId {} and uid {} to arbitrator {}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getOfferId(), arbitratorRequest.getUid(), trade.getArbitrator().getNodeAddress());
processModel.getP2PService().sendEncryptedDirectMessage( processModel.getP2PService().sendEncryptedDirectMessage(
trade.getArbitrator().getNodeAddress(), trade.getArbitrator().getNodeAddress(),
trade.getArbitrator().getPubKeyRing(), trade.getArbitrator().getPubKeyRing(),

View file

@ -150,7 +150,7 @@ public class ProcessInitMultisigRequest extends TradeTask {
sendInitMultisigRequest(peer1Address, peer1PubKeyRing, new SendDirectMessageListener() { sendInitMultisigRequest(peer1Address, peer1PubKeyRing, new SendDirectMessageListener() {
@Override @Override
public void onArrived() { public void onArrived() {
log.info("{} arrived: peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), peer1Address, request.getTradeId(), request.getUid()); log.info("{} arrived: peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), peer1Address, request.getOfferId(), request.getUid());
ack1 = true; ack1 = true;
if (ack1 && ack2) completeAux(); if (ack1 && ack2) completeAux();
} }
@ -166,7 +166,7 @@ public class ProcessInitMultisigRequest extends TradeTask {
sendInitMultisigRequest(peer2Address, peer2PubKeyRing, new SendDirectMessageListener() { sendInitMultisigRequest(peer2Address, peer2PubKeyRing, new SendDirectMessageListener() {
@Override @Override
public void onArrived() { public void onArrived() {
log.info("{} arrived: peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), peer2Address, request.getTradeId(), request.getUid()); log.info("{} arrived: peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), peer2Address, request.getOfferId(), request.getUid());
ack2 = true; ack2 = true;
if (ack1 && ack2) completeAux(); if (ack1 && ack2) completeAux();
} }
@ -212,7 +212,7 @@ public class ProcessInitMultisigRequest extends TradeTask {
trade.getSelf().getMadeMultisigHex(), trade.getSelf().getMadeMultisigHex(),
trade.getSelf().getExchangedMultisigHex()); trade.getSelf().getExchangedMultisigHex());
log.info("Send {} with offerId {} and uid {} to peer {}", request.getClass().getSimpleName(), request.getTradeId(), request.getUid(), recipient); log.info("Send {} with offerId {} and uid {} to peer {}", request.getClass().getSimpleName(), request.getOfferId(), request.getUid(), recipient);
processModel.getP2PService().sendEncryptedDirectMessage(recipient, pubKeyRing, request, listener); processModel.getP2PService().sendEncryptedDirectMessage(recipient, pubKeyRing, request, listener);
} }

View file

@ -69,20 +69,20 @@ public abstract class SendMailboxMessageTask extends TradeTask {
new SendMailboxMessageListener() { new SendMailboxMessageListener() {
@Override @Override
public void onArrived() { public void onArrived() {
log.info("{} arrived at peer {}. tradeId={}, uid={}", message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid()); log.info("{} arrived at peer {}. tradeId={}, uid={}", message.getClass().getSimpleName(), peersNodeAddress, message.getOfferId(), message.getUid());
setStateArrived(); setStateArrived();
if (!task.isCompleted()) complete(); if (!task.isCompleted()) complete();
} }
@Override @Override
public void onStoredInMailbox() { public void onStoredInMailbox() {
log.info("{} stored in mailbox for peer {}. tradeId={}, uid={}", message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid()); log.info("{} stored in mailbox for peer {}. tradeId={}, uid={}", message.getClass().getSimpleName(), peersNodeAddress, message.getOfferId(), message.getUid());
SendMailboxMessageTask.this.onStoredInMailbox(); SendMailboxMessageTask.this.onStoredInMailbox();
} }
@Override @Override
public void onFault(String errorMessage) { public void onFault(String errorMessage) {
log.error("{} failed: Peer {}. tradeId={}, uid={}, errorMessage={}", message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid(), errorMessage); log.error("{} failed: Peer {}. tradeId={}, uid={}, errorMessage={}", message.getClass().getSimpleName(), peersNodeAddress, message.getOfferId(), message.getUid(), errorMessage);
SendMailboxMessageTask.this.onFault(errorMessage, message); SendMailboxMessageTask.this.onFault(errorMessage, message);
} }
} }

View file

@ -19,6 +19,7 @@ package haveno.core.trade.protocol.tasks;
import haveno.common.taskrunner.TaskRunner; import haveno.common.taskrunner.TaskRunner;
import haveno.core.offer.OfferDirection; import haveno.core.offer.OfferDirection;
import haveno.core.trade.HavenoUtils;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.protocol.TradeProtocol; import haveno.core.trade.protocol.TradeProtocol;
import haveno.core.xmr.model.XmrAddressEntry; import haveno.core.xmr.model.XmrAddressEntry;
@ -41,11 +42,12 @@ public class TakerReserveTradeFunds extends TradeTask {
runInterceptHook(); runInterceptHook();
// create reserve tx // create reserve tx
BigInteger penaltyFee = HavenoUtils.multiply(trade.getAmount(), trade.getOffer().getPenaltyFeePct());
BigInteger takerFee = trade.getTakerFee(); BigInteger takerFee = trade.getTakerFee();
BigInteger sendAmount = trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getAmount() : BigInteger.ZERO; BigInteger sendAmount = trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getAmount() : BigInteger.ZERO;
BigInteger securityDeposit = trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getSellerSecurityDepositBeforeMiningFee() : trade.getBuyerSecurityDepositBeforeMiningFee(); BigInteger securityDeposit = trade.getOffer().getDirection() == OfferDirection.BUY ? trade.getSellerSecurityDepositBeforeMiningFee() : trade.getBuyerSecurityDepositBeforeMiningFee();
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, sendAmount, securityDeposit, returnAddress, false, null); MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, takerFee, sendAmount, securityDeposit, returnAddress, false, null);
// check if trade still exists // check if trade still exists
if (!processModel.getTradeManager().hasOpenTrade(trade)) { if (!processModel.getTradeManager().hasOpenTrade(trade)) {

View file

@ -117,12 +117,11 @@ public class TakerSendInitTradeRequestToArbitrator extends TradeTask {
// create request to arbitrator // create request to arbitrator
InitTradeRequest makerRequest = (InitTradeRequest) processModel.getTradeMessage(); // taker's InitTradeRequest to maker InitTradeRequest makerRequest = (InitTradeRequest) processModel.getTradeMessage(); // taker's InitTradeRequest to maker
InitTradeRequest arbitratorRequest = new InitTradeRequest( InitTradeRequest arbitratorRequest = new InitTradeRequest(
makerRequest.getTradeId(), makerRequest.getOfferId(),
makerRequest.getSenderNodeAddress(), makerRequest.getSenderNodeAddress(),
makerRequest.getPubKeyRing(), makerRequest.getPubKeyRing(),
makerRequest.getTradeAmount(), makerRequest.getTradeAmount(),
makerRequest.getTradePrice(), makerRequest.getTradePrice(),
makerRequest.getTradeFee(),
makerRequest.getAccountId(), makerRequest.getAccountId(),
makerRequest.getPaymentAccountId(), makerRequest.getPaymentAccountId(),
makerRequest.getPaymentMethodId(), makerRequest.getPaymentMethodId(),
@ -140,7 +139,7 @@ public class TakerSendInitTradeRequestToArbitrator extends TradeTask {
processModel.getMakerSignature()); processModel.getMakerSignature());
// send request to arbitrator // send request to arbitrator
log.info("Sending {} with offerId {} and uid {} to arbitrator {}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getTradeId(), arbitratorRequest.getUid(), trade.getArbitrator().getNodeAddress()); log.info("Sending {} with offerId {} and uid {} to arbitrator {}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getOfferId(), arbitratorRequest.getUid(), trade.getArbitrator().getNodeAddress());
processModel.getP2PService().sendEncryptedDirectMessage( processModel.getP2PService().sendEncryptedDirectMessage(
arbitratorNodeAddress, arbitratorNodeAddress,
arbitrator.getPubKeyRing(), arbitrator.getPubKeyRing(),

View file

@ -53,8 +53,8 @@ public class SendMediatedPayoutSignatureMessage extends TradeTask {
trade.getId(), trade.getId(),
p2PService.getAddress(), p2PService.getAddress(),
UUID.randomUUID().toString()); UUID.randomUUID().toString());
log.info("Send {} to peer {}. tradeId={}, uid={}", log.info("Send {} to peer {}. offerId={}, uid={}",
message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid()); message.getClass().getSimpleName(), peersNodeAddress, message.getOfferId(), message.getUid());
trade.setMediationResultState(MediationResultState.SIG_MSG_SENT); trade.setMediationResultState(MediationResultState.SIG_MSG_SENT);
processModel.getTradeManager().requestPersistence(); processModel.getTradeManager().requestPersistence();
@ -64,8 +64,8 @@ public class SendMediatedPayoutSignatureMessage extends TradeTask {
new SendMailboxMessageListener() { new SendMailboxMessageListener() {
@Override @Override
public void onArrived() { public void onArrived() {
log.info("{} arrived at peer {}. tradeId={}, uid={}", log.info("{} arrived at peer {}. offerId={}, uid={}",
message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid()); message.getClass().getSimpleName(), peersNodeAddress, message.getOfferId(), message.getUid());
trade.setMediationResultState(MediationResultState.SIG_MSG_ARRIVED); trade.setMediationResultState(MediationResultState.SIG_MSG_ARRIVED);
processModel.getTradeManager().requestPersistence(); processModel.getTradeManager().requestPersistence();
@ -74,8 +74,8 @@ public class SendMediatedPayoutSignatureMessage extends TradeTask {
@Override @Override
public void onStoredInMailbox() { public void onStoredInMailbox() {
log.info("{} stored in mailbox for peer {}. tradeId={}, uid={}", log.info("{} stored in mailbox for peer {}. offerId={}, uid={}",
message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid()); message.getClass().getSimpleName(), peersNodeAddress, message.getOfferId(), message.getUid());
trade.setMediationResultState(MediationResultState.SIG_MSG_IN_MAILBOX); trade.setMediationResultState(MediationResultState.SIG_MSG_IN_MAILBOX);
processModel.getTradeManager().requestPersistence(); processModel.getTradeManager().requestPersistence();
@ -84,8 +84,8 @@ public class SendMediatedPayoutSignatureMessage extends TradeTask {
@Override @Override
public void onFault(String errorMessage) { public void onFault(String errorMessage) {
log.error("{} failed: Peer {}. tradeId={}, uid={}, errorMessage={}", log.error("{} failed: Peer {}. offerId={}, uid={}, errorMessage={}",
message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid(), errorMessage); message.getClass().getSimpleName(), peersNodeAddress, message.getOfferId(), message.getUid(), errorMessage);
trade.setMediationResultState(MediationResultState.SIG_MSG_SEND_FAILED); trade.setMediationResultState(MediationResultState.SIG_MSG_SEND_FAILED);
appendToErrorMessage("Sending message failed: message=" + message + "\nerrorMessage=" + errorMessage); appendToErrorMessage("Sending message failed: message=" + message + "\nerrorMessage=" + errorMessage);
processModel.getTradeManager().requestPersistence(); processModel.getTradeManager().requestPersistence();

View file

@ -56,6 +56,6 @@ public class Validator {
} }
public static boolean isTradeIdValid(String tradeId, TradeMessage tradeMessage) { public static boolean isTradeIdValid(String tradeId, TradeMessage tradeMessage) {
return tradeId.equals(tradeMessage.getTradeId()); return tradeId.equals(tradeMessage.getOfferId());
} }
} }

View file

@ -532,20 +532,21 @@ public class XmrWalletService {
/** /**
* Create the reserve tx and freeze its inputs. The full amount is returned * Create the reserve tx and freeze its inputs. The full amount is returned
* to the sender's payout address less the security deposit and mining fee. * to the sender's payout address less the penalty and mining fees.
* *
* @param penaltyFee penalty fee for breaking protocol
* @param tradeFee trade fee * @param tradeFee trade fee
* @param sendAmount amount to give peer * @param sendAmount amount to send peer
* @param securityDeposit security deposit amount * @param securityDeposit security deposit amount
* @param returnAddress return address for reserved funds * @param returnAddress return address for reserved funds
* @param reserveExactAmount specifies to reserve the exact input amount * @param reserveExactAmount specifies to reserve the exact input amount
* @param preferredSubaddressIndex preferred source subaddress to spend from (optional) * @param preferredSubaddressIndex preferred source subaddress to spend from (optional)
* @return a transaction to reserve a trade * @return a transaction to reserve a trade
*/ */
public MoneroTxWallet createReserveTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String returnAddress, boolean reserveExactAmount, Integer preferredSubaddressIndex) { public MoneroTxWallet createReserveTx(BigInteger penaltyFee, BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String returnAddress, boolean reserveExactAmount, Integer preferredSubaddressIndex) {
log.info("Creating reserve tx with preferred subaddress index={}, return address={}", preferredSubaddressIndex, returnAddress); log.info("Creating reserve tx with preferred subaddress index={}, return address={}", preferredSubaddressIndex, returnAddress);
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
MoneroTxWallet reserveTx = createTradeTx(tradeFee, sendAmount, securityDeposit, returnAddress, reserveExactAmount, preferredSubaddressIndex); MoneroTxWallet reserveTx = createTradeTx(penaltyFee, tradeFee, sendAmount, securityDeposit, returnAddress, reserveExactAmount, preferredSubaddressIndex);
log.info("Done creating reserve tx in {} ms", System.currentTimeMillis() - time); log.info("Done creating reserve tx in {} ms", System.currentTimeMillis() - time);
return reserveTx; return reserveTx;
} }
@ -568,18 +569,18 @@ public class XmrWalletService {
// create deposit tx // create deposit tx
String multisigAddress = trade.getProcessModel().getMultisigAddress(); String multisigAddress = trade.getProcessModel().getMultisigAddress();
BigInteger tradeFee = trade instanceof MakerTrade ? trade.getOffer().getMakerFee() : trade.getTakerFee(); BigInteger tradeFee = trade instanceof MakerTrade ? trade.getMakerFee() : trade.getTakerFee();
BigInteger sendAmount = trade instanceof BuyerTrade ? BigInteger.ZERO : trade.getAmount(); BigInteger sendAmount = trade instanceof BuyerTrade ? BigInteger.ZERO : trade.getAmount();
BigInteger securityDeposit = trade instanceof BuyerTrade ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee(); BigInteger securityDeposit = trade instanceof BuyerTrade ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee();
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
log.info("Creating deposit tx with multisig address={}", multisigAddress); log.info("Creating deposit tx with multisig address={}", multisigAddress);
MoneroTxWallet depositTx = createTradeTx(tradeFee, sendAmount, securityDeposit, multisigAddress, reserveExactAmount, preferredSubaddressIndex); MoneroTxWallet depositTx = createTradeTx(null, tradeFee, sendAmount, securityDeposit, multisigAddress, reserveExactAmount, preferredSubaddressIndex);
log.info("Done creating deposit tx for trade {} {} in {} ms", trade.getClass().getSimpleName(), trade.getId(), System.currentTimeMillis() - time); log.info("Done creating deposit tx for trade {} {} in {} ms", trade.getClass().getSimpleName(), trade.getId(), System.currentTimeMillis() - time);
return depositTx; return depositTx;
} }
} }
private MoneroTxWallet createTradeTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, boolean reserveExactAmount, Integer preferredSubaddressIndex) { private MoneroTxWallet createTradeTx(BigInteger penaltyFee, BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, boolean reserveExactAmount, Integer preferredSubaddressIndex) {
synchronized (walletLock) { synchronized (walletLock) {
MoneroWallet wallet = getWallet(); MoneroWallet wallet = getWallet();
@ -604,27 +605,31 @@ public class XmrWalletService {
// first try preferred subaddressess // first try preferred subaddressess
for (int i = 0; i < subaddressIndices.size(); i++) { for (int i = 0; i < subaddressIndices.size(); i++) {
try { try {
return createTradeTxFromSubaddress(tradeFee, sendAmount, securityDeposit, address, reserveExactAmount, subaddressIndices.get(i)); return createTradeTxFromSubaddress(penaltyFee, tradeFee, sendAmount, securityDeposit, address, reserveExactAmount, subaddressIndices.get(i));
} catch (Exception e) { } catch (Exception e) {
if (i == subaddressIndices.size() - 1 && reserveExactAmount) throw e; // throw if no subaddress with exact output if (i == subaddressIndices.size() - 1 && reserveExactAmount) throw e; // throw if no subaddress with exact output
} }
} }
// try any subaddress // try any subaddress
return createTradeTxFromSubaddress(tradeFee, sendAmount, securityDeposit, address, reserveExactAmount, null); return createTradeTxFromSubaddress(penaltyFee, tradeFee, sendAmount, securityDeposit, address, reserveExactAmount, null);
} }
} }
private MoneroTxWallet createTradeTxFromSubaddress(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, boolean reserveExactAmount, Integer subaddressIndex) { private MoneroTxWallet createTradeTxFromSubaddress(BigInteger penaltyFee, BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, boolean reserveExactAmount, Integer subaddressIndex) {
// create tx // create tx
MoneroTxWallet tradeTx = wallet.createTx(new MoneroTxConfig() boolean isDepositTx = penaltyFee == null;
BigInteger feeAmount = isDepositTx ? tradeFee : penaltyFee;
BigInteger transferAmount = isDepositTx ? sendAmount.add(securityDeposit) : sendAmount.add(securityDeposit).add(tradeFee).subtract(penaltyFee);
MoneroTxConfig txConfig = new MoneroTxConfig()
.setAccountIndex(0) .setAccountIndex(0)
.setSubaddressIndices(subaddressIndex) .setSubaddressIndices(subaddressIndex)
.addDestination(HavenoUtils.getTradeFeeAddress(), tradeFee) .addDestination(address, transferAmount)
.addDestination(address, sendAmount.add(securityDeposit)) .setSubtractFeeFrom(0) // pay fee from transfer amount
.setSubtractFeeFrom(1) .setPriority(XmrWalletService.PROTOCOL_FEE_PRIORITY);
.setPriority(XmrWalletService.PROTOCOL_FEE_PRIORITY)); // pay fee from security deposit if (!BigInteger.valueOf(0).equals(feeAmount)) txConfig.addDestination(HavenoUtils.getTradeFeeAddress(), feeAmount);
MoneroTxWallet tradeTx = wallet.createTx(txConfig);
// freeze inputs // freeze inputs
List<String> keyImages = new ArrayList<String>(); List<String> keyImages = new ArrayList<String>();
@ -639,6 +644,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 offerId id of offer to verify trade tx * @param offerId id of offer to verify trade tx
* @param penaltyFee penalty fee for breaking protocol
* @param tradeFee trade fee * @param tradeFee trade fee
* @param sendAmount amount to give peer * @param sendAmount amount to give peer
* @param securityDeposit security deposit amount * @param securityDeposit security deposit amount
@ -647,9 +653,9 @@ public class XmrWalletService {
* @param txHex transaction hex * @param txHex transaction hex
* @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
* @return tuple with the verified tx and its actual security deposit * @return tuple with the verified tx and the actual security deposit
*/ */
public Tuple2<MoneroTx, BigInteger> verifyTradeTx(String offerId, BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, String txHash, String txHex, String txKey, List<String> keyImages) { public Tuple2<MoneroTx, BigInteger> verifyTradeTx(String offerId, BigInteger penaltyFee, BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, String txHash, String txHex, String txKey, List<String> keyImages) {
if (txHash == null) throw new IllegalArgumentException("Cannot verify trade tx with null id"); if (txHash == null) throw new IllegalArgumentException("Cannot verify trade tx with null id");
MoneroDaemonRpc daemon = getDaemon(); MoneroDaemonRpc daemon = getDaemon();
MoneroWallet wallet = getWallet(); MoneroWallet wallet = getWallet();
@ -681,39 +687,45 @@ public class XmrWalletService {
if (!BigInteger.ZERO.equals(tx.getUnlockTime())) throw new RuntimeException("Unlock height must be 0"); if (!BigInteger.ZERO.equals(tx.getUnlockTime())) throw new RuntimeException("Unlock height must be 0");
// verify miner fee // verify miner fee
BigInteger feeEstimate = getElevatedFeeEstimate(tx.getWeight()); BigInteger minerFeeEstimate = getElevatedFeeEstimate(tx.getWeight());
double feeDiff = tx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); double minerFeeDiff = tx.getFee().subtract(minerFeeEstimate).abs().doubleValue() / minerFeeEstimate.doubleValue();
if (feeDiff > MINER_FEE_TOLERANCE) throw new Error("Miner fee is not within " + (MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + tx.getFee()); if (minerFeeDiff > MINER_FEE_TOLERANCE) throw new Error("Miner fee is not within " + (MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + minerFeeEstimate + " but was " + tx.getFee());
log.info("Trade tx fee {} is within tolerance, diff%={}", tx.getFee(), feeDiff); log.info("Trade tx fee {} is within tolerance, diff%={}", tx.getFee(), minerFeeDiff);
// verify transfer proof to fee address // verify proof to fee address
MoneroCheckTx tradeFeeCheck = wallet.checkTxKey(txHash, txKey, HavenoUtils.getTradeFeeAddress()); MoneroCheckTx feeCheck = wallet.checkTxKey(txHash, txKey, HavenoUtils.getTradeFeeAddress());
if (!tradeFeeCheck.isGood()) throw new RuntimeException("Invalid proof to trade fee address"); if (!feeCheck.isGood()) throw new RuntimeException("Invalid proof to trade fee address");
// verify transfer proof to address // verify proof to transfer address
MoneroCheckTx transferCheck = wallet.checkTxKey(txHash, txKey, address); MoneroCheckTx transferCheck = wallet.checkTxKey(txHash, txKey, address);
if (!transferCheck.isGood()) throw new RuntimeException("Invalid proof to transfer address"); if (!transferCheck.isGood()) throw new RuntimeException("Invalid proof to transfer address");
// collect actual trade fee, send amount, and security deposit // verify fee and transfer amounts
BigInteger actualTradeFee = tradeFeeCheck.getReceivedAmount(); BigInteger actualFee = feeCheck.getReceivedAmount();
actualSecurityDeposit = transferCheck.getReceivedAmount().subtract(sendAmount); BigInteger actualTransferAmount = transferCheck.getReceivedAmount();
BigInteger actualSendAmount = transferCheck.getReceivedAmount().subtract(actualSecurityDeposit); boolean isDepositTx = penaltyFee == null;
if (isDepositTx) {
// verify trade fee // verify trade fee
if (actualTradeFee.compareTo(tradeFee) < 0) { if (!actualFee.equals(tradeFee)) throw new RuntimeException("Invalid trade fee amount, expected " + tradeFee + " but was " + actualFee);
throw new RuntimeException("Insufficient trade fee, expected=" + tradeFee + ", actual=" + actualTradeFee + ", transfer address check=" + JsonUtils.serialize(transferCheck) + ", trade fee address check=" + JsonUtils.serialize(tradeFeeCheck));
// verify multisig deposit amount
BigInteger expectedTransferAmount = sendAmount.add(securityDeposit).subtract(tx.getFee());
if (!actualTransferAmount.equals(expectedTransferAmount)) throw new RuntimeException("Invalid multisig deposit amount, expected " + expectedTransferAmount + " but was " + actualTransferAmount);
actualSecurityDeposit = actualTransferAmount.subtract(sendAmount);
} else {
// verify penalty fee
if (!actualFee.equals(penaltyFee)) throw new RuntimeException("Invalid penalty fee amount, expected " + penaltyFee + " but was " + actualFee);
// verify return amount
BigInteger expectedTransferAmount = sendAmount.add(securityDeposit).add(tradeFee).subtract(penaltyFee).subtract(tx.getFee());
if (!actualTransferAmount.equals(expectedTransferAmount)) throw new RuntimeException("Invalid return amount, expected " + expectedTransferAmount + " but was " + actualTransferAmount);
actualSecurityDeposit = actualTransferAmount.subtract(sendAmount).subtract(tradeFee);
} }
// verify send amount // return the result
if (!actualSendAmount.equals(sendAmount)) { return new Tuple2<>(tx, actualSecurityDeposit);
throw new RuntimeException("Unexpected send amount, expected " + sendAmount + " but was " + actualSendAmount);
}
// verify security deposit
BigInteger expectedSecurityDeposit = securityDeposit.subtract(tx.getFee()); // fee is paid from security deposit
if (!actualSecurityDeposit.equals(expectedSecurityDeposit)) {
throw new RuntimeException("Unexpected security deposit amount, expected " + expectedSecurityDeposit + " but was " + actualSecurityDeposit);
}
} catch (Exception e) { } catch (Exception e) {
log.warn("Error verifying trade tx with offer id=" + offerId + (tx == null ? "" : ", tx=" + tx) + ": " + e.getMessage()); log.warn("Error verifying trade tx with offer id=" + offerId + (tx == null ? "" : ", tx=" + tx) + ": " + e.getMessage());
throw e; throw e;
@ -725,7 +737,6 @@ public class XmrWalletService {
throw err.getCode().equals(-32601) ? new RuntimeException("Failed to flush tx from pool. Arbitrator must use trusted, unrestricted daemon") : err; throw err.getCode().equals(-32601) ? new RuntimeException("Failed to flush tx from pool. Arbitrator must use trusted, unrestricted daemon") : err;
} }
} }
return new Tuple2<>(tx, actualSecurityDeposit);
} }
} }

View file

@ -46,6 +46,11 @@ public class OfferMaker {
lookup.valueOf(useMarketBasedPrice, false), lookup.valueOf(useMarketBasedPrice, false),
lookup.valueOf(amount, 100000L), lookup.valueOf(amount, 100000L),
lookup.valueOf(minAmount, 100000L), lookup.valueOf(minAmount, 100000L),
0L,
0L,
0L,
0L,
0L,
lookup.valueOf(baseCurrencyCode, "XMR"), lookup.valueOf(baseCurrencyCode, "XMR"),
lookup.valueOf(counterCurrencyCode, "USD"), lookup.valueOf(counterCurrencyCode, "USD"),
"SEPA", "SEPA",
@ -58,9 +63,6 @@ public class OfferMaker {
0L, 0L,
0L, 0L,
0L, 0L,
0L,
0L,
0L,
false, false,
false, false,
0L, 0L,

View file

@ -31,11 +31,26 @@ import static org.junit.jupiter.api.Assertions.fail;
public class CoinUtilTest { public class CoinUtilTest {
@Test @Test
public void testGetFeePerBtc() { public void testGetPercentOfAmount() {
assertEquals(HavenoUtils.xmrToAtomicUnits(1), HavenoUtils.getFeePerXmr(HavenoUtils.xmrToAtomicUnits(1), HavenoUtils.xmrToAtomicUnits(1))); BigInteger bi = new BigInteger("703100000000");
assertEquals(HavenoUtils.xmrToAtomicUnits(0.1), HavenoUtils.getFeePerXmr(HavenoUtils.xmrToAtomicUnits(0.1), HavenoUtils.xmrToAtomicUnits(1))); assertEquals(new BigInteger("105465000000"), HavenoUtils.multiply(bi, .15));
assertEquals(HavenoUtils.xmrToAtomicUnits(0.01), HavenoUtils.getFeePerXmr(HavenoUtils.xmrToAtomicUnits(0.1), HavenoUtils.xmrToAtomicUnits(0.1))); }
assertEquals(HavenoUtils.xmrToAtomicUnits(0.015), HavenoUtils.getFeePerXmr(HavenoUtils.xmrToAtomicUnits(0.3), HavenoUtils.xmrToAtomicUnits(0.05)));
@Test
public void testGetFeePerXmr() {
assertEquals(HavenoUtils.xmrToAtomicUnits(1), HavenoUtils.multiply(HavenoUtils.xmrToAtomicUnits(1), 1.0));
assertEquals(HavenoUtils.xmrToAtomicUnits(0.1), HavenoUtils.multiply(HavenoUtils.xmrToAtomicUnits(0.1), 1.0));
assertEquals(HavenoUtils.xmrToAtomicUnits(0.01), HavenoUtils.multiply(HavenoUtils.xmrToAtomicUnits(0.1), 0.1));
assertEquals(HavenoUtils.xmrToAtomicUnits(0.015), HavenoUtils.multiply(HavenoUtils.xmrToAtomicUnits(0.3), 0.05));
}
@Test
public void testParseXmr() {
String xmrStr = "0.266394780889";
BigInteger au = HavenoUtils.parseXmr(xmrStr);
assertEquals(new BigInteger("266394780889"), au);
assertEquals(xmrStr, "" + HavenoUtils.atomicUnitsToXmr(au));
assertEquals(xmrStr, HavenoUtils.formatXmr(au, false));
} }
@Test @Test

View file

@ -545,7 +545,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
// Maker does not pay the mining fee for the trade txs because the mining fee might be different when maker // Maker does not pay the mining fee for the trade txs because the mining fee might be different when maker
// created the offer and reserved his funds, so that would not work well with dynamic fees. // created the offer and reserved his funds, so that would not work well with dynamic fees.
// The mining fee for the createOfferFee tx is deducted from the createOfferFee and not visible to the trader // The mining fee for the createOfferFee tx is deducted from the createOfferFee and not visible to the trader
final BigInteger makerFee = getMakerFee(); final BigInteger makerFee = getMaxMakerFee();
if (direction != null && amount.get() != null && makerFee != null) { if (direction != null && amount.get() != null && makerFee != null) {
BigInteger feeAndSecDeposit = getSecurityDeposit().add(makerFee); BigInteger feeAndSecDeposit = getSecurityDeposit().add(makerFee);
BigInteger total = isBuyOffer() ? feeAndSecDeposit : feeAndSecDeposit.add(amount.get()); BigInteger total = isBuyOffer() ? feeAndSecDeposit : feeAndSecDeposit.add(amount.get());
@ -677,8 +677,8 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
this.marketPriceAvailable = marketPriceAvailable; this.marketPriceAvailable = marketPriceAvailable;
} }
public BigInteger getMakerFee() { public BigInteger getMaxMakerFee() {
return HavenoUtils.getMakerFee(amount.get()); return HavenoUtils.multiply(amount.get(), HavenoUtils.MAKER_FEE_PCT);
} }
boolean canPlaceOffer() { boolean canPlaceOffer() {

View file

@ -491,7 +491,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
tradeFeeCurrencyCode.set(Res.getBaseCurrencyCode()); tradeFeeCurrencyCode.set(Res.getBaseCurrencyCode());
tradeFeeDescription.set(Res.get("createOffer.tradeFee.descriptionXMROnly")); tradeFeeDescription.set(Res.get("createOffer.tradeFee.descriptionXMROnly"));
BigInteger makerFee = dataModel.getMakerFee(); BigInteger makerFee = dataModel.getMaxMakerFee();
if (makerFee == null) { if (makerFee == null) {
return; return;
} }
@ -499,7 +499,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
isTradeFeeVisible.setValue(true); isTradeFeeVisible.setValue(true);
tradeFee.set(HavenoUtils.formatXmr(makerFee)); tradeFee.set(HavenoUtils.formatXmr(makerFee));
tradeFeeInXmrWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil, tradeFeeInXmrWithFiat.set(OfferViewModelUtil.getTradeFeeWithFiatEquivalent(offerUtil,
dataModel.getMakerFee(), dataModel.getMaxMakerFee(),
btcFormatter)); btcFormatter));
} }
@ -1004,8 +1004,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil, return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil,
dataModel.getSecurityDeposit(), dataModel.getSecurityDeposit(),
dataModel.getAmount().get(), dataModel.getAmount().get(),
btcFormatter, btcFormatter
Restrictions.getMinBuyerSecurityDeposit()
); );
} }
@ -1016,14 +1015,13 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
public String getTradeFee() { public String getTradeFee() {
return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil, return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil,
dataModel.getMakerFee(), dataModel.getMaxMakerFee(),
dataModel.getAmount().get(), dataModel.getAmount().get(),
btcFormatter, btcFormatter);
HavenoUtils.getMinMakerFee());
} }
public String getMakerFeePercentage() { public String getMakerFeePercentage() {
final BigInteger makerFee = dataModel.getMakerFee(); final BigInteger makerFee = dataModel.getMaxMakerFee();
return GUIUtil.getPercentage(makerFee, dataModel.getAmount().get()); return GUIUtil.getPercentage(makerFee, dataModel.getAmount().get());
} }

View file

@ -44,18 +44,10 @@ public class OfferViewModelUtil {
public static String getTradeFeeWithFiatEquivalentAndPercentage(OfferUtil offerUtil, public static String getTradeFeeWithFiatEquivalentAndPercentage(OfferUtil offerUtil,
BigInteger tradeFee, BigInteger tradeFee,
BigInteger tradeAmount, BigInteger tradeAmount,
CoinFormatter formatter, CoinFormatter formatter) {
BigInteger minTradeFee) {
String feeAsXmr = HavenoUtils.formatXmr(tradeFee, true); String feeAsXmr = HavenoUtils.formatXmr(tradeFee, true);
String percentage; String percentage;
if (tradeFee.compareTo(minTradeFee) <= 0) { percentage = GUIUtil.getPercentage(tradeFee, tradeAmount) + " " + Res.get("guiUtil.ofTradeAmount");
percentage = Res.get("guiUtil.requiredMinimum")
.replace("(", "")
.replace(")", "");
} else {
percentage = GUIUtil.getPercentage(tradeFee, tradeAmount) +
" " + Res.get("guiUtil.ofTradeAmount");
}
return offerUtil.getFeeInUserFiatCurrency(tradeFee, return offerUtil.getFeeInUserFiatCurrency(tradeFee,
formatter) formatter)
.map(VolumeUtil::formatAverageVolumeWithCode) .map(VolumeUtil::formatAverageVolumeWithCode)

View file

@ -618,10 +618,6 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
return true; return true;
} }
public String getMakerFeeAsString(Offer offer) {
return HavenoUtils.formatXmr(offer.getMakerFee(), true);
}
private static String getDirectionWithCodeDetailed(OfferDirection direction, String currencyCode) { private static String getDirectionWithCodeDetailed(OfferDirection direction, String currencyCode) {
if (CurrencyUtil.isTraditionalCurrency(currencyCode)) if (CurrencyUtil.isTraditionalCurrency(currencyCode))
return (direction == OfferDirection.BUY) ? Res.get("shared.buyingXMRWith", currencyCode) : Res.get("shared.sellingXMRFor", currencyCode); return (direction == OfferDirection.BUY) ? Res.get("shared.buyingXMRWith", currencyCode) : Res.get("shared.sellingXMRFor", currencyCode);

View file

@ -248,7 +248,6 @@ class TakeOfferDataModel extends OfferDataModel {
errorMsg = Res.get("offerbook.warning.offerWasAlreadyUsedInTrade"); errorMsg = Res.get("offerbook.warning.offerWasAlreadyUsedInTrade");
} else { } else {
tradeManager.onTakeOffer(amount.get(), tradeManager.onTakeOffer(amount.get(),
getTakerFee(),
fundsNeededForTrade, fundsNeededForTrade,
offer, offer,
paymentAccount.getId(), paymentAccount.getId(),
@ -404,7 +403,7 @@ class TakeOfferDataModel extends OfferDataModel {
@Nullable @Nullable
BigInteger getTakerFee() { BigInteger getTakerFee() {
return HavenoUtils.getTakerFee(this.amount.get()); return HavenoUtils.multiply(this.amount.get(), offer.getTakerFeePct());
} }
public void swapTradeToSavings() { public void swapTradeToSavings() {

View file

@ -38,7 +38,6 @@ import haveno.core.util.VolumeUtil;
import haveno.core.util.coin.CoinFormatter; import haveno.core.util.coin.CoinFormatter;
import haveno.core.util.coin.CoinUtil; import haveno.core.util.coin.CoinUtil;
import haveno.core.util.validation.InputValidator; import haveno.core.util.validation.InputValidator;
import haveno.core.xmr.wallet.Restrictions;
import haveno.desktop.Navigation; import haveno.desktop.Navigation;
import haveno.desktop.common.model.ActivatableWithDataModel; import haveno.desktop.common.model.ActivatableWithDataModel;
import haveno.desktop.common.model.ViewModel; import haveno.desktop.common.model.ViewModel;
@ -656,9 +655,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil, return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil,
dataModel.getSecurityDeposit(), dataModel.getSecurityDeposit(),
dataModel.getAmount().get(), dataModel.getAmount().get(),
xmrFormatter, xmrFormatter);
Restrictions.getMinBuyerSecurityDeposit()
);
} }
public String getSecurityDepositWithCode() { public String getSecurityDepositWithCode() {
@ -669,8 +666,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil, return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil,
dataModel.getTakerFee(), dataModel.getTakerFee(),
dataModel.getAmount().get(), dataModel.getAmount().get(),
xmrFormatter, xmrFormatter);
HavenoUtils.getMinMakerFee());
} }
public String getTakerFeePercentage() { public String getTakerFeePercentage() {

View file

@ -189,6 +189,11 @@ class EditOfferDataModel extends MutableOfferDataModel {
newOfferPayload.isUseMarketBasedPrice(), newOfferPayload.isUseMarketBasedPrice(),
offerPayload.getAmount(), offerPayload.getAmount(),
offerPayload.getMinAmount(), offerPayload.getMinAmount(),
offerPayload.getMakerFeePct(),
offerPayload.getTakerFeePct(),
offerPayload.getPenaltyFeePct(),
offerPayload.getBuyerSecurityDepositPct(),
offerPayload.getSellerSecurityDepositPct(),
newOfferPayload.getBaseCurrencyCode(), newOfferPayload.getBaseCurrencyCode(),
newOfferPayload.getCounterCurrencyCode(), newOfferPayload.getCounterCurrencyCode(),
newOfferPayload.getPaymentMethodId(), newOfferPayload.getPaymentMethodId(),
@ -199,9 +204,6 @@ class EditOfferDataModel extends MutableOfferDataModel {
newOfferPayload.getAcceptedBankIds(), newOfferPayload.getAcceptedBankIds(),
offerPayload.getVersionNr(), offerPayload.getVersionNr(),
offerPayload.getBlockHeightAtOfferCreation(), offerPayload.getBlockHeightAtOfferCreation(),
offerPayload.getMakerFee(),
offerPayload.getBuyerSecurityDepositPct(),
offerPayload.getSellerSecurityDepositPct(),
offerPayload.getMaxTradeLimit(), offerPayload.getMaxTradeLimit(),
offerPayload.getMaxTradePeriod(), offerPayload.getMaxTradePeriod(),
offerPayload.isUseAutoClose(), offerPayload.isUseAutoClose(),

View file

@ -27,7 +27,6 @@ import haveno.core.locale.Res;
import haveno.core.monetary.Price; import haveno.core.monetary.Price;
import haveno.core.offer.Offer; import haveno.core.offer.Offer;
import haveno.core.offer.OpenOffer; import haveno.core.offer.OpenOffer;
import haveno.core.trade.HavenoUtils;
import haveno.core.util.FormattingUtils; import haveno.core.util.FormattingUtils;
import haveno.core.util.PriceUtil; import haveno.core.util.PriceUtil;
import haveno.core.util.VolumeUtil; import haveno.core.util.VolumeUtil;
@ -155,11 +154,6 @@ class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel>
return GUIUtil.isBootstrappedOrShowPopup(p2PService); return GUIUtil.isBootstrappedOrShowPopup(p2PService);
} }
public String getMakerFeeAsString(OpenOffer openOffer) {
Offer offer = openOffer.getOffer();
return HavenoUtils.formatXmr(offer.getMakerFee(), true);
}
String getTriggerPrice(OpenOfferListItem item) { String getTriggerPrice(OpenOfferListItem item) {
if ((item == null)) { if ((item == null)) {
return ""; return "";

View file

@ -271,7 +271,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
Offer offer = trade.getOffer(); Offer offer = trade.getOffer();
if (isMaker()) { if (isMaker()) {
if (offer != null) { if (offer != null) {
return offer.getMakerFee(); return trade.getMakerFee();
} else { } else {
log.error("offer is null"); log.error("offer is null");
return BigInteger.ZERO; return BigInteger.ZERO;

View file

@ -38,7 +38,6 @@ import haveno.core.util.FormattingUtils;
import haveno.core.util.VolumeUtil; import haveno.core.util.VolumeUtil;
import haveno.core.util.coin.CoinFormatter; import haveno.core.util.coin.CoinFormatter;
import haveno.core.util.validation.BtcAddressValidator; import haveno.core.util.validation.BtcAddressValidator;
import haveno.core.xmr.wallet.Restrictions;
import haveno.desktop.Navigation; import haveno.desktop.Navigation;
import haveno.desktop.common.model.ActivatableWithDataModel; import haveno.desktop.common.model.ActivatableWithDataModel;
import haveno.desktop.common.model.ViewModel; import haveno.desktop.common.model.ViewModel;
@ -272,12 +271,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
BigInteger tradeFeeInXmr = dataModel.getTradeFee(); BigInteger tradeFeeInXmr = dataModel.getTradeFee();
BigInteger minTradeFee = dataModel.isMaker() ? String percentage = GUIUtil.getPercentageOfTradeAmount(tradeFeeInXmr, trade.getAmount());
HavenoUtils.getMinMakerFee() :
HavenoUtils.getMinTakerFee();
String percentage = GUIUtil.getPercentageOfTradeAmount(tradeFeeInXmr, trade.getAmount(),
minTradeFee);
return HavenoUtils.formatXmr(tradeFeeInXmr, true) + percentage; return HavenoUtils.formatXmr(tradeFeeInXmr, true) + percentage;
} else { } else {
return ""; return "";
@ -292,13 +286,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
offer.getMaxBuyerSecurityDeposit() offer.getMaxBuyerSecurityDeposit()
: offer.getMaxSellerSecurityDeposit(); : offer.getMaxSellerSecurityDeposit();
BigInteger minSecurityDeposit = dataModel.isBuyer() ? String percentage = GUIUtil.getPercentageOfTradeAmount(securityDeposit, trade.getAmount());
Restrictions.getMinBuyerSecurityDeposit() :
Restrictions.getMinSellerSecurityDeposit();
String percentage = GUIUtil.getPercentageOfTradeAmount(securityDeposit,
trade.getAmount(),
minSecurityDeposit);
return HavenoUtils.formatXmr(securityDeposit, true) + percentage; return HavenoUtils.formatXmr(securityDeposit, true) + percentage;
} else { } else {
return ""; return "";

View file

@ -623,14 +623,10 @@ public class GUIUtil {
} }
} }
public static String getPercentageOfTradeAmount(BigInteger fee, BigInteger tradeAmount, BigInteger minFee) { public static String getPercentageOfTradeAmount(BigInteger fee, BigInteger tradeAmount) {
String result = " (" + getPercentage(fee, tradeAmount) + String result = " (" + getPercentage(fee, tradeAmount) +
" " + Res.get("guiUtil.ofTradeAmount") + ")"; " " + Res.get("guiUtil.ofTradeAmount") + ")";
if (fee.compareTo(minFee) <= 0) {
result = " " + Res.get("guiUtil.requiredMinimum");
}
return result; return result;
} }

View file

@ -67,6 +67,11 @@ public class TradesChartsViewModelTest {
false, false,
0, 0,
0, 0,
0,
0,
0,
0,
0,
"XMR", "XMR",
"EUR", "EUR",
null, null,
@ -79,9 +84,6 @@ public class TradesChartsViewModelTest {
0, 0,
0, 0,
0, 0,
0,
0,
0,
false, false,
false, false,
0, 0,

View file

@ -605,6 +605,11 @@ public class OfferBookViewModelTest {
false, false,
0, 0,
0, 0,
0,
0,
0,
0,
0,
"BTC", "BTC",
tradeCurrencyCode, tradeCurrencyCode,
paymentMethodId, paymentMethodId,
@ -617,9 +622,6 @@ public class OfferBookViewModelTest {
0, 0,
0, 0,
0, 0,
0,
0,
0,
false, false,
false, false,
0, 0,

View file

@ -60,9 +60,11 @@ public class OfferMaker {
public static final Property<Offer, PubKeyRing> pubKeyRing = newProperty(); public static final Property<Offer, PubKeyRing> pubKeyRing = newProperty();
public static final Property<Offer, Long> blockHeight = newProperty(); public static final Property<Offer, Long> blockHeight = newProperty();
public static final Property<Offer, Long> txFee = newProperty(); public static final Property<Offer, Long> txFee = newProperty();
public static final Property<Offer, Long> makerFee = newProperty(); public static final Property<Offer, Double> makerFeePct = newProperty();
public static final Property<Offer, Long> buyerSecurityDeposit = newProperty(); public static final Property<Offer, Double> takerFeePct = newProperty();
public static final Property<Offer, Long> sellerSecurityDeposit = newProperty(); public static final Property<Offer, Double> penaltyFeePct = newProperty();
public static final Property<Offer, Double> buyerSecurityDepositPct = newProperty();
public static final Property<Offer, Double> sellerSecurityDepositPct = newProperty();
public static final Property<Offer, Long> tradeLimit = newProperty(); public static final Property<Offer, Long> tradeLimit = newProperty();
public static final Property<Offer, Long> maxTradePeriod = newProperty(); public static final Property<Offer, Long> maxTradePeriod = newProperty();
public static final Property<Offer, Long> lowerClosePrice = newProperty(); public static final Property<Offer, Long> lowerClosePrice = newProperty();
@ -80,6 +82,11 @@ public class OfferMaker {
lookup.valueOf(useMarketBasedPrice, false), lookup.valueOf(useMarketBasedPrice, false),
lookup.valueOf(amount, 100000L), lookup.valueOf(amount, 100000L),
lookup.valueOf(minAmount, 100000L), lookup.valueOf(minAmount, 100000L),
lookup.valueOf(makerFeePct, .0015),
lookup.valueOf(takerFeePct, .0075),
lookup.valueOf(penaltyFeePct, 0.03),
lookup.valueOf(buyerSecurityDepositPct, .15),
lookup.valueOf(sellerSecurityDepositPct, .15),
lookup.valueOf(baseCurrencyCode, "XMR"), lookup.valueOf(baseCurrencyCode, "XMR"),
lookup.valueOf(counterCurrencyCode, "USD"), lookup.valueOf(counterCurrencyCode, "USD"),
lookup.valueOf(paymentMethodId, "SEPA"), lookup.valueOf(paymentMethodId, "SEPA"),
@ -92,9 +99,6 @@ public class OfferMaker {
null, null,
"2", "2",
lookup.valueOf(blockHeight, 700000L), lookup.valueOf(blockHeight, 700000L),
lookup.valueOf(makerFee, 1000L),
lookup.valueOf(buyerSecurityDeposit, 10000L),
lookup.valueOf(sellerSecurityDeposit, 10000L),
lookup.valueOf(tradeLimit, 0L), lookup.valueOf(tradeLimit, 0L),
lookup.valueOf(maxTradePeriod, 0L), lookup.valueOf(maxTradePeriod, 0L),
false, false,

View file

@ -103,30 +103,19 @@ public class GUIUtilTest {
} }
@Test @Test
public void percentageOfTradeAmount_higherFeeAsMin() { public void percentageOfTradeAmount1() {
BigInteger fee = BigInteger.valueOf(200000000L); BigInteger fee = BigInteger.valueOf(200000000L);
BigInteger min = BigInteger.valueOf(100000000L);
assertEquals(" (0.02% of trade amount)", GUIUtil.getPercentageOfTradeAmount(fee, HavenoUtils.xmrToAtomicUnits(1.0), min)); assertEquals(" (0.02% of trade amount)", GUIUtil.getPercentageOfTradeAmount(fee, HavenoUtils.xmrToAtomicUnits(1.0)));
} }
@Test @Test
public void percentageOfTradeAmount_minFee() { public void percentageOfTradeAmount2() {
BigInteger fee = BigInteger.valueOf(100000000L);
BigInteger min = BigInteger.valueOf(100000000L);
assertEquals(" (required minimum)",
GUIUtil.getPercentageOfTradeAmount(fee, HavenoUtils.xmrToAtomicUnits(1.0), min));
}
@Test
public void percentageOfTradeAmount_minFeeZERO() {
BigInteger fee = BigInteger.valueOf(100000000L); BigInteger fee = BigInteger.valueOf(100000000L);
assertEquals(" (0.01% of trade amount)", assertEquals(" (0.01% of trade amount)",
GUIUtil.getPercentageOfTradeAmount(fee, HavenoUtils.xmrToAtomicUnits(1.0), BigInteger.ZERO)); GUIUtil.getPercentageOfTradeAmount(fee, HavenoUtils.xmrToAtomicUnits(1.0)));
} }
} }

View file

@ -538,28 +538,30 @@ message OfferInfo {
double market_price_margin_pct = 5; double market_price_margin_pct = 5;
uint64 amount = 6 [jstype = JS_STRING]; uint64 amount = 6 [jstype = JS_STRING];
uint64 min_amount = 7 [jstype = JS_STRING]; uint64 min_amount = 7 [jstype = JS_STRING];
string volume = 8; double maker_fee_pct = 8;
string min_volume = 9; double taker_fee_pct = 9;
double buyer_security_deposit_pct = 10; double penalty_fee_pct = 10;
double seller_security_deposit_pct = 11; double buyer_security_deposit_pct = 11;
string trigger_price = 12; double seller_security_deposit_pct = 12;
string payment_account_id = 13; string volume = 13;
string payment_method_id = 14; string min_volume = 14;
string payment_method_short_name = 15; string trigger_price = 15;
string base_currency_code = 16; string payment_account_id = 16;
string counter_currency_code = 17; string payment_method_id = 17;
uint64 date = 18; string payment_method_short_name = 18;
string state = 19; string base_currency_code = 19;
uint64 maker_fee = 20 [jstype = JS_STRING]; string counter_currency_code = 20;
bool is_activated = 21; uint64 date = 21;
bool is_my_offer = 22; string state = 22;
string owner_node_address = 23; bool is_activated = 23;
string pub_key_ring = 24; bool is_my_offer = 24;
string version_nr = 25; string owner_node_address = 25;
int32 protocol_version = 26; string pub_key_ring = 26;
string arbitrator_signer = 27; string version_nr = 27;
string split_output_tx_hash = 28; int32 protocol_version = 28;
uint64 split_output_tx_fee = 29 [jstype = JS_STRING]; string arbitrator_signer = 29;
string split_output_tx_hash = 30;
uint64 split_output_tx_fee = 31 [jstype = JS_STRING];
} }
message AvailabilityResultWithDescription { message AvailabilityResultWithDescription {
@ -847,39 +849,40 @@ message TradeInfo {
string short_id = 3; string short_id = 3;
uint64 date = 4; uint64 date = 4;
string role = 5; string role = 5;
uint64 taker_fee = 6 [jstype = JS_STRING]; uint64 amount = 6 [jstype = JS_STRING];
uint64 amount = 7 [jstype = JS_STRING]; uint64 maker_fee = 7 [jstype = JS_STRING];
uint64 buyer_security_deposit = 8 [jstype = JS_STRING]; uint64 taker_fee = 8 [jstype = JS_STRING];
uint64 seller_security_deposit = 9 [jstype = JS_STRING]; uint64 buyer_security_deposit = 9 [jstype = JS_STRING];
uint64 buyer_deposit_tx_fee = 10 [jstype = JS_STRING]; uint64 seller_security_deposit = 10 [jstype = JS_STRING];
uint64 seller_deposit_tx_fee = 11 [jstype = JS_STRING]; uint64 buyer_deposit_tx_fee = 11 [jstype = JS_STRING];
uint64 buyer_payout_tx_fee = 12 [jstype = JS_STRING]; uint64 seller_deposit_tx_fee = 12 [jstype = JS_STRING];
uint64 seller_payout_tx_fee = 13 [jstype = JS_STRING]; uint64 buyer_payout_tx_fee = 13 [jstype = JS_STRING];
uint64 buyer_payout_amount = 14 [jstype = JS_STRING]; uint64 seller_payout_tx_fee = 14 [jstype = JS_STRING];
uint64 seller_payout_amount = 15 [jstype = JS_STRING]; uint64 buyer_payout_amount = 15 [jstype = JS_STRING];
string price = 16; uint64 seller_payout_amount = 16 [jstype = JS_STRING];
string arbitrator_node_address = 17; string price = 17;
string trade_peer_node_address = 18; string arbitrator_node_address = 18;
string state = 19; string trade_peer_node_address = 19;
string phase = 20; string state = 20;
string period_state = 21; string phase = 21;
string payout_state = 22; string period_state = 22;
string dispute_state = 23; string payout_state = 23;
bool is_deposits_published = 24; string dispute_state = 24;
bool is_deposits_confirmed = 25; bool is_deposits_published = 25;
bool is_deposits_unlocked = 26; bool is_deposits_confirmed = 26;
bool is_payment_sent = 27; bool is_deposits_unlocked = 27;
bool is_payment_received = 28; bool is_payment_sent = 28;
bool is_payout_published = 29; bool is_payment_received = 29;
bool is_payout_confirmed = 30; bool is_payout_published = 30;
bool is_payout_unlocked = 31; bool is_payout_confirmed = 31;
bool is_completed = 32; bool is_payout_unlocked = 32;
string contract_as_json = 33; bool is_completed = 33;
ContractInfo contract = 34; string contract_as_json = 34;
string trade_volume = 35; ContractInfo contract = 35;
string maker_deposit_tx_id = 36; string trade_volume = 36;
string taker_deposit_tx_id = 37; string maker_deposit_tx_id = 37;
string payout_tx_id = 38; string taker_deposit_tx_id = 38;
string payout_tx_id = 39;
} }
message ContractInfo { message ContractInfo {

View file

@ -226,26 +226,25 @@ message PrefixedSealedAndSignedMessage {
} }
message InitTradeRequest { message InitTradeRequest {
string trade_id = 1; string offer_id = 1;
NodeAddress sender_node_address = 2; NodeAddress sender_node_address = 2;
PubKeyRing pub_key_ring = 3; PubKeyRing pub_key_ring = 3;
int64 trade_amount = 4; int64 trade_amount = 4;
int64 trade_price = 5; int64 trade_price = 5;
int64 trade_fee = 6; string account_id = 6;
string account_id = 7; string payment_account_id = 7;
string payment_account_id = 8; string payment_method_id = 8;
string payment_method_id = 9; string uid = 9;
string uid = 10; bytes account_age_witness_signature_of_offer_id = 10;
bytes account_age_witness_signature_of_offer_id = 11; int64 current_date = 11;
int64 current_date = 12; NodeAddress maker_node_address = 12;
NodeAddress maker_node_address = 13; NodeAddress taker_node_address = 13;
NodeAddress taker_node_address = 14; NodeAddress arbitrator_node_address = 14;
NodeAddress arbitrator_node_address = 15; string reserve_tx_hash = 15;
string reserve_tx_hash = 16; string reserve_tx_hex = 16;
string reserve_tx_hex = 17; string reserve_tx_key = 17;
string reserve_tx_key = 18; string payout_address = 18;
string payout_address = 19; bytes maker_signature = 19;
bytes maker_signature = 20;
} }
message InitMultisigRequest { message InitMultisigRequest {
@ -623,29 +622,31 @@ message OfferPayload {
bool use_market_based_price = 8; bool use_market_based_price = 8;
int64 amount = 9; int64 amount = 9;
int64 min_amount = 10; int64 min_amount = 10;
string base_currency_code = 11; double maker_fee_pct = 11;
string counter_currency_code = 12; double taker_fee_pct = 12;
string payment_method_id = 13; double penalty_fee_pct = 13;
string maker_payment_account_id = 14; double buyer_security_deposit_pct = 14;
string country_code = 15; double seller_security_deposit_pct = 15;
repeated string accepted_country_codes = 16; string base_currency_code = 16;
string bank_id = 17; string counter_currency_code = 17;
repeated string accepted_bank_ids = 18; string payment_method_id = 18;
string version_nr = 19; string maker_payment_account_id = 19;
int64 block_height_at_offer_creation = 20; string country_code = 20;
int64 maker_fee = 21; repeated string accepted_country_codes = 21;
double buyer_security_deposit_pct = 22; string bank_id = 22;
double seller_security_deposit_pct = 23; repeated string accepted_bank_ids = 23;
int64 max_trade_limit = 24; string version_nr = 24;
int64 max_trade_period = 25; int64 block_height_at_offer_creation = 25;
bool use_auto_close = 26; int64 max_trade_limit = 26;
bool use_re_open_after_auto_close = 27; int64 max_trade_period = 27;
int64 lower_close_price = 28; bool use_auto_close = 28;
int64 upper_close_price = 29; bool use_re_open_after_auto_close = 29;
bool is_private_offer = 30; int64 lower_close_price = 30;
string hash_of_challenge = 31; int64 upper_close_price = 31;
map<string, string> extra_data = 32; bool is_private_offer = 32;
int32 protocol_version = 33; string hash_of_challenge = 33;
map<string, string> extra_data = 34;
int32 protocol_version = 35;
NodeAddress arbitrator_signer = 1001; NodeAddress arbitrator_signer = 1001;
bytes arbitrator_signature = 1002; bytes arbitrator_signature = 1002;
@ -1496,29 +1497,28 @@ message Trade {
string payout_tx_hex = 4; string payout_tx_hex = 4;
string payout_tx_key = 5; string payout_tx_key = 5;
int64 amount = 6; int64 amount = 6;
int64 taker_fee = 8; int64 take_offer_date = 7;
int64 take_offer_date = 9; int64 price = 8;
int64 price = 10; State state = 9;
State state = 11; PayoutState payout_state = 10;
PayoutState payout_state = 12; DisputeState dispute_state = 11;
DisputeState dispute_state = 13; TradePeriodState period_state = 12;
TradePeriodState period_state = 14; Contract contract = 13;
Contract contract = 15; string contract_as_json = 14;
string contract_as_json = 16; bytes contract_hash = 15;
bytes contract_hash = 17; NodeAddress arbitrator_node_address = 16;
NodeAddress arbitrator_node_address = 18; NodeAddress mediator_node_address = 17;
NodeAddress mediator_node_address = 19; string error_message = 18;
string error_message = 20; string counter_currency_tx_id = 19;
string counter_currency_tx_id = 21; repeated ChatMessage chat_message = 20;
repeated ChatMessage chat_message = 22; MediationResultState mediation_result_state = 21;
MediationResultState mediation_result_state = 23; int64 lock_time = 22;
int64 lock_time = 24; int64 start_time = 23;
int64 start_time = 25; NodeAddress refund_agent_node_address = 24;
NodeAddress refund_agent_node_address = 26; RefundResultState refund_result_state = 25;
RefundResultState refund_result_state = 27; string counter_currency_extra_data = 26;
string counter_currency_extra_data = 28; string uid = 27;
string uid = 29; bool is_completed = 28;
bool is_completed = 30;
} }
message BuyerAsMakerTrade { message BuyerAsMakerTrade {