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) -> {
var isMyOffer = t.getOffer().getIsMyOffer();
if (isMyOffer) {
return t.getOffer().getMakerFee();
return t.getMakerFee();
} else {
return t.getTakerFee();
}
@ -198,7 +198,7 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
protected final Function<TradeInfo, Long> toMyMakerOrTakerFee = (t) -> {
return isTaker.test(t)
? t.getTakerFee()
: t.getOffer().getMakerFee();
: t.getMakerFee();
};
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
BigInteger takerFee;
BigInteger fundsNeededForTrade;
synchronized (takeOfferModel) {
takeOfferModel.initModel(offer, paymentAccount, amount, useSavingsWallet);
takerFee = takeOfferModel.getTakerFee();
fundsNeededForTrade = takeOfferModel.getFundsNeededForTrade();
log.debug("Initiating take {} offer, {}", offer.isBuyOffer() ? "buy" : "sell", takeOfferModel);
}
// take offer
tradeManager.onTakeOffer(amount,
takerFee,
fundsNeededForTrade,
offer,
paymentAccountId,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,6 +22,7 @@ import haveno.common.taskrunner.TaskRunner;
import haveno.core.offer.Offer;
import haveno.core.offer.OfferDirection;
import haveno.core.offer.placeoffer.PlaceOfferModel;
import haveno.core.trade.HavenoUtils;
import haveno.core.xmr.model.XmrAddressEntry;
import lombok.extern.slf4j.Slf4j;
import monero.daemon.model.MoneroOutput;
@ -50,13 +51,14 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
model.getXmrWalletService().getConnectionService().verifyConnection();
// 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 securityDeposit = offer.getDirection() == OfferDirection.BUY ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit();
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);
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?
if (!model.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).isPresent()) {

View File

@ -44,11 +44,12 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
// Coins
checkBINotNullOrZero(offer.getAmount(), "Amount");
checkBINotNullOrZero(offer.getMinAmount(), "MinAmount");
checkBINotNullOrZero(offer.getMakerFee(), "MakerFee");
//checkCoinNotNullOrZero(offer.getTxFee(), "txFee"); // TODO: remove from data model
checkBINotNullOrZero(offer.getMaxTradeLimit(), "MaxTradeLimit");
if (offer.getBuyerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Buyer security deposit must be positive but was " + offer.getBuyerSecurityDepositPct());
if (offer.getSellerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Seller security deposit must be positive but was " + offer.getSellerSecurityDepositPct());
if (offer.getMakerFeePct() < 0) throw new IllegalArgumentException("Maker fee must be >= 0% but was " + offer.getMakerFeePct());
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.
/*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.getMinAmount(), "MinAmount is null");
checkNotNull(offer.getPrice(), "Price is null");
checkNotNull(offer.getMakerFee(), "MakerFee is null");
checkNotNull(offer.getVersionNr(), "VersionNr is null");
checkArgument(offer.getMaxTradePeriod() > 0,
"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) {
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
? offer.getOfferPayload().getBuyerSecurityDepositForTradeAmount(amount)
: offer.getOfferPayload().getSellerSecurityDepositForTradeAmount(amount);
this.takerFee = HavenoUtils.getTakerFee(amount);
this.takerFee = HavenoUtils.multiply(amount, offer.getTakerFeePct());
calculateVolume();
calculateTotalToPay();

View File

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

View File

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

View File

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

View File

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

View File

@ -106,7 +106,7 @@ public class CleanupMailboxMessages {
}
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) {

View File

@ -105,7 +105,7 @@ public class CleanupMailboxMessagesService {
}
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) {

View File

@ -34,11 +34,9 @@ import haveno.core.trade.messages.InitTradeRequest;
import haveno.core.trade.messages.PaymentReceivedMessage;
import haveno.core.trade.messages.PaymentSentMessage;
import haveno.core.util.JsonUtil;
import haveno.core.util.ParsingUtils;
import haveno.network.p2p.NodeAddress;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.URI;
import java.security.PrivateKey;
import java.text.DecimalFormat;
@ -48,7 +46,6 @@ import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
import javax.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import monero.common.MoneroRpcConnection;
import monero.wallet.model.MoneroDestination;
@ -62,12 +59,17 @@ import org.bitcoinj.core.Coin;
@Slf4j
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"
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 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
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;
@ -125,7 +127,7 @@ public class HavenoUtils {
}
public static long atomicUnitsToCentineros(long atomicUnits) {
return atomicUnits / CENTINEROS_AU_MULTIPLIER;
return atomicUnitsToCentineros(BigInteger.valueOf(atomicUnits));
}
public static long atomicUnitsToCentineros(BigInteger atomicUnits) {
@ -149,7 +151,7 @@ public class HavenoUtils {
}
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) {
@ -161,7 +163,11 @@ public class HavenoUtils {
}
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 ---------------------------------
@ -221,56 +227,12 @@ public class HavenoUtils {
public static BigInteger parseXmr(String input) {
if (input == null || input.length() == 0) return BigInteger.ZERO;
try {
return xmrToAtomicUnits(new BigDecimal(ParsingUtils.parseNumberStringToDouble(input)).doubleValue());
return new BigDecimal(input).multiply(new BigDecimal(XMR_AU_MULTIPLIER)).toBigInteger();
} catch (Exception e) {
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 -------------------------
public static byte[] sign(KeyRing keyRing, String message) {
@ -351,12 +313,11 @@ public class HavenoUtils {
// re-create trade request with signed fields
InitTradeRequest signedRequest = new InitTradeRequest(
request.getTradeId(),
request.getOfferId(),
request.getSenderNodeAddress(),
request.getPubKeyRing(),
request.getTradeAmount(),
request.getTradePrice(),
request.getTradeFee(),
request.getAccountId(),
request.getPaymentAccountId(),
request.getPaymentMethodId(),
@ -380,7 +341,7 @@ public class HavenoUtils {
// verify maker signature
boolean isSignatureValid = isSignatureValid(makerPubKeyRing, tradeRequestAsJson, signature);
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("Maker pub key ring: " + (makerPubKeyRing == null ? null : "..."));
log.warn("Maker signature: " + (signature == null ? null : Utilities.bytesAsHexString(signature)));
@ -413,7 +374,7 @@ public class HavenoUtils {
}
// 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
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
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,
BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice,
XmrWalletService xmrWalletService,
ProcessModel processModel,
@ -48,7 +47,6 @@ public final class SellerAsMakerTrade extends SellerTrade implements MakerTrade
@Nullable NodeAddress arbitratorNodeAddress) {
super(offer,
tradeAmount,
takerFee,
tradePrice,
xmrWalletService,
processModel,
@ -83,7 +81,6 @@ public final class SellerAsMakerTrade extends SellerTrade implements MakerTrade
SellerAsMakerTrade trade = new SellerAsMakerTrade(
Offer.fromProto(proto.getOffer()),
BigInteger.valueOf(proto.getAmount()),
BigInteger.valueOf(proto.getTakerFee()),
proto.getPrice(),
xmrWalletService,
processModel,

View File

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

View File

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

View File

@ -65,7 +65,7 @@ public interface Tradable extends PersistablePayload {
}
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() {

View File

@ -345,7 +345,6 @@ public abstract class Trade implements Tradable, Model {
private final ProcessModel processModel;
@Getter
private final Offer offer;
private final long takerFee;
// Added in 1.5.1
@Getter
@ -490,7 +489,6 @@ public abstract class Trade implements Tradable, Model {
// maker
protected Trade(Offer offer,
BigInteger tradeAmount,
BigInteger takerFee,
long tradePrice,
XmrWalletService xmrWalletService,
ProcessModel processModel,
@ -500,7 +498,6 @@ public abstract class Trade implements Tradable, Model {
@Nullable NodeAddress arbitratorNodeAddress) {
this.offer = offer;
this.amount = tradeAmount.longValueExact();
this.takerFee = takerFee.longValueExact();
this.price = tradePrice;
this.xmrWalletService = xmrWalletService;
this.xmrConnectionService = xmrWalletService.getConnectionService();
@ -523,7 +520,6 @@ public abstract class Trade implements Tradable, Model {
protected Trade(Offer offer,
BigInteger tradeAmount,
BigInteger txFee,
BigInteger takerFee,
long tradePrice,
@Nullable NodeAddress mediatorNodeAddress, // TODO (woodser): remove mediator, refund agent from trade
@Nullable NodeAddress refundAgentNodeAddress,
@ -536,7 +532,6 @@ public abstract class Trade implements Tradable, Model {
this(offer,
tradeAmount,
takerFee,
tradePrice,
xmrWalletService,
processModel,
@ -552,7 +547,6 @@ public abstract class Trade implements Tradable, Model {
protected Trade(Offer offer,
BigInteger tradeAmount,
Coin txFee,
BigInteger takerFee,
long tradePrice,
NodeAddress makerNodeAddress,
NodeAddress takerNodeAddress,
@ -563,7 +557,6 @@ public abstract class Trade implements Tradable, Model {
this(offer,
tradeAmount,
takerFee,
tradePrice,
xmrWalletService,
processModel,
@ -926,7 +919,6 @@ public abstract class Trade implements Tradable, Model {
private void forceCloseWallet() {
if (wallet != null) {
log.warn("Force closing wallet for {} {}", getClass().getSimpleName(), getId());
xmrWalletService.forceCloseWallet(wallet, wallet.getPath());
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");
}
// force close wallet
// force close wallet without warning
forceCloseWallet();
// delete wallet
@ -1138,7 +1130,7 @@ public abstract class Trade implements Tradable, Model {
// 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?
MoneroTxWallet feeEstimateTx = createPayoutTx();;
MoneroTxWallet feeEstimateTx = createPayoutTx();
BigInteger feeEstimate = feeEstimateTx.getFee();
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());
@ -1912,11 +1904,11 @@ public abstract class Trade implements Tradable, Model {
}
public BigInteger getMakerFee() {
return offer.getMakerFee();
return offer.getMakerFee(getAmount());
}
public BigInteger getTakerFee() {
return BigInteger.valueOf(takerFee);
return offer.getTakerFee(getAmount());
}
public BigInteger getBuyerSecurityDepositBeforeMiningFee() {
@ -2308,7 +2300,6 @@ public abstract class Trade implements Tradable, Model {
public Message toProtoMessage() {
protobuf.Trade.Builder builder = protobuf.Trade.newBuilder()
.setOffer(offer.toProtoMessage())
.setTakerFee(takerFee)
.setTakeOfferDate(takeOfferDate)
.setProcessModel(processModel.toProtoMessage())
.setAmount(amount)
@ -2371,7 +2362,6 @@ public abstract class Trade implements Tradable, Model {
public String toString() {
return "Trade{" +
"\n offer=" + offer +
",\n takerFee=" + takerFee +
",\n totalTxFee=" + getTotalTxFee() +
",\n takeOfferDate=" + takeOfferDate +
",\n processModel=" + processModel +
@ -2389,7 +2379,6 @@ public abstract class Trade implements Tradable, Model {
",\n counterCurrencyTxId='" + counterCurrencyTxId + '\'' +
",\n counterCurrencyExtraData='" + counterCurrencyExtraData + '\'' +
",\n chatMessages=" + chatMessages +
",\n takerFee=" + takerFee +
",\n xmrWalletService=" + xmrWalletService +
",\n stateProperty=" + stateProperty +
",\n statePhaseProperty=" + phaseProperty +

View File

@ -283,7 +283,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
public void onDirectMessage(DecryptedMessageWithPubKey message, NodeAddress peer) {
NetworkEnvelope networkEnvelope = message.getNetworkEnvelope();
if (!(networkEnvelope instanceof TradeMessage)) return;
String tradeId = ((TradeMessage) networkEnvelope).getTradeId();
String tradeId = ((TradeMessage) networkEnvelope).getOfferId();
ThreadUtils.execute(() -> {
if (networkEnvelope instanceof InitTradeRequest) {
handleInitTradeRequest((InitTradeRequest) networkEnvelope, peer);
@ -532,10 +532,10 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
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 {
Validator.nonEmptyStringOf(request.getTradeId());
Validator.nonEmptyStringOf(request.getOfferId());
} catch (Throwable t) {
log.warn("Invalid InitTradeRequest message " + request.toString());
return;
@ -549,19 +549,19 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
Arbitrator thisArbitrator = user.getRegisteredArbitrator();
NodeAddress thisAddress = p2PService.getNetworkNode().getNodeAddress();
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;
}
// get offer associated with trade
Offer offer = null;
for (Offer anOffer : offerBookService.getOffers()) {
if (anOffer.getId().equals(request.getTradeId())) {
if (anOffer.getId().equals(request.getOfferId())) {
offer = anOffer;
}
}
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;
}
@ -571,7 +571,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// 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?
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;
}
@ -585,7 +585,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
if (!sender.equals(request.getMakerNodeAddress())) {
// 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);
sendAckMessage(sender, request.getPubKeyRing(), request, false, errMsg);
return;
@ -594,17 +594,13 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// verify request is from taker
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;
}
// get expected taker fee
BigInteger takerFee = HavenoUtils.getTakerFee(BigInteger.valueOf(request.getTradeAmount()));
// create arbitrator trade
trade = new ArbitratorTrade(offer,
BigInteger.valueOf(request.getTradeAmount()),
takerFee,
offer.getOfferPayload().getPrice(),
xmrWalletService,
getNewProcessModel(offer),
@ -614,7 +610,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
request.getArbitratorNodeAddress());
// set reserve tx hash if available
Optional<SignedOffer> signedOfferOptional = openOfferManager.getSignedOfferById(request.getTradeId());
Optional<SignedOffer> signedOfferOptional = openOfferManager.getSignedOfferById(request.getOfferId());
if (signedOfferOptional.isPresent()) {
SignedOffer signedOffer = signedOfferOptional.get();
trade.getMaker().setReserveTxHash(signedOffer.getReserveTxHash());
@ -637,7 +633,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// handle request as maker
else {
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(request.getTradeId());
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(request.getOfferId());
if (!openOfferOptional.isPresent()) {
return;
}
@ -652,28 +648,24 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// verify request is from arbitrator
Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(sender);
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;
}
Optional<Trade> tradeOptional = getOpenTrade(request.getTradeId());
Optional<Trade> tradeOptional = getOpenTrade(request.getOfferId());
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;
}
// reserve open offer
openOfferManager.reserveOpenOffer(openOffer);
// get expected taker fee
BigInteger takerFee = HavenoUtils.getTakerFee(BigInteger.valueOf(request.getTradeAmount()));
// initialize trade
Trade trade;
if (offer.isBuyOffer())
trade = new BuyerAsMakerTrade(offer,
BigInteger.valueOf(request.getTradeAmount()),
takerFee,
offer.getOfferPayload().getPrice(),
xmrWalletService,
getNewProcessModel(offer),
@ -684,7 +676,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
else
trade = new SellerAsMakerTrade(offer,
BigInteger.valueOf(request.getTradeAmount()),
takerFee,
offer.getOfferPayload().getPrice(),
xmrWalletService,
getNewProcessModel(offer),
@ -720,18 +711,18 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
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 {
Validator.nonEmptyStringOf(request.getTradeId());
Validator.nonEmptyStringOf(request.getOfferId());
} catch (Throwable t) {
log.warn("Invalid InitMultisigRequest " + request.toString());
return;
}
Optional<Trade> tradeOptional = getOpenTrade(request.getTradeId());
Optional<Trade> tradeOptional = getOpenTrade(request.getOfferId());
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;
}
Trade trade = tradeOptional.get();
@ -739,18 +730,18 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
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 {
Validator.nonEmptyStringOf(request.getTradeId());
Validator.nonEmptyStringOf(request.getOfferId());
} catch (Throwable t) {
log.warn("Invalid SignContractRequest message " + request.toString());
return;
}
Optional<Trade> tradeOptional = getOpenTrade(request.getTradeId());
Optional<Trade> tradeOptional = getOpenTrade(request.getOfferId());
if (!tradeOptional.isPresent()) {
log.warn("No trade with id " + request.getTradeId());
log.warn("No trade with id " + request.getOfferId());
return;
}
Trade trade = tradeOptional.get();
@ -758,18 +749,18 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
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 {
Validator.nonEmptyStringOf(request.getTradeId());
Validator.nonEmptyStringOf(request.getOfferId());
} catch (Throwable t) {
log.warn("Invalid SignContractResponse message " + request.toString());
return;
}
Optional<Trade> tradeOptional = getOpenTrade(request.getTradeId());
Optional<Trade> tradeOptional = getOpenTrade(request.getOfferId());
if (!tradeOptional.isPresent()) {
log.warn("No trade with id " + request.getTradeId());
log.warn("No trade with id " + request.getOfferId());
return;
}
Trade trade = tradeOptional.get();
@ -777,18 +768,18 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
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 {
Validator.nonEmptyStringOf(request.getTradeId());
Validator.nonEmptyStringOf(request.getOfferId());
} catch (Throwable t) {
log.warn("Invalid DepositRequest message " + request.toString());
return;
}
Optional<Trade> tradeOptional = getOpenTrade(request.getTradeId());
Optional<Trade> tradeOptional = getOpenTrade(request.getOfferId());
if (!tradeOptional.isPresent()) {
log.warn("No trade with id " + request.getTradeId());
log.warn("No trade with id " + request.getOfferId());
return;
}
Trade trade = tradeOptional.get();
@ -796,18 +787,18 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
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 {
Validator.nonEmptyStringOf(response.getTradeId());
Validator.nonEmptyStringOf(response.getOfferId());
} catch (Throwable t) {
log.warn("Invalid DepositResponse message " + response.toString());
return;
}
Optional<Trade> tradeOptional = getOpenTrade(response.getTradeId());
Optional<Trade> tradeOptional = getOpenTrade(response.getOfferId());
if (!tradeOptional.isPresent()) {
log.warn("No trade with id " + response.getTradeId());
log.warn("No trade with id " + response.getOfferId());
return;
}
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
public void onTakeOffer(BigInteger amount,
BigInteger takerFee,
BigInteger fundsNeededForTrade,
Offer offer,
String paymentAccountId,
@ -852,7 +842,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
if (offer.isBuyOffer()) {
trade = new SellerAsTakerTrade(offer,
amount,
takerFee,
model.getTradeRequest().getTradePrice(),
xmrWalletService,
getNewProcessModel(offer),
@ -863,7 +852,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
} else {
trade = new BuyerAsTakerTrade(offer,
amount,
takerFee,
model.getTradeRequest().getTradePrice(),
xmrWalletService,
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) {
// create ack message
String tradeId = message.getTradeId();
String tradeId = message.getOfferId();
String sourceUid = message.getUid();
AckMessage ackMessage = new AckMessage(P2PService.getMyNodeAddress(),
AckMessageSourceType.TRADE_MESSAGE,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -109,7 +109,7 @@ public class ArbitratorProtocol extends DisputeProtocol {
@Override
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")

View File

@ -237,7 +237,7 @@ public class FluentProtocol {
boolean isTradeIdValid = message == null || isTradeIdValid(trade.getId(), message);
if (!isTradeIdValid) {
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);
return result;
}

View File

@ -121,12 +121,12 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
///////////////////////////////////////////////////////////////////////////////////////////
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());
}
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());
}
@ -156,7 +156,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
// notify trade listeners
// 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);
}
} else if (networkEnvelope instanceof AckMessage) {
@ -862,7 +862,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
private boolean isMyMessage(NetworkEnvelope message) {
if (message instanceof TradeMessage) {
TradeMessage tradeMessage = (TradeMessage) message;
return tradeMessage.getTradeId().equals(trade.getId());
return tradeMessage.getOfferId().equals(trade.getId());
} else if (message instanceof AckMessage) {
AckMessage ackMessage = (AckMessage) message;
return ackMessage.getSourceType() == AckMessageSourceType.TRADE_MESSAGE && ackMessage.getSourceId().equals(trade.getId());

View File

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

View File

@ -21,6 +21,7 @@ import haveno.common.taskrunner.TaskRunner;
import haveno.common.util.Tuple2;
import haveno.core.offer.Offer;
import haveno.core.offer.OfferDirection;
import haveno.core.trade.HavenoUtils;
import haveno.core.trade.Trade;
import haveno.core.trade.messages.InitTradeRequest;
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
// 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 securityDeposit = isFromMaker ? isFromBuyer ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit() : isFromBuyer ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee();
Tuple2<MoneroTx, BigInteger> txResult;
try {
txResult = trade.getXmrWalletService().verifyTradeTx(
offer.getId(),
penaltyFee,
tradeFee,
sendAmount,
securityDeposit,

View File

@ -60,7 +60,6 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
request.getPubKeyRing(),
trade.getAmount().longValueExact(),
trade.getPrice().getValue(),
trade.getTakerFee().longValueExact(),
request.getAccountId(),
request.getPaymentAccountId(),
request.getPaymentMethodId(),
@ -78,7 +77,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
null);
// 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(
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(),
@ -86,7 +85,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
new SendDirectMessageListener() {
@Override
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();
}
@Override
@ -136,7 +135,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
null);
// 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(
trade.getMaker().getNodeAddress(),
trade.getMaker().getPubKeyRing(),
@ -144,7 +143,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
new SendDirectMessageListener() {
@Override
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
public void onFault(String errorMessage) {
@ -154,7 +153,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
);
// 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(
trade.getTaker().getNodeAddress(),
trade.getTaker().getPubKeyRing(),
@ -162,7 +161,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask {
new SendDirectMessageListener() {
@Override
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
public void onFault(String errorMessage) {

View File

@ -57,7 +57,6 @@ public class MakerSendInitTradeRequest extends TradeTask {
processModel.getPubKeyRing(),
trade.getAmount().longValueExact(),
trade.getPrice().getValue(),
offer.getMakerFee().longValueExact(),
trade.getProcessModel().getAccountId(),
offer.getMakerPaymentAccountId(),
offer.getOfferPayload().getPaymentMethodId(),
@ -75,7 +74,7 @@ public class MakerSendInitTradeRequest extends TradeTask {
null);
// 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(
trade.getArbitrator().getNodeAddress(),
trade.getArbitrator().getPubKeyRing(),

View File

@ -150,7 +150,7 @@ public class ProcessInitMultisigRequest extends TradeTask {
sendInitMultisigRequest(peer1Address, peer1PubKeyRing, new SendDirectMessageListener() {
@Override
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;
if (ack1 && ack2) completeAux();
}
@ -166,7 +166,7 @@ public class ProcessInitMultisigRequest extends TradeTask {
sendInitMultisigRequest(peer2Address, peer2PubKeyRing, new SendDirectMessageListener() {
@Override
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;
if (ack1 && ack2) completeAux();
}
@ -212,7 +212,7 @@ public class ProcessInitMultisigRequest extends TradeTask {
trade.getSelf().getMadeMultisigHex(),
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);
}

View File

@ -69,20 +69,20 @@ public abstract class SendMailboxMessageTask extends TradeTask {
new SendMailboxMessageListener() {
@Override
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();
if (!task.isCompleted()) complete();
}
@Override
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();
}
@Override
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);
}
}

View File

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

View File

@ -117,12 +117,11 @@ public class TakerSendInitTradeRequestToArbitrator extends TradeTask {
// create request to arbitrator
InitTradeRequest makerRequest = (InitTradeRequest) processModel.getTradeMessage(); // taker's InitTradeRequest to maker
InitTradeRequest arbitratorRequest = new InitTradeRequest(
makerRequest.getTradeId(),
makerRequest.getOfferId(),
makerRequest.getSenderNodeAddress(),
makerRequest.getPubKeyRing(),
makerRequest.getTradeAmount(),
makerRequest.getTradePrice(),
makerRequest.getTradeFee(),
makerRequest.getAccountId(),
makerRequest.getPaymentAccountId(),
makerRequest.getPaymentMethodId(),
@ -140,7 +139,7 @@ public class TakerSendInitTradeRequestToArbitrator extends TradeTask {
processModel.getMakerSignature());
// 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(
arbitratorNodeAddress,
arbitrator.getPubKeyRing(),

View File

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

View File

@ -56,6 +56,6 @@ public class Validator {
}
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
* 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 sendAmount amount to give peer
* @param sendAmount amount to send peer
* @param securityDeposit security deposit amount
* @param returnAddress return address for reserved funds
* @param reserveExactAmount specifies to reserve the exact input amount
* @param preferredSubaddressIndex preferred source subaddress to spend from (optional)
* @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);
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);
return reserveTx;
}
@ -568,18 +569,18 @@ public class XmrWalletService {
// create deposit tx
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 securityDeposit = trade instanceof BuyerTrade ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee();
long time = System.currentTimeMillis();
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);
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) {
MoneroWallet wallet = getWallet();
@ -604,27 +605,31 @@ public class XmrWalletService {
// first try preferred subaddressess
for (int i = 0; i < subaddressIndices.size(); i++) {
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) {
if (i == subaddressIndices.size() - 1 && reserveExactAmount) throw e; // throw if no subaddress with exact output
}
}
// 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
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)
.setSubaddressIndices(subaddressIndex)
.addDestination(HavenoUtils.getTradeFeeAddress(), tradeFee)
.addDestination(address, sendAmount.add(securityDeposit))
.setSubtractFeeFrom(1)
.setPriority(XmrWalletService.PROTOCOL_FEE_PRIORITY)); // pay fee from security deposit
.addDestination(address, transferAmount)
.setSubtractFeeFrom(0) // pay fee from transfer amount
.setPriority(XmrWalletService.PROTOCOL_FEE_PRIORITY);
if (!BigInteger.valueOf(0).equals(feeAmount)) txConfig.addDestination(HavenoUtils.getTradeFeeAddress(), feeAmount);
MoneroTxWallet tradeTx = wallet.createTx(txConfig);
// freeze inputs
List<String> keyImages = new ArrayList<String>();
@ -639,6 +644,7 @@ public class XmrWalletService {
* The transaction is submitted to the pool then flushed without relaying.
*
* @param offerId id of offer to verify trade tx
* @param penaltyFee penalty fee for breaking protocol
* @param tradeFee trade fee
* @param sendAmount amount to give peer
* @param securityDeposit security deposit amount
@ -647,9 +653,9 @@ public class XmrWalletService {
* @param txHex transaction hex
* @param txKey transaction key
* @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");
MoneroDaemonRpc daemon = getDaemon();
MoneroWallet wallet = getWallet();
@ -681,39 +687,45 @@ public class XmrWalletService {
if (!BigInteger.ZERO.equals(tx.getUnlockTime())) throw new RuntimeException("Unlock height must be 0");
// verify miner fee
BigInteger feeEstimate = getElevatedFeeEstimate(tx.getWeight());
double feeDiff = tx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.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());
log.info("Trade tx fee {} is within tolerance, diff%={}", tx.getFee(), feeDiff);
BigInteger minerFeeEstimate = getElevatedFeeEstimate(tx.getWeight());
double minerFeeDiff = tx.getFee().subtract(minerFeeEstimate).abs().doubleValue() / minerFeeEstimate.doubleValue();
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(), minerFeeDiff);
// verify transfer proof to fee address
MoneroCheckTx tradeFeeCheck = wallet.checkTxKey(txHash, txKey, HavenoUtils.getTradeFeeAddress());
if (!tradeFeeCheck.isGood()) throw new RuntimeException("Invalid proof to trade fee address");
// verify proof to fee address
MoneroCheckTx feeCheck = wallet.checkTxKey(txHash, txKey, HavenoUtils.getTradeFeeAddress());
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);
if (!transferCheck.isGood()) throw new RuntimeException("Invalid proof to transfer address");
// collect actual trade fee, send amount, and security deposit
BigInteger actualTradeFee = tradeFeeCheck.getReceivedAmount();
actualSecurityDeposit = transferCheck.getReceivedAmount().subtract(sendAmount);
BigInteger actualSendAmount = transferCheck.getReceivedAmount().subtract(actualSecurityDeposit);
// verify fee and transfer amounts
BigInteger actualFee = feeCheck.getReceivedAmount();
BigInteger actualTransferAmount = transferCheck.getReceivedAmount();
boolean isDepositTx = penaltyFee == null;
if (isDepositTx) {
// verify trade fee
if (actualTradeFee.compareTo(tradeFee) < 0) {
throw new RuntimeException("Insufficient trade fee, expected=" + tradeFee + ", actual=" + actualTradeFee + ", transfer address check=" + JsonUtils.serialize(transferCheck) + ", trade fee address check=" + JsonUtils.serialize(tradeFeeCheck));
// verify trade fee
if (!actualFee.equals(tradeFee)) throw new RuntimeException("Invalid trade fee amount, expected " + tradeFee + " but was " + actualFee);
// 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
if (!actualSendAmount.equals(sendAmount)) {
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);
}
// return the result
return new Tuple2<>(tx, actualSecurityDeposit);
} catch (Exception e) {
log.warn("Error verifying trade tx with offer id=" + offerId + (tx == null ? "" : ", tx=" + tx) + ": " + e.getMessage());
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;
}
}
return new Tuple2<>(tx, actualSecurityDeposit);
}
}

View File

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

View File

@ -31,11 +31,26 @@ import static org.junit.jupiter.api.Assertions.fail;
public class CoinUtilTest {
@Test
public void testGetFeePerBtc() {
assertEquals(HavenoUtils.xmrToAtomicUnits(1), HavenoUtils.getFeePerXmr(HavenoUtils.xmrToAtomicUnits(1), HavenoUtils.xmrToAtomicUnits(1)));
assertEquals(HavenoUtils.xmrToAtomicUnits(0.1), HavenoUtils.getFeePerXmr(HavenoUtils.xmrToAtomicUnits(0.1), HavenoUtils.xmrToAtomicUnits(1)));
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)));
public void testGetPercentOfAmount() {
BigInteger bi = new BigInteger("703100000000");
assertEquals(new BigInteger("105465000000"), HavenoUtils.multiply(bi, .15));
}
@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

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
// 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
final BigInteger makerFee = getMakerFee();
final BigInteger makerFee = getMaxMakerFee();
if (direction != null && amount.get() != null && makerFee != null) {
BigInteger feeAndSecDeposit = getSecurityDeposit().add(makerFee);
BigInteger total = isBuyOffer() ? feeAndSecDeposit : feeAndSecDeposit.add(amount.get());
@ -677,8 +677,8 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
this.marketPriceAvailable = marketPriceAvailable;
}
public BigInteger getMakerFee() {
return HavenoUtils.getMakerFee(amount.get());
public BigInteger getMaxMakerFee() {
return HavenoUtils.multiply(amount.get(), HavenoUtils.MAKER_FEE_PCT);
}
boolean canPlaceOffer() {

View File

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

View File

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

View File

@ -618,10 +618,6 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
return true;
}
public String getMakerFeeAsString(Offer offer) {
return HavenoUtils.formatXmr(offer.getMakerFee(), true);
}
private static String getDirectionWithCodeDetailed(OfferDirection direction, String currencyCode) {
if (CurrencyUtil.isTraditionalCurrency(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");
} else {
tradeManager.onTakeOffer(amount.get(),
getTakerFee(),
fundsNeededForTrade,
offer,
paymentAccount.getId(),
@ -404,7 +403,7 @@ class TakeOfferDataModel extends OfferDataModel {
@Nullable
BigInteger getTakerFee() {
return HavenoUtils.getTakerFee(this.amount.get());
return HavenoUtils.multiply(this.amount.get(), offer.getTakerFeePct());
}
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.CoinUtil;
import haveno.core.util.validation.InputValidator;
import haveno.core.xmr.wallet.Restrictions;
import haveno.desktop.Navigation;
import haveno.desktop.common.model.ActivatableWithDataModel;
import haveno.desktop.common.model.ViewModel;
@ -656,9 +655,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil,
dataModel.getSecurityDeposit(),
dataModel.getAmount().get(),
xmrFormatter,
Restrictions.getMinBuyerSecurityDeposit()
);
xmrFormatter);
}
public String getSecurityDepositWithCode() {
@ -669,8 +666,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
return OfferViewModelUtil.getTradeFeeWithFiatEquivalentAndPercentage(offerUtil,
dataModel.getTakerFee(),
dataModel.getAmount().get(),
xmrFormatter,
HavenoUtils.getMinMakerFee());
xmrFormatter);
}
public String getTakerFeePercentage() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -60,9 +60,11 @@ public class OfferMaker {
public static final Property<Offer, PubKeyRing> pubKeyRing = newProperty();
public static final Property<Offer, Long> blockHeight = newProperty();
public static final Property<Offer, Long> txFee = newProperty();
public static final Property<Offer, Long> makerFee = newProperty();
public static final Property<Offer, Long> buyerSecurityDeposit = newProperty();
public static final Property<Offer, Long> sellerSecurityDeposit = newProperty();
public static final Property<Offer, Double> makerFeePct = newProperty();
public static final Property<Offer, Double> takerFeePct = 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> maxTradePeriod = newProperty();
public static final Property<Offer, Long> lowerClosePrice = newProperty();
@ -80,6 +82,11 @@ public class OfferMaker {
lookup.valueOf(useMarketBasedPrice, false),
lookup.valueOf(amount, 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(counterCurrencyCode, "USD"),
lookup.valueOf(paymentMethodId, "SEPA"),
@ -92,9 +99,6 @@ public class OfferMaker {
null,
"2",
lookup.valueOf(blockHeight, 700000L),
lookup.valueOf(makerFee, 1000L),
lookup.valueOf(buyerSecurityDeposit, 10000L),
lookup.valueOf(sellerSecurityDeposit, 10000L),
lookup.valueOf(tradeLimit, 0L),
lookup.valueOf(maxTradePeriod, 0L),
false,

View File

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

View File

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