Skip to content

Commit

Permalink
Merge pull request bisq-network#7119 from HenrikJannsen/hide-offers-w…
Browse files Browse the repository at this point in the history
…hich-are-over-the-trade-limit

Hide offers which are over the trade limit
  • Loading branch information
alejandrogarcia83 authored May 29, 2024
2 parents 4a64ade + db3f05e commit 2b06a69
Show file tree
Hide file tree
Showing 12 changed files with 142 additions and 55 deletions.
4 changes: 4 additions & 0 deletions core/src/main/java/bisq/core/offer/OfferUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,10 @@ public static boolean isAltcoinOffer(Offer offer) {
return offer.getCounterCurrencyCode().equals("BTC") && !offer.isBsqSwapOffer();
}

public static boolean doesOfferAmountExceedTradeLimit(Offer offer) {
return offer.getAmount().isGreaterThan(offer.getPaymentMethod().getMaxTradeLimitAsCoin(offer.getCurrencyCode()));
}

public static Optional<String> getInvalidMakerFeeTxErrorMessage(Offer offer, BtcWalletService btcWalletService) {
String offerFeePaymentTxId = offer.getOfferFeePaymentTxId();
if (offerFeePaymentTxId == null) {
Expand Down
35 changes: 33 additions & 2 deletions core/src/main/java/bisq/core/offer/OpenOfferManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
import bisq.core.dao.DaoFacade;
import bisq.core.dao.burningman.BtcFeeReceiverService;
import bisq.core.dao.burningman.DelayedPayoutTxReceiverService;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.exceptions.TradePriceOutOfToleranceException;
import bisq.core.filter.FilterManager;
import bisq.core.locale.Res;
Expand Down Expand Up @@ -102,7 +105,7 @@
import static com.google.common.base.Preconditions.checkNotNull;

@Slf4j
public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMessageListener, PersistedDataHost {
public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMessageListener, PersistedDataHost, DaoStateListener {

private static final long RETRY_REPUBLISH_DELAY_SEC = 10;
private static final long REPUBLISH_AGAIN_AT_STARTUP_DELAY_SEC = 30;
Expand Down Expand Up @@ -131,6 +134,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
private final DelayedPayoutTxReceiverService delayedPayoutTxReceiverService;
private final Broadcaster broadcaster;
private final PersistenceManager<TradableList<OpenOffer>> persistenceManager;
private final DaoStateService daoStateService;
private final Map<String, OpenOffer> offersToBeEdited = new HashMap<>();
private final TradableList<OpenOffer> openOffers = new TradableList<>();
private boolean stopped;
Expand Down Expand Up @@ -167,7 +171,8 @@ public OpenOfferManager(CoreContext coreContext,
BtcFeeReceiverService btcFeeReceiverService,
DelayedPayoutTxReceiverService delayedPayoutTxReceiverService,
Broadcaster broadcaster,
PersistenceManager<TradableList<OpenOffer>> persistenceManager) {
PersistenceManager<TradableList<OpenOffer>> persistenceManager,
DaoStateService daoStateService) {
this.coreContext = coreContext;
this.createOfferService = createOfferService;
this.keyRing = keyRing;
Expand All @@ -190,6 +195,7 @@ public OpenOfferManager(CoreContext coreContext,
this.delayedPayoutTxReceiverService = delayedPayoutTxReceiverService;
this.broadcaster = broadcaster;
this.persistenceManager = persistenceManager;
this.daoStateService = daoStateService;

this.persistenceManager.initialize(openOffers, "OpenOffers", PersistenceManager.Source.PRIVATE);
}
Expand All @@ -204,8 +210,32 @@ public void readPersisted(Runnable completeHandler) {
completeHandler);
}


///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener implementation
///////////////////////////////////////////////////////////////////////////////////////////

@Override
public void onParseBlockCompleteAfterBatchProcessing(Block block) {
Set<String> invalidOfferIds = invalidOffers.stream().map(e -> e.first.getId()).collect(Collectors.toSet());
Set<Tuple2<OpenOffer, String>> exceedingOffers = openOffers.stream()
.filter(openOffer -> !invalidOfferIds.contains(openOffer.getId()))
.filter(openOffer -> OfferUtil.doesOfferAmountExceedTradeLimit(openOffer.getOffer()))
.map(openOffer -> {
String message = "Your offer with ID `" + openOffer.getOffer().getShortId() + "` has become invalid because the max. allowed trade amount has been changed.\n\n" +
"The new trade limit has been activated by DAO voting. See https://github.com/bisq-network/proposals/issues/453 for more details.\n\n" +
"You can request a reimbursement from the Bisq DAO for the lost `maker-fee` at: https://github.com/bisq-network/support/issues.\n" +
"If you have any questions please reach out to the Bisq community at: https://bisq.network/community.";
return new Tuple2<>(openOffer, message);
})
.collect(Collectors.toSet());
invalidOffers.addAll(exceedingOffers);
}


public void onAllServicesInitialized() {
p2PService.addDecryptedDirectMessageListener(this);
daoStateService.addDaoStateListener(this);

if (p2PService.isBootstrapped()) {
onBootstrapComplete();
Expand Down Expand Up @@ -243,6 +273,7 @@ private void cleanUpAddressEntries() {

public void shutDown(@Nullable Runnable completeHandler) {
stopped = true;
daoStateService.removeDaoStateListener(this);
p2PService.getPeerManager().removeListener(this);
p2PService.removeDecryptedDirectMessageListener(this);

Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/bisq/core/payment/TradeLimits.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void onParseBlockCompleteAfterBatchProcessing(Block block) {
* @see bisq.core.payment.payload.PaymentMethod
* @return the maximum trade limit set by the DAO.
*/
public Coin getMaxTradeLimit() {
public Coin getMaxTradeLimitFromDaoParam() {
Coin limit = cachedMaxTradeLimit;
if (limit == null) {
cachedMaxTradeLimit = limit = daoStateService.getParamValueAsCoin(Param.MAX_TRADE_LIMIT, periodService.getChainHeight());
Expand Down
29 changes: 18 additions & 11 deletions core/src/main/java/bisq/core/payment/payload/PaymentMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import bisq.core.payment.TradeLimits;

import bisq.common.proto.persistable.PersistablePayload;
import bisq.common.util.MathUtils;

import org.bitcoinj.core.Coin;

Expand All @@ -41,8 +42,6 @@

import org.jetbrains.annotations.NotNull;

import static com.google.common.base.Preconditions.checkNotNull;

@EqualsAndHashCode(exclude = {"maxTradePeriod", "maxTradeLimit"})
@ToString
@Slf4j
Expand Down Expand Up @@ -377,13 +376,24 @@ public static Optional<PaymentMethod> getActivePaymentMethod(String id) {
return Optional.ofNullable(PAYMENT_METHOD_MAP.get(id));
}

// We leave currencyCode as param for being flexible if we need custom handling of a currency in future
// again (as we had in the past)
public Coin getMaxTradeLimitAsCoin(String currencyCode) {
// Hack for SF as the smallest unit is 1 SF ;-( and price is about 3 BTC!
if (currencyCode.equals("SF"))
return Coin.parseCoin("4");
// payment methods which define their own trade limits
// We adjust the custom trade limits with the factor of the change of the DAO param. Initially it was set to 2 BTC.
long initialTradeLimit = 200000000;
TradeLimits tradeLimits = TradeLimits.getINSTANCE();
if (tradeLimits == null) {
// is null in some tests...
log.warn("tradeLimits was null");
return Coin.valueOf(initialTradeLimit);
}
long maxTradeLimitFromDaoParam = tradeLimits.getMaxTradeLimitFromDaoParam().value;

// Payment methods which define their own trade limits
if (id.equals(NEFT_ID) || id.equals(UPI_ID) || id.equals(PAYTM_ID) || id.equals(BIZUM_ID) || id.equals(TIKKIE_ID)) {
return Coin.valueOf(maxTradeLimit);
double factor = maxTradeLimitFromDaoParam / (double) initialTradeLimit;
long value = MathUtils.roundDoubleToLong(Coin.valueOf(maxTradeLimit).getValue() * factor);
return Coin.valueOf(value);
}

// We use the class field maxTradeLimit only for mapping the risk factor.
Expand All @@ -403,10 +413,7 @@ else if (maxTradeLimit == DEFAULT_TRADE_LIMIT_HIGH_RISK.value)
Coin.valueOf(maxTradeLimit).toFriendlyString(), this);
}

TradeLimits tradeLimits = TradeLimits.getINSTANCE();
checkNotNull(tradeLimits, "tradeLimits must not be null");
long maxTradeLimit = tradeLimits.getMaxTradeLimit().value;
return Coin.valueOf(tradeLimits.getRoundedRiskBasedTradeLimit(maxTradeLimit, riskFactor));
return Coin.valueOf(tradeLimits.getRoundedRiskBasedTradeLimit(maxTradeLimitFromDaoParam, riskFactor));
}

public String getShortName() {
Expand Down
9 changes: 6 additions & 3 deletions core/src/test/java/bisq/core/offer/OpenOfferManagerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ public void testStartEditOfferForActiveOffer() {
null,
null,
null,
persistenceManager
persistenceManager,
null
);

AtomicBoolean startEditOfferSuccessful = new AtomicBoolean(false);
Expand Down Expand Up @@ -118,7 +119,8 @@ public void testStartEditOfferForDeactivatedOffer() {
null,
null,
null,
persistenceManager
persistenceManager,
null
);

AtomicBoolean startEditOfferSuccessful = new AtomicBoolean(false);
Expand Down Expand Up @@ -159,7 +161,8 @@ public void testStartEditOfferForOfferThatIsCurrentlyEdited() {
null,
null,
null,
persistenceManager
persistenceManager,
null
);

AtomicBoolean startEditOfferSuccessful = new AtomicBoolean(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import bisq.core.api.CoreApi;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.state.DaoStateService;
import bisq.core.locale.TradeCurrency;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
Expand Down Expand Up @@ -74,8 +75,11 @@ public BsqOfferBookViewModel(User user,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
BsqFormatter bsqFormatter,
BsqWalletService bsqWalletService,
CoreApi coreApi) {
super(user, openOfferManager, offerBook, preferences, walletsSetup, p2PService, priceFeedService, closedTradableManager, bsqSwapTradeManager, accountAgeWitnessService, navigation, priceUtil, offerFilterService, btcFormatter, bsqFormatter, bsqWalletService, coreApi);
CoreApi coreApi,
DaoStateService daoStateService) {
super(user, openOfferManager, offerBook, preferences, walletsSetup, p2PService, priceFeedService,
closedTradableManager, bsqSwapTradeManager, accountAgeWitnessService, navigation, priceUtil,
offerFilterService, btcFormatter, bsqFormatter, bsqWalletService, coreApi, daoStateService);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import bisq.core.api.CoreApi;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.state.DaoStateService;
import bisq.core.locale.CryptoCurrency;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.GlobalSettings;
Expand Down Expand Up @@ -75,8 +76,12 @@ public BtcOfferBookViewModel(User user,
OfferFilterService offerFilterService,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
BsqFormatter bsqFormatter,
BsqWalletService bsqWalletService, CoreApi coreApi) {
super(user, openOfferManager, offerBook, preferences, walletsSetup, p2PService, priceFeedService, closedTradableManager, bsqSwapTradeManager, accountAgeWitnessService, navigation, priceUtil, offerFilterService, btcFormatter, bsqFormatter, bsqWalletService, coreApi);
BsqWalletService bsqWalletService,
CoreApi coreApi,
DaoStateService daoStateService) {
super(user, openOfferManager, offerBook, preferences, walletsSetup, p2PService, priceFeedService,
closedTradableManager, bsqSwapTradeManager, accountAgeWitnessService, navigation, priceUtil,
offerFilterService, btcFormatter, bsqFormatter, bsqWalletService, coreApi, daoStateService);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
import bisq.core.api.CoreApi;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.locale.BankUtil;
import bisq.core.locale.CountryUtil;
import bisq.core.locale.CryptoCurrency;
Expand All @@ -41,6 +44,7 @@
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.offer.OfferFilterService;
import bisq.core.offer.OfferUtil;
import bisq.core.offer.OpenOffer;
import bisq.core.offer.OpenOfferManager;
import bisq.core.payment.PaymentAccount;
Expand Down Expand Up @@ -97,7 +101,7 @@
import lombok.extern.slf4j.Slf4j;

@Slf4j
abstract class OfferBookViewModel extends ActivatableViewModel {
abstract class OfferBookViewModel extends ActivatableViewModel implements DaoStateListener {
private final OpenOfferManager openOfferManager;
private final User user;
private final OfferBook offerBook;
Expand All @@ -117,6 +121,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
private final FilteredList<OfferBookListItem> filteredItems;
private final BsqWalletService bsqWalletService;
private final CoreApi coreApi;
private final DaoStateService daoStateService;
private final SortedList<OfferBookListItem> sortedItems;
private final ListChangeListener<TradeCurrency> tradeCurrencyListChangeListener;
private final ListChangeListener<OfferBookListItem> filterItemsListener;
Expand Down Expand Up @@ -167,7 +172,8 @@ public OfferBookViewModel(User user,
CoinFormatter btcFormatter,
BsqFormatter bsqFormatter,
BsqWalletService bsqWalletService,
CoreApi coreApi) {
CoreApi coreApi,
DaoStateService daoStateService) {
super();

this.openOfferManager = openOfferManager;
Expand All @@ -189,6 +195,7 @@ public OfferBookViewModel(User user,
this.filteredItems = new FilteredList<>(offerBook.getOfferBookListItems());
this.bsqWalletService = bsqWalletService;
this.coreApi = coreApi;
this.daoStateService = daoStateService;
this.sortedItems = new SortedList<>(filteredItems);

tradeCurrencyListChangeListener = c -> fillCurrencies();
Expand Down Expand Up @@ -227,6 +234,17 @@ public OfferBookViewModel(User user,
};
}


///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener implementation
///////////////////////////////////////////////////////////////////////////////////////////

@Override
public void onParseBlockCompleteAfterBatchProcessing(Block block) {
filterOffers();
}


@Override
protected void activate() {
filteredItems.addListener(filterItemsListener);
Expand All @@ -241,12 +259,14 @@ protected void activate() {
setMarketPriceFeedCurrency();

priceUtil.recalculateBsq30DayAveragePrice();
daoStateService.addDaoStateListener(this);
}

@Override
protected void deactivate() {
filteredItems.removeListener(filterItemsListener);
preferences.getTradeCurrenciesAsObservable().removeListener(tradeCurrencyListChangeListener);
daoStateService.removeDaoStateListener(this);
}


Expand Down Expand Up @@ -593,10 +613,12 @@ boolean canCreateOrTakeOffer() {
///////////////////////////////////////////////////////////////////////////////////////////

private void filterOffers() {
Predicate<OfferBookListItem> predicate = useOffersMatchingMyAccountsFilter ?
Predicate<OfferBookListItem> predicate1 = useOffersMatchingMyAccountsFilter ?
getCurrencyAndMethodPredicate(direction, selectedTradeCurrency).and(getOffersMatchingMyAccountsPredicate()) :
getCurrencyAndMethodPredicate(direction, selectedTradeCurrency);
filteredItems.setPredicate(predicate);

Predicate<OfferBookListItem> predicate2 = item -> !OfferUtil.doesOfferAmountExceedTradeLimit(item.getOffer());
filteredItems.setPredicate(predicate1.and(predicate2));
}

abstract Predicate<OfferBookListItem> getCurrencyAndMethodPredicate(OfferDirection direction,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import bisq.core.api.CoreApi;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.state.DaoStateService;
import bisq.core.locale.CryptoCurrency;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.GlobalSettings;
Expand Down Expand Up @@ -77,8 +78,12 @@ public OtherOfferBookViewModel(User user,
OfferFilterService offerFilterService,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
BsqFormatter bsqFormatter,
BsqWalletService bsqWalletService, CoreApi coreApi) {
super(user, openOfferManager, offerBook, preferences, walletsSetup, p2PService, priceFeedService, closedTradableManager, bsqSwapTradeManager, accountAgeWitnessService, navigation, priceUtil, offerFilterService, btcFormatter, bsqFormatter, bsqWalletService, coreApi);
BsqWalletService bsqWalletService,
CoreApi coreApi,
DaoStateService daoStateService) {
super(user, openOfferManager, offerBook, preferences, walletsSetup, p2PService, priceFeedService,
closedTradableManager, bsqSwapTradeManager, accountAgeWitnessService, navigation, priceUtil,
offerFilterService, btcFormatter, bsqFormatter, bsqWalletService, coreApi, daoStateService);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import bisq.core.api.CoreApi;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.state.DaoStateService;
import bisq.core.locale.TradeCurrency;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
Expand Down Expand Up @@ -73,8 +74,11 @@ public TopAltcoinOfferBookViewModel(User user,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
BsqFormatter bsqFormatter,
BsqWalletService bsqWalletService,
CoreApi coreApi) {
super(user, openOfferManager, offerBook, preferences, walletsSetup, p2PService, priceFeedService, closedTradableManager, bsqSwapTradeManager, accountAgeWitnessService, navigation, priceUtil, offerFilterService, btcFormatter, bsqFormatter, bsqWalletService, coreApi);
CoreApi coreApi,
DaoStateService daoStateService) {
super(user, openOfferManager, offerBook, preferences, walletsSetup, p2PService, priceFeedService,
closedTradableManager, bsqSwapTradeManager, accountAgeWitnessService, navigation, priceUtil,
offerFilterService, btcFormatter, bsqFormatter, bsqWalletService, coreApi, daoStateService);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ private void initializeTradeLimitOptions() {
btcValidator.setMinValue(Coin.valueOf(Preferences.INITIAL_TRADE_LIMIT));
TradeLimits tradeLimits = TradeLimits.getINSTANCE();
checkNotNull(tradeLimits, "tradeLimits must not be null");
btcValidator.setMaxValue(tradeLimits.getMaxTradeLimit());
btcValidator.setMaxValue(tradeLimits.getMaxTradeLimitFromDaoParam());
tradeLimitTf.setValidator(btcValidator);
displayCurrenciesGridRowIndex++;

Expand Down
Loading

0 comments on commit 2b06a69

Please sign in to comment.