diff --git a/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java b/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java index 79841b80d0..ac7219caa7 100644 --- a/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java +++ b/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java @@ -231,7 +231,9 @@ public DesktopApplicationService(String[] args, ShutDownHandler shutDownHandler) networkService, userService, bondedRolesService, - chatService); + chatService, + supportService, + tradeService); } @Override diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/TradeStateController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/TradeStateController.java index 64cd410da1..486b9c9da4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/TradeStateController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/TradeStateController.java @@ -169,7 +169,7 @@ public void onActivate() { PriceSpecFormatter.getFormattedPriceSpec(bisqEasyTrade.getOffer().getPriceSpec()))); model.getSellerPriceDescriptionApprovalOverlay().set( Res.get("bisqEasy.tradeState.acceptOrRejectSellersPrice.description.sellersPrice", - PriceSpecFormatter.getFormattedPriceSpec(bisqEasyTrade.getContract().getAgreedPriceSpec()))); + PriceSpecFormatter.getFormattedPriceSpec(bisqEasyTrade.getContract().getPriceSpec()))); }); } @@ -526,7 +526,7 @@ && requiresSellerPriceAcceptance() private boolean requiresSellerPriceAcceptance() { PriceSpec buyerPriceSpec = model.getBisqEasyTrade().get().getOffer().getPriceSpec(); - PriceSpec sellerPriceSpec = model.getBisqEasyTrade().get().getContract().getAgreedPriceSpec(); + PriceSpec sellerPriceSpec = model.getBisqEasyTrade().get().getContract().getPriceSpec(); boolean priceSpecChanged = !buyerPriceSpec.equals(sellerPriceSpec); Set<BisqEasyTradeState> validStatesToRejectPrice = Set.of( diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/review/TakeOfferReviewController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/review/TakeOfferReviewController.java index 73065fe025..02bad935d0 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/review/TakeOfferReviewController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/review/TakeOfferReviewController.java @@ -125,14 +125,11 @@ public void init(BisqEasyOffer bisqEasyOffer) { .ifPresent(model::setTakersQuoteSideAmount); } - PriceSpec priceSpec = bisqEasyOffer.getPriceSpec(); - model.setSellersPriceSpec(priceSpec); - Optional<PriceQuote> priceQuote = PriceUtil.findQuote(marketPriceService, bisqEasyOffer); priceQuote.ifPresent(priceInput::setQuote); applyPriceQuote(priceQuote); - applyPriceDetails(priceSpec, market); + applyPriceDetails(bisqEasyOffer.getPriceSpec(), market); } public void setTakersBaseSideAmount(Monetary amount) { @@ -192,7 +189,7 @@ private void doTakeOffer(BisqEasyOffer bisqEasyOffer, UserIdentity takerIdentity Monetary takersQuoteSideAmount = model.getTakersQuoteSideAmount(); BitcoinPaymentMethodSpec bitcoinPaymentMethodSpec = model.getBitcoinPaymentMethodSpec(); FiatPaymentMethodSpec fiatPaymentMethodSpec = model.getFiatPaymentMethodSpec(); - PriceSpec sellersPriceSpec = model.getSellersPriceSpec(); + PriceSpec priceSpec = bisqEasyOffer.getPriceSpec(); long marketPrice = model.getMarketPrice(); BisqEasyProtocol bisqEasyProtocol = bisqEasyTradeService.createBisqEasyProtocol(takerIdentity.getIdentity(), bisqEasyOffer, @@ -201,7 +198,7 @@ private void doTakeOffer(BisqEasyOffer bisqEasyOffer, UserIdentity takerIdentity bitcoinPaymentMethodSpec, fiatPaymentMethodSpec, mediator, - sellersPriceSpec, + priceSpec, marketPrice); BisqEasyTrade bisqEasyTrade = bisqEasyProtocol.getModel(); log.info("Selected mediator for trade {}: {}", bisqEasyTrade.getShortId(), mediator.map(UserProfile::getUserName).orElse("N/A")); @@ -335,8 +332,7 @@ private void applyPriceDetails(PriceSpec priceSpec, Market market) { marketPrice.ifPresent(price -> model.setMarketPrice(price.getPriceQuote().getValue())); Optional<PriceQuote> marketPriceQuote = marketPrice.map(MarketPrice::getPriceQuote); String marketPriceAsString = marketPriceQuote.map(PriceFormatter::formatWithCode).orElse(Res.get("data.na")); - Optional<Double> percentFromMarketPrice; - percentFromMarketPrice = PriceUtil.findPercentFromMarketPrice(marketPriceService, priceSpec, market); + Optional<Double> percentFromMarketPrice = PriceUtil.findPercentFromMarketPrice(marketPriceService, priceSpec, market); double percent = percentFromMarketPrice.orElse(0d); if ((priceSpec instanceof FloatPriceSpec || priceSpec instanceof MarketPriceSpec) && percent == 0) { model.setPriceDetails(Res.get("bisqEasy.tradeWizard.review.priceDetails", marketPriceAsString)); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/review/TakeOfferReviewModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/review/TakeOfferReviewModel.java index 49d03a3c86..117c8d940e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/review/TakeOfferReviewModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/review/TakeOfferReviewModel.java @@ -22,7 +22,6 @@ import bisq.offer.bisq_easy.BisqEasyOffer; import bisq.offer.payment_method.BitcoinPaymentMethodSpec; import bisq.offer.payment_method.FiatPaymentMethodSpec; -import bisq.offer.price.spec.PriceSpec; import bisq.trade.bisq_easy.BisqEasyTrade; import bisq.user.profile.UserProfile; import javafx.beans.property.ObjectProperty; @@ -50,8 +49,6 @@ class TakeOfferReviewModel implements Model { private Monetary takersBaseSideAmount; @Setter private Monetary takersQuoteSideAmount; - @Setter - private PriceSpec sellersPriceSpec; private final ObjectProperty<TakeOfferStatus> takeOfferStatus = new SimpleObjectProperty<>(TakeOfferStatus.NOT_STARTED); @Setter private String price; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java index 548e872e44..4fe44c38a6 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java @@ -612,11 +612,10 @@ private void applyPriceDetails(PriceSpec priceSpec, Market market) { marketPrice.ifPresent(price -> model.setMarketPrice(price.getPriceQuote().getValue())); Optional<PriceQuote> marketPriceQuote = marketPriceService.findMarketPrice(market).map(MarketPrice::getPriceQuote); String marketPriceAsString = marketPriceQuote.map(PriceFormatter::formatWithCode).orElse(Res.get("data.na")); - Optional<Double> percentFromMarketPrice; - percentFromMarketPrice = PriceUtil.findPercentFromMarketPrice(marketPriceService, priceSpec, market); + Optional<Double> percentFromMarketPrice = PriceUtil.findPercentFromMarketPrice(marketPriceService, priceSpec, market); double percent = percentFromMarketPrice.orElse(0d); if ((priceSpec instanceof FloatPriceSpec || priceSpec instanceof MarketPriceSpec) && percent == 0) { - model.setPriceDetails(Res.get("bisqEasy.tradeWizard.review.priceDetails", marketPriceAsString)); + model.setPriceDetails(Res.get("bisqEasy.tradeWizard.review.priceDetails")); } else { String aboveOrBelow = percent > 0 ? Res.get("offer.price.above") : Res.get("offer.price.below"); String percentAsString = percentFromMarketPrice.map(Math::abs).map(PercentageFormatter::formatToPercentWithSymbol) diff --git a/apps/http-api-app/src/main/java/bisq/http_api_node/HttpApiApplicationService.java b/apps/http-api-app/src/main/java/bisq/http_api_node/HttpApiApplicationService.java index 11be8a4d84..13ac0db91c 100644 --- a/apps/http-api-app/src/main/java/bisq/http_api_node/HttpApiApplicationService.java +++ b/apps/http-api-app/src/main/java/bisq/http_api_node/HttpApiApplicationService.java @@ -169,7 +169,9 @@ public HttpApiApplicationService(String[] args) { networkService, userService, bondedRolesService, - chatService); + chatService, + supportService, + tradeService); } @Override diff --git a/build.gradle.kts b/build.gradle.kts index 5c309ceec6..0e8c4cae07 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,17 +10,18 @@ tasks.register("buildAll") { doLast { listOf( "build", - ":wallets:build", - ":apps:seed-node-app:build", - ":apps:seed-node-app:installDist", + //":apps:seed-node-app:installDist", + ":apps:desktop:desktop:build", ":apps:desktop:desktop-app:build", - ":apps:desktop:desktop-app:installDist", - ":apps:desktop:desktop-app-launcher:generateInstallers", + ":apps:desktop:desktop-app-launcher:build", + //":apps:desktop:desktop-app:installDist", + // ":apps:desktop:desktop-app-launcher:generateInstallers", ":apps:desktop:webcam-app:build", - ":apps:oracle-node-app:build", ":apps:http-api-app:build", ":apps:node-monitor-web-app:build", -// ":REPLACEME:build", + ":apps:oracle-node-app:build", + ":apps:seed-node-app:build", + ":wallets:build", ).forEach { exec { println("Executing Build: $it") @@ -69,7 +70,6 @@ tasks.register("publishAll") { listOf( ":account:publishToMavenLocal", ":application:publishToMavenLocal", -// ":bisq-easy:publishToMavenLocal", ":bonded-roles:publishToMavenLocal", ":chat:publishToMavenLocal", ":common:publishToMavenLocal", diff --git a/common/src/main/java/bisq/common/monetary/Coin.java b/common/src/main/java/bisq/common/monetary/Coin.java index 3cd8ab4eec..7cd94b91bb 100644 --- a/common/src/main/java/bisq/common/monetary/Coin.java +++ b/common/src/main/java/bisq/common/monetary/Coin.java @@ -151,7 +151,7 @@ private Coin(double faceValue, String code, int precision) { super(code + " [crypto]", faceValue, code, precision, code.equals("BSQ") ? 2 : 4); } - private Coin(String id, long value, String code, int precision, int lowPrecision) { + public Coin(String id, long value, String code, int precision, int lowPrecision) { super(id, value, code, precision, lowPrecision); } @@ -191,11 +191,6 @@ public Coin divide(long divisor) { return new Coin(this.value / divisor, this.code, this.precision); } - @Override - public double toDouble(long value) { - return MathUtils.roundDouble(BigDecimal.valueOf(value).movePointLeft(precision).doubleValue(), precision); - } - private static int derivePrecision(String code) { if (code.equals("XMR")) return 12; if (code.equals("BSQ")) return 2; @@ -204,7 +199,7 @@ private static int derivePrecision(String code) { public Coin round(int roundPrecision) { //todo (low prio) add tests - double rounded = MathUtils.roundDouble(toDouble(value), roundPrecision); + double rounded = MathUtils.roundDouble(asDouble(), roundPrecision); long shifted = BigDecimal.valueOf(rounded).movePointRight(precision).longValue(); return Coin.fromValue(shifted, code, precision); } diff --git a/common/src/main/java/bisq/common/monetary/Fiat.java b/common/src/main/java/bisq/common/monetary/Fiat.java index e2d24dfad4..daefad2339 100644 --- a/common/src/main/java/bisq/common/monetary/Fiat.java +++ b/common/src/main/java/bisq/common/monetary/Fiat.java @@ -86,7 +86,7 @@ private Fiat(double faceValue, String code, int precision) { super(code, faceValue, code, precision, 2); } - private Fiat(String id, long value, String code, int precision, int lowPrecision) { + public Fiat(String id, long value, String code, int precision, int lowPrecision) { super(id, value, code, precision, lowPrecision); } @@ -127,13 +127,8 @@ public Fiat divide(long divisor) { return new Fiat(this.value / divisor, this.code, this.precision); } - @Override - public double toDouble(long value) { - return MathUtils.roundDouble(BigDecimal.valueOf(value).movePointLeft(precision).doubleValue(), precision); - } - public Fiat round(int roundPrecision) { - double rounded = MathUtils.roundDouble(toDouble(value), roundPrecision); + double rounded = MathUtils.roundDouble(asDouble(), roundPrecision); long shifted = BigDecimal.valueOf(rounded).movePointRight(precision).longValue(); return Fiat.fromValue(shifted, code, precision); } diff --git a/common/src/main/java/bisq/common/monetary/Monetary.java b/common/src/main/java/bisq/common/monetary/Monetary.java index 279daebcf8..7fcc8e8de3 100644 --- a/common/src/main/java/bisq/common/monetary/Monetary.java +++ b/common/src/main/java/bisq/common/monetary/Monetary.java @@ -115,8 +115,9 @@ public static Monetary fromProto(bisq.common.protobuf.Monetary proto) { }; } - public abstract double toDouble(long value); - + public double toDouble(long value) { + return MathUtils.roundDouble(BigDecimal.valueOf(value).movePointLeft(precision).doubleValue(), precision); + } public double asDouble() { return toDouble(value); } diff --git a/common/src/main/java/bisq/common/monetary/PriceQuote.java b/common/src/main/java/bisq/common/monetary/PriceQuote.java index cbf9fc4623..b2e4b94d34 100644 --- a/common/src/main/java/bisq/common/monetary/PriceQuote.java +++ b/common/src/main/java/bisq/common/monetary/PriceQuote.java @@ -56,7 +56,7 @@ public final class PriceQuote implements Comparable<PriceQuote>, PersistableProt private final int lowPrecision; private final Market market; - private PriceQuote(long value, Monetary baseSideMonetary, Monetary quoteSideMonetary) { + public PriceQuote(long value, Monetary baseSideMonetary, Monetary quoteSideMonetary) { this.value = value; this.baseSideMonetary = baseSideMonetary; this.quoteSideMonetary = quoteSideMonetary; diff --git a/contract/src/main/java/bisq/contract/bisq_easy/BisqEasyContract.java b/contract/src/main/java/bisq/contract/bisq_easy/BisqEasyContract.java index 63149d25e9..82b8add9c0 100644 --- a/contract/src/main/java/bisq/contract/bisq_easy/BisqEasyContract.java +++ b/contract/src/main/java/bisq/contract/bisq_easy/BisqEasyContract.java @@ -43,7 +43,7 @@ public final class BisqEasyContract extends TwoPartyContract<BisqEasyOffer> { private final BitcoinPaymentMethodSpec baseSidePaymentMethodSpec; private final FiatPaymentMethodSpec quoteSidePaymentMethodSpec; private final Optional<UserProfile> mediator; - private final PriceSpec agreedPriceSpec; + private final PriceSpec priceSpec; private final long marketPrice; private final long takeOfferDate; @@ -55,7 +55,7 @@ public BisqEasyContract(long takeOfferDate, BitcoinPaymentMethodSpec baseSidePaymentMethodSpec, FiatPaymentMethodSpec quoteSidePaymentMethodSpec, Optional<UserProfile> mediator, - PriceSpec agreedPriceSpec, + PriceSpec priceSpec, long marketPrice) { this(takeOfferDate, offer, @@ -66,7 +66,7 @@ public BisqEasyContract(long takeOfferDate, baseSidePaymentMethodSpec, quoteSidePaymentMethodSpec, mediator, - agreedPriceSpec, + priceSpec, marketPrice); } @@ -79,7 +79,7 @@ private BisqEasyContract(long takeOfferDate, BitcoinPaymentMethodSpec baseSidePaymentMethodSpec, FiatPaymentMethodSpec quoteSidePaymentMethodSpec, Optional<UserProfile> mediator, - PriceSpec agreedPriceSpec, + PriceSpec priceSpec, long marketPrice) { super(takeOfferDate, offer, protocolType, taker); this.baseSideAmount = baseSideAmount; @@ -87,7 +87,7 @@ private BisqEasyContract(long takeOfferDate, this.baseSidePaymentMethodSpec = baseSidePaymentMethodSpec; this.quoteSidePaymentMethodSpec = quoteSidePaymentMethodSpec; this.mediator = mediator; - this.agreedPriceSpec = agreedPriceSpec; + this.priceSpec = priceSpec; this.marketPrice = marketPrice; this.takeOfferDate = takeOfferDate; @@ -120,7 +120,7 @@ private bisq.contract.protobuf.BisqEasyContract.Builder getBisqEasyContractBuild .setQuoteSideAmount(quoteSideAmount) .setBaseSidePaymentMethodSpec(baseSidePaymentMethodSpec.toProto(serializeForHash)) .setQuoteSidePaymentMethodSpec(quoteSidePaymentMethodSpec.toProto(serializeForHash)) - .setAgreedPriceSpec(agreedPriceSpec.toProto(serializeForHash)) + .setPriceSpec(priceSpec.toProto(serializeForHash)) .setMarketPrice(marketPrice); mediator.ifPresent(mediator -> builder.setMediator(mediator.toProto(serializeForHash))); return builder; @@ -145,7 +145,7 @@ public static BisqEasyContract fromProto(bisq.contract.protobuf.Contract proto) bisqEasyContractProto.hasMediator() ? Optional.of(UserProfile.fromProto(bisqEasyContractProto.getMediator())) : Optional.empty(), - PriceSpec.fromProto(bisqEasyContractProto.getAgreedPriceSpec()), + PriceSpec.fromProto(bisqEasyContractProto.getPriceSpec()), bisqEasyContractProto.getMarketPrice()); } } diff --git a/contract/src/main/proto/contract.proto b/contract/src/main/proto/contract.proto index 46f148c490..8ee495483c 100644 --- a/contract/src/main/proto/contract.proto +++ b/contract/src/main/proto/contract.proto @@ -83,7 +83,7 @@ message BisqEasyContract { offer.PaymentMethodSpec baseSidePaymentMethodSpec = 3; offer.PaymentMethodSpec quoteSidePaymentMethodSpec = 4; optional user.UserProfile mediator = 12; - offer.PriceSpec agreedPriceSpec = 13; + offer.PriceSpec priceSpec = 13; uint64 marketPrice = 14; } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 207d57ad69..baa733f860 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -129,6 +129,8 @@ i2p-router = { module = 'net.i2p:router', version.ref = 'i2p-lib' } jackson-core = { module = 'com.fasterxml.jackson.core:jackson-core', version.ref = 'jackson-lib' } jackson-annotations = { module = 'com.fasterxml.jackson.core:jackson-annotations', version.ref = 'jackson-lib' } jackson-databind = { module = 'com.fasterxml.jackson.core:jackson-databind', version.ref = 'jackson-lib' } +jackson-datatype = { module = 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8', version.ref = 'jackson-lib' } + jakarta-websocket = { module = 'jakarta.websocket:jakarta.websocket-api', version.ref = 'jakarta-lib' } javacv = { module = "org.bytedeco:javacv-platform", version.ref = "javacv" } @@ -177,13 +179,13 @@ glassfish-jersey = ['glassfish-jersey-jdk-http', 'glassfish-jersey-json-jackson' grpc = ['grpc-protobuf', 'grpc-services', 'grpc-stub'] i2p = ['i2p-core', 'i2p-router', 'i2p-streaming'] i2p-v2 = ['i2p-core-v2', 'i2p-streaming-v2'] -jackson = ['jackson-core', 'jackson-annotations', 'jackson-databind'] +jackson = ['jackson-core', 'jackson-annotations', 'jackson-databind', 'jackson-datatype'] springfox-libs = ['springfox-boot-starter', 'springfox-swagger2', 'springfox-swagger-ui'] rest-api-libs = ['swagger-jaxrs2-jakarta', 'glassfish-jersey-jdk-http', 'glassfish-jersey-json-jackson', - 'glassfish-jersey-inject-hk2', - 'glassfish-jaxb-runtime', 'jackson-core', 'jackson-annotations', 'jackson-databind'] + 'glassfish-jersey-inject-hk2', 'glassfish-jaxb-runtime', + 'jackson-core', 'jackson-annotations', 'jackson-databind', 'jackson-datatype'] websocket-libs = ['glassfish-jersey-json-jackson', 'glassfish-jersey-server', 'glassfish-jersey-containers-grizzly', - 'glassfish-grizzly-websockets-server', 'jakarta-websocket', 'jackson-databind', 'swagger-swagger-annotations'] + 'glassfish-grizzly-websockets-server', 'jakarta-websocket', 'jackson-databind', 'jackson-datatype', 'swagger-swagger-annotations'] # Referenced in subproject's build.gradle > plugin block as alias: `alias(libs.plugins.protobuf)` # Note: plugin version constraints are not supported by the java-platform plugin, so cannot be enforced there. However, diff --git a/http-api/build.gradle.kts b/http-api/build.gradle.kts index 958727cee6..1a2e91b7eb 100644 --- a/http-api/build.gradle.kts +++ b/http-api/build.gradle.kts @@ -30,6 +30,7 @@ dependencies { implementation("bisq:os-specific") implementation("network:network") + implementation("network:network-identity") implementation("bitcoind:core") implementation("wallets:wallet") diff --git a/http-api/src/main/java/bisq/dto/DtoMappings.java b/http-api/src/main/java/bisq/dto/DtoMappings.java new file mode 100644 index 0000000000..e7f6d36030 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/DtoMappings.java @@ -0,0 +1,683 @@ +/* + * 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.dto; + +import bisq.account.payment_method.BitcoinPaymentMethod; +import bisq.account.payment_method.FiatPaymentMethod; +import bisq.account.protocol_type.TradeProtocolType; +import bisq.common.currency.Market; +import bisq.common.encoding.Hex; +import bisq.common.monetary.Coin; +import bisq.common.monetary.Fiat; +import bisq.common.monetary.Monetary; +import bisq.common.monetary.PriceQuote; +import bisq.common.network.Address; +import bisq.common.network.AddressByTransportTypeMap; +import bisq.common.network.TransportType; +import bisq.dto.account.protocol_type.TradeProtocolTypeDto; +import bisq.dto.common.currency.MarketDto; +import bisq.dto.common.monetary.CoinDto; +import bisq.dto.common.monetary.FiatDto; +import bisq.dto.common.monetary.MonetaryDto; +import bisq.dto.common.monetary.PriceQuoteDto; +import bisq.dto.common.network.AddressByTransportTypeMapDto; +import bisq.dto.common.network.AddressDto; +import bisq.dto.common.network.TransportTypeDto; +import bisq.dto.network.identity.NetworkIdDto; +import bisq.dto.offer.DirectionDto; +import bisq.dto.offer.amount.spec.*; +import bisq.dto.offer.bisq_easy.BisqEasyOfferDto; +import bisq.dto.offer.options.OfferOptionDto; +import bisq.dto.offer.options.ReputationOptionDto; +import bisq.dto.offer.options.TradeTermsOptionDto; +import bisq.dto.offer.payment_method.BitcoinPaymentMethodSpecDto; +import bisq.dto.offer.payment_method.FiatPaymentMethodSpecDto; +import bisq.dto.offer.payment_method.PaymentMethodSpecDto; +import bisq.dto.offer.price.spec.FixPriceSpecDto; +import bisq.dto.offer.price.spec.FloatPriceSpecDto; +import bisq.dto.offer.price.spec.MarketPriceSpecDto; +import bisq.dto.offer.price.spec.PriceSpecDto; +import bisq.dto.security.keys.KeyPairDto; +import bisq.dto.security.keys.PrivateKeyDto; +import bisq.dto.security.keys.PubKeyDto; +import bisq.dto.security.keys.PublicKeyDto; +import bisq.dto.security.pow.ProofOfWorkDto; +import bisq.dto.user.profile.UserProfileDto; +import bisq.dto.user.reputation.ReputationScoreDto; +import bisq.network.identity.NetworkId; +import bisq.offer.Direction; +import bisq.offer.amount.spec.*; +import bisq.offer.bisq_easy.BisqEasyOffer; +import bisq.offer.options.OfferOption; +import bisq.offer.options.ReputationOption; +import bisq.offer.options.TradeTermsOption; +import bisq.offer.payment_method.BitcoinPaymentMethodSpec; +import bisq.offer.payment_method.FiatPaymentMethodSpec; +import bisq.offer.payment_method.PaymentMethodSpec; +import bisq.offer.payment_method.PaymentMethodSpecUtil; +import bisq.offer.price.spec.FixPriceSpec; +import bisq.offer.price.spec.FloatPriceSpec; +import bisq.offer.price.spec.MarketPriceSpec; +import bisq.offer.price.spec.PriceSpec; +import bisq.security.DigestUtil; +import bisq.security.keys.KeyGeneration; +import bisq.security.keys.PubKey; +import bisq.security.pow.ProofOfWork; +import bisq.user.profile.UserProfile; +import bisq.user.reputation.ReputationScore; + +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.Base64; +import java.util.stream.Collectors; + +public class DtoMappings { + + // account.protocol_type + + public static class TradeProtocolTypeMapping { + public static TradeProtocolType toPojo(TradeProtocolTypeDto dto) { + return switch (dto) { + case BISQ_EASY -> TradeProtocolType.BISQ_EASY; + case BISQ_MU_SIG -> TradeProtocolType.BISQ_MU_SIG; + case SUBMARINE -> TradeProtocolType.SUBMARINE; + case LIQUID_MU_SIG -> TradeProtocolType.LIQUID_MU_SIG; + case BISQ_LIGHTNING -> TradeProtocolType.BISQ_LIGHTNING; + case LIQUID_SWAP -> TradeProtocolType.LIQUID_SWAP; + case BSQ_SWAP -> TradeProtocolType.BSQ_SWAP; + case LIGHTNING_ESCROW -> TradeProtocolType.LIGHTNING_ESCROW; + case MONERO_SWAP -> TradeProtocolType.MONERO_SWAP; + }; + } + + public static TradeProtocolTypeDto from(TradeProtocolType value) { + return switch (value) { + case BISQ_EASY -> TradeProtocolTypeDto.BISQ_EASY; + case BISQ_MU_SIG -> TradeProtocolTypeDto.BISQ_MU_SIG; + case SUBMARINE -> TradeProtocolTypeDto.SUBMARINE; + case LIQUID_MU_SIG -> TradeProtocolTypeDto.LIQUID_MU_SIG; + case BISQ_LIGHTNING -> TradeProtocolTypeDto.BISQ_LIGHTNING; + case LIQUID_SWAP -> TradeProtocolTypeDto.LIQUID_SWAP; + case BSQ_SWAP -> TradeProtocolTypeDto.BSQ_SWAP; + case LIGHTNING_ESCROW -> TradeProtocolTypeDto.LIGHTNING_ESCROW; + case MONERO_SWAP -> TradeProtocolTypeDto.MONERO_SWAP; + }; + } + } + + + // common.currency + + public static class MarketMapping { + public static Market toPojo(MarketDto dto) { + return new Market(dto.baseCurrencyCode(), dto.quoteCurrencyCode(), dto.baseCurrencyName(), dto.quoteCurrencyName()); + } + + public static MarketDto from(Market value) { + return new MarketDto(value.getBaseCurrencyCode(), value.getQuoteCurrencyCode(), value.getBaseCurrencyName(), value.getQuoteCurrencyName()); + } + } + + + // common.monetary + + public static class CoinMapping { + public static Coin toPojo(CoinDto dto) { + return new Coin(dto.getId(), dto.getValue(), dto.getCode(), dto.getPrecision(), dto.getLowPrecision()); + } + + public static CoinDto from(Coin value) { + return new CoinDto(value.getId(), value.getValue(), value.getCode(), value.getPrecision(), value.getLowPrecision()); + } + } + + public static class FiatMapping { + public static Fiat toPojo(FiatDto dto) { + return new Fiat(dto.getId(), dto.getValue(), dto.getCode(), dto.getPrecision(), dto.getLowPrecision()); + } + + public static FiatDto from(Fiat value) { + return new FiatDto(value.getId(), value.getValue(), value.getCode(), value.getPrecision(), value.getLowPrecision()); + } + } + + + public static class MonetaryMapping { + public static Monetary toPojo(MonetaryDto dto) { + if (dto instanceof FiatDto) { + return FiatMapping.toPojo((FiatDto) dto); + } else { + return CoinMapping.toPojo((CoinDto) dto); + } + } + + public static MonetaryDto from(Monetary value) { + if (value instanceof Fiat) { + return new FiatDto(value.getId(), value.getValue(), value.getCode(), value.getPrecision(), value.getLowPrecision()); + } else { + return new CoinDto(value.getId(), value.getValue(), value.getCode(), value.getPrecision(), value.getLowPrecision()); + } + } + } + + public static class PriceQuoteMapping { + public static PriceQuote toPojo(PriceQuoteDto dto) { + String baseCurrencyCode = dto.market().baseCurrencyCode(); + String quoteCurrencyCode = dto.market().quoteCurrencyCode(); + if (baseCurrencyCode.equals("BTC")) { + Monetary baseSideMonetary = Coin.asBtcFromFaceValue(1); + Monetary quoteSideMonetary = Fiat.from(dto.value(), quoteCurrencyCode); + return new PriceQuote(dto.value(), baseSideMonetary, quoteSideMonetary); + } else { + throw new UnsupportedOperationException("Altcoin price quote mapping is not supported yet"); + } + } + + public static PriceQuoteDto from(PriceQuote value) { + return new PriceQuoteDto(value.getValue(), MarketMapping.from(value.getMarket())); + } + } + + + // common.network + + public static class AddressByTransportTypeMapMapping { + public static AddressByTransportTypeMap toPojo(AddressByTransportTypeMapDto dto) { + return new AddressByTransportTypeMap(dto.map().entrySet().stream().collect(Collectors.toMap(entry -> TransportTypeMapping.toPojo(entry.getKey()), entry -> AddressMapping.toPojo(entry.getValue())))); + } + + public static AddressByTransportTypeMapDto from(AddressByTransportTypeMap map) { + return new AddressByTransportTypeMapDto(map.getMap().entrySet().stream().collect(Collectors.toMap(entry -> TransportTypeMapping.from(entry.getKey()), entry -> AddressMapping.from(entry.getValue())))); + } + } + + public static class AddressMapping { + public static Address toPojo(AddressDto dto) { + return new Address(dto.host(), dto.port()); + } + + public static AddressDto from(Address value) { + return new AddressDto(value.getHost(), value.getPort()); + } + } + + public static class TransportTypeMapping { + public static TransportType toPojo(TransportTypeDto dto) { + if (dto == TransportTypeDto.CLEAR) { + return TransportType.CLEAR; + } else if (dto == TransportTypeDto.TOR) { + return TransportType.TOR; + } else if (dto == TransportTypeDto.I2P) { + return TransportType.I2P; + } else { + throw new IllegalArgumentException("Unsupported enum " + dto); + } + } + + public static TransportTypeDto from(TransportType value) { + if (value == TransportType.CLEAR) { + return TransportTypeDto.CLEAR; + } else if (value == TransportType.TOR) { + return TransportTypeDto.TOR; + } else if (value == TransportType.I2P) { + return TransportTypeDto.I2P; + } else { + throw new IllegalArgumentException("Unsupported enum " + value); + } + } + } + + + // network.identity + + public static class NetworkIdMapping { + public static NetworkId toPojo(NetworkIdDto dto) { + return new NetworkId(AddressByTransportTypeMapMapping.toPojo(dto.addressByTransportTypeMap()), PubKeyMapping.toPojo(dto.pubKey())); + } + + public static NetworkIdDto from(NetworkId value) { + return new NetworkIdDto(AddressByTransportTypeMapMapping.from(value.getAddressByTransportTypeMap()), PubKeyMapping.from(value.getPubKey())); + } + } + + + // offer + + public static class DirectionMapping { + public static Direction toPojo(DirectionDto dto) { + if (dto == DirectionDto.BUY) { + return Direction.BUY; + } else { + return Direction.SELL; + } + } + + public static DirectionDto from(Direction value) { + if (value == Direction.BUY) { + return DirectionDto.BUY; + } else { + return DirectionDto.SELL; + } + } + } + + + // offer.amount.spec + + public static class AmountSpecMapping { + public static AmountSpec toPojo(AmountSpecDto dto) { + if (dto instanceof RangeAmountSpecDto) { + return RangeAmountSpecMapping.toPojo((RangeAmountSpecDto) dto); + } else { + return FixedAmountSpecMapping.toPojo((FixedAmountSpecDto) dto); + } + } + + public static AmountSpecDto from(AmountSpec value) { + if (value instanceof RangeAmountSpec) { + return RangeAmountSpecMapping.from((RangeAmountSpec) value); + } else { + return FixedAmountSpecMapping.from((FixedAmountSpec) value); + } + } + } + + public static class BaseSideFixedAmountSpecMapping { + public static BaseSideFixedAmountSpec toPojo(BaseSideFixedAmountSpecDto dto) { + return new BaseSideFixedAmountSpec(dto.getAmount()); + } + + public static BaseSideFixedAmountSpecDto from(BaseSideFixedAmountSpec value) { + return new BaseSideFixedAmountSpecDto(value.getAmount()); + } + } + + public static class BaseSideRangeAmountSpecMapping { + public static BaseSideRangeAmountSpec toPojo(BaseSideRangeAmountSpecDto dto) { + return new BaseSideRangeAmountSpec(dto.getMinAmount(), dto.getMaxAmount()); + } + + public static BaseSideRangeAmountSpecDto from(BaseSideRangeAmountSpec value) { + return new BaseSideRangeAmountSpecDto(value.getMinAmount(), value.getMaxAmount()); + } + } + + public static class FixedAmountSpecMapping { + public static FixedAmountSpec toPojo(FixedAmountSpecDto dto) { + if (dto instanceof BaseSideFixedAmountSpecDto) { + return BaseSideFixedAmountSpecMapping.toPojo((BaseSideFixedAmountSpecDto) dto); + } else if (dto instanceof QuoteSideFixedAmountSpecDto) { + return QuoteSideFixedAmountSpecMapping.toPojo((QuoteSideFixedAmountSpecDto) dto); + } else { + throw new IllegalArgumentException("Unsupported FixedAmountSpecDto " + dto); + } + } + + public static FixedAmountSpecDto from(FixedAmountSpec value) { + if (value instanceof BaseSideFixedAmountSpec) { + return BaseSideFixedAmountSpecMapping.from((BaseSideFixedAmountSpec) value); + } else if (value instanceof QuoteSideFixedAmountSpec) { + return QuoteSideFixedAmountSpecMapping.from((QuoteSideFixedAmountSpec) value); + } else { + throw new IllegalArgumentException("Unsupported FixedAmountSpec " + value); + } + } + } + + public static class QuoteSideFixedAmountSpecMapping { + public static QuoteSideFixedAmountSpec toPojo(QuoteSideFixedAmountSpecDto dto) { + return new QuoteSideFixedAmountSpec(dto.getAmount()); + } + + public static QuoteSideFixedAmountSpecDto from(QuoteSideFixedAmountSpec value) { + return new QuoteSideFixedAmountSpecDto(value.getAmount()); + } + } + + public static class QuoteSideRangeAmountSpecMapping { + public static QuoteSideRangeAmountSpec toPojo(QuoteSideRangeAmountSpecDto dto) { + return new QuoteSideRangeAmountSpec(dto.getMinAmount(), dto.getMaxAmount()); + } + + public static QuoteSideRangeAmountSpecDto from(QuoteSideRangeAmountSpec value) { + return new QuoteSideRangeAmountSpecDto(value.getMinAmount(), value.getMaxAmount()); + } + } + + public static class RangeAmountSpecMapping { + public static RangeAmountSpec toPojo(RangeAmountSpecDto dto) { + if (dto instanceof BaseSideRangeAmountSpecDto) { + return BaseSideRangeAmountSpecMapping.toPojo((BaseSideRangeAmountSpecDto) dto); + } else if (dto instanceof QuoteSideRangeAmountSpecDto) { + return QuoteSideRangeAmountSpecMapping.toPojo((QuoteSideRangeAmountSpecDto) dto); + } else { + throw new IllegalArgumentException("Unsupported RangeAmountSpecDto " + dto); + } + } + + public static RangeAmountSpecDto from(RangeAmountSpec value) { + if (value instanceof BaseSideRangeAmountSpec) { + return BaseSideRangeAmountSpecMapping.from((BaseSideRangeAmountSpec) value); + } else if (value instanceof QuoteSideRangeAmountSpec) { + return QuoteSideRangeAmountSpecMapping.from((QuoteSideRangeAmountSpec) value); + } else { + throw new IllegalArgumentException("Unsupported RangeAmountSpec " + value); + } + } + } + + + // offer.bisq_easy + + public static class BisqEasyOfferMapping { + public static BisqEasyOffer toPojo(BisqEasyOfferDto dto) { + return new BisqEasyOffer(dto.id(), dto.date(), NetworkIdMapping.toPojo(dto.makerNetworkId()), DirectionMapping.toPojo(dto.direction()), MarketMapping.toPojo(dto.market()), AmountSpecMapping.toPojo(dto.amountSpec()), PriceSpecMapping.toPojo(dto.priceSpec()), dto.protocolTypes().stream().map(TradeProtocolTypeMapping::toPojo).collect(Collectors.toList()), dto.baseSidePaymentMethodSpecs().stream().map(BitcoinPaymentMethodSpecMapping::toPojo).collect(Collectors.toList()), dto.quoteSidePaymentMethodSpecs().stream().map(FiatPaymentMethodSpecMapping::toPojo).collect(Collectors.toList()), dto.offerOptions().stream().map(OfferOptionMapping::toPojo).collect(Collectors.toList()), dto.supportedLanguageCodes()); + } + + public static BisqEasyOfferDto from(BisqEasyOffer value) { + return new BisqEasyOfferDto(value.getId(), value.getDate(), NetworkIdMapping.from(value.getMakerNetworkId()), DirectionMapping.from(value.getDirection()), MarketMapping.from(value.getMarket()), AmountSpecMapping.from(value.getAmountSpec()), PriceSpecMapping.from(value.getPriceSpec()), value.getProtocolTypes().stream().map(TradeProtocolTypeMapping::from).collect(Collectors.toList()), value.getBaseSidePaymentMethodSpecs().stream().map(BitcoinPaymentMethodSpecMapping::from).collect(Collectors.toList()), value.getQuoteSidePaymentMethodSpecs().stream().map(FiatPaymentMethodSpecMapping::from).collect(Collectors.toList()), value.getOfferOptions().stream().map(OfferOptionMapping::from).collect(Collectors.toList()), value.getSupportedLanguageCodes()); + } + } + + + // offer.options + + public static class OfferOptionMapping { + public static OfferOption toPojo(OfferOptionDto dto) { + if (dto instanceof ReputationOptionDto) { + return ReputationOptionMapping.toPojo((ReputationOptionDto) dto); + } else if (dto instanceof TradeTermsOptionDto) { + return TradeTermsOptionMapping.toPojo((TradeTermsOptionDto) dto); + } else { + throw new IllegalArgumentException("Unsupported OfferOptionDto " + dto); + } + } + + public static OfferOptionDto from(OfferOption value) { + if (value instanceof ReputationOption) { + //noinspection deprecation + return new ReputationOptionDto(((ReputationOption) value).getRequiredTotalReputationScore()); + } else if (value instanceof TradeTermsOption) { + return new TradeTermsOptionDto(((TradeTermsOption) value).getMakersTradeTerms()); + } else { + throw new IllegalArgumentException("Unsupported OfferOption " + value); + } + } + } + + public static class ReputationOptionMapping { + public static ReputationOption toPojo(ReputationOptionDto dto) { + //noinspection deprecation + return new ReputationOption(dto.getRequiredTotalReputationScore()); + } + + public static ReputationOptionDto from(ReputationOption value) { + //noinspection deprecation + return new ReputationOptionDto(value.getRequiredTotalReputationScore()); + } + } + + public static class TradeTermsOptionMapping { + public static TradeTermsOption toPojo(TradeTermsOptionDto dto) { + return new TradeTermsOption(dto.getMakersTradeTerms()); + } + + public static TradeTermsOptionDto from(TradeTermsOption value) { + return new TradeTermsOptionDto(value.getMakersTradeTerms()); + } + } + + + // offer.payment_method + + public static class BitcoinPaymentMethodSpecMapping { + public static BitcoinPaymentMethodSpec toPojo(BitcoinPaymentMethodSpecDto dto) { + String paymentMethod = dto.getPaymentMethod(); + BitcoinPaymentMethod method = PaymentMethodSpecUtil.getBitcoinPaymentMethod(paymentMethod); + return new BitcoinPaymentMethodSpec(method, dto.getSaltedMakerAccountId()); + } + + public static BitcoinPaymentMethodSpecDto from(BitcoinPaymentMethodSpec value) { + return new BitcoinPaymentMethodSpecDto(value.getPaymentMethod().getName(), value.getSaltedMakerAccountId()); + } + } + + public static class FiatPaymentMethodSpecMapping { + public static FiatPaymentMethodSpec toPojo(FiatPaymentMethodSpecDto dto) { + String paymentMethod = dto.getPaymentMethod(); + FiatPaymentMethod method = PaymentMethodSpecUtil.getFiatPaymentMethod(paymentMethod); + return new FiatPaymentMethodSpec(method, dto.getSaltedMakerAccountId()); + } + + public static FiatPaymentMethodSpecDto from(FiatPaymentMethodSpec value) { + return new FiatPaymentMethodSpecDto(value.getPaymentMethod().getName(), value.getSaltedMakerAccountId()); + } + } + + public static class PaymentMethodSpecMapping { + public static PaymentMethodSpec<?> toPojo(PaymentMethodSpecDto dto) { + if (dto instanceof FiatPaymentMethodSpecDto) { + return FiatPaymentMethodSpecMapping.toPojo((FiatPaymentMethodSpecDto) dto); + } else if (dto instanceof BitcoinPaymentMethodSpecDto) { + return BitcoinPaymentMethodSpecMapping.toPojo((BitcoinPaymentMethodSpecDto) dto); + } else { + throw new IllegalArgumentException("Unsupported PaymentMethodSpecDto " + dto); + } + } + + public static PaymentMethodSpecDto from(PaymentMethodSpec<?> value) { + if (value instanceof FiatPaymentMethodSpec) { + return FiatPaymentMethodSpecMapping.from((FiatPaymentMethodSpec) value); + } else if (value instanceof BitcoinPaymentMethodSpec) { + return BitcoinPaymentMethodSpecMapping.from((BitcoinPaymentMethodSpec) value); + } else { + throw new IllegalArgumentException("Unsupported PaymentMethodSpec " + value); + } + } + } + + + // offer.price.spec + + public static class MarketPriceSpecMapping { + public static MarketPriceSpec toPojo(MarketPriceSpecDto dto) { + return new MarketPriceSpec(); + } + + public static MarketPriceSpecDto from(MarketPriceSpec value) { + return new MarketPriceSpecDto(); + } + } + + public static class FloatPriceSpecMapping { + public static FloatPriceSpec toPojo(FloatPriceSpecDto dto) { + return new FloatPriceSpec(dto.getPercentage()); + } + + public static FloatPriceSpecDto from(FloatPriceSpec value) { + return new FloatPriceSpecDto(value.getPercentage()); + } + } + + public static class FixPriceSpecMapping { + public static FixPriceSpec toPojo(FixPriceSpecDto dto) { + return new FixPriceSpec(PriceQuoteMapping.toPojo(dto.getPriceQuote())); + } + + public static FixPriceSpecDto from(FixPriceSpec value) { + return new FixPriceSpecDto(PriceQuoteMapping.from(value.getPriceQuote())); + } + } + + public static class PriceSpecMapping { + public static PriceSpec toPojo(PriceSpecDto dto) { + return switch (dto) { + case MarketPriceSpecDto marketPriceSpecDto -> MarketPriceSpecMapping.toPojo(marketPriceSpecDto); + case FixPriceSpecDto fixPriceSpecDto -> FixPriceSpecMapping.toPojo(fixPriceSpecDto); + case FloatPriceSpecDto floatPriceSpecDto -> FloatPriceSpecMapping.toPojo(floatPriceSpecDto); + case null, default -> throw new IllegalArgumentException("Unsupported PriceSpecDto " + dto); + }; + } + + public static PriceSpecDto from(PriceSpec value) { + return switch (value) { + case MarketPriceSpec marketPriceSpec -> MarketPriceSpecMapping.from(marketPriceSpec); + case FixPriceSpec fixPriceSpec -> FixPriceSpecMapping.from(fixPriceSpec); + case FloatPriceSpec floatPriceSpec -> FloatPriceSpecMapping.from(floatPriceSpec); + case null, default -> throw new IllegalArgumentException("Unsupported PriceSpec " + value); + }; + } + } + + + // security.keys + + public static class KeyPairDtoMapping { + public static KeyPair toPojo(KeyPairDto dto) { + PublicKey publicKey = PublicKeyMapping.toPojo(dto.publicKey()); + PrivateKey privateKey = PrivateKeyMapping.toPojo(dto.privateKey()); + return new KeyPair(publicKey, privateKey); + } + + public static KeyPairDto from(KeyPair value) { + PrivateKeyDto privateKeyDto = PrivateKeyMapping.from(value.getPrivate()); + PublicKeyDto publicKeyDto = PublicKeyMapping.from(value.getPublic()); + return new KeyPairDto(publicKeyDto, privateKeyDto); + } + } + + public static class PrivateKeyMapping { + public static PrivateKey toPojo(PrivateKeyDto dto) { + try { + byte[] decoded = Base64.getDecoder().decode(dto.encoded()); + return KeyGeneration.generatePrivate(decoded); + } catch (Exception e) { + throw new RuntimeException("Failed to generate privateKey", e); + } + } + + public static PrivateKeyDto from(PrivateKey value) { + return new PrivateKeyDto(Base64.getEncoder().encodeToString(value.getEncoded())); + } + } + + public static class PubKeyMapping { + public static PubKey toPojo(PubKeyDto dto) { + return new PubKey(PublicKeyMapping.toPojo(dto.publicKey()), dto.keyId()); + } + + public static PubKeyDto from(PubKey value) { + PublicKey publicKey = value.getPublicKey(); + PublicKeyDto publicKeyDto = PublicKeyMapping.from(publicKey); + String keyId = value.getKeyId(); + byte[] hashAsBytes = DigestUtil.hash(publicKey.getEncoded()); + String hash = Base64.getEncoder().encodeToString(hashAsBytes); + String id = Hex.encode(hashAsBytes); + return new PubKeyDto(publicKeyDto, keyId, hash, id); + } + } + + public static class PublicKeyMapping { + public static PublicKey toPojo(PublicKeyDto dto) { + try { + byte[] decoded = Base64.getDecoder().decode(dto.encoded()); + return KeyGeneration.generatePublic(decoded); + } catch (Exception e) { + throw new RuntimeException("Failed to generate publicKey", e); + } + } + + public static PublicKeyDto from(PublicKey value) { + return new PublicKeyDto(Base64.getEncoder().encodeToString(value.getEncoded())); + } + } + + + // security.pow + + public static class ProofOfWorkDtoMapping { + public static ProofOfWork toPojo(ProofOfWorkDto dto) { + return new ProofOfWork( + Base64.getDecoder().decode(dto.payload()), + dto.counter(), + dto.challenge() != null ? Base64.getDecoder().decode(dto.challenge()) : null, + dto.difficulty(), + Base64.getDecoder().decode(dto.solution()), + dto.duration() + ); + } + + public static ProofOfWorkDto from(ProofOfWork value) { + return new ProofOfWorkDto( + Base64.getEncoder().encodeToString(value.getPayload()), + value.getCounter(), + value.getChallenge() != null ? Base64.getEncoder().encodeToString(value.getChallenge()) : null, + value.getDifficulty(), + Base64.getEncoder().encodeToString(value.getSolution()), + value.getDuration() + ); + } + } + + + // user.profile + + public static class UserProfileDtoMapping { + public static UserProfile toPojo(UserProfileDto dto) { + return new UserProfile(dto.version(), + dto.nickName(), + ProofOfWorkDtoMapping.toPojo(dto.proofOfWork()), + dto.avatarVersion(), + NetworkIdMapping.toPojo(dto.networkId()), + dto.terms(), + dto.statement(), + dto.applicationVersion() + ); + } + + public static UserProfileDto from(UserProfile value) { + return new UserProfileDto( + value.getVersion(), + value.getNickName(), + ProofOfWorkDtoMapping.from(value.getProofOfWork()), + value.getAvatarVersion(), + NetworkIdMapping.from(value.getNetworkId()), + value.getTerms(), + value.getStatement(), + value.getApplicationVersion(), + value.getNym(), + value.getUserName(), + value.getPublishDate() + ); + } + } + + + // user.reputation + + public static class ReputationScoreMapping { + public static ReputationScore toPojo(ReputationScoreDto dto) { + return new ReputationScore(dto.totalScore(), dto.fiveSystemScore(), dto.ranking()); + } + + public static ReputationScoreDto from(ReputationScore value) { + return new ReputationScoreDto(value.getTotalScore(), value.getFiveSystemScore(), value.getRanking()); + } + } +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/OfferListItemDtoFactory.java b/http-api/src/main/java/bisq/dto/OfferListItemDtoFactory.java new file mode 100644 index 0000000000..6f5becf4a4 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/OfferListItemDtoFactory.java @@ -0,0 +1,123 @@ +/* + * 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.dto; + + +import bisq.account.payment_method.PaymentMethod; +import bisq.bonded_roles.market_price.MarketPriceService; +import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; +import bisq.common.currency.Market; +import bisq.dto.offer.bisq_easy.BisqEasyOfferDto; +import bisq.dto.offer.bisq_easy.OfferListItemDto; +import bisq.dto.user.reputation.ReputationScoreDto; +import bisq.i18n.Res; +import bisq.offer.Direction; +import bisq.offer.amount.OfferAmountFormatter; +import bisq.offer.amount.spec.AmountSpec; +import bisq.offer.amount.spec.RangeAmountSpec; +import bisq.offer.bisq_easy.BisqEasyOffer; +import bisq.offer.payment_method.PaymentMethodSpecUtil; +import bisq.offer.price.PriceUtil; +import bisq.offer.price.spec.PriceSpec; +import bisq.offer.price.spec.PriceSpecFormatter; +import bisq.presentation.formatters.DateFormatter; +import bisq.presentation.formatters.PriceFormatter; +import bisq.user.identity.UserIdentityService; +import bisq.user.profile.UserProfile; +import bisq.user.profile.UserProfileService; +import bisq.user.reputation.ReputationScore; +import bisq.user.reputation.ReputationService; + +import java.text.DateFormat; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class OfferListItemDtoFactory { + public static OfferListItemDto createOfferListItemDto(UserProfileService userProfileService, + UserIdentityService userIdentityService, + ReputationService reputationService, + MarketPriceService marketPriceService, + BisqEasyOfferbookMessage bisqEasyOfferbookMessage) { + BisqEasyOffer bisqEasyOffer = bisqEasyOfferbookMessage.getBisqEasyOffer().orElseThrow(); + boolean isMyOffer = bisqEasyOfferbookMessage.isMyMessage(userIdentityService); + Direction direction = bisqEasyOffer.getDirection(); + String messageId = bisqEasyOfferbookMessage.getId(); + String offerId = bisqEasyOffer.getId(); + BisqEasyOfferDto bisqEasyOfferDto = DtoMappings.BisqEasyOfferMapping.from(bisqEasyOffer); + String authorUserProfileId = bisqEasyOfferbookMessage.getAuthorUserProfileId(); + Optional<UserProfile> senderUserProfile = userProfileService.findUserProfile(authorUserProfileId); + String nym = senderUserProfile.map(UserProfile::getNym).orElse(""); + String userName = senderUserProfile.map(UserProfile::getUserName).orElse(""); + ReputationScoreDto reputationScore = senderUserProfile.flatMap(reputationService::findReputationScore) + .map(DtoMappings.ReputationScoreMapping::from) + .orElse(DtoMappings.ReputationScoreMapping.from(ReputationScore.NONE)); + + // For now, we send also the formatted values as we have not the complex formatters in mobile impl. yet. + // We might need to replicate the formatters anyway later and then those fields could be removed + long date = bisqEasyOfferbookMessage.getDate(); + String formattedDate = DateFormatter.formatDateTime(new Date(date), DateFormat.MEDIUM, DateFormat.SHORT, + true, " " + Res.get("temporal.at") + " "); + AmountSpec amountSpec = bisqEasyOffer.getAmountSpec(); + PriceSpec priceSpec = bisqEasyOffer.getPriceSpec(); + boolean hasAmountRange = amountSpec instanceof RangeAmountSpec; + Market market = bisqEasyOffer.getMarket(); + String formattedQuoteAmount = OfferAmountFormatter.formatQuoteAmount( + marketPriceService, + amountSpec, + priceSpec, + market, + hasAmountRange, + true + ); + String formattedBaseAmount = OfferAmountFormatter.formatBaseAmount( + marketPriceService, + amountSpec, + priceSpec, + market, + hasAmountRange, + true, + false + ); + String formattedPrice = PriceUtil.findQuote(marketPriceService, bisqEasyOffer) + .map(PriceFormatter::format) + .orElse(""); + String formattedPriceSpec = PriceSpecFormatter.getFormattedPriceSpec(priceSpec, true); + List<String> quoteSidePaymentMethods = PaymentMethodSpecUtil.getPaymentMethods(bisqEasyOffer.getQuoteSidePaymentMethodSpecs()) + .stream() + .map(PaymentMethod::getName) + .collect(Collectors.toList()); + List<String> baseSidePaymentMethods = PaymentMethodSpecUtil.getPaymentMethods(bisqEasyOffer.getBaseSidePaymentMethodSpecs()) + .stream() + .map(PaymentMethod::getName) + .collect(Collectors.toList()); + return new OfferListItemDto(bisqEasyOfferDto, + isMyOffer, + nym, + userName, + reputationScore, + formattedDate, + formattedQuoteAmount, + formattedBaseAmount, + formattedPrice, + formattedPriceSpec, + quoteSidePaymentMethods, + baseSidePaymentMethods); + } +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/account/protocol_type/TradeProtocolTypeDto.java b/http-api/src/main/java/bisq/dto/account/protocol_type/TradeProtocolTypeDto.java new file mode 100644 index 0000000000..10656f2be3 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/account/protocol_type/TradeProtocolTypeDto.java @@ -0,0 +1,30 @@ +/* + * 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.dto.account.protocol_type; + +public enum TradeProtocolTypeDto { + BISQ_EASY, + BISQ_MU_SIG, + SUBMARINE, + LIQUID_MU_SIG, + BISQ_LIGHTNING, + LIQUID_SWAP, + BSQ_SWAP, + LIGHTNING_ESCROW, + MONERO_SWAP; +} diff --git a/http-api/src/main/java/bisq/dto/common/currency/MarketDto.java b/http-api/src/main/java/bisq/dto/common/currency/MarketDto.java new file mode 100644 index 0000000000..35fa64a41b --- /dev/null +++ b/http-api/src/main/java/bisq/dto/common/currency/MarketDto.java @@ -0,0 +1,24 @@ +/* + * 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.dto.common.currency; + +public record MarketDto(String baseCurrencyCode, + String quoteCurrencyCode, + String baseCurrencyName, + String quoteCurrencyName) { +} diff --git a/http-api/src/main/java/bisq/dto/common/monetary/CoinDto.java b/http-api/src/main/java/bisq/dto/common/monetary/CoinDto.java new file mode 100644 index 0000000000..a5b2c22a92 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/common/monetary/CoinDto.java @@ -0,0 +1,38 @@ +/* + * 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.dto.common.monetary; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@ToString +@Getter +@EqualsAndHashCode(callSuper = true) +public final class CoinDto extends MonetaryDto { + @JsonCreator + public CoinDto(@JsonProperty("id") String id, + @JsonProperty("value") long value, + @JsonProperty("code") String code, + @JsonProperty("precision") int precision, + @JsonProperty("lowPrecision") int lowPrecision) { + super(id, value, code, precision, lowPrecision); + } +} diff --git a/http-api/src/main/java/bisq/dto/common/monetary/FiatDto.java b/http-api/src/main/java/bisq/dto/common/monetary/FiatDto.java new file mode 100644 index 0000000000..ac1c0edaf3 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/common/monetary/FiatDto.java @@ -0,0 +1,38 @@ +/* + * 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.dto.common.monetary; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@EqualsAndHashCode(callSuper = true) +public class FiatDto extends MonetaryDto { + @JsonCreator + public FiatDto(@JsonProperty("id") String id, + @JsonProperty("value") long value, + @JsonProperty("code") String code, + @JsonProperty("precision") int precision, + @JsonProperty("lowPrecision") int lowPrecision) { + super(id, value, code, precision, lowPrecision); + } +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/common/monetary/MonetaryDto.java b/http-api/src/main/java/bisq/dto/common/monetary/MonetaryDto.java new file mode 100644 index 0000000000..bdbf337832 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/common/monetary/MonetaryDto.java @@ -0,0 +1,50 @@ +/* + * 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.dto.common.monetary; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = CoinDto.class, name = "Coin"), + @JsonSubTypes.Type(value = FiatDto.class, name = "Fiat"), +}) +@EqualsAndHashCode +@Getter +public abstract class MonetaryDto { + protected final String id; + protected final long value; + protected final String code; + protected final int precision; + protected final int lowPrecision; + + protected MonetaryDto(String id, long value, String code, int precision, int lowPrecision) { + this.id = id; + this.value = value; + this.code = code; + this.precision = precision; + this.lowPrecision = lowPrecision; + } +} diff --git a/http-api/src/main/java/bisq/dto/common/monetary/PriceQuoteDto.java b/http-api/src/main/java/bisq/dto/common/monetary/PriceQuoteDto.java new file mode 100644 index 0000000000..aa03ed93b6 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/common/monetary/PriceQuoteDto.java @@ -0,0 +1,23 @@ +/* + * 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.dto.common.monetary; + +import bisq.dto.common.currency.MarketDto; + +public record PriceQuoteDto(long value, MarketDto market) { +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/common/network/AddressByTransportTypeMapDto.java b/http-api/src/main/java/bisq/dto/common/network/AddressByTransportTypeMapDto.java new file mode 100644 index 0000000000..1f3b125dae --- /dev/null +++ b/http-api/src/main/java/bisq/dto/common/network/AddressByTransportTypeMapDto.java @@ -0,0 +1,23 @@ +/* + * 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.dto.common.network; + +import java.util.Map; + +public record AddressByTransportTypeMapDto(Map<TransportTypeDto, AddressDto> map) { +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/common/network/AddressDto.java b/http-api/src/main/java/bisq/dto/common/network/AddressDto.java new file mode 100644 index 0000000000..0f0803257f --- /dev/null +++ b/http-api/src/main/java/bisq/dto/common/network/AddressDto.java @@ -0,0 +1,21 @@ +/* + * 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.dto.common.network; + +public record AddressDto(String host, int port) { +} diff --git a/http-api/src/main/java/bisq/dto/common/network/TransportTypeDto.java b/http-api/src/main/java/bisq/dto/common/network/TransportTypeDto.java new file mode 100644 index 0000000000..afb2bb30b6 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/common/network/TransportTypeDto.java @@ -0,0 +1,24 @@ +/* + * 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.dto.common.network; + +public enum TransportTypeDto { + TOR, + I2P, + CLEAR; +} diff --git a/http-api/src/main/java/bisq/dto/network/identity/NetworkIdDto.java b/http-api/src/main/java/bisq/dto/network/identity/NetworkIdDto.java new file mode 100644 index 0000000000..bf94bf878a --- /dev/null +++ b/http-api/src/main/java/bisq/dto/network/identity/NetworkIdDto.java @@ -0,0 +1,25 @@ +/* + * 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.dto.network.identity; + + +import bisq.dto.common.network.AddressByTransportTypeMapDto; +import bisq.dto.security.keys.PubKeyDto; + +public record NetworkIdDto(AddressByTransportTypeMapDto addressByTransportTypeMap, PubKeyDto pubKey) { +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/offer/DirectionDto.java b/http-api/src/main/java/bisq/dto/offer/DirectionDto.java new file mode 100644 index 0000000000..4d8a9ffde0 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/DirectionDto.java @@ -0,0 +1,23 @@ +/* + * 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.dto.offer; + +public enum DirectionDto { + BUY, + SELL; +} diff --git a/http-api/src/main/java/bisq/dto/offer/amount/spec/AmountSpecDto.java b/http-api/src/main/java/bisq/dto/offer/amount/spec/AmountSpecDto.java new file mode 100644 index 0000000000..443b454aba --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/amount/spec/AmountSpecDto.java @@ -0,0 +1,42 @@ +/* + * 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.dto.offer.amount.spec; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = BaseSideFixedAmountSpecDto.class, name = "BaseSideFixedAmountSpec"), + @JsonSubTypes.Type(value = BaseSideRangeAmountSpecDto.class, name = "BaseSideRangeAmountSpec"), + @JsonSubTypes.Type(value = QuoteSideFixedAmountSpecDto.class, name = "QuoteSideFixedAmountSpec"), + @JsonSubTypes.Type(value = QuoteSideRangeAmountSpecDto.class, name = "QuoteSideRangeAmountSpec") +}) +@Getter +@EqualsAndHashCode +public abstract class AmountSpecDto { + protected AmountSpecDto() { + } +} + diff --git a/http-api/src/main/java/bisq/dto/offer/amount/spec/BaseSideFixedAmountSpecDto.java b/http-api/src/main/java/bisq/dto/offer/amount/spec/BaseSideFixedAmountSpecDto.java new file mode 100644 index 0000000000..3cb3c62ce5 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/amount/spec/BaseSideFixedAmountSpecDto.java @@ -0,0 +1,34 @@ +/* + * 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.dto.offer.amount.spec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@ToString +@Getter +@EqualsAndHashCode(callSuper = true) +public class BaseSideFixedAmountSpecDto extends FixedAmountSpecDto { + @JsonCreator + public BaseSideFixedAmountSpecDto(@JsonProperty("amount") long amount) { + super(amount); + } +} diff --git a/http-api/src/main/java/bisq/dto/offer/amount/spec/BaseSideRangeAmountSpecDto.java b/http-api/src/main/java/bisq/dto/offer/amount/spec/BaseSideRangeAmountSpecDto.java new file mode 100644 index 0000000000..87f9252f59 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/amount/spec/BaseSideRangeAmountSpecDto.java @@ -0,0 +1,35 @@ +/* + * 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.dto.offer.amount.spec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@ToString +@Getter +@EqualsAndHashCode(callSuper = true) +public class BaseSideRangeAmountSpecDto extends RangeAmountSpecDto { + @JsonCreator + public BaseSideRangeAmountSpecDto(@JsonProperty("minAmount") long minAmount, + @JsonProperty("maxAmount") long maxAmount) { + super(minAmount, maxAmount); + } +} diff --git a/http-api/src/main/java/bisq/dto/offer/amount/spec/FixedAmountSpecDto.java b/http-api/src/main/java/bisq/dto/offer/amount/spec/FixedAmountSpecDto.java new file mode 100644 index 0000000000..7ae7366668 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/amount/spec/FixedAmountSpecDto.java @@ -0,0 +1,32 @@ +/* + * 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.dto.offer.amount.spec; + +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode(callSuper = true) +public abstract class FixedAmountSpecDto extends AmountSpecDto { + public long amount; + + public FixedAmountSpecDto(long amount) { + super(); + this.amount = amount; + } +} diff --git a/http-api/src/main/java/bisq/dto/offer/amount/spec/QuoteSideFixedAmountSpecDto.java b/http-api/src/main/java/bisq/dto/offer/amount/spec/QuoteSideFixedAmountSpecDto.java new file mode 100644 index 0000000000..44ca872380 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/amount/spec/QuoteSideFixedAmountSpecDto.java @@ -0,0 +1,34 @@ +/* + * 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.dto.offer.amount.spec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@ToString +@Getter +@EqualsAndHashCode(callSuper = true) +public class QuoteSideFixedAmountSpecDto extends FixedAmountSpecDto { + @JsonCreator + public QuoteSideFixedAmountSpecDto(@JsonProperty("amount") long amount) { + super(amount); + } +} diff --git a/http-api/src/main/java/bisq/dto/offer/amount/spec/QuoteSideRangeAmountSpecDto.java b/http-api/src/main/java/bisq/dto/offer/amount/spec/QuoteSideRangeAmountSpecDto.java new file mode 100644 index 0000000000..c881dec32e --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/amount/spec/QuoteSideRangeAmountSpecDto.java @@ -0,0 +1,35 @@ +/* + * 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.dto.offer.amount.spec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@ToString +@Getter +@EqualsAndHashCode(callSuper = true) +public class QuoteSideRangeAmountSpecDto extends RangeAmountSpecDto { + @JsonCreator + public QuoteSideRangeAmountSpecDto(@JsonProperty("minAmount") long minAmount, + @JsonProperty("maxAmount") long maxAmount) { + super(minAmount, maxAmount); + } +} diff --git a/http-api/src/main/java/bisq/dto/offer/amount/spec/RangeAmountSpecDto.java b/http-api/src/main/java/bisq/dto/offer/amount/spec/RangeAmountSpecDto.java new file mode 100644 index 0000000000..b80975d562 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/amount/spec/RangeAmountSpecDto.java @@ -0,0 +1,34 @@ +/* + * 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.dto.offer.amount.spec; + +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode(callSuper = true) +public abstract class RangeAmountSpecDto extends AmountSpecDto { + protected final long minAmount; + protected final long maxAmount; + + public RangeAmountSpecDto(long minAmount, long maxAmount) { + super(); + this.minAmount = minAmount; + this.maxAmount = maxAmount; + } +} diff --git a/http-api/src/main/java/bisq/dto/offer/bisq_easy/BisqEasyOfferDto.java b/http-api/src/main/java/bisq/dto/offer/bisq_easy/BisqEasyOfferDto.java new file mode 100644 index 0000000000..02007548a9 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/bisq_easy/BisqEasyOfferDto.java @@ -0,0 +1,44 @@ +/* + * 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.dto.offer.bisq_easy; + +import bisq.dto.account.protocol_type.TradeProtocolTypeDto; +import bisq.dto.common.currency.MarketDto; +import bisq.dto.network.identity.NetworkIdDto; +import bisq.dto.offer.DirectionDto; +import bisq.dto.offer.amount.spec.AmountSpecDto; +import bisq.dto.offer.options.OfferOptionDto; +import bisq.dto.offer.payment_method.BitcoinPaymentMethodSpecDto; +import bisq.dto.offer.payment_method.FiatPaymentMethodSpecDto; +import bisq.dto.offer.price.spec.PriceSpecDto; + +import java.util.List; + +public record BisqEasyOfferDto(String id, + long date, + NetworkIdDto makerNetworkId, + DirectionDto direction, + MarketDto market, + AmountSpecDto amountSpec, + PriceSpecDto priceSpec, + List<TradeProtocolTypeDto> protocolTypes, + List<BitcoinPaymentMethodSpecDto> baseSidePaymentMethodSpecs, + List<FiatPaymentMethodSpecDto> quoteSidePaymentMethodSpecs, + List<OfferOptionDto> offerOptions, + List<String> supportedLanguageCodes) { +} diff --git a/http-api/src/main/java/bisq/dto/offer/bisq_easy/OfferListItemDto.java b/http-api/src/main/java/bisq/dto/offer/bisq_easy/OfferListItemDto.java new file mode 100644 index 0000000000..63598edf13 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/bisq_easy/OfferListItemDto.java @@ -0,0 +1,73 @@ +/* + * 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.dto.offer.bisq_easy; + +import bisq.dto.user.reputation.ReputationScoreDto; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +import java.util.List; + +@Getter +@ToString +@EqualsAndHashCode +public class OfferListItemDto { + private final BisqEasyOfferDto bisqEasyOffer; + @JsonProperty("isMyOffer") + private final boolean isMyOffer; + private final String nym; + private final String userName; + private final ReputationScoreDto reputationScore; + private final String formattedDate; + private final String formattedQuoteAmount; + private final String formattedBaseAmount; + private final String formattedPrice; + private final String formattedPriceSpec; + private final List<String> quoteSidePaymentMethods; + private final List<String> baseSidePaymentMethods; + + @JsonCreator + public OfferListItemDto(BisqEasyOfferDto bisqEasyOffer, + boolean isMyOffer, + String nym, + String userName, + ReputationScoreDto reputationScore, + String formattedDate, + String formattedQuoteAmount, + String formattedBaseAmount, + String formattedPrice, + String formattedPriceSpec, + List<String> quoteSidePaymentMethods, + List<String> baseSidePaymentMethods) { + this.bisqEasyOffer = bisqEasyOffer; + this.isMyOffer = isMyOffer; + this.nym = nym; + this.userName = userName; + this.reputationScore = reputationScore; + this.formattedDate = formattedDate; + this.formattedQuoteAmount = formattedQuoteAmount; + this.formattedBaseAmount = formattedBaseAmount; + this.formattedPrice = formattedPrice; + this.formattedPriceSpec = formattedPriceSpec; + this.quoteSidePaymentMethods = quoteSidePaymentMethods; + this.baseSidePaymentMethods = baseSidePaymentMethods; + } +} diff --git a/http-api/src/main/java/bisq/dto/offer/options/OfferOptionDto.java b/http-api/src/main/java/bisq/dto/offer/options/OfferOptionDto.java new file mode 100644 index 0000000000..677446eace --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/options/OfferOptionDto.java @@ -0,0 +1,40 @@ +/* + * 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.dto.offer.options; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.EqualsAndHashCode; +import lombok.Getter; + + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = ReputationOptionDto.class, name = "ReputationOption"), + @JsonSubTypes.Type(value = TradeTermsOptionDto.class, name = "TradeTermsOption"), +}) +@Getter +@EqualsAndHashCode +public abstract class OfferOptionDto { + protected OfferOptionDto() { + } +} diff --git a/http-api/src/main/java/bisq/dto/offer/options/ReputationOptionDto.java b/http-api/src/main/java/bisq/dto/offer/options/ReputationOptionDto.java new file mode 100644 index 0000000000..55ca3efdb8 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/options/ReputationOptionDto.java @@ -0,0 +1,37 @@ +/* + * 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.dto.offer.options; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@EqualsAndHashCode(callSuper = true) +public final class ReputationOptionDto extends OfferOptionDto { + @Deprecated(since = "2.1.1") + private final long requiredTotalReputationScore; + + @JsonCreator + public ReputationOptionDto(@JsonProperty("requiredTotalReputationScore") long requiredTotalReputationScore) { + this.requiredTotalReputationScore = requiredTotalReputationScore; + } +} diff --git a/http-api/src/main/java/bisq/dto/offer/options/TradeTermsOptionDto.java b/http-api/src/main/java/bisq/dto/offer/options/TradeTermsOptionDto.java new file mode 100644 index 0000000000..c6301e9f48 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/options/TradeTermsOptionDto.java @@ -0,0 +1,36 @@ +/* + * 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.dto.offer.options; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@EqualsAndHashCode(callSuper = true) +public final class TradeTermsOptionDto extends OfferOptionDto { + private final String makersTradeTerms; + + @JsonCreator + public TradeTermsOptionDto(@JsonProperty("makersTradeTerms") String makersTradeTerms) { + this.makersTradeTerms = makersTradeTerms; + } +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/offer/payment_method/BitcoinPaymentMethodSpecDto.java b/http-api/src/main/java/bisq/dto/offer/payment_method/BitcoinPaymentMethodSpecDto.java new file mode 100644 index 0000000000..0660e92d0e --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/payment_method/BitcoinPaymentMethodSpecDto.java @@ -0,0 +1,39 @@ +/* + * 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.dto.offer.payment_method; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +import java.util.Optional; + +@Getter +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class BitcoinPaymentMethodSpecDto extends PaymentMethodSpecDto { + + @JsonCreator + public BitcoinPaymentMethodSpecDto(@JsonProperty("paymentMethod") String paymentMethod, @JsonProperty("saltedMakerAccountId") Optional<String> saltedMakerAccountId) { + super(paymentMethod, saltedMakerAccountId); + } +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/offer/payment_method/FiatPaymentMethodSpecDto.java b/http-api/src/main/java/bisq/dto/offer/payment_method/FiatPaymentMethodSpecDto.java new file mode 100644 index 0000000000..2ce096fcd0 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/payment_method/FiatPaymentMethodSpecDto.java @@ -0,0 +1,38 @@ +/* + * 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.dto.offer.payment_method; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +import java.util.Optional; + +@Getter +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class FiatPaymentMethodSpecDto extends PaymentMethodSpecDto { + @JsonCreator + public FiatPaymentMethodSpecDto(@JsonProperty("paymentMethod") String paymentMethod, @JsonProperty("saltedMakerAccountId") Optional<String> saltedMakerAccountId) { + super(paymentMethod, saltedMakerAccountId); + } +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/offer/payment_method/PaymentMethodSpecDto.java b/http-api/src/main/java/bisq/dto/offer/payment_method/PaymentMethodSpecDto.java new file mode 100644 index 0000000000..15fc3c358c --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/payment_method/PaymentMethodSpecDto.java @@ -0,0 +1,55 @@ +/* + * 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.dto.offer.payment_method; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +import java.util.Optional; + +@ToString +@Getter +@EqualsAndHashCode + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = FiatPaymentMethodSpecDto.class, name = "FiatPaymentMethodSpec"), + @JsonSubTypes.Type(value = BitcoinPaymentMethodSpecDto.class, name = "BitcoinPaymentMethodSpec"), +}) +@JsonInclude(JsonInclude.Include.NON_NULL) +public abstract class PaymentMethodSpecDto { + protected final Optional<String> saltedMakerAccountId; + protected final String paymentMethod; + + protected PaymentMethodSpecDto(String paymentMethod) { + this(paymentMethod, Optional.empty()); + } + + protected PaymentMethodSpecDto(String paymentMethod, Optional<String> saltedMakerAccountId) { + this.paymentMethod = paymentMethod; + this.saltedMakerAccountId = saltedMakerAccountId; + } +} diff --git a/http-api/src/main/java/bisq/dto/offer/price/spec/FixPriceSpecDto.java b/http-api/src/main/java/bisq/dto/offer/price/spec/FixPriceSpecDto.java new file mode 100644 index 0000000000..5e5e68f480 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/price/spec/FixPriceSpecDto.java @@ -0,0 +1,37 @@ +/* + * 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.dto.offer.price.spec; + +import bisq.dto.common.monetary.PriceQuoteDto; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@ToString +@Getter +@EqualsAndHashCode(callSuper = true) +public class FixPriceSpecDto extends PriceSpecDto { + private final PriceQuoteDto priceQuote; + + @JsonCreator + public FixPriceSpecDto(@JsonProperty("priceQuote") PriceQuoteDto priceQuote) { + this.priceQuote = priceQuote; + } +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/offer/price/spec/FloatPriceSpecDto.java b/http-api/src/main/java/bisq/dto/offer/price/spec/FloatPriceSpecDto.java new file mode 100644 index 0000000000..a0ee7da649 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/price/spec/FloatPriceSpecDto.java @@ -0,0 +1,36 @@ +/* + * 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.dto.offer.price.spec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@ToString +@Getter +@EqualsAndHashCode(callSuper = true) +public class FloatPriceSpecDto extends PriceSpecDto { + private final double percentage; + + @JsonCreator + public FloatPriceSpecDto(@JsonProperty("percentage") double percentage) { + this.percentage = percentage; + } +} diff --git a/http-api/src/main/java/bisq/dto/offer/price/spec/MarketPriceSpecDto.java b/http-api/src/main/java/bisq/dto/offer/price/spec/MarketPriceSpecDto.java new file mode 100644 index 0000000000..382ff00a06 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/price/spec/MarketPriceSpecDto.java @@ -0,0 +1,31 @@ +/* + * 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.dto.offer.price.spec; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@ToString +@Getter +@EqualsAndHashCode(callSuper = true) +//@JsonIgnoreProperties(ignoreUnknown = true) +public class MarketPriceSpecDto extends PriceSpecDto { + public MarketPriceSpecDto() { + } +} diff --git a/http-api/src/main/java/bisq/dto/offer/price/spec/PriceSpecDto.java b/http-api/src/main/java/bisq/dto/offer/price/spec/PriceSpecDto.java new file mode 100644 index 0000000000..3916d377c2 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/offer/price/spec/PriceSpecDto.java @@ -0,0 +1,41 @@ +/* + * 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.dto.offer.price.spec; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@EqualsAndHashCode +@Getter +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = FixPriceSpecDto.class, name = "FixPriceSpec"), + @JsonSubTypes.Type(value = FloatPriceSpecDto.class, name = "FloatPriceSpec"), + @JsonSubTypes.Type(value = MarketPriceSpecDto.class, name = "MarketPriceSpec") +}) + +public abstract class PriceSpecDto { + protected PriceSpecDto() { + } +} diff --git a/http-api/src/main/java/bisq/dto/security/keys/KeyPairDto.java b/http-api/src/main/java/bisq/dto/security/keys/KeyPairDto.java new file mode 100644 index 0000000000..44bc46e392 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/security/keys/KeyPairDto.java @@ -0,0 +1,21 @@ +/* + * 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.dto.security.keys; + +public record KeyPairDto(PublicKeyDto publicKey, PrivateKeyDto privateKey) { +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/security/keys/PrivateKeyDto.java b/http-api/src/main/java/bisq/dto/security/keys/PrivateKeyDto.java new file mode 100644 index 0000000000..57fb23e1a2 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/security/keys/PrivateKeyDto.java @@ -0,0 +1,22 @@ +/* + * 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.dto.security.keys; + + +public record PrivateKeyDto(String encoded) { +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/security/keys/PubKeyDto.java b/http-api/src/main/java/bisq/dto/security/keys/PubKeyDto.java new file mode 100644 index 0000000000..befd24201b --- /dev/null +++ b/http-api/src/main/java/bisq/dto/security/keys/PubKeyDto.java @@ -0,0 +1,21 @@ +/* + * 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.dto.security.keys; + +public record PubKeyDto(PublicKeyDto publicKey, String keyId, String hash, String id) { +} diff --git a/http-api/src/main/java/bisq/dto/security/keys/PublicKeyDto.java b/http-api/src/main/java/bisq/dto/security/keys/PublicKeyDto.java new file mode 100644 index 0000000000..da0354e285 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/security/keys/PublicKeyDto.java @@ -0,0 +1,22 @@ +/* + * 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.dto.security.keys; + + +public record PublicKeyDto(String encoded) { +} \ No newline at end of file diff --git a/http-api/src/main/java/bisq/dto/security/pow/ProofOfWorkDto.java b/http-api/src/main/java/bisq/dto/security/pow/ProofOfWorkDto.java new file mode 100644 index 0000000000..6d661320c5 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/security/pow/ProofOfWorkDto.java @@ -0,0 +1,29 @@ +/* + * 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.dto.security.pow; + +import javax.annotation.Nullable; + +public record ProofOfWorkDto( + String payload, + long counter, + @Nullable String challenge, + double difficulty, + String solution, + long duration) { +} diff --git a/http-api/src/main/java/bisq/dto/user/profile/UserProfileDto.java b/http-api/src/main/java/bisq/dto/user/profile/UserProfileDto.java new file mode 100644 index 0000000000..51df686dca --- /dev/null +++ b/http-api/src/main/java/bisq/dto/user/profile/UserProfileDto.java @@ -0,0 +1,34 @@ +/* + * 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.dto.user.profile; + +import bisq.dto.network.identity.NetworkIdDto; +import bisq.dto.security.pow.ProofOfWorkDto; + +public record UserProfileDto(int version, + String nickName, + ProofOfWorkDto proofOfWork, + int avatarVersion, + NetworkIdDto networkId, + String terms, + String statement, + String applicationVersion, + String nym, + String userName, + long publishDate) { +} diff --git a/http-api/src/main/java/bisq/dto/user/reputation/ReputationScoreDto.java b/http-api/src/main/java/bisq/dto/user/reputation/ReputationScoreDto.java new file mode 100644 index 0000000000..cf6dcce0a2 --- /dev/null +++ b/http-api/src/main/java/bisq/dto/user/reputation/ReputationScoreDto.java @@ -0,0 +1,21 @@ +/* + * 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.dto.user.reputation; + +public record ReputationScoreDto(long totalScore, double fiveSystemScore, int ranking) { +} diff --git a/http-api/src/main/java/bisq/http_api/HttpApiService.java b/http-api/src/main/java/bisq/http_api/HttpApiService.java index 127d695751..682ca6b07f 100644 --- a/http-api/src/main/java/bisq/http_api/HttpApiService.java +++ b/http-api/src/main/java/bisq/http_api/HttpApiService.java @@ -18,19 +18,23 @@ package bisq.http_api; import bisq.bonded_roles.BondedRolesService; -import bisq.http_api.rest_api.domain.market_price.MarketPriceRestApi; import bisq.chat.ChatService; -import bisq.http_api.rest_api.domain.offerbook.OfferbookRestApi; import bisq.common.application.Service; import bisq.common.util.CompletableFutureUtils; import bisq.http_api.rest_api.RestApiResourceConfig; import bisq.http_api.rest_api.RestApiService; +import bisq.http_api.rest_api.domain.market_price.MarketPriceRestApi; +import bisq.http_api.rest_api.domain.offer.OfferRestApi; +import bisq.http_api.rest_api.domain.offerbook.OfferbookRestApi; +import bisq.http_api.rest_api.domain.trade.TradeRestApi; +import bisq.http_api.rest_api.domain.user_identity.UserIdentityRestApi; import bisq.http_api.web_socket.WebSocketRestApiResourceConfig; import bisq.http_api.web_socket.WebSocketService; import bisq.network.NetworkService; import bisq.security.SecurityService; +import bisq.support.SupportService; +import bisq.trade.TradeService; import bisq.user.UserService; -import bisq.http_api.rest_api.domain.user_identity.UserIdentityRestApi; import lombok.extern.slf4j.Slf4j; import java.util.Optional; @@ -51,18 +55,35 @@ public HttpApiService(RestApiService.Config restApiConfig, NetworkService networkService, UserService userService, BondedRolesService bondedRolesService, - ChatService chatService) { + ChatService chatService, + SupportService supportedService, + TradeService tradeService) { boolean restApiConfigEnabled = restApiConfig.isEnabled(); boolean webSocketConfigEnabled = webSocketConfig.isEnabled(); if (restApiConfigEnabled || webSocketConfigEnabled) { OfferbookRestApi offerbookRestApi = new OfferbookRestApi(chatService.getBisqEasyOfferbookChannelService(), bondedRolesService.getMarketPriceService(), userService); + OfferRestApi offerRestApi = new OfferRestApi(chatService, + bondedRolesService.getMarketPriceService(), + userService, + supportedService, + tradeService); + TradeRestApi tradeRestApi = new TradeRestApi(chatService, + bondedRolesService.getMarketPriceService(), + userService, + supportedService, + tradeService); UserIdentityRestApi userIdentityRestApi = new UserIdentityRestApi(securityService, userService.getUserIdentityService()); MarketPriceRestApi marketPriceRestApi = new MarketPriceRestApi(bondedRolesService.getMarketPriceService()); if (restApiConfigEnabled) { - var restApiResourceConfig = new RestApiResourceConfig(restApiConfig.getRestApiBaseUrl(), offerbookRestApi, userIdentityRestApi, marketPriceRestApi); + var restApiResourceConfig = new RestApiResourceConfig(restApiConfig.getRestApiBaseUrl(), + offerbookRestApi, + offerRestApi, + tradeRestApi, + userIdentityRestApi, + marketPriceRestApi); this.restApiService = Optional.of(new RestApiService(restApiConfig, restApiResourceConfig)); } else { this.restApiService = Optional.empty(); @@ -71,6 +92,8 @@ public HttpApiService(RestApiService.Config restApiConfig, if (webSocketConfigEnabled) { var webSocketResourceConfig = new WebSocketRestApiResourceConfig(webSocketConfig.getRestApiBaseUrl(), offerbookRestApi, + offerRestApi, + tradeRestApi, userIdentityRestApi, marketPriceRestApi); this.webSocketService = Optional.of(new WebSocketService(webSocketConfig, diff --git a/http-api/src/main/java/bisq/http_api/rest_api/RestApiResourceConfig.java b/http-api/src/main/java/bisq/http_api/rest_api/RestApiResourceConfig.java index 5966cbb814..24c81f3d57 100644 --- a/http-api/src/main/java/bisq/http_api/rest_api/RestApiResourceConfig.java +++ b/http-api/src/main/java/bisq/http_api/rest_api/RestApiResourceConfig.java @@ -1,7 +1,9 @@ package bisq.http_api.rest_api; import bisq.http_api.rest_api.domain.market_price.MarketPriceRestApi; +import bisq.http_api.rest_api.domain.offer.OfferRestApi; import bisq.http_api.rest_api.domain.offerbook.OfferbookRestApi; +import bisq.http_api.rest_api.domain.trade.TradeRestApi; import bisq.http_api.rest_api.domain.user_identity.UserIdentityRestApi; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -12,6 +14,8 @@ public class RestApiResourceConfig extends BaseRestApiResourceConfig { public RestApiResourceConfig(String swaggerBaseUrl, OfferbookRestApi offerbookRestApi, + OfferRestApi offerRestApi, + TradeRestApi tradeRestApi, UserIdentityRestApi userIdentityRestApi , MarketPriceRestApi marketPriceRestApi) { super(swaggerBaseUrl); @@ -22,6 +26,8 @@ public RestApiResourceConfig(String swaggerBaseUrl, // As we want to pass the dependencies in the constructor, so we need the hack // with AbstractBinder to register resources as classes for Swagger register(OfferbookRestApi.class); + register(OfferRestApi.class); + register(TradeRestApi.class); register(UserIdentityRestApi.class); register(MarketPriceRestApi.class); @@ -29,6 +35,8 @@ public RestApiResourceConfig(String swaggerBaseUrl, @Override protected void configure() { bind(offerbookRestApi).to(OfferbookRestApi.class); + bind(offerRestApi).to(OfferRestApi.class); + bind(tradeRestApi).to(TradeRestApi.class); bind(userIdentityRestApi).to(UserIdentityRestApi.class); bind(marketPriceRestApi).to(MarketPriceRestApi.class); } diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/market_price/MarketPriceRestApi.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/market_price/MarketPriceRestApi.java index 4635c58176..c0358680a8 100644 --- a/http-api/src/main/java/bisq/http_api/rest_api/domain/market_price/MarketPriceRestApi.java +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/market_price/MarketPriceRestApi.java @@ -20,6 +20,8 @@ import bisq.bonded_roles.market_price.MarketPrice; import bisq.bonded_roles.market_price.MarketPriceService; import bisq.common.currency.Market; +import bisq.dto.DtoMappings; +import bisq.dto.common.monetary.PriceQuoteDto; import bisq.http_api.rest_api.domain.RestApiBase; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -58,7 +60,7 @@ public MarketPriceRestApi(MarketPriceService marketPriceService) { @ApiResponse( responseCode = "200", description = "Market price quotes retrieved successfully", - content = @Content(schema = @Schema(implementation = MarketPriceResponse.class)) + content = @Content(schema = @Schema(implementation = QuotesResponse.class)) ), @ApiResponse(responseCode = "404", description = "Market price quotes not found"), @ApiResponse(responseCode = "500", description = "Internal server error") @@ -67,19 +69,19 @@ public MarketPriceRestApi(MarketPriceService marketPriceService) { public Response getQuotes() { try { Map<Market, MarketPrice> marketPriceByCurrencyMap = marketPriceService.getMarketPriceByCurrencyMap(); - Map<String, Long> result = marketPriceService.getMarketPriceByCurrencyMap() + Map<String, PriceQuoteDto> result = marketPriceService.getMarketPriceByCurrencyMap() .entrySet().stream() .filter(entry->entry.getKey().getBaseCurrencyCode().equals("BTC")) // We get altcoin quotes as well .collect(Collectors.toMap( entry -> entry.getKey().getQuoteCurrencyCode(), - entry -> entry.getValue().getPriceQuote().getValue() + entry -> DtoMappings.PriceQuoteMapping.from(entry.getValue().getPriceQuote()) )); if (result.isEmpty()) { return buildNotFoundResponse("No market price quotes found."); } - return buildOkResponse(new MarketPriceResponse(result)); + return buildOkResponse(new QuotesResponse(result)); } catch (Exception ex) { log.error("Failed to retrieve market price quotes", ex); return buildErrorResponse("An error occurred while retrieving market prices."); diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/market_price/MarketPriceResponse.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/market_price/QuotesResponse.java similarity index 75% rename from http-api/src/main/java/bisq/http_api/rest_api/domain/market_price/MarketPriceResponse.java rename to http-api/src/main/java/bisq/http_api/rest_api/domain/market_price/QuotesResponse.java index c500bdf00f..a192a4bebf 100644 --- a/http-api/src/main/java/bisq/http_api/rest_api/domain/market_price/MarketPriceResponse.java +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/market_price/QuotesResponse.java @@ -17,20 +17,14 @@ package bisq.http_api.rest_api.domain.market_price; +import bisq.dto.common.monetary.PriceQuoteDto; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; import java.util.Map; /** * Response DTO for market price quotes. */ -@Getter -public class MarketPriceResponse { - @Schema(description = "Map of currency codes to market price quotes") - private final Map<String, Long> quotes; - - public MarketPriceResponse(Map<String, Long> quotes) { - this.quotes = quotes; - } +public record QuotesResponse( + @Schema(description = "Map of currency codes to market price quotes") Map<String, PriceQuoteDto> quotes) { } diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/offer/CreateOfferRequest.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/offer/CreateOfferRequest.java new file mode 100644 index 0000000000..5adf13f115 --- /dev/null +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/offer/CreateOfferRequest.java @@ -0,0 +1,34 @@ +/* + * 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.http_api.rest_api.domain.offer; + +import bisq.dto.common.currency.MarketDto; +import bisq.dto.offer.DirectionDto; +import bisq.dto.offer.amount.spec.AmountSpecDto; +import bisq.dto.offer.price.spec.PriceSpecDto; + +import java.util.Set; + +public record CreateOfferRequest(DirectionDto direction, + MarketDto market, + Set<String> bitcoinPaymentMethods, + Set<String> fiatPaymentMethods, + AmountSpecDto amountSpec, + PriceSpecDto priceSpec, + Set<String> supportedLanguageCodes) { +} diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/offer/CreateOfferResponse.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/offer/CreateOfferResponse.java new file mode 100644 index 0000000000..fac78179f6 --- /dev/null +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/offer/CreateOfferResponse.java @@ -0,0 +1,21 @@ +/* + * 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.http_api.rest_api.domain.offer; + +public record CreateOfferResponse(String offerId) { +} diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/offer/OfferRestApi.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/offer/OfferRestApi.java new file mode 100644 index 0000000000..b0bccda22d --- /dev/null +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/offer/OfferRestApi.java @@ -0,0 +1,170 @@ +/* + * 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.http_api.rest_api.domain.offer; + +import bisq.account.payment_method.BitcoinPaymentMethod; +import bisq.account.payment_method.BitcoinPaymentMethodUtil; +import bisq.account.payment_method.FiatPaymentMethod; +import bisq.account.payment_method.FiatPaymentMethodUtil; +import bisq.bisq_easy.BisqEasyServiceUtil; +import bisq.bonded_roles.market_price.MarketPriceService; +import bisq.chat.ChatService; +import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannelService; +import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; +import bisq.chat.bisqeasy.open_trades.BisqEasyOpenTradeChannelService; +import bisq.common.currency.Market; +import bisq.dto.DtoMappings; +import bisq.http_api.rest_api.domain.RestApiBase; +import bisq.offer.Direction; +import bisq.offer.amount.spec.AmountSpec; +import bisq.offer.bisq_easy.BisqEasyOffer; +import bisq.offer.price.spec.PriceSpec; +import bisq.support.SupportService; +import bisq.support.mediation.MediationRequestService; +import bisq.trade.TradeService; +import bisq.trade.bisq_easy.BisqEasyTradeService; +import bisq.user.UserService; +import bisq.user.banned.BannedUserService; +import bisq.user.identity.UserIdentity; +import bisq.user.identity.UserIdentityService; +import bisq.user.profile.UserProfile; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.container.AsyncResponse; +import jakarta.ws.rs.container.Suspended; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Slf4j +@Path("/offers") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Tag(name = "Bisq Easy Offer API") +public class OfferRestApi extends RestApiBase { + private final BisqEasyOfferbookChannelService bisqEasyOfferbookChannelService; + private final ChatService chatService; + private final MarketPriceService marketPriceService; + private final UserIdentityService userIdentityService; + private final BannedUserService bannedUserService; + private final MediationRequestService mediationRequestService; + private final BisqEasyTradeService bisqEasyTradeService; + private final BisqEasyOpenTradeChannelService bisqEasyOpenTradeChannelService; + + public OfferRestApi(ChatService chatService, + MarketPriceService marketPriceService, + UserService userService, + SupportService supportedService, + TradeService tradeService) { + this.bisqEasyOfferbookChannelService = chatService.getBisqEasyOfferbookChannelService(); + this.chatService = chatService; + this.marketPriceService = marketPriceService; + userIdentityService = userService.getUserIdentityService(); + bannedUserService = userService.getBannedUserService(); + mediationRequestService = supportedService.getMediationRequestService(); + bisqEasyTradeService = tradeService.getBisqEasyTradeService(); + bisqEasyOpenTradeChannelService = chatService.getBisqEasyOpenTradeChannelService(); + } + + @POST + @Operation( + summary = "Create and Publish Bisq Easy Offer", + description = "Creates a Bisq Easy Offer and publish it to the network.", + requestBody = @RequestBody( + description = "", + content = @Content(schema = @Schema(implementation = CreateOfferRequest.class)) + ), + responses = { + @ApiResponse(responseCode = "201", description = "", + content = @Content(schema = @Schema(example = ""))), + @ApiResponse(responseCode = "400", description = "Invalid input"), + @ApiResponse(responseCode = "500", description = "Internal server error") + } + ) + public void createOffer(CreateOfferRequest request, @Suspended AsyncResponse asyncResponse) { + asyncResponse.setTimeout(10, TimeUnit.SECONDS); + asyncResponse.setTimeoutHandler(response -> { + response.resume(buildResponse(Response.Status.SERVICE_UNAVAILABLE, "Request timed out")); + }); + try { + UserIdentity userIdentity = userIdentityService.getSelectedUserIdentity(); + Direction direction = DtoMappings.DirectionMapping.toPojo(request.direction()); + Market market = DtoMappings.MarketMapping.toPojo(request.market()); + List<BitcoinPaymentMethod> bitcoinPaymentMethods = request.bitcoinPaymentMethods().stream() + .map(BitcoinPaymentMethodUtil::getPaymentMethod) + .collect(Collectors.toList()); + List<FiatPaymentMethod> fiatPaymentMethods = request.fiatPaymentMethods().stream() + .map(FiatPaymentMethodUtil::getPaymentMethod) + .collect(Collectors.toList()); + AmountSpec amountSpec = DtoMappings.AmountSpecMapping.toPojo(request.amountSpec()); + PriceSpec priceSpec = DtoMappings.PriceSpecMapping.toPojo(request.priceSpec()); + List<String> supportedLanguageCodes = new ArrayList<>(request.supportedLanguageCodes()); + String chatMessageText = BisqEasyServiceUtil.createOfferBookMessageFromPeerPerspective(userIdentity.getNickName(), + marketPriceService, + direction, + market, + bitcoinPaymentMethods, + fiatPaymentMethods, + amountSpec, + priceSpec); + UserProfile userProfile = userIdentity.getUserProfile(); + BisqEasyOffer bisqEasyOffer = new BisqEasyOffer( + userProfile.getNetworkId(), + direction, + market, + amountSpec, + priceSpec, + bitcoinPaymentMethods, + fiatPaymentMethods, + userProfile.getTerms(), + supportedLanguageCodes); + String channelId = bisqEasyOfferbookChannelService.findChannel(market).orElseThrow().getId(); + BisqEasyOfferbookMessage myOfferMessage = new BisqEasyOfferbookMessage(channelId, + userProfile.getId(), + Optional.of(bisqEasyOffer), + Optional.of(chatMessageText), + Optional.empty(), + new Date().getTime(), + false); + bisqEasyOfferbookChannelService.publishChatMessage(myOfferMessage, userIdentity).get(); + asyncResponse.resume(buildResponse(Response.Status.CREATED, new CreateOfferResponse(bisqEasyOffer.getId()))); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + asyncResponse.resume(buildErrorResponse("Thread was interrupted.")); + } catch (IllegalArgumentException e) { + asyncResponse.resume(buildResponse(Response.Status.BAD_REQUEST, "Invalid input: " + e.getMessage())); + } catch (Exception e) { + asyncResponse.resume(buildErrorResponse("An unexpected error occurred.")); + } + } +} diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/offerbook/OfferListItemDto.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/offerbook/OfferListItemDto.java deleted file mode 100644 index 500befd577..0000000000 --- a/http-api/src/main/java/bisq/http_api/rest_api/domain/offerbook/OfferListItemDto.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.http_api.rest_api.domain.offerbook; - -import bisq.offer.Direction; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.ToString; - -import java.util.List; - -@Getter -@ToString -@Schema(name = "OfferListItem", description = "Detailed information about an offer in the offerbook.") -public class OfferListItemDto { - @Schema(description = "Unique identifier for the message.", example = "msg-123456") - private final String messageId; - @Schema(description = "Unique identifier for the offer.", example = "offer-987654") - private final String offerId; - @JsonProperty("isMyMessage") - @Schema(description = "Indicates whether this message belongs to the current user.", example = "true") - private final boolean isMyMessage; - @Schema(description = "Direction of the offer (buy or sell).", implementation = Direction.class) - private final Direction direction; - @Schema(description = "Quote currency code of the offer.", example = "USD") - private final String quoteCurrencyCode; - @Schema(description = "Title of the offer.", example = "Buy 1 BTC at $30,000") - private final String offerTitle; - @Schema(description = "Timestamp of the offer in milliseconds since epoch.", example = "1672531200000") - private final long date; - @Schema(description = "Formatted date string for the offer.", example = "2023-01-01 12:00:00") - private final String formattedDate; - @Schema(description = "Anonymous pseudonym of the user.", example = "Nym123") - private final String nym; - @Schema(description = "Username of the offer's creator.", example = "Alice") - private final String userName; - @Schema(description = "Reputation score of the user who created the offer.", implementation = ReputationScoreDto.class) - private final ReputationScoreDto reputationScore; - @Schema(description = "Formatted amount for the quoted currency.", example = "30,000 USD") - private final String formattedQuoteAmount; - @Schema(description = "Formatted price of the offer.", example = "$30,000 per BTC") - private final String formattedPrice; - @Schema(description = "List of payment methods supported by the quote side.", example = "[\"Bank Transfer\", \"PayPal\"]") - private final List<String> quoteSidePaymentMethods; - @Schema(description = "List of payment methods supported by the base side.", example = "[\"Cash Deposit\"]") - private final List<String> baseSidePaymentMethods; - @Schema(description = "Supported language codes for the offer.", example = "en,es,fr") - private final String supportedLanguageCodes; - - public OfferListItemDto(String messageId, - String offerId, - boolean isMyMessage, - Direction direction, - String quoteCurrencyCode, - String offerTitle, - long date, - String formattedDate, - String nym, - String userName, - ReputationScoreDto reputationScore, - String formattedQuoteAmount, - String formattedPrice, - List<String> quoteSidePaymentMethods, - List<String> baseSidePaymentMethods, - String supportedLanguageCodes) { - this.messageId = messageId; - this.offerId = offerId; - this.isMyMessage = isMyMessage; - this.direction = direction; - this.quoteCurrencyCode = quoteCurrencyCode; - this.offerTitle = offerTitle; - this.date = date; - this.formattedDate = formattedDate; - this.nym = nym; - this.userName = userName; - this.reputationScore = reputationScore; - this.formattedQuoteAmount = formattedQuoteAmount; - this.formattedPrice = formattedPrice; - this.quoteSidePaymentMethods = quoteSidePaymentMethods; - this.baseSidePaymentMethods = baseSidePaymentMethods; - this.supportedLanguageCodes = supportedLanguageCodes; - } -} diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/offerbook/OfferbookRestApi.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/offerbook/OfferbookRestApi.java index 8177f67229..2ecc65a076 100644 --- a/http-api/src/main/java/bisq/http_api/rest_api/domain/offerbook/OfferbookRestApi.java +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/offerbook/OfferbookRestApi.java @@ -17,31 +17,19 @@ package bisq.http_api.rest_api.domain.offerbook; -import bisq.account.payment_method.PaymentMethod; import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannelService; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; import bisq.common.currency.Market; import bisq.common.currency.MarketRepository; +import bisq.dto.OfferListItemDtoFactory; +import bisq.dto.offer.bisq_easy.OfferListItemDto; import bisq.http_api.rest_api.domain.RestApiBase; -import bisq.common.util.StringUtils; -import bisq.i18n.Res; -import bisq.offer.Direction; -import bisq.offer.amount.OfferAmountFormatter; -import bisq.offer.amount.spec.AmountSpec; -import bisq.offer.amount.spec.RangeAmountSpec; -import bisq.offer.bisq_easy.BisqEasyOffer; -import bisq.offer.payment_method.PaymentMethodSpecUtil; -import bisq.offer.price.spec.PriceSpec; -import bisq.offer.price.spec.PriceSpecFormatter; -import bisq.presentation.formatters.DateFormatter; import bisq.user.UserService; import bisq.user.identity.UserIdentityService; -import bisq.user.profile.UserProfile; import bisq.user.profile.UserProfileService; import bisq.user.reputation.ReputationService; -import com.google.common.base.Joiner; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -52,8 +40,6 @@ import jakarta.ws.rs.core.Response; import lombok.extern.slf4j.Slf4j; -import java.text.DateFormat; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; @@ -187,81 +173,17 @@ private Optional<List<OfferListItemDto>> findOffer(String marketCodes) { .map(channel -> channel.getChatMessages() .stream() .filter(BisqEasyOfferbookMessage::hasBisqEasyOffer) - .map(message -> { - BisqEasyOffer bisqEasyOffer = message.getBisqEasyOffer().orElseThrow(); - long date = message.getDate(); - String formattedDate = DateFormatter.formatDateTime(new Date(date), DateFormat.MEDIUM, DateFormat.SHORT, - true, " " + Res.get("temporal.at") + " "); - String authorUserProfileId = message.getAuthorUserProfileId(); - Optional<UserProfile> senderUserProfile = userProfileService.findUserProfile(authorUserProfileId); - String nym = senderUserProfile.map(UserProfile::getNym).orElse(""); - String userName = senderUserProfile.map(UserProfile::getUserName).orElse(""); - - ReputationScoreDto reputationScore = senderUserProfile.flatMap(reputationService::findReputationScore) - .map(score -> new ReputationScoreDto( - score.getTotalScore(), - score.getFiveSystemScore(), - score.getRanking() - )) - .orElse(new ReputationScoreDto(0, 0, 0)); - AmountSpec amountSpec = bisqEasyOffer.getAmountSpec(); - PriceSpec priceSpec = bisqEasyOffer.getPriceSpec(); - boolean hasAmountRange = amountSpec instanceof RangeAmountSpec; - // Market market= bisqEasyOffer.getMarket(); - String formattedQuoteAmount = OfferAmountFormatter.formatQuoteAmount( - marketPriceService, - amountSpec, - priceSpec, - market, - hasAmountRange, - true - ); - String formattedPrice = PriceSpecFormatter.getFormattedPriceSpec(priceSpec, true); - - List<String> quoteSidePaymentMethods = PaymentMethodSpecUtil.getPaymentMethods(bisqEasyOffer.getQuoteSidePaymentMethodSpecs()) - .stream() - .map(PaymentMethod::getName) - .collect(Collectors.toList()); - - List<String> baseSidePaymentMethods = PaymentMethodSpecUtil.getPaymentMethods(bisqEasyOffer.getBaseSidePaymentMethodSpecs()) - .stream() - .map(PaymentMethod::getName) - .collect(Collectors.toList()); - - String supportedLanguageCodes = Joiner.on(",").join(bisqEasyOffer.getSupportedLanguageCodes()); - boolean isMyMessage = message.isMyMessage(userIdentityService); - Direction direction = bisqEasyOffer.getDirection(); - String offerTitle = getOfferTitle(message, direction, isMyMessage); - String messageId = message.getId(); - String offerId = bisqEasyOffer.getId(); - return new OfferListItemDto(messageId, - offerId, - isMyMessage, - direction, - market.getQuoteCurrencyCode(), - offerTitle, - date, - formattedDate, - nym, - userName, - reputationScore, - formattedQuoteAmount, - formattedPrice, - quoteSidePaymentMethods, - baseSidePaymentMethods, - supportedLanguageCodes); - }) + .map(this::createOfferListItemDto) .collect(Collectors.toList()) ) ); } - private String getOfferTitle(BisqEasyOfferbookMessage message, Direction direction, boolean isMyMessage) { - if (isMyMessage) { - String directionString = StringUtils.capitalize(Res.get("offer." + direction.name().toLowerCase())); - return Res.get("bisqEasy.tradeWizard.review.chatMessage.myMessageTitle", directionString); - } else { - return message.getText(); - } + private OfferListItemDto createOfferListItemDto(BisqEasyOfferbookMessage bisqEasyOfferbookMessage) { + return OfferListItemDtoFactory.createOfferListItemDto(userProfileService, + userIdentityService, + reputationService, + marketPriceService, + bisqEasyOfferbookMessage); } } diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/offerbook/ReputationScoreDto.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/offerbook/ReputationScoreDto.java deleted file mode 100644 index d1184d225e..0000000000 --- a/http-api/src/main/java/bisq/http_api/rest_api/domain/offerbook/ReputationScoreDto.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.http_api.rest_api.domain.offerbook; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; - -@Getter -@Schema(name = "ReputationScoreDto", description = "User reputation details including total score, 5-star rating, and ranking.") -public class ReputationScoreDto { - @Schema(description = "Total reputation score of the user.", example = "1500") - private final long totalScore; - @Schema(description = "5-star system equivalent score (out of 5).", example = "4.8") - private final double fiveSystemScore; - @Schema(description = "User's ranking among peers.", example = "12") - private final int ranking; - - public ReputationScoreDto(long totalScore, double fiveSystemScore, int ranking) { - this.totalScore = totalScore; - this.fiveSystemScore = fiveSystemScore; - this.ranking = ranking; - } -} diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/trade/TakeOfferRequest.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/trade/TakeOfferRequest.java new file mode 100644 index 0000000000..78a1b6e5ed --- /dev/null +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/trade/TakeOfferRequest.java @@ -0,0 +1,25 @@ +/* + * 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.http_api.rest_api.domain.trade; + +public record TakeOfferRequest(String offerId, + long baseSideAmount, + long quoteSideAmount, + String bitcoinPaymentMethod, + String fiatPaymentMethod) { +} diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/trade/TakeOfferResponse.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/trade/TakeOfferResponse.java new file mode 100644 index 0000000000..071fc27c8a --- /dev/null +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/trade/TakeOfferResponse.java @@ -0,0 +1,21 @@ +/* + * 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.http_api.rest_api.domain.trade; + +public record TakeOfferResponse(String tradeId) { +} diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/trade/TradeRestApi.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/trade/TradeRestApi.java new file mode 100644 index 0000000000..f7bdf9a746 --- /dev/null +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/trade/TradeRestApi.java @@ -0,0 +1,199 @@ +/* + * 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.http_api.rest_api.domain.trade; + +import bisq.account.payment_method.BitcoinPaymentMethod; +import bisq.account.payment_method.FiatPaymentMethod; +import bisq.bonded_roles.market_price.MarketPriceService; +import bisq.chat.ChatChannelDomain; +import bisq.chat.ChatChannelSelectionService; +import bisq.chat.ChatService; +import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannelService; +import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; +import bisq.chat.bisqeasy.open_trades.BisqEasyOpenTradeChannelService; +import bisq.common.monetary.Monetary; +import bisq.common.util.StringUtils; +import bisq.contract.bisq_easy.BisqEasyContract; +import bisq.http_api.rest_api.domain.RestApiBase; +import bisq.http_api.rest_api.domain.offer.CreateOfferRequest; +import bisq.i18n.Res; +import bisq.offer.bisq_easy.BisqEasyOffer; +import bisq.offer.payment_method.BitcoinPaymentMethodSpec; +import bisq.offer.payment_method.FiatPaymentMethodSpec; +import bisq.offer.payment_method.PaymentMethodSpecUtil; +import bisq.offer.price.spec.PriceSpec; +import bisq.support.SupportService; +import bisq.support.mediation.MediationRequestService; +import bisq.trade.TradeService; +import bisq.trade.bisq_easy.BisqEasyTrade; +import bisq.trade.bisq_easy.BisqEasyTradeService; +import bisq.trade.bisq_easy.protocol.BisqEasyProtocol; +import bisq.user.UserService; +import bisq.user.banned.BannedUserService; +import bisq.user.identity.UserIdentity; +import bisq.user.identity.UserIdentityService; +import bisq.user.profile.UserProfile; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.container.AsyncResponse; +import jakarta.ws.rs.container.Suspended; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import lombok.extern.slf4j.Slf4j; + +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import static com.google.common.base.Preconditions.checkArgument; + +@Slf4j +@Path("/trades") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Tag(name = "Bisq Easy Trade API") +public class TradeRestApi extends RestApiBase { + private final BisqEasyOfferbookChannelService bisqEasyOfferbookChannelService; + private final ChatService chatService; + private final MarketPriceService marketPriceService; + private final UserIdentityService userIdentityService; + private final BannedUserService bannedUserService; + private final MediationRequestService mediationRequestService; + private final BisqEasyTradeService bisqEasyTradeService; + private final BisqEasyOpenTradeChannelService bisqEasyOpenTradeChannelService; + + public TradeRestApi(ChatService chatService, + MarketPriceService marketPriceService, + UserService userService, + SupportService supportedService, + TradeService tradeService) { + this.bisqEasyOfferbookChannelService = chatService.getBisqEasyOfferbookChannelService(); + this.chatService = chatService; + this.marketPriceService = marketPriceService; + userIdentityService = userService.getUserIdentityService(); + bannedUserService = userService.getBannedUserService(); + mediationRequestService = supportedService.getMediationRequestService(); + bisqEasyTradeService = tradeService.getBisqEasyTradeService(); + bisqEasyOpenTradeChannelService = chatService.getBisqEasyOpenTradeChannelService(); + } + + @POST + @Operation( + summary = "Create a Trade by Taking a Bisq Easy Offer", + description = "Create a Trade by Taking a Bisq Easy Offer.", + requestBody = @RequestBody( + description = "", + content = @Content(schema = @Schema(implementation = CreateOfferRequest.class)) + ), + responses = { + @ApiResponse(responseCode = "201", description = "", + content = @Content(schema = @Schema(example = ""))), + @ApiResponse(responseCode = "400", description = "Invalid input"), + @ApiResponse(responseCode = "500", description = "Internal server error") + } + ) + public void takeOffer(TakeOfferRequest request, @Suspended AsyncResponse asyncResponse) { + asyncResponse.setTimeout(150, TimeUnit.SECONDS); // We have 120 seconds socket timeout, so we should never get triggered here, as the message will be sent as mailbox message + asyncResponse.setTimeoutHandler(response -> { + response.resume(buildResponse(Response.Status.SERVICE_UNAVAILABLE, "Request timed out")); + }); + + try { + UserIdentity takerIdentity = userIdentityService.getSelectedUserIdentity(); + checkArgument(!bannedUserService.isUserProfileBanned(takerIdentity.getUserProfile()), "Taker profile is banned"); + //noinspection OptionalGetWithoutIsPresent + BisqEasyOffer bisqEasyOffer = bisqEasyOfferbookChannelService.getChannels().stream().flatMap(c -> c.getChatMessages().stream()) + .filter(BisqEasyOfferbookMessage::hasBisqEasyOffer) + .map(e -> e.getBisqEasyOffer().get()) + .filter(e -> e.getId().equals(request.offerId())) + .findFirst() + .orElseThrow(); + checkArgument(!bannedUserService.isNetworkIdBanned(bisqEasyOffer.getMakerNetworkId()), "Maker profile is banned"); + Monetary baseSideAmount = Monetary.from(request.baseSideAmount(), bisqEasyOffer.getMarket().getBaseCurrencyCode()); + Monetary quoteSideAmount = Monetary.from(request.quoteSideAmount(), bisqEasyOffer.getMarket().getBaseCurrencyCode()); + BitcoinPaymentMethod bitcoinPaymentMethod = PaymentMethodSpecUtil.getBitcoinPaymentMethod(request.bitcoinPaymentMethod()); + BitcoinPaymentMethodSpec bitcoinPaymentMethodSpec = new BitcoinPaymentMethodSpec(bitcoinPaymentMethod); + FiatPaymentMethod fiatPaymentMethod = PaymentMethodSpecUtil.getFiatPaymentMethod(request.fiatPaymentMethod()); + FiatPaymentMethodSpec fiatPaymentMethodSpec = new FiatPaymentMethodSpec(fiatPaymentMethod); + Optional<UserProfile> mediator = mediationRequestService.selectMediator(bisqEasyOffer.getMakersUserProfileId(), takerIdentity.getId()); + PriceSpec makersPriceSpec = bisqEasyOffer.getPriceSpec(); + long marketPrice = marketPriceService.findMarketPrice(bisqEasyOffer.getMarket()) + .map(e -> e.getPriceQuote().getValue()) + .orElseThrow(); + BisqEasyProtocol bisqEasyProtocol = bisqEasyTradeService.createBisqEasyProtocol(takerIdentity.getIdentity(), + bisqEasyOffer, + baseSideAmount, + quoteSideAmount, + bitcoinPaymentMethodSpec, + fiatPaymentMethodSpec, + mediator, + makersPriceSpec, + marketPrice); + BisqEasyTrade bisqEasyTrade = bisqEasyProtocol.getModel(); + log.info("Selected mediator for trade {}: {}", bisqEasyTrade.getShortId(), mediator.map(UserProfile::getUserName).orElse("N/A")); + + bisqEasyTradeService.takeOffer(bisqEasyTrade); + BisqEasyContract contract = bisqEasyTrade.getContract(); + + String tradeId = bisqEasyTrade.getId(); + bisqEasyOpenTradeChannelService.sendTakeOfferMessage(tradeId, bisqEasyOffer, contract.getMediator()) + .thenAccept(result -> + { + // In case the user has switched to another market we want to select that market in the offer book + ChatChannelSelectionService chatChannelSelectionService = + chatService.getChatChannelSelectionService(ChatChannelDomain.BISQ_EASY_OFFERBOOK); + bisqEasyOfferbookChannelService.findChannel(contract.getOffer().getMarket()) + .ifPresent(chatChannelSelectionService::selectChannel); + bisqEasyOpenTradeChannelService.findChannelByTradeId(tradeId) + .ifPresent(channel -> { + String taker = userIdentityService.getSelectedUserIdentity().getUserProfile().getUserName(); + String maker = channel.getPeer().getUserName(); + String encoded = Res.encode("bisqEasy.takeOffer.tradeLogMessage", taker, maker); + chatService.getBisqEasyOpenTradeChannelService().sendTradeLogMessage(encoded, channel); + }); + } + ) + .get(); + + // After the take offer is completed we check if errors happened + String errorMessage = bisqEasyTrade.errorMessageObservable().get(); + checkArgument(errorMessage == null, "An error occurred at taking the offer: " + errorMessage + + ". ErrorStackTrace: " + StringUtils.truncate(bisqEasyTrade.getErrorStackTrace(), 500)); + + String peersErrorMessage = bisqEasyTrade.peersErrorMessageObservable().get(); + checkArgument(peersErrorMessage == null, "An error occurred at the peers side at taking the offer: " + peersErrorMessage + + ". ErrorStackTrace: " + StringUtils.truncate(bisqEasyTrade.getPeersErrorStackTrace(), 500)); + + asyncResponse.resume(buildResponse(Response.Status.CREATED, new TakeOfferResponse(bisqEasyTrade.getId()))); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + asyncResponse.resume(buildErrorResponse("Thread was interrupted.")); + } catch (IllegalArgumentException e) { + asyncResponse.resume(buildResponse(Response.Status.BAD_REQUEST, "Invalid input: " + e.getMessage())); + } catch (Exception e) { + asyncResponse.resume(buildErrorResponse("An unexpected error occurred.")); + } + } +} diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/CreateUserIdentityRequest.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/CreateUserIdentityRequest.java index 56c6187682..d0b5731920 100644 --- a/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/CreateUserIdentityRequest.java +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/CreateUserIdentityRequest.java @@ -21,7 +21,7 @@ import lombok.Data; @Data -@Schema(description = "Request payload for creating a new user identity.") +@Schema(description = "Request key material for creating a new user identity.") public class CreateUserIdentityRequest { @Schema(description = "Nickname for the user", example = "Satoshi", required = true) private String nickName; @@ -32,6 +32,6 @@ public class CreateUserIdentityRequest { @Schema(description = "User statement", example = "I am Satoshi") private String statement = ""; - @Schema(description = "Prepared data as JSON object", required = true) - private PreparedData preparedData; + @Schema(description = "Key material for creating an user identity", required = true) + private KeyMaterialResponse keyMaterialResponse; } diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/UserProfileResponse.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/CreateUserIdentityResponse.java similarity index 89% rename from http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/UserProfileResponse.java rename to http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/CreateUserIdentityResponse.java index b0da323a33..8a85cdaef0 100644 --- a/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/UserProfileResponse.java +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/CreateUserIdentityResponse.java @@ -24,11 +24,11 @@ @Getter @Schema(name = "UserProfileResponse", description = "Response payload containing the user profile ID.") -public class UserProfileResponse { +public class CreateUserIdentityResponse { private final String userProfileId; @JsonCreator - public UserProfileResponse(@JsonProperty("userProfileId") String userProfileId) { + public CreateUserIdentityResponse(@JsonProperty("userProfileId") String userProfileId) { this.userProfileId = userProfileId; } } diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/PreparedData.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/KeyMaterialResponse.java similarity index 62% rename from http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/PreparedData.java rename to http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/KeyMaterialResponse.java index 6369f44072..e715216cfc 100644 --- a/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/PreparedData.java +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/KeyMaterialResponse.java @@ -17,33 +17,25 @@ package bisq.http_api.rest_api.domain.user_identity; -import bisq.security.keys.JsonSerialization; -import bisq.security.pow.ProofOfWork; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import bisq.dto.security.keys.KeyPairDto; +import bisq.dto.security.pow.ProofOfWorkDto; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; -import java.security.KeyPair; - @Getter -@Schema(name = "PreparedData") -public final class PreparedData { - @JsonSerialize(using = JsonSerialization.KeyPair.Serializer.class) - @JsonDeserialize(using = JsonSerialization.KeyPair.Deserializer.class) +@Schema(name = "KeyMaterial") +public final class KeyMaterialResponse { @Schema(description = "Key pair", example = "{ \"privateKey\": \"MIGNAgEAMBAGByqGSM49AgEGBSuBBAAKBHYwdAIBAQQgky6PNO163DColHrGmSNMgY93amwpAO8ZA8/Pb+Xl5magBwYFK4EEAAqhRANCAARyZim9kPgZixR2+ALUs72fO2zzSkeV89w4oQpkRUct5ob4yHRIIwwrggjoCGmNUWqX/pNA18R46vNYTp8NWuSu\", \"publicKey\": \"MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEcmYpvZD4GYsUdvgC1LO9nzts80pHlfPcOKEKZEVHLeaG+Mh0SCMMK4II6AhpjVFql/6TQNfEeOrzWE6fDVrkrg==\" }") - private KeyPair keyPair; + private KeyPairDto keyPair; @Schema(description = "ID", example = "b0edc477ec967379867ae44b1e030fa4f8e68327") private String id; @Schema(description = "Nym", example = "Ravenously-Poignant-Coordinate-695") private String nym; - @Schema(description = "User statement", - example = "{ \"payload\": \"[-80, -19, -60, 119, -20, -106, 115, 121, -122, 122, -28, 75, 30, 3, 15, -92, -8, -26, -125, 39]\", \"counter\": 93211, \"difficulty\": 65536.0, \"solution\": [0, 0, 0, 0, 0, 1, 108, 27], \"duration\": 19 }") - private ProofOfWork proofOfWork; + private ProofOfWorkDto proofOfWork; - public static PreparedData from(KeyPair keyPair, String id, String nym, ProofOfWork proofOfWork) { - PreparedData dto = new PreparedData(); + public static KeyMaterialResponse from(KeyPairDto keyPair, String id, String nym, ProofOfWorkDto proofOfWork) { + KeyMaterialResponse dto = new KeyMaterialResponse(); dto.keyPair = keyPair; dto.id = id; dto.nym = nym; diff --git a/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/UserIdentityRestApi.java b/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/UserIdentityRestApi.java index 71dde9fb13..75e286175d 100644 --- a/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/UserIdentityRestApi.java +++ b/http-api/src/main/java/bisq/http_api/rest_api/domain/user_identity/UserIdentityRestApi.java @@ -18,6 +18,9 @@ package bisq.http_api.rest_api.domain.user_identity; import bisq.common.encoding.Hex; +import bisq.dto.DtoMappings; +import bisq.dto.security.keys.KeyPairDto; +import bisq.dto.security.pow.ProofOfWorkDto; import bisq.http_api.rest_api.domain.RestApiBase; import bisq.security.DigestUtil; import bisq.security.SecurityService; @@ -33,6 +36,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.ws.rs.*; +import jakarta.ws.rs.container.AsyncResponse; +import jakarta.ws.rs.container.Suspended; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import lombok.extern.slf4j.Slf4j; @@ -41,6 +46,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Slf4j @@ -60,31 +66,88 @@ public UserIdentityRestApi(SecurityService securityService, UserIdentityService } @GET - @Path("/prepared-data") + @Path("/key-material") @Operation( summary = "Generate Prepared Data", description = "Generates a key pair, public key hash, Nym, and proof of work for a new user identity.", responses = { @ApiResponse(responseCode = "201", description = "Prepared data generated successfully", - content = @Content(schema = @Schema(implementation = PreparedData.class))), + content = @Content(schema = @Schema(implementation = KeyMaterialResponse.class))), @ApiResponse(responseCode = "500", description = "Internal server error") } ) - public Response createPreparedData() { + public void getKeyMaterial(@Suspended AsyncResponse asyncResponse) { + asyncResponse.setTimeout(5, TimeUnit.SECONDS); + asyncResponse.setTimeoutHandler(response -> { + response.resume(buildResponse(Response.Status.SERVICE_UNAVAILABLE, "Request timed out")); + }); try { KeyPair keyPair = securityService.getKeyBundleService().generateKeyPair(); byte[] pubKeyHash = DigestUtil.hash(keyPair.getPublic().getEncoded()); String id = Hex.encode(pubKeyHash); ProofOfWork proofOfWork = userIdentityService.mintNymProofOfWork(pubKeyHash); String nym = NymIdGenerator.generate(pubKeyHash, proofOfWork.getSolution()); - PreparedData preparedData = PreparedData.from(keyPair, id, nym, proofOfWork); - return buildResponse(Response.Status.CREATED, preparedData); + KeyPairDto keyPairDto = DtoMappings.KeyPairDtoMapping.from(keyPair); + ProofOfWorkDto proofOfWorkDto = DtoMappings.ProofOfWorkDtoMapping.from(proofOfWork); + KeyMaterialResponse keyMaterialResponse = KeyMaterialResponse.from(keyPairDto, id, nym, proofOfWorkDto); + asyncResponse.resume(buildResponse(Response.Status.CREATED, keyMaterialResponse)); } catch (Exception e) { log.error("Error generating prepared data", e); - return buildErrorResponse("Could not generate prepared data."); + asyncResponse.resume(buildErrorResponse("Could not generate prepared data.")); } } + + @POST + @Operation( + summary = "Create User Identity and Publish User Profile", + description = "Creates a new user identity and publishes the associated user profile.", + requestBody = @RequestBody( + description = "Request payload containing user nickname, terms, statement, and prepared data.", + content = @Content(schema = @Schema(implementation = CreateUserIdentityRequest.class)) + ), + responses = { + @ApiResponse(responseCode = "201", description = "User identity created successfully", + content = @Content(schema = @Schema(example = "{ \"userProfileId\": \"d22d7b62ef442b5df03378f134bc8f54a2171cba\" }"))), + @ApiResponse(responseCode = "400", description = "Invalid input"), + @ApiResponse(responseCode = "500", description = "Internal server error") + } + ) + public void createUserIdentity(CreateUserIdentityRequest request, + @Suspended AsyncResponse asyncResponse) { + asyncResponse.setTimeout(10, TimeUnit.SECONDS); + asyncResponse.setTimeoutHandler(response -> { + response.resume(buildResponse(Response.Status.SERVICE_UNAVAILABLE, "Request timed out")); + }); + + try { + KeyMaterialResponse keyMaterialResponse = request.getKeyMaterialResponse(); + KeyPairDto keyPairDto = keyMaterialResponse.getKeyPair(); + KeyPair keyPair = DtoMappings.KeyPairDtoMapping.toPojo(keyPairDto); + byte[] pubKeyHash = DigestUtil.hash(keyPair.getPublic().getEncoded()); + int avatarVersion = 0; + ProofOfWorkDto proofOfWorkDto = keyMaterialResponse.getProofOfWork(); + ProofOfWork proofOfWork = DtoMappings.ProofOfWorkDtoMapping.toPojo(proofOfWorkDto); + UserIdentity userIdentity = userIdentityService.createAndPublishNewUserProfile(request.getNickName(), + keyPair, + pubKeyHash, + proofOfWork, + avatarVersion, + request.getTerms(), + request.getStatement()).get(); + + asyncResponse.resume(buildResponse(Response.Status.CREATED, new CreateUserIdentityResponse(userIdentity.getId()))); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + asyncResponse.resume(buildErrorResponse("Thread was interrupted.")); + } catch (IllegalArgumentException e) { + asyncResponse.resume(buildResponse(Response.Status.BAD_REQUEST, "Invalid input: " + e.getMessage())); + } catch (Exception e) { + asyncResponse.resume(buildErrorResponse("An unexpected error occurred.")); + } + } + + @GET @Path("/{id}") @Operation( @@ -144,44 +207,4 @@ public Response getSelectedUserProfile() { UserProfile userProfile = selectedUserIdentity.getUserProfile(); return buildOkResponse(userProfile); } - - @POST - @Operation( - summary = "Create and Publish User Identity", - description = "Creates a new user identity and publishes the associated user profile.", - requestBody = @RequestBody( - description = "Request payload containing user nickname, terms, statement, and prepared data.", - content = @Content(schema = @Schema(implementation = CreateUserIdentityRequest.class)) - ), - responses = { - @ApiResponse(responseCode = "201", description = "User identity created successfully", - content = @Content(schema = @Schema(example = "{ \"userProfileId\": \"d22d7b62ef442b5df03378f134bc8f54a2171cba\" }"))), - @ApiResponse(responseCode = "400", description = "Invalid input"), - @ApiResponse(responseCode = "500", description = "Internal server error") - } - ) - public Response createUserIdentityAndPublishUserProfile(CreateUserIdentityRequest request) { - try { - PreparedData preparedData = request.getPreparedData(); - KeyPair keyPair = preparedData.getKeyPair(); - byte[] pubKeyHash = DigestUtil.hash(keyPair.getPublic().getEncoded()); - int avatarVersion = 0; - UserIdentity userIdentity = userIdentityService.createAndPublishNewUserProfile(request.getNickName(), - keyPair, - pubKeyHash, - preparedData.getProofOfWork(), - avatarVersion, - request.getTerms(), - request.getStatement()).get(); - return buildResponse(Response.Status.CREATED, new UserProfileResponse(userIdentity.getId())); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return buildErrorResponse("Thread was interrupted."); - } catch (IllegalArgumentException e) { - return buildResponse(Response.Status.BAD_REQUEST, "Invalid input: " + e.getMessage()); - } catch (Exception e) { - log.error("Error creating user identity", e); - return buildErrorResponse("An unexpected error occurred."); - } - } } diff --git a/http-api/src/main/java/bisq/http_api/web_socket/WebSocketMessage.java b/http-api/src/main/java/bisq/http_api/web_socket/WebSocketMessage.java new file mode 100644 index 0000000000..5fab38eeef --- /dev/null +++ b/http-api/src/main/java/bisq/http_api/web_socket/WebSocketMessage.java @@ -0,0 +1,42 @@ +/* + * 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.http_api.web_socket; + +import bisq.http_api.web_socket.rest_api_proxy.WebSocketRestApiRequest; +import bisq.http_api.web_socket.rest_api_proxy.WebSocketRestApiResponse; +import bisq.http_api.web_socket.subscription.SubscriptionRequest; +import bisq.http_api.web_socket.subscription.SubscriptionResponse; +import bisq.http_api.web_socket.subscription.WebSocketEvent; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = WebSocketRestApiRequest.class, name = "WebSocketRestApiRequest"), + @JsonSubTypes.Type(value = WebSocketRestApiResponse.class, name = "WebSocketRestApiResponse"), + @JsonSubTypes.Type(value = SubscriptionRequest.class, name = "SubscriptionRequest"), + @JsonSubTypes.Type(value = SubscriptionResponse.class, name = "SubscriptionResponse"), + @JsonSubTypes.Type(value = WebSocketEvent.class, name = "WebSocketEvent") +}) +public interface WebSocketMessage { +} + diff --git a/http-api/src/main/java/bisq/http_api/web_socket/WebSocketRestApiResourceConfig.java b/http-api/src/main/java/bisq/http_api/web_socket/WebSocketRestApiResourceConfig.java index fcbd45c743..087dd3910c 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/WebSocketRestApiResourceConfig.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/WebSocketRestApiResourceConfig.java @@ -1,8 +1,10 @@ package bisq.http_api.web_socket; +import bisq.http_api.rest_api.RestApiResourceConfig; import bisq.http_api.rest_api.domain.market_price.MarketPriceRestApi; +import bisq.http_api.rest_api.domain.offer.OfferRestApi; import bisq.http_api.rest_api.domain.offerbook.OfferbookRestApi; -import bisq.http_api.rest_api.RestApiResourceConfig; +import bisq.http_api.rest_api.domain.trade.TradeRestApi; import bisq.http_api.rest_api.domain.user_identity.UserIdentityRestApi; import jakarta.ws.rs.ApplicationPath; import lombok.extern.slf4j.Slf4j; @@ -11,8 +13,10 @@ public class WebSocketRestApiResourceConfig extends RestApiResourceConfig { public WebSocketRestApiResourceConfig(String swaggerBaseUrl, OfferbookRestApi offerbookRestApi, + OfferRestApi offerRestApi, + TradeRestApi tradeRestApi, UserIdentityRestApi userIdentityRestApi , MarketPriceRestApi marketPriceRestApi) { - super(swaggerBaseUrl, offerbookRestApi, userIdentityRestApi, marketPriceRestApi); + super(swaggerBaseUrl, offerbookRestApi, offerRestApi, tradeRestApi, userIdentityRestApi, marketPriceRestApi); } } diff --git a/http-api/src/main/java/bisq/http_api/web_socket/WebSocketService.java b/http-api/src/main/java/bisq/http_api/web_socket/WebSocketService.java index 86a576485a..3fdc020181 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/WebSocketService.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/WebSocketService.java @@ -25,6 +25,7 @@ import bisq.http_api.web_socket.util.GrizzlySwaggerHttpHandler; import bisq.user.UserService; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import jakarta.ws.rs.core.UriBuilder; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -116,6 +117,8 @@ public WebSocketService(Config config, this.config = config; this.restApiResourceConfig = restApiResourceConfig; ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new Jdk8Module()); + //objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); subscriptionService = new SubscriptionService(objectMapper, bondedRolesService, chatService, userService); diff --git a/http-api/src/main/java/bisq/http_api/web_socket/domain/BaseWebSocketService.java b/http-api/src/main/java/bisq/http_api/web_socket/domain/BaseWebSocketService.java index a36cf75919..89c4e12163 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/domain/BaseWebSocketService.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/domain/BaseWebSocketService.java @@ -19,11 +19,17 @@ import bisq.common.application.Service; +import bisq.dto.account.protocol_type.TradeProtocolTypeDto; +import bisq.dto.offer.amount.spec.AmountSpecDto; +import bisq.dto.offer.bisq_easy.OfferListItemDto; +import bisq.dto.offer.options.OfferOptionDto; +import bisq.dto.offer.price.spec.PriceSpecDto; import bisq.http_api.web_socket.subscription.*; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; +import java.util.List; import java.util.Optional; import java.util.Set; @@ -42,8 +48,40 @@ public BaseWebSocketService(ObjectMapper objectMapper, abstract public Optional<String> getJsonPayload(); + //todo protected <T> Optional<String> toJson(T payload) { try { + if (payload instanceof List<?> list && list.get(0) instanceof OfferListItemDto offerListItemDto) { + try { + PriceSpecDto value = offerListItemDto.getBisqEasyOffer().priceSpec(); + objectMapper.writeValueAsString(value); + } catch (JsonProcessingException e) { + log.error("Json serialisation failed", e); + } + try { + AmountSpecDto value = offerListItemDto.getBisqEasyOffer().amountSpec(); + objectMapper.writeValueAsString(value); + } catch (JsonProcessingException e) { + log.error("Json serialisation failed", e); + } + try { + List<OfferOptionDto> value = offerListItemDto.getBisqEasyOffer().offerOptions(); + objectMapper.writeValueAsString(value); + } catch (JsonProcessingException e) { + log.error("Json serialisation failed", e); + } + try { + List<TradeProtocolTypeDto> value = offerListItemDto.getBisqEasyOffer().protocolTypes(); + objectMapper.writeValueAsString(value); + } catch (JsonProcessingException e) { + log.error("Json serialisation failed", e); //failed + } + try { + objectMapper.writeValueAsString(offerListItemDto.getBisqEasyOffer()); + } catch (JsonProcessingException e) { + log.error("Json serialisation failed", e); //failed + } + } return Optional.of(objectMapper.writeValueAsString(payload)); } catch (JsonProcessingException e) { log.error("Json serialisation failed", e); @@ -73,7 +111,6 @@ protected void send(String json, Subscriber subscriber, ModificationType modificationType) { WebSocketEvent.toJson(objectMapper, - subscriber.getWebSocketEventClassName(), subscriber.getTopic(), subscriber.getSubscriberId(), json, diff --git a/http-api/src/main/java/bisq/http_api/web_socket/domain/market_price/MarketPriceWebSocketService.java b/http-api/src/main/java/bisq/http_api/web_socket/domain/market_price/MarketPriceWebSocketService.java index 24e2e5cf1c..8872a2b2b0 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/domain/market_price/MarketPriceWebSocketService.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/domain/market_price/MarketPriceWebSocketService.java @@ -23,6 +23,8 @@ import bisq.common.currency.Market; import bisq.common.observable.Pin; import bisq.common.observable.map.ObservableHashMap; +import bisq.dto.DtoMappings; +import bisq.dto.common.monetary.PriceQuoteDto; import bisq.http_api.web_socket.domain.SimpleObservableWebSocketService; import bisq.http_api.web_socket.subscription.SubscriberRepository; import bisq.http_api.web_socket.subscription.Topic; @@ -34,7 +36,7 @@ import java.util.stream.Collectors; @Slf4j -public class MarketPriceWebSocketService extends SimpleObservableWebSocketService<ObservableHashMap<Market, MarketPrice>, Map<String, Long>> { +public class MarketPriceWebSocketService extends SimpleObservableWebSocketService<ObservableHashMap<Market, MarketPrice>, Map<String, PriceQuoteDto>> { private final MarketPriceService marketPriceService; public MarketPriceWebSocketService(ObjectMapper objectMapper, @@ -50,18 +52,23 @@ protected ObservableHashMap<Market, MarketPrice> getObservable() { } @Override - protected HashMap<String, Long> toPayload(ObservableHashMap<Market, MarketPrice> observable) { + protected HashMap<String, PriceQuoteDto> toPayload(ObservableHashMap<Market, MarketPrice> observable) { return getObservable() .entrySet().stream() - .filter(entry -> entry.getKey().getBaseCurrencyCode().equals("BTC")) // We get altcoin quotes as well which have BTC as quote currency + .filter(MarketPriceWebSocketService::isBaseCurrencyBtc) .collect(Collectors.toMap( entry -> entry.getKey().getQuoteCurrencyCode(), - entry -> entry.getValue().getPriceQuote().getValue(), + entry -> DtoMappings.PriceQuoteMapping.from(entry.getValue().getPriceQuote()), (existing, replacement) -> existing, HashMap::new )); } + private static boolean isBaseCurrencyBtc(Map.Entry<Market, MarketPrice> entry) { + // We get altcoin quotes as well which have BTC as quote currency + return entry.getKey().getBaseCurrencyCode().equals("BTC"); + } + @Override protected Pin setupObserver() { return getObservable().addObserver(this::onChange); diff --git a/http-api/src/main/java/bisq/http_api/web_socket/domain/offerbook/OffersWebSocketService.java b/http-api/src/main/java/bisq/http_api/web_socket/domain/offerbook/OffersWebSocketService.java index ea4eb41e6c..6d14d3a09a 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/domain/offerbook/OffersWebSocketService.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/domain/offerbook/OffersWebSocketService.java @@ -17,43 +17,27 @@ package bisq.http_api.web_socket.domain.offerbook; -import bisq.account.payment_method.PaymentMethod; import bisq.bonded_roles.BondedRolesService; import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.ChatService; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannelService; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; -import bisq.http_api.rest_api.domain.offerbook.OfferListItemDto; -import bisq.http_api.rest_api.domain.offerbook.ReputationScoreDto; -import bisq.common.currency.Market; import bisq.common.observable.Pin; import bisq.common.observable.collection.CollectionObserver; -import bisq.common.util.StringUtils; +import bisq.dto.OfferListItemDtoFactory; +import bisq.dto.offer.bisq_easy.OfferListItemDto; import bisq.http_api.web_socket.domain.BaseWebSocketService; import bisq.http_api.web_socket.subscription.ModificationType; import bisq.http_api.web_socket.subscription.SubscriberRepository; -import bisq.i18n.Res; -import bisq.offer.Direction; -import bisq.offer.amount.OfferAmountFormatter; -import bisq.offer.amount.spec.AmountSpec; -import bisq.offer.amount.spec.RangeAmountSpec; -import bisq.offer.bisq_easy.BisqEasyOffer; -import bisq.offer.payment_method.PaymentMethodSpecUtil; -import bisq.offer.price.spec.PriceSpec; -import bisq.offer.price.spec.PriceSpecFormatter; -import bisq.presentation.formatters.DateFormatter; import bisq.user.UserService; import bisq.user.identity.UserIdentityService; -import bisq.user.profile.UserProfile; import bisq.user.profile.UserProfileService; import bisq.user.reputation.ReputationService; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Joiner; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.NotImplementedException; -import java.text.DateFormat; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -71,10 +55,10 @@ public class OffersWebSocketService extends BaseWebSocketService { private final Set<Pin> pins = new HashSet<>(); public OffersWebSocketService(ObjectMapper objectMapper, - SubscriberRepository subscriberRepository, - ChatService chatService, - UserService userService, - BondedRolesService bondedRolesService) { + SubscriberRepository subscriberRepository, + ChatService chatService, + UserService userService, + BondedRolesService bondedRolesService) { super(objectMapper, subscriberRepository, OFFERS); bisqEasyOfferbookChannelService = chatService.getBisqEasyOfferbookChannelService(); @@ -127,15 +111,15 @@ public Optional<String> getJsonPayload(Stream<BisqEasyOfferbookChannel> channels ArrayList<OfferListItemDto> payload = channels .flatMap(channel -> channel.getChatMessages().stream() - .map(this::toOfferListItemDto)) + .map(this::createOfferListItemDto)) .collect(Collectors.toCollection(ArrayList::new)); return toJson(payload); } private void send(String quoteCurrencyCode, - BisqEasyOfferbookMessage message, + BisqEasyOfferbookMessage bisqEasyOfferbookMessage, ModificationType modificationType) { - OfferListItemDto item = toOfferListItemDto(message); + OfferListItemDto item = createOfferListItemDto(bisqEasyOfferbookMessage); // The payload is defined as a list to support batch data delivery at subscribe. ArrayList<OfferListItemDto> payload = new ArrayList<>(List.of(item)); toJson(payload).ifPresent(json -> { @@ -145,78 +129,11 @@ private void send(String quoteCurrencyCode, }); } - private OfferListItemDto toOfferListItemDto(BisqEasyOfferbookMessage message) { - BisqEasyOffer bisqEasyOffer = message.getBisqEasyOffer().orElseThrow(); - Market market = bisqEasyOffer.getMarket(); - - long date = message.getDate(); - String formattedDate = DateFormatter.formatDateTime(new Date(date), DateFormat.MEDIUM, DateFormat.SHORT, - true, " " + Res.get("temporal.at") + " "); - String authorUserProfileId = message.getAuthorUserProfileId(); - Optional<UserProfile> senderUserProfile = userProfileService.findUserProfile(authorUserProfileId); - String nym = senderUserProfile.map(UserProfile::getNym).orElse(""); - String userName = senderUserProfile.map(UserProfile::getUserName).orElse(""); - - ReputationScoreDto reputationScore = senderUserProfile.flatMap(reputationService::findReputationScore) - .map(score -> new ReputationScoreDto( - score.getTotalScore(), - score.getFiveSystemScore(), - score.getRanking() - )) - .orElse(new ReputationScoreDto(0, 0, 0)); - AmountSpec amountSpec = bisqEasyOffer.getAmountSpec(); - PriceSpec priceSpec = bisqEasyOffer.getPriceSpec(); - boolean hasAmountRange = amountSpec instanceof RangeAmountSpec; - String formattedQuoteAmount = OfferAmountFormatter.formatQuoteAmount( + private OfferListItemDto createOfferListItemDto(BisqEasyOfferbookMessage bisqEasyOfferbookMessage) { + return OfferListItemDtoFactory.createOfferListItemDto(userProfileService, + userIdentityService, + reputationService, marketPriceService, - amountSpec, - priceSpec, - market, - hasAmountRange, - true - ); - String formattedPrice = PriceSpecFormatter.getFormattedPriceSpec(priceSpec, true); - - List<String> quoteSidePaymentMethods = PaymentMethodSpecUtil.getPaymentMethods(bisqEasyOffer.getQuoteSidePaymentMethodSpecs()) - .stream() - .map(PaymentMethod::getName) - .collect(Collectors.toList()); - - List<String> baseSidePaymentMethods = PaymentMethodSpecUtil.getPaymentMethods(bisqEasyOffer.getBaseSidePaymentMethodSpecs()) - .stream() - .map(PaymentMethod::getName) - .collect(Collectors.toList()); - - String supportedLanguageCodes = Joiner.on(",").join(bisqEasyOffer.getSupportedLanguageCodes()); - boolean isMyMessage = message.isMyMessage(userIdentityService); - Direction direction = bisqEasyOffer.getDirection(); - String offerTitle = getOfferTitle(message, direction, isMyMessage); - String messageId = message.getId(); - String offerId = bisqEasyOffer.getId(); - return new OfferListItemDto(messageId, - offerId, - isMyMessage, - direction, - market.getQuoteCurrencyCode(), - offerTitle, - date, - formattedDate, - nym, - userName, - reputationScore, - formattedQuoteAmount, - formattedPrice, - quoteSidePaymentMethods, - baseSidePaymentMethods, - supportedLanguageCodes); - } - - private String getOfferTitle(BisqEasyOfferbookMessage message, Direction direction, boolean isMyMessage) { - if (isMyMessage) { - String directionString = StringUtils.capitalize(Res.get("offer." + direction.name().toLowerCase())); - return Res.get("bisqEasy.tradeWizard.review.chatMessage.myMessageTitle", directionString); - } else { - return message.getText(); - } + bisqEasyOfferbookMessage); } } diff --git a/http-api/src/main/java/bisq/http_api/web_socket/rest_api_proxy/WebSocketRestApiRequest.java b/http-api/src/main/java/bisq/http_api/web_socket/rest_api_proxy/WebSocketRestApiRequest.java index 011352bfb9..95ef61d76f 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/rest_api_proxy/WebSocketRestApiRequest.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/rest_api_proxy/WebSocketRestApiRequest.java @@ -17,6 +17,7 @@ package bisq.http_api.web_socket.rest_api_proxy; +import bisq.http_api.web_socket.WebSocketMessage; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.EqualsAndHashCode; @@ -29,15 +30,13 @@ @Getter @EqualsAndHashCode @ToString -public class WebSocketRestApiRequest { +public class WebSocketRestApiRequest implements WebSocketMessage { // Client side full qualified class name for response class required for polymorphism support private String responseClassName; private String requestId; private String path; private String method; private String body; - // Client side full qualified class name set by serialize to support polymorphism - private String className; public static boolean isExpectedJson(String message) { return message.contains("requestId") && diff --git a/http-api/src/main/java/bisq/http_api/web_socket/rest_api_proxy/WebSocketRestApiResponse.java b/http-api/src/main/java/bisq/http_api/web_socket/rest_api_proxy/WebSocketRestApiResponse.java index dd416833e5..efc8324784 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/rest_api_proxy/WebSocketRestApiResponse.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/rest_api_proxy/WebSocketRestApiResponse.java @@ -17,6 +17,7 @@ package bisq.http_api.web_socket.rest_api_proxy; +import bisq.http_api.web_socket.WebSocketMessage; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; @@ -32,19 +33,15 @@ @Getter @EqualsAndHashCode @ToString -public class WebSocketRestApiResponse { - // Client side full qualified class name required for polymorphism support - private final String className; +public class WebSocketRestApiResponse implements WebSocketMessage { private final String requestId; private final int statusCode; private final String body; @JsonCreator - public WebSocketRestApiResponse(@JsonProperty("className") String className, - @JsonProperty("requestId") String requestId, + public WebSocketRestApiResponse(@JsonProperty("requestId") String requestId, @JsonProperty("statusCode") int statusCode, @JsonProperty("body") String body) { - this.className = className; this.requestId = requestId; this.statusCode = statusCode; this.body = body; diff --git a/http-api/src/main/java/bisq/http_api/web_socket/rest_api_proxy/WebSocketRestApiService.java b/http-api/src/main/java/bisq/http_api/web_socket/rest_api_proxy/WebSocketRestApiService.java index bcf9af8e8b..f07da42daa 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/rest_api_proxy/WebSocketRestApiService.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/rest_api_proxy/WebSocketRestApiService.java @@ -20,6 +20,7 @@ import bisq.common.application.Service; import bisq.http_api.web_socket.util.JsonUtil; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.ws.rs.core.Response; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; @@ -76,7 +77,7 @@ private WebSocketRestApiResponse sendToRestApiServer(WebSocketRestApiRequest req String errorMessage = RequestValidation.validateRequest(request); if (errorMessage != null) { log.error(errorMessage); - return new WebSocketRestApiResponse(request.getResponseClassName(), request.getRequestId(), 400, errorMessage); + return new WebSocketRestApiResponse(request.getRequestId(), Response.Status.BAD_REQUEST.getStatusCode(), errorMessage); } String url = restApiAddress + request.getPath(); @@ -91,14 +92,13 @@ private WebSocketRestApiResponse sendToRestApiServer(WebSocketRestApiRequest req HttpRequest httpRequest = requestBuilder.build(); log.info("Send {} httpRequest to {}. httpRequest={} ", method, url, httpRequest); // Blocking send - HttpClient httpClient1 = httpClient.orElseThrow(); - HttpResponse<String> httpResponse = httpClient1.send(httpRequest, HttpResponse.BodyHandlers.ofString()); + HttpResponse<String> httpResponse = httpClient.orElseThrow().send(httpRequest, HttpResponse.BodyHandlers.ofString()); log.info("httpResponse {}", httpResponse); - return new WebSocketRestApiResponse(request.getResponseClassName(), request.getRequestId(), httpResponse.statusCode(), httpResponse.body()); + return new WebSocketRestApiResponse( request.getRequestId(), httpResponse.statusCode(), httpResponse.body()); } catch (Exception e) { errorMessage = String.format("Error at sending a '%s' request to '%s' with body: '%s'. Error: %s", method, url, body, e.getMessage()); log.error(errorMessage, e); - return new WebSocketRestApiResponse(request.getResponseClassName(), request.getRequestId(), 500, errorMessage); + return new WebSocketRestApiResponse(request.getRequestId(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), errorMessage); } } } \ No newline at end of file diff --git a/http-api/src/main/java/bisq/http_api/web_socket/subscription/Subscriber.java b/http-api/src/main/java/bisq/http_api/web_socket/subscription/Subscriber.java index 7ba2f9c898..bf88566cd3 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/subscription/Subscriber.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/subscription/Subscriber.java @@ -37,20 +37,17 @@ public class Subscriber { private final Optional<String> parameter; private final String subscriberId; private final WebSocket webSocket; - private final String webSocketEventClassName; private final AtomicInteger sequenceNumber = new AtomicInteger(0); // sequenceNumber start with 0 at subscribe time and gets increased at each emitted WebSocketEvent private final ExecutorService executorService; public Subscriber(Topic topic, Optional<String> parameter, String subscriberId, - WebSocket webSocket, - String webSocketEventClassName) { + WebSocket webSocket) { this.topic = topic; this.parameter = parameter; this.subscriberId = subscriberId; this.webSocket = webSocket; - this.webSocketEventClassName = webSocketEventClassName; executorService = ExecutorFactory.newSingleThreadExecutor("Subscriber-" + topic.name() + "-" + subscriberId); } diff --git a/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriberRepository.java b/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriberRepository.java index 94890c918a..d772e035a2 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriberRepository.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriberRepository.java @@ -39,7 +39,7 @@ public void onConnectionClosed(WebSocket webSocket) { public void add(SubscriptionRequest request, WebSocket webSocket) { Topic topic = request.getTopic(); Optional<String> parameter = StringUtils.toOptional(request.getParameter()); - Subscriber subscriber = new Subscriber(topic, parameter, request.getRequestId(), webSocket, request.getWebSocketEventClassName()); + Subscriber subscriber = new Subscriber(topic, parameter, request.getRequestId(), webSocket); synchronized (subscribersByTopicLock) { Set<Subscriber> subscribers = subscribersByTopic.computeIfAbsent(topic, key -> new HashSet<>()); subscribers.add(subscriber); diff --git a/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriptionRequest.java b/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriptionRequest.java index 86e8b26e72..6311845c8b 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriptionRequest.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriptionRequest.java @@ -17,6 +17,7 @@ package bisq.http_api.web_socket.subscription; +import bisq.http_api.web_socket.WebSocketMessage; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.EqualsAndHashCode; @@ -31,20 +32,12 @@ @Getter @EqualsAndHashCode @ToString -public class SubscriptionRequest { - // Client side full qualified class name for response class required for polymorphism support - private String responseClassName; - // Client side full qualified class name for WebSocketEvent class required for polymorphism support - private String webSocketEventClassName; +public class SubscriptionRequest implements WebSocketMessage { private String requestId; private Topic topic; @Nullable private String parameter; - // Client side full qualified class name set by serialize to support polymorphism - private String className; - - public static Optional<SubscriptionRequest> fromJson(ObjectMapper objectMapper, String json) { try { return Optional.of(objectMapper.readValue(json, SubscriptionRequest.class)); diff --git a/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriptionResponse.java b/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriptionResponse.java index fb2973e058..fca4121ff4 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriptionResponse.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriptionResponse.java @@ -17,6 +17,7 @@ package bisq.http_api.web_socket.subscription; +import bisq.http_api.web_socket.WebSocketMessage; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; @@ -34,9 +35,7 @@ @Getter @EqualsAndHashCode @ToString -public class SubscriptionResponse { - // Client side full qualified class name required for polymorphism support - private final String className; +public class SubscriptionResponse implements WebSocketMessage { private final String requestId; @Nullable private final String payload; @@ -44,11 +43,9 @@ public class SubscriptionResponse { private final String errorMessage; @JsonCreator - public SubscriptionResponse(@JsonProperty("className") String className, - @JsonProperty("requestId") String requestId, + public SubscriptionResponse(@JsonProperty("requestId") String requestId, @JsonProperty("payload") @Nullable String payload, @JsonProperty("errorMessage") @Nullable String errorMessage) { - this.className = className; this.requestId = requestId; this.payload = payload; this.errorMessage = errorMessage; diff --git a/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriptionService.java b/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriptionService.java index 1a31e995db..f69d20ae1c 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriptionService.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/subscription/SubscriptionService.java @@ -87,7 +87,7 @@ private void subscribe(SubscriptionRequest request, WebSocket webSocket) { findWebSocketService(request.getTopic()) .flatMap(BaseWebSocketService::getJsonPayload) - .flatMap(json -> new SubscriptionResponse(request.getResponseClassName(), request.getRequestId(), json, null) + .flatMap(json -> new SubscriptionResponse(request.getRequestId(), json, null) .toJson(objectMapper)) .ifPresent(webSocket::send); } diff --git a/http-api/src/main/java/bisq/http_api/web_socket/subscription/WebSocketEvent.java b/http-api/src/main/java/bisq/http_api/web_socket/subscription/WebSocketEvent.java index 0130a7b387..24fcd29e24 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/subscription/WebSocketEvent.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/subscription/WebSocketEvent.java @@ -17,6 +17,7 @@ package bisq.http_api.web_socket.subscription; +import bisq.http_api.web_socket.WebSocketMessage; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; @@ -32,9 +33,7 @@ @Getter @EqualsAndHashCode @ToString -public class WebSocketEvent { - // Client side full qualified class name required for polymorphism support - private final String className; +public class WebSocketEvent implements WebSocketMessage { private final Topic topic; private final String subscriberId; private final String payload; @@ -42,13 +41,11 @@ public class WebSocketEvent { private final int sequenceNumber; @JsonCreator - public WebSocketEvent(@JsonProperty("className") String className, - @JsonProperty("topic") Topic topic, + public WebSocketEvent(@JsonProperty("topic") Topic topic, @JsonProperty("subscriberId") String subscriberId, @JsonProperty("payload") String payload, @JsonProperty("modificationType") ModificationType modificationType, @JsonProperty("sequenceNumber") int sequenceNumber) { - this.className = className; this.topic = topic; this.subscriberId = subscriberId; this.payload = payload; @@ -57,14 +54,13 @@ public WebSocketEvent(@JsonProperty("className") String className, } public static Optional<String> toJson(ObjectMapper objectMapper, - String className, Topic topic, String subscriberId, String payload, ModificationType modificationType, int sequenceNumber) { try { - var webSocketEvent = new WebSocketEvent(className, topic, subscriberId, payload, modificationType, sequenceNumber); + var webSocketEvent = new WebSocketEvent(topic, subscriberId, payload, modificationType, sequenceNumber); return Optional.of(objectMapper.writeValueAsString(webSocketEvent)); } catch (JsonProcessingException e) { log.error("Json serialisation failed", e); diff --git a/http-api/src/main/java/bisq/http_api/web_socket/util/JsonUtil.java b/http-api/src/main/java/bisq/http_api/web_socket/util/JsonUtil.java index 7b6b4b7986..92203549eb 100644 --- a/http-api/src/main/java/bisq/http_api/web_socket/util/JsonUtil.java +++ b/http-api/src/main/java/bisq/http_api/web_socket/util/JsonUtil.java @@ -21,14 +21,15 @@ import java.util.regex.Pattern; public class JsonUtil { - // We use by convention same class name. We get the className field set by the client. + // We use by convention same class name. We get the type field set by the client. public static boolean hasExpectedJsonClassName(Class<?> clazz, String json) { - String regex = "\"className\":\"[^\"]*\\.([^\"]+)\""; + String regex = "\"type\":\\s*\"([^\"]+)\""; // We use simple name Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(json); if (matcher.find()) { - String className = matcher.group(1); - return clazz.getSimpleName().equals(className); + String type = matcher.group(1); + String simpleName = clazz.getSimpleName(); + return simpleName.equals(type); } else { return false; } diff --git a/offer/src/main/java/bisq/offer/amount/OfferAmountFormatter.java b/offer/src/main/java/bisq/offer/amount/OfferAmountFormatter.java index 1877db2bbf..bfc2413d9c 100644 --- a/offer/src/main/java/bisq/offer/amount/OfferAmountFormatter.java +++ b/offer/src/main/java/bisq/offer/amount/OfferAmountFormatter.java @@ -28,7 +28,7 @@ import bisq.presentation.formatters.AmountFormatter; import lombok.extern.slf4j.Slf4j; -import java.util.function.Function; +import java.util.function.BiFunction; @Slf4j public class OfferAmountFormatter { @@ -39,11 +39,11 @@ public class OfferAmountFormatter { // Either min-max or fixed public static String formatBaseAmount(MarketPriceService marketPriceService, Offer<?, ?> offer) { - return formatBaseAmount(marketPriceService, offer.getAmountSpec(), offer.getPriceSpec(), offer.getMarket(), offer.hasAmountRange(), true); + return formatBaseAmount(marketPriceService, offer.getAmountSpec(), offer.getPriceSpec(), offer.getMarket(), offer.hasAmountRange(), true, true); } public static String formatBaseAmount(MarketPriceService marketPriceService, Offer<?, ?> offer, boolean withCode) { - return formatBaseAmount(marketPriceService, offer.getAmountSpec(), offer.getPriceSpec(), offer.getMarket(), offer.hasAmountRange(), withCode); + return formatBaseAmount(marketPriceService, offer.getAmountSpec(), offer.getPriceSpec(), offer.getMarket(), offer.hasAmountRange(), withCode, true); } public static String formatBaseAmount(MarketPriceService marketPriceService, @@ -51,11 +51,12 @@ public static String formatBaseAmount(MarketPriceService marketPriceService, PriceSpec priceSpec, Market market, boolean hasAmountRange, - boolean withCode) { + boolean withCode, + boolean useLowPrecision) { if (hasAmountRange) { - return formatBaseSideRangeAmount(marketPriceService, amountSpec, priceSpec, market, withCode); + return formatBaseSideRangeAmount(marketPriceService, amountSpec, priceSpec, market, withCode, useLowPrecision); } else { - return formatBaseSideFixedAmount(marketPriceService, amountSpec, priceSpec, market, withCode); + return formatBaseSideFixedAmount(marketPriceService, amountSpec, priceSpec, market, withCode, useLowPrecision); } } @@ -67,15 +68,18 @@ public static String formatBaseSideFixedAmount(MarketPriceService marketPriceSer public static String formatBaseSideFixedAmount(MarketPriceService marketPriceService, Offer<?, ?> offer, boolean withCode) { - return formatBaseSideFixedAmount(marketPriceService, offer.getAmountSpec(), offer.getPriceSpec(), offer.getMarket(), withCode); + return formatBaseSideFixedAmount(marketPriceService, offer.getAmountSpec(), offer.getPriceSpec(), offer.getMarket(), withCode, true); } public static String formatBaseSideFixedAmount(MarketPriceService marketPriceService, AmountSpec amountSpec, PriceSpec priceSpec, Market market, - boolean withCode) { - return OfferAmountUtil.findBaseSideFixedAmount(marketPriceService, amountSpec, priceSpec, market).map(getFormatFunction(withCode)).orElse(Res.get("data.na")); + boolean withCode, + boolean useLowPrecision) { + return OfferAmountUtil.findBaseSideFixedAmount(marketPriceService, amountSpec, priceSpec, market) + .map(monetary -> getFormatFunction(withCode).apply(monetary, useLowPrecision)) + .orElse(Res.get("data.na")); } // Min @@ -94,7 +98,9 @@ public static String formatBaseSideMinAmount(MarketPriceService marketPriceServi PriceSpec priceSpec, Market market, boolean withCode) { - return OfferAmountUtil.findBaseSideMinAmount(marketPriceService, amountSpec, priceSpec, market).map(getFormatFunction(withCode)).orElse(Res.get("data.na")); + return OfferAmountUtil.findBaseSideMinAmount(marketPriceService, amountSpec, priceSpec, market) + .map(monetary -> getFormatFunction(withCode).apply(monetary, true)) + .orElse(Res.get("data.na")); } // Max @@ -113,36 +119,43 @@ public static String formatBaseSideMaxAmount(MarketPriceService marketPriceServi PriceSpec priceSpec, Market market, boolean withCode) { - return OfferAmountUtil.findBaseSideMaxAmount(marketPriceService, amountSpec, priceSpec, market).map(getFormatFunction(withCode)).orElse(Res.get("data.na")); + return OfferAmountUtil.findBaseSideMaxAmount(marketPriceService, amountSpec, priceSpec, market) + .map(monetary -> getFormatFunction(withCode).apply(monetary, true)) + .orElse(Res.get("data.na")); } // Max or fixed public static String formatBaseSideMaxOrFixedAmount(MarketPriceService marketPriceService, Offer<?, ?> offer, - boolean withCode) { - return formatBaseSideMaxOrFixedAmount(marketPriceService, offer.getAmountSpec(), offer.getPriceSpec(), offer.getMarket(), withCode); + boolean withCode, + boolean useLowPrecision) { + return formatBaseSideMaxOrFixedAmount(marketPriceService, offer.getAmountSpec(), offer.getPriceSpec(), offer.getMarket(), withCode, useLowPrecision); } public static String formatBaseSideMaxOrFixedAmount(MarketPriceService marketPriceService, AmountSpec amountSpec, PriceSpec priceSpec, Market market, - boolean withCode) { - return OfferAmountUtil.findBaseSideMaxOrFixedAmount(marketPriceService, amountSpec, priceSpec, market).map(getFormatFunction(withCode)).orElse(Res.get("data.na")); + boolean withCode, + boolean useLowPrecision) { + return OfferAmountUtil.findBaseSideMaxOrFixedAmount(marketPriceService, amountSpec, priceSpec, market) + .map(monetary -> getFormatFunction(withCode).apply(monetary, useLowPrecision)) + .orElse(Res.get("data.na")); } // Range (Min - Max) public static String formatBaseSideRangeAmount(MarketPriceService marketPriceService, Offer<?, ?> offer, boolean withCode) { - return formatBaseSideRangeAmount(marketPriceService, offer.getAmountSpec(), offer.getPriceSpec(), offer.getMarket(), withCode); + return formatBaseSideRangeAmount(marketPriceService, offer.getAmountSpec(), offer.getPriceSpec(), offer.getMarket(), withCode, true); } public static String formatBaseSideRangeAmount(MarketPriceService marketPriceService, AmountSpec amountSpec, PriceSpec priceSpec, Market market, - boolean withCode) { + boolean withCode, + boolean useLowPrecision) { return formatBaseSideMinAmount(marketPriceService, amountSpec, priceSpec, market, false) + " - " + formatBaseSideMaxAmount(marketPriceService, amountSpec, priceSpec, market, withCode); } @@ -198,7 +211,9 @@ public static String formatQuoteSideFixedAmount(MarketPriceService marketPriceSe PriceSpec priceSpec, Market market, boolean withCode) { - return OfferAmountUtil.findQuoteSideFixedAmount(marketPriceService, amountSpec, priceSpec, market).map(getFormatFunction(withCode)).orElse(Res.get("data.na")); + return OfferAmountUtil.findQuoteSideFixedAmount(marketPriceService, amountSpec, priceSpec, market) + .map(monetary -> getFormatFunction(withCode).apply(monetary, true)) + .orElse(Res.get("data.na")); } // Min @@ -217,7 +232,9 @@ public static String formatQuoteSideMinAmount(MarketPriceService marketPriceServ PriceSpec priceSpec, Market market, boolean withCode) { - return OfferAmountUtil.findQuoteSideMinAmount(marketPriceService, amountSpec, priceSpec, market).map(getFormatFunction(withCode)).orElse(Res.get("data.na")); + return OfferAmountUtil.findQuoteSideMinAmount(marketPriceService, amountSpec, priceSpec, market) + .map(monetary -> getFormatFunction(withCode).apply(monetary, true)) + .orElse(Res.get("data.na")); } // Min or fixed @@ -236,7 +253,9 @@ public static String formatQuoteSideMinOrFixedAmount(MarketPriceService marketPr PriceSpec priceSpec, Market market, boolean withCode) { - return OfferAmountUtil.findQuoteSideMinOrFixedAmount(marketPriceService, amountSpec, priceSpec, market).map(getFormatFunction(withCode)).orElse(Res.get("data.na")); + return OfferAmountUtil.findQuoteSideMinOrFixedAmount(marketPriceService, amountSpec, priceSpec, market) + .map(monetary -> getFormatFunction(withCode).apply(monetary, true)) + .orElse(Res.get("data.na")); } // Max @@ -255,7 +274,9 @@ public static String formatQuoteSideMaxAmount(MarketPriceService marketPriceServ PriceSpec priceSpec, Market market, boolean withCode) { - return OfferAmountUtil.findQuoteSideMaxAmount(marketPriceService, amountSpec, priceSpec, market).map(getFormatFunction(withCode)).orElse(Res.get("data.na")); + return OfferAmountUtil.findQuoteSideMaxAmount(marketPriceService, amountSpec, priceSpec, market) + .map(monetary -> getFormatFunction(withCode).apply(monetary, true)) + .orElse(Res.get("data.na")); } // Max or fixed @@ -270,7 +291,7 @@ public static String formatQuoteSideMaxOrFixedAmount(MarketPriceService marketPr PriceSpec priceSpec, Market market, boolean withCode) { - return OfferAmountUtil.findQuoteSideMaxOrFixedAmount(marketPriceService, amountSpec, priceSpec, market).map(getFormatFunction(withCode)).orElse(Res.get("data.na")); + return OfferAmountUtil.findQuoteSideMaxOrFixedAmount(marketPriceService, amountSpec, priceSpec, market).map(monetary -> getFormatFunction(withCode).apply(monetary, true)).orElse(Res.get("data.na")); } // Range (Min - Max) @@ -294,7 +315,9 @@ public static String formatQuoteSideRangeAmount(MarketPriceService marketPriceSe // Private /////////////////////////////////////////////////////////////////////////////////////////////////// - private static Function<Monetary, String> getFormatFunction(boolean withCode) { - return withCode ? AmountFormatter::formatAmountWithCode : AmountFormatter::formatAmount; + private static BiFunction<Monetary, Boolean, String> getFormatFunction(boolean withCode) { + BiFunction<Monetary, Boolean, String> formatAmount = AmountFormatter::formatAmount; + BiFunction<Monetary, Boolean, String> formatAmountWithCode = AmountFormatter::formatAmountWithCode; + return withCode ? formatAmountWithCode : formatAmount; } } diff --git a/offer/src/main/java/bisq/offer/bisq_easy/BisqEasyOffer.java b/offer/src/main/java/bisq/offer/bisq_easy/BisqEasyOffer.java index a77a1d568b..be58de5e2b 100644 --- a/offer/src/main/java/bisq/offer/bisq_easy/BisqEasyOffer.java +++ b/offer/src/main/java/bisq/offer/bisq_easy/BisqEasyOffer.java @@ -78,7 +78,7 @@ public BisqEasyOffer(NetworkId makerNetworkId, ); } - private BisqEasyOffer(String id, + public BisqEasyOffer(String id, long date, NetworkId makerNetworkId, Direction direction, @@ -101,7 +101,9 @@ private BisqEasyOffer(String id, baseSidePaymentMethodSpecs, quoteSidePaymentMethodSpecs, offerOptions); - this.supportedLanguageCodes = supportedLanguageCodes; + + // We might get an immutable list, but we need to sort it, so wrap it into an ArrayList + this.supportedLanguageCodes = new ArrayList<>(supportedLanguageCodes); Collections.sort(this.supportedLanguageCodes); verify(); diff --git a/offer/src/main/java/bisq/offer/payment_method/PaymentMethodSpecUtil.java b/offer/src/main/java/bisq/offer/payment_method/PaymentMethodSpecUtil.java index bf83b16a04..0398e287b5 100644 --- a/offer/src/main/java/bisq/offer/payment_method/PaymentMethodSpecUtil.java +++ b/offer/src/main/java/bisq/offer/payment_method/PaymentMethodSpecUtil.java @@ -17,16 +17,22 @@ package bisq.offer.payment_method; -import bisq.account.payment_method.BitcoinPaymentMethod; -import bisq.account.payment_method.BitcoinPaymentRail; -import bisq.account.payment_method.FiatPaymentMethod; -import bisq.account.payment_method.PaymentMethod; +import bisq.account.payment_method.*; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; public class PaymentMethodSpecUtil { + public static BitcoinPaymentMethod getBitcoinPaymentMethod(String paymentMethod) { + return BitcoinPaymentMethodUtil.getPaymentMethod(paymentMethod); + } + + public static FiatPaymentMethod getFiatPaymentMethod(String paymentMethod) { + return FiatPaymentMethodUtil.getPaymentMethod(paymentMethod); + } + + public static List<BitcoinPaymentMethodSpec> createBitcoinPaymentMethodSpecs(List<BitcoinPaymentMethod> paymentMethods) { return paymentMethods.stream() .map(BitcoinPaymentMethodSpec::new) diff --git a/trade/src/main/java/bisq/trade/bisq_easy/BisqEasyTradeService.java b/trade/src/main/java/bisq/trade/bisq_easy/BisqEasyTradeService.java index 56222c0c92..ba66e568d8 100644 --- a/trade/src/main/java/bisq/trade/bisq_easy/BisqEasyTradeService.java +++ b/trade/src/main/java/bisq/trade/bisq_easy/BisqEasyTradeService.java @@ -229,7 +229,7 @@ public BisqEasyProtocol createBisqEasyProtocol(Identity takerIdentity, BitcoinPaymentMethodSpec bitcoinPaymentMethodSpec, FiatPaymentMethodSpec fiatPaymentMethodSpec, Optional<UserProfile> mediator, - PriceSpec agreedPriceSpec, + PriceSpec priceSpec, long marketPrice) { verifyTradingNotOnHalt(); verifyMinVersionForTrading(); @@ -244,7 +244,7 @@ public BisqEasyProtocol createBisqEasyProtocol(Identity takerIdentity, bitcoinPaymentMethodSpec, fiatPaymentMethodSpec, mediator, - agreedPriceSpec, + priceSpec, marketPrice); boolean isBuyer = bisqEasyOffer.getTakersDirection().isBuy(); NetworkId makerNetworkId = contract.getMaker().getNetworkId(); diff --git a/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyTakeOfferRequestHandler.java b/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyTakeOfferRequestHandler.java index 26f4bb8514..5a3d45293e 100644 --- a/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyTakeOfferRequestHandler.java +++ b/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyTakeOfferRequestHandler.java @@ -150,7 +150,7 @@ private void validateAmount(BisqEasyOffer takersOffer, BisqEasyContract takersCo Market market = takersOffer.getMarket(); MarketPrice marketPrice = marketPriceService.getMarketPriceByCurrencyMap().get(market); Optional<PriceQuote> priceQuote = PriceUtil.findQuote(marketPriceService, - takersContract.getAgreedPriceSpec(), market); + takersContract.getPriceSpec(), market); Optional<Monetary> amount = priceQuote.map(quote -> quote.toBaseSideMonetary(Monetary.from(takersContract.getQuoteSideAmount(), market.getQuoteCurrencyCode()))); diff --git a/user/src/main/java/bisq/user/profile/UserProfile.java b/user/src/main/java/bisq/user/profile/UserProfile.java index 632e4e1325..547b66a089 100644 --- a/user/src/main/java/bisq/user/profile/UserProfile.java +++ b/user/src/main/java/bisq/user/profile/UserProfile.java @@ -133,7 +133,7 @@ private UserProfile(String nickName, ApplicationVersion.getVersion().getVersionAsString()); } - private UserProfile(int version, + public UserProfile(int version, String nickName, ProofOfWork proofOfWork, int avatarVersion,