Skip to content

Commit

Permalink
Merge pull request #3110 from axpoems/update-amount-limits
Browse files Browse the repository at this point in the history
Restrict selling offers to users with enough reputation
  • Loading branch information
axpoems authored Jan 18, 2025
2 parents 57f8442 + 02f85dd commit b27ed29
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ public void onActivate() {
NavigationTarget.TRADE_WIZARD_TAKE_OFFER_OFFER,
NavigationTarget.TRADE_WIZARD_REVIEW_OFFER
));
model.getSelectedChildTarget().set(NavigationTarget.TRADE_WIZARD_DIRECTION_AND_MARKET);

directionPin = EasyBind.subscribe(tradeWizardDirectionAndMarketController.getDirection(), direction -> {
tradeWizardSelectOfferController.setDirection(direction);
Expand Down Expand Up @@ -292,9 +293,13 @@ void onBack() {
}

private boolean validate(boolean calledFromNext) {
if (model.getSelectedChildTarget().get() == NavigationTarget.TRADE_WIZARD_DIRECTION_AND_MARKET) {
return tradeWizardDirectionAndMarketController.validate();
}
if (model.getSelectedChildTarget().get() == NavigationTarget.TRADE_WIZARD_AMOUNT_AND_PRICE) {
return tradeWizardAmountAndPriceController.validate();
} else if (calledFromNext && model.getSelectedChildTarget().get() == NavigationTarget.TRADE_WIZARD_PAYMENT_METHODS) {
}
if (calledFromNext && model.getSelectedChildTarget().get() == NavigationTarget.TRADE_WIZARD_PAYMENT_METHODS) {
// For PaymentMethod we tolerate to go back without having one selected
return tradeWizardPaymentMethodsController.validate();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import bisq.offer.Direction;
import bisq.presentation.formatters.AmountFormatter;
import bisq.user.identity.UserIdentityService;
import bisq.user.reputation.ReputationScore;
import bisq.user.reputation.ReputationService;
import javafx.beans.property.ReadOnlyObjectProperty;
import lombok.Getter;
Expand Down Expand Up @@ -79,6 +78,7 @@ public TradeWizardDirectionAndMarketController(ServiceProvider serviceProvider,
model = new TradeWizardDirectionAndMarketModel();
view = new TradeWizardDirectionAndMarketView(model, this);
setDirection(Direction.BUY);
setIsAllowedToCreateOffer();
applyShowReputationInfo();
}

Expand All @@ -97,6 +97,7 @@ public void reset() {
@Override
public void onActivate() {
setDirection(Direction.BUY);
setIsAllowedToCreateOffer();
applyShowReputationInfo();

model.setFormattedAmountWithoutReputationNeeded(Optional.ofNullable(bisqEasyOfferbookSelectionService.getSelectedChannel().get())
Expand Down Expand Up @@ -162,6 +163,15 @@ public void onDeactivate() {
searchTextPin.unsubscribe();
}

public boolean validate() {
if (model.getDirection().get() == Direction.SELL && !model.isAllowedToCreateSellOffer()) {
showReputationInfoOverlay();
return false;
}

return true;
}

void onSelectDirection(Direction direction) {
setDirection(direction);
applyShowReputationInfo();
Expand All @@ -179,11 +189,6 @@ void onBuildReputation() {
closeAndNavigateToHandler.accept(NavigationTarget.BUILD_REPUTATION);
}

void onTradeWithoutReputation() {
navigationButtonsVisibleHandler.accept(true);
onNextHandler.run();
}

void onMarketListItemClicked(TradeWizardDirectionAndMarketView.ListItem item) {
if (item == null) {
return;
Expand All @@ -201,24 +206,33 @@ private void setDirection(Direction direction) {
model.getDirection().set(direction);
}

private void setIsAllowedToCreateOffer() {
model.setAllowedToCreateSellOffer(BisqEasyTradeAmountLimits.isAllowedToCreateSellOffer(
reputationService, userIdentityService.getSelectedUserIdentity().getUserProfile()));
}

private void applyShowReputationInfo() {
if (model.getDirection().get() == Direction.BUY) {
model.getShowReputationInfo().set(false);
navigationButtonsVisibleHandler.accept(true);
return;
}

ReputationScore reputationScore = reputationService.getReputationScore(userIdentityService.getSelectedUserIdentity().getUserProfile());
if (!reputationScore.hasReputation()) {
navigationButtonsVisibleHandler.accept(false);
model.getShowReputationInfo().set(true);
view.getRoot().setOnKeyPressed(keyEvent -> {
KeyHandlerUtil.handleEnterKeyEvent(keyEvent, () -> {
});
KeyHandlerUtil.handleEscapeKeyEvent(keyEvent, this::onCloseReputationInfo);
});
// Sell offer
if (!model.isAllowedToCreateSellOffer()) {
showReputationInfoOverlay();
} else {
view.getRoot().setOnKeyPressed(null);
}
}

private void showReputationInfoOverlay() {
navigationButtonsVisibleHandler.accept(false);
model.getShowReputationInfo().set(true);
view.getRoot().setOnKeyPressed(keyEvent -> {
KeyHandlerUtil.handleEnterKeyEvent(keyEvent, () -> {
});
KeyHandlerUtil.handleEscapeKeyEvent(keyEvent, this::onCloseReputationInfo);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@

@Getter
public class TradeWizardDirectionAndMarketModel implements Model {
@Setter
private boolean isAllowedToCreateSellOffer;

private final ObjectProperty<Direction> direction = new SimpleObjectProperty<>(Direction.BUY);
private final BooleanProperty showReputationInfo = new SimpleBooleanProperty();
private final BooleanProperty buyButtonDisabled = new SimpleBooleanProperty();
Expand All @@ -48,6 +51,7 @@ public class TradeWizardDirectionAndMarketModel implements Model {
private String formattedAmountWithoutReputationNeeded;

void reset() {
isAllowedToCreateSellOffer = false;
direction.set(Direction.BUY);
showReputationInfo.set(false);
buyButtonDisabled.set(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import bisq.common.currency.FiatCurrency;
import bisq.common.currency.Market;
import bisq.desktop.common.Icons;
import bisq.desktop.common.Transitions;
import bisq.desktop.common.utils.ImageUtil;
import bisq.desktop.common.view.View;
Expand All @@ -32,6 +33,7 @@
import bisq.desktop.main.content.components.MarketImageComposition;
import bisq.i18n.Res;
import bisq.offer.Direction;
import de.jensd.fx.fontawesome.AwesomeIcon;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
Expand Down Expand Up @@ -69,9 +71,7 @@ public class TradeWizardDirectionAndMarketView extends View<StackPane, TradeWiza
private final BisqPopup marketSelectionPopup;
private final HBox currencyLabelBox;
private Subscription directionSubscription, showReputationInfoPin, marketPin, marketSelectionPin;
private Button withoutReputationButton, backToBuyButton;
private Button gainReputationButton;
private Label subtitleLabel2;
private Button backToBuyButton, gainReputationButton;

public TradeWizardDirectionAndMarketView(TradeWizardDirectionAndMarketModel model,
TradeWizardDirectionAndMarketController controller) {
Expand Down Expand Up @@ -154,13 +154,10 @@ protected void onViewAttached() {

searchBox.textProperty().bindBidirectional(model.getSearchText());

subtitleLabel2.setText(Res.get("bisqEasy.tradeWizard.directionAndMarket.feedback.subTitle2", model.getFormattedAmountWithoutReputationNeeded()));

buyButton.disableProperty().bind(model.getBuyButtonDisabled());
buyButton.setOnAction(evt -> controller.onSelectDirection(Direction.BUY));
sellButton.setOnAction(evt -> controller.onSelectDirection(Direction.SELL));
gainReputationButton.setOnAction(evt -> controller.onBuildReputation());
withoutReputationButton.setOnAction(evt -> controller.onTradeWithoutReputation());
backToBuyButton.setOnAction(evt -> controller.onCloseReputationInfo());

directionSubscription = EasyBind.subscribe(model.getDirection(), direction -> {
Expand Down Expand Up @@ -216,7 +213,6 @@ protected void onViewDetached() {
buyButton.setOnAction(null);
sellButton.setOnAction(null);
gainReputationButton.setOnAction(null);
withoutReputationButton.setOnAction(null);
backToBuyButton.setOnAction(null);

directionSubscription.unsubscribe();
Expand Down Expand Up @@ -252,29 +248,38 @@ private void setupReputationInfo() {
headlineLabel.setAlignment(Pos.CENTER);
headlineLabel.setMaxWidth(width - 60);

Label warningIcon = new Label();
Icons.getIconForLabel(AwesomeIcon.WARNING_SIGN, warningIcon, "1.7em");
warningIcon.getStyleClass().add("text-fill-light-dimmed");

HBox headlineBox = new HBox(15, warningIcon, headlineLabel);
headlineBox.setAlignment(Pos.CENTER);

Label subtitleLabel1 = new Label(Res.get("bisqEasy.tradeWizard.directionAndMarket.feedback.subTitle1"));
subtitleLabel1.setMaxWidth(width - 60);
subtitleLabel1.getStyleClass().addAll("bisq-text-21", "wrap-text");

gainReputationButton = new Button(Res.get("bisqEasy.tradeWizard.directionAndMarket.feedback.gainReputation"));
gainReputationButton.getStyleClass().add("outlined-button");

subtitleLabel2 = new Label();
Label subtitleLabel2 = new Label(Res.get("bisqEasy.tradeWizard.directionAndMarket.feedback.subTitle2"));
subtitleLabel2.setMaxWidth(width - 60);
subtitleLabel2.getStyleClass().addAll("bisq-text-21", "wrap-text");

withoutReputationButton = new Button(Res.get("bisqEasy.tradeWizard.directionAndMarket.feedback.tradeWithoutReputation"));
Label subtitleLabel3 = new Label(Res.get("bisqEasy.tradeWizard.directionAndMarket.feedback.subTitle3"));
subtitleLabel3.setMaxWidth(width - 60);
subtitleLabel3.getStyleClass().addAll("bisq-text-21", "wrap-text");

backToBuyButton = new Button(Res.get("bisqEasy.tradeWizard.directionAndMarket.feedback.backToBuy"));
gainReputationButton = new Button(Res.get("bisqEasy.tradeWizard.directionAndMarket.feedback.gainReputation"));
gainReputationButton.setDefaultButton(true);

HBox buttons = new HBox(7, backToBuyButton, withoutReputationButton);
HBox buttons = new HBox(7, backToBuyButton, gainReputationButton);
buttons.setAlignment(Pos.CENTER);

VBox.setMargin(gainReputationButton, new Insets(10, 0, 20, 0));
VBox.setMargin(headlineBox, new Insets(20, 0, 20, 0));
VBox.setMargin(buttons, new Insets(30, 0, 0, 0));
contentBox.getChildren().addAll(headlineLabel,
contentBox.getChildren().addAll(headlineBox,
subtitleLabel1,
gainReputationButton,
subtitleLabel2,
subtitleLabel3,
buttons);
reputationInfo.getChildren().addAll(contentBox, Spacer.fillVBox());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public class BisqEasyTradeAmountLimits {
private static final double REQUIRED_REPUTATION_SCORE_PER_USD = 200d;
public static final long MIN_REPUTATION_SCORE = 5000;
public static final double TOLERANCE = 0.05;
private static final long MIN_REPUTATION_SCORE_TO_CREATE_SELL_OFFER = 1200;

public static Optional<Monetary> getMinQuoteSideTradeAmount(MarketPriceService marketPriceService, Market market) {
return marketPriceService.findMarketPriceQuote(MarketRepository.getUSDBitcoinMarket())
Expand Down Expand Up @@ -128,6 +129,12 @@ public static long getSellersReputationScore(ReputationService reputationService
return reputationService.getReputationScore(sellersUserProfile).getTotalScore();
}

public static boolean isAllowedToCreateSellOffer(ReputationService reputationService,
UserProfile userProfile) {
long reputationScore = reputationService.getReputationScore(userProfile).getTotalScore();
return reputationScore >= MIN_REPUTATION_SCORE_TO_CREATE_SELL_OFFER;
}

private static Result getResult(long sellersReputationScore,
long requiredReputationScore) {
Result result;
Expand Down
22 changes: 8 additions & 14 deletions i18n/src/main/resources/bisq_easy.properties
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,21 @@ bisqEasy.tradeWizard.progress.review=Review
bisqEasy.tradeWizard.directionAndMarket.headline=Do you want to buy or sell Bitcoin with
bisqEasy.tradeWizard.directionAndMarket.buy=Buy Bitcoin
bisqEasy.tradeWizard.directionAndMarket.sell=Sell Bitcoin
bisqEasy.tradeWizard.directionAndMarket.feedback.headline=How to build up reputation?
bisqEasy.tradeWizard.directionAndMarket.feedback.subTitle1=You haven't established any reputation yet. \
For Bitcoin sellers, building reputation is crucial because buyers are required to send fiat currency first \
in the trade process, relying on the seller's integrity.\n\n\
This reputation-building process is better suited for experienced Bisq users, and you can find detailed information \
about it in the 'Reputation' section.
bisqEasy.tradeWizard.directionAndMarket.feedback.gainReputation=Learn how to build up reputation
bisqEasy.tradeWizard.directionAndMarket.feedback.subTitle2=While it''s possible for sellers to trade without (or insufficient) reputation for relatively low amounts up to {0}, \
the likelihood of finding a trading partner is considerably reduced. This is because buyers tend to avoid \
engaging with sellers who lack reputation due to security risks.
bisqEasy.tradeWizard.directionAndMarket.feedback.tradeWithoutReputation=Continue without reputation
bisqEasy.tradeWizard.directionAndMarket.feedback.headline=As a seller, you need to have reputation
bisqEasy.tradeWizard.directionAndMarket.feedback.subTitle1=You don't have enough reputation to create a sell offer.
bisqEasy.tradeWizard.directionAndMarket.feedback.subTitle2=For Bitcoin sellers, building reputation is crucial because buyers \
are required to send fiat currency first in the trade process, relying on the seller's integrity. \
Hence, sellers lacking reputation increase security risks for buyers.
bisqEasy.tradeWizard.directionAndMarket.feedback.subTitle3=This reputation-building process is better suited for \
experienced Bisq users, and you can find detailed information about it in the 'Reputation' section.
bisqEasy.tradeWizard.directionAndMarket.feedback.gainReputation=Build reputation
bisqEasy.tradeWizard.directionAndMarket.feedback.backToBuy=Back


################################################################################
# Create offer / Market
################################################################################

bisqEasy.tradeWizard.market.headline.buyer=In which currency do you want to pay?
bisqEasy.tradeWizard.market.headline.seller=In which currency do you want to get paid?
bisqEasy.tradeWizard.market.subTitle=Choose your trade currency
bisqEasy.tradeWizard.market.columns.name=Fiat currency
bisqEasy.tradeWizard.market.columns.numOffers=Num. offers
bisqEasy.tradeWizard.market.columns.numPeers=Online peers
Expand Down

0 comments on commit b27ed29

Please sign in to comment.