Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hide offers which are over the trade limit #7119

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading