From 81da6fbe5aed21446aa4106cecba9db28d04a819 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sat, 12 Jun 2021 18:35:27 -0300
Subject: [PATCH 01/40] Refactor EditOfferDataModel for new editoffer api
method
- Define set of editable offer payload fields in MutableOfferPayloadFields.
- Move bulk of EditOfferDataModel#onPublishOffer logic to OfferUtil.
---
.../core/offer/MutableOfferPayloadFields.java | 89 +++++++++++++++++++
.../main/java/bisq/core/offer/OfferUtil.java | 47 ++++++++++
.../editoffer/EditOfferDataModel.java | 49 +---------
3 files changed, 140 insertions(+), 45 deletions(-)
create mode 100644 core/src/main/java/bisq/core/offer/MutableOfferPayloadFields.java
diff --git a/core/src/main/java/bisq/core/offer/MutableOfferPayloadFields.java b/core/src/main/java/bisq/core/offer/MutableOfferPayloadFields.java
new file mode 100644
index 00000000000..56700fd63aa
--- /dev/null
+++ b/core/src/main/java/bisq/core/offer/MutableOfferPayloadFields.java
@@ -0,0 +1,89 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.core.offer;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import javax.annotation.Nullable;
+
+/**
+ * The set of editable OfferPayload fields.
+ */
+@Getter
+@Setter
+@ToString
+public final class MutableOfferPayloadFields {
+
+ private final long price;
+ private final double marketPriceMargin;
+ private final boolean useMarketBasedPrice;
+ private final String baseCurrencyCode;
+ private final String counterCurrencyCode;
+ private final String paymentMethodId;
+ private final String makerPaymentAccountId;
+ @Nullable
+ private final String countryCode;
+ @Nullable
+ private final List acceptedCountryCodes;
+ @Nullable
+ private final String bankId;
+ @Nullable
+ private final List acceptedBankIds;
+
+ public MutableOfferPayloadFields(OfferPayload offerPayload) {
+ this(offerPayload.getPrice(),
+ offerPayload.getMarketPriceMargin(),
+ offerPayload.isUseMarketBasedPrice(),
+ offerPayload.getBaseCurrencyCode(),
+ offerPayload.getCounterCurrencyCode(),
+ offerPayload.getPaymentMethodId(),
+ offerPayload.getMakerPaymentAccountId(),
+ offerPayload.getCountryCode(),
+ offerPayload.getAcceptedCountryCodes(),
+ offerPayload.getBankId(),
+ offerPayload.getAcceptedBankIds());
+ }
+
+ public MutableOfferPayloadFields(long price,
+ double marketPriceMargin,
+ boolean useMarketBasedPrice,
+ String baseCurrencyCode,
+ String counterCurrencyCode,
+ String paymentMethodId,
+ String makerPaymentAccountId,
+ @Nullable String countryCode,
+ @Nullable List acceptedCountryCodes,
+ @Nullable String bankId,
+ @Nullable List acceptedBankIds) {
+ this.price = price;
+ this.marketPriceMargin = marketPriceMargin;
+ this.useMarketBasedPrice = useMarketBasedPrice;
+ this.baseCurrencyCode = baseCurrencyCode;
+ this.counterCurrencyCode = counterCurrencyCode;
+ this.paymentMethodId = paymentMethodId;
+ this.makerPaymentAccountId = makerPaymentAccountId;
+ this.countryCode = countryCode;
+ this.acceptedCountryCodes = acceptedCountryCodes;
+ this.bankId = bankId;
+ this.acceptedBankIds = acceptedBankIds;
+ }
+}
diff --git a/core/src/main/java/bisq/core/offer/OfferUtil.java b/core/src/main/java/bisq/core/offer/OfferUtil.java
index 8ea47fd2be0..f67a73d286f 100644
--- a/core/src/main/java/bisq/core/offer/OfferUtil.java
+++ b/core/src/main/java/bisq/core/offer/OfferUtil.java
@@ -364,6 +364,53 @@ public void validateOfferData(double buyerSecurityDeposit,
Res.get("offerbook.warning.paymentMethodBanned"));
}
+ // Returns an edited payload: a merge of the original offerPayload and
+ // editedOfferPayload fields. Mutable fields are sourced from
+ // mutableOfferPayloadFields param, e.g., payment account details, price, etc.
+ // Immutable fields are sourced from the original openOffer payload param.
+ public OfferPayload getMergedOfferPayload(OpenOffer openOffer,
+ MutableOfferPayloadFields mutableOfferPayloadFields) {
+ OfferPayload originalOfferPayload = openOffer.getOffer().getOfferPayload();
+ return new OfferPayload(originalOfferPayload.getId(),
+ originalOfferPayload.getDate(),
+ originalOfferPayload.getOwnerNodeAddress(),
+ originalOfferPayload.getPubKeyRing(),
+ originalOfferPayload.getDirection(),
+ mutableOfferPayloadFields.getPrice(),
+ mutableOfferPayloadFields.getMarketPriceMargin(),
+ mutableOfferPayloadFields.isUseMarketBasedPrice(),
+ originalOfferPayload.getAmount(),
+ originalOfferPayload.getMinAmount(),
+ mutableOfferPayloadFields.getBaseCurrencyCode(),
+ mutableOfferPayloadFields.getCounterCurrencyCode(),
+ originalOfferPayload.getArbitratorNodeAddresses(),
+ originalOfferPayload.getMediatorNodeAddresses(),
+ mutableOfferPayloadFields.getPaymentMethodId(),
+ mutableOfferPayloadFields.getMakerPaymentAccountId(),
+ originalOfferPayload.getOfferFeePaymentTxId(),
+ mutableOfferPayloadFields.getCountryCode(),
+ mutableOfferPayloadFields.getAcceptedCountryCodes(),
+ mutableOfferPayloadFields.getBankId(),
+ mutableOfferPayloadFields.getAcceptedBankIds(),
+ originalOfferPayload.getVersionNr(),
+ originalOfferPayload.getBlockHeightAtOfferCreation(),
+ originalOfferPayload.getTxFee(),
+ originalOfferPayload.getMakerFee(),
+ originalOfferPayload.isCurrencyForMakerFeeBtc(),
+ originalOfferPayload.getBuyerSecurityDeposit(),
+ originalOfferPayload.getSellerSecurityDeposit(),
+ originalOfferPayload.getMaxTradeLimit(),
+ originalOfferPayload.getMaxTradePeriod(),
+ originalOfferPayload.isUseAutoClose(),
+ originalOfferPayload.isUseReOpenAfterAutoClose(),
+ originalOfferPayload.getLowerClosePrice(),
+ originalOfferPayload.getUpperClosePrice(),
+ originalOfferPayload.isPrivateOffer(),
+ originalOfferPayload.getHashOfChallenge(),
+ originalOfferPayload.getExtraDataMap(),
+ originalOfferPayload.getProtocolVersion());
+ }
+
private Optional getFeeInUserFiatCurrency(Coin makerFee,
boolean isCurrencyForMakerFeeBtc,
String userCurrencyCode,
diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferDataModel.java
index dc7d29c7c08..abf533c4723 100644
--- a/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferDataModel.java
+++ b/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferDataModel.java
@@ -28,6 +28,7 @@
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.TradeCurrency;
import bisq.core.offer.CreateOfferService;
+import bisq.core.offer.MutableOfferPayloadFields;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferPayload;
import bisq.core.offer.OfferUtil;
@@ -182,54 +183,12 @@ public void onStartEditOffer(ErrorMessageHandler errorMessageHandler) {
}
public void onPublishOffer(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
- // editedPayload is a merge of the original offerPayload and newOfferPayload
- // fields which are editable are merged in from newOfferPayload (such as payment account details)
- // fields which cannot change (most importantly BTC amount) are sourced from the original offerPayload
- final OfferPayload offerPayload = openOffer.getOffer().getOfferPayload();
- final OfferPayload newOfferPayload = createAndGetOffer().getOfferPayload();
- final OfferPayload editedPayload = new OfferPayload(offerPayload.getId(),
- offerPayload.getDate(),
- offerPayload.getOwnerNodeAddress(),
- offerPayload.getPubKeyRing(),
- offerPayload.getDirection(),
- newOfferPayload.getPrice(),
- newOfferPayload.getMarketPriceMargin(),
- newOfferPayload.isUseMarketBasedPrice(),
- offerPayload.getAmount(),
- offerPayload.getMinAmount(),
- newOfferPayload.getBaseCurrencyCode(),
- newOfferPayload.getCounterCurrencyCode(),
- offerPayload.getArbitratorNodeAddresses(),
- offerPayload.getMediatorNodeAddresses(),
- newOfferPayload.getPaymentMethodId(),
- newOfferPayload.getMakerPaymentAccountId(),
- offerPayload.getOfferFeePaymentTxId(),
- newOfferPayload.getCountryCode(),
- newOfferPayload.getAcceptedCountryCodes(),
- newOfferPayload.getBankId(),
- newOfferPayload.getAcceptedBankIds(),
- offerPayload.getVersionNr(),
- offerPayload.getBlockHeightAtOfferCreation(),
- offerPayload.getTxFee(),
- offerPayload.getMakerFee(),
- offerPayload.isCurrencyForMakerFeeBtc(),
- offerPayload.getBuyerSecurityDeposit(),
- offerPayload.getSellerSecurityDeposit(),
- offerPayload.getMaxTradeLimit(),
- offerPayload.getMaxTradePeriod(),
- offerPayload.isUseAutoClose(),
- offerPayload.isUseReOpenAfterAutoClose(),
- offerPayload.getLowerClosePrice(),
- offerPayload.getUpperClosePrice(),
- offerPayload.isPrivateOffer(),
- offerPayload.getHashOfChallenge(),
- offerPayload.getExtraDataMap(),
- offerPayload.getProtocolVersion());
-
+ MutableOfferPayloadFields mutableOfferPayloadFields =
+ new MutableOfferPayloadFields(createAndGetOffer().getOfferPayload());
+ final OfferPayload editedPayload = offerUtil.getMergedOfferPayload(openOffer, mutableOfferPayloadFields);
final Offer editedOffer = new Offer(editedPayload);
editedOffer.setPriceFeedService(priceFeedService);
editedOffer.setState(Offer.State.AVAILABLE);
-
openOfferManager.editOpenOfferPublish(editedOffer, triggerPrice, initialState, () -> {
openOffer = null;
resultHandler.handleResult();
From d9dd718b4c63489c373df12602b8b8b2f49e80ed Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sat, 12 Jun 2021 18:42:14 -0300
Subject: [PATCH 02/40] Fix comment
---
core/src/main/java/bisq/core/offer/OfferUtil.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/src/main/java/bisq/core/offer/OfferUtil.java b/core/src/main/java/bisq/core/offer/OfferUtil.java
index f67a73d286f..91c894feb0e 100644
--- a/core/src/main/java/bisq/core/offer/OfferUtil.java
+++ b/core/src/main/java/bisq/core/offer/OfferUtil.java
@@ -367,7 +367,7 @@ public void validateOfferData(double buyerSecurityDeposit,
// Returns an edited payload: a merge of the original offerPayload and
// editedOfferPayload fields. Mutable fields are sourced from
// mutableOfferPayloadFields param, e.g., payment account details, price, etc.
- // Immutable fields are sourced from the original openOffer payload param.
+ // Immutable fields are sourced from the original openOffer param.
public OfferPayload getMergedOfferPayload(OpenOffer openOffer,
MutableOfferPayloadFields mutableOfferPayloadFields) {
OfferPayload originalOfferPayload = openOffer.getOffer().getOfferPayload();
From 1daf4715f843acdd816c90ee9ae0bf5aaf630a93 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 13 Jun 2021 11:59:58 -0300
Subject: [PATCH 03/40] Add OfferInfo field isActivated, rpc EditOffer to proto
---
proto/src/main/proto/grpc.proto | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto
index b8d50bfe4f8..0fa653e4c24 100644
--- a/proto/src/main/proto/grpc.proto
+++ b/proto/src/main/proto/grpc.proto
@@ -72,6 +72,8 @@ service Offers {
}
rpc CreateOffer (CreateOfferRequest) returns (CreateOfferReply) {
}
+ rpc EditOffer (EditOfferRequest) returns (EditOfferReply) {
+ }
rpc CancelOffer (CancelOfferRequest) returns (CancelOfferReply) {
}
}
@@ -128,6 +130,35 @@ message CreateOfferReply {
OfferInfo offer = 1;
}
+message EditOfferRequest {
+ string id = 1;
+ string price = 2;
+ bool useMarketBasedPrice = 3;
+ double marketPriceMargin = 4;
+ uint64 triggerPrice = 5;
+ // Send a signed int, not a bool (with default=false).
+ // -1 = do not change activation state
+ // 0 = disable
+ // 1 = enable
+ sint32 enable = 6;
+ // The EditType constricts what offer details can be modified and simplifies param validation.
+ enum EditType {
+ ACTIVATION_STATE_ONLY = 0;
+ FIXED_PRICE_ONLY = 1;
+ FIXED_PRICE_AND_ACTIVATION_STATE = 2;
+ MKT_PRICE_MARGIN_ONLY = 3;
+ MKT_PRICE_MARGIN_AND_ACTIVATION_STATE = 4;
+ TRIGGER_PRICE_ONLY = 5;
+ TRIGGER_PRICE_AND_ACTIVATION_STATE = 6;
+ MKT_PRICE_MARGIN_AND_TRIGGER_PRICE = 7;
+ MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE = 8;
+ }
+ EditType editType = 7;
+}
+
+message EditOfferReply {
+}
+
message CancelOfferRequest {
string id = 1;
}
@@ -159,6 +190,7 @@ message OfferInfo {
string offerFeePaymentTxId = 21;
uint64 txFee = 22;
uint64 makerFee = 23;
+ bool isActivated = 24;
}
message AvailabilityResultWithDescription {
From 2b8b53bba86579d71e184aac38531e15f462084b Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 13 Jun 2021 12:24:45 -0300
Subject: [PATCH 04/40] Add server/core editOffer, adjust getMyOffer(s) impls
- Add editOffer to GrpcOffersService, CoreApi, CoreOffersService.
- Set editOffer call rate meter to 1 / minute.
- Use new EditOfferValidator to verify editOffer params OK.
- Adust getMyOffer(s) rpc impl and OfferInfo model to use OpenOffer
for accessing activation state and trigger price.
---
core/src/main/java/bisq/core/api/CoreApi.java | 43 ++---
.../java/bisq/core/api/CoreOffersService.java | 156 ++++++++++++------
.../bisq/core/api/EditOfferValidator.java | 135 +++++++++++++++
.../java/bisq/core/api/model/OfferInfo.java | 22 ++-
.../bisq/daemon/grpc/GrpcOffersService.java | 30 +++-
5 files changed, 302 insertions(+), 84 deletions(-)
create mode 100644 core/src/main/java/bisq/core/api/EditOfferValidator.java
diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java
index fe5bbceba58..1c223df2b7f 100644
--- a/core/src/main/java/bisq/core/api/CoreApi.java
+++ b/core/src/main/java/bisq/core/api/CoreApi.java
@@ -21,9 +21,7 @@
import bisq.core.api.model.BalancesInfo;
import bisq.core.api.model.TxFeeRateInfo;
import bisq.core.btc.wallet.TxBroadcaster;
-import bisq.core.monetary.Price;
import bisq.core.offer.Offer;
-import bisq.core.offer.OfferPayload;
import bisq.core.offer.OpenOffer;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
@@ -36,7 +34,6 @@
import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.handlers.ResultHandler;
-import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import javax.inject.Inject;
@@ -52,6 +49,8 @@
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
+import static bisq.proto.grpc.EditOfferRequest.EditType;
+
/**
* Provides high level interface to functionality of core Bisq features.
* E.g. useful for different APIs to access data of different domains of Bisq.
@@ -122,7 +121,7 @@ public Offer getOffer(String id) {
return coreOffersService.getOffer(id);
}
- public Offer getMyOffer(String id) {
+ public OpenOffer getMyOffer(String id) {
return coreOffersService.getMyOffer(id);
}
@@ -130,14 +129,10 @@ public List getOffers(String direction, String currencyCode) {
return coreOffersService.getOffers(direction, currencyCode);
}
- public List getMyOffers(String direction, String currencyCode) {
+ public List getMyOffers(String direction, String currencyCode) {
return coreOffersService.getMyOffers(direction, currencyCode);
}
- public OpenOffer getMyOpenOffer(String id) {
- return coreOffersService.getMyOpenOffer(id);
- }
-
public void createAnPlaceOffer(String currencyCode,
String directionAsString,
String priceAsString,
@@ -164,26 +159,20 @@ public void createAnPlaceOffer(String currencyCode,
resultHandler);
}
- public Offer editOffer(String offerId,
- String currencyCode,
- OfferPayload.Direction direction,
- Price price,
- boolean useMarketBasedPrice,
- double marketPriceMargin,
- Coin amount,
- Coin minAmount,
- double buyerSecurityDeposit,
- PaymentAccount paymentAccount) {
- return coreOffersService.editOffer(offerId,
- currencyCode,
- direction,
- price,
+ public void editOffer(String offerId,
+ String priceAsString,
+ boolean useMarketBasedPrice,
+ double marketPriceMargin,
+ long triggerPrice,
+ int enable,
+ EditType editType) {
+ coreOffersService.editOffer(offerId,
+ priceAsString,
useMarketBasedPrice,
marketPriceMargin,
- amount,
- minAmount,
- buyerSecurityDeposit,
- paymentAccount);
+ triggerPrice,
+ enable,
+ editType);
}
public void cancelOffer(String id) {
diff --git a/core/src/main/java/bisq/core/api/CoreOffersService.java b/core/src/main/java/bisq/core/api/CoreOffersService.java
index e18a760c077..365072f1967 100644
--- a/core/src/main/java/bisq/core/api/CoreOffersService.java
+++ b/core/src/main/java/bisq/core/api/CoreOffersService.java
@@ -20,13 +20,16 @@
import bisq.core.monetary.Altcoin;
import bisq.core.monetary.Price;
import bisq.core.offer.CreateOfferService;
+import bisq.core.offer.MutableOfferPayloadFields;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferBookService;
import bisq.core.offer.OfferFilter;
+import bisq.core.offer.OfferPayload;
import bisq.core.offer.OfferUtil;
import bisq.core.offer.OpenOffer;
import bisq.core.offer.OpenOfferManager;
import bisq.core.payment.PaymentAccount;
+import bisq.core.provider.price.PriceFeedService;
import bisq.core.user.User;
import bisq.common.crypto.KeyRing;
@@ -54,7 +57,10 @@
import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
import static bisq.core.offer.OfferPayload.Direction;
import static bisq.core.offer.OfferPayload.Direction.BUY;
+import static bisq.core.offer.OpenOffer.State.AVAILABLE;
+import static bisq.core.offer.OpenOffer.State.DEACTIVATED;
import static bisq.core.payment.PaymentAccountUtil.isPaymentAccountValidForOffer;
+import static bisq.proto.grpc.EditOfferRequest.EditType;
import static java.lang.String.format;
import static java.util.Comparator.comparing;
@@ -62,8 +68,11 @@
@Slf4j
class CoreOffersService {
- private final Supplier> priceComparator = () -> comparing(Offer::getPrice);
- private final Supplier> reversePriceComparator = () -> comparing(Offer::getPrice).reversed();
+ private final Supplier> priceComparator = () ->
+ comparing(Offer::getPrice);
+
+ private final Supplier> openOfferPriceComparator = () ->
+ comparing(openOffer -> openOffer.getOffer().getPrice());
private final CoreContext coreContext;
private final KeyRing keyRing;
@@ -76,6 +85,7 @@ class CoreOffersService {
private final OfferFilter offerFilter;
private final OpenOfferManager openOfferManager;
private final OfferUtil offerUtil;
+ private final PriceFeedService priceFeedService;
private final User user;
@Inject
@@ -87,6 +97,7 @@ public CoreOffersService(CoreContext coreContext,
OfferFilter offerFilter,
OpenOfferManager openOfferManager,
OfferUtil offerUtil,
+ PriceFeedService priceFeedService,
User user) {
this.coreContext = coreContext;
this.keyRing = keyRing;
@@ -96,6 +107,7 @@ public CoreOffersService(CoreContext coreContext,
this.offerFilter = offerFilter;
this.openOfferManager = openOfferManager;
this.offerUtil = offerUtil;
+ this.priceFeedService = priceFeedService;
this.user = user;
}
@@ -108,10 +120,10 @@ Offer getOffer(String id) {
new IllegalStateException(format("offer with id '%s' not found", id)));
}
- Offer getMyOffer(String id) {
- return offerBookService.getOffers().stream()
+ OpenOffer getMyOffer(String id) {
+ return openOfferManager.getObservableList().stream()
.filter(o -> o.getId().equals(id))
- .filter(o -> o.isMyOffer(keyRing))
+ .filter(o -> o.getOffer().isMyOffer(keyRing))
.findAny().orElseThrow(() ->
new IllegalStateException(format("offer with id '%s' not found", id)));
}
@@ -125,11 +137,11 @@ List getOffers(String direction, String currencyCode) {
.collect(Collectors.toList());
}
- List getMyOffers(String direction, String currencyCode) {
- return offerBookService.getOffers().stream()
- .filter(o -> o.isMyOffer(keyRing))
- .filter(o -> offerMatchesDirectionAndCurrency(o, direction, currencyCode))
- .sorted(priceComparator(direction))
+ List getMyOffers(String direction, String currencyCode) {
+ return openOfferManager.getObservableList().stream()
+ .filter(o -> o.getOffer().isMyOffer(keyRing))
+ .filter(o -> offerMatchesDirectionAndCurrency(o.getOffer(), direction, currencyCode))
+ .sorted(openOfferPriceComparator(direction))
.collect(Collectors.toList());
}
@@ -137,7 +149,7 @@ OpenOffer getMyOpenOffer(String id) {
return openOfferManager.getOpenOfferById(id)
.filter(open -> open.getOffer().isMyOffer(keyRing))
.orElseThrow(() ->
- new IllegalStateException(format("openoffer with id '%s' not found", id)));
+ new IllegalStateException(format("offer with id '%s' not found", id)));
}
// Create and place new offer.
@@ -193,47 +205,56 @@ void createAndPlaceOffer(String currencyCode,
}
// Edit a placed offer.
- Offer editOffer(String offerId,
- String currencyCode,
- Direction direction,
- Price price,
- boolean useMarketBasedPrice,
- double marketPriceMargin,
- Coin amount,
- Coin minAmount,
- double buyerSecurityDeposit,
- PaymentAccount paymentAccount) {
- Coin useDefaultTxFee = Coin.ZERO;
- return createOfferService.createAndGetOffer(offerId,
- direction,
- currencyCode.toUpperCase(),
- amount,
- minAmount,
- price,
- useDefaultTxFee,
- useMarketBasedPrice,
- exactMultiply(marketPriceMargin, 0.01),
- buyerSecurityDeposit,
- paymentAccount);
- }
-
- void cancelOffer(String id) {
- Offer offer = getMyOffer(id);
- openOfferManager.removeOffer(offer,
+ void editOffer(String offerId,
+ String editedPriceAsString,
+ boolean editedUseMarketBasedPrice,
+ double editedMarketPriceMargin,
+ long editedTriggerPrice,
+ int editedEnable,
+ EditType editType) {
+ OpenOffer openOffer = getMyOpenOffer(offerId);
+ new EditOfferValidator(openOffer,
+ editedPriceAsString,
+ editedUseMarketBasedPrice,
+ editedMarketPriceMargin,
+ editedTriggerPrice,
+ editType).validate();
+ OfferPayload editedPayload = getMergedOfferPayload(openOffer, editedPriceAsString,
+ editedUseMarketBasedPrice,
+ editedMarketPriceMargin);
+ Offer editedOffer = new Offer(editedPayload);
+ priceFeedService.setCurrencyCode(openOffer.getOffer().getOfferPayload().getCurrencyCode());
+ editedOffer.setPriceFeedService(priceFeedService);
+ editedOffer.setState(Offer.State.AVAILABLE);
+ openOfferManager.editOpenOfferStart(openOffer,
() -> {
+ log.info("EditOpenOfferStart: offer {}", openOffer.getId());
},
errorMessage -> {
- throw new IllegalStateException(errorMessage);
+ log.error(errorMessage);
});
+ // Client sent (sint32) newEnable, not a bool (with default=false).
+ // If newEnable = -1, do not change activation state
+ // If newEnable = 0, set state = AVAILABLE
+ // If newEnable = 1, set state = DEACTIVATED
+ OpenOffer.State newOfferState = editedEnable < 0
+ ? openOffer.getState()
+ : editedEnable > 0 ? AVAILABLE : DEACTIVATED;
+ openOfferManager.editOpenOfferPublish(editedOffer,
+ editedTriggerPrice,
+ newOfferState,
+ () -> {
+ log.info("EditOpenOfferPublish: offer {}", openOffer.getId());
+ },
+ log::error);
}
- private void verifyPaymentAccountIsValidForNewOffer(Offer offer, PaymentAccount paymentAccount) {
- if (!isPaymentAccountValidForOffer(offer, paymentAccount)) {
- String error = format("cannot create %s offer with payment account %s",
- offer.getOfferPayload().getCounterCurrencyCode(),
- paymentAccount.getId());
- throw new IllegalStateException(error);
- }
+ void cancelOffer(String id) {
+ OpenOffer openOffer = getMyOffer(id);
+ openOfferManager.removeOffer(openOffer.getOffer(),
+ () -> {
+ },
+ log::error);
}
private void placeOffer(Offer offer,
@@ -252,6 +273,39 @@ private void placeOffer(Offer offer,
throw new IllegalStateException(offer.getErrorMessage());
}
+ private OfferPayload getMergedOfferPayload(OpenOffer openOffer,
+ String editedPriceAsString,
+ boolean editedUseMarketBasedPrice,
+ double editedMarketPriceMargin) {
+ // API supports editing price, marketPriceMargin, useMarketBasedPrice payload
+ // fields. API does not support editing payment acct or currency code fields.
+ Offer offer = openOffer.getOffer();
+ String currencyCode = offer.getOfferPayload().getCurrencyCode();
+ Price editedPrice = Price.valueOf(currencyCode, priceStringToLong(editedPriceAsString, currencyCode));
+ MutableOfferPayloadFields mutableOfferPayloadFields = new MutableOfferPayloadFields(
+ editedPrice.getValue(),
+ exactMultiply(editedMarketPriceMargin, 0.01),
+ editedUseMarketBasedPrice,
+ offer.getOfferPayload().getBaseCurrencyCode(),
+ offer.getOfferPayload().getCounterCurrencyCode(),
+ offer.getPaymentMethod().getId(),
+ offer.getMakerPaymentAccountId(),
+ offer.getOfferPayload().getCountryCode(),
+ offer.getOfferPayload().getAcceptedCountryCodes(),
+ offer.getOfferPayload().getBankId(),
+ offer.getOfferPayload().getAcceptedBankIds());
+ return offerUtil.getMergedOfferPayload(openOffer, mutableOfferPayloadFields);
+ }
+
+ private void verifyPaymentAccountIsValidForNewOffer(Offer offer, PaymentAccount paymentAccount) {
+ if (!isPaymentAccountValidForOffer(offer, paymentAccount)) {
+ String error = format("cannot create %s offer with payment account %s",
+ offer.getOfferPayload().getCounterCurrencyCode(),
+ paymentAccount.getId());
+ throw new IllegalStateException(error);
+ }
+ }
+
private boolean offerMatchesDirectionAndCurrency(Offer offer,
String direction,
String currencyCode) {
@@ -261,11 +315,19 @@ private boolean offerMatchesDirectionAndCurrency(Offer offer,
return offerOfWantedDirection && offerInWantedCurrency;
}
+ private Comparator openOfferPriceComparator(String direction) {
+ // A buyer probably wants to see sell orders in price ascending order.
+ // A seller probably wants to see buy orders in price descending order.
+ return direction.equalsIgnoreCase(BUY.name())
+ ? openOfferPriceComparator.get().reversed()
+ : openOfferPriceComparator.get();
+ }
+
private Comparator priceComparator(String direction) {
// A buyer probably wants to see sell orders in price ascending order.
// A seller probably wants to see buy orders in price descending order.
return direction.equalsIgnoreCase(BUY.name())
- ? reversePriceComparator.get()
+ ? priceComparator.get().reversed()
: priceComparator.get();
}
diff --git a/core/src/main/java/bisq/core/api/EditOfferValidator.java b/core/src/main/java/bisq/core/api/EditOfferValidator.java
new file mode 100644
index 00000000000..f451e330811
--- /dev/null
+++ b/core/src/main/java/bisq/core/api/EditOfferValidator.java
@@ -0,0 +1,135 @@
+package bisq.core.api;
+
+import bisq.core.offer.OpenOffer;
+
+import bisq.proto.grpc.EditOfferRequest;
+
+import java.math.BigDecimal;
+
+import lombok.extern.slf4j.Slf4j;
+
+import static java.lang.String.format;
+
+@Slf4j
+class EditOfferValidator {
+
+ private final OpenOffer currentlyOpenOffer;
+ private final String editedPriceAsString;
+ private final boolean editedUseMarketBasedPrice;
+ private final double editedMarketPriceMargin;
+ private final long editedTriggerPrice;
+ private final EditOfferRequest.EditType editType;
+
+ private final boolean isZeroEditedFixedPriceString;
+ private final boolean isZeroEditedMarketPriceMargin;
+ private final boolean isZeroEditedTriggerPrice;
+
+ EditOfferValidator(OpenOffer currentlyOpenOffer,
+ String editedPriceAsString,
+ boolean editedUseMarketBasedPrice,
+ double editedMarketPriceMargin,
+ long editedTriggerPrice,
+ EditOfferRequest.EditType editType) {
+ this.currentlyOpenOffer = currentlyOpenOffer;
+ this.editedPriceAsString = editedPriceAsString;
+ this.editedUseMarketBasedPrice = editedUseMarketBasedPrice;
+ this.editedMarketPriceMargin = editedMarketPriceMargin;
+ this.editedTriggerPrice = editedTriggerPrice;
+ this.editType = editType;
+
+ this.isZeroEditedFixedPriceString = new BigDecimal(editedPriceAsString).doubleValue() == 0;
+ this.isZeroEditedMarketPriceMargin = editedMarketPriceMargin == 0;
+ this.isZeroEditedTriggerPrice = editedTriggerPrice == 0;
+ }
+
+ void validate() {
+ log.info("Verifying 'editoffer' params OK for editType {}", editType);
+ switch (editType) {
+ case ACTIVATION_STATE_ONLY: {
+ validateEditedActivationState();
+ break;
+ }
+ case FIXED_PRICE_ONLY:
+ case FIXED_PRICE_AND_ACTIVATION_STATE: {
+ validateEditedFixedPrice();
+ break;
+ }
+ case MKT_PRICE_MARGIN_ONLY:
+ case MKT_PRICE_MARGIN_AND_ACTIVATION_STATE:
+ case TRIGGER_PRICE_ONLY:
+ case TRIGGER_PRICE_AND_ACTIVATION_STATE: {
+ // Make sure the edited trigger price is OK, even if not being changed.
+ validateEditedTriggerPrice();
+ // Continue, no break.
+ }
+ case MKT_PRICE_MARGIN_AND_TRIGGER_PRICE:
+ case MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE: {
+ validateEditedMarketPriceMargin();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ private void validateEditedActivationState() {
+ if (!isZeroEditedFixedPriceString || !isZeroEditedMarketPriceMargin || !isZeroEditedTriggerPrice)
+ throw new IllegalStateException(
+ format("programmer error: cannot change fixed price (%s), "
+ + " mkt price margin (%s), or trigger price (%s) "
+ + " in offer with id '%s' when only changing activation state",
+ editedPriceAsString,
+ editedMarketPriceMargin,
+ editedTriggerPrice,
+ currentlyOpenOffer.getId()));
+ }
+
+ private void validateEditedFixedPrice() {
+ if (currentlyOpenOffer.getOffer().isUseMarketBasedPrice())
+ log.info("Attempting to change mkt price margin based offer with id '%s' to fixed price offer.",
+ currentlyOpenOffer.getId());
+
+ if (editedUseMarketBasedPrice)
+ throw new IllegalStateException(
+ format("programmer error: cannot change fixed price (%s)"
+ + " in mkt price based offer with id '%s'",
+ editedMarketPriceMargin,
+ currentlyOpenOffer.getId()));
+
+ if (!isZeroEditedTriggerPrice)
+ throw new IllegalStateException(
+ format("programmer error: cannot change trigger price (%s)"
+ + " in offer with id '%s' when changing fixed price",
+ editedTriggerPrice,
+ currentlyOpenOffer.getId()));
+
+ }
+
+ private void validateEditedMarketPriceMargin() {
+ if (!currentlyOpenOffer.getOffer().isUseMarketBasedPrice())
+ log.info("Attempting to change fixed price offer with id '%s' to mkt price margin based offer.",
+ currentlyOpenOffer.getId());
+
+ if (!editedUseMarketBasedPrice && !isZeroEditedTriggerPrice)
+ throw new IllegalStateException(
+ format("programmer error: cannot set a trigger price (%s)"
+ + " in fixed price offer with id '%s'",
+ editedTriggerPrice,
+ currentlyOpenOffer.getId()));
+
+ if (!isZeroEditedFixedPriceString)
+ throw new IllegalStateException(
+ format("programmer error: cannot set fixed price (%s)"
+ + " in mkt price margin based offer with id '%s'",
+ editedPriceAsString,
+ currentlyOpenOffer.getId()));
+ }
+
+ private void validateEditedTriggerPrice() {
+ if (editedTriggerPrice < 0)
+ throw new IllegalStateException(
+ format("programmer error: cannot set trigger price to a negative value"
+ + " in offer with id '%s'",
+ currentlyOpenOffer.getId()));
+ }
+}
diff --git a/core/src/main/java/bisq/core/api/model/OfferInfo.java b/core/src/main/java/bisq/core/api/model/OfferInfo.java
index f8501f7df1f..e0588817041 100644
--- a/core/src/main/java/bisq/core/api/model/OfferInfo.java
+++ b/core/src/main/java/bisq/core/api/model/OfferInfo.java
@@ -18,6 +18,7 @@
package bisq.core.api.model;
import bisq.core.offer.Offer;
+import bisq.core.offer.OpenOffer;
import bisq.common.Payload;
@@ -61,7 +62,7 @@ public class OfferInfo implements Payload {
private final String counterCurrencyCode;
private final long date;
private final String state;
-
+ private final boolean isActivated;
public OfferInfo(OfferInfoBuilder builder) {
this.id = builder.id;
@@ -87,17 +88,18 @@ public OfferInfo(OfferInfoBuilder builder) {
this.counterCurrencyCode = builder.counterCurrencyCode;
this.date = builder.date;
this.state = builder.state;
-
+ this.isActivated = builder.isActivated;
}
public static OfferInfo toOfferInfo(Offer offer) {
return getOfferInfoBuilder(offer).build();
}
- public static OfferInfo toOfferInfo(Offer offer, long triggerPrice) {
- // The Offer does not have a triggerPrice attribute, so we get
- // the base OfferInfoBuilder, then add the OpenOffer's triggerPrice.
- return getOfferInfoBuilder(offer).withTriggerPrice(triggerPrice).build();
+ public static OfferInfo toOfferInfo(OpenOffer openOffer) {
+ return getOfferInfoBuilder(openOffer.getOffer())
+ .withTriggerPrice(openOffer.getTriggerPrice())
+ .withIsActivated(!openOffer.isDeactivated())
+ .build();
}
private static OfferInfoBuilder getOfferInfoBuilder(Offer offer) {
@@ -156,6 +158,7 @@ public bisq.proto.grpc.OfferInfo toProtoMessage() {
.setCounterCurrencyCode(counterCurrencyCode)
.setDate(date)
.setState(state)
+ .setIsActivated(isActivated)
.build();
}
@@ -185,6 +188,7 @@ public static OfferInfo fromProto(bisq.proto.grpc.OfferInfo proto) {
.withCounterCurrencyCode(proto.getCounterCurrencyCode())
.withDate(proto.getDate())
.withState(proto.getState())
+ .withIsActivated(proto.getIsActivated())
.build();
}
@@ -218,6 +222,7 @@ public static class OfferInfoBuilder {
private String counterCurrencyCode;
private long date;
private String state;
+ private boolean isActivated;
public OfferInfoBuilder withId(String id) {
this.id = id;
@@ -334,6 +339,11 @@ public OfferInfoBuilder withState(String state) {
return this;
}
+ public OfferInfoBuilder withIsActivated(boolean isActivated) {
+ this.isActivated = isActivated;
+ return this;
+ }
+
public OfferInfo build() {
return new OfferInfo(this);
}
diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java
index 54658e4c9a9..2917bae6289 100644
--- a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java
+++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java
@@ -26,6 +26,8 @@
import bisq.proto.grpc.CancelOfferRequest;
import bisq.proto.grpc.CreateOfferReply;
import bisq.proto.grpc.CreateOfferRequest;
+import bisq.proto.grpc.EditOfferReply;
+import bisq.proto.grpc.EditOfferRequest;
import bisq.proto.grpc.GetMyOfferReply;
import bisq.proto.grpc.GetMyOfferRequest;
import bisq.proto.grpc.GetMyOffersReply;
@@ -89,10 +91,9 @@ public void getOffer(GetOfferRequest req,
public void getMyOffer(GetMyOfferRequest req,
StreamObserver responseObserver) {
try {
- Offer offer = coreApi.getMyOffer(req.getId());
- OpenOffer openOffer = coreApi.getMyOpenOffer(req.getId());
+ OpenOffer openOffer = coreApi.getMyOffer(req.getId());
var reply = GetMyOfferReply.newBuilder()
- .setOffer(toOfferInfo(offer, openOffer.getTriggerPrice()).toProtoMessage())
+ .setOffer(toOfferInfo(openOffer).toProtoMessage())
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
@@ -125,7 +126,8 @@ public void getMyOffers(GetMyOffersRequest req,
StreamObserver responseObserver) {
try {
List result = coreApi.getMyOffers(req.getDirection(), req.getCurrencyCode())
- .stream().map(OfferInfo::toOfferInfo)
+ .stream()
+ .map(OfferInfo::toOfferInfo)
.collect(Collectors.toList());
var reply = GetMyOffersReply.newBuilder()
.addAllOffers(result.stream()
@@ -170,6 +172,25 @@ public void createOffer(CreateOfferRequest req,
}
}
+ @Override
+ public void editOffer(EditOfferRequest req,
+ StreamObserver responseObserver) {
+ try {
+ coreApi.editOffer(req.getId(),
+ req.getPrice(),
+ req.getUseMarketBasedPrice(),
+ req.getMarketPriceMargin(),
+ req.getTriggerPrice(),
+ req.getEnable(),
+ req.getEditType());
+ var reply = EditOfferReply.newBuilder().build();
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ } catch (Throwable cause) {
+ exceptionHandler.handleException(log, cause, responseObserver);
+ }
+ }
+
@Override
public void cancelOffer(CancelOfferRequest req,
StreamObserver responseObserver) {
@@ -198,6 +219,7 @@ final Optional rateMeteringInterceptor() {
put(getGetOffersMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
put(getGetMyOffersMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
put(getCreateOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES));
+ put(getEditOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES));
put(getCancelOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES));
}}
)));
From 9231e48c439debc44213132e4b3a7f540503cad0 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 13 Jun 2021 12:35:43 -0300
Subject: [PATCH 05/40] Refactor GrpcClient: request builders moved
bisq.cli.request pkg
Reduces size of GrpcClient while allowing for additional bot-friendly
variations of the new grpc editOffer method.
---
cli/src/main/java/bisq/cli/GrpcClient.java | 377 ++++++------------
.../cli/request/OffersServiceRequest.java | 317 +++++++++++++++
.../PaymentAccountsServiceRequest.java | 85 ++++
.../cli/request/TradesServiceRequest.java | 94 +++++
.../cli/request/WalletsServiceRequest.java | 192 +++++++++
5 files changed, 816 insertions(+), 249 deletions(-)
create mode 100644 cli/src/main/java/bisq/cli/request/OffersServiceRequest.java
create mode 100644 cli/src/main/java/bisq/cli/request/PaymentAccountsServiceRequest.java
create mode 100644 cli/src/main/java/bisq/cli/request/TradesServiceRequest.java
create mode 100644 cli/src/main/java/bisq/cli/request/WalletsServiceRequest.java
diff --git a/cli/src/main/java/bisq/cli/GrpcClient.java b/cli/src/main/java/bisq/cli/GrpcClient.java
index 92784e88298..9b2195bf20d 100644
--- a/cli/src/main/java/bisq/cli/GrpcClient.java
+++ b/cli/src/main/java/bisq/cli/GrpcClient.java
@@ -21,63 +21,31 @@
import bisq.proto.grpc.BalancesInfo;
import bisq.proto.grpc.BsqBalanceInfo;
import bisq.proto.grpc.BtcBalanceInfo;
-import bisq.proto.grpc.CancelOfferRequest;
-import bisq.proto.grpc.ConfirmPaymentReceivedRequest;
-import bisq.proto.grpc.ConfirmPaymentStartedRequest;
-import bisq.proto.grpc.CreateCryptoCurrencyPaymentAccountRequest;
-import bisq.proto.grpc.CreateOfferRequest;
-import bisq.proto.grpc.CreatePaymentAccountRequest;
-import bisq.proto.grpc.GetAddressBalanceRequest;
-import bisq.proto.grpc.GetBalancesRequest;
-import bisq.proto.grpc.GetCryptoCurrencyPaymentMethodsRequest;
-import bisq.proto.grpc.GetFundingAddressesRequest;
import bisq.proto.grpc.GetMethodHelpRequest;
-import bisq.proto.grpc.GetMyOfferRequest;
-import bisq.proto.grpc.GetMyOffersRequest;
-import bisq.proto.grpc.GetOfferRequest;
-import bisq.proto.grpc.GetOffersRequest;
-import bisq.proto.grpc.GetPaymentAccountFormRequest;
-import bisq.proto.grpc.GetPaymentAccountsRequest;
-import bisq.proto.grpc.GetPaymentMethodsRequest;
-import bisq.proto.grpc.GetTradeRequest;
-import bisq.proto.grpc.GetTransactionRequest;
-import bisq.proto.grpc.GetTxFeeRateRequest;
-import bisq.proto.grpc.GetUnusedBsqAddressRequest;
import bisq.proto.grpc.GetVersionRequest;
-import bisq.proto.grpc.KeepFundsRequest;
-import bisq.proto.grpc.LockWalletRequest;
-import bisq.proto.grpc.MarketPriceRequest;
import bisq.proto.grpc.OfferInfo;
import bisq.proto.grpc.RegisterDisputeAgentRequest;
-import bisq.proto.grpc.RemoveWalletPasswordRequest;
-import bisq.proto.grpc.SendBsqRequest;
-import bisq.proto.grpc.SendBtcRequest;
-import bisq.proto.grpc.SetTxFeeRatePreferenceRequest;
-import bisq.proto.grpc.SetWalletPasswordRequest;
import bisq.proto.grpc.StopRequest;
import bisq.proto.grpc.TakeOfferReply;
-import bisq.proto.grpc.TakeOfferRequest;
import bisq.proto.grpc.TradeInfo;
import bisq.proto.grpc.TxFeeRateInfo;
import bisq.proto.grpc.TxInfo;
-import bisq.proto.grpc.UnlockWalletRequest;
-import bisq.proto.grpc.UnsetTxFeeRatePreferenceRequest;
-import bisq.proto.grpc.VerifyBsqSentToAddressRequest;
-import bisq.proto.grpc.WithdrawFundsRequest;
import protobuf.PaymentAccount;
import protobuf.PaymentMethod;
-import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
-import static bisq.cli.CryptoCurrencyUtil.isSupportedCryptoCurrency;
-import static java.util.Comparator.comparing;
-import static java.util.stream.Collectors.toList;
-import static protobuf.OfferPayload.Direction.BUY;
-import static protobuf.OfferPayload.Direction.SELL;
+import static bisq.proto.grpc.EditOfferRequest.EditType;
+
+
+
+import bisq.cli.request.OffersServiceRequest;
+import bisq.cli.request.PaymentAccountsServiceRequest;
+import bisq.cli.request.TradesServiceRequest;
+import bisq.cli.request.WalletsServiceRequest;
@SuppressWarnings("ResultOfMethodCallIgnored")
@@ -85,9 +53,19 @@
public final class GrpcClient {
private final GrpcStubs grpcStubs;
-
- public GrpcClient(String apiHost, int apiPort, String apiPassword) {
+ private final OffersServiceRequest offersServiceRequest;
+ private final TradesServiceRequest tradesServiceRequest;
+ private final WalletsServiceRequest walletsServiceRequest;
+ private final PaymentAccountsServiceRequest paymentAccountsServiceRequest;
+
+ public GrpcClient(String apiHost,
+ int apiPort,
+ String apiPassword) {
this.grpcStubs = new GrpcStubs(apiHost, apiPort, apiPassword);
+ this.offersServiceRequest = new OffersServiceRequest(grpcStubs);
+ this.tradesServiceRequest = new TradesServiceRequest(grpcStubs);
+ this.walletsServiceRequest = new WalletsServiceRequest(grpcStubs);
+ this.paymentAccountsServiceRequest = new PaymentAccountsServiceRequest(grpcStubs);
}
public String getVersion() {
@@ -96,108 +74,67 @@ public String getVersion() {
}
public BalancesInfo getBalances() {
- return getBalances("");
+ return walletsServiceRequest.getBalances();
}
public BsqBalanceInfo getBsqBalances() {
- return getBalances("BSQ").getBsq();
+ return walletsServiceRequest.getBsqBalances();
}
public BtcBalanceInfo getBtcBalances() {
- return getBalances("BTC").getBtc();
+ return walletsServiceRequest.getBtcBalances();
}
public BalancesInfo getBalances(String currencyCode) {
- var request = GetBalancesRequest.newBuilder()
- .setCurrencyCode(currencyCode)
- .build();
- return grpcStubs.walletsService.getBalances(request).getBalances();
+ return walletsServiceRequest.getBalances(currencyCode);
}
public AddressBalanceInfo getAddressBalance(String address) {
- var request = GetAddressBalanceRequest.newBuilder()
- .setAddress(address).build();
- return grpcStubs.walletsService.getAddressBalance(request).getAddressBalanceInfo();
+ return walletsServiceRequest.getAddressBalance(address);
}
public double getBtcPrice(String currencyCode) {
- var request = MarketPriceRequest.newBuilder()
- .setCurrencyCode(currencyCode)
- .build();
- return grpcStubs.priceService.getMarketPrice(request).getPrice();
+ return walletsServiceRequest.getBtcPrice(currencyCode);
}
public List getFundingAddresses() {
- var request = GetFundingAddressesRequest.newBuilder().build();
- return grpcStubs.walletsService.getFundingAddresses(request).getAddressBalanceInfoList();
+ return walletsServiceRequest.getFundingAddresses();
}
public String getUnusedBsqAddress() {
- var request = GetUnusedBsqAddressRequest.newBuilder().build();
- return grpcStubs.walletsService.getUnusedBsqAddress(request).getAddress();
+ return walletsServiceRequest.getUnusedBsqAddress();
}
public String getUnusedBtcAddress() {
- var request = GetFundingAddressesRequest.newBuilder().build();
- var addressBalances = grpcStubs.walletsService.getFundingAddresses(request)
- .getAddressBalanceInfoList();
- //noinspection OptionalGetWithoutIsPresent
- return addressBalances.stream()
- .filter(AddressBalanceInfo::getIsAddressUnused)
- .findFirst()
- .get()
- .getAddress();
+ return walletsServiceRequest.getUnusedBtcAddress();
}
public TxInfo sendBsq(String address, String amount, String txFeeRate) {
- var request = SendBsqRequest.newBuilder()
- .setAddress(address)
- .setAmount(amount)
- .setTxFeeRate(txFeeRate)
- .build();
- return grpcStubs.walletsService.sendBsq(request).getTxInfo();
+ return walletsServiceRequest.sendBsq(address, amount, txFeeRate);
}
public TxInfo sendBtc(String address, String amount, String txFeeRate, String memo) {
- var request = SendBtcRequest.newBuilder()
- .setAddress(address)
- .setAmount(amount)
- .setTxFeeRate(txFeeRate)
- .setMemo(memo)
- .build();
- return grpcStubs.walletsService.sendBtc(request).getTxInfo();
+ return walletsServiceRequest.sendBtc(address, amount, txFeeRate, memo);
}
public boolean verifyBsqSentToAddress(String address, String amount) {
- var request = VerifyBsqSentToAddressRequest.newBuilder()
- .setAddress(address)
- .setAmount(amount)
- .build();
- return grpcStubs.walletsService.verifyBsqSentToAddress(request).getIsAmountReceived();
+ return walletsServiceRequest.verifyBsqSentToAddress(address, amount);
}
public TxFeeRateInfo getTxFeeRate() {
- var request = GetTxFeeRateRequest.newBuilder().build();
- return grpcStubs.walletsService.getTxFeeRate(request).getTxFeeRateInfo();
+ return walletsServiceRequest.getTxFeeRate();
}
public TxFeeRateInfo setTxFeeRate(long txFeeRate) {
- var request = SetTxFeeRatePreferenceRequest.newBuilder()
- .setTxFeeRatePreference(txFeeRate)
- .build();
- return grpcStubs.walletsService.setTxFeeRatePreference(request).getTxFeeRateInfo();
+ return walletsServiceRequest.setTxFeeRate(txFeeRate);
}
public TxFeeRateInfo unsetTxFeeRate() {
- var request = UnsetTxFeeRatePreferenceRequest.newBuilder().build();
- return grpcStubs.walletsService.unsetTxFeeRatePreference(request).getTxFeeRateInfo();
+ return walletsServiceRequest.unsetTxFeeRate();
}
public TxInfo getTransaction(String txId) {
- var request = GetTransactionRequest.newBuilder()
- .setTxId(txId)
- .build();
- return grpcStubs.walletsService.getTransaction(request).getTxInfo();
+ return walletsServiceRequest.getTransaction(txId);
}
public OfferInfo createFixedPricedOffer(String direction,
@@ -208,7 +145,7 @@ public OfferInfo createFixedPricedOffer(String direction,
double securityDeposit,
String paymentAcctId,
String makerFeeCurrencyCode) {
- return createOffer(direction,
+ return offersServiceRequest.createOffer(direction,
currencyCode,
amount,
minAmount,
@@ -217,7 +154,8 @@ public OfferInfo createFixedPricedOffer(String direction,
0.00,
securityDeposit,
paymentAcctId,
- makerFeeCurrencyCode);
+ makerFeeCurrencyCode,
+ 0 /* no trigger price */);
}
public OfferInfo createMarketBasedPricedOffer(String direction,
@@ -227,8 +165,9 @@ public OfferInfo createMarketBasedPricedOffer(String direction,
double marketPriceMargin,
double securityDeposit,
String paymentAcctId,
- String makerFeeCurrencyCode) {
- return createOffer(direction,
+ String makerFeeCurrencyCode,
+ long triggerPrice) {
+ return offersServiceRequest.createOffer(direction,
currencyCode,
amount,
minAmount,
@@ -237,7 +176,8 @@ public OfferInfo createMarketBasedPricedOffer(String direction,
marketPriceMargin,
securityDeposit,
paymentAcctId,
- makerFeeCurrencyCode);
+ makerFeeCurrencyCode,
+ triggerPrice);
}
public OfferInfo createOffer(String direction,
@@ -249,253 +189,192 @@ public OfferInfo createOffer(String direction,
double marketPriceMargin,
double securityDeposit,
String paymentAcctId,
- String makerFeeCurrencyCode) {
- var request = CreateOfferRequest.newBuilder()
- .setDirection(direction)
- .setCurrencyCode(currencyCode)
- .setAmount(amount)
- .setMinAmount(minAmount)
- .setUseMarketBasedPrice(useMarketBasedPrice)
- .setPrice(fixedPrice)
- .setMarketPriceMargin(marketPriceMargin)
- .setBuyerSecurityDeposit(securityDeposit)
- .setPaymentAccountId(paymentAcctId)
- .setMakerFeeCurrencyCode(makerFeeCurrencyCode)
- .build();
- return grpcStubs.offersService.createOffer(request).getOffer();
+ String makerFeeCurrencyCode,
+ long triggerPrice) {
+ return offersServiceRequest.createOffer(direction,
+ currencyCode,
+ amount,
+ minAmount,
+ useMarketBasedPrice,
+ fixedPrice,
+ marketPriceMargin,
+ securityDeposit,
+ paymentAcctId,
+ makerFeeCurrencyCode,
+ triggerPrice);
+ }
+
+ public void editOfferActivationState(String offerId, int enable) {
+ offersServiceRequest.editOfferActivationState(offerId, enable);
+ }
+
+ public void editOfferFixedPrice(String offerId, String priceAsString) {
+ offersServiceRequest.editOfferFixedPrice(offerId, priceAsString);
+ }
+
+ public void editOfferPriceMargin(String offerId, double marketPriceMargin) {
+ offersServiceRequest.editOfferPriceMargin(offerId, marketPriceMargin);
+ }
+
+ public void editOfferTriggerPrice(String offerId, long triggerPrice) {
+ offersServiceRequest.editOfferTriggerPrice(offerId, triggerPrice);
+ }
+
+ public void editOffer(String offerId,
+ String priceAsString,
+ boolean useMarketBasedPrice,
+ double marketPriceMargin,
+ long triggerPrice,
+ int enable,
+ EditType editType) {
+ // Take care when using this method directly:
+ // useMarketBasedPrice = true if margin based offer, false for fixed priced offer
+ // scaledPriceString fmt = ######.####
+ offersServiceRequest.editOffer(offerId,
+ priceAsString,
+ useMarketBasedPrice,
+ marketPriceMargin,
+ triggerPrice,
+ enable,
+ editType);
}
public void cancelOffer(String offerId) {
- var request = CancelOfferRequest.newBuilder()
- .setId(offerId)
- .build();
- grpcStubs.offersService.cancelOffer(request);
+ offersServiceRequest.cancelOffer(offerId);
}
public OfferInfo getOffer(String offerId) {
- var request = GetOfferRequest.newBuilder()
- .setId(offerId)
- .build();
- return grpcStubs.offersService.getOffer(request).getOffer();
+ return offersServiceRequest.getOffer(offerId);
}
public OfferInfo getMyOffer(String offerId) {
- var request = GetMyOfferRequest.newBuilder()
- .setId(offerId)
- .build();
- return grpcStubs.offersService.getMyOffer(request).getOffer();
+ return offersServiceRequest.getMyOffer(offerId);
}
public List getOffers(String direction, String currencyCode) {
- if (isSupportedCryptoCurrency(currencyCode)) {
- return getCryptoCurrencyOffers(direction, currencyCode);
- } else {
- var request = GetOffersRequest.newBuilder()
- .setDirection(direction)
- .setCurrencyCode(currencyCode)
- .build();
- return grpcStubs.offersService.getOffers(request).getOffersList();
- }
+ return offersServiceRequest.getOffers(direction, currencyCode);
}
public List getCryptoCurrencyOffers(String direction, String currencyCode) {
- return getOffers(direction, "BTC").stream()
- .filter(o -> o.getBaseCurrencyCode().equalsIgnoreCase(currencyCode))
- .collect(toList());
+ return offersServiceRequest.getCryptoCurrencyOffers(direction, currencyCode);
}
public List getOffersSortedByDate(String currencyCode) {
- ArrayList offers = new ArrayList<>();
- offers.addAll(getOffers(BUY.name(), currencyCode));
- offers.addAll(getOffers(SELL.name(), currencyCode));
- return sortOffersByDate(offers);
+ return offersServiceRequest.getOffersSortedByDate(currencyCode);
}
public List getOffersSortedByDate(String direction, String currencyCode) {
- var offers = getOffers(direction, currencyCode);
- return offers.isEmpty() ? offers : sortOffersByDate(offers);
+ return offersServiceRequest.getOffersSortedByDate(direction, currencyCode);
}
public List getBsqOffersSortedByDate() {
- ArrayList offers = new ArrayList<>();
- offers.addAll(getCryptoCurrencyOffers(BUY.name(), "BSQ"));
- offers.addAll(getCryptoCurrencyOffers(SELL.name(), "BSQ"));
- return sortOffersByDate(offers);
+ return offersServiceRequest.getBsqOffersSortedByDate();
}
public List getMyOffers(String direction, String currencyCode) {
- if (isSupportedCryptoCurrency(currencyCode)) {
- return getMyCryptoCurrencyOffers(direction, currencyCode);
- } else {
- var request = GetMyOffersRequest.newBuilder()
- .setDirection(direction)
- .setCurrencyCode(currencyCode)
- .build();
- return grpcStubs.offersService.getMyOffers(request).getOffersList();
- }
+ return offersServiceRequest.getMyOffers(direction, currencyCode);
}
public List getMyCryptoCurrencyOffers(String direction, String currencyCode) {
- return getMyOffers(direction, "BTC").stream()
- .filter(o -> o.getBaseCurrencyCode().equalsIgnoreCase(currencyCode))
- .collect(toList());
+ return offersServiceRequest.getMyCryptoCurrencyOffers(direction, currencyCode);
}
public List getMyOffersSortedByDate(String direction, String currencyCode) {
- var offers = getMyOffers(direction, currencyCode);
- return offers.isEmpty() ? offers : sortOffersByDate(offers);
+ return offersServiceRequest.getMyOffersSortedByDate(direction, currencyCode);
}
public List getMyOffersSortedByDate(String currencyCode) {
- ArrayList offers = new ArrayList<>();
- offers.addAll(getMyOffers(BUY.name(), currencyCode));
- offers.addAll(getMyOffers(SELL.name(), currencyCode));
- return sortOffersByDate(offers);
+ return offersServiceRequest.getMyOffersSortedByDate(currencyCode);
}
public List getMyBsqOffersSortedByDate() {
- ArrayList offers = new ArrayList<>();
- offers.addAll(getMyCryptoCurrencyOffers(BUY.name(), "BSQ"));
- offers.addAll(getMyCryptoCurrencyOffers(SELL.name(), "BSQ"));
- return sortOffersByDate(offers);
+ return offersServiceRequest.getMyBsqOffersSortedByDate();
}
public OfferInfo getMostRecentOffer(String direction, String currencyCode) {
- List offers = getOffersSortedByDate(direction, currencyCode);
- return offers.isEmpty() ? null : offers.get(offers.size() - 1);
+ return offersServiceRequest.getMostRecentOffer(direction, currencyCode);
}
public List sortOffersByDate(List offerInfoList) {
- return offerInfoList.stream()
- .sorted(comparing(OfferInfo::getDate))
- .collect(toList());
+ return offersServiceRequest.sortOffersByDate(offerInfoList);
}
public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId, String takerFeeCurrencyCode) {
- var request = TakeOfferRequest.newBuilder()
- .setOfferId(offerId)
- .setPaymentAccountId(paymentAccountId)
- .setTakerFeeCurrencyCode(takerFeeCurrencyCode)
- .build();
- return grpcStubs.tradesService.takeOffer(request);
+ return tradesServiceRequest.getTakeOfferReply(offerId, paymentAccountId, takerFeeCurrencyCode);
}
public TradeInfo takeOffer(String offerId, String paymentAccountId, String takerFeeCurrencyCode) {
- var reply = getTakeOfferReply(offerId, paymentAccountId, takerFeeCurrencyCode);
- if (reply.hasTrade())
- return reply.getTrade();
- else
- throw new IllegalStateException(reply.getFailureReason().getDescription());
+ return tradesServiceRequest.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode);
}
public TradeInfo getTrade(String tradeId) {
- var request = GetTradeRequest.newBuilder()
- .setTradeId(tradeId)
- .build();
- return grpcStubs.tradesService.getTrade(request).getTrade();
+ return tradesServiceRequest.getTrade(tradeId);
}
public void confirmPaymentStarted(String tradeId) {
- var request = ConfirmPaymentStartedRequest.newBuilder()
- .setTradeId(tradeId)
- .build();
- grpcStubs.tradesService.confirmPaymentStarted(request);
+ tradesServiceRequest.confirmPaymentStarted(tradeId);
}
public void confirmPaymentReceived(String tradeId) {
- var request = ConfirmPaymentReceivedRequest.newBuilder()
- .setTradeId(tradeId)
- .build();
- grpcStubs.tradesService.confirmPaymentReceived(request);
+ tradesServiceRequest.confirmPaymentReceived(tradeId);
}
public void keepFunds(String tradeId) {
- var request = KeepFundsRequest.newBuilder()
- .setTradeId(tradeId)
- .build();
- grpcStubs.tradesService.keepFunds(request);
+ tradesServiceRequest.keepFunds(tradeId);
}
public void withdrawFunds(String tradeId, String address, String memo) {
- var request = WithdrawFundsRequest.newBuilder()
- .setTradeId(tradeId)
- .setAddress(address)
- .setMemo(memo)
- .build();
- grpcStubs.tradesService.withdrawFunds(request);
+ tradesServiceRequest.withdrawFunds(tradeId, address, memo);
}
public List getPaymentMethods() {
- var request = GetPaymentMethodsRequest.newBuilder().build();
- return grpcStubs.paymentAccountsService.getPaymentMethods(request).getPaymentMethodsList();
+ return paymentAccountsServiceRequest.getPaymentMethods();
}
public String getPaymentAcctFormAsJson(String paymentMethodId) {
- var request = GetPaymentAccountFormRequest.newBuilder()
- .setPaymentMethodId(paymentMethodId)
- .build();
- return grpcStubs.paymentAccountsService.getPaymentAccountForm(request).getPaymentAccountFormJson();
+ return paymentAccountsServiceRequest.getPaymentAcctFormAsJson(paymentMethodId);
}
public PaymentAccount createPaymentAccount(String json) {
- var request = CreatePaymentAccountRequest.newBuilder()
- .setPaymentAccountForm(json)
- .build();
- return grpcStubs.paymentAccountsService.createPaymentAccount(request).getPaymentAccount();
+ return paymentAccountsServiceRequest.createPaymentAccount(json);
}
public List getPaymentAccounts() {
- var request = GetPaymentAccountsRequest.newBuilder().build();
- return grpcStubs.paymentAccountsService.getPaymentAccounts(request).getPaymentAccountsList();
+ return paymentAccountsServiceRequest.getPaymentAccounts();
}
public PaymentAccount createCryptoCurrencyPaymentAccount(String accountName,
String currencyCode,
String address,
boolean tradeInstant) {
- var request = CreateCryptoCurrencyPaymentAccountRequest.newBuilder()
- .setAccountName(accountName)
- .setCurrencyCode(currencyCode)
- .setAddress(address)
- .setTradeInstant(tradeInstant)
- .build();
- return grpcStubs.paymentAccountsService.createCryptoCurrencyPaymentAccount(request).getPaymentAccount();
+ return paymentAccountsServiceRequest.createCryptoCurrencyPaymentAccount(accountName,
+ currencyCode,
+ address,
+ tradeInstant);
}
public List getCryptoPaymentMethods() {
- var request = GetCryptoCurrencyPaymentMethodsRequest.newBuilder().build();
- return grpcStubs.paymentAccountsService.getCryptoCurrencyPaymentMethods(request).getPaymentMethodsList();
+ return paymentAccountsServiceRequest.getCryptoPaymentMethods();
}
public void lockWallet() {
- var request = LockWalletRequest.newBuilder().build();
- grpcStubs.walletsService.lockWallet(request);
+ walletsServiceRequest.lockWallet();
}
public void unlockWallet(String walletPassword, long timeout) {
- var request = UnlockWalletRequest.newBuilder()
- .setPassword(walletPassword)
- .setTimeout(timeout).build();
- grpcStubs.walletsService.unlockWallet(request);
+ walletsServiceRequest.unlockWallet(walletPassword, timeout);
}
public void removeWalletPassword(String walletPassword) {
- var request = RemoveWalletPasswordRequest.newBuilder()
- .setPassword(walletPassword).build();
- grpcStubs.walletsService.removeWalletPassword(request);
+ walletsServiceRequest.removeWalletPassword(walletPassword);
}
public void setWalletPassword(String walletPassword) {
- var request = SetWalletPasswordRequest.newBuilder()
- .setPassword(walletPassword).build();
- grpcStubs.walletsService.setWalletPassword(request);
+ walletsServiceRequest.setWalletPassword(walletPassword);
}
public void setWalletPassword(String oldWalletPassword, String newWalletPassword) {
- var request = SetWalletPasswordRequest.newBuilder()
- .setPassword(oldWalletPassword)
- .setNewPassword(newWalletPassword).build();
- grpcStubs.walletsService.setWalletPassword(request);
+ walletsServiceRequest.setWalletPassword(oldWalletPassword, newWalletPassword);
}
public void registerDisputeAgent(String disputeAgentType, String registrationKey) {
diff --git a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java
new file mode 100644
index 00000000000..b340f25ea7a
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java
@@ -0,0 +1,317 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.cli.request;
+
+import bisq.proto.grpc.CancelOfferRequest;
+import bisq.proto.grpc.CreateOfferRequest;
+import bisq.proto.grpc.EditOfferRequest;
+import bisq.proto.grpc.GetMyOfferRequest;
+import bisq.proto.grpc.GetMyOffersRequest;
+import bisq.proto.grpc.GetOfferRequest;
+import bisq.proto.grpc.GetOffersRequest;
+import bisq.proto.grpc.OfferInfo;
+
+import java.math.BigDecimal;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+import static bisq.proto.grpc.EditOfferRequest.EditType.ACTIVATION_STATE_ONLY;
+import static bisq.proto.grpc.EditOfferRequest.EditType.FIXED_PRICE_ONLY;
+import static bisq.proto.grpc.EditOfferRequest.EditType.MKT_PRICE_MARGIN_ONLY;
+import static bisq.proto.grpc.EditOfferRequest.EditType.TRIGGER_PRICE_ONLY;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+import static protobuf.OfferPayload.Direction.BUY;
+import static protobuf.OfferPayload.Direction.SELL;
+
+
+
+import bisq.cli.GrpcStubs;
+
+public class OffersServiceRequest {
+
+ private final GrpcStubs grpcStubs;
+
+ public OffersServiceRequest(GrpcStubs grpcStubs) {
+ this.grpcStubs = grpcStubs;
+ }
+
+ public OfferInfo createFixedPricedOffer(String direction,
+ String currencyCode,
+ long amount,
+ long minAmount,
+ String fixedPrice,
+ double securityDeposit,
+ String paymentAcctId,
+ String makerFeeCurrencyCode) {
+ return createOffer(direction,
+ currencyCode,
+ amount,
+ minAmount,
+ false,
+ fixedPrice,
+ 0.00,
+ securityDeposit,
+ paymentAcctId,
+ makerFeeCurrencyCode,
+ 0 /* no trigger price */);
+ }
+
+ public OfferInfo createMarketBasedPricedOffer(String direction,
+ String currencyCode,
+ long amount,
+ long minAmount,
+ double marketPriceMargin,
+ double securityDeposit,
+ String paymentAcctId,
+ String makerFeeCurrencyCode,
+ long triggerPrice) {
+ return createOffer(direction,
+ currencyCode,
+ amount,
+ minAmount,
+ true,
+ "0",
+ marketPriceMargin,
+ securityDeposit,
+ paymentAcctId,
+ makerFeeCurrencyCode,
+ triggerPrice);
+ }
+
+ public OfferInfo createOffer(String direction,
+ String currencyCode,
+ long amount,
+ long minAmount,
+ boolean useMarketBasedPrice,
+ String fixedPrice,
+ double marketPriceMargin,
+ double securityDeposit,
+ String paymentAcctId,
+ String makerFeeCurrencyCode,
+ long triggerPrice) {
+ var request = CreateOfferRequest.newBuilder()
+ .setDirection(direction)
+ .setCurrencyCode(currencyCode)
+ .setAmount(amount)
+ .setMinAmount(minAmount)
+ .setUseMarketBasedPrice(useMarketBasedPrice)
+ .setPrice(fixedPrice)
+ .setMarketPriceMargin(marketPriceMargin)
+ .setBuyerSecurityDeposit(securityDeposit)
+ .setPaymentAccountId(paymentAcctId)
+ .setMakerFeeCurrencyCode(makerFeeCurrencyCode)
+ .setTriggerPrice(triggerPrice)
+ .build();
+ return grpcStubs.offersService.createOffer(request).getOffer();
+ }
+
+ // TODO Make sure this is not duplicated anywhere on CLI side.
+ private final Function scaledPriceStringFormat = (price) -> {
+ BigDecimal factor = new BigDecimal(10).pow(4);
+ return new BigDecimal(price).divide(factor).toPlainString();
+ };
+
+ public void editOfferActivationState(String offerId, int enable) {
+ var offer = getMyOffer(offerId);
+ var scaledPriceString = offer.getUseMarketBasedPrice()
+ ? "0.00"
+ : scaledPriceStringFormat.apply(offer.getPrice());
+ editOffer(offerId,
+ scaledPriceString,
+ offer.getUseMarketBasedPrice(),
+ offer.getMarketPriceMargin(),
+ offer.getTriggerPrice(),
+ enable,
+ ACTIVATION_STATE_ONLY);
+ }
+
+ public void editOfferFixedPrice(String offerId, String rawPriceString) {
+ var offer = getMyOffer(offerId);
+ editOffer(offerId,
+ rawPriceString,
+ false,
+ offer.getMarketPriceMargin(),
+ offer.getTriggerPrice(),
+ offer.getIsActivated() ? 1 : 0,
+ FIXED_PRICE_ONLY);
+ }
+
+ public void editOfferPriceMargin(String offerId, double marketPriceMargin) {
+ var offer = getMyOffer(offerId);
+ editOffer(offerId,
+ "0.00",
+ true,
+ marketPriceMargin,
+ offer.getTriggerPrice(),
+ offer.getIsActivated() ? 1 : 0,
+ MKT_PRICE_MARGIN_ONLY);
+ }
+
+ public void editOfferTriggerPrice(String offerId, long triggerPrice) {
+ var offer = getMyOffer(offerId);
+ editOffer(offerId,
+ "0.00",
+ offer.getUseMarketBasedPrice(),
+ offer.getMarketPriceMargin(),
+ triggerPrice,
+ offer.getIsActivated() ? 1 : 0,
+ TRIGGER_PRICE_ONLY);
+ }
+
+ public void editOffer(String offerId,
+ String scaledPriceString,
+ boolean useMarketBasedPrice,
+ double marketPriceMargin,
+ long triggerPrice,
+ int enable,
+ EditOfferRequest.EditType editType) {
+ // Take care when using this method directly:
+ // useMarketBasedPrice = true if margin based offer, false for fixed priced offer
+ // scaledPriceString fmt = ######.####
+ var request = EditOfferRequest.newBuilder()
+ .setId(offerId)
+ .setPrice(scaledPriceString)
+ .setUseMarketBasedPrice(useMarketBasedPrice)
+ .setMarketPriceMargin(marketPriceMargin)
+ .setTriggerPrice(triggerPrice)
+ .setEnable(enable)
+ .setEditType(editType)
+ .build();
+ grpcStubs.offersService.editOffer(request);
+ }
+
+ public void cancelOffer(String offerId) {
+ var request = CancelOfferRequest.newBuilder()
+ .setId(offerId)
+ .build();
+ grpcStubs.offersService.cancelOffer(request);
+ }
+
+ public OfferInfo getOffer(String offerId) {
+ var request = GetOfferRequest.newBuilder()
+ .setId(offerId)
+ .build();
+ return grpcStubs.offersService.getOffer(request).getOffer();
+ }
+
+ public OfferInfo getMyOffer(String offerId) {
+ var request = GetMyOfferRequest.newBuilder()
+ .setId(offerId)
+ .build();
+ return grpcStubs.offersService.getMyOffer(request).getOffer();
+ }
+
+ public List getOffers(String direction, String currencyCode) {
+ if (isSupportedCryptoCurrency(currencyCode)) {
+ return getCryptoCurrencyOffers(direction, currencyCode);
+ } else {
+ var request = GetOffersRequest.newBuilder()
+ .setDirection(direction)
+ .setCurrencyCode(currencyCode)
+ .build();
+ return grpcStubs.offersService.getOffers(request).getOffersList();
+ }
+ }
+
+ public List getCryptoCurrencyOffers(String direction, String currencyCode) {
+ return getOffers(direction, "BTC").stream()
+ .filter(o -> o.getBaseCurrencyCode().equalsIgnoreCase(currencyCode))
+ .collect(toList());
+ }
+
+ public List getOffersSortedByDate(String currencyCode) {
+ ArrayList offers = new ArrayList<>();
+ offers.addAll(getOffers(BUY.name(), currencyCode));
+ offers.addAll(getOffers(SELL.name(), currencyCode));
+ return sortOffersByDate(offers);
+ }
+
+ public List getOffersSortedByDate(String direction, String currencyCode) {
+ var offers = getOffers(direction, currencyCode);
+ return offers.isEmpty() ? offers : sortOffersByDate(offers);
+ }
+
+ public List getBsqOffersSortedByDate() {
+ ArrayList offers = new ArrayList<>();
+ offers.addAll(getCryptoCurrencyOffers(BUY.name(), "BSQ"));
+ offers.addAll(getCryptoCurrencyOffers(SELL.name(), "BSQ"));
+ return sortOffersByDate(offers);
+ }
+
+ public List getMyOffers(String direction, String currencyCode) {
+ if (isSupportedCryptoCurrency(currencyCode)) {
+ return getMyCryptoCurrencyOffers(direction, currencyCode);
+ } else {
+ var request = GetMyOffersRequest.newBuilder()
+ .setDirection(direction)
+ .setCurrencyCode(currencyCode)
+ .build();
+ return grpcStubs.offersService.getMyOffers(request).getOffersList();
+ }
+ }
+
+ public List getMyCryptoCurrencyOffers(String direction, String currencyCode) {
+ return getMyOffers(direction, "BTC").stream()
+ .filter(o -> o.getBaseCurrencyCode().equalsIgnoreCase(currencyCode))
+ .collect(toList());
+ }
+
+ public List getMyOffersSortedByDate(String direction, String currencyCode) {
+ var offers = getMyOffers(direction, currencyCode);
+ return offers.isEmpty() ? offers : sortOffersByDate(offers);
+ }
+
+ public List getMyOffersSortedByDate(String currencyCode) {
+ ArrayList offers = new ArrayList<>();
+ offers.addAll(getMyOffers(BUY.name(), currencyCode));
+ offers.addAll(getMyOffers(SELL.name(), currencyCode));
+ return sortOffersByDate(offers);
+ }
+
+ public List getMyBsqOffersSortedByDate() {
+ ArrayList offers = new ArrayList<>();
+ offers.addAll(getMyCryptoCurrencyOffers(BUY.name(), "BSQ"));
+ offers.addAll(getMyCryptoCurrencyOffers(SELL.name(), "BSQ"));
+ return sortOffersByDate(offers);
+ }
+
+ public OfferInfo getMostRecentOffer(String direction, String currencyCode) {
+ List offers = getOffersSortedByDate(direction, currencyCode);
+ return offers.isEmpty() ? null : offers.get(offers.size() - 1);
+ }
+
+ public List sortOffersByDate(List offerInfoList) {
+ return offerInfoList.stream()
+ .sorted(comparing(OfferInfo::getDate))
+ .collect(toList());
+ }
+
+ private static boolean isSupportedCryptoCurrency(String currencyCode) {
+ return getSupportedCryptoCurrencies().contains(currencyCode.toUpperCase());
+ }
+
+ private static List getSupportedCryptoCurrencies() {
+ final List result = new ArrayList<>();
+ result.add("BSQ");
+ result.sort(String::compareTo);
+ return result;
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/request/PaymentAccountsServiceRequest.java b/cli/src/main/java/bisq/cli/request/PaymentAccountsServiceRequest.java
new file mode 100644
index 00000000000..467aa51462e
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/request/PaymentAccountsServiceRequest.java
@@ -0,0 +1,85 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.cli.request;
+
+import bisq.proto.grpc.CreateCryptoCurrencyPaymentAccountRequest;
+import bisq.proto.grpc.CreatePaymentAccountRequest;
+import bisq.proto.grpc.GetCryptoCurrencyPaymentMethodsRequest;
+import bisq.proto.grpc.GetPaymentAccountFormRequest;
+import bisq.proto.grpc.GetPaymentAccountsRequest;
+import bisq.proto.grpc.GetPaymentMethodsRequest;
+
+import protobuf.PaymentAccount;
+import protobuf.PaymentMethod;
+
+import java.util.List;
+
+
+
+import bisq.cli.GrpcStubs;
+
+public class PaymentAccountsServiceRequest {
+
+ private final GrpcStubs grpcStubs;
+
+ public PaymentAccountsServiceRequest(GrpcStubs grpcStubs) {
+ this.grpcStubs = grpcStubs;
+ }
+
+ public List getPaymentMethods() {
+ var request = GetPaymentMethodsRequest.newBuilder().build();
+ return grpcStubs.paymentAccountsService.getPaymentMethods(request).getPaymentMethodsList();
+ }
+
+ public String getPaymentAcctFormAsJson(String paymentMethodId) {
+ var request = GetPaymentAccountFormRequest.newBuilder()
+ .setPaymentMethodId(paymentMethodId)
+ .build();
+ return grpcStubs.paymentAccountsService.getPaymentAccountForm(request).getPaymentAccountFormJson();
+ }
+
+ public PaymentAccount createPaymentAccount(String json) {
+ var request = CreatePaymentAccountRequest.newBuilder()
+ .setPaymentAccountForm(json)
+ .build();
+ return grpcStubs.paymentAccountsService.createPaymentAccount(request).getPaymentAccount();
+ }
+
+ public List getPaymentAccounts() {
+ var request = GetPaymentAccountsRequest.newBuilder().build();
+ return grpcStubs.paymentAccountsService.getPaymentAccounts(request).getPaymentAccountsList();
+ }
+
+ public PaymentAccount createCryptoCurrencyPaymentAccount(String accountName,
+ String currencyCode,
+ String address,
+ boolean tradeInstant) {
+ var request = CreateCryptoCurrencyPaymentAccountRequest.newBuilder()
+ .setAccountName(accountName)
+ .setCurrencyCode(currencyCode)
+ .setAddress(address)
+ .setTradeInstant(tradeInstant)
+ .build();
+ return grpcStubs.paymentAccountsService.createCryptoCurrencyPaymentAccount(request).getPaymentAccount();
+ }
+
+ public List getCryptoPaymentMethods() {
+ var request = GetCryptoCurrencyPaymentMethodsRequest.newBuilder().build();
+ return grpcStubs.paymentAccountsService.getCryptoCurrencyPaymentMethods(request).getPaymentMethodsList();
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java b/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java
new file mode 100644
index 00000000000..6d57bb03547
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java
@@ -0,0 +1,94 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.cli.request;
+
+import bisq.proto.grpc.ConfirmPaymentReceivedRequest;
+import bisq.proto.grpc.ConfirmPaymentStartedRequest;
+import bisq.proto.grpc.GetTradeRequest;
+import bisq.proto.grpc.KeepFundsRequest;
+import bisq.proto.grpc.TakeOfferReply;
+import bisq.proto.grpc.TakeOfferRequest;
+import bisq.proto.grpc.TradeInfo;
+import bisq.proto.grpc.WithdrawFundsRequest;
+
+
+
+import bisq.cli.GrpcStubs;
+
+public class TradesServiceRequest {
+
+ private final GrpcStubs grpcStubs;
+
+ public TradesServiceRequest(GrpcStubs grpcStubs) {
+ this.grpcStubs = grpcStubs;
+ }
+
+ public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId, String takerFeeCurrencyCode) {
+ var request = TakeOfferRequest.newBuilder()
+ .setOfferId(offerId)
+ .setPaymentAccountId(paymentAccountId)
+ .setTakerFeeCurrencyCode(takerFeeCurrencyCode)
+ .build();
+ return grpcStubs.tradesService.takeOffer(request);
+ }
+
+ public TradeInfo takeOffer(String offerId, String paymentAccountId, String takerFeeCurrencyCode) {
+ var reply = getTakeOfferReply(offerId, paymentAccountId, takerFeeCurrencyCode);
+ if (reply.hasTrade())
+ return reply.getTrade();
+ else
+ throw new IllegalStateException(reply.getFailureReason().getDescription());
+ }
+
+ public TradeInfo getTrade(String tradeId) {
+ var request = GetTradeRequest.newBuilder()
+ .setTradeId(tradeId)
+ .build();
+ return grpcStubs.tradesService.getTrade(request).getTrade();
+ }
+
+ public void confirmPaymentStarted(String tradeId) {
+ var request = ConfirmPaymentStartedRequest.newBuilder()
+ .setTradeId(tradeId)
+ .build();
+ grpcStubs.tradesService.confirmPaymentStarted(request);
+ }
+
+ public void confirmPaymentReceived(String tradeId) {
+ var request = ConfirmPaymentReceivedRequest.newBuilder()
+ .setTradeId(tradeId)
+ .build();
+ grpcStubs.tradesService.confirmPaymentReceived(request);
+ }
+
+ public void keepFunds(String tradeId) {
+ var request = KeepFundsRequest.newBuilder()
+ .setTradeId(tradeId)
+ .build();
+ grpcStubs.tradesService.keepFunds(request);
+ }
+
+ public void withdrawFunds(String tradeId, String address, String memo) {
+ var request = WithdrawFundsRequest.newBuilder()
+ .setTradeId(tradeId)
+ .setAddress(address)
+ .setMemo(memo)
+ .build();
+ grpcStubs.tradesService.withdrawFunds(request);
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/request/WalletsServiceRequest.java b/cli/src/main/java/bisq/cli/request/WalletsServiceRequest.java
new file mode 100644
index 00000000000..e4e7f07c5f5
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/request/WalletsServiceRequest.java
@@ -0,0 +1,192 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.cli.request;
+
+import bisq.proto.grpc.AddressBalanceInfo;
+import bisq.proto.grpc.BalancesInfo;
+import bisq.proto.grpc.BsqBalanceInfo;
+import bisq.proto.grpc.BtcBalanceInfo;
+import bisq.proto.grpc.GetAddressBalanceRequest;
+import bisq.proto.grpc.GetBalancesRequest;
+import bisq.proto.grpc.GetFundingAddressesRequest;
+import bisq.proto.grpc.GetTransactionRequest;
+import bisq.proto.grpc.GetTxFeeRateRequest;
+import bisq.proto.grpc.GetUnusedBsqAddressRequest;
+import bisq.proto.grpc.LockWalletRequest;
+import bisq.proto.grpc.MarketPriceRequest;
+import bisq.proto.grpc.RemoveWalletPasswordRequest;
+import bisq.proto.grpc.SendBsqRequest;
+import bisq.proto.grpc.SendBtcRequest;
+import bisq.proto.grpc.SetTxFeeRatePreferenceRequest;
+import bisq.proto.grpc.SetWalletPasswordRequest;
+import bisq.proto.grpc.TxFeeRateInfo;
+import bisq.proto.grpc.TxInfo;
+import bisq.proto.grpc.UnlockWalletRequest;
+import bisq.proto.grpc.UnsetTxFeeRatePreferenceRequest;
+import bisq.proto.grpc.VerifyBsqSentToAddressRequest;
+
+import java.util.List;
+
+
+
+import bisq.cli.GrpcStubs;
+
+public class WalletsServiceRequest {
+
+ private final GrpcStubs grpcStubs;
+
+ public WalletsServiceRequest(GrpcStubs grpcStubs) {
+ this.grpcStubs = grpcStubs;
+ }
+
+ public BalancesInfo getBalances() {
+ return getBalances("");
+ }
+
+ public BsqBalanceInfo getBsqBalances() {
+ return getBalances("BSQ").getBsq();
+ }
+
+ public BtcBalanceInfo getBtcBalances() {
+ return getBalances("BTC").getBtc();
+ }
+
+ public BalancesInfo getBalances(String currencyCode) {
+ var request = GetBalancesRequest.newBuilder()
+ .setCurrencyCode(currencyCode)
+ .build();
+ return grpcStubs.walletsService.getBalances(request).getBalances();
+ }
+
+ public AddressBalanceInfo getAddressBalance(String address) {
+ var request = GetAddressBalanceRequest.newBuilder()
+ .setAddress(address).build();
+ return grpcStubs.walletsService.getAddressBalance(request).getAddressBalanceInfo();
+ }
+
+ public double getBtcPrice(String currencyCode) {
+ var request = MarketPriceRequest.newBuilder()
+ .setCurrencyCode(currencyCode)
+ .build();
+ return grpcStubs.priceService.getMarketPrice(request).getPrice();
+ }
+
+ public List getFundingAddresses() {
+ var request = GetFundingAddressesRequest.newBuilder().build();
+ return grpcStubs.walletsService.getFundingAddresses(request).getAddressBalanceInfoList();
+ }
+
+ public String getUnusedBsqAddress() {
+ var request = GetUnusedBsqAddressRequest.newBuilder().build();
+ return grpcStubs.walletsService.getUnusedBsqAddress(request).getAddress();
+ }
+
+ public String getUnusedBtcAddress() {
+ var request = GetFundingAddressesRequest.newBuilder().build();
+ var addressBalances = grpcStubs.walletsService.getFundingAddresses(request)
+ .getAddressBalanceInfoList();
+ //noinspection OptionalGetWithoutIsPresent
+ return addressBalances.stream()
+ .filter(AddressBalanceInfo::getIsAddressUnused)
+ .findFirst()
+ .get()
+ .getAddress();
+ }
+
+ public TxInfo sendBsq(String address, String amount, String txFeeRate) {
+ var request = SendBsqRequest.newBuilder()
+ .setAddress(address)
+ .setAmount(amount)
+ .setTxFeeRate(txFeeRate)
+ .build();
+ return grpcStubs.walletsService.sendBsq(request).getTxInfo();
+ }
+
+ public TxInfo sendBtc(String address, String amount, String txFeeRate, String memo) {
+ var request = SendBtcRequest.newBuilder()
+ .setAddress(address)
+ .setAmount(amount)
+ .setTxFeeRate(txFeeRate)
+ .setMemo(memo)
+ .build();
+ return grpcStubs.walletsService.sendBtc(request).getTxInfo();
+ }
+
+ public boolean verifyBsqSentToAddress(String address, String amount) {
+ var request = VerifyBsqSentToAddressRequest.newBuilder()
+ .setAddress(address)
+ .setAmount(amount)
+ .build();
+ return grpcStubs.walletsService.verifyBsqSentToAddress(request).getIsAmountReceived();
+ }
+
+ public TxFeeRateInfo getTxFeeRate() {
+ var request = GetTxFeeRateRequest.newBuilder().build();
+ return grpcStubs.walletsService.getTxFeeRate(request).getTxFeeRateInfo();
+ }
+
+ public TxFeeRateInfo setTxFeeRate(long txFeeRate) {
+ var request = SetTxFeeRatePreferenceRequest.newBuilder()
+ .setTxFeeRatePreference(txFeeRate)
+ .build();
+ return grpcStubs.walletsService.setTxFeeRatePreference(request).getTxFeeRateInfo();
+ }
+
+ public TxFeeRateInfo unsetTxFeeRate() {
+ var request = UnsetTxFeeRatePreferenceRequest.newBuilder().build();
+ return grpcStubs.walletsService.unsetTxFeeRatePreference(request).getTxFeeRateInfo();
+ }
+
+ public TxInfo getTransaction(String txId) {
+ var request = GetTransactionRequest.newBuilder()
+ .setTxId(txId)
+ .build();
+ return grpcStubs.walletsService.getTransaction(request).getTxInfo();
+ }
+
+ public void lockWallet() {
+ var request = LockWalletRequest.newBuilder().build();
+ grpcStubs.walletsService.lockWallet(request);
+ }
+
+ public void unlockWallet(String walletPassword, long timeout) {
+ var request = UnlockWalletRequest.newBuilder()
+ .setPassword(walletPassword)
+ .setTimeout(timeout).build();
+ grpcStubs.walletsService.unlockWallet(request);
+ }
+
+ public void removeWalletPassword(String walletPassword) {
+ var request = RemoveWalletPasswordRequest.newBuilder()
+ .setPassword(walletPassword).build();
+ grpcStubs.walletsService.removeWalletPassword(request);
+ }
+
+ public void setWalletPassword(String walletPassword) {
+ var request = SetWalletPasswordRequest.newBuilder()
+ .setPassword(walletPassword).build();
+ grpcStubs.walletsService.setWalletPassword(request);
+ }
+
+ public void setWalletPassword(String oldWalletPassword, String newWalletPassword) {
+ var request = SetWalletPasswordRequest.newBuilder()
+ .setPassword(oldWalletPassword)
+ .setNewPassword(newWalletPassword).build();
+ grpcStubs.walletsService.setWalletPassword(request);
+ }
+}
From d2939cc5676ce840d6af771d0321b0ffe2579b7c Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 13 Jun 2021 12:43:02 -0300
Subject: [PATCH 06/40] Add new EditOfferOptionParser and test
---
.../cli/opts/AbstractMethodOptionParser.java | 4 +
.../bisq/cli/opts/EditOfferOptionParser.java | 264 ++++++++++++++
cli/src/main/java/bisq/cli/opts/OptLabel.java | 2 +
.../cli/opt/EditOfferOptionParserTest.java | 325 ++++++++++++++++++
.../java/bisq/cli/opt/OptionParsersTest.java | 2 +-
5 files changed, 596 insertions(+), 1 deletion(-)
create mode 100644 cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java
create mode 100644 cli/src/test/java/bisq/cli/opt/EditOfferOptionParserTest.java
diff --git a/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java b/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java
index 25256eb6a99..e0b08ed7713 100644
--- a/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java
+++ b/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java
@@ -24,6 +24,7 @@
import java.util.List;
import java.util.function.Function;
+import java.util.function.Predicate;
import lombok.Getter;
@@ -64,6 +65,9 @@ public boolean isForHelp() {
return options.has(helpOpt);
}
+ protected final Predicate> valueNotSpecified = (opt) ->
+ !options.hasArgument(opt) || options.valueOf(opt).isEmpty();
+
private final Function cliExceptionMessageStyle = (ex) -> {
if (ex.getMessage() == null)
return null;
diff --git a/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java
new file mode 100644
index 00000000000..8a59c891dae
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java
@@ -0,0 +1,264 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.cli.opts;
+
+
+import bisq.proto.grpc.EditOfferRequest;
+
+import joptsimple.OptionSpec;
+
+import java.math.BigDecimal;
+
+import static bisq.cli.opts.OptLabel.*;
+import static bisq.proto.grpc.EditOfferRequest.EditType.*;
+import static java.lang.String.format;
+
+
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class EditOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to cancel")
+ .withRequiredArg();
+
+ final OptionSpec fixedPriceOpt = parser.accepts(OPT_FIXED_PRICE, "fixed btc price")
+ .withOptionalArg()
+ .defaultsTo("0");
+
+ final OptionSpec mktPriceMarginOpt = parser.accepts(OPT_MKT_PRICE_MARGIN,
+ "market btc price margin (%)")
+ .withOptionalArg()
+ .defaultsTo("0.00");
+
+ final OptionSpec triggerPriceOpt = parser.accepts(OPT_TRIGGER_PRICE,
+ "trigger price (applies to mkt price margin based offers)")
+ .withOptionalArg()
+ .defaultsTo("0");
+
+ // The 'enable' string opt is optional, and can be empty (meaning do not change
+ // activation state). For this reason, a boolean type is not used (can only be
+ // true or false).
+ final OptionSpec enableOpt = parser.accepts(OPT_ENABLE,
+ "enable or disable offer")
+ .withOptionalArg()
+ .ofType(String.class);
+
+ private EditOfferRequest.EditType offerEditType;
+
+ public EditOfferOptionParser(String[] args) {
+ super(args);
+ }
+
+ public EditOfferOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(offerIdOpt) || options.valueOf(offerIdOpt).isEmpty())
+ throw new IllegalArgumentException("no offer id specified");
+
+ boolean hasNoEditDetails = !options.has(fixedPriceOpt)
+ && !options.has(mktPriceMarginOpt)
+ && !options.has(triggerPriceOpt)
+ && !options.has(enableOpt);
+ if (hasNoEditDetails)
+ throw new IllegalArgumentException("no edit details specified");
+
+ if (options.has(enableOpt)) {
+ if (valueNotSpecified.test(enableOpt))
+ throw new IllegalArgumentException("invalid enable value specified, must be true|false");
+
+ var enableOptValue = options.valueOf(enableOpt);
+ if (!enableOptValue.equalsIgnoreCase("true")
+ && !enableOptValue.equalsIgnoreCase("false"))
+ throw new IllegalArgumentException("invalid enable value specified, must be true|false");
+
+ // A single enable opt is a valid opt combo.
+ boolean enableOptIsOnlyOpt = !options.has(fixedPriceOpt)
+ && !options.has(mktPriceMarginOpt)
+ && !options.has(triggerPriceOpt);
+ if (enableOptIsOnlyOpt) {
+ offerEditType = ACTIVATION_STATE_ONLY;
+ return this;
+ }
+ }
+
+ if (options.has(fixedPriceOpt)) {
+ if (valueNotSpecified.test(fixedPriceOpt))
+ throw new IllegalArgumentException("no fixed price specified");
+
+ String fixedPriceAsString = options.valueOf(fixedPriceOpt);
+ try {
+ Double.valueOf(fixedPriceAsString);
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException(format("%s is not a number", fixedPriceAsString));
+ }
+
+ boolean fixedPriceOptIsOnlyOpt = !options.has(mktPriceMarginOpt)
+ && !options.has(triggerPriceOpt)
+ && !options.has(enableOpt);
+ if (fixedPriceOptIsOnlyOpt) {
+ offerEditType = FIXED_PRICE_ONLY;
+ return this;
+ }
+
+ boolean fixedPriceOptAndEnableOptAreOnlyOpts = options.has(enableOpt)
+ && !options.has(mktPriceMarginOpt)
+ && !options.has(triggerPriceOpt);
+ if (fixedPriceOptAndEnableOptAreOnlyOpts) {
+ offerEditType = FIXED_PRICE_AND_ACTIVATION_STATE;
+ return this;
+ }
+ }
+
+ if (options.has(mktPriceMarginOpt)) {
+ if (valueNotSpecified.test(mktPriceMarginOpt))
+ throw new IllegalArgumentException("no mkt price margin specified");
+
+ String priceMarginAsString = options.valueOf(mktPriceMarginOpt);
+ if (priceMarginAsString.isEmpty())
+ throw new IllegalArgumentException("no market price margin specified");
+
+ try {
+ Double.valueOf(priceMarginAsString);
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException(format("%s is not a number", priceMarginAsString));
+ }
+
+ boolean mktPriceMarginOptIsOnlyOpt = !options.has(triggerPriceOpt)
+ && !options.has(fixedPriceOpt)
+ && !options.has(enableOpt);
+ if (mktPriceMarginOptIsOnlyOpt) {
+ offerEditType = MKT_PRICE_MARGIN_ONLY;
+ return this;
+ }
+
+ boolean mktPriceMarginOptAndEnableOptAreOnlyOpts = options.has(enableOpt)
+ && !options.has(triggerPriceOpt);
+ if (mktPriceMarginOptAndEnableOptAreOnlyOpts) {
+ offerEditType = MKT_PRICE_MARGIN_AND_ACTIVATION_STATE;
+ return this;
+ }
+ }
+
+ if (options.has(triggerPriceOpt)) {
+ if (valueNotSpecified.test(triggerPriceOpt))
+ throw new IllegalArgumentException("no trigger price specified");
+
+ String triggerPriceAsString = options.valueOf(fixedPriceOpt);
+ if (triggerPriceAsString.isEmpty())
+ throw new IllegalArgumentException("trigger price not specified");
+
+ try {
+ Double.valueOf(triggerPriceAsString);
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException(format("%s is not a number", triggerPriceAsString));
+ }
+
+ boolean triggerPriceOptIsOnlyOpt = !options.has(mktPriceMarginOpt)
+ && !options.has(fixedPriceOpt)
+ && !options.has(enableOpt);
+ if (triggerPriceOptIsOnlyOpt) {
+ offerEditType = TRIGGER_PRICE_ONLY;
+ return this;
+ }
+
+ boolean triggerPriceOptAndEnableOptAreOnlyOpts = !options.has(mktPriceMarginOpt)
+ && !options.has(fixedPriceOpt)
+ && options.has(enableOpt);
+ if (triggerPriceOptAndEnableOptAreOnlyOpts) {
+ offerEditType = TRIGGER_PRICE_AND_ACTIVATION_STATE;
+ return this;
+ }
+ }
+
+ if (options.has(mktPriceMarginOpt) && options.has(fixedPriceOpt))
+ throw new IllegalArgumentException("cannot specify market price margin and fixed price");
+
+ if (options.has(fixedPriceOpt) && options.has(triggerPriceOpt))
+ throw new IllegalArgumentException("trigger price cannot be set on fixed price offers");
+
+ if (options.has(mktPriceMarginOpt) && options.has(triggerPriceOpt) && !options.has(enableOpt)) {
+ offerEditType = MKT_PRICE_MARGIN_AND_TRIGGER_PRICE;
+ return this;
+ }
+
+ if (options.has(mktPriceMarginOpt) && options.has(triggerPriceOpt) && options.has(enableOpt)) {
+ offerEditType = MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE;
+ return this;
+ }
+
+ return this;
+ }
+
+ public String getOfferId() {
+ return options.valueOf(offerIdOpt);
+ }
+
+ public String getFixedPrice() {
+ return options.has(fixedPriceOpt) ? options.valueOf(fixedPriceOpt) : "0";
+ }
+
+ public String getTriggerPrice() {
+ return options.has(triggerPriceOpt) ? options.valueOf(triggerPriceOpt) : "0";
+ }
+
+ public BigDecimal getTriggerPriceAsBigDecimal() {
+ return new BigDecimal(getTriggerPrice());
+ }
+
+ public String getMktPriceMargin() {
+ return isUsingMktPriceMargin() ? options.valueOf(mktPriceMarginOpt) : "0.00";
+ }
+
+ public BigDecimal getMktPriceMarginAsBigDecimal() {
+ return isUsingMktPriceMargin()
+ ? new BigDecimal(options.valueOf(mktPriceMarginOpt))
+ : BigDecimal.ZERO;
+ }
+
+ public boolean isUsingMktPriceMargin() {
+ return options.has(mktPriceMarginOpt);
+ }
+
+ public int getEnableAsSignedInt() {
+ // Client sends sint32 in grpc request, not a bool that can only be true or false.
+ // If enable = -1, do not change activation state
+ // If enable = 0, set state = AVAILABLE
+ // If enable = 1, set state = DEACTIVATED
+ @Nullable
+ Boolean input = isEnable();
+ return input == null
+ ? -1
+ : input ? 1 : 0;
+ }
+
+ @Nullable
+ public Boolean isEnable() {
+ return options.has(enableOpt)
+ ? Boolean.valueOf(options.valueOf(enableOpt))
+ : null;
+ }
+
+ public EditOfferRequest.EditType getOfferEditType() {
+ return offerEditType;
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/OptLabel.java b/cli/src/main/java/bisq/cli/opts/OptLabel.java
index 084c230aae3..70dda3e6fc3 100644
--- a/cli/src/main/java/bisq/cli/opts/OptLabel.java
+++ b/cli/src/main/java/bisq/cli/opts/OptLabel.java
@@ -27,6 +27,7 @@ public class OptLabel {
public final static String OPT_CURRENCY_CODE = "currency-code";
public final static String OPT_DIRECTION = "direction";
public final static String OPT_DISPUTE_AGENT_TYPE = "dispute-agent-type";
+ public final static String OPT_ENABLE = "enable";
public final static String OPT_FEE_CURRENCY = "fee-currency";
public final static String OPT_FIXED_PRICE = "fixed-price";
public final static String OPT_HELP = "help";
@@ -47,6 +48,7 @@ public class OptLabel {
public final static String OPT_TRADE_INSTANT = "trade-instant";
public final static String OPT_TIMEOUT = "timeout";
public final static String OPT_TRANSACTION_ID = "transaction-id";
+ public final static String OPT_TRIGGER_PRICE = "trigger-price";
public final static String OPT_TX_FEE_RATE = "tx-fee-rate";
public final static String OPT_WALLET_PASSWORD = "wallet-password";
public final static String OPT_NEW_WALLET_PASSWORD = "new-wallet-password";
diff --git a/cli/src/test/java/bisq/cli/opt/EditOfferOptionParserTest.java b/cli/src/test/java/bisq/cli/opt/EditOfferOptionParserTest.java
new file mode 100644
index 00000000000..3305b0cb2cf
--- /dev/null
+++ b/cli/src/test/java/bisq/cli/opt/EditOfferOptionParserTest.java
@@ -0,0 +1,325 @@
+package bisq.cli.opt;
+
+import org.junit.jupiter.api.Test;
+
+import static bisq.cli.Method.editoffer;
+import static bisq.cli.opts.OptLabel.*;
+import static bisq.proto.grpc.EditOfferRequest.EditType.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+
+
+import bisq.cli.opts.EditOfferOptionParser;
+
+// This opt parser test has the most thorough coverage,
+// and is a reference for other opt parser tests.
+public class EditOfferOptionParserTest {
+
+ private static final String PASSWORD_OPT = "--" + OPT_PASSWORD + "=" + "xyz";
+
+ @Test
+ public void testEditOfferWithMissingOfferIdOptShouldThrowException() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name()
+ };
+ Throwable exception = assertThrows(RuntimeException.class, () ->
+ new EditOfferOptionParser(args).parse());
+ assertEquals("no offer id specified", exception.getMessage());
+ }
+
+ @Test
+ public void testEditOfferWithoutAnyOptsShouldThrowException() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID"
+ };
+ Throwable exception = assertThrows(RuntimeException.class, () ->
+ new EditOfferOptionParser(args).parse());
+ assertEquals("no edit details specified", exception.getMessage());
+ }
+
+ @Test
+ public void testEditOfferWithEmptyEnableOptValueShouldThrowException() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_ENABLE + "=" // missing opt value
+ };
+ Throwable exception = assertThrows(RuntimeException.class, () ->
+ new EditOfferOptionParser(args).parse());
+ assertEquals("invalid enable value specified, must be true|false",
+ exception.getMessage());
+ }
+
+ @Test
+ public void testEditOfferWithMissingEnableValueShouldThrowException() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_ENABLE // missing equals sign & opt value
+ };
+ Throwable exception = assertThrows(RuntimeException.class, () ->
+ new EditOfferOptionParser(args).parse());
+ assertEquals("invalid enable value specified, must be true|false",
+ exception.getMessage());
+ }
+
+ @Test
+ public void testEditOfferWithInvalidEnableValueShouldThrowException() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_ENABLE + "=0"
+ };
+ Throwable exception = assertThrows(RuntimeException.class, () ->
+ new EditOfferOptionParser(args).parse());
+ assertEquals("invalid enable value specified, must be true|false",
+ exception.getMessage());
+ }
+
+ @Test
+ public void testEditOfferWithMktPriceOptAndFixedPriceOptShouldThrowException() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_MKT_PRICE_MARGIN + "=0.11",
+ "--" + OPT_FIXED_PRICE + "=50000.0000"
+ };
+ Throwable exception = assertThrows(RuntimeException.class, () ->
+ new EditOfferOptionParser(args).parse());
+ assertEquals("cannot specify market price margin and fixed price",
+ exception.getMessage());
+ }
+
+ @Test
+ public void testEditOfferWithFixedPriceOptAndTriggerPriceOptShouldThrowException() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_FIXED_PRICE + "=50000.0000",
+ "--" + OPT_TRIGGER_PRICE + "=51000.0000"
+ };
+ Throwable exception = assertThrows(RuntimeException.class, () ->
+ new EditOfferOptionParser(args).parse());
+ assertEquals("trigger price cannot be set on fixed price offers",
+ exception.getMessage());
+ }
+
+ @Test
+ public void testEditOfferActivationStateOnly() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_ENABLE + "=" + "true"
+ };
+ EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
+ assertEquals(ACTIVATION_STATE_ONLY, parser.getOfferEditType());
+ assertEquals(1, parser.getEnableAsSignedInt());
+ }
+
+ @Test
+ public void testEditOfferFixedPriceWithoutOptValueShouldThrowException1() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_FIXED_PRICE + "="
+ };
+ Throwable exception = assertThrows(RuntimeException.class, () ->
+ new EditOfferOptionParser(args).parse());
+ assertEquals("no fixed price specified",
+ exception.getMessage());
+ }
+
+ @Test
+ public void testEditOfferFixedPriceWithoutOptValueShouldThrowException2() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_FIXED_PRICE
+ };
+ Throwable exception = assertThrows(RuntimeException.class, () ->
+ new EditOfferOptionParser(args).parse());
+ assertEquals("no fixed price specified",
+ exception.getMessage());
+ }
+
+ @Test
+ public void testEditOfferFixedPriceOnly() {
+ String fixedPriceAsString = "50000.0000";
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_FIXED_PRICE + "=" + fixedPriceAsString
+ };
+ EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
+ assertEquals(FIXED_PRICE_ONLY, parser.getOfferEditType());
+ assertEquals(fixedPriceAsString, parser.getFixedPrice());
+ }
+
+ @Test
+ public void testEditOfferFixedPriceAndActivationStateOnly() {
+ String fixedPriceAsString = "50000.0000";
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_FIXED_PRICE + "=" + fixedPriceAsString,
+ "--" + OPT_ENABLE + "=" + "false"
+ };
+ EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
+ assertEquals(FIXED_PRICE_AND_ACTIVATION_STATE, parser.getOfferEditType());
+ assertEquals(fixedPriceAsString, parser.getFixedPrice());
+ assertEquals(0, parser.getEnableAsSignedInt());
+ }
+
+ @Test
+ public void testEditOfferMktPriceMarginOnly() {
+ String mktPriceMarginAsString = "0.25";
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_MKT_PRICE_MARGIN + "=" + mktPriceMarginAsString
+ };
+ EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
+ assertEquals(MKT_PRICE_MARGIN_ONLY, parser.getOfferEditType());
+ assertEquals(mktPriceMarginAsString, parser.getMktPriceMargin());
+ }
+
+ @Test
+ public void testEditOfferMktPriceMarginWithoutOptValueShouldThrowException() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_MKT_PRICE_MARGIN
+ };
+ Throwable exception = assertThrows(RuntimeException.class, () ->
+ new EditOfferOptionParser(args).parse());
+ assertEquals("no mkt price margin specified",
+ exception.getMessage());
+ }
+
+ @Test
+ public void testEditOfferMktPriceMarginAndActivationStateOnly() {
+ String mktPriceMarginAsString = "0.15";
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_MKT_PRICE_MARGIN + "=" + mktPriceMarginAsString,
+ "--" + OPT_ENABLE + "=" + "false"
+ };
+ EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
+ assertEquals(MKT_PRICE_MARGIN_AND_ACTIVATION_STATE, parser.getOfferEditType());
+ assertEquals(mktPriceMarginAsString, parser.getMktPriceMargin());
+ assertEquals(0, parser.getEnableAsSignedInt());
+ }
+
+ @Test
+ public void testEditTriggerPriceOnly() {
+ String triggerPriceAsString = "50000.0000";
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_TRIGGER_PRICE + "=" + triggerPriceAsString
+ };
+ EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
+ assertEquals(TRIGGER_PRICE_ONLY, parser.getOfferEditType());
+ assertEquals(triggerPriceAsString, parser.getTriggerPrice());
+ }
+
+ @Test
+ public void testEditTriggerPriceWithoutOptValueShouldThrowException1() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_TRIGGER_PRICE + "="
+ };
+ Throwable exception = assertThrows(RuntimeException.class, () ->
+ new EditOfferOptionParser(args).parse());
+ assertEquals("no trigger price specified",
+ exception.getMessage());
+ }
+
+ @Test
+ public void testEditTriggerPriceWithoutOptValueShouldThrowException2() {
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_TRIGGER_PRICE
+ };
+ Throwable exception = assertThrows(RuntimeException.class, () ->
+ new EditOfferOptionParser(args).parse());
+ assertEquals("no trigger price specified",
+ exception.getMessage());
+ }
+
+ @Test
+ public void testEditTriggerPriceAndActivationStateOnly() {
+ String triggerPriceAsString = "50000.0000";
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_TRIGGER_PRICE + "=" + triggerPriceAsString,
+ "--" + OPT_ENABLE + "=" + "true"
+ };
+ EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
+ assertEquals(TRIGGER_PRICE_AND_ACTIVATION_STATE, parser.getOfferEditType());
+ assertEquals(triggerPriceAsString, parser.getTriggerPrice());
+ assertEquals(1, parser.getEnableAsSignedInt());
+ }
+
+ @Test
+ public void testEditMKtPriceMarginAndTriggerPrice() {
+ String mktPriceMarginAsString = "0.25";
+ String triggerPriceAsString = "50000.0000";
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_MKT_PRICE_MARGIN + "=" + mktPriceMarginAsString,
+ "--" + OPT_TRIGGER_PRICE + "=" + triggerPriceAsString
+ };
+ EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
+ assertEquals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE, parser.getOfferEditType());
+ assertEquals(mktPriceMarginAsString, parser.getMktPriceMargin());
+ assertEquals(triggerPriceAsString, parser.getTriggerPrice());
+ }
+
+ @Test
+ public void testEditMKtPriceMarginAndTriggerPriceAndEnableState() {
+ String mktPriceMarginAsString = "0.25";
+ String triggerPriceAsString = "50000.0000";
+ String[] args = new String[]{
+ PASSWORD_OPT,
+ editoffer.name(),
+ "--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID",
+ "--" + OPT_MKT_PRICE_MARGIN + "=" + mktPriceMarginAsString,
+ "--" + OPT_TRIGGER_PRICE + "=" + triggerPriceAsString,
+ "--" + OPT_ENABLE + "=" + "FALSE"
+ };
+ EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
+ assertEquals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE, parser.getOfferEditType());
+ assertEquals(mktPriceMarginAsString, parser.getMktPriceMargin());
+ assertEquals(triggerPriceAsString, parser.getTriggerPrice());
+ assertFalse(parser.isEnable());
+ }
+}
diff --git a/cli/src/test/java/bisq/cli/opt/OptionParsersTest.java b/cli/src/test/java/bisq/cli/opt/OptionParsersTest.java
index 951b56a5e3e..1df62cf2aa8 100644
--- a/cli/src/test/java/bisq/cli/opt/OptionParsersTest.java
+++ b/cli/src/test/java/bisq/cli/opt/OptionParsersTest.java
@@ -178,7 +178,7 @@ public void testCreatePaymentAcctOptParserWithInvalidPaymentFormOptValueShouldTh
new CreatePaymentAcctOptionParser(args).parse());
if (System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)
assertEquals("json payment account form '\\tmp\\milkyway\\solarsystem\\mars' could not be found",
- exception.getMessage());
+ exception.getMessage());
else
assertEquals("json payment account form '/tmp/milkyway/solarsystem/mars' could not be found",
exception.getMessage());
From 2344285ed3d7afd098a2bcc5920d8e5083720d88 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 13 Jun 2021 12:55:23 -0300
Subject: [PATCH 07/40] Add editoffer method help
---
.../main/resources/help/editoffer-help.txt | 95 +++++++++++++++++++
1 file changed, 95 insertions(+)
create mode 100644 core/src/main/resources/help/editoffer-help.txt
diff --git a/core/src/main/resources/help/editoffer-help.txt b/core/src/main/resources/help/editoffer-help.txt
new file mode 100644
index 00000000000..b56bc6e8bb7
--- /dev/null
+++ b/core/src/main/resources/help/editoffer-help.txt
@@ -0,0 +1,95 @@
+editoffer
+
+NAME
+----
+editoffer - edit an existing offer to buy or sell BTC
+
+SYNOPSIS
+--------
+editoffer
+ --offer-id=
+ [--market-price-margin=]
+ [--trigger-price=]
+ [--fixed-price=]
+ [--enabled=]
+
+DESCRIPTION
+-----------
+Edit an existing offer. Offers can be changed in the following ways:
+
+ Change a fixed-price offer to a market-price-margin based offer.
+ Change a market-price-margin based offer to a fixed-price offer.
+ Change a market-price-margin.
+ Change a fixed-price.
+ Define, change, or remove a market-price-margin based offer's trigger price.
+ Disable an enabled offer.
+ Enable a disabled offer.
+
+OPTIONS
+-------
+--offer-id
+ The ID of the buy or sell offer to edit.
+
+--market-price-margin
+ Changes the % above or below market BTC price, e.g., 1.00 (1%).
+ A --fixed-price offer can be changed to a --market-price-margin offer with this option.
+ The --market-price-margin and --trigger-price options can be used in the same editoffer command.
+ The --market-price-margin and --fixed-price options cannot be used in the same editoffer command.
+
+--fixed-price
+ Changes the fixed BTC price in fiat used to buy or sell BTC, e.g., 34000 (USD).
+ A --market-price-margin offer can be changed to a --fixed-price offer with this option.
+ The --fixed-price and --market-price-margin options cannot be used in the same editoffer command.
+
+--trigger-price
+ Sets the market price for triggering the de-activation of an offer, or defines trigger-price on an
+ offer that did not have a trigger-price when it was created.
+ A buy BTC offer is de-activated when the market price rises above the trigger-price.
+ A sell BTC offer is de-activated when the market price falls below the trigger-price.
+ Only applies to market-price-margin based offers; a fixed-price offer's trigger-price is ignored.
+ The --fixed-price and --trigger-price options cannot be used in the same editoffer command.
+
+--enabled
+ If true, enables a disabled offer. Does nothing if offer is already enabled.
+ If false, disabled an enabled offer. Does nothing if offer is already disabled.
+
+EXAMPLES
+--------
+
+To change a fixed-price offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea
+ to a 0.10% market-price-margin based offer:
+$ ./bisq-cli --password=xyz --port=9998 editoffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --market-price-margin=0.10
+
+To change a market-price-margin based offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea
+ to a fixed-price offer:
+$ ./bisq-cli --password=xyz --port=9998 editoffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --fixed-price=50000.0000
+
+To set or change the trigger-price on a market-price-margin
+ based offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea:
+$ ./bisq-cli --password=xyz --port=9998 editoffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --trigger-price=50000.0000
+
+To remove a trigger-price on a market-price-margin
+ based offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea:
+$ ./bisq-cli --password=xyz --port=9998 editoffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --trigger-price=0
+
+To change the market-price-margin and trigger-price on an
+ offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea:
+$ ./bisq-cli --password=xyz --port=9998 editoffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --market-price-margin=0.05 \
+ --trigger-price=50000.0000
+
+To disable an offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea:
+$ ./bisq-cli --password=xyz --port=9998 editoffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --enable=false
+
+To enable a disabled offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea,
+ and change it from a fixed-price offer to a 0.50% market-price-margin based offer,
+ and set the trigger-price to 50000.0000:
+$ ./bisq-cli --password=xyz --port=9998 editoffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --market-price-margin=0.50 \
+ --trigger-price=50000.0000 \
+ --enable=true
From be249c5e795065c627afe320cd9aa01ab2121894 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 13 Jun 2021 12:56:29 -0300
Subject: [PATCH 08/40] Add editoffer to CLI
---
cli/src/main/java/bisq/cli/CliMain.java | 42 +++++++++++--
.../main/java/bisq/cli/CurrencyFormat.java | 63 ++++++++++++-------
cli/src/main/java/bisq/cli/Method.java | 1 +
3 files changed, 76 insertions(+), 30 deletions(-)
diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java
index 95bfd0d7b84..2b481c45875 100644
--- a/cli/src/main/java/bisq/cli/CliMain.java
+++ b/cli/src/main/java/bisq/cli/CliMain.java
@@ -39,10 +39,7 @@
import lombok.extern.slf4j.Slf4j;
-import static bisq.cli.CurrencyFormat.formatMarketPrice;
-import static bisq.cli.CurrencyFormat.formatTxFeeRateInfo;
-import static bisq.cli.CurrencyFormat.toSatoshis;
-import static bisq.cli.CurrencyFormat.toSecurityDepositAsPct;
+import static bisq.cli.CurrencyFormat.*;
import static bisq.cli.Method.*;
import static bisq.cli.TableFormat.*;
import static bisq.cli.opts.OptLabel.*;
@@ -59,6 +56,7 @@
import bisq.cli.opts.CreateCryptoCurrencyPaymentAcctOptionParser;
import bisq.cli.opts.CreateOfferOptionParser;
import bisq.cli.opts.CreatePaymentAcctOptionParser;
+import bisq.cli.opts.EditOfferOptionParser;
import bisq.cli.opts.GetAddressBalanceOptionParser;
import bisq.cli.opts.GetBTCMarketPriceOptionParser;
import bisq.cli.opts.GetBalanceOptionParser;
@@ -200,7 +198,7 @@ public static void run(String[] args) {
}
var currencyCode = opts.getCurrencyCode();
var price = client.getBtcPrice(currencyCode);
- out.println(formatMarketPrice(price));
+ out.println(formatInternalFiatPrice(price));
return;
}
case getfundingaddresses: {
@@ -337,6 +335,7 @@ public static void run(String[] args) {
var marketPriceMargin = opts.getMktPriceMarginAsBigDecimal();
var securityDeposit = toSecurityDepositAsPct(opts.getSecurityDeposit());
var makerFeeCurrencyCode = opts.getMakerFeeCurrencyCode();
+ var triggerPrice = 0; // Cannot be defined until offer is in book.
var offer = client.createOffer(direction,
currencyCode,
amount,
@@ -346,10 +345,34 @@ public static void run(String[] args) {
marketPriceMargin.doubleValue(),
securityDeposit,
paymentAcctId,
- makerFeeCurrencyCode);
+ makerFeeCurrencyCode,
+ triggerPrice);
out.println(formatOfferTable(singletonList(offer), currencyCode));
return;
}
+ case editoffer: {
+ var opts = new EditOfferOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(client.getMethodHelp(method));
+ return;
+ }
+ var offerId = opts.getOfferId();
+ var fixedPrice = opts.getFixedPrice();
+ var isUsingMktPriceMargin = opts.isUsingMktPriceMargin();
+ var marketPriceMargin = opts.getMktPriceMarginAsBigDecimal();
+ var triggerPrice = toInternalFiatPrice(opts.getTriggerPriceAsBigDecimal());
+ var enable = opts.getEnableAsSignedInt();
+ var editOfferType = opts.getOfferEditType();
+ client.editOffer(offerId,
+ fixedPrice,
+ isUsingMktPriceMargin,
+ marketPriceMargin.doubleValue(),
+ triggerPrice,
+ enable,
+ editOfferType);
+ out.println("edited offer being re-added to offer book");
+ return;
+ }
case canceloffer: {
var opts = new CancelOfferOptionParser(args).parse();
if (opts.isForHelp()) {
@@ -754,6 +777,13 @@ private static void printHelp(OptionParser parser, @SuppressWarnings("SameParame
stream.format(rowFormat, "", "--fixed-price= | --market-price=margin= \\", "");
stream.format(rowFormat, "", "--security-deposit= \\", "");
stream.format(rowFormat, "", "[--fee-currency=]", "");
+ stream.format(rowFormat, "", "[--trigger-price=]", "");
+ stream.println();
+ stream.format(rowFormat, editoffer.name(), "--offer-id= \\", "Edit offer with id");
+ stream.format(rowFormat, "", "[--fixed-price=] \\", "");
+ stream.format(rowFormat, "", "[--market-price=margin=] \\", "");
+ stream.format(rowFormat, "", "[--trigger-price=] \\", "");
+ stream.format(rowFormat, "", "[--enabled=]", "");
stream.println();
stream.format(rowFormat, canceloffer.name(), "--offer-id=", "Cancel offer with id");
stream.println();
diff --git a/cli/src/main/java/bisq/cli/CurrencyFormat.java b/cli/src/main/java/bisq/cli/CurrencyFormat.java
index 4abf20276ee..47bdc42df81 100644
--- a/cli/src/main/java/bisq/cli/CurrencyFormat.java
+++ b/cli/src/main/java/bisq/cli/CurrencyFormat.java
@@ -35,7 +35,12 @@
@VisibleForTesting
public class CurrencyFormat {
- private static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance(Locale.US);
+ // Formats numbers in US locale, human friendly style.
+ private static final NumberFormat FRIENDLY_NUMBER_FORMAT = NumberFormat.getInstance(Locale.US);
+
+ // Formats numbers for internal use, i.e., grpc request parameters.
+ private static final DecimalFormat INTERNAL_FIAT_DECIMAL_FORMAT = new DecimalFormat("##############0.0000");
+ private static final DecimalFormat INTERNAL_ALTCOIN_DECIMAL_FORMAT = new DecimalFormat("##############0.00000000");
static final BigDecimal SATOSHI_DIVISOR = new BigDecimal(100_000_000);
static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.00000000");
@@ -59,9 +64,9 @@ public static String formatBsq(long sats) {
public static String formatBsqAmount(long bsqSats) {
// BSQ sats = trade.getOffer().getVolume()
- NUMBER_FORMAT.setMinimumFractionDigits(2);
- NUMBER_FORMAT.setMaximumFractionDigits(2);
- NUMBER_FORMAT.setRoundingMode(HALF_UP);
+ FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(2);
+ FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(2);
+ FRIENDLY_NUMBER_FORMAT.setRoundingMode(HALF_UP);
return SEND_BSQ_FORMAT.format((double) bsqSats / SATOSHI_DIVISOR.doubleValue());
}
@@ -95,38 +100,48 @@ public static String formatCryptoCurrencyVolumeRange(long minVolume, long volume
: formatCryptoCurrencyOfferVolume(volume);
}
- public static String formatMarketPrice(double price) {
- NUMBER_FORMAT.setMinimumFractionDigits(4);
- NUMBER_FORMAT.setMaximumFractionDigits(4);
- return NUMBER_FORMAT.format(price);
+ public static String formatInternalFiatPrice(BigDecimal price) {
+ INTERNAL_FIAT_DECIMAL_FORMAT.setMinimumFractionDigits(4);
+ INTERNAL_FIAT_DECIMAL_FORMAT.setMaximumFractionDigits(4);
+ return INTERNAL_FIAT_DECIMAL_FORMAT.format(price);
+ }
+
+ public static String formatInternalFiatPrice(double price) {
+ FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(4);
+ FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(4);
+ return FRIENDLY_NUMBER_FORMAT.format(price);
}
public static String formatPrice(long price) {
- NUMBER_FORMAT.setMinimumFractionDigits(4);
- NUMBER_FORMAT.setMaximumFractionDigits(4);
- NUMBER_FORMAT.setRoundingMode(UNNECESSARY);
- return NUMBER_FORMAT.format((double) price / 10_000);
+ FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(4);
+ FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(4);
+ FRIENDLY_NUMBER_FORMAT.setRoundingMode(UNNECESSARY);
+ return FRIENDLY_NUMBER_FORMAT.format((double) price / 10_000);
}
public static String formatCryptoCurrencyPrice(long price) {
- NUMBER_FORMAT.setMinimumFractionDigits(8);
- NUMBER_FORMAT.setMaximumFractionDigits(8);
- NUMBER_FORMAT.setRoundingMode(UNNECESSARY);
- return NUMBER_FORMAT.format((double) price / SATOSHI_DIVISOR.doubleValue());
+ FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(8);
+ FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(8);
+ FRIENDLY_NUMBER_FORMAT.setRoundingMode(UNNECESSARY);
+ return FRIENDLY_NUMBER_FORMAT.format((double) price / SATOSHI_DIVISOR.doubleValue());
}
public static String formatOfferVolume(long volume) {
- NUMBER_FORMAT.setMinimumFractionDigits(0);
- NUMBER_FORMAT.setMaximumFractionDigits(0);
- NUMBER_FORMAT.setRoundingMode(HALF_UP);
- return NUMBER_FORMAT.format((double) volume / 10_000);
+ FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(0);
+ FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(0);
+ FRIENDLY_NUMBER_FORMAT.setRoundingMode(HALF_UP);
+ return FRIENDLY_NUMBER_FORMAT.format((double) volume / 10_000);
}
public static String formatCryptoCurrencyOfferVolume(long volume) {
- NUMBER_FORMAT.setMinimumFractionDigits(2);
- NUMBER_FORMAT.setMaximumFractionDigits(2);
- NUMBER_FORMAT.setRoundingMode(HALF_UP);
- return NUMBER_FORMAT.format((double) volume / SATOSHI_DIVISOR.doubleValue());
+ FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(2);
+ FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(2);
+ FRIENDLY_NUMBER_FORMAT.setRoundingMode(HALF_UP);
+ return FRIENDLY_NUMBER_FORMAT.format((double) volume / SATOSHI_DIVISOR.doubleValue());
+ }
+
+ public static long toInternalFiatPrice(BigDecimal humanFriendlyFiatPrice) {
+ return humanFriendlyFiatPrice.multiply(new BigDecimal(10_000)).longValue();
}
public static long toSatoshis(String btc) {
diff --git a/cli/src/main/java/bisq/cli/Method.java b/cli/src/main/java/bisq/cli/Method.java
index cf8b1d7df5f..76011877310 100644
--- a/cli/src/main/java/bisq/cli/Method.java
+++ b/cli/src/main/java/bisq/cli/Method.java
@@ -25,6 +25,7 @@ public enum Method {
confirmpaymentreceived,
confirmpaymentstarted,
createoffer,
+ editoffer,
createpaymentacct,
createcryptopaymentacct,
getaddressbalance,
From 929b28cb8cb1667f0d64d3eb1bd017e56ee2d37a Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 13 Jun 2021 13:00:27 -0300
Subject: [PATCH 09/40] Add editoffer api tests & minor apitest refactoring
---
.../method/offer/AbstractOfferTest.java | 50 +-
.../apitest/method/offer/CancelOfferTest.java | 3 +-
...CreateOfferUsingMarketPriceMarginTest.java | 70 ++-
.../apitest/method/offer/EditOfferTest.java | 429 ++++++++++++++++++
.../payment/CreatePaymentAccountTest.java | 23 +
.../method/trade/TakeBuyBTCOfferTest.java | 3 +-
.../method/trade/TakeSellBTCOfferTest.java | 3 +-
.../LongRunningOfferDeactivationTest.java | 167 +++++++
.../java/bisq/apitest/scenario/OfferTest.java | 21 +-
.../apitest/scenario/PaymentAccountTest.java | 1 +
.../bisq/apitest/scenario/bot/BotClient.java | 6 +-
.../apitest/scenario/bot/RandomOffer.java | 9 +-
12 files changed, 749 insertions(+), 36 deletions(-)
create mode 100644 apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java
create mode 100644 apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java
diff --git a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java
index f0e95dd25f8..81903f8efcc 100644
--- a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java
@@ -17,14 +17,13 @@
package bisq.apitest.method.offer;
-import bisq.core.monetary.Altcoin;
-
import protobuf.PaymentAccount;
-import org.bitcoinj.utils.Fiat;
-
import java.math.BigDecimal;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -37,10 +36,7 @@
import static bisq.apitest.config.BisqAppConfig.arbdaemon;
import static bisq.apitest.config.BisqAppConfig.bobdaemon;
import static bisq.apitest.config.BisqAppConfig.seednode;
-import static bisq.common.util.MathUtils.roundDouble;
-import static bisq.common.util.MathUtils.scaleDownByPowerOf10;
-import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
-import static java.math.RoundingMode.HALF_UP;
+import static bisq.common.util.MathUtils.exactMultiply;
@@ -49,6 +45,10 @@
@Slf4j
public abstract class AbstractOfferTest extends MethodTest {
+ protected static final int ACTIVATE_OFFER = 1;
+ protected static final int DEACTIVATE_OFFER = 0;
+ protected static final long NO_TRIGGER_PRICE = 0;
+
@Setter
protected static boolean isLongRunningTest;
@@ -58,7 +58,7 @@ public abstract class AbstractOfferTest extends MethodTest {
@BeforeAll
public static void setUp() {
startSupportingApps(true,
- false,
+ true,
bitcoind,
seednode,
arbdaemon,
@@ -67,6 +67,27 @@ public static void setUp() {
}
+ // Mkt Price Margin value of offer returned from server is scaled down by 10^-2.
+ protected final Function scaledDownMktPriceMargin = (mktPriceMargin) ->
+ exactMultiply(mktPriceMargin, 0.01);
+
+ // Price value of offer returned from server is scaled up by 10^4.
+ protected final Function scaledUpFiatPrice = (price) -> {
+ BigDecimal factor = new BigDecimal(10).pow(4);
+ return price.multiply(factor).longValue();
+ };
+
+ protected final BiFunction calcTriggerPriceAsLong = (base, delta) -> {
+ var triggerPriceAsDouble = new BigDecimal(base).add(new BigDecimal(delta)).doubleValue();
+ return Double.valueOf(exactMultiply(triggerPriceAsDouble, 10_000)).longValue();
+ };
+
+ protected final BiFunction calcFixedPriceAsString = (base, delta) -> {
+ var fixedPriceAsBigDecimal = new BigDecimal(Double.toString(base))
+ .add(new BigDecimal(Double.toString(delta)));
+ return fixedPriceAsBigDecimal.toPlainString();
+ };
+
public static void createBsqPaymentAccounts() {
alicesBsqAcct = aliceClient.createCryptoCurrencyPaymentAccount("Alice's BSQ Account",
BSQ,
@@ -78,17 +99,6 @@ public static void createBsqPaymentAccounts() {
false);
}
- protected double getScaledOfferPrice(double offerPrice, String currencyCode) {
- int precision = isCryptoCurrency(currencyCode) ? Altcoin.SMALLEST_UNIT_EXPONENT : Fiat.SMALLEST_UNIT_EXPONENT;
- return scaleDownByPowerOf10(offerPrice, precision);
- }
-
- protected final double getPercentageDifference(double price1, double price2) {
- return BigDecimal.valueOf(roundDouble((1 - (price1 / price2)), 5))
- .setScale(4, HALF_UP)
- .doubleValue();
- }
-
@AfterAll
public static void tearDown() {
tearDownScaffold();
diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java
index fe21e4aa8f2..8db313583cd 100644
--- a/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java
@@ -54,7 +54,8 @@ public class CancelOfferTest extends AbstractOfferTest {
0.00,
getDefaultBuyerSecurityDepositAsPercent(),
paymentAccountId,
- BSQ);
+ BSQ,
+ NO_TRIGGER_PRICE);
};
@Test
diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java
index 94c2519d913..df1f9079fb1 100644
--- a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java
@@ -17,15 +17,20 @@
package bisq.apitest.method.offer;
+import bisq.core.monetary.Altcoin;
+import bisq.core.monetary.Price;
import bisq.core.payment.PaymentAccount;
import bisq.proto.grpc.OfferInfo;
+import org.bitcoinj.utils.Fiat;
+
import java.text.DecimalFormat;
+import java.math.BigDecimal;
+
import lombok.extern.slf4j.Slf4j;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
@@ -33,11 +38,14 @@
import static bisq.apitest.config.ApiTestConfig.BTC;
import static bisq.cli.TableFormat.formatOfferTable;
+import static bisq.common.util.MathUtils.roundDouble;
import static bisq.common.util.MathUtils.scaleDownByPowerOf10;
import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
+import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
import static java.lang.Math.abs;
import static java.lang.String.format;
+import static java.math.RoundingMode.HALF_UP;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@@ -45,7 +53,7 @@
import static protobuf.OfferPayload.Direction.BUY;
import static protobuf.OfferPayload.Direction.SELL;
-@Disabled
+// @Disabled
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
@@ -68,7 +76,8 @@ public void testCreateUSDBTCBuyOffer5PctPriceMargin() {
priceMarginPctInput,
getDefaultBuyerSecurityDepositAsPercent(),
usdAccount.getId(),
- MAKER_FEE_CURRENCY_CODE);
+ MAKER_FEE_CURRENCY_CODE,
+ NO_TRIGGER_PRICE);
log.info("OFFER #1:\n{}", formatOfferTable(singletonList(newOffer), "usd"));
String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
@@ -109,7 +118,8 @@ public void testCreateNZDBTCBuyOfferMinus2PctPriceMargin() {
priceMarginPctInput,
getDefaultBuyerSecurityDepositAsPercent(),
nzdAccount.getId(),
- MAKER_FEE_CURRENCY_CODE);
+ MAKER_FEE_CURRENCY_CODE,
+ NO_TRIGGER_PRICE);
log.info("OFFER #2:\n{}", formatOfferTable(singletonList(newOffer), "nzd"));
String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
@@ -150,7 +160,8 @@ public void testCreateGBPBTCSellOfferMinus1Point5PctPriceMargin() {
priceMarginPctInput,
getDefaultBuyerSecurityDepositAsPercent(),
gbpAccount.getId(),
- MAKER_FEE_CURRENCY_CODE);
+ MAKER_FEE_CURRENCY_CODE,
+ NO_TRIGGER_PRICE);
log.info("OFFER #3:\n{}", formatOfferTable(singletonList(newOffer), "gbp"));
String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
@@ -191,7 +202,8 @@ public void testCreateBRLBTCSellOffer6Point55PctPriceMargin() {
priceMarginPctInput,
getDefaultBuyerSecurityDepositAsPercent(),
brlAccount.getId(),
- MAKER_FEE_CURRENCY_CODE);
+ MAKER_FEE_CURRENCY_CODE,
+ NO_TRIGGER_PRICE);
log.info("OFFER #4:\n{}", formatOfferTable(singletonList(newOffer), "brl"));
String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
@@ -220,6 +232,41 @@ public void testCreateBRLBTCSellOffer6Point55PctPriceMargin() {
assertCalculatedPriceIsCorrect(newOffer, priceMarginPctInput);
}
+ @Test
+ @Order(5)
+ public void testCreateUSDBTCBuyOfferWithTriggerPrice() {
+ PaymentAccount usdAccount = createDummyF2FAccount(aliceClient, "US");
+ double mktPriceAsDouble = aliceClient.getBtcPrice("usd");
+ BigDecimal mktPrice = new BigDecimal(Double.toString(mktPriceAsDouble));
+ BigDecimal triggerPrice = mktPrice.add(new BigDecimal("1000.9999"));
+ // TODO Duplicate this Price class logic in CLI.
+ long triggerPriceAsLong = Price.parse("USD", triggerPrice.toString()).getValue();
+
+ var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
+ "usd",
+ 10_000_000L,
+ 5_000_000L,
+ 0.0,
+ getDefaultBuyerSecurityDepositAsPercent(),
+ usdAccount.getId(),
+ MAKER_FEE_CURRENCY_CODE,
+ triggerPriceAsLong);
+ genBtcBlocksThenWait(1, 4000); // give time to add to offer book
+ newOffer = aliceClient.getMyOffer(newOffer.getId());
+ log.info("OFFER #5:\n{}", formatOfferTable(singletonList(newOffer), "usd"));
+ assertEquals(triggerPriceAsLong, newOffer.getTriggerPrice());
+ }
+
+ public static void main(String[] args) {
+ // TODO DELETE ME
+ String triggerPriceAsString = "10.1111";
+ Price price = Price.parse("USD", triggerPriceAsString);
+ long triggerPriceAsLong = price.getValue();
+ log.info("triggerPriceAsString: {}", triggerPriceAsString);
+ log.info("triggerPriceAsPrice: {}", price);
+ log.info("triggerPriceAsLong: {}", triggerPriceAsLong);
+ }
+
private void assertCalculatedPriceIsCorrect(OfferInfo offer, double priceMarginPctInput) {
assertTrue(() -> {
String counterCurrencyCode = offer.getCounterCurrencyCode();
@@ -239,6 +286,17 @@ private void assertCalculatedPriceIsCorrect(OfferInfo offer, double priceMarginP
});
}
+ private double getPercentageDifference(double price1, double price2) {
+ return BigDecimal.valueOf(roundDouble((1 - (price1 / price2)), 5))
+ .setScale(4, HALF_UP)
+ .doubleValue();
+ }
+
+ private double getScaledOfferPrice(double offerPrice, String currencyCode) {
+ int precision = isCryptoCurrency(currencyCode) ? Altcoin.SMALLEST_UNIT_EXPONENT : Fiat.SMALLEST_UNIT_EXPONENT;
+ return scaleDownByPowerOf10(offerPrice, precision);
+ }
+
private boolean isCalculatedPriceWithinErrorTolerance(double delta,
double expectedDiffPct,
double actualDiffPct,
diff --git a/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java
new file mode 100644
index 00000000000..d7af5694af9
--- /dev/null
+++ b/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java
@@ -0,0 +1,429 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.apitest.method.offer;
+
+import bisq.core.payment.PaymentAccount;
+
+import bisq.proto.grpc.OfferInfo;
+
+import io.grpc.StatusRuntimeException;
+
+import java.math.BigDecimal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import static bisq.apitest.config.ApiTestConfig.BSQ;
+import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
+import static bisq.proto.grpc.EditOfferRequest.EditType.FIXED_PRICE_AND_ACTIVATION_STATE;
+import static bisq.proto.grpc.EditOfferRequest.EditType.MKT_PRICE_MARGIN_AND_TRIGGER_PRICE;
+import static bisq.proto.grpc.EditOfferRequest.EditType.MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE;
+import static bisq.proto.grpc.EditOfferRequest.EditType.MKT_PRICE_MARGIN_ONLY;
+import static java.lang.String.format;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static protobuf.OfferPayload.Direction.BUY;
+import static protobuf.OfferPayload.Direction.SELL;
+
+@Disabled
+@Slf4j
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class EditOfferTest extends AbstractOfferTest {
+
+ // Some test fixtures to reduce duplication.
+ private static final Map paymentAcctCache = new HashMap<>();
+ private static final String DOLLAR = "USD";
+ private static final String RUBLE = "RUB";
+ private static final long AMOUNT = 10000000L;
+
+ @Test
+ @Order(1)
+ public void testOfferDisableAndEnable() {
+ PaymentAccount paymentAcct = getOrCreatePaymentAccount("DE");
+ OfferInfo originalOffer = createMktPricedOfferForEdit(BUY.name(),
+ "EUR",
+ paymentAcct.getId(),
+ 0.0,
+ NO_TRIGGER_PRICE);
+ assertFalse(originalOffer.getIsActivated()); // Not activated until prep is done.
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ originalOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertTrue(originalOffer.getIsActivated());
+ // Disable offer
+ aliceClient.editOfferActivationState(originalOffer.getId(), DEACTIVATE_OFFER);
+ genBtcBlocksThenWait(1, 1500); // Wait for offer book removal.
+ OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertFalse(editedOffer.getIsActivated());
+ // Re-enable offer
+ aliceClient.editOfferActivationState(editedOffer.getId(), ACTIVATE_OFFER);
+ genBtcBlocksThenWait(1, 1500); // Wait for offer book re-entry.
+ editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertTrue(editedOffer.getIsActivated());
+
+ doSanityCheck(originalOffer, editedOffer);
+ }
+
+ @Test
+ @Order(2)
+ public void testEditTriggerPrice() {
+ PaymentAccount paymentAcct = getOrCreatePaymentAccount("FI");
+ OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
+ "EUR",
+ paymentAcct.getId(),
+ 0.0,
+ NO_TRIGGER_PRICE);
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ originalOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertEquals(0 /*no trigger price*/, originalOffer.getTriggerPrice());
+
+ // Edit the offer's trigger price, nothing else.
+ var mktPrice = aliceClient.getBtcPrice("EUR");
+ var delta = 5_000.00;
+ var newTriggerPriceAsLong = calcTriggerPriceAsLong.apply(mktPrice, delta);
+
+ aliceClient.editOfferTriggerPrice(originalOffer.getId(), newTriggerPriceAsLong);
+ sleep(2500); // Wait for offer book re-entry.
+ OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice());
+ assertTrue(editedOffer.getUseMarketBasedPrice());
+
+ doSanityCheck(originalOffer, editedOffer);
+ }
+
+ @Test
+ @Order(3)
+ public void testSetTriggerPriceToNegativeValueShouldThrowException() {
+ PaymentAccount paymentAcct = getOrCreatePaymentAccount("FI");
+ final OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
+ "EUR",
+ paymentAcct.getId(),
+ 0.0,
+ NO_TRIGGER_PRICE);
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ // Edit the offer's trigger price, set to -1, check error.
+ Throwable exception = assertThrows(StatusRuntimeException.class, () ->
+ aliceClient.editOfferTriggerPrice(originalOffer.getId(), -1L));
+ String expectedExceptionMessage =
+ format("UNKNOWN: programmer error: cannot set trigger price to a negative value in offer with id '%s'",
+ originalOffer.getId());
+ assertEquals(expectedExceptionMessage, exception.getMessage());
+ }
+
+ @Test
+ @Order(4)
+ public void testEditMktPriceMargin() {
+ PaymentAccount paymentAcct = getOrCreatePaymentAccount("US");
+ var originalMktPriceMargin = new BigDecimal("0.1").doubleValue();
+ OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
+ DOLLAR,
+ paymentAcct.getId(),
+ originalMktPriceMargin,
+ NO_TRIGGER_PRICE);
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin());
+ // Edit the offer's price margin, nothing else.
+ var newMktPriceMargin = new BigDecimal("0.5").doubleValue();
+ aliceClient.editOfferPriceMargin(originalOffer.getId(), newMktPriceMargin);
+ OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
+
+ doSanityCheck(originalOffer, editedOffer);
+ }
+
+ @Test
+ @Order(5)
+ public void testEditFixedPrice() {
+ PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU");
+ double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE);
+ String fixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 200_000.0000);
+ OfferInfo originalOffer = createFixedPricedOfferForEdit(BUY.name(),
+ RUBLE,
+ paymentAcct.getId(),
+ fixedPriceAsString);
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ // Edit the offer's fixed price, nothing else.
+ String editedFixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 100_000.0000);
+ aliceClient.editOfferFixedPrice(originalOffer.getId(), editedFixedPriceAsString);
+ // Wait for edited offer to be removed from offer-book, edited, and re-published.
+ genBtcBlocksThenWait(1, 2500);
+ OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ var expectedNewFixedPrice = scaledUpFiatPrice.apply(new BigDecimal(editedFixedPriceAsString));
+ assertEquals(expectedNewFixedPrice, editedOffer.getPrice());
+
+ doSanityCheck(originalOffer, editedOffer);
+ }
+
+ @Test
+ @Order(6)
+ public void testEditFixedPriceAndDeactivation() {
+ PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU");
+ double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE);
+ String fixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 200_000.0000);
+ OfferInfo originalOffer = createFixedPricedOfferForEdit(BUY.name(),
+ RUBLE,
+ paymentAcct.getId(),
+ fixedPriceAsString);
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ // Edit the offer's fixed price and deactivate it.
+ String editedFixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 100_000.0000);
+ aliceClient.editOffer(originalOffer.getId(),
+ editedFixedPriceAsString,
+ originalOffer.getUseMarketBasedPrice(),
+ 0.0,
+ NO_TRIGGER_PRICE,
+ DEACTIVATE_OFFER,
+ FIXED_PRICE_AND_ACTIVATION_STATE);
+ // Wait for edited offer to be removed from offer-book, edited, and re-published.
+ genBtcBlocksThenWait(1, 2500);
+ OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ var expectedNewFixedPrice = scaledUpFiatPrice.apply(new BigDecimal(editedFixedPriceAsString));
+ assertEquals(expectedNewFixedPrice, editedOffer.getPrice());
+ assertFalse(editedOffer.getIsActivated());
+
+ doSanityCheck(originalOffer, editedOffer);
+ }
+
+ @Test
+ @Order(7)
+ public void testEditMktPriceMarginAndTriggerPriceAndDeactivation() {
+ PaymentAccount paymentAcct = getOrCreatePaymentAccount("US");
+
+ var originalMktPriceMargin = new BigDecimal("0.0").doubleValue();
+ var mktPriceAsDouble = aliceClient.getBtcPrice(DOLLAR);
+ var originalTriggerPriceAsLong = calcTriggerPriceAsLong.apply(mktPriceAsDouble, -5_000.0000);
+
+ OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
+ DOLLAR,
+ paymentAcct.getId(),
+ originalMktPriceMargin,
+ originalTriggerPriceAsLong);
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ originalOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin());
+ assertEquals(originalTriggerPriceAsLong, originalOffer.getTriggerPrice());
+
+ // Edit the offer's price margin and trigger price, and deactivate it.
+ var newMktPriceMargin = new BigDecimal("0.1").doubleValue();
+ var newTriggerPriceAsLong = calcTriggerPriceAsLong.apply(mktPriceAsDouble, -2_000.0000);
+ aliceClient.editOffer(originalOffer.getId(),
+ "0.00",
+ originalOffer.getUseMarketBasedPrice(),
+ newMktPriceMargin,
+ newTriggerPriceAsLong,
+ DEACTIVATE_OFFER,
+ MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE);
+ // Wait for edited offer to be removed from offer-book, edited, and re-published.
+ genBtcBlocksThenWait(1, 2500);
+ OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
+ assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice());
+ assertFalse(editedOffer.getIsActivated());
+
+ doSanityCheck(originalOffer, editedOffer);
+ }
+
+ @Test
+ @Order(8)
+ public void testEditingFixedPriceInMktPriceMarginBasedOfferShouldThrowException() {
+ PaymentAccount paymentAcct = getOrCreatePaymentAccount("US");
+ var originalMktPriceMargin = new BigDecimal("0.0").doubleValue();
+ final OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
+ DOLLAR,
+ paymentAcct.getId(),
+ originalMktPriceMargin,
+ NO_TRIGGER_PRICE);
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ // Try to edit both the fixed price and mkt price margin.
+ var newMktPriceMargin = new BigDecimal("0.25").doubleValue();
+ var newFixedPrice = "50000.0000";
+ Throwable exception = assertThrows(StatusRuntimeException.class, () ->
+ aliceClient.editOffer(originalOffer.getId(),
+ newFixedPrice,
+ originalOffer.getUseMarketBasedPrice(),
+ newMktPriceMargin,
+ NO_TRIGGER_PRICE,
+ ACTIVATE_OFFER,
+ MKT_PRICE_MARGIN_ONLY));
+ String expectedExceptionMessage =
+ format("UNKNOWN: programmer error: cannot set fixed price (%s) in"
+ + " mkt price margin based offer with id '%s'",
+ newFixedPrice,
+ originalOffer.getId());
+ assertEquals(expectedExceptionMessage, exception.getMessage());
+ }
+
+ @Test
+ @Order(9)
+ public void testEditingTriggerPriceInFixedPriceOfferShouldThrowException() {
+ PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU");
+ double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE);
+ String fixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 200_000.0000);
+ OfferInfo originalOffer = createFixedPricedOfferForEdit(BUY.name(),
+ RUBLE,
+ paymentAcct.getId(),
+ fixedPriceAsString);
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ long newTriggerPrice = 1000000L;
+ Throwable exception = assertThrows(StatusRuntimeException.class, () ->
+ aliceClient.editOfferTriggerPrice(originalOffer.getId(), newTriggerPrice));
+ String expectedExceptionMessage =
+ format("UNKNOWN: programmer error: cannot set a trigger price (%s) in"
+ + " fixed price offer with id '%s'",
+ newTriggerPrice,
+ originalOffer.getId());
+ assertEquals(expectedExceptionMessage, exception.getMessage());
+ }
+
+ @Test
+ @Order(10)
+ public void testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice() {
+ PaymentAccount paymentAcct = getOrCreatePaymentAccount("MX");
+ double mktPriceAsDouble = aliceClient.getBtcPrice("MXN");
+ String fixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 0.00);
+ OfferInfo originalOffer = createFixedPricedOfferForEdit(BUY.name(),
+ "MXN",
+ paymentAcct.getId(),
+ fixedPriceAsString);
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+
+ // Change the offer to mkt price based and set a trigger price.
+ var newMktPriceMargin = new BigDecimal("0.05").doubleValue();
+ var delta = 200_000.0000; // trigger price on buy offer is 200K above mkt price
+ var newTriggerPriceAsLong = calcTriggerPriceAsLong.apply(mktPriceAsDouble, delta);
+ aliceClient.editOffer(originalOffer.getId(),
+ "0.00",
+ true,
+ newMktPriceMargin,
+ newTriggerPriceAsLong,
+ ACTIVATE_OFFER,
+ MKT_PRICE_MARGIN_AND_TRIGGER_PRICE);
+ // Wait for edited offer to be removed from offer-book, edited, and re-published.
+ genBtcBlocksThenWait(1, 2500);
+ OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertTrue(editedOffer.getUseMarketBasedPrice());
+ assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
+ assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice());
+ assertTrue(editedOffer.getIsActivated());
+
+ doSanityCheck(originalOffer, editedOffer);
+ }
+
+ @Test
+ @Order(11)
+ public void testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt() {
+ PaymentAccount paymentAcct = getOrCreatePaymentAccount("GB");
+ double mktPriceAsDouble = aliceClient.getBtcPrice("GBP");
+ var originalMktPriceMargin = new BigDecimal("0.25").doubleValue();
+ var delta = 1_000.0000; // trigger price on sell offer is 1K below mkt price
+ var originalTriggerPriceAsLong = calcTriggerPriceAsLong.apply(mktPriceAsDouble, delta);
+ final OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
+ "GBP",
+ paymentAcct.getId(),
+ originalMktPriceMargin,
+ originalTriggerPriceAsLong);
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+
+ String fixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 0.00);
+ aliceClient.editOffer(originalOffer.getId(),
+ fixedPriceAsString,
+ false,
+ 0.00,
+ 0,
+ DEACTIVATE_OFFER,
+ FIXED_PRICE_AND_ACTIVATION_STATE);
+ // Wait for edited offer to be removed from offer-book, edited, and re-published.
+ genBtcBlocksThenWait(1, 2500);
+ OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertFalse(editedOffer.getUseMarketBasedPrice());
+ assertEquals(0.00, editedOffer.getMarketPriceMargin());
+ assertEquals(0, editedOffer.getTriggerPrice());
+ assertEquals(scaledUpFiatPrice.apply(new BigDecimal(fixedPriceAsString)), editedOffer.getPrice());
+ assertFalse(editedOffer.getIsActivated());
+ }
+
+ private OfferInfo createMktPricedOfferForEdit(String direction,
+ String currencyCode,
+ String paymentAccountId,
+ double marketPriceMargin,
+ long triggerPrice) {
+ return aliceClient.createMarketBasedPricedOffer(direction,
+ currencyCode,
+ AMOUNT,
+ AMOUNT,
+ marketPriceMargin,
+ getDefaultBuyerSecurityDepositAsPercent(),
+ paymentAccountId,
+ BSQ,
+ triggerPrice);
+ }
+
+ private OfferInfo createFixedPricedOfferForEdit(String direction,
+ String currencyCode,
+ String paymentAccountId,
+ String priceAsString) {
+ return aliceClient.createFixedPricedOffer(direction,
+ currencyCode,
+ AMOUNT,
+ AMOUNT,
+ priceAsString,
+ getDefaultBuyerSecurityDepositAsPercent(),
+ paymentAccountId,
+ BSQ);
+ }
+
+ private void doSanityCheck(OfferInfo originalOffer, OfferInfo editedOffer) {
+ // Assert some of the immutable offer fields are unchanged.
+ assertEquals(originalOffer.getDirection(), editedOffer.getDirection());
+ assertEquals(originalOffer.getAmount(), editedOffer.getAmount());
+ assertEquals(originalOffer.getMinAmount(), editedOffer.getMinAmount());
+ assertEquals(originalOffer.getTxFee(), editedOffer.getTxFee());
+ assertEquals(originalOffer.getMakerFee(), editedOffer.getMakerFee());
+ assertEquals(originalOffer.getPaymentAccountId(), editedOffer.getPaymentAccountId());
+ assertEquals(originalOffer.getDate(), editedOffer.getDate());
+ if (originalOffer.getDirection().equals(BUY.name()))
+ assertEquals(originalOffer.getBuyerSecurityDeposit(), editedOffer.getBuyerSecurityDeposit());
+ else
+ assertEquals(originalOffer.getSellerSecurityDeposit(), editedOffer.getSellerSecurityDeposit());
+ }
+
+ private PaymentAccount getOrCreatePaymentAccount(String countryCode) {
+ if (paymentAcctCache.containsKey(countryCode)) {
+ return paymentAcctCache.get(countryCode);
+ } else {
+ PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, countryCode);
+ paymentAcctCache.put(countryCode, paymentAcct);
+ return paymentAcct;
+ }
+ }
+
+ @AfterAll
+ public static void clearPaymentAcctCache() {
+ paymentAcctCache.clear();
+ }
+}
diff --git a/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java b/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java
index b7eb7f7ebb7..3caef6ee391 100644
--- a/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java
@@ -22,6 +22,7 @@
import bisq.core.payment.AliPayAccount;
import bisq.core.payment.AustraliaPayid;
import bisq.core.payment.CashDepositAccount;
+import bisq.core.payment.ChaseQuickPayAccount;
import bisq.core.payment.ClearXchangeAccount;
import bisq.core.payment.F2FAccount;
import bisq.core.payment.FasterPaymentsAccount;
@@ -252,6 +253,28 @@ public void testCreateBrazilNationalBankAccount(TestInfo testInfo) {
print(paymentAccount);
}
+ @Test
+ public void testCreateChaseQuickPayAccount(TestInfo testInfo) {
+ File emptyForm = getEmptyForm(testInfo, CHASE_QUICK_PAY_ID);
+ verifyEmptyForm(emptyForm,
+ CHASE_QUICK_PAY_ID,
+ PROPERTY_NAME_EMAIL,
+ PROPERTY_NAME_HOLDER_NAME);
+ COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, CHASE_QUICK_PAY_ID);
+ COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Quick Pay Acct");
+ COMPLETED_FORM_MAP.put(PROPERTY_NAME_EMAIL, "johndoe@quickpay.com");
+ COMPLETED_FORM_MAP.put(PROPERTY_NAME_HOLDER_NAME, "John Doe");
+ COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, "");
+ String jsonString = getCompletedFormAsJsonString();
+ ChaseQuickPayAccount paymentAccount = (ChaseQuickPayAccount) createPaymentAccount(aliceClient, jsonString);
+ verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
+ verifyAccountSingleTradeCurrency("USD", paymentAccount);
+ verifyCommonFormEntries(paymentAccount);
+ assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL), paymentAccount.getEmail());
+ assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
+ print(paymentAccount);
+ }
+
@Test
public void testCreateClearXChangeAccount(TestInfo testInfo) {
File emptyForm = getEmptyForm(testInfo, CLEAR_X_CHANGE_ID);
diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java
index 93d9b1b9c8b..8f03520b525 100644
--- a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java
@@ -72,7 +72,8 @@ public void testTakeAlicesBuyOffer(final TestInfo testInfo) {
0.00,
getDefaultBuyerSecurityDepositAsPercent(),
alicesUsdAccount.getId(),
- TRADE_FEE_CURRENCY_CODE);
+ TRADE_FEE_CURRENCY_CODE,
+ NO_TRIGGER_PRICE);
var offerId = alicesOffer.getId();
assertFalse(alicesOffer.getIsCurrencyForMakerFeeBtc());
diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java
index ece3432123b..c4abd90934b 100644
--- a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java
@@ -75,7 +75,8 @@ public void testTakeAlicesSellOffer(final TestInfo testInfo) {
0.00,
getDefaultBuyerSecurityDepositAsPercent(),
alicesUsdAccount.getId(),
- TRADE_FEE_CURRENCY_CODE);
+ TRADE_FEE_CURRENCY_CODE,
+ NO_TRIGGER_PRICE);
var offerId = alicesOffer.getId();
assertTrue(alicesOffer.getIsCurrencyForMakerFeeBtc());
diff --git a/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java b/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java
new file mode 100644
index 00000000000..80d70b71656
--- /dev/null
+++ b/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java
@@ -0,0 +1,167 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.apitest.scenario;
+
+import bisq.core.payment.PaymentAccount;
+
+import bisq.proto.grpc.OfferInfo;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.condition.EnabledIf;
+
+import static bisq.apitest.config.ApiTestConfig.BTC;
+import static bisq.cli.CurrencyFormat.formatPrice;
+import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
+import static java.lang.System.getenv;
+import static org.junit.jupiter.api.Assertions.fail;
+import static protobuf.OfferPayload.Direction.BUY;
+import static protobuf.OfferPayload.Direction.SELL;
+
+
+
+import bisq.apitest.method.offer.AbstractOfferTest;
+
+/**
+ * Used to verify trigger based, automatic offer deactivation works.
+ * Disabled by default.
+ * Set ENV or IDE-ENV LONG_RUNNING_OFFER_DEACTIVATION_TEST_ENABLED=true to run.
+ */
+@EnabledIf("envLongRunningTestEnabled")
+@Slf4j
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class LongRunningOfferDeactivationTest extends AbstractOfferTest {
+
+ private static final int MAX_ITERATIONS = 500;
+
+ @Test
+ @Order(1)
+ public void testSellOfferAutoDisable(final TestInfo testInfo) {
+ PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, "US");
+ double mktPriceAsDouble = aliceClient.getBtcPrice("USD");
+ long triggerPrice = calcTriggerPriceAsLong.apply(mktPriceAsDouble, -50.0000);
+ log.info("Current USD mkt price = {} Trigger Price = {}", mktPriceAsDouble, formatPrice(triggerPrice));
+ OfferInfo offer = aliceClient.createMarketBasedPricedOffer(SELL.name(),
+ "USD",
+ 1_000_000,
+ 1_000_000,
+ 0.00,
+ getDefaultBuyerSecurityDepositAsPercent(),
+ paymentAcct.getId(),
+ BTC,
+ triggerPrice);
+ log.info("SELL offer {} created with margin based price {}.",
+ offer.getId(),
+ formatPrice(offer.getPrice()));
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+
+ offer = aliceClient.getMyOffer(offer.getId()); // Offer has trigger price now.
+ log.info("SELL offer should be automatically disabled when mkt price falls below {}.",
+ formatPrice(offer.getTriggerPrice()));
+
+ int numIterations = 0;
+ while (++numIterations < MAX_ITERATIONS) {
+ offer = aliceClient.getMyOffer(offer.getId());
+ ;
+ var mktPrice = aliceClient.getBtcPrice("USD");
+ if (offer.getIsActivated()) {
+ log.info("Offer still enabled at mkt price {} > {} trigger price",
+ mktPrice,
+ formatPrice(offer.getTriggerPrice()));
+ sleep(1000 * 60); // 60s
+ } else {
+ log.info("Successful test completion after offer disabled at mkt price {} < {} trigger price.",
+ mktPrice,
+ formatPrice(offer.getTriggerPrice()));
+ break;
+ }
+ if (numIterations == MAX_ITERATIONS)
+ fail("Offer never disabled");
+
+ genBtcBlocksThenWait(1, 0);
+ }
+ }
+
+ @Test
+ @Order(2)
+ public void testBuyOfferAutoDisable(final TestInfo testInfo) {
+ PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, "US");
+ double mktPriceAsDouble = aliceClient.getBtcPrice("USD");
+ long triggerPrice = calcTriggerPriceAsLong.apply(mktPriceAsDouble, 50.0000);
+ log.info("Current USD mkt price = {} Trigger Price = {}", mktPriceAsDouble, formatPrice(triggerPrice));
+ OfferInfo offer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
+ "USD",
+ 1_000_000,
+ 1_000_000,
+ 0.00,
+ getDefaultBuyerSecurityDepositAsPercent(),
+ paymentAcct.getId(),
+ BTC,
+ triggerPrice);
+ log.info("BUY offer {} created with margin based price {}.",
+ offer.getId(),
+ formatPrice(offer.getPrice()));
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+
+ offer = aliceClient.getMyOffer(offer.getId()); // Offer has trigger price now.
+ log.info("BUY offer should be automatically disabled when mkt price rises above {}.",
+ formatPrice(offer.getTriggerPrice()));
+
+ int numIterations = 0;
+ while (++numIterations < MAX_ITERATIONS) {
+ offer = aliceClient.getMyOffer(offer.getId());
+ ;
+ var mktPrice = aliceClient.getBtcPrice("USD");
+ if (offer.getIsActivated()) {
+ log.info("Offer still enabled at mkt price {} < {} trigger price",
+ mktPrice,
+ formatPrice(offer.getTriggerPrice()));
+ sleep(1000 * 60); // 60s
+ } else {
+ log.info("Successful test completion after offer disabled at mkt price {} > {} trigger price.",
+ mktPrice,
+ formatPrice(offer.getTriggerPrice()));
+ break;
+ }
+ if (numIterations == MAX_ITERATIONS)
+ fail("Offer never disabled");
+
+ genBtcBlocksThenWait(1, 0);
+ }
+ }
+
+ protected static boolean envLongRunningTestEnabled() {
+ String envName = "LONG_RUNNING_OFFER_DEACTIVATION_TEST_ENABLED";
+ String envX = getenv(envName);
+ if (envX != null) {
+ log.info("Enabled, found {}.", envName);
+ return true;
+ } else {
+ log.info("Skipped, no environment variable {} defined.", envName);
+ log.info("To enable on Mac OS or Linux:"
+ + "\tIf running in terminal, export LONG_RUNNING_OFFER_DEACTIVATION_TEST_ENABLED=true in bash shell."
+ + "\tIf running in Intellij, set LONG_RUNNING_OFFER_DEACTIVATION_TEST_ENABLED=true in launcher's Environment variables field.");
+ return false;
+ }
+ }
+}
diff --git a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
index 15c11e65b49..292df14a511 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
@@ -32,6 +32,7 @@
import bisq.apitest.method.offer.CreateBSQOffersTest;
import bisq.apitest.method.offer.CreateOfferUsingFixedPriceTest;
import bisq.apitest.method.offer.CreateOfferUsingMarketPriceMarginTest;
+import bisq.apitest.method.offer.EditOfferTest;
import bisq.apitest.method.offer.ValidateCreateOfferTest;
@Slf4j
@@ -71,11 +72,12 @@ public void testCreateOfferUsingMarketPriceMargin() {
test.testCreateNZDBTCBuyOfferMinus2PctPriceMargin();
test.testCreateGBPBTCSellOfferMinus1Point5PctPriceMargin();
test.testCreateBRLBTCSellOffer6Point55PctPriceMargin();
+ test.testCreateUSDBTCBuyOfferWithTriggerPrice();
}
@Test
@Order(5)
- public void testCreateBSQOffersTest() {
+ public void testCreateBSQOffers() {
CreateBSQOffersTest test = new CreateBSQOffersTest();
CreateBSQOffersTest.createBsqPaymentAccounts();
test.testCreateBuy1BTCFor20KBSQOffer();
@@ -85,4 +87,21 @@ public void testCreateBSQOffersTest() {
test.testGetAllMyBsqOffers();
test.testGetAvailableBsqOffers();
}
+
+ @Test
+ @Order(6)
+ public void testEditOffer() {
+ EditOfferTest test = new EditOfferTest();
+ test.testOfferDisableAndEnable();
+ test.testEditTriggerPrice();
+ test.testSetTriggerPriceToNegativeValueShouldThrowException();
+ test.testEditMktPriceMargin();
+ test.testEditFixedPrice();
+ test.testEditFixedPriceAndDeactivation();
+ test.testEditMktPriceMarginAndTriggerPriceAndDeactivation();
+ test.testEditingFixedPriceInMktPriceMarginBasedOfferShouldThrowException();
+ test.testEditingTriggerPriceInFixedPriceOfferShouldThrowException();
+ test.testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice();
+ test.testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt();
+ }
}
diff --git a/apitest/src/test/java/bisq/apitest/scenario/PaymentAccountTest.java b/apitest/src/test/java/bisq/apitest/scenario/PaymentAccountTest.java
index 1aaf553c857..c3eb41343a9 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/PaymentAccountTest.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/PaymentAccountTest.java
@@ -51,6 +51,7 @@ public void testCreatePaymentAccount(TestInfo testInfo) {
test.testCreateAustraliaPayidAccount(testInfo);
test.testCreateCashDepositAccount(testInfo);
test.testCreateBrazilNationalBankAccount(testInfo);
+ test.testCreateChaseQuickPayAccount(testInfo);
test.testCreateClearXChangeAccount(testInfo);
test.testCreateF2FAccount(testInfo);
test.testCreateFasterPaymentsAccount(testInfo);
diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java b/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java
index c34dc14d28b..d6941a0a402 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java
@@ -134,7 +134,8 @@ public OfferInfo createOfferAtMarketBasedPrice(PaymentAccount paymentAccount,
long minAmountInSatoshis,
double priceMarginAsPercent,
double securityDepositAsPercent,
- String feeCurrency) {
+ String feeCurrency,
+ long triggerPrice) {
return grpcClient.createMarketBasedPricedOffer(direction,
currencyCode,
amountInSatoshis,
@@ -142,7 +143,8 @@ public OfferInfo createOfferAtMarketBasedPrice(PaymentAccount paymentAccount,
priceMarginAsPercent,
securityDepositAsPercent,
paymentAccount.getId(),
- feeCurrency);
+ feeCurrency,
+ triggerPrice);
}
/**
diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java b/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java
index 1942f8ad073..de728aa76e9 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java
@@ -33,7 +33,7 @@
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
-import static bisq.cli.CurrencyFormat.formatMarketPrice;
+import static bisq.cli.CurrencyFormat.formatInternalFiatPrice;
import static bisq.cli.CurrencyFormat.formatSatoshis;
import static bisq.common.util.MathUtils.scaleDownByPowerOf10;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
@@ -128,7 +128,8 @@ public RandomOffer create() throws InvalidRandomOfferException {
minAmount,
priceMargin,
getDefaultBuyerSecurityDepositAsPercent(),
- feeCurrency);
+ feeCurrency,
+ 0 /*no trigger price*/);
} else {
this.offer = botClient.createOfferAtFixedPrice(paymentAccount,
direction,
@@ -167,11 +168,11 @@ private void printDescription() {
log.info(description);
if (useMarketBasedPrice) {
log.info("Offer Price Margin = {}%", priceMargin);
- log.info("Expected Offer Price = {} {}", formatMarketPrice(Double.parseDouble(fixedOfferPrice)), currencyCode);
+ log.info("Expected Offer Price = {} {}", formatInternalFiatPrice(Double.parseDouble(fixedOfferPrice)), currencyCode);
} else {
log.info("Fixed Offer Price = {} {}", fixedOfferPrice, currencyCode);
}
- log.info("Current Market Price = {} {}", formatMarketPrice(currentMarketPrice), currencyCode);
+ log.info("Current Market Price = {} {}", formatInternalFiatPrice(currentMarketPrice), currencyCode);
}
}
From 571568a5e5201af2cef53aa1af3eb2dab82009ff Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 13 Jun 2021 13:10:41 -0300
Subject: [PATCH 10/40] Remove chase quickpay acct test
---
.../payment/CreatePaymentAccountTest.java | 22 -------------------
.../apitest/scenario/PaymentAccountTest.java | 1 -
2 files changed, 23 deletions(-)
diff --git a/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java b/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java
index 3caef6ee391..90440781829 100644
--- a/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java
@@ -253,28 +253,6 @@ public void testCreateBrazilNationalBankAccount(TestInfo testInfo) {
print(paymentAccount);
}
- @Test
- public void testCreateChaseQuickPayAccount(TestInfo testInfo) {
- File emptyForm = getEmptyForm(testInfo, CHASE_QUICK_PAY_ID);
- verifyEmptyForm(emptyForm,
- CHASE_QUICK_PAY_ID,
- PROPERTY_NAME_EMAIL,
- PROPERTY_NAME_HOLDER_NAME);
- COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, CHASE_QUICK_PAY_ID);
- COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Quick Pay Acct");
- COMPLETED_FORM_MAP.put(PROPERTY_NAME_EMAIL, "johndoe@quickpay.com");
- COMPLETED_FORM_MAP.put(PROPERTY_NAME_HOLDER_NAME, "John Doe");
- COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, "");
- String jsonString = getCompletedFormAsJsonString();
- ChaseQuickPayAccount paymentAccount = (ChaseQuickPayAccount) createPaymentAccount(aliceClient, jsonString);
- verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
- verifyAccountSingleTradeCurrency("USD", paymentAccount);
- verifyCommonFormEntries(paymentAccount);
- assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL), paymentAccount.getEmail());
- assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
- print(paymentAccount);
- }
-
@Test
public void testCreateClearXChangeAccount(TestInfo testInfo) {
File emptyForm = getEmptyForm(testInfo, CLEAR_X_CHANGE_ID);
diff --git a/apitest/src/test/java/bisq/apitest/scenario/PaymentAccountTest.java b/apitest/src/test/java/bisq/apitest/scenario/PaymentAccountTest.java
index c3eb41343a9..1aaf553c857 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/PaymentAccountTest.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/PaymentAccountTest.java
@@ -51,7 +51,6 @@ public void testCreatePaymentAccount(TestInfo testInfo) {
test.testCreateAustraliaPayidAccount(testInfo);
test.testCreateCashDepositAccount(testInfo);
test.testCreateBrazilNationalBankAccount(testInfo);
- test.testCreateChaseQuickPayAccount(testInfo);
test.testCreateClearXChangeAccount(testInfo);
test.testCreateF2FAccount(testInfo);
test.testCreateFasterPaymentsAccount(testInfo);
From 9a5e2d0df107e92f04322a26a19f924d07f79a05 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 13 Jun 2021 13:12:45 -0300
Subject: [PATCH 11/40] Remove unused import
---
.../bisq/apitest/method/payment/CreatePaymentAccountTest.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java b/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java
index 90440781829..b7eb7f7ebb7 100644
--- a/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java
@@ -22,7 +22,6 @@
import bisq.core.payment.AliPayAccount;
import bisq.core.payment.AustraliaPayid;
import bisq.core.payment.CashDepositAccount;
-import bisq.core.payment.ChaseQuickPayAccount;
import bisq.core.payment.ClearXchangeAccount;
import bisq.core.payment.F2FAccount;
import bisq.core.payment.FasterPaymentsAccount;
From 05f39854471d0bac45de65b4bf0acdd25add358e Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 13 Jun 2021 13:54:16 -0300
Subject: [PATCH 12/40] Fix problems found in codacy check
---
.../apitest/scenario/LongRunningOfferDeactivationTest.java | 2 +-
cli/src/main/java/bisq/cli/CurrencyFormat.java | 1 -
core/src/main/java/bisq/core/api/CoreOffersService.java | 3 ++-
core/src/main/java/bisq/core/api/EditOfferValidator.java | 7 ++-----
4 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java b/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java
index 80d70b71656..c15aaba6ade 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java
@@ -82,7 +82,7 @@ public void testSellOfferAutoDisable(final TestInfo testInfo) {
int numIterations = 0;
while (++numIterations < MAX_ITERATIONS) {
offer = aliceClient.getMyOffer(offer.getId());
- ;
+
var mktPrice = aliceClient.getBtcPrice("USD");
if (offer.getIsActivated()) {
log.info("Offer still enabled at mkt price {} > {} trigger price",
diff --git a/cli/src/main/java/bisq/cli/CurrencyFormat.java b/cli/src/main/java/bisq/cli/CurrencyFormat.java
index 47bdc42df81..29639ec7b83 100644
--- a/cli/src/main/java/bisq/cli/CurrencyFormat.java
+++ b/cli/src/main/java/bisq/cli/CurrencyFormat.java
@@ -40,7 +40,6 @@ public class CurrencyFormat {
// Formats numbers for internal use, i.e., grpc request parameters.
private static final DecimalFormat INTERNAL_FIAT_DECIMAL_FORMAT = new DecimalFormat("##############0.0000");
- private static final DecimalFormat INTERNAL_ALTCOIN_DECIMAL_FORMAT = new DecimalFormat("##############0.00000000");
static final BigDecimal SATOSHI_DIVISOR = new BigDecimal(100_000_000);
static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.00000000");
diff --git a/core/src/main/java/bisq/core/api/CoreOffersService.java b/core/src/main/java/bisq/core/api/CoreOffersService.java
index 365072f1967..39603387764 100644
--- a/core/src/main/java/bisq/core/api/CoreOffersService.java
+++ b/core/src/main/java/bisq/core/api/CoreOffersService.java
@@ -55,6 +55,7 @@
import static bisq.common.util.MathUtils.roundDoubleToLong;
import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
+import static bisq.core.offer.Offer.State;
import static bisq.core.offer.OfferPayload.Direction;
import static bisq.core.offer.OfferPayload.Direction.BUY;
import static bisq.core.offer.OpenOffer.State.AVAILABLE;
@@ -225,7 +226,7 @@ void editOffer(String offerId,
Offer editedOffer = new Offer(editedPayload);
priceFeedService.setCurrencyCode(openOffer.getOffer().getOfferPayload().getCurrencyCode());
editedOffer.setPriceFeedService(priceFeedService);
- editedOffer.setState(Offer.State.AVAILABLE);
+ editedOffer.setState(State.AVAILABLE);
openOfferManager.editOpenOfferStart(openOffer,
() -> {
log.info("EditOpenOfferStart: offer {}", openOffer.getId());
diff --git a/core/src/main/java/bisq/core/api/EditOfferValidator.java b/core/src/main/java/bisq/core/api/EditOfferValidator.java
index f451e330811..deea892b47b 100644
--- a/core/src/main/java/bisq/core/api/EditOfferValidator.java
+++ b/core/src/main/java/bisq/core/api/EditOfferValidator.java
@@ -57,13 +57,10 @@ void validate() {
case MKT_PRICE_MARGIN_ONLY:
case MKT_PRICE_MARGIN_AND_ACTIVATION_STATE:
case TRIGGER_PRICE_ONLY:
- case TRIGGER_PRICE_AND_ACTIVATION_STATE: {
- // Make sure the edited trigger price is OK, even if not being changed.
- validateEditedTriggerPrice();
- // Continue, no break.
- }
+ case TRIGGER_PRICE_AND_ACTIVATION_STATE:
case MKT_PRICE_MARGIN_AND_TRIGGER_PRICE:
case MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE: {
+ validateEditedTriggerPrice();
validateEditedMarketPriceMargin();
break;
}
From 54efad097d0eafbfcc427f9879f22415dde75285 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 13 Jun 2021 14:05:00 -0300
Subject: [PATCH 13/40] Fix codacy issue
---
.../bisq/apitest/scenario/LongRunningOfferDeactivationTest.java | 2 +-
core/src/main/java/bisq/core/api/CoreOffersService.java | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java b/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java
index c15aaba6ade..2aeea5a436c 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java
@@ -130,7 +130,7 @@ public void testBuyOfferAutoDisable(final TestInfo testInfo) {
int numIterations = 0;
while (++numIterations < MAX_ITERATIONS) {
offer = aliceClient.getMyOffer(offer.getId());
- ;
+
var mktPrice = aliceClient.getBtcPrice("USD");
if (offer.getIsActivated()) {
log.info("Offer still enabled at mkt price {} < {} trigger price",
diff --git a/core/src/main/java/bisq/core/api/CoreOffersService.java b/core/src/main/java/bisq/core/api/CoreOffersService.java
index 39603387764..0464efb4812 100644
--- a/core/src/main/java/bisq/core/api/CoreOffersService.java
+++ b/core/src/main/java/bisq/core/api/CoreOffersService.java
@@ -56,6 +56,7 @@
import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
import static bisq.core.offer.Offer.State;
+import static bisq.core.offer.Offer.State.*;
import static bisq.core.offer.OfferPayload.Direction;
import static bisq.core.offer.OfferPayload.Direction.BUY;
import static bisq.core.offer.OpenOffer.State.AVAILABLE;
From 21ac46ac0f7488627ac8a9855edef0b48be86051 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 13 Jun 2021 17:22:04 -0300
Subject: [PATCH 14/40] Fix log arg spec bug
---
core/src/main/java/bisq/core/api/EditOfferValidator.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/core/src/main/java/bisq/core/api/EditOfferValidator.java b/core/src/main/java/bisq/core/api/EditOfferValidator.java
index deea892b47b..0ba354d76b7 100644
--- a/core/src/main/java/bisq/core/api/EditOfferValidator.java
+++ b/core/src/main/java/bisq/core/api/EditOfferValidator.java
@@ -83,7 +83,7 @@ private void validateEditedActivationState() {
private void validateEditedFixedPrice() {
if (currentlyOpenOffer.getOffer().isUseMarketBasedPrice())
- log.info("Attempting to change mkt price margin based offer with id '%s' to fixed price offer.",
+ log.info("Attempting to change mkt price margin based offer with id '{}' to fixed price offer.",
currentlyOpenOffer.getId());
if (editedUseMarketBasedPrice)
@@ -104,7 +104,7 @@ private void validateEditedFixedPrice() {
private void validateEditedMarketPriceMargin() {
if (!currentlyOpenOffer.getOffer().isUseMarketBasedPrice())
- log.info("Attempting to change fixed price offer with id '%s' to mkt price margin based offer.",
+ log.info("Attempting to change fixed price offer with id '{}' to mkt price margin based offer.",
currentlyOpenOffer.getId());
if (!editedUseMarketBasedPrice && !isZeroEditedTriggerPrice)
From 32688a713f4c09e75414fee37c7f5a2647d5a754 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Tue, 15 Jun 2021 11:17:02 -0300
Subject: [PATCH 15/40] Add bool isMyOffer to OfferInfo proto
---
.../java/bisq/core/api/model/OfferInfo.java | 21 +++++++++++++++----
proto/src/main/proto/grpc.proto | 1 +
2 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/core/src/main/java/bisq/core/api/model/OfferInfo.java b/core/src/main/java/bisq/core/api/model/OfferInfo.java
index e0588817041..ce645e6ab69 100644
--- a/core/src/main/java/bisq/core/api/model/OfferInfo.java
+++ b/core/src/main/java/bisq/core/api/model/OfferInfo.java
@@ -63,6 +63,7 @@ public class OfferInfo implements Payload {
private final long date;
private final String state;
private final boolean isActivated;
+ private final boolean isMyOffer;
public OfferInfo(OfferInfoBuilder builder) {
this.id = builder.id;
@@ -89,20 +90,23 @@ public OfferInfo(OfferInfoBuilder builder) {
this.date = builder.date;
this.state = builder.state;
this.isActivated = builder.isActivated;
+ this.isMyOffer = builder.isMyOffer;
}
public static OfferInfo toOfferInfo(Offer offer) {
- return getOfferInfoBuilder(offer).build();
+ // Offer is not mine.
+ return getOfferInfoBuilder(offer, false).build();
}
public static OfferInfo toOfferInfo(OpenOffer openOffer) {
- return getOfferInfoBuilder(openOffer.getOffer())
+ // OpenOffer is mine.
+ return getOfferInfoBuilder(openOffer.getOffer(), true)
.withTriggerPrice(openOffer.getTriggerPrice())
.withIsActivated(!openOffer.isDeactivated())
.build();
}
- private static OfferInfoBuilder getOfferInfoBuilder(Offer offer) {
+ private static OfferInfoBuilder getOfferInfoBuilder(Offer offer, boolean isMyOffer) {
return new OfferInfoBuilder()
.withId(offer.getId())
.withDirection(offer.getDirection().name())
@@ -125,7 +129,8 @@ private static OfferInfoBuilder getOfferInfoBuilder(Offer offer) {
.withBaseCurrencyCode(offer.getOfferPayload().getBaseCurrencyCode())
.withCounterCurrencyCode(offer.getOfferPayload().getCounterCurrencyCode())
.withDate(offer.getDate().getTime())
- .withState(offer.getState().name());
+ .withState(offer.getState().name())
+ .withIsMyOffer(isMyOffer);
}
///////////////////////////////////////////////////////////////////////////////////////////
@@ -159,6 +164,7 @@ public bisq.proto.grpc.OfferInfo toProtoMessage() {
.setDate(date)
.setState(state)
.setIsActivated(isActivated)
+ .setIsMyOffer(isMyOffer)
.build();
}
@@ -189,6 +195,7 @@ public static OfferInfo fromProto(bisq.proto.grpc.OfferInfo proto) {
.withDate(proto.getDate())
.withState(proto.getState())
.withIsActivated(proto.getIsActivated())
+ .withIsMyOffer(proto.getIsMyOffer())
.build();
}
@@ -223,6 +230,7 @@ public static class OfferInfoBuilder {
private long date;
private String state;
private boolean isActivated;
+ private boolean isMyOffer;
public OfferInfoBuilder withId(String id) {
this.id = id;
@@ -344,6 +352,11 @@ public OfferInfoBuilder withIsActivated(boolean isActivated) {
return this;
}
+ public OfferInfoBuilder withIsMyOffer(boolean isMyOffer) {
+ this.isMyOffer = isMyOffer;
+ return this;
+ }
+
public OfferInfo build() {
return new OfferInfo(this);
}
diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto
index 0fa653e4c24..ff2467f713f 100644
--- a/proto/src/main/proto/grpc.proto
+++ b/proto/src/main/proto/grpc.proto
@@ -191,6 +191,7 @@ message OfferInfo {
uint64 txFee = 22;
uint64 makerFee = 23;
bool isActivated = 24;
+ bool isMyOffer = 25;
}
message AvailabilityResultWithDescription {
From 738d2f70ef7804a8e69707855ef944dbd4b01221 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Tue, 15 Jun 2021 11:18:07 -0300
Subject: [PATCH 16/40] Fix editoffer validation bugs, tidy up
CoreOffersService
---
.../java/bisq/core/api/CoreOffersService.java | 78 ++++++++++++-------
.../bisq/core/api/EditOfferValidator.java | 29 +++----
2 files changed, 67 insertions(+), 40 deletions(-)
diff --git a/core/src/main/java/bisq/core/api/CoreOffersService.java b/core/src/main/java/bisq/core/api/CoreOffersService.java
index 0464efb4812..adcbc53f8c8 100644
--- a/core/src/main/java/bisq/core/api/CoreOffersService.java
+++ b/core/src/main/java/bisq/core/api/CoreOffersService.java
@@ -56,13 +56,13 @@
import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
import static bisq.core.offer.Offer.State;
-import static bisq.core.offer.Offer.State.*;
import static bisq.core.offer.OfferPayload.Direction;
import static bisq.core.offer.OfferPayload.Direction.BUY;
import static bisq.core.offer.OpenOffer.State.AVAILABLE;
import static bisq.core.offer.OpenOffer.State.DEACTIVATED;
import static bisq.core.payment.PaymentAccountUtil.isPaymentAccountValidForOffer;
import static bisq.proto.grpc.EditOfferRequest.EditType;
+import static bisq.proto.grpc.EditOfferRequest.EditType.*;
import static java.lang.String.format;
import static java.util.Comparator.comparing;
@@ -220,34 +220,45 @@ void editOffer(String offerId,
editedUseMarketBasedPrice,
editedMarketPriceMargin,
editedTriggerPrice,
+ editedEnable,
editType).validate();
- OfferPayload editedPayload = getMergedOfferPayload(openOffer, editedPriceAsString,
+ log.info("'editoffer' params OK for offerId={}"
+ + "\n\teditedPriceAsString={}"
+ + "\n\teditedUseMarketBasedPrice={}"
+ + "\n\teditedMarketPriceMargin={}"
+ + "\n\teditedTriggerPrice={}"
+ + "\n\teditedEnable={}"
+ + "\n\teditType={}",
+ offerId,
+ editedPriceAsString,
editedUseMarketBasedPrice,
- editedMarketPriceMargin);
+ editedMarketPriceMargin,
+ editedTriggerPrice,
+ editedEnable,
+ editType);
+ OpenOffer.State currentOfferState = openOffer.getState();
+ // Client sent (sint32) editedEnable, not a bool (with default=false).
+ // If editedEnable = -1, do not change current state
+ // If editedEnable = 0, set state = AVAILABLE
+ // If editedEnable = 1, set state = DEACTIVATED
+ OpenOffer.State newOfferState = editedEnable < 0
+ ? currentOfferState
+ : editedEnable > 0 ? AVAILABLE : DEACTIVATED;
+ OfferPayload editedPayload = getMergedOfferPayload(openOffer,
+ editedPriceAsString,
+ editedMarketPriceMargin,
+ editType);
Offer editedOffer = new Offer(editedPayload);
priceFeedService.setCurrencyCode(openOffer.getOffer().getOfferPayload().getCurrencyCode());
editedOffer.setPriceFeedService(priceFeedService);
editedOffer.setState(State.AVAILABLE);
openOfferManager.editOpenOfferStart(openOffer,
- () -> {
- log.info("EditOpenOfferStart: offer {}", openOffer.getId());
- },
- errorMessage -> {
- log.error(errorMessage);
- });
- // Client sent (sint32) newEnable, not a bool (with default=false).
- // If newEnable = -1, do not change activation state
- // If newEnable = 0, set state = AVAILABLE
- // If newEnable = 1, set state = DEACTIVATED
- OpenOffer.State newOfferState = editedEnable < 0
- ? openOffer.getState()
- : editedEnable > 0 ? AVAILABLE : DEACTIVATED;
+ () -> log.info("EditOpenOfferStart: offer {}", openOffer.getId()),
+ log::error);
openOfferManager.editOpenOfferPublish(editedOffer,
editedTriggerPrice,
newOfferState,
- () -> {
- log.info("EditOpenOfferPublish: offer {}", openOffer.getId());
- },
+ () -> log.info("EditOpenOfferPublish: offer {}", openOffer.getId()),
log::error);
}
@@ -277,17 +288,31 @@ private void placeOffer(Offer offer,
private OfferPayload getMergedOfferPayload(OpenOffer openOffer,
String editedPriceAsString,
- boolean editedUseMarketBasedPrice,
- double editedMarketPriceMargin) {
- // API supports editing price, marketPriceMargin, useMarketBasedPrice payload
- // fields. API does not support editing payment acct or currency code fields.
+ double editedMarketPriceMargin,
+ EditType editType) {
+ // API supports editing (1) price, OR (2) marketPriceMargin & useMarketBasedPrice
+ // OfferPayload fields. API does not support editing payment acct or currency
+ // code fields. Note: triggerPrice isDeactivated fields are in OpenOffer, not
+ // in OfferPayload.
Offer offer = openOffer.getOffer();
String currencyCode = offer.getOfferPayload().getCurrencyCode();
- Price editedPrice = Price.valueOf(currencyCode, priceStringToLong(editedPriceAsString, currencyCode));
+ boolean isEditingPrice = editType.equals(FIXED_PRICE_ONLY) || editType.equals(FIXED_PRICE_AND_ACTIVATION_STATE);
+ Price editedPrice;
+ if (isEditingPrice) {
+ editedPrice = Price.valueOf(currencyCode, priceStringToLong(editedPriceAsString, currencyCode));
+ } else {
+ editedPrice = offer.getPrice();
+ }
+ boolean isUsingMktPriceMargin = editType.equals(MKT_PRICE_MARGIN_ONLY)
+ || editType.equals(MKT_PRICE_MARGIN_AND_ACTIVATION_STATE)
+ || editType.equals(TRIGGER_PRICE_ONLY)
+ || editType.equals(TRIGGER_PRICE_AND_ACTIVATION_STATE)
+ || editType.equals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE)
+ || editType.equals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE);
MutableOfferPayloadFields mutableOfferPayloadFields = new MutableOfferPayloadFields(
editedPrice.getValue(),
- exactMultiply(editedMarketPriceMargin, 0.01),
- editedUseMarketBasedPrice,
+ isUsingMktPriceMargin ? exactMultiply(editedMarketPriceMargin, 0.01) : 0.00,
+ isUsingMktPriceMargin,
offer.getOfferPayload().getBaseCurrencyCode(),
offer.getOfferPayload().getCounterCurrencyCode(),
offer.getPaymentMethod().getId(),
@@ -296,6 +321,7 @@ private OfferPayload getMergedOfferPayload(OpenOffer openOffer,
offer.getOfferPayload().getAcceptedCountryCodes(),
offer.getOfferPayload().getBankId(),
offer.getOfferPayload().getAcceptedBankIds());
+ log.info("Merging OfferPayload with {}", mutableOfferPayloadFields);
return offerUtil.getMergedOfferPayload(openOffer, mutableOfferPayloadFields);
}
diff --git a/core/src/main/java/bisq/core/api/EditOfferValidator.java b/core/src/main/java/bisq/core/api/EditOfferValidator.java
index 0ba354d76b7..82f6b78b737 100644
--- a/core/src/main/java/bisq/core/api/EditOfferValidator.java
+++ b/core/src/main/java/bisq/core/api/EditOfferValidator.java
@@ -18,6 +18,7 @@ class EditOfferValidator {
private final boolean editedUseMarketBasedPrice;
private final double editedMarketPriceMargin;
private final long editedTriggerPrice;
+ private final int editedEnable;
private final EditOfferRequest.EditType editType;
private final boolean isZeroEditedFixedPriceString;
@@ -29,12 +30,14 @@ class EditOfferValidator {
boolean editedUseMarketBasedPrice,
double editedMarketPriceMargin,
long editedTriggerPrice,
+ int editedEnable,
EditOfferRequest.EditType editType) {
this.currentlyOpenOffer = currentlyOpenOffer;
this.editedPriceAsString = editedPriceAsString;
this.editedUseMarketBasedPrice = editedUseMarketBasedPrice;
this.editedMarketPriceMargin = editedMarketPriceMargin;
this.editedTriggerPrice = editedTriggerPrice;
+ this.editedEnable = editedEnable;
this.editType = editType;
this.isZeroEditedFixedPriceString = new BigDecimal(editedPriceAsString).doubleValue() == 0;
@@ -70,14 +73,10 @@ void validate() {
}
private void validateEditedActivationState() {
- if (!isZeroEditedFixedPriceString || !isZeroEditedMarketPriceMargin || !isZeroEditedTriggerPrice)
+ if (editedEnable < 0)
throw new IllegalStateException(
- format("programmer error: cannot change fixed price (%s), "
- + " mkt price margin (%s), or trigger price (%s) "
- + " in offer with id '%s' when only changing activation state",
- editedPriceAsString,
- editedMarketPriceMargin,
- editedTriggerPrice,
+ format("programmer error: the 'enable' request parameter does not"
+ + " indicate activation state of offer with id '{}' should be changed.",
currentlyOpenOffer.getId()));
}
@@ -107,13 +106,6 @@ private void validateEditedMarketPriceMargin() {
log.info("Attempting to change fixed price offer with id '{}' to mkt price margin based offer.",
currentlyOpenOffer.getId());
- if (!editedUseMarketBasedPrice && !isZeroEditedTriggerPrice)
- throw new IllegalStateException(
- format("programmer error: cannot set a trigger price (%s)"
- + " in fixed price offer with id '%s'",
- editedTriggerPrice,
- currentlyOpenOffer.getId()));
-
if (!isZeroEditedFixedPriceString)
throw new IllegalStateException(
format("programmer error: cannot set fixed price (%s)"
@@ -123,6 +115,15 @@ private void validateEditedMarketPriceMargin() {
}
private void validateEditedTriggerPrice() {
+ if (!currentlyOpenOffer.getOffer().isUseMarketBasedPrice()
+ && !editedUseMarketBasedPrice
+ && !isZeroEditedTriggerPrice)
+ throw new IllegalStateException(
+ format("programmer error: cannot set a trigger price (%s)"
+ + " in fixed price offer with id '%s'",
+ editedTriggerPrice,
+ currentlyOpenOffer.getId()));
+
if (editedTriggerPrice < 0)
throw new IllegalStateException(
format("programmer error: cannot set trigger price to a negative value"
From e2a205a31d04d94bbcdb825be45a297c9a630047 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Tue, 15 Jun 2021 11:24:27 -0300
Subject: [PATCH 17/40] Show enable/trigger-price cols for 'getmyoffer'
---
cli/src/main/java/bisq/cli/CliMain.java | 2 +-
.../java/bisq/cli/ColumnHeaderConstants.java | 3 +-
cli/src/main/java/bisq/cli/TableFormat.java | 109 ++++++++++++++----
3 files changed, 88 insertions(+), 26 deletions(-)
diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java
index 2b481c45875..bf3978675c6 100644
--- a/cli/src/main/java/bisq/cli/CliMain.java
+++ b/cli/src/main/java/bisq/cli/CliMain.java
@@ -370,7 +370,7 @@ public static void run(String[] args) {
triggerPrice,
enable,
editOfferType);
- out.println("edited offer being re-added to offer book");
+ out.println("offer has been edited");
return;
}
case canceloffer: {
diff --git a/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java b/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java
index 775221b5ed5..32a4564d16d 100644
--- a/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java
+++ b/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java
@@ -46,6 +46,7 @@ class ColumnHeaderConstants {
static final String COL_HEADER_CREATION_DATE = padEnd("Creation Date (UTC)", 20, ' ');
static final String COL_HEADER_CURRENCY = "Currency";
static final String COL_HEADER_DIRECTION = "Buy/Sell";
+ static final String COL_HEADER_ENABLED = "Enabled";
static final String COL_HEADER_NAME = "Name";
static final String COL_HEADER_PAYMENT_METHOD = "Payment Method";
static final String COL_HEADER_PRICE = "Price in %-3s for 1 BTC";
@@ -64,7 +65,7 @@ class ColumnHeaderConstants {
static final String COL_HEADER_TRADE_TX_FEE = padEnd("Tx Fee(BTC)", 12, ' ');
static final String COL_HEADER_TRADE_MAKER_FEE = padEnd("Maker Fee(%-3s)", 12, ' '); // "Maker Fee(%-3s)";
static final String COL_HEADER_TRADE_TAKER_FEE = padEnd("Taker Fee(%-3s)", 12, ' '); // "Taker Fee(%-3s)";
-
+ static final String COL_HEADER_TRIGGER_PRICE = "Trigger Price(%-3s)";
static final String COL_HEADER_TX_ID = "Tx ID";
static final String COL_HEADER_TX_INPUT_SUM = "Tx Inputs (BTC)";
static final String COL_HEADER_TX_OUTPUT_SUM = "Tx Outputs (BTC)";
diff --git a/cli/src/main/java/bisq/cli/TableFormat.java b/cli/src/main/java/bisq/cli/TableFormat.java
index 5c123184e94..d1d4aa729ac 100644
--- a/cli/src/main/java/bisq/cli/TableFormat.java
+++ b/cli/src/main/java/bisq/cli/TableFormat.java
@@ -147,48 +147,109 @@ public static String formatPaymentAcctTbl(List paymentAccounts)
public static String formatOfferTable(List offers, String currencyCode) {
if (offers == null || offers.isEmpty())
- throw new IllegalArgumentException(format("%s offers argument is empty", currencyCode.toLowerCase()));
+ throw new IllegalArgumentException(format("%s offer list is empty", currencyCode.toLowerCase()));
String baseCurrencyCode = offers.get(0).getBaseCurrencyCode();
+ boolean isMyOffer = offers.get(0).getIsMyOffer();
return baseCurrencyCode.equalsIgnoreCase("BTC")
- ? formatFiatOfferTable(offers, currencyCode)
+ ? formatFiatOfferTable(offers, currencyCode, isMyOffer)
: formatCryptoCurrencyOfferTable(offers, baseCurrencyCode);
}
- private static String formatFiatOfferTable(List offers, String fiatCurrencyCode) {
+ private static String formatFiatOfferTable(List offers,
+ String fiatCurrencyCode,
+ boolean isMyOffer) {
// Some column values might be longer than header, so we need to calculate them.
int amountColWith = getLongestAmountColWidth(offers);
int volumeColWidth = getLongestVolumeColWidth(offers);
int paymentMethodColWidth = getLongestPaymentMethodColWidth(offers);
- String headersFormat = COL_HEADER_DIRECTION + COL_HEADER_DELIMITER
- + COL_HEADER_PRICE + COL_HEADER_DELIMITER // includes %s -> fiatCurrencyCode
+ // "Enabled" and "Trigger Price" columns are displayed for my offers only.
+ String enabledHeaderFormat = isMyOffer ?
+ COL_HEADER_ENABLED + COL_HEADER_DELIMITER
+ : "";
+ String triggerPriceHeaderFormat = isMyOffer ?
+ // COL_HEADER_TRIGGER_PRICE includes %s -> fiatCurrencyCode
+ COL_HEADER_TRIGGER_PRICE + COL_HEADER_DELIMITER
+ : "";
+ String headersFormat = enabledHeaderFormat
+ + COL_HEADER_DIRECTION + COL_HEADER_DELIMITER
+ // COL_HEADER_PRICE includes %s -> fiatCurrencyCode
+ + COL_HEADER_PRICE + COL_HEADER_DELIMITER
+ padStart(COL_HEADER_AMOUNT, amountColWith, ' ') + COL_HEADER_DELIMITER
// COL_HEADER_VOLUME includes %s -> fiatCurrencyCode
+ padStart(COL_HEADER_VOLUME, volumeColWidth, ' ') + COL_HEADER_DELIMITER
+ + triggerPriceHeaderFormat
+ padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER
+ COL_HEADER_CREATION_DATE + COL_HEADER_DELIMITER
+ COL_HEADER_UUID.trim() + "%n";
String headerLine = format(headersFormat,
fiatCurrencyCode.toUpperCase(),
- fiatCurrencyCode.toUpperCase());
- String colDataFormat = "%-" + (COL_HEADER_DIRECTION.length() + COL_HEADER_DELIMITER.length()) + "s"
- + "%" + (COL_HEADER_PRICE.length() - 1) + "s"
- + " %" + amountColWith + "s"
- + " %" + (volumeColWidth - 1) + "s"
- + " %-" + paymentMethodColWidth + "s"
- + " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
- + " %-" + COL_HEADER_UUID.length() + "s";
- return headerLine
- + offers.stream()
- .map(o -> format(colDataFormat,
- o.getDirection(),
- formatPrice(o.getPrice()),
- formatAmountRange(o.getMinAmount(), o.getAmount()),
- formatVolumeRange(o.getMinVolume(), o.getVolume()),
- o.getPaymentMethodShortName(),
- formatTimestamp(o.getDate()),
- o.getId()))
- .collect(Collectors.joining("\n"));
+ fiatCurrencyCode.toUpperCase(),
+ // COL_HEADER_TRIGGER_PRICE includes %s -> fiatCurrencyCode
+ isMyOffer ? fiatCurrencyCode.toUpperCase() : "");
+ String colDataFormat = getFiatOfferColDataFormat(isMyOffer,
+ amountColWith,
+ volumeColWidth,
+ paymentMethodColWidth);
+ return formattedFiatOfferTable(offers, isMyOffer, headerLine, colDataFormat);
+ }
+
+ private static String formattedFiatOfferTable(List offers,
+ boolean isMyOffer,
+ String headerLine,
+ String colDataFormat) {
+ if (isMyOffer) {
+ return headerLine
+ + offers.stream()
+ .map(o -> format(colDataFormat,
+ o.getIsActivated() ? "YES" : "NO",
+ o.getDirection(),
+ formatPrice(o.getPrice()),
+ formatAmountRange(o.getMinAmount(), o.getAmount()),
+ formatVolumeRange(o.getMinVolume(), o.getVolume()),
+ o.getTriggerPrice() == 0 ? "" : formatPrice(o.getTriggerPrice()),
+ o.getPaymentMethodShortName(),
+ formatTimestamp(o.getDate()),
+ o.getId()))
+ .collect(Collectors.joining("\n"));
+ } else {
+ return headerLine
+ + offers.stream()
+ .map(o -> format(colDataFormat,
+ o.getDirection(),
+ formatPrice(o.getPrice()),
+ formatAmountRange(o.getMinAmount(), o.getAmount()),
+ formatVolumeRange(o.getMinVolume(), o.getVolume()),
+ o.getPaymentMethodShortName(),
+ formatTimestamp(o.getDate()),
+ o.getId()))
+ .collect(Collectors.joining("\n"));
+ }
+ }
+
+ private static String getFiatOfferColDataFormat(boolean isMyOffer,
+ int amountColWith,
+ int volumeColWidth,
+ int paymentMethodColWidth) {
+ if (isMyOffer) {
+ return "%-" + (COL_HEADER_ENABLED.length() + COL_HEADER_DELIMITER.length()) + "s"
+ + "%-" + (COL_HEADER_DIRECTION.length() + COL_HEADER_DELIMITER.length()) + "s"
+ + "%" + (COL_HEADER_PRICE.length() - 1) + "s"
+ + " %" + amountColWith + "s"
+ + " %" + (volumeColWidth - 1) + "s"
+ + " %" + (COL_HEADER_TRIGGER_PRICE.length() - 1) + "s"
+ + " %-" + paymentMethodColWidth + "s"
+ + " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
+ + " %-" + COL_HEADER_UUID.length() + "s";
+ } else {
+ return "%-" + (COL_HEADER_DIRECTION.length() + COL_HEADER_DELIMITER.length()) + "s"
+ + "%" + (COL_HEADER_PRICE.length() - 1) + "s"
+ + " %" + amountColWith + "s"
+ + " %" + (volumeColWidth - 1) + "s"
+ + " %-" + paymentMethodColWidth + "s"
+ + " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
+ + " %-" + COL_HEADER_UUID.length() + "s";
+ }
}
private static String formatCryptoCurrencyOfferTable(List offers, String cryptoCurrencyCode) {
From 4da64b9bd0fd277cce9e0bc66204625fc52589aa Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Tue, 15 Jun 2021 11:27:23 -0300
Subject: [PATCH 18/40] Improve 'editoffer' opt parsing, fix test pkg name
---
.../bisq/cli/opts/EditOfferOptionParser.java | 65 ++++++++++++-------
.../EditOfferOptionParserTest.java | 47 ++++++++++----
.../cli/{opt => opts}/OptionParsersTest.java | 9 +--
3 files changed, 76 insertions(+), 45 deletions(-)
rename cli/src/test/java/bisq/cli/{opt => opts}/EditOfferOptionParserTest.java (87%)
rename cli/src/test/java/bisq/cli/{opt => opts}/OptionParsersTest.java (97%)
diff --git a/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java
index 8a59c891dae..288f1a9f40d 100644
--- a/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java
+++ b/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java
@@ -34,6 +34,10 @@
public class EditOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+ static int OPT_ENABLE_ON = 1;
+ static int OPT_ENABLE_OFF = 0;
+ static int OPT_ENABLE_IGNORED = -1;
+
final OptionSpec offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to cancel")
.withRequiredArg();
@@ -106,11 +110,7 @@ public EditOfferOptionParser parse() {
throw new IllegalArgumentException("no fixed price specified");
String fixedPriceAsString = options.valueOf(fixedPriceOpt);
- try {
- Double.valueOf(fixedPriceAsString);
- } catch (NumberFormatException ex) {
- throw new IllegalArgumentException(format("%s is not a number", fixedPriceAsString));
- }
+ verifyStringIsValidDouble(fixedPriceAsString);
boolean fixedPriceOptIsOnlyOpt = !options.has(mktPriceMarginOpt)
&& !options.has(triggerPriceOpt)
@@ -137,11 +137,7 @@ public EditOfferOptionParser parse() {
if (priceMarginAsString.isEmpty())
throw new IllegalArgumentException("no market price margin specified");
- try {
- Double.valueOf(priceMarginAsString);
- } catch (NumberFormatException ex) {
- throw new IllegalArgumentException(format("%s is not a number", priceMarginAsString));
- }
+ verifyStringIsValidDouble(priceMarginAsString);
boolean mktPriceMarginOptIsOnlyOpt = !options.has(triggerPriceOpt)
&& !options.has(fixedPriceOpt)
@@ -167,11 +163,7 @@ public EditOfferOptionParser parse() {
if (triggerPriceAsString.isEmpty())
throw new IllegalArgumentException("trigger price not specified");
- try {
- Double.valueOf(triggerPriceAsString);
- } catch (NumberFormatException ex) {
- throw new IllegalArgumentException(format("%s is not a number", triggerPriceAsString));
- }
+ verifyStringIsValidDouble(triggerPriceAsString);
boolean triggerPriceOptIsOnlyOpt = !options.has(mktPriceMarginOpt)
&& !options.has(fixedPriceOpt)
@@ -214,11 +206,22 @@ public String getOfferId() {
}
public String getFixedPrice() {
- return options.has(fixedPriceOpt) ? options.valueOf(fixedPriceOpt) : "0";
+ if (offerEditType.equals(FIXED_PRICE_ONLY) || offerEditType.equals(FIXED_PRICE_AND_ACTIVATION_STATE)) {
+ return options.has(fixedPriceOpt) ? options.valueOf(fixedPriceOpt) : "0";
+ } else {
+ return "0";
+ }
}
public String getTriggerPrice() {
- return options.has(triggerPriceOpt) ? options.valueOf(triggerPriceOpt) : "0";
+ if (offerEditType.equals(TRIGGER_PRICE_ONLY)
+ || offerEditType.equals(TRIGGER_PRICE_AND_ACTIVATION_STATE)
+ || offerEditType.equals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE)
+ || offerEditType.equals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE)) {
+ return options.has(triggerPriceOpt) ? options.valueOf(triggerPriceOpt) : "0";
+ } else {
+ return "0";
+ }
}
public BigDecimal getTriggerPriceAsBigDecimal() {
@@ -226,17 +229,23 @@ public BigDecimal getTriggerPriceAsBigDecimal() {
}
public String getMktPriceMargin() {
- return isUsingMktPriceMargin() ? options.valueOf(mktPriceMarginOpt) : "0.00";
+ if (offerEditType.equals(MKT_PRICE_MARGIN_ONLY)
+ || offerEditType.equals(MKT_PRICE_MARGIN_AND_ACTIVATION_STATE)
+ || offerEditType.equals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE)
+ || offerEditType.equals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE)) {
+ return isUsingMktPriceMargin() ? options.valueOf(mktPriceMarginOpt) : "0.00";
+ } else {
+ return "0.00";
+ }
}
public BigDecimal getMktPriceMarginAsBigDecimal() {
- return isUsingMktPriceMargin()
- ? new BigDecimal(options.valueOf(mktPriceMarginOpt))
- : BigDecimal.ZERO;
+ return new BigDecimal(options.valueOf(mktPriceMarginOpt));
}
public boolean isUsingMktPriceMargin() {
- return options.has(mktPriceMarginOpt);
+ return !offerEditType.equals(FIXED_PRICE_ONLY)
+ && !offerEditType.equals(FIXED_PRICE_AND_ACTIVATION_STATE);
}
public int getEnableAsSignedInt() {
@@ -247,8 +256,8 @@ public int getEnableAsSignedInt() {
@Nullable
Boolean input = isEnable();
return input == null
- ? -1
- : input ? 1 : 0;
+ ? OPT_ENABLE_IGNORED
+ : input ? OPT_ENABLE_ON : OPT_ENABLE_OFF;
}
@Nullable
@@ -261,4 +270,12 @@ public Boolean isEnable() {
public EditOfferRequest.EditType getOfferEditType() {
return offerEditType;
}
+
+ private void verifyStringIsValidDouble(String string) {
+ try {
+ Double.valueOf(string);
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException(format("%s is not a number", string));
+ }
+ }
}
diff --git a/cli/src/test/java/bisq/cli/opt/EditOfferOptionParserTest.java b/cli/src/test/java/bisq/cli/opts/EditOfferOptionParserTest.java
similarity index 87%
rename from cli/src/test/java/bisq/cli/opt/EditOfferOptionParserTest.java
rename to cli/src/test/java/bisq/cli/opts/EditOfferOptionParserTest.java
index 3305b0cb2cf..ccabec4ea88 100644
--- a/cli/src/test/java/bisq/cli/opt/EditOfferOptionParserTest.java
+++ b/cli/src/test/java/bisq/cli/opts/EditOfferOptionParserTest.java
@@ -1,19 +1,19 @@
-package bisq.cli.opt;
+package bisq.cli.opts;
import org.junit.jupiter.api.Test;
import static bisq.cli.Method.editoffer;
+import static bisq.cli.opts.EditOfferOptionParser.OPT_ENABLE_IGNORED;
+import static bisq.cli.opts.EditOfferOptionParser.OPT_ENABLE_OFF;
+import static bisq.cli.opts.EditOfferOptionParser.OPT_ENABLE_ON;
import static bisq.cli.opts.OptLabel.*;
import static bisq.proto.grpc.EditOfferRequest.EditType.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-
-
-import bisq.cli.opts.EditOfferOptionParser;
-
-// This opt parser test has the most thorough coverage,
+// This opt parser test ahs the most thorough coverage,
// and is a reference for other opt parser tests.
public class EditOfferOptionParserTest {
@@ -124,7 +124,7 @@ public void testEditOfferActivationStateOnly() {
};
EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
assertEquals(ACTIVATION_STATE_ONLY, parser.getOfferEditType());
- assertEquals(1, parser.getEnableAsSignedInt());
+ assertEquals(OPT_ENABLE_ON, parser.getEnableAsSignedInt());
}
@Test
@@ -167,6 +167,9 @@ public void testEditOfferFixedPriceOnly() {
EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
assertEquals(FIXED_PRICE_ONLY, parser.getOfferEditType());
assertEquals(fixedPriceAsString, parser.getFixedPrice());
+ assertFalse(parser.isUsingMktPriceMargin());
+ assertEquals("0.00", parser.getMktPriceMargin());
+ assertEquals(OPT_ENABLE_IGNORED, parser.getEnableAsSignedInt());
}
@Test
@@ -182,7 +185,9 @@ public void testEditOfferFixedPriceAndActivationStateOnly() {
EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
assertEquals(FIXED_PRICE_AND_ACTIVATION_STATE, parser.getOfferEditType());
assertEquals(fixedPriceAsString, parser.getFixedPrice());
- assertEquals(0, parser.getEnableAsSignedInt());
+ assertFalse(parser.isUsingMktPriceMargin());
+ assertEquals("0.00", parser.getMktPriceMargin());
+ assertEquals(OPT_ENABLE_OFF, parser.getEnableAsSignedInt());
}
@Test
@@ -196,7 +201,10 @@ public void testEditOfferMktPriceMarginOnly() {
};
EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
assertEquals(MKT_PRICE_MARGIN_ONLY, parser.getOfferEditType());
+ assertTrue(parser.isUsingMktPriceMargin());
assertEquals(mktPriceMarginAsString, parser.getMktPriceMargin());
+ assertEquals("0", parser.getTriggerPrice());
+ assertEquals(OPT_ENABLE_IGNORED, parser.getEnableAsSignedInt());
}
@Test
@@ -225,8 +233,10 @@ public void testEditOfferMktPriceMarginAndActivationStateOnly() {
};
EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
assertEquals(MKT_PRICE_MARGIN_AND_ACTIVATION_STATE, parser.getOfferEditType());
+ assertTrue(parser.isUsingMktPriceMargin());
assertEquals(mktPriceMarginAsString, parser.getMktPriceMargin());
- assertEquals(0, parser.getEnableAsSignedInt());
+ assertEquals("0", parser.getTriggerPrice());
+ assertEquals(OPT_ENABLE_OFF, parser.getEnableAsSignedInt());
}
@Test
@@ -241,6 +251,9 @@ public void testEditTriggerPriceOnly() {
EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
assertEquals(TRIGGER_PRICE_ONLY, parser.getOfferEditType());
assertEquals(triggerPriceAsString, parser.getTriggerPrice());
+ assertTrue(parser.isUsingMktPriceMargin());
+ assertEquals("0.00", parser.getMktPriceMargin());
+ assertEquals(OPT_ENABLE_IGNORED, parser.getEnableAsSignedInt());
}
@Test
@@ -284,7 +297,10 @@ public void testEditTriggerPriceAndActivationStateOnly() {
EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
assertEquals(TRIGGER_PRICE_AND_ACTIVATION_STATE, parser.getOfferEditType());
assertEquals(triggerPriceAsString, parser.getTriggerPrice());
- assertEquals(1, parser.getEnableAsSignedInt());
+ assertTrue(parser.isUsingMktPriceMargin());
+ assertEquals("0.00", parser.getMktPriceMargin());
+ assertEquals("0", parser.getFixedPrice());
+ assertEquals(OPT_ENABLE_ON, parser.getEnableAsSignedInt());
}
@Test
@@ -300,8 +316,11 @@ public void testEditMKtPriceMarginAndTriggerPrice() {
};
EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
assertEquals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE, parser.getOfferEditType());
- assertEquals(mktPriceMarginAsString, parser.getMktPriceMargin());
assertEquals(triggerPriceAsString, parser.getTriggerPrice());
+ assertTrue(parser.isUsingMktPriceMargin());
+ assertEquals(mktPriceMarginAsString, parser.getMktPriceMargin());
+ assertEquals("0", parser.getFixedPrice());
+ assertEquals(OPT_ENABLE_IGNORED, parser.getEnableAsSignedInt());
}
@Test
@@ -318,8 +337,10 @@ public void testEditMKtPriceMarginAndTriggerPriceAndEnableState() {
};
EditOfferOptionParser parser = new EditOfferOptionParser(args).parse();
assertEquals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE, parser.getOfferEditType());
- assertEquals(mktPriceMarginAsString, parser.getMktPriceMargin());
assertEquals(triggerPriceAsString, parser.getTriggerPrice());
- assertFalse(parser.isEnable());
+ assertTrue(parser.isUsingMktPriceMargin());
+ assertEquals(mktPriceMarginAsString, parser.getMktPriceMargin());
+ assertEquals("0", parser.getFixedPrice());
+ assertEquals(OPT_ENABLE_OFF, parser.getEnableAsSignedInt());
}
}
diff --git a/cli/src/test/java/bisq/cli/opt/OptionParsersTest.java b/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java
similarity index 97%
rename from cli/src/test/java/bisq/cli/opt/OptionParsersTest.java
rename to cli/src/test/java/bisq/cli/opts/OptionParsersTest.java
index 1df62cf2aa8..58b8712fe90 100644
--- a/cli/src/test/java/bisq/cli/opt/OptionParsersTest.java
+++ b/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java
@@ -1,4 +1,4 @@
-package bisq.cli.opt;
+package bisq.cli.opts;
import org.junit.jupiter.api.Test;
@@ -11,13 +11,6 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
-
-import bisq.cli.opts.CancelOfferOptionParser;
-import bisq.cli.opts.CreateCryptoCurrencyPaymentAcctOptionParser;
-import bisq.cli.opts.CreateOfferOptionParser;
-import bisq.cli.opts.CreatePaymentAcctOptionParser;
-
-
public class OptionParsersTest {
private static final String PASSWORD_OPT = "--" + OPT_PASSWORD + "=" + "xyz";
From 063b52eb701ef312d55075e95817f8956a711402 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Tue, 15 Jun 2021 11:39:06 -0300
Subject: [PATCH 19/40] Add editoffer test case, suppress annoying warnings
---
.../apitest/method/offer/EditOfferTest.java | 51 +++++++++++++++----
.../java/bisq/apitest/scenario/OfferTest.java | 1 +
.../cli/request/OffersServiceRequest.java | 3 ++
3 files changed, 46 insertions(+), 9 deletions(-)
diff --git a/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java
index d7af5694af9..a7c661708ae 100644
--- a/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java
@@ -39,10 +39,7 @@
import static bisq.apitest.config.ApiTestConfig.BSQ;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
-import static bisq.proto.grpc.EditOfferRequest.EditType.FIXED_PRICE_AND_ACTIVATION_STATE;
-import static bisq.proto.grpc.EditOfferRequest.EditType.MKT_PRICE_MARGIN_AND_TRIGGER_PRICE;
-import static bisq.proto.grpc.EditOfferRequest.EditType.MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE;
-import static bisq.proto.grpc.EditOfferRequest.EditType.MKT_PRICE_MARGIN_ONLY;
+import static bisq.proto.grpc.EditOfferRequest.EditType.*;
import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -51,6 +48,7 @@
import static protobuf.OfferPayload.Direction.BUY;
import static protobuf.OfferPayload.Direction.SELL;
+@SuppressWarnings("ALL")
@Disabled
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@@ -175,6 +173,7 @@ public void testEditFixedPrice() {
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
var expectedNewFixedPrice = scaledUpFiatPrice.apply(new BigDecimal(editedFixedPriceAsString));
assertEquals(expectedNewFixedPrice, editedOffer.getPrice());
+ assertFalse(editedOffer.getUseMarketBasedPrice());
doSanityCheck(originalOffer, editedOffer);
}
@@ -211,6 +210,40 @@ public void testEditFixedPriceAndDeactivation() {
@Test
@Order(7)
+ public void testEditMktPriceMarginAndDeactivation() {
+ PaymentAccount paymentAcct = getOrCreatePaymentAccount("US");
+
+ var originalMktPriceMargin = new BigDecimal("0.0").doubleValue();
+ OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
+ DOLLAR,
+ paymentAcct.getId(),
+ originalMktPriceMargin,
+ 0);
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ originalOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin());
+
+ // Edit the offer's price margin and trigger price, and deactivate it.
+ var newMktPriceMargin = new BigDecimal("1.50").doubleValue();
+ aliceClient.editOffer(originalOffer.getId(),
+ "0.00",
+ originalOffer.getUseMarketBasedPrice(),
+ newMktPriceMargin,
+ 0,
+ DEACTIVATE_OFFER,
+ MKT_PRICE_MARGIN_AND_ACTIVATION_STATE);
+ // Wait for edited offer to be removed from offer-book, edited, and re-published.
+ genBtcBlocksThenWait(1, 2500);
+ OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
+ assertEquals(0, editedOffer.getTriggerPrice());
+ assertFalse(editedOffer.getIsActivated());
+
+ doSanityCheck(originalOffer, editedOffer);
+ }
+
+ @Test
+ @Order(8)
public void testEditMktPriceMarginAndTriggerPriceAndDeactivation() {
PaymentAccount paymentAcct = getOrCreatePaymentAccount("US");
@@ -249,7 +282,7 @@ public void testEditMktPriceMarginAndTriggerPriceAndDeactivation() {
}
@Test
- @Order(8)
+ @Order(9)
public void testEditingFixedPriceInMktPriceMarginBasedOfferShouldThrowException() {
PaymentAccount paymentAcct = getOrCreatePaymentAccount("US");
var originalMktPriceMargin = new BigDecimal("0.0").doubleValue();
@@ -279,7 +312,7 @@ public void testEditingFixedPriceInMktPriceMarginBasedOfferShouldThrowException(
}
@Test
- @Order(9)
+ @Order(10)
public void testEditingTriggerPriceInFixedPriceOfferShouldThrowException() {
PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU");
double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE);
@@ -301,7 +334,7 @@ public void testEditingTriggerPriceInFixedPriceOfferShouldThrowException() {
}
@Test
- @Order(10)
+ @Order(11)
public void testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice() {
PaymentAccount paymentAcct = getOrCreatePaymentAccount("MX");
double mktPriceAsDouble = aliceClient.getBtcPrice("MXN");
@@ -335,7 +368,7 @@ public void testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice() {
}
@Test
- @Order(11)
+ @Order(12)
public void testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt() {
PaymentAccount paymentAcct = getOrCreatePaymentAccount("GB");
double mktPriceAsDouble = aliceClient.getBtcPrice("GBP");
@@ -360,10 +393,10 @@ public void testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt() {
// Wait for edited offer to be removed from offer-book, edited, and re-published.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ assertEquals(scaledUpFiatPrice.apply(new BigDecimal(fixedPriceAsString)), editedOffer.getPrice());
assertFalse(editedOffer.getUseMarketBasedPrice());
assertEquals(0.00, editedOffer.getMarketPriceMargin());
assertEquals(0, editedOffer.getTriggerPrice());
- assertEquals(scaledUpFiatPrice.apply(new BigDecimal(fixedPriceAsString)), editedOffer.getPrice());
assertFalse(editedOffer.getIsActivated());
}
diff --git a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
index 292df14a511..0c23c750ec6 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
@@ -98,6 +98,7 @@ public void testEditOffer() {
test.testEditMktPriceMargin();
test.testEditFixedPrice();
test.testEditFixedPriceAndDeactivation();
+ test.testEditMktPriceMarginAndDeactivation();
test.testEditMktPriceMarginAndTriggerPriceAndDeactivation();
test.testEditingFixedPriceInMktPriceMarginBasedOfferShouldThrowException();
test.testEditingTriggerPriceInFixedPriceOfferShouldThrowException();
diff --git a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java
index b340f25ea7a..c3cff777b1b 100644
--- a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java
+++ b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java
@@ -126,6 +126,7 @@ public OfferInfo createOffer(String direction,
// TODO Make sure this is not duplicated anywhere on CLI side.
private final Function scaledPriceStringFormat = (price) -> {
BigDecimal factor = new BigDecimal(10).pow(4);
+ //noinspection BigDecimalMethodWithoutRoundingCalled
return new BigDecimal(price).divide(factor).toPlainString();
};
@@ -195,6 +196,7 @@ public void editOffer(String offerId,
.setEnable(enable)
.setEditType(editType)
.build();
+ //noinspection ResultOfMethodCallIgnored
grpcStubs.offersService.editOffer(request);
}
@@ -202,6 +204,7 @@ public void cancelOffer(String offerId) {
var request = CancelOfferRequest.newBuilder()
.setId(offerId)
.build();
+ //noinspection ResultOfMethodCallIgnored
grpcStubs.offersService.cancelOffer(request);
}
From e5b5a06b9bc05e444532fa4903ed5f15a149d1a7 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Tue, 15 Jun 2021 12:04:39 -0300
Subject: [PATCH 20/40] Remove unused field
---
core/src/main/java/bisq/core/api/EditOfferValidator.java | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/core/src/main/java/bisq/core/api/EditOfferValidator.java b/core/src/main/java/bisq/core/api/EditOfferValidator.java
index 82f6b78b737..722c644ef95 100644
--- a/core/src/main/java/bisq/core/api/EditOfferValidator.java
+++ b/core/src/main/java/bisq/core/api/EditOfferValidator.java
@@ -22,7 +22,6 @@ class EditOfferValidator {
private final EditOfferRequest.EditType editType;
private final boolean isZeroEditedFixedPriceString;
- private final boolean isZeroEditedMarketPriceMargin;
private final boolean isZeroEditedTriggerPrice;
EditOfferValidator(OpenOffer currentlyOpenOffer,
@@ -41,7 +40,6 @@ class EditOfferValidator {
this.editType = editType;
this.isZeroEditedFixedPriceString = new BigDecimal(editedPriceAsString).doubleValue() == 0;
- this.isZeroEditedMarketPriceMargin = editedMarketPriceMargin == 0;
this.isZeroEditedTriggerPrice = editedTriggerPrice == 0;
}
@@ -76,7 +74,7 @@ private void validateEditedActivationState() {
if (editedEnable < 0)
throw new IllegalStateException(
format("programmer error: the 'enable' request parameter does not"
- + " indicate activation state of offer with id '{}' should be changed.",
+ + " indicate activation state of offer with id '%s' should be changed.",
currentlyOpenOffer.getId()));
}
From bc1576efbca5ab814b710d9d7df8bc95e8eff52d Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Tue, 15 Jun 2021 13:13:52 -0300
Subject: [PATCH 21/40] Throw exception is edit altcoin offer is attempted
Support for editing BSQ offers is in place, but will be added
in another PR.
---
.../apitest/method/offer/EditOfferTest.java | 20 +++++++++++++++++++
.../java/bisq/apitest/scenario/OfferTest.java | 2 ++
.../java/bisq/core/api/CoreOffersService.java | 9 ++++++++-
3 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java
index a7c661708ae..6cfa0241ca6 100644
--- a/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java
@@ -400,6 +400,26 @@ public void testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt() {
assertFalse(editedOffer.getIsActivated());
}
+ @Test
+ @Order(13)
+ public void testEditBsqOfferShouldThrowException() {
+ createBsqPaymentAccounts();
+ var newOffer = aliceClient.createFixedPricedOffer(BUY.name(),
+ BSQ,
+ 100_000_000L,
+ 100_000_000L,
+ "0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
+ getDefaultBuyerSecurityDepositAsPercent(),
+ alicesBsqAcct.getId(),
+ BSQ);
+ // TODO Allow editing BSQ offer fixed-price, enable/disable.
+ Throwable exception = assertThrows(StatusRuntimeException.class, () ->
+ aliceClient.editOfferActivationState(newOffer.getId(), DEACTIVATE_OFFER));
+ String expectedExceptionMessage = format("UNKNOWN: editing altcoin offer not supported");
+ assertEquals(expectedExceptionMessage, exception.getMessage());
+ }
+
+
private OfferInfo createMktPricedOfferForEdit(String direction,
String currencyCode,
String paymentAccountId,
diff --git a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
index 0c23c750ec6..e37c56a8302 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
@@ -104,5 +104,7 @@ public void testEditOffer() {
test.testEditingTriggerPriceInFixedPriceOfferShouldThrowException();
test.testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice();
test.testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt();
+ // TODO Allow editing BSQ offer fixed-price, enable/disable.
+ test.testEditBsqOfferShouldThrowException();
}
}
diff --git a/core/src/main/java/bisq/core/api/CoreOffersService.java b/core/src/main/java/bisq/core/api/CoreOffersService.java
index adcbc53f8c8..2a1aa09c721 100644
--- a/core/src/main/java/bisq/core/api/CoreOffersService.java
+++ b/core/src/main/java/bisq/core/api/CoreOffersService.java
@@ -17,6 +17,7 @@
package bisq.core.api;
+import bisq.core.locale.CurrencyUtil;
import bisq.core.monetary.Altcoin;
import bisq.core.monetary.Price;
import bisq.core.offer.CreateOfferService;
@@ -45,6 +46,7 @@
import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -215,6 +217,11 @@ void editOffer(String offerId,
int editedEnable,
EditType editType) {
OpenOffer openOffer = getMyOpenOffer(offerId);
+
+ boolean isCryptoCurrency = CurrencyUtil.isCryptoCurrency(openOffer.getOffer().getCurrencyCode());
+ if (isCryptoCurrency)
+ throw new IllegalStateException("editing altcoin offer not supported");
+
new EditOfferValidator(openOffer,
editedPriceAsString,
editedUseMarketBasedPrice,
@@ -310,7 +317,7 @@ private OfferPayload getMergedOfferPayload(OpenOffer openOffer,
|| editType.equals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE)
|| editType.equals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE);
MutableOfferPayloadFields mutableOfferPayloadFields = new MutableOfferPayloadFields(
- editedPrice.getValue(),
+ Objects.requireNonNull(editedPrice).getValue(),
isUsingMktPriceMargin ? exactMultiply(editedMarketPriceMargin, 0.01) : 0.00,
isUsingMktPriceMargin,
offer.getOfferPayload().getBaseCurrencyCode(),
From a3ea4ecbf6c75a2cfae92910a8c05d29ace224c0 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Tue, 15 Jun 2021 13:22:07 -0300
Subject: [PATCH 22/40] Avoid duplicate test run
---
.../method/offer/CreateOfferUsingMarketPriceMarginTest.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java
index df1f9079fb1..a34f144403e 100644
--- a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java
@@ -31,6 +31,7 @@
import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
@@ -53,7 +54,7 @@
import static protobuf.OfferPayload.Direction.BUY;
import static protobuf.OfferPayload.Direction.SELL;
-// @Disabled
+@Disabled
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
From 3d38a8555f4587da4afc629306b261afa23c81d3 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Tue, 15 Jun 2021 13:40:33 -0300
Subject: [PATCH 23/40] Make codacy just a bit happier
---
core/src/main/java/bisq/core/api/CoreOffersService.java | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/core/src/main/java/bisq/core/api/CoreOffersService.java b/core/src/main/java/bisq/core/api/CoreOffersService.java
index 2a1aa09c721..234bc51b854 100644
--- a/core/src/main/java/bisq/core/api/CoreOffersService.java
+++ b/core/src/main/java/bisq/core/api/CoreOffersService.java
@@ -17,7 +17,6 @@
package bisq.core.api;
-import bisq.core.locale.CurrencyUtil;
import bisq.core.monetary.Altcoin;
import bisq.core.monetary.Price;
import bisq.core.offer.CreateOfferService;
@@ -218,7 +217,7 @@ void editOffer(String offerId,
EditType editType) {
OpenOffer openOffer = getMyOpenOffer(offerId);
- boolean isCryptoCurrency = CurrencyUtil.isCryptoCurrency(openOffer.getOffer().getCurrencyCode());
+ boolean isCryptoCurrency = isCryptoCurrency(openOffer.getOffer().getCurrencyCode());
if (isCryptoCurrency)
throw new IllegalStateException("editing altcoin offer not supported");
From 1a56a5161adabe4209058aad55f7c1531a53b1aa Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Thu, 17 Jun 2021 10:13:18 -0300
Subject: [PATCH 24/40] Force codacy check after codacy config change
---
core/src/main/java/bisq/core/api/CoreOffersService.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/src/main/java/bisq/core/api/CoreOffersService.java b/core/src/main/java/bisq/core/api/CoreOffersService.java
index 234bc51b854..42eeb036f40 100644
--- a/core/src/main/java/bisq/core/api/CoreOffersService.java
+++ b/core/src/main/java/bisq/core/api/CoreOffersService.java
@@ -228,7 +228,7 @@ void editOffer(String offerId,
editedTriggerPrice,
editedEnable,
editType).validate();
- log.info("'editoffer' params OK for offerId={}"
+ log.info("Validated 'editoffer' params offerId={}"
+ "\n\teditedPriceAsString={}"
+ "\n\teditedUseMarketBasedPrice={}"
+ "\n\teditedMarketPriceMargin={}"
From 0e9c6650e3aca09947a91f6bb5222e638c7c1b90 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Fri, 18 Jun 2021 17:40:21 -0300
Subject: [PATCH 25/40] Include isMyOffer flag in API's trade/offer proto
wrappers
Optionally displaying an ENABLED column in CLI side getoffer output
depends on the value of offer.isMyOffer, which is passed via new
boolean arguments to the trade & offer pojo builders.
---
.../src/main/java/bisq/core/api/model/OfferInfo.java | 12 +++++++++---
.../src/main/java/bisq/core/api/model/TradeInfo.java | 10 ++++++----
2 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/core/src/main/java/bisq/core/api/model/OfferInfo.java b/core/src/main/java/bisq/core/api/model/OfferInfo.java
index ce645e6ab69..f99df264569 100644
--- a/core/src/main/java/bisq/core/api/model/OfferInfo.java
+++ b/core/src/main/java/bisq/core/api/model/OfferInfo.java
@@ -63,7 +63,7 @@ public class OfferInfo implements Payload {
private final long date;
private final String state;
private final boolean isActivated;
- private final boolean isMyOffer;
+ private boolean isMyOffer;
public OfferInfo(OfferInfoBuilder builder) {
this.id = builder.id;
@@ -93,13 +93,19 @@ public OfferInfo(OfferInfoBuilder builder) {
this.isMyOffer = builder.isMyOffer;
}
+ // Allow isMyOffer to be set on new offers' OfferInfo instances.
+ public void setIsMyOffer(boolean myOffer) {
+ isMyOffer = myOffer;
+ }
+
public static OfferInfo toOfferInfo(Offer offer) {
- // Offer is not mine.
+ // Assume the offer is not mine, but isMyOffer can be reset to true, i.e., when
+ // calling TradeInfo toTradeInfo(Trade trade, String role, boolean isMyOffer);
return getOfferInfoBuilder(offer, false).build();
}
public static OfferInfo toOfferInfo(OpenOffer openOffer) {
- // OpenOffer is mine.
+ // An OpenOffer is always my offer.
return getOfferInfoBuilder(openOffer.getOffer(), true)
.withTriggerPrice(openOffer.getTriggerPrice())
.withIsActivated(!openOffer.isDeactivated())
diff --git a/core/src/main/java/bisq/core/api/model/TradeInfo.java b/core/src/main/java/bisq/core/api/model/TradeInfo.java
index 078e5ee4d9c..5779baf348e 100644
--- a/core/src/main/java/bisq/core/api/model/TradeInfo.java
+++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java
@@ -92,11 +92,11 @@ public TradeInfo(TradeInfoBuilder builder) {
this.contract = builder.contract;
}
- public static TradeInfo toTradeInfo(Trade trade) {
- return toTradeInfo(trade, null);
+ public static TradeInfo toNewTradeInfo(Trade trade) {
+ return toTradeInfo(trade, null, false);
}
- public static TradeInfo toTradeInfo(Trade trade, String role) {
+ public static TradeInfo toTradeInfo(Trade trade, String role, boolean isMyOffer) {
ContractInfo contractInfo;
if (trade.getContract() != null) {
Contract contract = trade.getContract();
@@ -116,8 +116,10 @@ public static TradeInfo toTradeInfo(Trade trade, String role) {
contractInfo = ContractInfo.emptyContract.get();
}
+ OfferInfo offerInfo = toOfferInfo(trade.getOffer());
+ offerInfo.setIsMyOffer(isMyOffer);
return new TradeInfoBuilder()
- .withOffer(toOfferInfo(trade.getOffer()))
+ .withOffer(offerInfo)
.withTradeId(trade.getId())
.withShortId(trade.getShortId())
.withDate(trade.getDate().getTime())
From a603044f2eda650309646f3f0ab5e61be20945aa Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Fri, 18 Jun 2021 17:41:45 -0300
Subject: [PATCH 26/40] Pass isMyOffer flag to trade/offer proto wrappers from
core services
---
core/src/main/java/bisq/core/api/CoreApi.java | 4 ++++
.../main/java/bisq/core/api/CoreOffersService.java | 11 ++++++-----
.../main/java/bisq/daemon/grpc/GrpcOffersService.java | 1 +
.../main/java/bisq/daemon/grpc/GrpcTradesService.java | 6 ++++--
4 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java
index 1c223df2b7f..2fb5c39c5a0 100644
--- a/core/src/main/java/bisq/core/api/CoreApi.java
+++ b/core/src/main/java/bisq/core/api/CoreApi.java
@@ -179,6 +179,10 @@ public void cancelOffer(String id) {
coreOffersService.cancelOffer(id);
}
+ public boolean isMyOffer(String id) {
+ return coreOffersService.isMyOffer(id);
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////
// PaymentAccounts
///////////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/src/main/java/bisq/core/api/CoreOffersService.java b/core/src/main/java/bisq/core/api/CoreOffersService.java
index 42eeb036f40..04ce9c2ceb3 100644
--- a/core/src/main/java/bisq/core/api/CoreOffersService.java
+++ b/core/src/main/java/bisq/core/api/CoreOffersService.java
@@ -155,6 +155,12 @@ OpenOffer getMyOpenOffer(String id) {
new IllegalStateException(format("offer with id '%s' not found", id)));
}
+ boolean isMyOffer(String id) {
+ return openOfferManager.getOpenOfferById(id)
+ .filter(open -> open.getOffer().isMyOffer(keyRing))
+ .isPresent();
+ }
+
// Create and place new offer.
void createAndPlaceOffer(String currencyCode,
String directionAsString,
@@ -216,11 +222,6 @@ void editOffer(String offerId,
int editedEnable,
EditType editType) {
OpenOffer openOffer = getMyOpenOffer(offerId);
-
- boolean isCryptoCurrency = isCryptoCurrency(openOffer.getOffer().getCurrencyCode());
- if (isCryptoCurrency)
- throw new IllegalStateException("editing altcoin offer not supported");
-
new EditOfferValidator(openOffer,
editedPriceAsString,
editedUseMarketBasedPrice,
diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java
index 2917bae6289..b1f9212d46b 100644
--- a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java
+++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java
@@ -161,6 +161,7 @@ public void createOffer(CreateOfferRequest req,
// This result handling consumer's accept operation will return
// the new offer to the gRPC client after async placement is done.
OfferInfo offerInfo = toOfferInfo(offer);
+ offerInfo.setIsMyOffer(true);
CreateOfferReply reply = CreateOfferReply.newBuilder()
.setOffer(offerInfo.toProtoMessage())
.build();
diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java
index 8cfc74f7463..7ffea95e6a5 100644
--- a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java
+++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java
@@ -44,6 +44,7 @@
import lombok.extern.slf4j.Slf4j;
+import static bisq.core.api.model.TradeInfo.toNewTradeInfo;
import static bisq.core.api.model.TradeInfo.toTradeInfo;
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
import static bisq.proto.grpc.TradesGrpc.*;
@@ -72,9 +73,10 @@ public void getTrade(GetTradeRequest req,
StreamObserver responseObserver) {
try {
Trade trade = coreApi.getTrade(req.getTradeId());
+ boolean isMyOffer = coreApi.isMyOffer(trade.getOffer().getId());
String role = coreApi.getTradeRole(req.getTradeId());
var reply = GetTradeReply.newBuilder()
- .setTrade(toTradeInfo(trade, role).toProtoMessage())
+ .setTrade(toTradeInfo(trade, role, isMyOffer).toProtoMessage())
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
@@ -99,7 +101,7 @@ public void takeOffer(TakeOfferRequest req,
req.getPaymentAccountId(),
req.getTakerFeeCurrencyCode(),
trade -> {
- TradeInfo tradeInfo = toTradeInfo(trade);
+ TradeInfo tradeInfo = toNewTradeInfo(trade);
var reply = TakeOfferReply.newBuilder()
.setTrade(tradeInfo.toProtoMessage())
.build();
From e32e0d1fbbcaa1bdc2ef4cee498541604ee3434a Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Fri, 18 Jun 2021 17:46:16 -0300
Subject: [PATCH 27/40] Add altcoin (bsq) offer editing validation check
BSQ offers are fixed-price only. This change blocks an
attempt to change an altcoin offer to a margin price based
offer, or set a trigger price.
---
.../main/java/bisq/core/api/EditOfferValidator.java | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/core/src/main/java/bisq/core/api/EditOfferValidator.java b/core/src/main/java/bisq/core/api/EditOfferValidator.java
index 722c644ef95..7a9840c3cdb 100644
--- a/core/src/main/java/bisq/core/api/EditOfferValidator.java
+++ b/core/src/main/java/bisq/core/api/EditOfferValidator.java
@@ -8,6 +8,7 @@
import lombok.extern.slf4j.Slf4j;
+import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
import static java.lang.String.format;
@Slf4j
@@ -61,6 +62,7 @@ void validate() {
case TRIGGER_PRICE_AND_ACTIVATION_STATE:
case MKT_PRICE_MARGIN_AND_TRIGGER_PRICE:
case MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE: {
+ checkNotAltcoinOffer();
validateEditedTriggerPrice();
validateEditedMarketPriceMargin();
break;
@@ -128,4 +130,12 @@ private void validateEditedTriggerPrice() {
+ " in offer with id '%s'",
currentlyOpenOffer.getId()));
}
+
+ private void checkNotAltcoinOffer() {
+ if (isCryptoCurrency(currentlyOpenOffer.getOffer().getCurrencyCode())) {
+ throw new IllegalStateException(
+ format("cannot set mkt price margin or trigger price on fixed price altcoin offer with id '%s'",
+ currentlyOpenOffer.getId()));
+ }
+ }
}
From b74f084893f1893fd7ccf7d4200ae19812f140f5 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Fri, 18 Jun 2021 17:50:28 -0300
Subject: [PATCH 28/40] Optionally show ENABLED column in CLI's getoffer(bsq)
output
---
cli/src/main/java/bisq/cli/TableFormat.java | 76 +++++++++++++++------
1 file changed, 55 insertions(+), 21 deletions(-)
diff --git a/cli/src/main/java/bisq/cli/TableFormat.java b/cli/src/main/java/bisq/cli/TableFormat.java
index d1d4aa729ac..3d14b5991d4 100644
--- a/cli/src/main/java/bisq/cli/TableFormat.java
+++ b/cli/src/main/java/bisq/cli/TableFormat.java
@@ -153,7 +153,7 @@ public static String formatOfferTable(List offers, String currencyCod
boolean isMyOffer = offers.get(0).getIsMyOffer();
return baseCurrencyCode.equalsIgnoreCase("BTC")
? formatFiatOfferTable(offers, currencyCode, isMyOffer)
- : formatCryptoCurrencyOfferTable(offers, baseCurrencyCode);
+ : formatCryptoCurrencyOfferTable(offers, baseCurrencyCode, isMyOffer);
}
private static String formatFiatOfferTable(List offers,
@@ -252,14 +252,21 @@ private static String getFiatOfferColDataFormat(boolean isMyOffer,
}
}
- private static String formatCryptoCurrencyOfferTable(List offers, String cryptoCurrencyCode) {
+ private static String formatCryptoCurrencyOfferTable(List offers,
+ String cryptoCurrencyCode,
+ boolean isMyOffer) {
// Some column values might be longer than header, so we need to calculate them.
int directionColWidth = getLongestDirectionColWidth(offers);
int amountColWith = getLongestAmountColWidth(offers);
int volumeColWidth = getLongestCryptoCurrencyVolumeColWidth(offers);
int paymentMethodColWidth = getLongestPaymentMethodColWidth(offers);
+ // "Enabled" column is displayed for my offers only.
+ String enabledHeaderFormat = isMyOffer ?
+ COL_HEADER_ENABLED + COL_HEADER_DELIMITER
+ : "";
// TODO use memoize function to avoid duplicate the formatting done above?
- String headersFormat = padEnd(COL_HEADER_DIRECTION, directionColWidth, ' ') + COL_HEADER_DELIMITER
+ String headersFormat = enabledHeaderFormat
+ + padEnd(COL_HEADER_DIRECTION, directionColWidth, ' ') + COL_HEADER_DELIMITER
+ COL_HEADER_PRICE_OF_ALTCOIN + COL_HEADER_DELIMITER // includes %s -> cryptoCurrencyCode
+ padStart(COL_HEADER_AMOUNT, amountColWith, ' ') + COL_HEADER_DELIMITER
// COL_HEADER_VOLUME includes %s -> cryptoCurrencyCode
@@ -270,24 +277,51 @@ private static String formatCryptoCurrencyOfferTable(List offers, Str
String headerLine = format(headersFormat,
cryptoCurrencyCode.toUpperCase(),
cryptoCurrencyCode.toUpperCase());
- String colDataFormat = "%-" + directionColWidth + "s"
- + "%" + (COL_HEADER_PRICE_OF_ALTCOIN.length() + 1) + "s"
- + " %" + amountColWith + "s"
- + " %" + (volumeColWidth - 1) + "s"
- + " %-" + paymentMethodColWidth + "s"
- + " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
- + " %-" + COL_HEADER_UUID.length() + "s";
- return headerLine
- + offers.stream()
- .map(o -> format(colDataFormat,
- directionFormat.apply(o),
- formatCryptoCurrencyPrice(o.getPrice()),
- formatAmountRange(o.getMinAmount(), o.getAmount()),
- formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()),
- o.getPaymentMethodShortName(),
- formatTimestamp(o.getDate()),
- o.getId()))
- .collect(Collectors.joining("\n"));
+ String colDataFormat;
+ if (isMyOffer) {
+ colDataFormat = "%-" + (COL_HEADER_ENABLED.length() + COL_HEADER_DELIMITER.length()) + "s"
+ + "%-" + directionColWidth + "s"
+ + "%" + (COL_HEADER_PRICE_OF_ALTCOIN.length() + 1) + "s"
+ + " %" + amountColWith + "s"
+ + " %" + (volumeColWidth - 1) + "s"
+ + " %-" + paymentMethodColWidth + "s"
+ + " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
+ + " %-" + COL_HEADER_UUID.length() + "s";
+ } else {
+ colDataFormat = "%-" + directionColWidth + "s"
+ + "%" + (COL_HEADER_PRICE_OF_ALTCOIN.length() + 1) + "s"
+ + " %" + amountColWith + "s"
+ + " %" + (volumeColWidth - 1) + "s"
+ + " %-" + paymentMethodColWidth + "s"
+ + " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
+ + " %-" + COL_HEADER_UUID.length() + "s";
+ }
+ if (isMyOffer) {
+ return headerLine
+ + offers.stream()
+ .map(o -> format(colDataFormat,
+ o.getIsActivated() ? "YES" : "NO",
+ directionFormat.apply(o),
+ formatCryptoCurrencyPrice(o.getPrice()),
+ formatAmountRange(o.getMinAmount(), o.getAmount()),
+ formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()),
+ o.getPaymentMethodShortName(),
+ formatTimestamp(o.getDate()),
+ o.getId()))
+ .collect(Collectors.joining("\n"));
+ } else {
+ return headerLine
+ + offers.stream()
+ .map(o -> format(colDataFormat,
+ directionFormat.apply(o),
+ formatCryptoCurrencyPrice(o.getPrice()),
+ formatAmountRange(o.getMinAmount(), o.getAmount()),
+ formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()),
+ o.getPaymentMethodShortName(),
+ formatTimestamp(o.getDate()),
+ o.getId()))
+ .collect(Collectors.joining("\n"));
+ }
}
private static int getLongestPaymentMethodColWidth(List offers) {
From 7880a84a00d8e3a9739e464ee5710699872a28b3 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Fri, 18 Jun 2021 17:52:03 -0300
Subject: [PATCH 29/40] Add BSQ offer editing tests to EditOfferTest
And log CLI's getoffer output to see getoffer formatting -- after adding
new ENABLED and TRIGGER-PRICE columns.
---
.../method/offer/AbstractOfferTest.java | 24 ++-
.../apitest/method/offer/EditOfferTest.java | 202 ++++++++++++++++--
.../LongRunningOfferDeactivationTest.java | 4 +-
.../java/bisq/apitest/scenario/OfferTest.java | 10 +-
4 files changed, 208 insertions(+), 32 deletions(-)
diff --git a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java
index 81903f8efcc..494b249af5d 100644
--- a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java
@@ -71,23 +71,31 @@ public static void setUp() {
protected final Function scaledDownMktPriceMargin = (mktPriceMargin) ->
exactMultiply(mktPriceMargin, 0.01);
- // Price value of offer returned from server is scaled up by 10^4.
- protected final Function scaledUpFiatPrice = (price) -> {
+ // Price value of fiat offer returned from server will be scaled up by 10^4.
+ protected final Function scaledUpFiatOfferPrice = (price) -> {
BigDecimal factor = new BigDecimal(10).pow(4);
return price.multiply(factor).longValue();
};
- protected final BiFunction calcTriggerPriceAsLong = (base, delta) -> {
- var triggerPriceAsDouble = new BigDecimal(base).add(new BigDecimal(delta)).doubleValue();
- return Double.valueOf(exactMultiply(triggerPriceAsDouble, 10_000)).longValue();
+ // Price value of altcoin offer returned from server will be scaled up by 10^8.
+ protected final Function scaledUpAltcoinOfferPrice = (altcoinPriceAsString) -> {
+ BigDecimal factor = new BigDecimal(10).pow(8);
+ BigDecimal priceAsBigDecimal = new BigDecimal(altcoinPriceAsString);
+ return priceAsBigDecimal.multiply(factor).longValue();
};
- protected final BiFunction calcFixedPriceAsString = (base, delta) -> {
- var fixedPriceAsBigDecimal = new BigDecimal(Double.toString(base))
+ protected final BiFunction calcPriceAsLong = (base, delta) -> {
+ var priceAsDouble = new BigDecimal(base).add(new BigDecimal(delta)).doubleValue();
+ return Double.valueOf(exactMultiply(priceAsDouble, 10_000)).longValue();
+ };
+
+ protected final BiFunction calcPriceAsString = (base, delta) -> {
+ var priceAsBigDecimal = new BigDecimal(Double.toString(base))
.add(new BigDecimal(Double.toString(delta)));
- return fixedPriceAsBigDecimal.toPlainString();
+ return priceAsBigDecimal.toPlainString();
};
+ @SuppressWarnings("ConstantConditions")
public static void createBsqPaymentAccounts() {
alicesBsqAcct = aliceClient.createCryptoCurrencyPaymentAccount("Alice's BSQ Account",
BSQ,
diff --git a/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java
index 6cfa0241ca6..a947044d07c 100644
--- a/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java
@@ -38,9 +38,11 @@
import org.junit.jupiter.api.TestMethodOrder;
import static bisq.apitest.config.ApiTestConfig.BSQ;
+import static bisq.cli.TableFormat.formatOfferTable;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
import static bisq.proto.grpc.EditOfferRequest.EditType.*;
import static java.lang.String.format;
+import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -69,6 +71,7 @@ public void testOfferDisableAndEnable() {
paymentAcct.getId(),
0.0,
NO_TRIGGER_PRICE);
+ log.info("ORIGINAL EUR OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "EUR"));
assertFalse(originalOffer.getIsActivated()); // Not activated until prep is done.
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
originalOffer = aliceClient.getMyOffer(originalOffer.getId());
@@ -77,11 +80,13 @@ public void testOfferDisableAndEnable() {
aliceClient.editOfferActivationState(originalOffer.getId(), DEACTIVATE_OFFER);
genBtcBlocksThenWait(1, 1500); // Wait for offer book removal.
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ log.info("EDITED EUR OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "EUR"));
assertFalse(editedOffer.getIsActivated());
// Re-enable offer
aliceClient.editOfferActivationState(editedOffer.getId(), ACTIVATE_OFFER);
genBtcBlocksThenWait(1, 1500); // Wait for offer book re-entry.
editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ log.info("EDITED EUR OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "EUR"));
assertTrue(editedOffer.getIsActivated());
doSanityCheck(originalOffer, editedOffer);
@@ -96,6 +101,7 @@ public void testEditTriggerPrice() {
paymentAcct.getId(),
0.0,
NO_TRIGGER_PRICE);
+ log.info("ORIGINAL EUR OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "EUR"));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
originalOffer = aliceClient.getMyOffer(originalOffer.getId());
assertEquals(0 /*no trigger price*/, originalOffer.getTriggerPrice());
@@ -103,11 +109,12 @@ public void testEditTriggerPrice() {
// Edit the offer's trigger price, nothing else.
var mktPrice = aliceClient.getBtcPrice("EUR");
var delta = 5_000.00;
- var newTriggerPriceAsLong = calcTriggerPriceAsLong.apply(mktPrice, delta);
+ var newTriggerPriceAsLong = calcPriceAsLong.apply(mktPrice, delta);
aliceClient.editOfferTriggerPrice(originalOffer.getId(), newTriggerPriceAsLong);
sleep(2500); // Wait for offer book re-entry.
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ log.info("EDITED EUR OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "EUR"));
assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice());
assertTrue(editedOffer.getUseMarketBasedPrice());
@@ -123,6 +130,7 @@ public void testSetTriggerPriceToNegativeValueShouldThrowException() {
paymentAcct.getId(),
0.0,
NO_TRIGGER_PRICE);
+ log.info("ORIGINAL EUR OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "EUR"));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
// Edit the offer's trigger price, set to -1, check error.
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
@@ -143,12 +151,14 @@ public void testEditMktPriceMargin() {
paymentAcct.getId(),
originalMktPriceMargin,
NO_TRIGGER_PRICE);
+ log.info("ORIGINAL USD OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "USD"));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin());
// Edit the offer's price margin, nothing else.
var newMktPriceMargin = new BigDecimal("0.5").doubleValue();
aliceClient.editOfferPriceMargin(originalOffer.getId(), newMktPriceMargin);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ log.info("EDITED USD OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "USD"));
assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
doSanityCheck(originalOffer, editedOffer);
@@ -159,19 +169,21 @@ public void testEditMktPriceMargin() {
public void testEditFixedPrice() {
PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU");
double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE);
- String fixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 200_000.0000);
+ String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 200_000.0000);
OfferInfo originalOffer = createFixedPricedOfferForEdit(BUY.name(),
RUBLE,
paymentAcct.getId(),
fixedPriceAsString);
+ log.info("ORIGINAL RUB OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "RUB"));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
// Edit the offer's fixed price, nothing else.
- String editedFixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 100_000.0000);
+ String editedFixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 100_000.0000);
aliceClient.editOfferFixedPrice(originalOffer.getId(), editedFixedPriceAsString);
// Wait for edited offer to be removed from offer-book, edited, and re-published.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
- var expectedNewFixedPrice = scaledUpFiatPrice.apply(new BigDecimal(editedFixedPriceAsString));
+ log.info("EDITED RUB OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "RUB"));
+ var expectedNewFixedPrice = scaledUpFiatOfferPrice.apply(new BigDecimal(editedFixedPriceAsString));
assertEquals(expectedNewFixedPrice, editedOffer.getPrice());
assertFalse(editedOffer.getUseMarketBasedPrice());
@@ -183,14 +195,15 @@ public void testEditFixedPrice() {
public void testEditFixedPriceAndDeactivation() {
PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU");
double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE);
- String fixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 200_000.0000);
+ String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 200_000.0000);
OfferInfo originalOffer = createFixedPricedOfferForEdit(BUY.name(),
RUBLE,
paymentAcct.getId(),
fixedPriceAsString);
+ log.info("ORIGINAL RUB OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "RUB"));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
// Edit the offer's fixed price and deactivate it.
- String editedFixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 100_000.0000);
+ String editedFixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 100_000.0000);
aliceClient.editOffer(originalOffer.getId(),
editedFixedPriceAsString,
originalOffer.getUseMarketBasedPrice(),
@@ -201,7 +214,8 @@ public void testEditFixedPriceAndDeactivation() {
// Wait for edited offer to be removed from offer-book, edited, and re-published.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
- var expectedNewFixedPrice = scaledUpFiatPrice.apply(new BigDecimal(editedFixedPriceAsString));
+ log.info("EDITED RUB OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "RUB"));
+ var expectedNewFixedPrice = scaledUpFiatOfferPrice.apply(new BigDecimal(editedFixedPriceAsString));
assertEquals(expectedNewFixedPrice, editedOffer.getPrice());
assertFalse(editedOffer.getIsActivated());
@@ -219,6 +233,7 @@ public void testEditMktPriceMarginAndDeactivation() {
paymentAcct.getId(),
originalMktPriceMargin,
0);
+ log.info("ORIGINAL USD OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "USD"));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
originalOffer = aliceClient.getMyOffer(originalOffer.getId());
assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin());
@@ -235,6 +250,7 @@ public void testEditMktPriceMarginAndDeactivation() {
// Wait for edited offer to be removed from offer-book, edited, and re-published.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ log.info("EDITED USD OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "USD"));
assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
assertEquals(0, editedOffer.getTriggerPrice());
assertFalse(editedOffer.getIsActivated());
@@ -249,13 +265,14 @@ public void testEditMktPriceMarginAndTriggerPriceAndDeactivation() {
var originalMktPriceMargin = new BigDecimal("0.0").doubleValue();
var mktPriceAsDouble = aliceClient.getBtcPrice(DOLLAR);
- var originalTriggerPriceAsLong = calcTriggerPriceAsLong.apply(mktPriceAsDouble, -5_000.0000);
+ var originalTriggerPriceAsLong = calcPriceAsLong.apply(mktPriceAsDouble, -5_000.0000);
OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
DOLLAR,
paymentAcct.getId(),
originalMktPriceMargin,
originalTriggerPriceAsLong);
+ log.info("ORIGINAL USD OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "USD"));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
originalOffer = aliceClient.getMyOffer(originalOffer.getId());
assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin());
@@ -263,7 +280,7 @@ public void testEditMktPriceMarginAndTriggerPriceAndDeactivation() {
// Edit the offer's price margin and trigger price, and deactivate it.
var newMktPriceMargin = new BigDecimal("0.1").doubleValue();
- var newTriggerPriceAsLong = calcTriggerPriceAsLong.apply(mktPriceAsDouble, -2_000.0000);
+ var newTriggerPriceAsLong = calcPriceAsLong.apply(mktPriceAsDouble, -2_000.0000);
aliceClient.editOffer(originalOffer.getId(),
"0.00",
originalOffer.getUseMarketBasedPrice(),
@@ -274,6 +291,7 @@ public void testEditMktPriceMarginAndTriggerPriceAndDeactivation() {
// Wait for edited offer to be removed from offer-book, edited, and re-published.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ log.info("EDITED USD OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "USD"));
assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice());
assertFalse(editedOffer.getIsActivated());
@@ -291,6 +309,7 @@ public void testEditingFixedPriceInMktPriceMarginBasedOfferShouldThrowException(
paymentAcct.getId(),
originalMktPriceMargin,
NO_TRIGGER_PRICE);
+ log.info("ORIGINAL USD OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "USD"));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
// Try to edit both the fixed price and mkt price margin.
var newMktPriceMargin = new BigDecimal("0.25").doubleValue();
@@ -316,11 +335,12 @@ public void testEditingFixedPriceInMktPriceMarginBasedOfferShouldThrowException(
public void testEditingTriggerPriceInFixedPriceOfferShouldThrowException() {
PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU");
double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE);
- String fixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 200_000.0000);
+ String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 200_000.0000);
OfferInfo originalOffer = createFixedPricedOfferForEdit(BUY.name(),
RUBLE,
paymentAcct.getId(),
fixedPriceAsString);
+ log.info("ORIGINAL RUB OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "RUB"));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
long newTriggerPrice = 1000000L;
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
@@ -338,17 +358,18 @@ public void testEditingTriggerPriceInFixedPriceOfferShouldThrowException() {
public void testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice() {
PaymentAccount paymentAcct = getOrCreatePaymentAccount("MX");
double mktPriceAsDouble = aliceClient.getBtcPrice("MXN");
- String fixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 0.00);
+ String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 0.00);
OfferInfo originalOffer = createFixedPricedOfferForEdit(BUY.name(),
"MXN",
paymentAcct.getId(),
fixedPriceAsString);
+ log.info("ORIGINAL MXN OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "MXN"));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
// Change the offer to mkt price based and set a trigger price.
var newMktPriceMargin = new BigDecimal("0.05").doubleValue();
var delta = 200_000.0000; // trigger price on buy offer is 200K above mkt price
- var newTriggerPriceAsLong = calcTriggerPriceAsLong.apply(mktPriceAsDouble, delta);
+ var newTriggerPriceAsLong = calcPriceAsLong.apply(mktPriceAsDouble, delta);
aliceClient.editOffer(originalOffer.getId(),
"0.00",
true,
@@ -359,6 +380,7 @@ public void testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice() {
// Wait for edited offer to be removed from offer-book, edited, and re-published.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ log.info("EDITED MXN OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "MXN"));
assertTrue(editedOffer.getUseMarketBasedPrice());
assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice());
@@ -374,15 +396,16 @@ public void testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt() {
double mktPriceAsDouble = aliceClient.getBtcPrice("GBP");
var originalMktPriceMargin = new BigDecimal("0.25").doubleValue();
var delta = 1_000.0000; // trigger price on sell offer is 1K below mkt price
- var originalTriggerPriceAsLong = calcTriggerPriceAsLong.apply(mktPriceAsDouble, delta);
+ var originalTriggerPriceAsLong = calcPriceAsLong.apply(mktPriceAsDouble, delta);
final OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
"GBP",
paymentAcct.getId(),
originalMktPriceMargin,
originalTriggerPriceAsLong);
+ log.info("ORIGINAL GBP OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "GBP"));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
- String fixedPriceAsString = calcFixedPriceAsString.apply(mktPriceAsDouble, 0.00);
+ String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 0.00);
aliceClient.editOffer(originalOffer.getId(),
fixedPriceAsString,
false,
@@ -393,7 +416,8 @@ public void testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt() {
// Wait for edited offer to be removed from offer-book, edited, and re-published.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
- assertEquals(scaledUpFiatPrice.apply(new BigDecimal(fixedPriceAsString)), editedOffer.getPrice());
+ log.info("EDITED GBP OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "GBP"));
+ assertEquals(scaledUpFiatOfferPrice.apply(new BigDecimal(fixedPriceAsString)), editedOffer.getPrice());
assertFalse(editedOffer.getUseMarketBasedPrice());
assertEquals(0.00, editedOffer.getMarketPriceMargin());
assertEquals(0, editedOffer.getTriggerPrice());
@@ -402,9 +426,9 @@ public void testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt() {
@Test
@Order(13)
- public void testEditBsqOfferShouldThrowException() {
+ public void testChangeFixedPricedBsqOfferToPriceMarginBasedOfferShouldThrowException() {
createBsqPaymentAccounts();
- var newOffer = aliceClient.createFixedPricedOffer(BUY.name(),
+ OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
BSQ,
100_000_000L,
100_000_000L,
@@ -412,13 +436,151 @@ public void testEditBsqOfferShouldThrowException() {
getDefaultBuyerSecurityDepositAsPercent(),
alicesBsqAcct.getId(),
BSQ);
- // TODO Allow editing BSQ offer fixed-price, enable/disable.
+ log.info("ORIGINAL BSQ OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "BSQ"));
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
- aliceClient.editOfferActivationState(newOffer.getId(), DEACTIVATE_OFFER));
- String expectedExceptionMessage = format("UNKNOWN: editing altcoin offer not supported");
+ aliceClient.editOffer(originalOffer.getId(),
+ "0.00",
+ true,
+ 0.1,
+ 0,
+ ACTIVATE_OFFER,
+ MKT_PRICE_MARGIN_ONLY));
+ String expectedExceptionMessage = format("UNKNOWN: cannot set mkt price margin or"
+ + " trigger price on fixed price altcoin offer with id '%s'",
+ originalOffer.getId());
assertEquals(expectedExceptionMessage, exception.getMessage());
}
+ @Test
+ @Order(14)
+ public void testEditTriggerPriceOnFixedPriceBsqOfferShouldThrowException() {
+ createBsqPaymentAccounts();
+ OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
+ BSQ,
+ 100_000_000L,
+ 100_000_000L,
+ "0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
+ getDefaultBuyerSecurityDepositAsPercent(),
+ alicesBsqAcct.getId(),
+ BSQ);
+ log.info("ORIGINAL BSQ OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "BSQ"));
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ var newTriggerPriceAsLong = calcPriceAsLong.apply(0.00005, 0.00);
+ Throwable exception = assertThrows(StatusRuntimeException.class, () ->
+ aliceClient.editOffer(originalOffer.getId(),
+ "0.00",
+ false,
+ 0.1,
+ newTriggerPriceAsLong,
+ ACTIVATE_OFFER,
+ TRIGGER_PRICE_ONLY));
+ String expectedExceptionMessage = format("UNKNOWN: cannot set mkt price margin or"
+ + " trigger price on fixed price altcoin offer with id '%s'",
+ originalOffer.getId());
+ assertEquals(expectedExceptionMessage, exception.getMessage());
+ }
+
+ @Test
+ @Order(15)
+ public void testEditFixedPriceOnBsqOffer() {
+ createBsqPaymentAccounts();
+ String fixedPriceAsString = "0.00005"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
+ final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
+ BSQ,
+ 100_000_000L,
+ 100_000_000L,
+ fixedPriceAsString,
+ getDefaultBuyerSecurityDepositAsPercent(),
+ alicesBsqAcct.getId(),
+ BSQ);
+ log.info("ORIGINAL BSQ OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "BSQ"));
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ String newFixedPriceAsString = "0.00003111";
+ aliceClient.editOffer(originalOffer.getId(),
+ newFixedPriceAsString,
+ false,
+ 0.0,
+ 0,
+ ACTIVATE_OFFER,
+ FIXED_PRICE_ONLY);
+ // Wait for edited offer to be edited and removed from offer-book.
+ genBtcBlocksThenWait(1, 2500);
+ OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ log.info("EDITED BSQ OFFER:\n{}", formatOfferTable(singletonList(editedOffer), BSQ));
+ assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice());
+ assertTrue(editedOffer.getIsActivated());
+ assertFalse(editedOffer.getUseMarketBasedPrice());
+ assertEquals(0.00, editedOffer.getMarketPriceMargin());
+ assertEquals(0, editedOffer.getTriggerPrice());
+ }
+
+ @Test
+ @Order(16)
+ public void testDisableBsqOffer() {
+ createBsqPaymentAccounts();
+ String fixedPriceAsString = "0.00005"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
+ final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
+ BSQ,
+ 100_000_000L,
+ 100_000_000L,
+ fixedPriceAsString,
+ getDefaultBuyerSecurityDepositAsPercent(),
+ alicesBsqAcct.getId(),
+ BSQ);
+ log.info("ORIGINAL BSQ OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "BSQ"));
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ aliceClient.editOffer(originalOffer.getId(),
+ fixedPriceAsString,
+ false,
+ 0.0,
+ 0,
+ DEACTIVATE_OFFER,
+ ACTIVATION_STATE_ONLY);
+ // Wait for edited offer to be removed from offer-book.
+ genBtcBlocksThenWait(1, 2500);
+ OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ log.info("EDITED BSQ OFFER:\n{}", formatOfferTable(singletonList(editedOffer), BSQ));
+ assertFalse(editedOffer.getIsActivated());
+ assertEquals(scaledUpAltcoinOfferPrice.apply(fixedPriceAsString), editedOffer.getPrice());
+ assertFalse(editedOffer.getUseMarketBasedPrice());
+ assertEquals(0.00, editedOffer.getMarketPriceMargin());
+ assertEquals(0, editedOffer.getTriggerPrice());
+ }
+
+ @Test
+ @Order(17)
+ public void testEditFixedPriceAndDisableBsqOffer() {
+ createBsqPaymentAccounts();
+ String fixedPriceAsString = "0.00005"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
+ final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
+ BSQ,
+ 100_000_000L,
+ 100_000_000L,
+ fixedPriceAsString,
+ getDefaultBuyerSecurityDepositAsPercent(),
+ alicesBsqAcct.getId(),
+ BSQ);
+ log.info("ORIGINAL BSQ OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "BSQ"));
+ genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
+ String newFixedPriceAsString = "0.000045";
+ aliceClient.editOffer(originalOffer.getId(),
+ newFixedPriceAsString,
+ false,
+ 0.0,
+ 0,
+ DEACTIVATE_OFFER,
+ FIXED_PRICE_AND_ACTIVATION_STATE);
+ // Wait for edited offer to be edited and removed from offer-book.
+ genBtcBlocksThenWait(1, 2500);
+ OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
+ log.info("EDITED BSQ OFFER:\n{}", formatOfferTable(singletonList(editedOffer), BSQ));
+ assertFalse(editedOffer.getIsActivated());
+ assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice());
+ assertFalse(editedOffer.getUseMarketBasedPrice());
+ assertEquals(0.00, editedOffer.getMarketPriceMargin());
+ assertEquals(0, editedOffer.getTriggerPrice());
+ }
private OfferInfo createMktPricedOfferForEdit(String direction,
String currencyCode,
diff --git a/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java b/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java
index 2aeea5a436c..e7f09247a86 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java
@@ -59,7 +59,7 @@ public class LongRunningOfferDeactivationTest extends AbstractOfferTest {
public void testSellOfferAutoDisable(final TestInfo testInfo) {
PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, "US");
double mktPriceAsDouble = aliceClient.getBtcPrice("USD");
- long triggerPrice = calcTriggerPriceAsLong.apply(mktPriceAsDouble, -50.0000);
+ long triggerPrice = calcPriceAsLong.apply(mktPriceAsDouble, -50.0000);
log.info("Current USD mkt price = {} Trigger Price = {}", mktPriceAsDouble, formatPrice(triggerPrice));
OfferInfo offer = aliceClient.createMarketBasedPricedOffer(SELL.name(),
"USD",
@@ -107,7 +107,7 @@ public void testSellOfferAutoDisable(final TestInfo testInfo) {
public void testBuyOfferAutoDisable(final TestInfo testInfo) {
PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, "US");
double mktPriceAsDouble = aliceClient.getBtcPrice("USD");
- long triggerPrice = calcTriggerPriceAsLong.apply(mktPriceAsDouble, 50.0000);
+ long triggerPrice = calcPriceAsLong.apply(mktPriceAsDouble, 50.0000);
log.info("Current USD mkt price = {} Trigger Price = {}", mktPriceAsDouble, formatPrice(triggerPrice));
OfferInfo offer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
"USD",
diff --git a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
index e37c56a8302..41ac197f1b5 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
@@ -92,6 +92,7 @@ public void testCreateBSQOffers() {
@Order(6)
public void testEditOffer() {
EditOfferTest test = new EditOfferTest();
+ // Edit fiat offer tests
test.testOfferDisableAndEnable();
test.testEditTriggerPrice();
test.testSetTriggerPriceToNegativeValueShouldThrowException();
@@ -104,7 +105,12 @@ public void testEditOffer() {
test.testEditingTriggerPriceInFixedPriceOfferShouldThrowException();
test.testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice();
test.testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt();
- // TODO Allow editing BSQ offer fixed-price, enable/disable.
- test.testEditBsqOfferShouldThrowException();
+ test.testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice();
+ // Edit bsq offer tests
+ test.testChangeFixedPricedBsqOfferToPriceMarginBasedOfferShouldThrowException();
+ test.testEditTriggerPriceOnFixedPriceBsqOfferShouldThrowException();
+ test.testEditFixedPriceOnBsqOffer();
+ test.testDisableBsqOffer();
+ test.testEditFixedPriceAndDisableBsqOffer();
}
}
From acbf1e4323c5bf12615a14ad1b4b3017a69a9b7e Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sat, 19 Jun 2021 11:40:21 -0300
Subject: [PATCH 30/40] Force rebuild after github action ECONNRESET
From 9703b87379ef179b7b47897a123efddaeef4767b Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sat, 19 Jun 2021 13:27:02 -0300
Subject: [PATCH 31/40] Document api 'editoffer' usage
---
apitest/docs/api-beta-test-guide.md | 114 +++++++++++++++++++++++++++-
1 file changed, 112 insertions(+), 2 deletions(-)
diff --git a/apitest/docs/api-beta-test-guide.md b/apitest/docs/api-beta-test-guide.md
index 4a110a303ac..ff86acd54ac 100644
--- a/apitest/docs/api-beta-test-guide.md
+++ b/apitest/docs/api-beta-test-guide.md
@@ -408,8 +408,118 @@ The offer will be removed from other Bisq users' offer views, and paid transacti
### Editing an Existing Offer
-Editing existing offers is not yet supported. You can cancel and re-create an offer, but paid transaction fees
-for the canceled offer will be forfeited.
+Offers you create can be edited in various ways:
+
+- Disable or re-enable an offer.
+- Change an offer's price model and disable (or re-enable) it.
+- Change a market price margin based offer to a fixed price offer.
+- Change a market price margin based offer's price margin.
+- Change, set, or remove a trigger price on a market price margin based offer.
+- Change a market price margin based offer's price margin and trigger price.
+- Change a market price margin based offer's price margin and remove its trigger price.
+- Change a fixed price offer to a market price margin based offer.
+- Change a fixed price offer's fixed price.
+
+_Note: the API does not support editing an offer's payment account._
+
+The subsections below contain examples related to specific use cases.
+
+#### Enable and Disable Offer
+
+Existing offers you create can be disabled (removed from offer book) and re-enabled (re-published to offer book).
+
+To disable an offer:
+```
+./bisq-cli --password=xyz --port=9998 editoffer \
+ --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --enabled=false
+```
+
+To enable an offer:
+```
+./bisq-cli --password=xyz --port=9998 editoffer \
+ --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --enabled=true
+```
+
+#### Change Offer Pricing Model
+The `editoffer` command can be used to change an existing market price margin based offer to a fixed price offer,
+and vice-versa.
+
+##### Change Market Price Margin Based to Fixed Price Offer
+Suppose you used `createoffer` to create a market price margin based offer as follows:
+```
+$ ./bisq-cli --password=xyz --port=9998 createoffer \
+ --payment-account=f3c1ec8b-9761-458d-b13d-9039c6892413 \
+ --direction=SELL \
+ --currency-code=JPY \
+ --amount=0.125 \
+ --market-price-margin=0.5 \
+ --security-deposit=15.0 \
+ --fee-currency=BSQ
+```
+To change the market price margin based offer to a fixed price offer:
+```
+./bisq-cli --password=xyz --port=9998 editoffer \
+ --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --fixed-price=3960000.5555
+```
+
+##### Change Fixed Price Offer to Market Price Margin Based Offer
+Suppose you used `createoffer` to create a fixed price offer as follows:
+```
+$ ./bisq-cli --password=xyz --port=9998 createoffer \
+ --payment-account=f3c1ec8b-9761-458d-b13d-9039c6892413 \
+ --direction=SELL \
+ --currency-code=JPY \
+ --amount=0.125 \
+ --fixed-price=3960000.0000 \
+ --security-deposit=15.0 \
+ --fee-currency=BSQ
+```
+To change the fixed price offer to a market price margin based offer:
+```
+./bisq-cli --password=xyz --port=9998 editoffer \
+ --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --market-price-margin=0.5
+```
+Alternatively, you can also set a trigger price on the re-published, market price margin based offer.
+A trigger price on a SELL offer causes the offer to be automatically disabled when the market price
+falls below the trigger price. In the `editoffer` example below, the SELL offer will be disabled when
+the JPY market price falls below 3960000.0000.
+
+```
+./bisq-cli --password=xyz --port=9998 editoffer \
+ --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --market-price-margin=0.5 \
+ --trigger-price=3960000.0000
+```
+On a BUY offer, a trigger price causes the BUY offer to be automatically disabled when the market price
+rises above the trigger price.
+
+_Note: Disabled offers never automatically re-enable; they can only be manually re-enabled via
+`editoffer --offer-id= --enable=true`._
+
+#### Remove Trigger Price
+To remove a trigger price on a market price margin based offer, set the trigger price to 0:
+```
+./bisq-cli --password=xyz --port=9998 editoffer \
+ --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --trigger-price=0
+```
+
+#### Change Pricing Model And Disable Offer
+You can use `editoffer` to simultaneously change an offer's price details and disable or re-enable it.
+
+Suppose you have a disabled, fixed price offer, and want to change it to a market price margin based offer, set
+a trigger price, and re-enable it:
+```
+./bisq-cli --password=xyz --port=9998 editoffer \
+ --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
+ --market-price-margin=0.5 \
+ --trigger-price=3960000.0000 \
+ --enable=true
+```
### Taking Offers
From 05f4f4dd8050682653179f69f0fcbb302198b91e Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sat, 19 Jun 2021 13:35:40 -0300
Subject: [PATCH 32/40] Fix header
---
apitest/docs/api-beta-test-guide.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apitest/docs/api-beta-test-guide.md b/apitest/docs/api-beta-test-guide.md
index ff86acd54ac..6cab5f4eae5 100644
--- a/apitest/docs/api-beta-test-guide.md
+++ b/apitest/docs/api-beta-test-guide.md
@@ -508,7 +508,7 @@ To remove a trigger price on a market price margin based offer, set the trigger
--trigger-price=0
```
-#### Change Pricing Model And Disable Offer
+#### Change Disabled Offer's Pricing Model and Enable It
You can use `editoffer` to simultaneously change an offer's price details and disable or re-enable it.
Suppose you have a disabled, fixed price offer, and want to change it to a market price margin based offer, set
From 06efcdfcb94b1fd60383bec02f7c6a75f169558f Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 11 Jul 2021 12:08:40 -0300
Subject: [PATCH 33/40] Delete tmp main() method
---
.../offer/CreateOfferUsingMarketPriceMarginTest.java | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java
index a34f144403e..25052057dac 100644
--- a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java
@@ -54,6 +54,7 @@
import static protobuf.OfferPayload.Direction.BUY;
import static protobuf.OfferPayload.Direction.SELL;
+@SuppressWarnings("ConstantConditions")
@Disabled
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@@ -258,16 +259,6 @@ public void testCreateUSDBTCBuyOfferWithTriggerPrice() {
assertEquals(triggerPriceAsLong, newOffer.getTriggerPrice());
}
- public static void main(String[] args) {
- // TODO DELETE ME
- String triggerPriceAsString = "10.1111";
- Price price = Price.parse("USD", triggerPriceAsString);
- long triggerPriceAsLong = price.getValue();
- log.info("triggerPriceAsString: {}", triggerPriceAsString);
- log.info("triggerPriceAsPrice: {}", price);
- log.info("triggerPriceAsLong: {}", triggerPriceAsLong);
- }
-
private void assertCalculatedPriceIsCorrect(OfferInfo offer, double priceMarginPctInput) {
assertTrue(() -> {
String counterCurrencyCode = offer.getCounterCurrencyCode();
From eb62f9354af3fdbc0fc4fb0074fea81ecf0667dd Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Mon, 12 Jul 2021 11:28:33 -0300
Subject: [PATCH 34/40] Rename and move private function
And make sure function is not duplicated CLI side logic.
---
.../bisq/cli/request/OffersServiceRequest.java | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java
index c3cff777b1b..215c4f3e80d 100644
--- a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java
+++ b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java
@@ -47,6 +47,12 @@
public class OffersServiceRequest {
+ private final Function scaledPriceStringRequestFormat = (price) -> {
+ BigDecimal factor = new BigDecimal(10).pow(4);
+ //noinspection BigDecimalMethodWithoutRoundingCalled
+ return new BigDecimal(price).divide(factor).toPlainString();
+ };
+
private final GrpcStubs grpcStubs;
public OffersServiceRequest(GrpcStubs grpcStubs) {
@@ -123,18 +129,11 @@ public OfferInfo createOffer(String direction,
return grpcStubs.offersService.createOffer(request).getOffer();
}
- // TODO Make sure this is not duplicated anywhere on CLI side.
- private final Function scaledPriceStringFormat = (price) -> {
- BigDecimal factor = new BigDecimal(10).pow(4);
- //noinspection BigDecimalMethodWithoutRoundingCalled
- return new BigDecimal(price).divide(factor).toPlainString();
- };
-
public void editOfferActivationState(String offerId, int enable) {
var offer = getMyOffer(offerId);
var scaledPriceString = offer.getUseMarketBasedPrice()
? "0.00"
- : scaledPriceStringFormat.apply(offer.getPrice());
+ : scaledPriceStringRequestFormat.apply(offer.getPrice());
editOffer(offerId,
scaledPriceString,
offer.getUseMarketBasedPrice(),
From 1992bcb1c0c08fba3bf24a21e4565f884ec379c4 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Mon, 12 Jul 2021 11:34:37 -0300
Subject: [PATCH 35/40] Do not duplicate Price.parse on CLI side for only one
use case
---
.../method/offer/CreateOfferUsingMarketPriceMarginTest.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java
index 25052057dac..451bd1f2c18 100644
--- a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java
@@ -241,7 +241,6 @@ public void testCreateUSDBTCBuyOfferWithTriggerPrice() {
double mktPriceAsDouble = aliceClient.getBtcPrice("usd");
BigDecimal mktPrice = new BigDecimal(Double.toString(mktPriceAsDouble));
BigDecimal triggerPrice = mktPrice.add(new BigDecimal("1000.9999"));
- // TODO Duplicate this Price class logic in CLI.
long triggerPriceAsLong = Price.parse("USD", triggerPrice.toString()).getValue();
var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
From 622f7e9adda51df60db0910ce79cf8898f633834 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Mon, 12 Jul 2021 11:39:53 -0300
Subject: [PATCH 36/40] Remove old TODO because relevant refactoring was
approved
---
.../src/test/java/bisq/apitest/scenario/bot/BotClient.java | 5 -----
1 file changed, 5 deletions(-)
diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java b/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java
index d6941a0a402..062ee742b19 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java
@@ -39,11 +39,6 @@
/**
* Convenience GrpcClient wrapper for bots using gRPC services.
- *
- * TODO Consider if the duplication smell is bad enough to force a BotClient user
- * to use the GrpcClient instead (and delete this class). But right now, I think it is
- * OK because moving some of the non-gRPC related methods to GrpcClient is even smellier.
- *
*/
@SuppressWarnings({"JavaDoc", "unused"})
@Slf4j
From a4278a4147c78ae7e4350058457af59a0083c9de Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Tue, 13 Jul 2021 10:32:35 -0300
Subject: [PATCH 37/40] Fix typo 'enabled' -> 'enable'
---
apitest/docs/api-beta-test-guide.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/apitest/docs/api-beta-test-guide.md b/apitest/docs/api-beta-test-guide.md
index 6cab5f4eae5..8c1b41112bc 100644
--- a/apitest/docs/api-beta-test-guide.md
+++ b/apitest/docs/api-beta-test-guide.md
@@ -432,14 +432,14 @@ To disable an offer:
```
./bisq-cli --password=xyz --port=9998 editoffer \
--offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
- --enabled=false
+ --enable=false
```
To enable an offer:
```
./bisq-cli --password=xyz --port=9998 editoffer \
--offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
- --enabled=true
+ --enable=true
```
#### Change Offer Pricing Model
From 649c98a3f0ba2916e0fde21ab70be7ff8ba9d60b Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Thu, 15 Jul 2021 12:43:29 -0300
Subject: [PATCH 38/40] Always use Locale.US in CLI DecimalFormats
Avoid inconsistent CLI output decimal formats across different
systems' default locales.
---
cli/src/main/java/bisq/cli/CurrencyFormat.java | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/cli/src/main/java/bisq/cli/CurrencyFormat.java b/cli/src/main/java/bisq/cli/CurrencyFormat.java
index 29639ec7b83..8d8a3d11fde 100644
--- a/cli/src/main/java/bisq/cli/CurrencyFormat.java
+++ b/cli/src/main/java/bisq/cli/CurrencyFormat.java
@@ -22,6 +22,7 @@
import com.google.common.annotations.VisibleForTesting;
import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.math.BigDecimal;
@@ -35,6 +36,9 @@
@VisibleForTesting
public class CurrencyFormat {
+ // Use the US locale for all DecimalFormat objects.
+ private static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = DecimalFormatSymbols.getInstance(Locale.US);
+
// Formats numbers in US locale, human friendly style.
private static final NumberFormat FRIENDLY_NUMBER_FORMAT = NumberFormat.getInstance(Locale.US);
@@ -42,12 +46,12 @@ public class CurrencyFormat {
private static final DecimalFormat INTERNAL_FIAT_DECIMAL_FORMAT = new DecimalFormat("##############0.0000");
static final BigDecimal SATOSHI_DIVISOR = new BigDecimal(100_000_000);
- static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.00000000");
- static final DecimalFormat BTC_TX_FEE_FORMAT = new DecimalFormat("###,###,##0");
+ static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.00000000", DECIMAL_FORMAT_SYMBOLS);
+ static final DecimalFormat BTC_TX_FEE_FORMAT = new DecimalFormat("###,###,##0", DECIMAL_FORMAT_SYMBOLS);
static final BigDecimal BSQ_SATOSHI_DIVISOR = new BigDecimal(100);
- static final DecimalFormat BSQ_FORMAT = new DecimalFormat("###,###,###,##0.00");
- static final DecimalFormat SEND_BSQ_FORMAT = new DecimalFormat("###########0.00");
+ static final DecimalFormat BSQ_FORMAT = new DecimalFormat("###,###,###,##0.00", DECIMAL_FORMAT_SYMBOLS);
+ static final DecimalFormat SEND_BSQ_FORMAT = new DecimalFormat("###########0.00", DECIMAL_FORMAT_SYMBOLS);
static final BigDecimal SECURITY_DEPOSIT_MULTIPLICAND = new BigDecimal("0.01");
@@ -62,7 +66,6 @@ public static String formatBsq(long sats) {
}
public static String formatBsqAmount(long bsqSats) {
- // BSQ sats = trade.getOffer().getVolume()
FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(2);
FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(2);
FRIENDLY_NUMBER_FORMAT.setRoundingMode(HALF_UP);
From 1d21cb9c10882238c29153de049c33cf17b64d9a Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 18 Jul 2021 17:49:04 -0300
Subject: [PATCH 39/40] Add missing 0 trigger-price param
---
.../method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java
index 3868fffc300..707df3bb40e 100644
--- a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java
@@ -103,7 +103,8 @@ public void testTakeAlicesBuyOffer(final TestInfo testInfo) {
0.00,
getDefaultBuyerSecurityDepositAsPercent(),
alicesPaymentAccount.getId(),
- TRADE_FEE_CURRENCY_CODE);
+ TRADE_FEE_CURRENCY_CODE,
+ 0L);
var offerId = alicesOffer.getId();
assertFalse(alicesOffer.getIsCurrencyForMakerFeeBtc());
From 92365720a5adf7bc39f6d20180f21e83bc0e87e9 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Sun, 18 Jul 2021 18:19:25 -0300
Subject: [PATCH 40/40] Workaround API editoffer event rcv order problem in
peer UI
When the API is used to edit an offer, the daemon's P2PDataStorage will
send onRemoved(offer) and onAdded(offer) events 1-3 ms apart, and those
events may arrive at a peer's UI in the wrong order: onAdded, onRemoved.
When the order is backwards, the edited offer disappears from the UI's
OfferBook view.
Reloading the offerBookListItems works around the problem. Reloading
100 offers usually takes less then 1 ms.
I would prefer to solve this in 2+ other classes:
- P2PDataStorage, where these events are fired in the correct order,
but down to 1ms apart.
- OfferBookService constructor, where the peer UI's HashMapChangedListener
methods 'onRemoved' and 'onAdded' are causing API edited offers to
dissappear if received in backwards order: 'onAdded', then 'onRemoved'.
Trying to insert a delay between remove an offer for edit, then
republishing the offer, does not work. The events can still
arrive in the wrong order.
I am going to need some help from @chimp1984 to solve it.
---
.../bisq/desktop/main/offer/offerbook/OfferBook.java | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBook.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBook.java
index 753506bce12..8bcc9d782c1 100644
--- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBook.java
+++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBook.java
@@ -120,7 +120,16 @@ public void removeOffer(Offer offer, TradeManager tradeManager) {
Optional candidateToRemove = offerBookListItems.stream()
.filter(item -> item.getOffer().getId().equals(offer.getId()))
.findAny();
- candidateToRemove.ifPresent(offerBookListItems::remove);
+ candidateToRemove.ifPresent((item) -> {
+ offerBookListItems.remove(item);
+ // When the API is used to edit an offer, the daemon's P2PDataStorage will
+ // send onRemoved(offer) and onAdded(offer) events 1-3 ms apart, and those
+ // events may arrive at a peer's UI in the wrong order (onAdded, onRemoved),
+ // resulting in an edited offer disappearing from the UI's OfferBook.
+ // Reloading the offerBookListItems works around the problem. Reloading
+ // 100 offers usually takes less then 1 ms.
+ fillOfferBookListItems();
+ });
}
public ObservableList getOfferBookListItems() {