mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-23 23:06:39 +01:00
Add isTakerApiUser field to OfferAvailabilityRequest
Add UNCONF_TX_LIMIT_HIT and MAKER_DENIED_API_USER to AvailabilityResult enum Apply handling for api filter features
This commit is contained in:
parent
95063b6c7f
commit
c2174607f5
17 changed files with 395 additions and 260 deletions
|
@ -112,6 +112,15 @@ public class CoreApi {
|
||||||
return coreOffersService.getOffers(direction, currencyCode);
|
return coreOffersService.getOffers(direction, currencyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param direction The offer direction
|
||||||
|
* @param currencyCode The offer currency
|
||||||
|
* @return Returns the offers which can be taken
|
||||||
|
*/
|
||||||
|
List<Offer> getOffersAvailableForTaker(String direction, String currencyCode) {
|
||||||
|
return coreOffersService.getOffersAvailableForTaker(direction, currencyCode, true);
|
||||||
|
}
|
||||||
|
|
||||||
public void createAnPlaceOffer(String currencyCode,
|
public void createAnPlaceOffer(String currencyCode,
|
||||||
String directionAsString,
|
String directionAsString,
|
||||||
String priceAsString,
|
String priceAsString,
|
||||||
|
@ -202,6 +211,7 @@ public class CoreApi {
|
||||||
coreTradesService.takeOffer(offer,
|
coreTradesService.takeOffer(offer,
|
||||||
paymentAccountId,
|
paymentAccountId,
|
||||||
takerFeeCurrencyCode,
|
takerFeeCurrencyCode,
|
||||||
|
true,
|
||||||
resultHandler);
|
resultHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import bisq.core.monetary.Price;
|
||||||
import bisq.core.offer.CreateOfferService;
|
import bisq.core.offer.CreateOfferService;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferBookService;
|
import bisq.core.offer.OfferBookService;
|
||||||
|
import bisq.core.offer.OfferFilter;
|
||||||
import bisq.core.offer.OfferUtil;
|
import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.offer.OpenOfferManager;
|
import bisq.core.offer.OpenOfferManager;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
|
@ -58,20 +59,24 @@ class CoreOffersService {
|
||||||
private final OpenOfferManager openOfferManager;
|
private final OpenOfferManager openOfferManager;
|
||||||
private final OfferUtil offerUtil;
|
private final OfferUtil offerUtil;
|
||||||
private final User user;
|
private final User user;
|
||||||
|
private final OfferFilter offerFilter;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public CoreOffersService(CreateOfferService createOfferService,
|
public CoreOffersService(CreateOfferService createOfferService,
|
||||||
OfferBookService offerBookService,
|
OfferBookService offerBookService,
|
||||||
OpenOfferManager openOfferManager,
|
OpenOfferManager openOfferManager,
|
||||||
OfferUtil offerUtil,
|
OfferUtil offerUtil,
|
||||||
User user) {
|
User user,
|
||||||
|
OfferFilter offerFilter) {
|
||||||
this.createOfferService = createOfferService;
|
this.createOfferService = createOfferService;
|
||||||
this.offerBookService = offerBookService;
|
this.offerBookService = offerBookService;
|
||||||
this.openOfferManager = openOfferManager;
|
this.openOfferManager = openOfferManager;
|
||||||
this.offerUtil = offerUtil;
|
this.offerUtil = offerUtil;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
this.offerFilter = offerFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO should we add a check for offerFilter.canTakeOffer?
|
||||||
Offer getOffer(String id) {
|
Offer getOffer(String id) {
|
||||||
return offerBookService.getOffers().stream()
|
return offerBookService.getOffers().stream()
|
||||||
.filter(o -> o.getId().equals(id))
|
.filter(o -> o.getId().equals(id))
|
||||||
|
@ -79,6 +84,8 @@ class CoreOffersService {
|
||||||
new IllegalStateException(format("offer with id '%s' not found", id)));
|
new IllegalStateException(format("offer with id '%s' not found", id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO returns all offers also those which cannot be taken. Should we use the filter from
|
||||||
|
// getOffersAvailableForTaker here and remove the getOffersAvailableForTaker method?
|
||||||
List<Offer> getOffers(String direction, String currencyCode) {
|
List<Offer> getOffers(String direction, String currencyCode) {
|
||||||
List<Offer> offers = offerBookService.getOffers().stream()
|
List<Offer> offers = offerBookService.getOffers().stream()
|
||||||
.filter(o -> {
|
.filter(o -> {
|
||||||
|
@ -99,6 +106,12 @@ class CoreOffersService {
|
||||||
return offers;
|
return offers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Offer> getOffersAvailableForTaker(String direction, String currencyCode, boolean isTakerApiUser) {
|
||||||
|
return getOffers(direction, currencyCode).stream()
|
||||||
|
.filter(offer -> offerFilter.canTakeOffer(offer, isTakerApiUser).isValid())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
// Create and place new offer.
|
// Create and place new offer.
|
||||||
void createAndPlaceOffer(String currencyCode,
|
void createAndPlaceOffer(String currencyCode,
|
||||||
String directionAsString,
|
String directionAsString,
|
||||||
|
|
|
@ -82,6 +82,7 @@ class CoreTradesService {
|
||||||
void takeOffer(Offer offer,
|
void takeOffer(Offer offer,
|
||||||
String paymentAccountId,
|
String paymentAccountId,
|
||||||
String takerFeeCurrencyCode,
|
String takerFeeCurrencyCode,
|
||||||
|
boolean isTakerApiUser,
|
||||||
Consumer<Trade> resultHandler) {
|
Consumer<Trade> resultHandler) {
|
||||||
coreWalletsService.verifyWalletsAreAvailable();
|
coreWalletsService.verifyWalletsAreAvailable();
|
||||||
coreWalletsService.verifyEncryptedWalletIsUnlocked();
|
coreWalletsService.verifyEncryptedWalletIsUnlocked();
|
||||||
|
@ -108,6 +109,7 @@ class CoreTradesService {
|
||||||
offer,
|
offer,
|
||||||
paymentAccountId,
|
paymentAccountId,
|
||||||
useSavingsWallet,
|
useSavingsWallet,
|
||||||
|
isTakerApiUser,
|
||||||
resultHandler::accept,
|
resultHandler::accept,
|
||||||
errorMessage -> {
|
errorMessage -> {
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
|
|
|
@ -27,5 +27,7 @@ public enum AvailabilityResult {
|
||||||
NO_MEDIATORS,
|
NO_MEDIATORS,
|
||||||
USER_IGNORED,
|
USER_IGNORED,
|
||||||
MISSING_MANDATORY_CAPABILITY,
|
MISSING_MANDATORY_CAPABILITY,
|
||||||
NO_REFUND_AGENTS
|
NO_REFUND_AGENTS,
|
||||||
|
UNCONF_TX_LIMIT_HIT,
|
||||||
|
MAKER_DENIED_API_USER
|
||||||
}
|
}
|
||||||
|
|
209
core/src/main/java/bisq/core/offer/OfferFilter.java
Normal file
209
core/src/main/java/bisq/core/offer/OfferFilter.java
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.core.offer;
|
||||||
|
|
||||||
|
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||||
|
import bisq.core.filter.FilterManager;
|
||||||
|
import bisq.core.locale.CurrencyUtil;
|
||||||
|
import bisq.core.payment.PaymentAccount;
|
||||||
|
import bisq.core.payment.PaymentAccountUtil;
|
||||||
|
import bisq.core.user.Preferences;
|
||||||
|
import bisq.core.user.User;
|
||||||
|
|
||||||
|
import bisq.common.app.Version;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import javafx.collections.SetChangeListener;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Singleton
|
||||||
|
public class OfferFilter {
|
||||||
|
private final User user;
|
||||||
|
private final Preferences preferences;
|
||||||
|
private final FilterManager filterManager;
|
||||||
|
private final AccountAgeWitnessService accountAgeWitnessService;
|
||||||
|
private final Map<String, Boolean> insufficientCounterpartyTradeLimitCache = new HashMap<>();
|
||||||
|
private final Map<String, Boolean> myInsufficientTradeLimitCache = new HashMap<>();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public OfferFilter(User user,
|
||||||
|
Preferences preferences,
|
||||||
|
FilterManager filterManager,
|
||||||
|
AccountAgeWitnessService accountAgeWitnessService) {
|
||||||
|
this.user = user;
|
||||||
|
this.preferences = preferences;
|
||||||
|
this.filterManager = filterManager;
|
||||||
|
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||||
|
|
||||||
|
if (user != null) {
|
||||||
|
// If our accounts have changed we reset our myInsufficientTradeLimitCache as it depends on account data
|
||||||
|
user.getPaymentAccountsAsObservable().addListener((SetChangeListener<PaymentAccount>) c ->
|
||||||
|
myInsufficientTradeLimitCache.clear());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Result {
|
||||||
|
VALID(true),
|
||||||
|
API_DISABLED,
|
||||||
|
HAS_NO_PAYMENT_ACCOUNT_VALID_FOR_OFFER,
|
||||||
|
HAS_NOT_SAME_PROTOCOL_VERSION,
|
||||||
|
IS_IGNORED,
|
||||||
|
IS_OFFER_BANNED,
|
||||||
|
IS_CURRENCY_BANNED,
|
||||||
|
IS_PAYMENT_METHOD_BANNED,
|
||||||
|
IS_NODE_ADDRESS_BANNED,
|
||||||
|
REQUIRE_UPDATE_TO_NEW_VERSION,
|
||||||
|
IS_INSUFFICIENT_COUNTERPARTY_TRADE_LIMIT,
|
||||||
|
IS_MY_INSUFFICIENT_TRADE_LIMIT;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final boolean isValid;
|
||||||
|
|
||||||
|
Result(boolean isValid) {
|
||||||
|
this.isValid = isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result canTakeOffer(Offer offer, boolean isTakerApiUser) {
|
||||||
|
if (isTakerApiUser && filterManager.getFilter() != null && filterManager.getFilter().isDisableApi()) {
|
||||||
|
return Result.API_DISABLED;
|
||||||
|
}
|
||||||
|
if (!isAnyPaymentAccountValidForOffer(offer)) {
|
||||||
|
return Result.HAS_NO_PAYMENT_ACCOUNT_VALID_FOR_OFFER;
|
||||||
|
}
|
||||||
|
if (!hasSameProtocolVersion(offer)) {
|
||||||
|
return Result.HAS_NOT_SAME_PROTOCOL_VERSION;
|
||||||
|
}
|
||||||
|
if (isIgnored(offer)) {
|
||||||
|
return Result.IS_IGNORED;
|
||||||
|
}
|
||||||
|
if (isOfferBanned(offer)) {
|
||||||
|
return Result.IS_OFFER_BANNED;
|
||||||
|
}
|
||||||
|
if (isCurrencyBanned(offer)) {
|
||||||
|
return Result.IS_CURRENCY_BANNED;
|
||||||
|
}
|
||||||
|
if (isPaymentMethodBanned(offer)) {
|
||||||
|
return Result.IS_PAYMENT_METHOD_BANNED;
|
||||||
|
}
|
||||||
|
if (isNodeAddressBanned(offer)) {
|
||||||
|
return Result.IS_NODE_ADDRESS_BANNED;
|
||||||
|
}
|
||||||
|
if (requireUpdateToNewVersion()) {
|
||||||
|
return Result.REQUIRE_UPDATE_TO_NEW_VERSION;
|
||||||
|
}
|
||||||
|
if (isInsufficientCounterpartyTradeLimit(offer)) {
|
||||||
|
return Result.IS_INSUFFICIENT_COUNTERPARTY_TRADE_LIMIT;
|
||||||
|
}
|
||||||
|
if (isMyInsufficientTradeLimit(offer)) {
|
||||||
|
return Result.IS_MY_INSUFFICIENT_TRADE_LIMIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.VALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAnyPaymentAccountValidForOffer(Offer offer) {
|
||||||
|
return user.getPaymentAccounts() != null &&
|
||||||
|
PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer(offer, user.getPaymentAccounts());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSameProtocolVersion(Offer offer) {
|
||||||
|
return offer.getProtocolVersion() == Version.TRADE_PROTOCOL_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIgnored(Offer offer) {
|
||||||
|
return preferences.getIgnoreTradersList().stream()
|
||||||
|
.anyMatch(i -> i.equals(offer.getMakerNodeAddress().getFullAddress()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOfferBanned(Offer offer) {
|
||||||
|
return filterManager.isOfferIdBanned(offer.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCurrencyBanned(Offer offer) {
|
||||||
|
return filterManager.isCurrencyBanned(offer.getCurrencyCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPaymentMethodBanned(Offer offer) {
|
||||||
|
return filterManager.isPaymentMethodBanned(offer.getPaymentMethod());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNodeAddressBanned(Offer offer) {
|
||||||
|
return filterManager.isNodeAddressBanned(offer.getMakerNodeAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean requireUpdateToNewVersion() {
|
||||||
|
return filterManager.requireUpdateToNewVersionForTrading();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This call is a bit expensive so we cache results
|
||||||
|
public boolean isInsufficientCounterpartyTradeLimit(Offer offer) {
|
||||||
|
String offerId = offer.getId();
|
||||||
|
if (insufficientCounterpartyTradeLimitCache.containsKey(offerId)) {
|
||||||
|
return insufficientCounterpartyTradeLimitCache.get(offerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean result = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
|
||||||
|
!accountAgeWitnessService.verifyPeersTradeAmount(offer, offer.getAmount(),
|
||||||
|
errorMessage -> {
|
||||||
|
});
|
||||||
|
insufficientCounterpartyTradeLimitCache.put(offerId, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This call is a bit expensive so we cache results
|
||||||
|
public boolean isMyInsufficientTradeLimit(Offer offer) {
|
||||||
|
String offerId = offer.getId();
|
||||||
|
if (myInsufficientTradeLimitCache.containsKey(offerId)) {
|
||||||
|
return myInsufficientTradeLimitCache.get(offerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<PaymentAccount> accountOptional = PaymentAccountUtil.getMostMaturePaymentAccountForOffer(offer,
|
||||||
|
user.getPaymentAccounts(),
|
||||||
|
accountAgeWitnessService);
|
||||||
|
long myTradeLimit = accountOptional
|
||||||
|
.map(paymentAccount -> accountAgeWitnessService.getMyTradeLimit(paymentAccount,
|
||||||
|
offer.getCurrencyCode(), offer.getMirroredDirection()))
|
||||||
|
.orElse(0L);
|
||||||
|
long offerMinAmount = offer.getMinAmount().value;
|
||||||
|
log.debug("isInsufficientTradeLimit accountOptional={}, myTradeLimit={}, offerMinAmount={}, ",
|
||||||
|
accountOptional.isPresent() ? accountOptional.get().getAccountName() : "null",
|
||||||
|
Coin.valueOf(myTradeLimit).toFriendlyString(),
|
||||||
|
Coin.valueOf(offerMinAmount).toFriendlyString());
|
||||||
|
boolean result = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
|
||||||
|
accountOptional.isPresent() &&
|
||||||
|
myTradeLimit < offerMinAmount;
|
||||||
|
myInsufficientTradeLimitCache.put(offerId, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -634,47 +634,50 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
NodeAddress refundAgentNodeAddress = null;
|
NodeAddress refundAgentNodeAddress = null;
|
||||||
if (openOfferOptional.isPresent()) {
|
if (openOfferOptional.isPresent()) {
|
||||||
OpenOffer openOffer = openOfferOptional.get();
|
OpenOffer openOffer = openOfferOptional.get();
|
||||||
if (openOffer.getState() == OpenOffer.State.AVAILABLE) {
|
if (!apiUserDeniedByOffer(request)) {
|
||||||
Offer offer = openOffer.getOffer();
|
if (openOffer.getState() == OpenOffer.State.AVAILABLE) {
|
||||||
if (preferences.getIgnoreTradersList().stream().noneMatch(fullAddress -> fullAddress.equals(peer.getFullAddress()))) {
|
Offer offer = openOffer.getOffer();
|
||||||
availabilityResult = AvailabilityResult.AVAILABLE;
|
if (preferences.getIgnoreTradersList().stream().noneMatch(fullAddress -> fullAddress.equals(peer.getFullAddress()))) {
|
||||||
|
mediatorNodeAddress = DisputeAgentSelection.getLeastUsedMediator(tradeStatisticsManager, mediatorManager).getNodeAddress();
|
||||||
|
openOffer.setMediatorNodeAddress(mediatorNodeAddress);
|
||||||
|
|
||||||
mediatorNodeAddress = DisputeAgentSelection.getLeastUsedMediator(tradeStatisticsManager, mediatorManager).getNodeAddress();
|
refundAgentNodeAddress = DisputeAgentSelection.getLeastUsedRefundAgent(tradeStatisticsManager, refundAgentManager).getNodeAddress();
|
||||||
openOffer.setMediatorNodeAddress(mediatorNodeAddress);
|
openOffer.setRefundAgentNodeAddress(refundAgentNodeAddress);
|
||||||
|
|
||||||
refundAgentNodeAddress = DisputeAgentSelection.getLeastUsedRefundAgent(tradeStatisticsManager, refundAgentManager).getNodeAddress();
|
try {
|
||||||
openOffer.setRefundAgentNodeAddress(refundAgentNodeAddress);
|
// Check also tradePrice to avoid failures after taker fee is paid caused by a too big difference
|
||||||
|
// in trade price between the peers. Also here poor connectivity might cause market price API connection
|
||||||
try {
|
// losses and therefore an outdated market price.
|
||||||
// Check also tradePrice to avoid failures after taker fee is paid caused by a too big difference
|
offer.checkTradePriceTolerance(request.getTakersTradePrice());
|
||||||
// in trade price between the peers. Also here poor connectivity might cause market price API connection
|
availabilityResult = AvailabilityResult.AVAILABLE;
|
||||||
// losses and therefore an outdated market price.
|
} catch (TradePriceOutOfToleranceException e) {
|
||||||
offer.checkTradePriceTolerance(request.getTakersTradePrice());
|
log.warn("Trade price check failed because takers price is outside out tolerance.");
|
||||||
} catch (TradePriceOutOfToleranceException e) {
|
availabilityResult = AvailabilityResult.PRICE_OUT_OF_TOLERANCE;
|
||||||
log.warn("Trade price check failed because takers price is outside out tolerance.");
|
} catch (MarketPriceNotAvailableException e) {
|
||||||
availabilityResult = AvailabilityResult.PRICE_OUT_OF_TOLERANCE;
|
log.warn(e.getMessage());
|
||||||
} catch (MarketPriceNotAvailableException e) {
|
availabilityResult = AvailabilityResult.MARKET_PRICE_NOT_AVAILABLE;
|
||||||
log.warn(e.getMessage());
|
} catch (Throwable e) {
|
||||||
availabilityResult = AvailabilityResult.MARKET_PRICE_NOT_AVAILABLE;
|
log.warn("Trade price check failed. " + e.getMessage());
|
||||||
} catch (Throwable e) {
|
availabilityResult = AvailabilityResult.UNKNOWN_FAILURE;
|
||||||
log.warn("Trade price check failed. " + e.getMessage());
|
}
|
||||||
availabilityResult = AvailabilityResult.UNKNOWN_FAILURE;
|
} else {
|
||||||
|
availabilityResult = AvailabilityResult.USER_IGNORED;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
availabilityResult = AvailabilityResult.USER_IGNORED;
|
availabilityResult = AvailabilityResult.OFFER_TAKEN;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
availabilityResult = AvailabilityResult.OFFER_TAKEN;
|
availabilityResult = AvailabilityResult.MAKER_DENIED_API_USER;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.warn("handleOfferAvailabilityRequest: openOffer not found. That should never happen.");
|
log.warn("handleOfferAvailabilityRequest: openOffer not found.");
|
||||||
availabilityResult = AvailabilityResult.OFFER_TAKEN;
|
availabilityResult = AvailabilityResult.OFFER_TAKEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (btcWalletService.isUnconfirmedTransactionsLimitHit() || bsqWalletService.isUnconfirmedTransactionsLimitHit()) {
|
if (btcWalletService.isUnconfirmedTransactionsLimitHit() || bsqWalletService.isUnconfirmedTransactionsLimitHit()) {
|
||||||
errorMessage = Res.get("shared.unconfirmedTransactionsLimitReached");
|
errorMessage = Res.get("shared.unconfirmedTransactionsLimitReached");
|
||||||
log.warn(errorMessage);
|
log.warn(errorMessage);
|
||||||
availabilityResult = AvailabilityResult.UNKNOWN_FAILURE;
|
availabilityResult = AvailabilityResult.UNCONF_TX_LIMIT_HIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
OfferAvailabilityResponse offerAvailabilityResponse = new OfferAvailabilityResponse(request.offerId,
|
OfferAvailabilityResponse offerAvailabilityResponse = new OfferAvailabilityResponse(request.offerId,
|
||||||
|
@ -716,6 +719,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean apiUserDeniedByOffer(OfferAvailabilityRequest request) {
|
||||||
|
return preferences.isDenyApiTaker() && request.isTakerApiUser();
|
||||||
|
}
|
||||||
|
|
||||||
private void sendAckMessage(OfferAvailabilityRequest message,
|
private void sendAckMessage(OfferAvailabilityRequest message,
|
||||||
NodeAddress sender,
|
NodeAddress sender,
|
||||||
boolean result,
|
boolean result,
|
||||||
|
|
|
@ -66,19 +66,24 @@ public class OfferAvailabilityModel implements Model {
|
||||||
@Getter
|
@Getter
|
||||||
private NodeAddress selectedRefundAgent;
|
private NodeAddress selectedRefundAgent;
|
||||||
|
|
||||||
|
// Added in v1.5.5
|
||||||
|
@Getter
|
||||||
|
private final boolean isTakerApiUser;
|
||||||
|
|
||||||
public OfferAvailabilityModel(Offer offer,
|
public OfferAvailabilityModel(Offer offer,
|
||||||
PubKeyRing pubKeyRing,
|
PubKeyRing pubKeyRing,
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
User user,
|
User user,
|
||||||
MediatorManager mediatorManager,
|
MediatorManager mediatorManager,
|
||||||
TradeStatisticsManager tradeStatisticsManager) {
|
TradeStatisticsManager tradeStatisticsManager,
|
||||||
|
boolean isTakerApiUser) {
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
this.pubKeyRing = pubKeyRing;
|
this.pubKeyRing = pubKeyRing;
|
||||||
this.p2PService = p2PService;
|
this.p2PService = p2PService;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.mediatorManager = mediatorManager;
|
this.mediatorManager = mediatorManager;
|
||||||
this.tradeStatisticsManager = tradeStatisticsManager;
|
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||||
|
this.isTakerApiUser = isTakerApiUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NodeAddress getPeerNodeAddress() {
|
public NodeAddress getPeerNodeAddress() {
|
||||||
|
|
|
@ -39,7 +39,8 @@ public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
OfferAvailabilityRequest message = new OfferAvailabilityRequest(model.getOffer().getId(), model.getPubKeyRing(), model.getTakersTradePrice());
|
OfferAvailabilityRequest message = new OfferAvailabilityRequest(model.getOffer().getId(),
|
||||||
|
model.getPubKeyRing(), model.getTakersTradePrice(), model.isTakerApiUser());
|
||||||
log.info("Send {} with offerId {} and uid {} to peer {}",
|
log.info("Send {} with offerId {} and uid {} to peer {}",
|
||||||
message.getClass().getSimpleName(), message.getOfferId(),
|
message.getClass().getSimpleName(), message.getOfferId(),
|
||||||
message.getUid(), model.getPeerNodeAddress());
|
message.getUid(), model.getPeerNodeAddress());
|
||||||
|
|
|
@ -42,13 +42,16 @@ public final class OfferAvailabilityRequest extends OfferMessage implements Supp
|
||||||
private final long takersTradePrice;
|
private final long takersTradePrice;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final Capabilities supportedCapabilities;
|
private final Capabilities supportedCapabilities;
|
||||||
|
private final boolean isTakerApiUser;
|
||||||
|
|
||||||
public OfferAvailabilityRequest(String offerId,
|
public OfferAvailabilityRequest(String offerId,
|
||||||
PubKeyRing pubKeyRing,
|
PubKeyRing pubKeyRing,
|
||||||
long takersTradePrice) {
|
long takersTradePrice,
|
||||||
|
boolean isTakerApiUser) {
|
||||||
this(offerId,
|
this(offerId,
|
||||||
pubKeyRing,
|
pubKeyRing,
|
||||||
takersTradePrice,
|
takersTradePrice,
|
||||||
|
isTakerApiUser,
|
||||||
Capabilities.app,
|
Capabilities.app,
|
||||||
Version.getP2PMessageVersion(),
|
Version.getP2PMessageVersion(),
|
||||||
UUID.randomUUID().toString());
|
UUID.randomUUID().toString());
|
||||||
|
@ -62,12 +65,14 @@ public final class OfferAvailabilityRequest extends OfferMessage implements Supp
|
||||||
private OfferAvailabilityRequest(String offerId,
|
private OfferAvailabilityRequest(String offerId,
|
||||||
PubKeyRing pubKeyRing,
|
PubKeyRing pubKeyRing,
|
||||||
long takersTradePrice,
|
long takersTradePrice,
|
||||||
|
boolean isTakerApiUser,
|
||||||
@Nullable Capabilities supportedCapabilities,
|
@Nullable Capabilities supportedCapabilities,
|
||||||
int messageVersion,
|
int messageVersion,
|
||||||
@Nullable String uid) {
|
@Nullable String uid) {
|
||||||
super(messageVersion, offerId, uid);
|
super(messageVersion, offerId, uid);
|
||||||
this.pubKeyRing = pubKeyRing;
|
this.pubKeyRing = pubKeyRing;
|
||||||
this.takersTradePrice = takersTradePrice;
|
this.takersTradePrice = takersTradePrice;
|
||||||
|
this.isTakerApiUser = isTakerApiUser;
|
||||||
this.supportedCapabilities = supportedCapabilities;
|
this.supportedCapabilities = supportedCapabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +81,8 @@ public final class OfferAvailabilityRequest extends OfferMessage implements Supp
|
||||||
final protobuf.OfferAvailabilityRequest.Builder builder = protobuf.OfferAvailabilityRequest.newBuilder()
|
final protobuf.OfferAvailabilityRequest.Builder builder = protobuf.OfferAvailabilityRequest.newBuilder()
|
||||||
.setOfferId(offerId)
|
.setOfferId(offerId)
|
||||||
.setPubKeyRing(pubKeyRing.toProtoMessage())
|
.setPubKeyRing(pubKeyRing.toProtoMessage())
|
||||||
.setTakersTradePrice(takersTradePrice);
|
.setTakersTradePrice(takersTradePrice)
|
||||||
|
.setIsTakerApiUser(isTakerApiUser);
|
||||||
|
|
||||||
Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities)));
|
Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities)));
|
||||||
Optional.ofNullable(uid).ifPresent(e -> builder.setUid(uid));
|
Optional.ofNullable(uid).ifPresent(e -> builder.setUid(uid));
|
||||||
|
@ -90,6 +96,7 @@ public final class OfferAvailabilityRequest extends OfferMessage implements Supp
|
||||||
return new OfferAvailabilityRequest(proto.getOfferId(),
|
return new OfferAvailabilityRequest(proto.getOfferId(),
|
||||||
PubKeyRing.fromProto(proto.getPubKeyRing()),
|
PubKeyRing.fromProto(proto.getPubKeyRing()),
|
||||||
proto.getTakersTradePrice(),
|
proto.getTakersTradePrice(),
|
||||||
|
proto.getIsTakerApiUser(),
|
||||||
Capabilities.fromIntList(proto.getSupportedCapabilitiesList()),
|
Capabilities.fromIntList(proto.getSupportedCapabilitiesList()),
|
||||||
messageVersion,
|
messageVersion,
|
||||||
proto.getUid().isEmpty() ? null : proto.getUid());
|
proto.getUid().isEmpty() ? null : proto.getUid());
|
||||||
|
|
|
@ -373,6 +373,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void checkOfferAvailability(Offer offer,
|
public void checkOfferAvailability(Offer offer,
|
||||||
|
boolean isTakerApiUser,
|
||||||
ResultHandler resultHandler,
|
ResultHandler resultHandler,
|
||||||
ErrorMessageHandler errorMessageHandler) {
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
if (btcWalletService.isUnconfirmedTransactionsLimitHit() ||
|
if (btcWalletService.isUnconfirmedTransactionsLimitHit() ||
|
||||||
|
@ -383,7 +384,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
offer.checkOfferAvailability(getOfferAvailabilityModel(offer), resultHandler, errorMessageHandler);
|
offer.checkOfferAvailability(getOfferAvailabilityModel(offer, isTakerApiUser), resultHandler, errorMessageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First we check if offer is still available then we create the trade with the protocol
|
// First we check if offer is still available then we create the trade with the protocol
|
||||||
|
@ -396,12 +397,13 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
Offer offer,
|
Offer offer,
|
||||||
String paymentAccountId,
|
String paymentAccountId,
|
||||||
boolean useSavingsWallet,
|
boolean useSavingsWallet,
|
||||||
|
boolean isTakerApiUser,
|
||||||
TradeResultHandler tradeResultHandler,
|
TradeResultHandler tradeResultHandler,
|
||||||
ErrorMessageHandler errorMessageHandler) {
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
|
|
||||||
checkArgument(!wasOfferAlreadyUsedInTrade(offer.getId()));
|
checkArgument(!wasOfferAlreadyUsedInTrade(offer.getId()));
|
||||||
|
|
||||||
OfferAvailabilityModel model = getOfferAvailabilityModel(offer);
|
OfferAvailabilityModel model = getOfferAvailabilityModel(offer, isTakerApiUser);
|
||||||
offer.checkOfferAvailability(model,
|
offer.checkOfferAvailability(model,
|
||||||
() -> {
|
() -> {
|
||||||
if (offer.getState() == Offer.State.AVAILABLE) {
|
if (offer.getState() == Offer.State.AVAILABLE) {
|
||||||
|
@ -464,14 +466,15 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
processModelServiceProvider.getKeyRing().getPubKeyRing());
|
processModelServiceProvider.getKeyRing().getPubKeyRing());
|
||||||
}
|
}
|
||||||
|
|
||||||
private OfferAvailabilityModel getOfferAvailabilityModel(Offer offer) {
|
private OfferAvailabilityModel getOfferAvailabilityModel(Offer offer, boolean isTakerApiUser) {
|
||||||
return new OfferAvailabilityModel(
|
return new OfferAvailabilityModel(
|
||||||
offer,
|
offer,
|
||||||
keyRing.getPubKeyRing(),
|
keyRing.getPubKeyRing(),
|
||||||
p2PService,
|
p2PService,
|
||||||
user,
|
user,
|
||||||
mediatorManager,
|
mediatorManager,
|
||||||
tradeStatisticsManager);
|
tradeStatisticsManager,
|
||||||
|
isTakerApiUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1223,6 +1223,7 @@ setting.preferences.useAnimations=Use animations
|
||||||
setting.preferences.useDarkMode=Use dark mode
|
setting.preferences.useDarkMode=Use dark mode
|
||||||
setting.preferences.sortWithNumOffers=Sort market lists with no. of offers/trades
|
setting.preferences.sortWithNumOffers=Sort market lists with no. of offers/trades
|
||||||
setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods
|
setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods
|
||||||
|
setting.preferences.denyApiTaker=Deny takers using the API
|
||||||
setting.preferences.resetAllFlags=Reset all \"Don't show again\" flags
|
setting.preferences.resetAllFlags=Reset all \"Don't show again\" flags
|
||||||
settings.preferences.languageChange=To apply the language change to all screens requires a restart.
|
settings.preferences.languageChange=To apply the language change to all screens requires a restart.
|
||||||
settings.preferences.supportLanguageWarning=In case of a dispute, please note that mediation is handled in {0} and arbitration in {1}.
|
settings.preferences.supportLanguageWarning=In case of a dispute, please note that mediation is handled in {0} and arbitration in {1}.
|
||||||
|
@ -2620,6 +2621,7 @@ filterWindow.disableTradeBelowVersion=Min. version required for trading
|
||||||
filterWindow.add=Add filter
|
filterWindow.add=Add filter
|
||||||
filterWindow.remove=Remove filter
|
filterWindow.remove=Remove filter
|
||||||
filterWindow.btcFeeReceiverAddresses=BTC fee receiver addresses
|
filterWindow.btcFeeReceiverAddresses=BTC fee receiver addresses
|
||||||
|
filterWindow.disableApi=Disable API
|
||||||
|
|
||||||
offerDetailsWindow.minBtcAmount=Min. BTC amount
|
offerDetailsWindow.minBtcAmount=Min. BTC amount
|
||||||
offerDetailsWindow.min=(min. {0})
|
offerDetailsWindow.min=(min. {0})
|
||||||
|
|
|
@ -236,7 +236,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
if (DevEnv.isDevMode()) {
|
if (DevEnv.isDevMode()) {
|
||||||
UserThread.runAfter(() -> {
|
UserThread.runAfter(() -> {
|
||||||
amount.set("0.001");
|
amount.set("0.001");
|
||||||
price.set("70000");
|
price.set("210000");
|
||||||
minAmount.set(amount.get());
|
minAmount.set(amount.get());
|
||||||
onFocusOutPriceAsPercentageTextField(true, false);
|
onFocusOutPriceAsPercentageTextField(true, false);
|
||||||
applyMakerFee();
|
applyMakerFee();
|
||||||
|
|
|
@ -52,6 +52,7 @@ import bisq.core.locale.Res;
|
||||||
import bisq.core.locale.TradeCurrency;
|
import bisq.core.locale.TradeCurrency;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
|
import bisq.core.offer.OfferFilter;
|
||||||
import bisq.core.offer.OfferPayload;
|
import bisq.core.offer.OfferPayload;
|
||||||
import bisq.core.offer.OfferRestrictions;
|
import bisq.core.offer.OfferRestrictions;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
|
@ -62,6 +63,7 @@ import bisq.core.util.coin.CoinFormatter;
|
||||||
|
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
|
|
||||||
|
import bisq.common.app.DevEnv;
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
import bisq.common.util.Tuple3;
|
import bisq.common.util.Tuple3;
|
||||||
|
|
||||||
|
@ -617,51 +619,61 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onShowInfo(Offer offer,
|
private void onShowInfo(Offer offer, OfferFilter.Result result) {
|
||||||
boolean isPaymentAccountValidForOffer,
|
switch (result) {
|
||||||
boolean isInsufficientCounterpartyTradeLimit,
|
case VALID:
|
||||||
boolean hasSameProtocolVersion,
|
break;
|
||||||
boolean isIgnored,
|
case API_DISABLED:
|
||||||
boolean isOfferBanned,
|
DevEnv.logErrorAndThrowIfDevMode("We are in desktop and in the taker position " +
|
||||||
boolean isCurrencyBanned,
|
"viewing offers, so it cannot be that we got that result as we are not an API user.");
|
||||||
boolean isPaymentMethodBanned,
|
break;
|
||||||
boolean isNodeAddressBanned,
|
case HAS_NO_PAYMENT_ACCOUNT_VALID_FOR_OFFER:
|
||||||
boolean requireUpdateToNewVersion,
|
openPopupForMissingAccountSetup(Res.get("offerbook.warning.noMatchingAccount.headline"),
|
||||||
boolean isInsufficientTradeLimit) {
|
Res.get("offerbook.warning.noMatchingAccount.msg"),
|
||||||
if (!isPaymentAccountValidForOffer) {
|
FiatAccountsView.class,
|
||||||
openPopupForMissingAccountSetup(Res.get("offerbook.warning.noMatchingAccount.headline"),
|
"navigation.account");
|
||||||
Res.get("offerbook.warning.noMatchingAccount.msg"),
|
break;
|
||||||
FiatAccountsView.class,
|
case HAS_NOT_SAME_PROTOCOL_VERSION:
|
||||||
"navigation.account");
|
new Popup().warning(Res.get("offerbook.warning.wrongTradeProtocol")).show();
|
||||||
} else if (isInsufficientCounterpartyTradeLimit) {
|
break;
|
||||||
new Popup().warning(Res.get("offerbook.warning.counterpartyTradeRestrictions")).show();
|
case IS_IGNORED:
|
||||||
} else if (!hasSameProtocolVersion) {
|
new Popup().warning(Res.get("offerbook.warning.userIgnored")).show();
|
||||||
new Popup().warning(Res.get("offerbook.warning.wrongTradeProtocol")).show();
|
break;
|
||||||
} else if (isIgnored) {
|
case IS_OFFER_BANNED:
|
||||||
new Popup().warning(Res.get("offerbook.warning.userIgnored")).show();
|
new Popup().warning(Res.get("offerbook.warning.offerBlocked")).show();
|
||||||
} else if (isOfferBanned) {
|
break;
|
||||||
new Popup().warning(Res.get("offerbook.warning.offerBlocked")).show();
|
case IS_CURRENCY_BANNED:
|
||||||
} else if (isCurrencyBanned) {
|
new Popup().warning(Res.get("offerbook.warning.currencyBanned")).show();
|
||||||
new Popup().warning(Res.get("offerbook.warning.currencyBanned")).show();
|
break;
|
||||||
} else if (isPaymentMethodBanned) {
|
case IS_PAYMENT_METHOD_BANNED:
|
||||||
new Popup().warning(Res.get("offerbook.warning.paymentMethodBanned")).show();
|
new Popup().warning(Res.get("offerbook.warning.paymentMethodBanned")).show();
|
||||||
} else if (isNodeAddressBanned) {
|
break;
|
||||||
new Popup().warning(Res.get("offerbook.warning.nodeBlocked")).show();
|
case IS_NODE_ADDRESS_BANNED:
|
||||||
} else if (requireUpdateToNewVersion) {
|
new Popup().warning(Res.get("offerbook.warning.nodeBlocked")).show();
|
||||||
new Popup().warning(Res.get("offerbook.warning.requireUpdateToNewVersion")).show();
|
break;
|
||||||
} else if (isInsufficientTradeLimit) {
|
case REQUIRE_UPDATE_TO_NEW_VERSION:
|
||||||
final Optional<PaymentAccount> account = model.getMostMaturePaymentAccountForOffer(offer);
|
new Popup().warning(Res.get("offerbook.warning.requireUpdateToNewVersion")).show();
|
||||||
if (account.isPresent()) {
|
break;
|
||||||
final long tradeLimit = model.accountAgeWitnessService.getMyTradeLimit(account.get(),
|
case IS_INSUFFICIENT_COUNTERPARTY_TRADE_LIMIT:
|
||||||
offer.getCurrencyCode(), offer.getMirroredDirection());
|
new Popup().warning(Res.get("offerbook.warning.counterpartyTradeRestrictions")).show();
|
||||||
new Popup()
|
break;
|
||||||
.warning(Res.get("popup.warning.tradeLimitDueAccountAgeRestriction.buyer",
|
case IS_MY_INSUFFICIENT_TRADE_LIMIT:
|
||||||
formatter.formatCoinWithCode(Coin.valueOf(tradeLimit)),
|
Optional<PaymentAccount> account = model.getMostMaturePaymentAccountForOffer(offer);
|
||||||
Res.get("offerbook.warning.newVersionAnnouncement")))
|
if (account.isPresent()) {
|
||||||
.show();
|
long tradeLimit = model.accountAgeWitnessService.getMyTradeLimit(account.get(),
|
||||||
} else {
|
offer.getCurrencyCode(), offer.getMirroredDirection());
|
||||||
log.warn("We don't found a payment account but got called the isInsufficientTradeLimit case. That must not happen.");
|
new Popup()
|
||||||
}
|
.warning(Res.get("popup.warning.tradeLimitDueAccountAgeRestriction.buyer",
|
||||||
|
formatter.formatCoinWithCode(Coin.valueOf(tradeLimit)),
|
||||||
|
Res.get("offerbook.warning.newVersionAnnouncement")))
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
DevEnv.logErrorAndThrowIfDevMode("We don't found a payment account but got called the " +
|
||||||
|
"isInsufficientTradeLimit case.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1015,11 +1027,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
return new TableCell<>() {
|
return new TableCell<>() {
|
||||||
final ImageView iconView = new ImageView();
|
final ImageView iconView = new ImageView();
|
||||||
final AutoTooltipButton button = new AutoTooltipButton();
|
final AutoTooltipButton button = new AutoTooltipButton();
|
||||||
boolean isTradable, isPaymentAccountValidForOffer,
|
OfferFilter.Result canTakeOfferResult = null;
|
||||||
isInsufficientCounterpartyTradeLimit,
|
|
||||||
hasSameProtocolVersion, isIgnored, isOfferBanned, isCurrencyBanned,
|
|
||||||
isPaymentMethodBanned, isNodeAddressBanned, isMyInsufficientTradeLimit,
|
|
||||||
requireUpdateToNewVersion;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
button.setGraphic(iconView);
|
button.setGraphic(iconView);
|
||||||
|
@ -1034,37 +1042,14 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
|
|
||||||
TableRow<OfferBookListItem> tableRow = getTableRow();
|
TableRow<OfferBookListItem> tableRow = getTableRow();
|
||||||
if (item != null && !empty) {
|
if (item != null && !empty) {
|
||||||
final Offer offer = item.getOffer();
|
Offer offer = item.getOffer();
|
||||||
boolean myOffer = model.isMyOffer(offer);
|
boolean myOffer = model.isMyOffer(offer);
|
||||||
|
|
||||||
if (tableRow != null) {
|
if (tableRow != null) {
|
||||||
// this code is duplicated in model.getOffersMatchingMyAccountsPredicate but as
|
canTakeOfferResult = model.offerFilter.canTakeOffer(offer, false);
|
||||||
// we want to pass the results for displaying relevant info in popups we
|
tableRow.setOpacity(canTakeOfferResult.isValid() || myOffer ? 1 : 0.4);
|
||||||
// cannot simply replace it with the predicate. If there are any changes we
|
|
||||||
// need to maintain both.
|
|
||||||
isPaymentAccountValidForOffer = model.isAnyPaymentAccountValidForOffer(offer);
|
|
||||||
isInsufficientCounterpartyTradeLimit = model.isInsufficientCounterpartyTradeLimit(offer);
|
|
||||||
hasSameProtocolVersion = model.hasSameProtocolVersion(offer);
|
|
||||||
isIgnored = model.isIgnored(offer);
|
|
||||||
isOfferBanned = model.isOfferBanned(offer);
|
|
||||||
isCurrencyBanned = model.isCurrencyBanned(offer);
|
|
||||||
isPaymentMethodBanned = model.isPaymentMethodBanned(offer);
|
|
||||||
isNodeAddressBanned = model.isNodeAddressBanned(offer);
|
|
||||||
requireUpdateToNewVersion = model.requireUpdateToNewVersion();
|
|
||||||
isMyInsufficientTradeLimit = model.isMyInsufficientTradeLimit(offer);
|
|
||||||
isTradable = isPaymentAccountValidForOffer &&
|
|
||||||
!isInsufficientCounterpartyTradeLimit &&
|
|
||||||
hasSameProtocolVersion &&
|
|
||||||
!isIgnored &&
|
|
||||||
!isOfferBanned &&
|
|
||||||
!isCurrencyBanned &&
|
|
||||||
!isPaymentMethodBanned &&
|
|
||||||
!isNodeAddressBanned &&
|
|
||||||
!requireUpdateToNewVersion &&
|
|
||||||
!isMyInsufficientTradeLimit;
|
|
||||||
|
|
||||||
tableRow.setOpacity(isTradable || myOffer ? 1 : 0.4);
|
if (canTakeOfferResult.isValid()) {
|
||||||
|
|
||||||
if (isTradable) {
|
|
||||||
// set first row button as default
|
// set first row button as default
|
||||||
button.setDefaultButton(getIndex() == 0);
|
button.setDefaultButton(getIndex() == 0);
|
||||||
tableRow.setOnMousePressed(null);
|
tableRow.setOnMousePressed(null);
|
||||||
|
@ -1073,17 +1058,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
tableRow.setOnMousePressed(e -> {
|
tableRow.setOnMousePressed(e -> {
|
||||||
// ugly hack to get the icon clickable when deactivated
|
// ugly hack to get the icon clickable when deactivated
|
||||||
if (!(e.getTarget() instanceof ImageView || e.getTarget() instanceof Canvas))
|
if (!(e.getTarget() instanceof ImageView || e.getTarget() instanceof Canvas))
|
||||||
onShowInfo(offer,
|
onShowInfo(offer, canTakeOfferResult);
|
||||||
isPaymentAccountValidForOffer,
|
|
||||||
isInsufficientCounterpartyTradeLimit,
|
|
||||||
hasSameProtocolVersion,
|
|
||||||
isIgnored,
|
|
||||||
isOfferBanned,
|
|
||||||
isCurrencyBanned,
|
|
||||||
isPaymentMethodBanned,
|
|
||||||
isNodeAddressBanned,
|
|
||||||
requireUpdateToNewVersion,
|
|
||||||
isMyInsufficientTradeLimit);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1113,18 +1088,15 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
button.setOnAction(e -> onTakeOffer(offer));
|
button.setOnAction(e -> onTakeOffer(offer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!myOffer && !isTradable)
|
if (!myOffer) {
|
||||||
button.setOnAction(e -> onShowInfo(offer,
|
if (canTakeOfferResult == null) {
|
||||||
isPaymentAccountValidForOffer,
|
canTakeOfferResult = model.offerFilter.canTakeOffer(offer, false);
|
||||||
isInsufficientCounterpartyTradeLimit,
|
}
|
||||||
hasSameProtocolVersion,
|
|
||||||
isIgnored,
|
if (!canTakeOfferResult.isValid()) {
|
||||||
isOfferBanned,
|
button.setOnAction(e -> onShowInfo(offer, canTakeOfferResult));
|
||||||
isCurrencyBanned,
|
}
|
||||||
isPaymentMethodBanned,
|
}
|
||||||
isNodeAddressBanned,
|
|
||||||
requireUpdateToNewVersion,
|
|
||||||
isMyInsufficientTradeLimit));
|
|
||||||
|
|
||||||
button.updateText(title);
|
button.updateText(title);
|
||||||
setPadding(new Insets(0, 15, 0, 0));
|
setPadding(new Insets(0, 15, 0, 0));
|
||||||
|
|
|
@ -28,7 +28,6 @@ import bisq.desktop.util.GUIUtil;
|
||||||
|
|
||||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||||
import bisq.core.btc.setup.WalletsSetup;
|
import bisq.core.btc.setup.WalletsSetup;
|
||||||
import bisq.core.filter.FilterManager;
|
|
||||||
import bisq.core.locale.BankUtil;
|
import bisq.core.locale.BankUtil;
|
||||||
import bisq.core.locale.CountryUtil;
|
import bisq.core.locale.CountryUtil;
|
||||||
import bisq.core.locale.CryptoCurrency;
|
import bisq.core.locale.CryptoCurrency;
|
||||||
|
@ -39,6 +38,7 @@ import bisq.core.locale.TradeCurrency;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.monetary.Volume;
|
import bisq.core.monetary.Volume;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
|
import bisq.core.offer.OfferFilter;
|
||||||
import bisq.core.offer.OfferPayload;
|
import bisq.core.offer.OfferPayload;
|
||||||
import bisq.core.offer.OpenOfferManager;
|
import bisq.core.offer.OpenOfferManager;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
|
@ -56,7 +56,6 @@ import bisq.core.util.coin.CoinFormatter;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.network.p2p.P2PService;
|
import bisq.network.p2p.P2PService;
|
||||||
|
|
||||||
import bisq.common.app.Version;
|
|
||||||
import bisq.common.handlers.ErrorMessageHandler;
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
import bisq.common.handlers.ResultHandler;
|
import bisq.common.handlers.ResultHandler;
|
||||||
|
|
||||||
|
@ -78,14 +77,12 @@ import javafx.beans.property.StringProperty;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.collections.SetChangeListener;
|
|
||||||
import javafx.collections.transformation.FilteredList;
|
import javafx.collections.transformation.FilteredList;
|
||||||
import javafx.collections.transformation.SortedList;
|
import javafx.collections.transformation.SortedList;
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -105,10 +102,10 @@ class OfferBookViewModel extends ActivatableViewModel {
|
||||||
private final P2PService p2PService;
|
private final P2PService p2PService;
|
||||||
final PriceFeedService priceFeedService;
|
final PriceFeedService priceFeedService;
|
||||||
private final ClosedTradableManager closedTradableManager;
|
private final ClosedTradableManager closedTradableManager;
|
||||||
private final FilterManager filterManager;
|
|
||||||
final AccountAgeWitnessService accountAgeWitnessService;
|
final AccountAgeWitnessService accountAgeWitnessService;
|
||||||
private final Navigation navigation;
|
private final Navigation navigation;
|
||||||
private final PriceUtil priceUtil;
|
private final PriceUtil priceUtil;
|
||||||
|
final OfferFilter offerFilter;
|
||||||
private final CoinFormatter btcFormatter;
|
private final CoinFormatter btcFormatter;
|
||||||
private final BsqFormatter bsqFormatter;
|
private final BsqFormatter bsqFormatter;
|
||||||
|
|
||||||
|
@ -136,8 +133,6 @@ class OfferBookViewModel extends ActivatableViewModel {
|
||||||
final IntegerProperty maxPlacesForMarketPriceMargin = new SimpleIntegerProperty();
|
final IntegerProperty maxPlacesForMarketPriceMargin = new SimpleIntegerProperty();
|
||||||
boolean showAllPaymentMethods = true;
|
boolean showAllPaymentMethods = true;
|
||||||
boolean useOffersMatchingMyAccountsFilter;
|
boolean useOffersMatchingMyAccountsFilter;
|
||||||
private final Map<String, Boolean> myInsufficientTradeLimitCache = new HashMap<>();
|
|
||||||
private final Map<String, Boolean> insufficientCounterpartyTradeLimitCache = new HashMap<>();
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -153,10 +148,10 @@ class OfferBookViewModel extends ActivatableViewModel {
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
PriceFeedService priceFeedService,
|
PriceFeedService priceFeedService,
|
||||||
ClosedTradableManager closedTradableManager,
|
ClosedTradableManager closedTradableManager,
|
||||||
FilterManager filterManager,
|
|
||||||
AccountAgeWitnessService accountAgeWitnessService,
|
AccountAgeWitnessService accountAgeWitnessService,
|
||||||
Navigation navigation,
|
Navigation navigation,
|
||||||
PriceUtil priceUtil,
|
PriceUtil priceUtil,
|
||||||
|
OfferFilter offerFilter,
|
||||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
||||||
BsqFormatter bsqFormatter) {
|
BsqFormatter bsqFormatter) {
|
||||||
super();
|
super();
|
||||||
|
@ -169,10 +164,10 @@ class OfferBookViewModel extends ActivatableViewModel {
|
||||||
this.p2PService = p2PService;
|
this.p2PService = p2PService;
|
||||||
this.priceFeedService = priceFeedService;
|
this.priceFeedService = priceFeedService;
|
||||||
this.closedTradableManager = closedTradableManager;
|
this.closedTradableManager = closedTradableManager;
|
||||||
this.filterManager = filterManager;
|
|
||||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||||
this.navigation = navigation;
|
this.navigation = navigation;
|
||||||
this.priceUtil = priceUtil;
|
this.priceUtil = priceUtil;
|
||||||
|
this.offerFilter = offerFilter;
|
||||||
this.btcFormatter = btcFormatter;
|
this.btcFormatter = btcFormatter;
|
||||||
this.bsqFormatter = bsqFormatter;
|
this.bsqFormatter = bsqFormatter;
|
||||||
|
|
||||||
|
@ -213,12 +208,6 @@ class OfferBookViewModel extends ActivatableViewModel {
|
||||||
|
|
||||||
highestMarketPriceMarginOffer.ifPresent(offerBookListItem -> maxPlacesForMarketPriceMargin.set(formatMarketPriceMargin(offerBookListItem.getOffer(), false).length()));
|
highestMarketPriceMarginOffer.ifPresent(offerBookListItem -> maxPlacesForMarketPriceMargin.set(formatMarketPriceMargin(offerBookListItem.getOffer(), false).length()));
|
||||||
};
|
};
|
||||||
|
|
||||||
// If our accounts have changed we reset our myInsufficientTradeLimitCache as it depends on account data
|
|
||||||
if (user != null) {
|
|
||||||
user.getPaymentAccountsAsObservable().addListener((SetChangeListener<PaymentAccount>) c ->
|
|
||||||
myInsufficientTradeLimitCache.clear());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -568,11 +557,6 @@ class OfferBookViewModel extends ActivatableViewModel {
|
||||||
// Checks
|
// Checks
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
boolean isAnyPaymentAccountValidForOffer(Offer offer) {
|
|
||||||
return user.getPaymentAccounts() != null &&
|
|
||||||
PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer(offer, user.getPaymentAccounts());
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasPaymentAccountForCurrency() {
|
boolean hasPaymentAccountForCurrency() {
|
||||||
return (showAllTradeCurrenciesProperty.get() &&
|
return (showAllTradeCurrenciesProperty.get() &&
|
||||||
user.getPaymentAccounts() != null &&
|
user.getPaymentAccounts() != null &&
|
||||||
|
@ -615,98 +599,11 @@ class OfferBookViewModel extends ActivatableViewModel {
|
||||||
// This code duplicates code in the view at the button column. We need there the different results for
|
// This code duplicates code in the view at the button column. We need there the different results for
|
||||||
// display in popups so we cannot replace that with the predicate. Any change need to be applied in both
|
// display in popups so we cannot replace that with the predicate. Any change need to be applied in both
|
||||||
// places.
|
// places.
|
||||||
return offerBookListItem -> {
|
return offerBookListItem -> offerFilter.canTakeOffer(offerBookListItem.getOffer(), false).isValid();
|
||||||
Offer offer = offerBookListItem.getOffer();
|
|
||||||
boolean isPaymentAccountValidForOffer = isAnyPaymentAccountValidForOffer(offer);
|
|
||||||
boolean isInsufficientCounterpartyTradeLimit = isInsufficientCounterpartyTradeLimit(offer);
|
|
||||||
boolean hasSameProtocolVersion = hasSameProtocolVersion(offer);
|
|
||||||
boolean isIgnored = isIgnored(offer);
|
|
||||||
boolean isOfferBanned = isOfferBanned(offer);
|
|
||||||
boolean isCurrencyBanned = isCurrencyBanned(offer);
|
|
||||||
boolean isPaymentMethodBanned = isPaymentMethodBanned(offer);
|
|
||||||
boolean isNodeAddressBanned = isNodeAddressBanned(offer);
|
|
||||||
boolean requireUpdateToNewVersion = requireUpdateToNewVersion();
|
|
||||||
boolean isMyInsufficientTradeLimit = isMyInsufficientTradeLimit(offer);
|
|
||||||
boolean isTradable = isPaymentAccountValidForOffer &&
|
|
||||||
!isInsufficientCounterpartyTradeLimit &&
|
|
||||||
hasSameProtocolVersion &&
|
|
||||||
!isIgnored &&
|
|
||||||
!isOfferBanned &&
|
|
||||||
!isCurrencyBanned &&
|
|
||||||
!isPaymentMethodBanned &&
|
|
||||||
!isNodeAddressBanned &&
|
|
||||||
!requireUpdateToNewVersion &&
|
|
||||||
!isMyInsufficientTradeLimit;
|
|
||||||
return isTradable;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isIgnored(Offer offer) {
|
|
||||||
return preferences.getIgnoreTradersList().stream()
|
|
||||||
.anyMatch(i -> i.equals(offer.getMakerNodeAddress().getFullAddress()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isOfferBanned(Offer offer) {
|
boolean isOfferBanned(Offer offer) {
|
||||||
return filterManager.isOfferIdBanned(offer.getId());
|
return offerFilter.isOfferBanned(offer);
|
||||||
}
|
|
||||||
|
|
||||||
boolean isCurrencyBanned(Offer offer) {
|
|
||||||
return filterManager.isCurrencyBanned(offer.getCurrencyCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isPaymentMethodBanned(Offer offer) {
|
|
||||||
return filterManager.isPaymentMethodBanned(offer.getPaymentMethod());
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isNodeAddressBanned(Offer offer) {
|
|
||||||
return filterManager.isNodeAddressBanned(offer.getMakerNodeAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean requireUpdateToNewVersion() {
|
|
||||||
return filterManager.requireUpdateToNewVersionForTrading();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This call is a bit expensive so we cache results
|
|
||||||
boolean isInsufficientCounterpartyTradeLimit(Offer offer) {
|
|
||||||
String offerId = offer.getId();
|
|
||||||
if (insufficientCounterpartyTradeLimitCache.containsKey(offerId)) {
|
|
||||||
return insufficientCounterpartyTradeLimitCache.get(offerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean result = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
|
|
||||||
!accountAgeWitnessService.verifyPeersTradeAmount(offer, offer.getAmount(),
|
|
||||||
errorMessage -> {
|
|
||||||
});
|
|
||||||
insufficientCounterpartyTradeLimitCache.put(offerId, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This call is a bit expensive so we cache results
|
|
||||||
boolean isMyInsufficientTradeLimit(Offer offer) {
|
|
||||||
String offerId = offer.getId();
|
|
||||||
if (myInsufficientTradeLimitCache.containsKey(offerId)) {
|
|
||||||
return myInsufficientTradeLimitCache.get(offerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<PaymentAccount> accountOptional = getMostMaturePaymentAccountForOffer(offer);
|
|
||||||
long myTradeLimit = accountOptional
|
|
||||||
.map(paymentAccount -> accountAgeWitnessService.getMyTradeLimit(paymentAccount,
|
|
||||||
offer.getCurrencyCode(), offer.getMirroredDirection()))
|
|
||||||
.orElse(0L);
|
|
||||||
long offerMinAmount = offer.getMinAmount().value;
|
|
||||||
log.debug("isInsufficientTradeLimit accountOptional={}, myTradeLimit={}, offerMinAmount={}, ",
|
|
||||||
accountOptional.isPresent() ? accountOptional.get().getAccountName() : "null",
|
|
||||||
Coin.valueOf(myTradeLimit).toFriendlyString(),
|
|
||||||
Coin.valueOf(offerMinAmount).toFriendlyString());
|
|
||||||
boolean result = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
|
|
||||||
accountOptional.isPresent() &&
|
|
||||||
myTradeLimit < offerMinAmount;
|
|
||||||
myInsufficientTradeLimitCache.put(offerId, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasSameProtocolVersion(Offer offer) {
|
|
||||||
return offer.getProtocolVersion() == Version.TRADE_PROTOCOL_VERSION;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isShowAllEntry(String id) {
|
private boolean isShowAllEntry(String id) {
|
||||||
|
|
|
@ -171,6 +171,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
|
|
||||||
if (canTakeOffer()) {
|
if (canTakeOffer()) {
|
||||||
tradeManager.checkOfferAvailability(offer,
|
tradeManager.checkOfferAvailability(offer,
|
||||||
|
false,
|
||||||
() -> {
|
() -> {
|
||||||
},
|
},
|
||||||
errorMessage -> new Popup().warning(errorMessage).show());
|
errorMessage -> new Popup().warning(errorMessage).show());
|
||||||
|
@ -319,7 +320,8 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
offer,
|
offer,
|
||||||
paymentAccount.getId(),
|
paymentAccount.getId(),
|
||||||
useSavingsWallet,
|
useSavingsWallet,
|
||||||
tradeResultHandler::handleResult,
|
false,
|
||||||
|
tradeResultHandler,
|
||||||
errorMessage -> {
|
errorMessage -> {
|
||||||
log.warn(errorMessage);
|
log.warn(errorMessage);
|
||||||
new Popup().warning(errorMessage).show();
|
new Popup().warning(errorMessage).show();
|
||||||
|
|
|
@ -239,7 +239,7 @@ public class OfferBookViewModelTest {
|
||||||
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
||||||
|
|
||||||
final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null,
|
final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null,
|
||||||
null, null, null, null, getPriceUtil(), coinFormatter, new BsqFormatter());
|
null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter());
|
||||||
assertEquals(0, model.maxPlacesForAmount.intValue());
|
assertEquals(0, model.maxPlacesForAmount.intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +253,7 @@ public class OfferBookViewModelTest {
|
||||||
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
||||||
|
|
||||||
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
|
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
|
||||||
null, null, null, null, getPriceUtil(), coinFormatter, new BsqFormatter());
|
null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter());
|
||||||
model.activate();
|
model.activate();
|
||||||
|
|
||||||
assertEquals(6, model.maxPlacesForAmount.intValue());
|
assertEquals(6, model.maxPlacesForAmount.intValue());
|
||||||
|
@ -271,7 +271,7 @@ public class OfferBookViewModelTest {
|
||||||
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
||||||
|
|
||||||
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
|
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
|
||||||
null, null, null, null, getPriceUtil(), coinFormatter, new BsqFormatter());
|
null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter());
|
||||||
model.activate();
|
model.activate();
|
||||||
|
|
||||||
assertEquals(15, model.maxPlacesForAmount.intValue());
|
assertEquals(15, model.maxPlacesForAmount.intValue());
|
||||||
|
@ -290,7 +290,7 @@ public class OfferBookViewModelTest {
|
||||||
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
||||||
|
|
||||||
final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null,
|
final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null,
|
||||||
null, null, null, null, getPriceUtil(), coinFormatter, new BsqFormatter());
|
null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter());
|
||||||
assertEquals(0, model.maxPlacesForVolume.intValue());
|
assertEquals(0, model.maxPlacesForVolume.intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ public class OfferBookViewModelTest {
|
||||||
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
||||||
|
|
||||||
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
|
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
|
||||||
null, null, null, null, getPriceUtil(), coinFormatter, new BsqFormatter());
|
null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter());
|
||||||
model.activate();
|
model.activate();
|
||||||
|
|
||||||
assertEquals(5, model.maxPlacesForVolume.intValue());
|
assertEquals(5, model.maxPlacesForVolume.intValue());
|
||||||
|
@ -322,7 +322,7 @@ public class OfferBookViewModelTest {
|
||||||
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
||||||
|
|
||||||
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
|
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
|
||||||
null, null, null, null, getPriceUtil(), coinFormatter, new BsqFormatter());
|
null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter());
|
||||||
model.activate();
|
model.activate();
|
||||||
|
|
||||||
assertEquals(9, model.maxPlacesForVolume.intValue());
|
assertEquals(9, model.maxPlacesForVolume.intValue());
|
||||||
|
@ -341,7 +341,7 @@ public class OfferBookViewModelTest {
|
||||||
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
||||||
|
|
||||||
final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null,
|
final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null,
|
||||||
null, null, null, null, getPriceUtil(), coinFormatter, new BsqFormatter());
|
null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter());
|
||||||
assertEquals(0, model.maxPlacesForPrice.intValue());
|
assertEquals(0, model.maxPlacesForPrice.intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,7 +355,7 @@ public class OfferBookViewModelTest {
|
||||||
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
||||||
|
|
||||||
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
|
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
|
||||||
null, null, null, null, getPriceUtil(), coinFormatter, new BsqFormatter());
|
null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter());
|
||||||
model.activate();
|
model.activate();
|
||||||
|
|
||||||
assertEquals(7, model.maxPlacesForPrice.intValue());
|
assertEquals(7, model.maxPlacesForPrice.intValue());
|
||||||
|
@ -373,7 +373,7 @@ public class OfferBookViewModelTest {
|
||||||
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
|
||||||
|
|
||||||
final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null,
|
final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null,
|
||||||
null, null, null, null, getPriceUtil(), coinFormatter, new BsqFormatter());
|
null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter());
|
||||||
assertEquals(0, model.maxPlacesForMarketPriceMargin.intValue());
|
assertEquals(0, model.maxPlacesForMarketPriceMargin.intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,7 +401,7 @@ public class OfferBookViewModelTest {
|
||||||
offerBookListItems.addAll(item1, item2);
|
offerBookListItems.addAll(item1, item2);
|
||||||
|
|
||||||
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, priceFeedService,
|
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, priceFeedService,
|
||||||
null, null, null, null, getPriceUtil(), coinFormatter, new BsqFormatter());
|
null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter());
|
||||||
model.activate();
|
model.activate();
|
||||||
|
|
||||||
assertEquals(8, model.maxPlacesForMarketPriceMargin.intValue()); //" (1.97%)"
|
assertEquals(8, model.maxPlacesForMarketPriceMargin.intValue()); //" (1.97%)"
|
||||||
|
@ -422,7 +422,7 @@ public class OfferBookViewModelTest {
|
||||||
when(priceFeedService.getMarketPrice(anyString())).thenReturn(new MarketPrice("USD", 12684.0450, Instant.now().getEpochSecond(), true));
|
when(priceFeedService.getMarketPrice(anyString())).thenReturn(new MarketPrice("USD", 12684.0450, Instant.now().getEpochSecond(), true));
|
||||||
|
|
||||||
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
|
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
|
||||||
null, null, null, null, getPriceUtil(), coinFormatter, new BsqFormatter());
|
null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter());
|
||||||
|
|
||||||
final OfferBookListItem item = make(btcBuyItem.but(
|
final OfferBookListItem item = make(btcBuyItem.but(
|
||||||
with(useMarketBasedPrice, true),
|
with(useMarketBasedPrice, true),
|
||||||
|
|
|
@ -158,6 +158,7 @@ message OfferAvailabilityRequest {
|
||||||
int64 takers_trade_price = 3;
|
int64 takers_trade_price = 3;
|
||||||
repeated int32 supported_capabilities = 4;
|
repeated int32 supported_capabilities = 4;
|
||||||
string uid = 5;
|
string uid = 5;
|
||||||
|
bool is_taker_api_user = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message OfferAvailabilityResponse {
|
message OfferAvailabilityResponse {
|
||||||
|
@ -914,6 +915,8 @@ enum AvailabilityResult {
|
||||||
USER_IGNORED = 8;
|
USER_IGNORED = 8;
|
||||||
MISSING_MANDATORY_CAPABILITY = 9;
|
MISSING_MANDATORY_CAPABILITY = 9;
|
||||||
NO_REFUND_AGENTS = 10;
|
NO_REFUND_AGENTS = 10;
|
||||||
|
UNCONF_TX_LIMIT_HIT = 11;
|
||||||
|
MAKER_DENIED_API_USER = 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Loading…
Add table
Reference in a new issue