diff --git a/apitest/src/test/java/bisq/apitest/method/MethodTest.java b/apitest/src/test/java/bisq/apitest/method/MethodTest.java index f01c4ecbf53..5ca04409449 100644 --- a/apitest/src/test/java/bisq/apitest/method/MethodTest.java +++ b/apitest/src/test/java/bisq/apitest/method/MethodTest.java @@ -30,7 +30,10 @@ import java.io.IOException; import java.io.PrintWriter; +import java.math.BigDecimal; + import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.slf4j.Logger; @@ -42,6 +45,7 @@ import static bisq.apitest.config.ApiTestRateMeterInterceptorConfig.getTestRateMeterInterceptorConfig; import static bisq.cli.table.builder.TableType.BSQ_BALANCE_TBL; import static bisq.cli.table.builder.TableType.BTC_BALANCE_TBL; +import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.stream; @@ -162,6 +166,17 @@ protected final bisq.core.payment.PaymentAccount createPaymentAccount(GrpcClient return bisq.core.payment.PaymentAccount.fromProto(paymentAccount, CORE_PROTO_RESOLVER); } + public static final Supplier defaultBuyerSecurityDepositPct = () -> { + var defaultPct = BigDecimal.valueOf(getDefaultBuyerSecurityDepositAsPercent()); + if (defaultPct.precision() != 2) + throw new IllegalStateException(format( + "Unexpected decimal precision, expected 2 but actual is %d%n." + + "Check for changes to Restrictions.getDefaultBuyerSecurityDepositAsPercent()", + defaultPct.precision())); + + return defaultPct.movePointRight(2).doubleValue(); + }; + public static String formatBalancesTbls(BalancesInfo allBalances) { StringBuilder balances = new StringBuilder(BTC).append("\n"); balances.append(new TableBuilder(BTC_BALANCE_TBL, allBalances.getBtc()).build()); diff --git a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java index 57495bb696a..627c1befe3b 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java @@ -24,10 +24,10 @@ import protobuf.PaymentAccount; import java.math.BigDecimal; +import java.math.MathContext; import java.util.ArrayList; import java.util.List; -import java.util.function.BiFunction; import java.util.function.Function; import lombok.Setter; @@ -44,7 +44,6 @@ import static bisq.apitest.config.BisqAppConfig.bobdaemon; import static bisq.apitest.config.BisqAppConfig.seednode; import static bisq.cli.table.builder.TableType.OFFER_TBL; -import static bisq.common.util.MathUtils.exactMultiply; import static java.lang.String.format; import static java.lang.System.out; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -62,7 +61,7 @@ public abstract class AbstractOfferTest extends MethodTest { protected static final int ACTIVATE_OFFER = 1; protected static final int DEACTIVATE_OFFER = 0; - protected static final long NO_TRIGGER_PRICE = 0; + protected static final String NO_TRIGGER_PRICE = "0"; @Setter protected static boolean isLongRunningTest; @@ -78,57 +77,35 @@ public abstract class AbstractOfferTest extends MethodTest { @BeforeAll public static void setUp() { + setUp(false); + } + + public static void setUp(boolean startSupportingAppsInDebugMode) { startSupportingApps(true, - false, + startSupportingAppsInDebugMode, bitcoind, seednode, arbdaemon, alicedaemon, bobdaemon); - initSwapPaymentAccounts(); createLegacyBsqPaymentAccounts(); } - // Mkt Price Margin value of offer returned from server is scaled down by 10^-2. - protected final Function scaledDownMktPriceMargin = (mktPriceMargin) -> - exactMultiply(mktPriceMargin, 0.01); - - // Price value of fiat offer returned from server will be scaled up by 10^4. - protected final Function scaledUpFiatOfferPrice = (price) -> { - BigDecimal factor = new BigDecimal(10).pow(4); - return price.multiply(factor).longValue(); - }; - - // Price value of altcoin offer returned from server will be scaled up by 10^8. - protected final Function scaledUpAltcoinOfferPrice = (altcoinPriceAsString) -> { - BigDecimal factor = new BigDecimal(10).pow(8); - BigDecimal priceAsBigDecimal = new BigDecimal(altcoinPriceAsString); - return priceAsBigDecimal.multiply(factor).longValue(); - }; - - protected final BiFunction calcFiatTriggerPriceAsLong = (base, delta) -> { - var priceAsDouble = new BigDecimal(base).add(new BigDecimal(delta)).doubleValue(); - return Double.valueOf(exactMultiply(priceAsDouble, 10_000)).longValue(); - }; - - protected final BiFunction calcAltcoinTriggerPriceAsLong = (base, delta) -> { - var priceAsDouble = new BigDecimal(base).add(new BigDecimal(delta)).doubleValue(); - return Double.valueOf(exactMultiply(priceAsDouble, 100_000_000)).longValue(); - }; - - protected final BiFunction calcPriceAsString = (base, delta) -> { - var priceAsBigDecimal = new BigDecimal(Double.toString(base)) - .add(new BigDecimal(Double.toString(delta))); - return priceAsBigDecimal.toPlainString(); - }; - - protected final Function toOfferTable = (offer) -> + protected static final Function toOfferTable = (offer) -> new TableBuilder(OFFER_TBL, offer).build().toString(); - protected final Function, String> toOffersTable = (offers) -> + protected static final Function, String> toOffersTable = (offers) -> new TableBuilder(OFFER_TBL, offers).build().toString(); + protected static String calcPriceAsString(double base, double delta, int precision) { + var mathContext = new MathContext(precision); + var priceAsBigDecimal = new BigDecimal(Double.toString(base), mathContext) + .add(new BigDecimal(Double.toString(delta), mathContext)) + .round(mathContext); + return format("%." + precision + "f", priceAsBigDecimal.doubleValue()); + } + protected OfferInfo getAvailableBsqSwapOffer(GrpcClient client, OfferDirection direction, boolean checkForLoggedExceptions) { @@ -183,12 +160,12 @@ public static void createXmrPaymentAccounts() { XMR, "44G4jWmSvTEfifSUZzTDnJVLPvYATmq9XhhtDqUof1BGCLceG82EQsVYG9Q9GN4bJcjbAJEc1JD1m5G7iK4UPZqACubV4Mq", false); - log.debug("Alices XMR Account: {}", alicesXmrAcct); + log.trace("Alices XMR Account: {}", alicesXmrAcct); bobsXmrAcct = bobClient.createCryptoCurrencyPaymentAccount("Bob's XMR Account", XMR, "4BDRhdSBKZqAXs3PuNTbMtaXBNqFj5idC2yMVnQj8Rm61AyKY8AxLTt9vGRJ8pwcG4EtpyD8YpGqdZWCZ2VZj6yVBN2RVKs", false); - log.debug("Bob's XMR Account: {}", bobsXmrAcct); + log.trace("Bob's XMR Account: {}", bobsXmrAcct); } @AfterAll diff --git a/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java index c506794201e..7c2ff5e6f92 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java @@ -113,13 +113,15 @@ private void createBsqSwapOffer() { 1_000_000L, 1_000_000L, "0.00005"); - log.debug("BsqSwap Sell BSQ (Buy BTC) OFFER:\n{}", bsqSwapOffer); + log.debug("BsqSwap SELL BSQ (BUY BTC) Offer:\n{}", toOfferTable.apply(bsqSwapOffer)); var newOfferId = bsqSwapOffer.getId(); assertNotEquals("", newOfferId); assertEquals(BUY.name(), bsqSwapOffer.getDirection()); - assertEquals(5_000, bsqSwapOffer.getPrice()); + assertEquals("0.00005000", bsqSwapOffer.getPrice()); assertEquals(1_000_000L, bsqSwapOffer.getAmount()); assertEquals(1_000_000L, bsqSwapOffer.getMinAmount()); + assertEquals("200.00", bsqSwapOffer.getVolume()); + assertEquals("200.00", bsqSwapOffer.getMinVolume()); assertEquals(BSQ, bsqSwapOffer.getBaseCurrencyCode()); assertEquals(BTC, bsqSwapOffer.getCounterCurrencyCode()); @@ -142,7 +144,7 @@ private void testGetMyBsqSwapOffer(OfferInfo bsqSwapOffer) { if (numFetchAttempts >= 9) fail(format("Alice giving up on fetching her (my) bsq swap offer after %d attempts.", numFetchAttempts), ex); - sleep(1000); + sleep(1500); } } } @@ -162,7 +164,7 @@ private void testGetBsqSwapOffer(OfferInfo bsqSwapOffer) { if (numFetchAttempts > 9) fail(format("Bob gave up on fetching available bsq swap offer after %d attempts.", numFetchAttempts), ex); - sleep(1000); + sleep(1500); } } } diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java index d0ec6d86f1b..119427c0cf1 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java @@ -33,7 +33,6 @@ import org.junit.jupiter.api.TestMethodOrder; import static bisq.apitest.config.ApiTestConfig.BSQ; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static org.junit.jupiter.api.Assertions.assertEquals; import static protobuf.OfferDirection.BUY; @@ -52,7 +51,7 @@ public class CancelOfferTest extends AbstractOfferTest { 10000000L, 10000000L, 0.00, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), paymentAccountId, BSQ, NO_TRIGGER_PRICE); diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CreateBSQOffersTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CreateBSQOffersTest.java index 43f87fd1568..1807d1000b5 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/CreateBSQOffersTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateBSQOffersTest.java @@ -32,7 +32,6 @@ import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.apitest.config.ApiTestConfig.BTC; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -63,10 +62,10 @@ public void testCreateBuy1BTCFor20KBSQOffer() { 100_000_000L, 100_000_000L, "0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesLegacyBsqAcct.getId(), MAKER_FEE_CURRENCY_CODE); - log.debug("Sell BSQ (Buy BTC) OFFER:\n{}", toOfferTable.apply(newOffer)); + log.debug("Sell BSQ (Buy BTC) Offer:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); @@ -74,9 +73,11 @@ public void testCreateBuy1BTCFor20KBSQOffer() { assertNotEquals("", newOfferId); assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); + assertEquals("0.00005000", newOffer.getPrice()); assertEquals(100_000_000L, newOffer.getAmount()); assertEquals(100_000_000L, newOffer.getMinAmount()); + assertEquals("20000.00", newOffer.getVolume()); + assertEquals("20000.00", newOffer.getMinVolume()); assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId()); assertEquals(BSQ, newOffer.getBaseCurrencyCode()); @@ -91,9 +92,11 @@ public void testCreateBuy1BTCFor20KBSQOffer() { assertEquals(newOfferId, newOffer.getId()); assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); + assertEquals("0.00005000", newOffer.getPrice()); assertEquals(100_000_000L, newOffer.getAmount()); assertEquals(100_000_000L, newOffer.getMinAmount()); + assertEquals("20000.00", newOffer.getVolume()); + assertEquals("20000.00", newOffer.getMinVolume()); assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId()); assertEquals(BSQ, newOffer.getBaseCurrencyCode()); @@ -110,10 +113,10 @@ public void testCreateSell1BTCFor20KBSQOffer() { 100_000_000L, 100_000_000L, "0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesLegacyBsqAcct.getId(), MAKER_FEE_CURRENCY_CODE); - log.debug("SELL 20K BSQ OFFER:\n{}", toOfferTable.apply(newOffer)); + log.debug("SELL 20K BSQ Offer:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); @@ -121,9 +124,11 @@ public void testCreateSell1BTCFor20KBSQOffer() { assertNotEquals("", newOfferId); assertEquals(SELL.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); + assertEquals("0.00005000", newOffer.getPrice()); assertEquals(100_000_000L, newOffer.getAmount()); assertEquals(100_000_000L, newOffer.getMinAmount()); + assertEquals("20000.00", newOffer.getVolume()); + assertEquals("20000.00", newOffer.getMinVolume()); assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId()); assertEquals(BSQ, newOffer.getBaseCurrencyCode()); @@ -138,9 +143,11 @@ public void testCreateSell1BTCFor20KBSQOffer() { assertEquals(newOfferId, newOffer.getId()); assertEquals(SELL.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); + assertEquals("0.00005000", newOffer.getPrice()); assertEquals(100_000_000L, newOffer.getAmount()); assertEquals(100_000_000L, newOffer.getMinAmount()); + assertEquals("20000.00", newOffer.getVolume()); + assertEquals("20000.00", newOffer.getMinVolume()); assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId()); assertEquals(BSQ, newOffer.getBaseCurrencyCode()); @@ -157,10 +164,10 @@ public void testCreateBuyBTCWith1To2KBSQOffer() { 10_000_000L, 5_000_000L, "0.00005", // FIXED PRICE IN BTC sats FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesLegacyBsqAcct.getId(), MAKER_FEE_CURRENCY_CODE); - log.debug("BUY 1-2K BSQ OFFER:\n{}", toOfferTable.apply(newOffer)); + log.debug("BUY 1-2K BSQ Offer:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); @@ -168,9 +175,11 @@ public void testCreateBuyBTCWith1To2KBSQOffer() { assertNotEquals("", newOfferId); assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); + assertEquals("0.00005000", newOffer.getPrice()); assertEquals(10_000_000L, newOffer.getAmount()); assertEquals(5_000_000L, newOffer.getMinAmount()); + assertEquals("2000.00", newOffer.getVolume()); + assertEquals("1000.00", newOffer.getMinVolume()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId()); assertEquals(BSQ, newOffer.getBaseCurrencyCode()); @@ -185,9 +194,11 @@ public void testCreateBuyBTCWith1To2KBSQOffer() { assertEquals(newOfferId, newOffer.getId()); assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); + assertEquals("0.00005000", newOffer.getPrice()); assertEquals(10_000_000L, newOffer.getAmount()); assertEquals(5_000_000L, newOffer.getMinAmount()); + assertEquals("2000.00", newOffer.getVolume()); + assertEquals("1000.00", newOffer.getMinVolume()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId()); assertEquals(BSQ, newOffer.getBaseCurrencyCode()); @@ -204,10 +215,10 @@ public void testCreateSellBTCFor5To10KBSQOffer() { 50_000_000L, 25_000_000L, "0.00005", // FIXED PRICE IN BTC sats FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesLegacyBsqAcct.getId(), MAKER_FEE_CURRENCY_CODE); - log.debug("SELL 5-10K BSQ OFFER:\n{}", toOfferTable.apply(newOffer)); + log.debug("SELL 5-10K BSQ Offer:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); @@ -215,9 +226,11 @@ public void testCreateSellBTCFor5To10KBSQOffer() { assertNotEquals("", newOfferId); assertEquals(SELL.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); + assertEquals("0.00005000", newOffer.getPrice()); assertEquals(50_000_000L, newOffer.getAmount()); assertEquals(25_000_000L, newOffer.getMinAmount()); + assertEquals("10000.00", newOffer.getVolume()); + assertEquals("5000.00", newOffer.getMinVolume()); assertEquals(7_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId()); assertEquals(BSQ, newOffer.getBaseCurrencyCode()); @@ -232,9 +245,11 @@ public void testCreateSellBTCFor5To10KBSQOffer() { assertEquals(newOfferId, newOffer.getId()); assertEquals(SELL.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); + assertEquals("0.00005000", newOffer.getPrice()); assertEquals(50_000_000L, newOffer.getAmount()); assertEquals(25_000_000L, newOffer.getMinAmount()); + assertEquals("10000.00", newOffer.getVolume()); + assertEquals("5000.00", newOffer.getMinVolume()); assertEquals(7_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId()); assertEquals(BSQ, newOffer.getBaseCurrencyCode()); @@ -246,7 +261,7 @@ public void testCreateSellBTCFor5To10KBSQOffer() { @Order(5) public void testGetAllMyBsqOffers() { List offers = aliceClient.getMyCryptoCurrencyOffersSortedByDate(BSQ); - log.debug("ALL ALICE'S BSQ OFFERS:\n{}", toOffersTable.apply(offers)); + log.debug("All Alice's BSQ Offers:\n{}", toOffersTable.apply(offers)); assertEquals(4, offers.size()); log.debug("ALICE'S BALANCES\n{}", formatBalancesTbls(aliceClient.getBalances())); } @@ -255,7 +270,7 @@ public void testGetAllMyBsqOffers() { @Order(6) public void testGetAvailableBsqOffers() { List offers = bobClient.getCryptoCurrencyOffersSortedByDate(BSQ); - log.debug("ALL BOB'S AVAILABLE BSQ OFFERS:\n{}", toOffersTable.apply(offers)); + log.debug("All Bob's Available BSQ Offers:\n{}", toOffersTable.apply(offers)); assertEquals(4, offers.size()); log.debug("BOB'S BALANCES\n{}", formatBalancesTbls(bobClient.getBalances())); } @@ -264,6 +279,6 @@ private void genBtcBlockAndWaitForOfferPreparation() { // Extra time is needed for the OfferUtils#isBsqForMakerFeeAvailable, which // can sometimes return an incorrect false value if the BsqWallet's // available confirmed balance is temporarily = zero during BSQ offer prep. - genBtcBlocksThenWait(1, 5000); + genBtcBlocksThenWait(1, 5_000); } } diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingFixedPriceTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingFixedPriceTest.java index 7ec7d98d1d4..7691a5e26d4 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingFixedPriceTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingFixedPriceTest.java @@ -31,7 +31,6 @@ import static bisq.apitest.config.ApiTestConfig.BTC; import static bisq.apitest.config.ApiTestConfig.EUR; import static bisq.apitest.config.ApiTestConfig.USD; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -55,10 +54,10 @@ public void testCreateAUDBTCBuyOfferUsingFixedPrice16000() { 10_000_000L, 10_000_000L, "36000", - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), audAccount.getId(), MAKER_FEE_CURRENCY_CODE); - log.debug("OFFER #1:\n{}", toOfferTable.apply(newOffer)); + log.debug("Offer #1:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); @@ -66,9 +65,11 @@ public void testCreateAUDBTCBuyOfferUsingFixedPrice16000() { assertNotEquals("", newOfferId); assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(360_000_000, newOffer.getPrice()); + assertEquals("36000.0000", newOffer.getPrice()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(10_000_000, newOffer.getMinAmount()); + assertEquals("3600", newOffer.getVolume()); + assertEquals("3600", newOffer.getMinVolume()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(audAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(BTC, newOffer.getBaseCurrencyCode()); @@ -81,9 +82,11 @@ public void testCreateAUDBTCBuyOfferUsingFixedPrice16000() { assertEquals(newOfferId, newOffer.getId()); assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(360_000_000, newOffer.getPrice()); + assertEquals("36000.0000", newOffer.getPrice()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(10_000_000, newOffer.getMinAmount()); + assertEquals("3600", newOffer.getVolume()); + assertEquals("3600", newOffer.getMinVolume()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(audAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(BTC, newOffer.getBaseCurrencyCode()); @@ -100,10 +103,10 @@ public void testCreateUSDBTCBuyOfferUsingFixedPrice100001234() { 10_000_000L, 10_000_000L, "30000.1234", - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), usdAccount.getId(), MAKER_FEE_CURRENCY_CODE); - log.debug("OFFER #2:\n{}", toOfferTable.apply(newOffer)); + log.debug("Offer #2:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); @@ -111,9 +114,11 @@ public void testCreateUSDBTCBuyOfferUsingFixedPrice100001234() { assertNotEquals("", newOfferId); assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(300_001_234, newOffer.getPrice()); + assertEquals("30000.1234", newOffer.getPrice()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(10_000_000, newOffer.getMinAmount()); + assertEquals("3000", newOffer.getVolume()); + assertEquals("3000", newOffer.getMinVolume()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(BTC, newOffer.getBaseCurrencyCode()); @@ -126,9 +131,11 @@ public void testCreateUSDBTCBuyOfferUsingFixedPrice100001234() { assertEquals(newOfferId, newOffer.getId()); assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(300_001_234, newOffer.getPrice()); + assertEquals("30000.1234", newOffer.getPrice()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(10_000_000, newOffer.getMinAmount()); + assertEquals("3000", newOffer.getVolume()); + assertEquals("3000", newOffer.getMinVolume()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(BTC, newOffer.getBaseCurrencyCode()); @@ -145,10 +152,10 @@ public void testCreateEURBTCSellOfferUsingFixedPrice95001234() { 10_000_000L, 5_000_000L, "29500.1234", - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), eurAccount.getId(), MAKER_FEE_CURRENCY_CODE); - log.debug("OFFER #3:\n{}", toOfferTable.apply(newOffer)); + log.debug("Offer #3:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); @@ -156,9 +163,11 @@ public void testCreateEURBTCSellOfferUsingFixedPrice95001234() { assertNotEquals("", newOfferId); assertEquals(SELL.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(295_001_234, newOffer.getPrice()); + assertEquals("29500.1234", newOffer.getPrice()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(5_000_000, newOffer.getMinAmount()); + assertEquals("2950", newOffer.getVolume()); + assertEquals("1475", newOffer.getMinVolume()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(eurAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(BTC, newOffer.getBaseCurrencyCode()); @@ -171,9 +180,11 @@ public void testCreateEURBTCSellOfferUsingFixedPrice95001234() { assertEquals(newOfferId, newOffer.getId()); assertEquals(SELL.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(295_001_234, newOffer.getPrice()); + assertEquals("29500.1234", newOffer.getPrice()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(5_000_000, newOffer.getMinAmount()); + assertEquals("2950", newOffer.getVolume()); + assertEquals("1475", newOffer.getMinVolume()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(eurAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(BTC, newOffer.getBaseCurrencyCode()); diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java index cce443206a3..fc61b5699dc 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java @@ -17,14 +17,10 @@ package bisq.apitest.method.offer; -import bisq.core.monetary.Altcoin; -import bisq.core.monetary.Price; import bisq.core.payment.PaymentAccount; import bisq.proto.grpc.OfferInfo; -import org.bitcoinj.utils.Fiat; - import java.text.DecimalFormat; import java.math.BigDecimal; @@ -42,8 +38,6 @@ import static bisq.common.util.MathUtils.roundDouble; import static bisq.common.util.MathUtils.scaleDownByPowerOf10; import static bisq.common.util.MathUtils.scaleUpByPowerOf10; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; -import static bisq.core.locale.CurrencyUtil.isCryptoCurrency; import static java.lang.Math.abs; import static java.lang.String.format; import static java.math.RoundingMode.HALF_UP; @@ -70,17 +64,17 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { @Order(1) public void testCreateUSDBTCBuyOffer5PctPriceMargin() { PaymentAccount usdAccount = createDummyF2FAccount(aliceClient, "US"); - double priceMarginPctInput = 5.00; + double priceMarginPctInput = 5.00d; var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(), "usd", 10_000_000L, 10_000_000L, priceMarginPctInput, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), usdAccount.getId(), MAKER_FEE_CURRENCY_CODE, NO_TRIGGER_PRICE); - log.debug("OFFER #1:\n{}", toOfferTable.apply(newOffer)); + log.debug("Offer #1:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); @@ -88,6 +82,7 @@ public void testCreateUSDBTCBuyOffer5PctPriceMargin() { assertNotEquals("", newOfferId); assertEquals(BUY.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); + assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(10_000_000, newOffer.getMinAmount()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); @@ -102,6 +97,7 @@ public void testCreateUSDBTCBuyOffer5PctPriceMargin() { assertEquals(newOfferId, newOffer.getId()); assertEquals(BUY.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); + assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(10_000_000, newOffer.getMinAmount()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); @@ -117,17 +113,17 @@ public void testCreateUSDBTCBuyOffer5PctPriceMargin() { @Order(2) public void testCreateNZDBTCBuyOfferMinus2PctPriceMargin() { PaymentAccount nzdAccount = createDummyF2FAccount(aliceClient, "NZ"); - double priceMarginPctInput = -2.00; + double priceMarginPctInput = -2.00d; // -2% var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(), "nzd", 10_000_000L, 10_000_000L, priceMarginPctInput, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), nzdAccount.getId(), MAKER_FEE_CURRENCY_CODE, NO_TRIGGER_PRICE); - log.debug("OFFER #2:\n{}", toOfferTable.apply(newOffer)); + log.debug("Offer #2:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); @@ -135,6 +131,7 @@ public void testCreateNZDBTCBuyOfferMinus2PctPriceMargin() { assertNotEquals("", newOfferId); assertEquals(BUY.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); + assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(10_000_000, newOffer.getMinAmount()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); @@ -149,6 +146,7 @@ public void testCreateNZDBTCBuyOfferMinus2PctPriceMargin() { assertEquals(newOfferId, newOffer.getId()); assertEquals(BUY.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); + assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(10_000_000, newOffer.getMinAmount()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); @@ -170,11 +168,11 @@ public void testCreateGBPBTCSellOfferMinus1Point5PctPriceMargin() { 10_000_000L, 5_000_000L, priceMarginPctInput, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), gbpAccount.getId(), MAKER_FEE_CURRENCY_CODE, NO_TRIGGER_PRICE); - log.debug("OFFER #3:\n{}", toOfferTable.apply(newOffer)); + log.debug("Offer #3:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); @@ -182,6 +180,7 @@ public void testCreateGBPBTCSellOfferMinus1Point5PctPriceMargin() { assertNotEquals("", newOfferId); assertEquals(SELL.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); + assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(5_000_000, newOffer.getMinAmount()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); @@ -196,6 +195,7 @@ public void testCreateGBPBTCSellOfferMinus1Point5PctPriceMargin() { assertEquals(newOfferId, newOffer.getId()); assertEquals(SELL.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); + assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(5_000_000, newOffer.getMinAmount()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); @@ -217,11 +217,11 @@ public void testCreateBRLBTCSellOffer6Point55PctPriceMargin() { 10_000_000L, 5_000_000L, priceMarginPctInput, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), brlAccount.getId(), MAKER_FEE_CURRENCY_CODE, NO_TRIGGER_PRICE); - log.debug("OFFER #4:\n{}", toOfferTable.apply(newOffer)); + log.debug("Offer #4:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); @@ -229,6 +229,7 @@ public void testCreateBRLBTCSellOffer6Point55PctPriceMargin() { assertNotEquals("", newOfferId); assertEquals(SELL.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); + assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(5_000_000, newOffer.getMinAmount()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); @@ -243,6 +244,7 @@ public void testCreateBRLBTCSellOffer6Point55PctPriceMargin() { assertEquals(newOfferId, newOffer.getId()); assertEquals(SELL.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); + assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct()); assertEquals(10_000_000, newOffer.getAmount()); assertEquals(5_000_000, newOffer.getMinAmount()); assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); @@ -259,45 +261,42 @@ public void testCreateBRLBTCSellOffer6Point55PctPriceMargin() { public void testCreateUSDBTCBuyOfferWithTriggerPrice() { PaymentAccount usdAccount = createDummyF2FAccount(aliceClient, "US"); double mktPriceAsDouble = aliceClient.getBtcPrice("usd"); - BigDecimal mktPrice = new BigDecimal(Double.toString(mktPriceAsDouble)); - BigDecimal triggerPrice = mktPrice.add(new BigDecimal("1000.9999")); - long triggerPriceAsLong = Price.parse(USD, triggerPrice.toString()).getValue(); - + String triggerPrice = calcPriceAsString(mktPriceAsDouble, Double.parseDouble("1000.9999"), 4); var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(), "usd", 10_000_000L, 5_000_000L, 0.0, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), usdAccount.getId(), MAKER_FEE_CURRENCY_CODE, - triggerPriceAsLong); + triggerPrice); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); genBtcBlocksThenWait(1, 4000); // give time to add to offer book newOffer = aliceClient.getOffer(newOffer.getId()); - log.debug("OFFER #5:\n{}", toOfferTable.apply(newOffer)); + log.debug("Offer #5:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertFalse(newOffer.getIsMyPendingOffer()); - assertEquals(triggerPriceAsLong, newOffer.getTriggerPrice()); + assertEquals(triggerPrice, newOffer.getTriggerPrice()); } private void assertCalculatedPriceIsCorrect(OfferInfo offer, double priceMarginPctInput) { assertTrue(() -> { String counterCurrencyCode = offer.getCounterCurrencyCode(); double mktPrice = aliceClient.getBtcPrice(counterCurrencyCode); - double scaledOfferPrice = getScaledOfferPrice(offer.getPrice(), counterCurrencyCode); + double priceAsDouble = Double.parseDouble(offer.getPrice()); double expectedDiffPct = scaleDownByPowerOf10(priceMarginPctInput, 2); double actualDiffPct = offer.getDirection().equals(BUY.name()) - ? getPercentageDifference(scaledOfferPrice, mktPrice) - : getPercentageDifference(mktPrice, scaledOfferPrice); + ? getPercentageDifference(priceAsDouble, mktPrice) + : getPercentageDifference(mktPrice, priceAsDouble); double pctDiffDelta = abs(expectedDiffPct) - abs(actualDiffPct); return isCalculatedPriceWithinErrorTolerance(pctDiffDelta, expectedDiffPct, actualDiffPct, mktPrice, - scaledOfferPrice, + priceAsDouble, offer); }); } @@ -308,11 +307,6 @@ private double getPercentageDifference(double price1, double price2) { .doubleValue(); } - private double getScaledOfferPrice(double offerPrice, String currencyCode) { - int precision = isCryptoCurrency(currencyCode) ? Altcoin.SMALLEST_UNIT_EXPONENT : Fiat.SMALLEST_UNIT_EXPONENT; - return scaleDownByPowerOf10(offerPrice, precision); - } - private boolean isCalculatedPriceWithinErrorTolerance(double delta, double expectedDiffPct, double actualDiffPct, @@ -333,7 +327,7 @@ private boolean isCalculatedPriceWithinErrorTolerance(double delta, actualDiffPct, mktPrice, scaledOfferPrice); - log.warn(offer.toString()); + log.trace(offer.toString()); } return true; diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CreateXMROffersTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CreateXMROffersTest.java index cf40632e8d9..cb9db33a145 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/CreateXMROffersTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateXMROffersTest.java @@ -33,7 +33,6 @@ import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.apitest.config.ApiTestConfig.BTC; import static bisq.apitest.config.ApiTestConfig.XMR; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -65,8 +64,8 @@ public void testCreateFixedPriceBuy1BTCFor200KXMROffer() { XMR, 100_000_000L, 75_000_000L, - "0.005", // FIXED PRICE IN BTC (satoshis) FOR 1 XMR - getDefaultBuyerSecurityDepositAsPercent(), + "0.005", // FIXED PRICE IN BTC FOR 1 XMR + defaultBuyerSecurityDepositPct.get(), alicesXmrAcct.getId(), MAKER_FEE_CURRENCY_CODE); log.debug("Sell XMR (Buy BTC) offer:\n{}", toOfferTable.apply(newOffer)); @@ -77,7 +76,7 @@ public void testCreateFixedPriceBuy1BTCFor200KXMROffer() { assertNotEquals("", newOfferId); assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(500_000L, newOffer.getPrice()); + assertEquals("0.00500000", newOffer.getPrice()); assertEquals(100_000_000L, newOffer.getAmount()); assertEquals(75_000_000L, newOffer.getMinAmount()); assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); @@ -94,7 +93,7 @@ public void testCreateFixedPriceBuy1BTCFor200KXMROffer() { assertEquals(newOfferId, newOffer.getId()); assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(500_000L, newOffer.getPrice()); + assertEquals("0.00500000", newOffer.getPrice()); assertEquals(100_000_000L, newOffer.getAmount()); assertEquals(75_000_000L, newOffer.getMinAmount()); assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); @@ -113,7 +112,7 @@ public void testCreateFixedPriceSell1BTCFor200KXMROffer() { 100_000_000L, 50_000_000L, "0.005", // FIXED PRICE IN BTC (satoshis) FOR 1 XMR - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesXmrAcct.getId(), MAKER_FEE_CURRENCY_CODE); log.debug("Buy XMR (Sell BTC) offer:\n{}", toOfferTable.apply(newOffer)); @@ -124,7 +123,7 @@ public void testCreateFixedPriceSell1BTCFor200KXMROffer() { assertNotEquals("", newOfferId); assertEquals(SELL.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(500_000L, newOffer.getPrice()); + assertEquals("0.00500000", newOffer.getPrice()); assertEquals(100_000_000L, newOffer.getAmount()); assertEquals(50_000_000L, newOffer.getMinAmount()); assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); @@ -141,7 +140,7 @@ public void testCreateFixedPriceSell1BTCFor200KXMROffer() { assertEquals(newOfferId, newOffer.getId()); assertEquals(SELL.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(500_000L, newOffer.getPrice()); + assertEquals("0.00500000", newOffer.getPrice()); assertEquals(100_000_000L, newOffer.getAmount()); assertEquals(50_000_000L, newOffer.getMinAmount()); assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); @@ -156,16 +155,16 @@ public void testCreateFixedPriceSell1BTCFor200KXMROffer() { public void testCreatePriceMarginBasedBuy1BTCOfferWithTriggerPrice() { double priceMarginPctInput = 1.00; double mktPriceAsDouble = aliceClient.getBtcPrice(XMR); - long triggerPriceAsLong = calcAltcoinTriggerPriceAsLong.apply(mktPriceAsDouble, -0.001); + String triggerPrice = calcPriceAsString(mktPriceAsDouble, Double.parseDouble("-0.001"), 8); var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(), XMR, 100_000_000L, 75_000_000L, priceMarginPctInput, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesXmrAcct.getId(), MAKER_FEE_CURRENCY_CODE, - triggerPriceAsLong); + triggerPrice); log.debug("Pending Sell XMR (Buy BTC) offer:\n{}", toOfferTable.apply(newOffer)); assertTrue(newOffer.getIsMyOffer()); assertTrue(newOffer.getIsMyPendingOffer()); @@ -176,7 +175,7 @@ public void testCreatePriceMarginBasedBuy1BTCOfferWithTriggerPrice() { assertTrue(newOffer.getUseMarketBasedPrice()); // There is no trigger price while offer is pending. - assertEquals(0, newOffer.getTriggerPrice()); + assertEquals(NO_TRIGGER_PRICE, newOffer.getTriggerPrice()); assertEquals(100_000_000L, newOffer.getAmount()); assertEquals(75_000_000L, newOffer.getMinAmount()); @@ -197,7 +196,7 @@ public void testCreatePriceMarginBasedBuy1BTCOfferWithTriggerPrice() { assertTrue(newOffer.getUseMarketBasedPrice()); // The trigger price should exist on the prepared offer. - assertEquals(triggerPriceAsLong, newOffer.getTriggerPrice()); + assertEquals(triggerPrice, newOffer.getTriggerPrice()); assertEquals(100_000_000L, newOffer.getAmount()); assertEquals(75_000_000L, newOffer.getMinAmount()); @@ -218,7 +217,7 @@ public void testCreatePriceMarginBasedSell1BTCOffer() { 100_000_000L, 50_000_000L, priceMarginPctInput, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesXmrAcct.getId(), MAKER_FEE_CURRENCY_CODE, NO_TRIGGER_PRICE); diff --git a/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java index 33944dece8d..4e839924019 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java @@ -38,7 +38,6 @@ import org.junit.jupiter.api.TestMethodOrder; import static bisq.apitest.config.ApiTestConfig.*; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static bisq.proto.grpc.EditOfferRequest.EditType.*; import static java.lang.String.format; import static org.junit.jupiter.api.Assertions.*; @@ -67,7 +66,7 @@ public void testOfferDisableAndEnable() { NO_TRIGGER_PRICE); log.debug("Original EUR offer:\n{}", toOfferTable.apply(originalOffer)); assertFalse(originalOffer.getIsActivated()); // Not activated until prep is done. - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. originalOffer = aliceClient.getOffer(originalOffer.getId()); assertTrue(originalOffer.getIsActivated()); // Disable offer @@ -98,20 +97,20 @@ public void testEditTriggerPrice() { 0.0, NO_TRIGGER_PRICE); log.debug("Original EUR offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. originalOffer = aliceClient.getOffer(originalOffer.getId()); - assertEquals(0 /*no trigger price*/, originalOffer.getTriggerPrice()); + assertEquals(NO_TRIGGER_PRICE, originalOffer.getTriggerPrice()); // Edit the offer's trigger price, nothing else. var mktPrice = aliceClient.getBtcPrice(EUR); var delta = 5_000.00; - var newTriggerPriceAsLong = calcFiatTriggerPriceAsLong.apply(mktPrice, delta); + var newTriggerPrice = calcPriceAsString(mktPrice, delta, 4); - aliceClient.editOfferTriggerPrice(originalOffer.getId(), newTriggerPriceAsLong); + aliceClient.editOfferTriggerPrice(originalOffer.getId(), newTriggerPrice); sleep(2_500); // Wait for offer book re-entry. OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited EUR offer:\n{}", toOfferTable.apply(editedOffer)); - assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice()); + assertEquals(newTriggerPrice, editedOffer.getTriggerPrice()); assertTrue(editedOffer.getUseMarketBasedPrice()); doSanityCheck(originalOffer, editedOffer); @@ -127,10 +126,10 @@ public void testSetTriggerPriceToNegativeValueShouldThrowException() { 0.0, NO_TRIGGER_PRICE); log.debug("Original EUR offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. // Edit the offer's trigger price, set to -1, check error. Throwable exception = assertThrows(StatusRuntimeException.class, () -> - aliceClient.editOfferTriggerPrice(originalOffer.getId(), -1L)); + aliceClient.editOfferTriggerPrice(originalOffer.getId(), "-1")); String expectedExceptionMessage = format("UNKNOWN: programmer error: cannot set trigger price to a negative value in offer with id '%s'", originalOffer.getId()); @@ -141,21 +140,21 @@ public void testSetTriggerPriceToNegativeValueShouldThrowException() { @Order(4) public void testEditMktPriceMargin() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("US"); - var originalMktPriceMargin = new BigDecimal("0.1").doubleValue(); + var originalMktPriceMarginPct = 0.1d; // 0.1% var originalOffer = createMktPricedOfferForEdit(SELL.name(), USD, paymentAcct.getId(), - originalMktPriceMargin, + originalMktPriceMarginPct, NO_TRIGGER_PRICE); log.debug("Original USD offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. - assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin()); + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. + assertEquals(originalMktPriceMarginPct, originalOffer.getMarketPriceMarginPct()); // Edit the offer's price margin, nothing else. - var newMktPriceMargin = new BigDecimal("0.5").doubleValue(); + var newMktPriceMargin = 0.5d; // 0.5% aliceClient.editOfferPriceMargin(originalOffer.getId(), newMktPriceMargin); OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited USD offer:\n{}", toOfferTable.apply(editedOffer)); - assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin()); + assertEquals(newMktPriceMargin, editedOffer.getMarketPriceMarginPct()); assertTrue(editedOffer.getUseMarketBasedPrice()); doSanityCheck(originalOffer, editedOffer); @@ -166,22 +165,21 @@ public void testEditMktPriceMargin() { public void testEditFixedPrice() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU"); double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE); - String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 200_000.0000); + String fixedPriceAsString = calcPriceAsString(mktPriceAsDouble, 200_000.0000, 4); var originalOffer = createFixedPricedOfferForEdit(BUY.name(), RUBLE, paymentAcct.getId(), fixedPriceAsString); log.debug("Original RUB offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. // Edit the offer's fixed price, nothing else. - String editedFixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 100_000.0000); + String editedFixedPriceAsString = calcPriceAsString(mktPriceAsDouble, 100_000.0000, 4); aliceClient.editOfferFixedPrice(originalOffer.getId(), editedFixedPriceAsString); // Wait for edited offer to be removed from offer-book, edited, and re-published. genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited RUB offer:\n{}", toOfferTable.apply(editedOffer)); - var expectedNewFixedPrice = scaledUpFiatOfferPrice.apply(new BigDecimal(editedFixedPriceAsString)); - assertEquals(expectedNewFixedPrice, editedOffer.getPrice()); + assertEquals(editedFixedPriceAsString, editedOffer.getPrice()); assertFalse(editedOffer.getUseMarketBasedPrice()); doSanityCheck(originalOffer, editedOffer); @@ -192,15 +190,15 @@ public void testEditFixedPrice() { public void testEditFixedPriceAndDeactivation() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU"); double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE); - String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 200_000.0000); + String fixedPriceAsString = calcPriceAsString(mktPriceAsDouble, 200_000.0000, 4); var originalOffer = createFixedPricedOfferForEdit(BUY.name(), RUBLE, paymentAcct.getId(), fixedPriceAsString); log.debug("Original RUB offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. // Edit the offer's fixed price and deactivate it. - String editedFixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 100_000.0000); + String editedFixedPriceAsString = calcPriceAsString(mktPriceAsDouble, 100_000.0000, 4); aliceClient.editOffer(originalOffer.getId(), editedFixedPriceAsString, originalOffer.getUseMarketBasedPrice(), @@ -212,8 +210,7 @@ public void testEditFixedPriceAndDeactivation() { genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited RUB offer:\n{}", toOfferTable.apply(editedOffer)); - var expectedNewFixedPrice = scaledUpFiatOfferPrice.apply(new BigDecimal(editedFixedPriceAsString)); - assertEquals(expectedNewFixedPrice, editedOffer.getPrice()); + assertEquals(editedFixedPriceAsString, editedOffer.getPrice()); assertFalse(editedOffer.getIsActivated()); assertFalse(editedOffer.getUseMarketBasedPrice()); @@ -224,33 +221,32 @@ public void testEditFixedPriceAndDeactivation() { @Order(7) public void testEditMktPriceMarginAndDeactivation() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("US"); - - var originalMktPriceMargin = new BigDecimal("0.0").doubleValue(); + var originalMktPriceMarginPct = 0.00d; OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(), USD, paymentAcct.getId(), - originalMktPriceMargin, - 0); + originalMktPriceMarginPct, + NO_TRIGGER_PRICE); log.debug("Original USD offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. originalOffer = aliceClient.getOffer(originalOffer.getId()); - assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin()); + assertEquals(originalMktPriceMarginPct, originalOffer.getMarketPriceMarginPct()); // Edit the offer's price margin and trigger price, and deactivate it. - var newMktPriceMargin = new BigDecimal("1.50").doubleValue(); + var newMktPriceMarginPct = 1.50d; // 1.5% aliceClient.editOffer(originalOffer.getId(), "0.00", originalOffer.getUseMarketBasedPrice(), - newMktPriceMargin, - 0, + newMktPriceMarginPct, + NO_TRIGGER_PRICE, DEACTIVATE_OFFER, MKT_PRICE_MARGIN_AND_ACTIVATION_STATE); // Wait for edited offer to be removed from offer-book, edited, and re-published. genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited USD offer:\n{}", toOfferTable.apply(editedOffer)); - assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin()); - assertEquals(0, editedOffer.getTriggerPrice()); + assertEquals(newMktPriceMarginPct, editedOffer.getMarketPriceMarginPct()); + assertEquals(NO_TRIGGER_PRICE, editedOffer.getTriggerPrice()); assertFalse(editedOffer.getIsActivated()); assertTrue(editedOffer.getUseMarketBasedPrice()); @@ -261,36 +257,36 @@ public void testEditMktPriceMarginAndDeactivation() { @Order(8) public void testEditMktPriceMarginAndTriggerPriceAndDeactivation() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("US"); - var originalMktPriceMargin = new BigDecimal("0.0").doubleValue(); + var originalMktPriceMarginPct = 0.00d; var mktPriceAsDouble = aliceClient.getBtcPrice(USD); - var originalTriggerPriceAsLong = calcFiatTriggerPriceAsLong.apply(mktPriceAsDouble, -5_000.0000); + var originalTriggerPrice = calcPriceAsString(mktPriceAsDouble, -5_000.0000, 4); OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(), USD, paymentAcct.getId(), - originalMktPriceMargin, - originalTriggerPriceAsLong); + originalMktPriceMarginPct, + originalTriggerPrice); log.debug("Original USD offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. originalOffer = aliceClient.getOffer(originalOffer.getId()); - assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin()); - assertEquals(originalTriggerPriceAsLong, originalOffer.getTriggerPrice()); + assertEquals(originalMktPriceMarginPct, originalOffer.getMarketPriceMarginPct()); + assertEquals(originalTriggerPrice, originalOffer.getTriggerPrice()); // Edit the offer's price margin and trigger price, and deactivate it. - var newMktPriceMargin = new BigDecimal("0.1").doubleValue(); - var newTriggerPriceAsLong = calcFiatTriggerPriceAsLong.apply(mktPriceAsDouble, -2_000.0000); + var newMktPriceMarginPct = 0.10d; // 0.1% + var newTriggerPrice = calcPriceAsString(mktPriceAsDouble, -2_000.0000, 4); aliceClient.editOffer(originalOffer.getId(), "0.00", originalOffer.getUseMarketBasedPrice(), - newMktPriceMargin, - newTriggerPriceAsLong, + newMktPriceMarginPct, + newTriggerPrice, DEACTIVATE_OFFER, MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE); // Wait for edited offer to be removed from offer-book, edited, and re-published. genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited USD offer:\n{}", toOfferTable.apply(editedOffer)); - assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin()); - assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice()); + assertEquals(newMktPriceMarginPct, editedOffer.getMarketPriceMarginPct()); + assertEquals(newTriggerPrice, editedOffer.getTriggerPrice()); assertFalse(editedOffer.getIsActivated()); doSanityCheck(originalOffer, editedOffer); @@ -300,22 +296,22 @@ public void testEditMktPriceMarginAndTriggerPriceAndDeactivation() { @Order(9) public void testEditingFixedPriceInMktPriceMarginBasedOfferShouldThrowException() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("US"); - var originalMktPriceMargin = new BigDecimal("0.0").doubleValue(); + var originalMktPriceMarginPct = 0.00d; var originalOffer = createMktPricedOfferForEdit(SELL.name(), USD, paymentAcct.getId(), - originalMktPriceMargin, + originalMktPriceMarginPct, NO_TRIGGER_PRICE); log.debug("Original USD offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. // Try to edit both the fixed price and mkt price margin. - var newMktPriceMargin = new BigDecimal("0.25").doubleValue(); + var newMktPriceMarginPct = 0.25d; // 0.25% var newFixedPrice = "50000.0000"; Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.editOffer(originalOffer.getId(), newFixedPrice, originalOffer.getUseMarketBasedPrice(), - newMktPriceMargin, + newMktPriceMarginPct, NO_TRIGGER_PRICE, ACTIVATE_OFFER, MKT_PRICE_MARGIN_ONLY)); @@ -332,14 +328,14 @@ public void testEditingFixedPriceInMktPriceMarginBasedOfferShouldThrowException( public void testEditingTriggerPriceInFixedPriceOfferShouldThrowException() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU"); double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE); - String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 200_000.0000); + String fixedPriceAsString = calcPriceAsString(mktPriceAsDouble, 200_000.0000, 4); var originalOffer = createFixedPricedOfferForEdit(BUY.name(), RUBLE, paymentAcct.getId(), fixedPriceAsString); log.debug("Original RUB offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. - long newTriggerPrice = 1000000L; + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. + var newTriggerPrice = "1000000"; Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.editOfferTriggerPrice(originalOffer.getId(), newTriggerPrice)); String expectedExceptionMessage = @@ -354,23 +350,23 @@ public void testEditingTriggerPriceInFixedPriceOfferShouldThrowException() { public void testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("MX"); double mktPriceAsDouble = aliceClient.getBtcPrice("MXN"); - String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 0.00); + String fixedPriceAsString = calcPriceAsString(mktPriceAsDouble, 0.00, 4); var originalOffer = createFixedPricedOfferForEdit(BUY.name(), "MXN", paymentAcct.getId(), fixedPriceAsString); log.debug("Original MXN offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. // Change the offer to mkt price based and set a trigger price. - var newMktPriceMargin = new BigDecimal("0.05").doubleValue(); + var newMktPriceMarginPct = 0.05d; // 0.05% var delta = 200_000.0000; // trigger price on buy offer is 200K above mkt price - var newTriggerPriceAsLong = calcFiatTriggerPriceAsLong.apply(mktPriceAsDouble, delta); + var newTriggerPrice = calcPriceAsString(mktPriceAsDouble, delta, 4); aliceClient.editOffer(originalOffer.getId(), "0.00", true, - newMktPriceMargin, - newTriggerPriceAsLong, + newMktPriceMarginPct, + newTriggerPrice, ACTIVATE_OFFER, MKT_PRICE_MARGIN_AND_TRIGGER_PRICE); // Wait for edited offer to be removed from offer-book, edited, and re-published. @@ -378,8 +374,8 @@ public void testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice() { OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited MXN offer:\n{}", toOfferTable.apply(editedOffer)); assertTrue(editedOffer.getUseMarketBasedPrice()); - assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin()); - assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice()); + assertEquals(newMktPriceMarginPct, editedOffer.getMarketPriceMarginPct()); + assertEquals(newTriggerPrice, editedOffer.getTriggerPrice()); assertTrue(editedOffer.getIsActivated()); doSanityCheck(originalOffer, editedOffer); @@ -390,33 +386,33 @@ public void testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice() { public void testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("GB"); double mktPriceAsDouble = aliceClient.getBtcPrice("GBP"); - var originalMktPriceMargin = new BigDecimal("0.25").doubleValue(); + var originalMktPriceMarginPct = new BigDecimal("0.25").doubleValue(); var delta = 1_000.0000; // trigger price on sell offer is 1K below mkt price - var originalTriggerPriceAsLong = calcFiatTriggerPriceAsLong.apply(mktPriceAsDouble, delta); + var originalTriggerPriceAsLong = calcPriceAsString(mktPriceAsDouble, delta, 4); var originalOffer = createMktPricedOfferForEdit(SELL.name(), "GBP", paymentAcct.getId(), - originalMktPriceMargin, + originalMktPriceMarginPct, originalTriggerPriceAsLong); log.debug("Original GBP offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. - String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 0.00); + String fixedPriceAsString = calcPriceAsString(mktPriceAsDouble, 0.00, 4); aliceClient.editOffer(originalOffer.getId(), fixedPriceAsString, false, 0.00, - 0, + NO_TRIGGER_PRICE, DEACTIVATE_OFFER, FIXED_PRICE_AND_ACTIVATION_STATE); // Wait for edited offer to be removed from offer-book, edited, and re-published. genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited GBP offer:\n{}", toOfferTable.apply(editedOffer)); - assertEquals(scaledUpFiatOfferPrice.apply(new BigDecimal(fixedPriceAsString)), editedOffer.getPrice()); + assertEquals(fixedPriceAsString, editedOffer.getPrice()); assertFalse(editedOffer.getUseMarketBasedPrice()); - assertEquals(0.00, editedOffer.getMarketPriceMargin()); - assertEquals(0, editedOffer.getTriggerPrice()); + assertEquals(0.00, editedOffer.getMarketPriceMarginPct()); + assertEquals(NO_TRIGGER_PRICE, editedOffer.getTriggerPrice()); assertFalse(editedOffer.getIsActivated()); } @@ -428,17 +424,17 @@ public void testChangeFixedPricedBsqOfferToPriceMarginBasedOfferShouldThrowExcep 100_000_000L, 100_000_000L, "0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesLegacyBsqAcct.getId(), BSQ); log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.editOffer(originalOffer.getId(), "0.00", true, 0.1, - 0, + NO_TRIGGER_PRICE, ACTIVATE_OFFER, MKT_PRICE_MARGIN_ONLY)); String expectedExceptionMessage = format("UNKNOWN: cannot set mkt price margin or" @@ -455,12 +451,12 @@ public void testEditTriggerPriceOnFixedPriceBsqOfferShouldThrowException() { 100_000_000L, 100_000_000L, "0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesLegacyBsqAcct.getId(), BSQ); log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. - var newTriggerPriceAsLong = calcFiatTriggerPriceAsLong.apply(0.00005, 0.00); + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. + var newTriggerPriceAsLong = calcPriceAsString(0.00005, 0.00, 8); Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.editOffer(originalOffer.getId(), "0.00", @@ -484,49 +480,49 @@ public void testEditFixedPriceOnBsqOffer() { 100_000_000L, 100_000_000L, fixedPriceAsString, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesLegacyBsqAcct.getId(), BSQ); log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. String newFixedPriceAsString = "0.00003111"; aliceClient.editOffer(originalOffer.getId(), newFixedPriceAsString, false, 0.0, - 0, + NO_TRIGGER_PRICE, ACTIVATE_OFFER, FIXED_PRICE_ONLY); // Wait for edited offer to be edited and removed from offer-book. genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited BSQ offer:\n{}", toOfferTable.apply(editedOffer)); - assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice()); + assertEquals(newFixedPriceAsString, editedOffer.getPrice()); assertTrue(editedOffer.getIsActivated()); assertFalse(editedOffer.getUseMarketBasedPrice()); - assertEquals(0.00, editedOffer.getMarketPriceMargin()); - assertEquals(0, editedOffer.getTriggerPrice()); + assertEquals(0.00, editedOffer.getMarketPriceMarginPct()); + assertEquals(NO_TRIGGER_PRICE, editedOffer.getTriggerPrice()); } @Test @Order(16) public void testDisableBsqOffer() { - String fixedPriceAsString = "0.00005"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ + String fixedPriceAsString = "0.00005000"; var originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), BSQ, 100_000_000L, 100_000_000L, fixedPriceAsString, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesLegacyBsqAcct.getId(), BSQ); log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. aliceClient.editOffer(originalOffer.getId(), fixedPriceAsString, false, 0.0, - 0, + NO_TRIGGER_PRICE, DEACTIVATE_OFFER, ACTIVATION_STATE_ONLY); // Wait for edited offer to be removed from offer-book. @@ -534,32 +530,32 @@ public void testDisableBsqOffer() { OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited BSQ offer:\n{}", toOfferTable.apply(editedOffer)); assertFalse(editedOffer.getIsActivated()); - assertEquals(scaledUpAltcoinOfferPrice.apply(fixedPriceAsString), editedOffer.getPrice()); + assertEquals(fixedPriceAsString, editedOffer.getPrice()); assertFalse(editedOffer.getUseMarketBasedPrice()); - assertEquals(0.00, editedOffer.getMarketPriceMargin()); - assertEquals(0, editedOffer.getTriggerPrice()); + assertEquals(0.00, editedOffer.getMarketPriceMarginPct()); + assertEquals(NO_TRIGGER_PRICE, editedOffer.getTriggerPrice()); } @Test @Order(17) public void testEditFixedPriceAndDisableBsqOffer() { - String fixedPriceAsString = "0.00005"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ + String fixedPriceAsString = "0.00005000"; var originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), BSQ, 100_000_000L, 100_000_000L, fixedPriceAsString, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesLegacyBsqAcct.getId(), BSQ); log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. - String newFixedPriceAsString = "0.000045"; + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. + String newFixedPriceAsString = "0.00004500"; aliceClient.editOffer(originalOffer.getId(), newFixedPriceAsString, false, 0.0, - 0, + NO_TRIGGER_PRICE, DEACTIVATE_OFFER, FIXED_PRICE_AND_ACTIVATION_STATE); // Wait for edited offer to be edited and removed from offer-book. @@ -567,10 +563,10 @@ public void testEditFixedPriceAndDisableBsqOffer() { OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited BSQ offer:\n{}", toOfferTable.apply(editedOffer)); assertFalse(editedOffer.getIsActivated()); - assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice()); + assertEquals(newFixedPriceAsString, editedOffer.getPrice()); assertFalse(editedOffer.getUseMarketBasedPrice()); - assertEquals(0.00, editedOffer.getMarketPriceMargin()); - assertEquals(0, editedOffer.getTriggerPrice()); + assertEquals(0.00, editedOffer.getMarketPriceMarginPct()); + assertEquals(NO_TRIGGER_PRICE, editedOffer.getTriggerPrice()); } // Edit XMR Offers @@ -580,34 +576,34 @@ public void testEditFixedPriceAndDisableBsqOffer() { public void testChangePriceMarginBasedXmrOfferWithTriggerPriceToFixedPricedAndDeactivateIt() { createXmrPaymentAccounts(); double mktPriceAsDouble = aliceClient.getBtcPrice(XMR); - long triggerPriceAsLong = calcAltcoinTriggerPriceAsLong.apply(mktPriceAsDouble, 0.001); + String triggerPrice = calcPriceAsString(mktPriceAsDouble, 0.001, 8); OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(), XMR, alicesXmrAcct.getId(), 0.0, - triggerPriceAsLong); + triggerPrice); log.debug("Pending XMR offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2500); // Wait for entry into offer book. originalOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Original XMR offer:\n{}", toOfferTable.apply(originalOffer)); - String newFixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, -0.001); + String newFixedPriceAsString = calcPriceAsString(mktPriceAsDouble, -0.001, 8); aliceClient.editOffer(originalOffer.getId(), newFixedPriceAsString, false, 0.00, - 0, + NO_TRIGGER_PRICE, DEACTIVATE_OFFER, FIXED_PRICE_AND_ACTIVATION_STATE); // Wait for edited offer to be removed from offer-book, edited & not re-published. genBtcBlocksThenWait(1, 2500); OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited XMR offer:\n{}", toOfferTable.apply(editedOffer)); - assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice()); + assertEquals(newFixedPriceAsString, editedOffer.getPrice()); assertFalse(editedOffer.getUseMarketBasedPrice()); - assertEquals(0.00, editedOffer.getMarketPriceMargin()); - assertEquals(0, editedOffer.getTriggerPrice()); + assertEquals(0.00, editedOffer.getMarketPriceMarginPct()); + assertEquals(NO_TRIGGER_PRICE, editedOffer.getTriggerPrice()); assertFalse(editedOffer.getIsActivated()); doSanityCheck(originalOffer, editedOffer); @@ -617,25 +613,25 @@ public void testChangePriceMarginBasedXmrOfferWithTriggerPriceToFixedPricedAndDe @Order(19) public void testEditTriggerPriceOnPriceMarginBasedXmrOffer() { createXmrPaymentAccounts(); - double mktPriceMargin = -0.075d; + double mktPriceMarginPct = -0.075d; OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(), XMR, alicesXmrAcct.getId(), - mktPriceMargin, + mktPriceMarginPct, NO_TRIGGER_PRICE); log.debug("Pending XMR offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2500); // Wait for entry into offer book. originalOffer = aliceClient.getOffer(originalOffer.getId()); log.info("Original XMR offer:\n{}", toOfferTable.apply(originalOffer)); double mktPriceAsDouble = aliceClient.getBtcPrice(XMR); - long triggerPriceAsLong = calcAltcoinTriggerPriceAsLong.apply(mktPriceAsDouble, 0.001); + String triggerPrice = calcPriceAsString(mktPriceAsDouble, 0.001, 8); aliceClient.editOffer(originalOffer.getId(), "0", true, - mktPriceMargin, - triggerPriceAsLong, + mktPriceMarginPct, + triggerPrice, ACTIVATE_OFFER, TRIGGER_PRICE_ONLY); // Wait for edited offer to be removed from offer-book, edited & not re-published. @@ -643,8 +639,8 @@ public void testEditTriggerPriceOnPriceMarginBasedXmrOffer() { OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.info("Edited XMR offer:\n{}", toOfferTable.apply(editedOffer)); assertTrue(editedOffer.getUseMarketBasedPrice()); - assertEquals(scaledDownMktPriceMargin.apply(mktPriceMargin), editedOffer.getMarketPriceMargin()); - assertEquals(triggerPriceAsLong, editedOffer.getTriggerPrice()); + assertEquals(mktPriceMarginPct, editedOffer.getMarketPriceMarginPct()); + assertEquals(triggerPrice, editedOffer.getTriggerPrice()); assertTrue(editedOffer.getIsActivated()); doSanityCheck(originalOffer, editedOffer); @@ -655,30 +651,30 @@ public void testEditTriggerPriceOnPriceMarginBasedXmrOffer() { public void testChangeFixedPricedXmrOfferToPriceMarginBasedOfferWithTriggerPrice() { createXmrPaymentAccounts(); double mktPriceAsDouble = aliceClient.getBtcPrice(XMR); - String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 0.00); + String fixedPriceAsString = calcPriceAsString(mktPriceAsDouble, 0.00, 8); OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), XMR, 100_000_000L, 50_000_000L, fixedPriceAsString, // FIXED PRICE IN BTC (satoshis) FOR 1 XMR - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesXmrAcct.getId(), BSQ); log.debug("Pending XMR offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2500); // Wait for entry into offer book. originalOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Original XMR offer:\n{}", toOfferTable.apply(originalOffer)); // Change the offer to mkt price based and set a trigger price. - var newMktPriceMargin = new BigDecimal("0.05").doubleValue(); + var newMktPriceMarginPct = 0.05d; // 0.05% var delta = -0.00100000; - var newTriggerPriceAsLong = calcAltcoinTriggerPriceAsLong.apply(mktPriceAsDouble, delta); + var newTriggerPrice = calcPriceAsString(mktPriceAsDouble, delta, 8); aliceClient.editOffer(originalOffer.getId(), "0.00", true, - newMktPriceMargin, - newTriggerPriceAsLong, + newMktPriceMarginPct, + newTriggerPrice, ACTIVATE_OFFER, MKT_PRICE_MARGIN_AND_TRIGGER_PRICE); // Wait for edited offer to be removed from offer-book, edited, and re-published. @@ -686,8 +682,8 @@ public void testChangeFixedPricedXmrOfferToPriceMarginBasedOfferWithTriggerPrice OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited XMR offer:\n{}", toOfferTable.apply(editedOffer)); assertTrue(editedOffer.getUseMarketBasedPrice()); - assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin()); - assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice()); + assertEquals(newMktPriceMarginPct, editedOffer.getMarketPriceMarginPct()); + assertEquals(newTriggerPrice, editedOffer.getTriggerPrice()); assertTrue(editedOffer.getIsActivated()); doSanityCheck(originalOffer, editedOffer); @@ -702,19 +698,19 @@ public void testEditTriggerPriceOnFixedPriceXmrOfferShouldThrowException() { 100_000_000L, 25_000_000L, "0.007", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesXmrAcct.getId(), BSQ); log.debug("Original XMR offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2500); // Wait for entry into offer book. - var newTriggerPriceAsLong = calcAltcoinTriggerPriceAsLong.apply(0.007, 0.001); + var newTriggerPrice = calcPriceAsString(0.007, 0.001, 8); Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.editOffer(originalOffer.getId(), "0.00", false, 0.1, - newTriggerPriceAsLong, + newTriggerPrice, ACTIVATE_OFFER, TRIGGER_PRICE_ONLY)); String expectedExceptionMessage = format("UNKNOWN: programmer error: cannot set a trigger price" @@ -733,24 +729,24 @@ public void testEditFixedPriceOnXmrOffer() { 100_000_000L, 100_000_000L, fixedPriceAsString, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesXmrAcct.getId(), BSQ); log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. - String newFixedPriceAsString = "0.009"; + genBtcBlocksThenWait(1, 2500); // Wait for entry into offer book. + String newFixedPrice = "0.00900000"; aliceClient.editOffer(originalOffer.getId(), - newFixedPriceAsString, + newFixedPrice, false, 0.0, - 0, + NO_TRIGGER_PRICE, ACTIVATE_OFFER, FIXED_PRICE_ONLY); // Wait for edited offer to be edited and removed from offer-book. genBtcBlocksThenWait(1, 2500); OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited XMR offer:\n{}", toOfferTable.apply(editedOffer)); - assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice()); + assertEquals(newFixedPrice, editedOffer.getPrice()); assertTrue(editedOffer.getIsActivated()); assertMarketBasedPriceFieldsAreIgnored(editedOffer); @@ -761,22 +757,22 @@ public void testEditFixedPriceOnXmrOffer() { @Order(23) public void testDisableXmrOffer() { createXmrPaymentAccounts(); - String fixedPriceAsString = "0.008"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ + String fixedPriceAsString = "0.00800000"; final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), XMR, 100_000_000L, 50_000_000L, fixedPriceAsString, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesXmrAcct.getId(), BSQ); log.debug("Original XMR offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2500); // Wait for entry into offer book. aliceClient.editOffer(originalOffer.getId(), fixedPriceAsString, false, 0.0, - 0, + NO_TRIGGER_PRICE, DEACTIVATE_OFFER, ACTIVATION_STATE_ONLY); // Wait for edited offer to be removed from offer-book. @@ -784,7 +780,7 @@ public void testDisableXmrOffer() { OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited XMR offer:\n{}", toOfferTable.apply(editedOffer)); assertFalse(editedOffer.getIsActivated()); - assertEquals(scaledUpAltcoinOfferPrice.apply(fixedPriceAsString), editedOffer.getPrice()); + assertEquals(fixedPriceAsString, editedOffer.getPrice()); assertMarketBasedPriceFieldsAreIgnored(editedOffer); doSanityCheck(originalOffer, editedOffer); @@ -794,23 +790,23 @@ public void testDisableXmrOffer() { @Order(24) public void testEditFixedPriceAndDisableXmrOffer() { createXmrPaymentAccounts(); - String fixedPriceAsString = "0.004"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ + String fixedPriceAsString = "0.004"; final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), XMR, 100_000_000L, 100_000_000L, fixedPriceAsString, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesXmrAcct.getId(), BSQ); log.debug("Original XMR offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. - String newFixedPriceAsString = "0.000045"; + genBtcBlocksThenWait(1, 2500); // Wait for entry into offer book. + String newFixedPriceAsString = "0.00004500"; aliceClient.editOffer(originalOffer.getId(), newFixedPriceAsString, false, 0.0, - 0, + NO_TRIGGER_PRICE, DEACTIVATE_OFFER, FIXED_PRICE_AND_ACTIVATION_STATE); // Wait for edited offer to be edited and removed from offer-book. @@ -818,7 +814,7 @@ public void testEditFixedPriceAndDisableXmrOffer() { OfferInfo editedOffer = aliceClient.getOffer(originalOffer.getId()); log.debug("Edited XMR offer:\n{}", toOfferTable.apply(editedOffer)); assertFalse(editedOffer.getIsActivated()); - assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice()); + assertEquals(newFixedPriceAsString, editedOffer.getPrice()); assertMarketBasedPriceFieldsAreIgnored(editedOffer); doSanityCheck(originalOffer, editedOffer); @@ -833,25 +829,25 @@ public void testEditBsqSwapOfferShouldThrowException() { 1_250_000L, 750_000L, "0.00005"); - log.debug("BsqSwap Buy BSQ (Buy BTC) offer:\n{}", originalOffer); + log.debug("BsqSwap BUY BSQ (SELL BTC) Offer:\n{}", toOfferTable.apply(originalOffer)); var newOfferId = originalOffer.getId(); assertNotEquals("", newOfferId); assertEquals(SELL.name(), originalOffer.getDirection()); - assertEquals(5_000, originalOffer.getPrice()); + assertEquals("0.00005000", originalOffer.getPrice()); assertEquals(1_250_000L, originalOffer.getAmount()); assertEquals(750_000L, originalOffer.getMinAmount()); assertEquals(BSQ, originalOffer.getBaseCurrencyCode()); assertEquals(BTC, originalOffer.getCounterCurrencyCode()); log.debug("Original BsqSwap offer:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for entry into offer book. var newFixedPrice = "0.000055"; Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.editOffer(originalOffer.getId(), newFixedPrice, false, 0.0, - 0, + NO_TRIGGER_PRICE, ACTIVATE_OFFER, TRIGGER_PRICE_ONLY)); String expectedExceptionMessage = format("UNKNOWN: cannot edit bsq swap offer with id '%s'", @@ -862,14 +858,14 @@ public void testEditBsqSwapOfferShouldThrowException() { private OfferInfo createMktPricedOfferForEdit(String direction, String currencyCode, String paymentAccountId, - double marketPriceMargin, - long triggerPrice) { + double marketPriceMarginPct, + String triggerPrice) { return aliceClient.createMarketBasedPricedOffer(direction, currencyCode, AMOUNT, AMOUNT, - marketPriceMargin, - getDefaultBuyerSecurityDepositAsPercent(), + marketPriceMarginPct, + defaultBuyerSecurityDepositPct.get(), paymentAccountId, BSQ, triggerPrice); @@ -884,7 +880,7 @@ private OfferInfo createFixedPricedOfferForEdit(String direction, AMOUNT, AMOUNT, priceAsString, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), paymentAccountId, BSQ); } @@ -906,8 +902,8 @@ private void doSanityCheck(OfferInfo originalOffer, OfferInfo editedOffer) { private void assertMarketBasedPriceFieldsAreIgnored(OfferInfo editedOffer) { assertFalse(editedOffer.getUseMarketBasedPrice()); - assertEquals(0.00, editedOffer.getMarketPriceMargin()); - assertEquals(0, editedOffer.getTriggerPrice()); + assertEquals(0.00, editedOffer.getMarketPriceMarginPct()); + assertEquals(NO_TRIGGER_PRICE, editedOffer.getTriggerPrice()); } private PaymentAccount getOrCreatePaymentAccount(String countryCode) { diff --git a/apitest/src/test/java/bisq/apitest/method/offer/ValidateCreateOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/ValidateCreateOfferTest.java index cc8a02f33d9..48a8ccbbbb5 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/ValidateCreateOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/ValidateCreateOfferTest.java @@ -31,7 +31,6 @@ import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.apitest.config.ApiTestConfig.BTC; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static java.lang.String.format; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -53,7 +52,7 @@ public void testAmtTooLargeShouldThrowException() { 100000000000L, // exceeds amount limit 100000000000L, "10000.0000", - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), usdAccount.getId(), BSQ)); assertEquals("UNKNOWN: An error occurred at task: ValidateOffer", exception.getMessage()); @@ -70,7 +69,7 @@ public void testNoMatchingEURPaymentAccountShouldThrowException() { 10000000L, 10000000L, "40000.0000", - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), chfAccount.getId(), BTC)); String expectedError = format("UNKNOWN: cannot create EUR offer with payment account %s", chfAccount.getId()); @@ -88,7 +87,7 @@ public void testNoMatchingCADPaymentAccountShouldThrowException() { 10000000L, 10000000L, "63000.0000", - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), audAccount.getId(), BTC)); String expectedError = format("UNKNOWN: cannot create CAD offer with payment account %s", audAccount.getId()); diff --git a/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java b/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java index 5ceebe026a2..b4bc79ab53f 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java @@ -14,7 +14,6 @@ import org.junit.jupiter.api.TestInfo; import static bisq.apitest.config.ApiTestConfig.BTC; -import static bisq.cli.CurrencyFormat.formatBsqAmount; import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL; import static bisq.core.trade.model.bisq_v1.Trade.Phase.DEPOSIT_CONFIRMED; import static bisq.core.trade.model.bisq_v1.Trade.Phase.FIAT_SENT; @@ -178,7 +177,7 @@ protected final void waitForSellerSeesPaymentInitiatedMessage(Logger log, TradeInfo trade = grpcClient.getTrade(tradeId); if (!isTradeInPaymentReceiptConfirmedStateAndPhase.test(trade)) { - fail(format("INVALID_PHASE for {}'s trade %s in STATE=%s PHASE=%s, cannot confirm payment received.", + fail(format("INVALID_PHASE for %s's trade %s in STATE=%s PHASE=%s, cannot confirm payment received.", userName, trade.getShortId(), trade.getState(), @@ -208,7 +207,10 @@ protected final void sendBsqPayment(Logger log, String receiverAddress = contract.getIsBuyerMakerAndSellerTaker() ? contract.getTakerPaymentAccountPayload().getAddress() : contract.getMakerPaymentAccountPayload().getAddress(); - String sendBsqAmount = formatBsqAmount(trade.getOffer().getVolume()); + // TODO Fix trade vol src bug for subclasses. + // This bug was fixed for production CLI with https://github.com/bisq-network/bisq/pull/5704 on Sep 27, 2021 + String sendBsqAmount = trade.getOffer().getVolume(); + // String sendBsqAmount = trade.getTradeVolume(); log.debug("Sending {} BSQ to address {}", sendBsqAmount, receiverAddress); grpcClient.sendBsq(receiverAddress, sendBsqAmount, ""); } @@ -217,8 +219,10 @@ protected final void verifyBsqPaymentHasBeenReceived(Logger log, GrpcClient grpcClient, TradeInfo trade) { var contract = trade.getContract(); - var bsqSats = trade.getOffer().getVolume(); - var receiveAmountAsString = formatBsqAmount(bsqSats); + // TODO Fix trade vol src bug for subclasses. + // This bug was fixed for production with https://github.com/bisq-network/bisq/pull/5704 on Sep 27, 2021 + var receiveAmountAsString = trade.getOffer().getVolume(); + // String receiveAmountAsString = trade.getTradeVolume(); var address = contract.getIsBuyerMakerAndSellerTaker() ? contract.getTakerPaymentAccountPayload().getAddress() : contract.getMakerPaymentAccountPayload().getAddress(); diff --git a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapBuyBtcTradeTest.java b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapBuyBtcTradeTest.java index cfa330e7478..8dc20b54170 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapBuyBtcTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapBuyBtcTradeTest.java @@ -85,11 +85,11 @@ public void testAliceCreateBsqSwapBuyBtcOffer() { 1_000_000L, // 0.01 BTC 1_000_000L, "0.00005"); - log.debug("Pending BsqSwap Sell BSQ (Buy BTC) OFFER:\n{}", toOfferTable.apply(mySwapOffer)); + log.debug("Pending BsqSwap SELL BSQ (BUY BTC) Offer:\n{}", toOfferTable.apply(mySwapOffer)); var newOfferId = mySwapOffer.getId(); assertNotEquals("", newOfferId); assertEquals(OfferDirection.BUY.name(), mySwapOffer.getDirection()); - assertEquals(5_000, mySwapOffer.getPrice()); + assertEquals("0.00005000", mySwapOffer.getPrice()); assertEquals(1_000_000L, mySwapOffer.getAmount()); assertEquals(1_000_000L, mySwapOffer.getMinAmount()); assertEquals(BSQ, mySwapOffer.getBaseCurrencyCode()); @@ -98,7 +98,7 @@ public void testAliceCreateBsqSwapBuyBtcOffer() { genBtcBlocksThenWait(1, 2_500); mySwapOffer = aliceClient.getOffer(newOfferId); - log.debug("My fetched BsqSwap Sell BSQ (Buy BTC) OFFER:\n{}", toOfferTable.apply(mySwapOffer)); + log.debug("My fetched BsqSwap SELL BSQ (BUY BTC) Offer:\n{}", toOfferTable.apply(mySwapOffer)); assertNotEquals(0, mySwapOffer.getMakerFee()); runCliGetOffer(newOfferId); diff --git a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapSellBtcTradeTest.java b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapSellBtcTradeTest.java index 80cd48ec1f0..50f78a4970d 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapSellBtcTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapSellBtcTradeTest.java @@ -83,11 +83,11 @@ public void testAliceCreateBsqSwapSellBtcOffer() { 1_000_000L, // 0.01 BTC 1_000_000L, "0.00005"); - log.debug("Pending BsqSwap Buy BSQ (Sell BTC) OFFER:\n{}", toOfferTable.apply(mySwapOffer)); + log.debug("Pending BsqSwap BUY BSQ (SELL BTC) Offer:\n{}", toOfferTable.apply(mySwapOffer)); var newOfferId = mySwapOffer.getId(); assertNotEquals("", newOfferId); assertEquals(SELL.name(), mySwapOffer.getDirection()); - assertEquals(5_000, mySwapOffer.getPrice()); + assertEquals("0.00005000", mySwapOffer.getPrice()); assertEquals(1_000_000L, mySwapOffer.getAmount()); assertEquals(1_000_000L, mySwapOffer.getMinAmount()); assertEquals(BSQ, mySwapOffer.getBaseCurrencyCode()); diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBSQOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBSQOfferTest.java index 6a37a3d1217..503e5f41e30 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBSQOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBSQOfferTest.java @@ -30,7 +30,6 @@ import org.junit.jupiter.api.TestMethodOrder; import static bisq.apitest.config.ApiTestConfig.BSQ; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static bisq.core.trade.model.bisq_v1.Trade.Phase.PAYOUT_PUBLISHED; import static bisq.core.trade.model.bisq_v1.Trade.State.BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG; import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG; @@ -76,10 +75,10 @@ public void testTakeAlicesSellBTCForBSQOffer(final TestInfo testInfo) { 15_000_000L, 7_500_000L, "0.000035", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesLegacyBsqAcct.getId(), TRADE_FEE_CURRENCY_CODE); - log.debug("ALICE'S BUY BSQ (SELL BTC) OFFER:\n{}", toOfferTable.apply(alicesOffer)); + log.debug("Alice's BUY BSQ (SELL BTC) Offer:\n{}", toOfferTable.apply(alicesOffer)); genBtcBlocksThenWait(1, 5000); var offerId = alicesOffer.getId(); assertFalse(alicesOffer.getIsCurrencyForMakerFeeBtc()); diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java index 55487f740bd..9ec651b0813 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java @@ -32,7 +32,6 @@ import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.apitest.config.ApiTestConfig.USD; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static bisq.core.trade.model.bisq_v1.Trade.Phase.PAYOUT_PUBLISHED; import static bisq.core.trade.model.bisq_v1.Trade.State.BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG; import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG; @@ -62,7 +61,7 @@ public void testTakeAlicesBuyOffer(final TestInfo testInfo) { 12_500_000L, 12_500_000L, // min-amount = amount 0.00, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesUsdAccount.getId(), TRADE_FEE_CURRENCY_CODE, NO_TRIGGER_PRICE); diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java index e0e2ee746bd..0049cb54911 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferWithNationalBankAcctTest.java @@ -49,7 +49,6 @@ import org.junit.jupiter.api.TestMethodOrder; import static bisq.apitest.config.ApiTestConfig.BSQ; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static bisq.core.trade.model.bisq_v1.Trade.Phase.PAYOUT_PUBLISHED; import static bisq.core.trade.model.bisq_v1.Trade.State.BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG; import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG; @@ -94,7 +93,7 @@ public void testTakeAlicesBuyOffer(final TestInfo testInfo) { 1_000_000L, 1_000_000L, // min-amount = amount 0.00, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesPaymentAccount.getId(), TRADE_FEE_CURRENCY_CODE, NO_TRIGGER_PRICE); diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyXMROfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyXMROfferTest.java index 11f04fd890b..7ed209da5c1 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyXMROfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyXMROfferTest.java @@ -32,7 +32,6 @@ import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.apitest.config.ApiTestConfig.XMR; import static bisq.cli.table.builder.TableType.OFFER_TBL; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static bisq.core.trade.model.bisq_v1.Trade.Phase.PAYOUT_PUBLISHED; import static bisq.core.trade.model.bisq_v1.Trade.State.BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG; import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG; @@ -77,10 +76,10 @@ public void testTakeAlicesSellBTCForXMROffer(final TestInfo testInfo) { 15_000_000L, 7_500_000L, "0.00455500", // FIXED PRICE IN BTC (satoshis) FOR 1 XMR - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesXmrAcct.getId(), TRADE_FEE_CURRENCY_CODE); - log.debug("Alice's Buy XMR (Sell BTC) offer:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build()); + log.debug("Alice's BUY XMR (SELL BTC) Offer:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build()); genBtcBlocksThenWait(1, 5000); var offerId = alicesOffer.getId(); assertFalse(alicesOffer.getIsCurrencyForMakerFeeBtc()); diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBSQOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBSQOfferTest.java index f4411c51789..fcb79cee8ed 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBSQOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBSQOfferTest.java @@ -32,7 +32,6 @@ import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.apitest.config.ApiTestConfig.BTC; import static bisq.cli.table.builder.TableType.OFFER_TBL; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static bisq.core.trade.model.bisq_v1.Trade.Phase.PAYOUT_PUBLISHED; import static bisq.core.trade.model.bisq_v1.Trade.Phase.WITHDRAWN; import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG; @@ -78,10 +77,10 @@ public void testTakeAlicesBuyBTCForBSQOffer(final TestInfo testInfo) { 15_000_000L, 7_500_000L, "0.000035", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesLegacyBsqAcct.getId(), TRADE_FEE_CURRENCY_CODE); - log.debug("ALICE'S SELL BSQ (BUY BTC) OFFER:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build()); + log.debug("Alice's SELL BSQ (BUY BTC) Offer:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build()); genBtcBlocksThenWait(1, 4_000); var offerId = alicesOffer.getId(); assertTrue(alicesOffer.getIsCurrencyForMakerFeeBtc()); diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java index 9e205f5ef37..89735ef2c0e 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java @@ -32,7 +32,6 @@ import static bisq.apitest.config.ApiTestConfig.BTC; import static bisq.apitest.config.ApiTestConfig.USD; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static bisq.core.trade.model.bisq_v1.Trade.Phase.PAYOUT_PUBLISHED; import static bisq.core.trade.model.bisq_v1.Trade.Phase.WITHDRAWN; import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG; @@ -65,7 +64,7 @@ public void testTakeAlicesSellOffer(final TestInfo testInfo) { 12_500_000L, 12_500_000L, // min-amount = amount 0.00, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesUsdAccount.getId(), TRADE_FEE_CURRENCY_CODE, NO_TRIGGER_PRICE); diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellXMROfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellXMROfferTest.java index ec048da8923..6ecc572c3d9 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellXMROfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellXMROfferTest.java @@ -32,7 +32,6 @@ import static bisq.apitest.config.ApiTestConfig.BTC; import static bisq.apitest.config.ApiTestConfig.XMR; import static bisq.cli.table.builder.TableType.OFFER_TBL; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static bisq.core.trade.model.bisq_v1.Trade.Phase.PAYOUT_PUBLISHED; import static bisq.core.trade.model.bisq_v1.Trade.Phase.WITHDRAWN; import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG; @@ -80,11 +79,11 @@ public void testTakeAlicesBuyBTCForXMROffer(final TestInfo testInfo) { 20_000_000L, 10_500_000L, priceMarginPctInput, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), alicesXmrAcct.getId(), TRADE_FEE_CURRENCY_CODE, NO_TRIGGER_PRICE); - log.debug("Alice's SELL XMR (Buy BTC) offer:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build()); + log.debug("Alice's SELL XMR (BUY BTC) Offer:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build()); genBtcBlocksThenWait(1, 4000); var offerId = alicesOffer.getId(); assertTrue(alicesOffer.getIsCurrencyForMakerFeeBtc()); diff --git a/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java b/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java index 61904bfad8b..00293fd5f93 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/LongRunningOfferDeactivationTest.java @@ -31,8 +31,6 @@ import org.junit.jupiter.api.condition.EnabledIf; import static bisq.apitest.config.ApiTestConfig.BTC; -import static bisq.cli.CurrencyFormat.formatPrice; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static java.lang.System.getenv; import static org.junit.jupiter.api.Assertions.fail; import static protobuf.OfferDirection.BUY; @@ -59,25 +57,24 @@ public class LongRunningOfferDeactivationTest extends AbstractOfferTest { public void testSellOfferAutoDisable(final TestInfo testInfo) { PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, "US"); double mktPriceAsDouble = aliceClient.getBtcPrice("USD"); - long triggerPrice = calcFiatTriggerPriceAsLong.apply(mktPriceAsDouble, -50.0000); - log.info("Current USD mkt price = {} Trigger Price = {}", mktPriceAsDouble, formatPrice(triggerPrice)); + String triggerPrice = calcPriceAsString(mktPriceAsDouble, -50.0000, 4); + log.info("Current USD mkt price = {} Trigger Price = {}", mktPriceAsDouble, triggerPrice); OfferInfo offer = aliceClient.createMarketBasedPricedOffer(SELL.name(), "USD", 1_000_000, 1_000_000, 0.00, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), paymentAcct.getId(), BTC, triggerPrice); log.info("SELL offer {} created with margin based price {}.", offer.getId(), - formatPrice(offer.getPrice())); + offer.getPrice()); genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. offer = aliceClient.getOffer(offer.getId()); // Offer has trigger price now. - log.info("SELL offer should be automatically disabled when mkt price falls below {}.", - formatPrice(offer.getTriggerPrice())); + log.info("SELL offer should be automatically disabled when mkt price falls below {}.", offer.getTriggerPrice()); int numIterations = 0; while (++numIterations < MAX_ITERATIONS) { @@ -87,12 +84,12 @@ public void testSellOfferAutoDisable(final TestInfo testInfo) { if (offer.getIsActivated()) { log.info("Offer still enabled at mkt price {} > {} trigger price", mktPrice, - formatPrice(offer.getTriggerPrice())); + offer.getTriggerPrice()); sleep(1000 * 60); // 60s } else { log.info("Successful test completion after offer disabled at mkt price {} < {} trigger price.", mktPrice, - formatPrice(offer.getTriggerPrice())); + offer.getTriggerPrice()); break; } if (numIterations == MAX_ITERATIONS) @@ -107,25 +104,25 @@ public void testSellOfferAutoDisable(final TestInfo testInfo) { public void testBuyOfferAutoDisable(final TestInfo testInfo) { PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, "US"); double mktPriceAsDouble = aliceClient.getBtcPrice("USD"); - long triggerPrice = calcFiatTriggerPriceAsLong.apply(mktPriceAsDouble, 50.0000); - log.info("Current USD mkt price = {} Trigger Price = {}", mktPriceAsDouble, formatPrice(triggerPrice)); + String triggerPrice = calcPriceAsString(mktPriceAsDouble, 50.0000, 4); + log.info("Current USD mkt price = {} Trigger Price = {}", mktPriceAsDouble, triggerPrice); OfferInfo offer = aliceClient.createMarketBasedPricedOffer(BUY.name(), "USD", 1_000_000, 1_000_000, 0.00, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), paymentAcct.getId(), BTC, triggerPrice); log.info("BUY offer {} created with margin based price {}.", offer.getId(), - formatPrice(offer.getPrice())); + offer.getPrice()); genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. offer = aliceClient.getOffer(offer.getId()); // Offer has trigger price now. log.info("BUY offer should be automatically disabled when mkt price rises above {}.", - formatPrice(offer.getTriggerPrice())); + offer.getTriggerPrice()); int numIterations = 0; while (++numIterations < MAX_ITERATIONS) { @@ -135,12 +132,12 @@ public void testBuyOfferAutoDisable(final TestInfo testInfo) { if (offer.getIsActivated()) { log.info("Offer still enabled at mkt price {} < {} trigger price", mktPrice, - formatPrice(offer.getTriggerPrice())); + offer.getTriggerPrice()); sleep(1000 * 60); // 60s } else { log.info("Successful test completion after offer disabled at mkt price {} > {} trigger price.", mktPrice, - formatPrice(offer.getTriggerPrice())); + offer.getTriggerPrice()); break; } if (numIterations == MAX_ITERATIONS) diff --git a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java index 2d6eef8cbe9..f9a0b4c7188 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -41,9 +42,14 @@ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class OfferTest extends AbstractOfferTest { + @BeforeAll + public static void setUp() { + setUp(false); // Use setUp(true) for running API daemons in remote debug mode. + } + @Test @Order(1) - public void testAmtTooLargeShouldThrowException() { + public void testCreateOfferValidation() { ValidateCreateOfferTest test = new ValidateCreateOfferTest(); test.testAmtTooLargeShouldThrowException(); test.testNoMatchingEURPaymentAccountShouldThrowException(); diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java b/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java index 3ae53d7d55c..2a32aa77a54 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java +++ b/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java @@ -120,6 +120,7 @@ public List getSellOffers(String currencyCode) { * @param priceMarginAsPercent * @param securityDepositAsPercent * @param feeCurrency + * @param triggerPrice * @return OfferInfo */ public OfferInfo createOfferAtMarketBasedPrice(PaymentAccount paymentAccount, @@ -130,7 +131,7 @@ public OfferInfo createOfferAtMarketBasedPrice(PaymentAccount paymentAccount, double priceMarginAsPercent, double securityDepositAsPercent, String feeCurrency, - long triggerPrice) { + String triggerPrice) { return grpcClient.createMarketBasedPricedOffer(direction, currencyCode, amountInSatoshis, diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java b/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java index de728aa76e9..40afcadab95 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java +++ b/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java @@ -33,10 +33,10 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import static bisq.apitest.method.offer.AbstractOfferTest.defaultBuyerSecurityDepositPct; import static bisq.cli.CurrencyFormat.formatInternalFiatPrice; import static bisq.cli.CurrencyFormat.formatSatoshis; import static bisq.common.util.MathUtils.scaleDownByPowerOf10; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static bisq.core.payment.payload.PaymentMethod.F2F_ID; import static java.lang.String.format; import static java.math.RoundingMode.HALF_UP; @@ -127,9 +127,9 @@ public RandomOffer create() throws InvalidRandomOfferException { amount, minAmount, priceMargin, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), feeCurrency, - 0 /*no trigger price*/); + "0" /*no trigger price*/); } else { this.offer = botClient.createOfferAtFixedPrice(paymentAccount, direction, @@ -137,7 +137,7 @@ public RandomOffer create() throws InvalidRandomOfferException { amount, minAmount, fixedOfferPrice, - getDefaultBuyerSecurityDepositAsPercent(), + defaultBuyerSecurityDepositPct.get(), feeCurrency); } this.id = offer.getId(); diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/RobotBob.java b/apitest/src/test/java/bisq/apitest/scenario/bot/RobotBob.java index d81f385a2ba..618b64c66ad 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/RobotBob.java +++ b/apitest/src/test/java/bisq/apitest/scenario/bot/RobotBob.java @@ -22,7 +22,8 @@ import static bisq.apitest.scenario.bot.protocol.ProtocolStep.DONE; import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.isShutdownCalled; -import static bisq.cli.TableFormat.formatBalancesTbls; +import static bisq.cli.table.builder.TableType.BSQ_BALANCE_TBL; +import static bisq.cli.table.builder.TableType.BTC_BALANCE_TBL; import static java.util.concurrent.TimeUnit.SECONDS; @@ -34,6 +35,7 @@ import bisq.apitest.scenario.bot.script.BashScriptGenerator; import bisq.apitest.scenario.bot.script.BotScript; import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException; +import bisq.cli.table.builder.TableBuilder; @Slf4j public @@ -74,10 +76,16 @@ public void run() { throw new IllegalStateException(botProtocol.getClass().getSimpleName() + " failed to complete."); } + StringBuilder balancesBuilder = new StringBuilder(); + balancesBuilder.append("BTC").append("\n"); + balancesBuilder.append(new TableBuilder(BTC_BALANCE_TBL, botClient.getBalance().getBtc()).build().toString()).append("\n"); + balancesBuilder.append("BSQ").append("\n"); + balancesBuilder.append(new TableBuilder(BSQ_BALANCE_TBL, botClient.getBalance().getBsq()).build().toString()); + log.info("Completed {} successful trade{}. Current Balance:\n{}", ++numTrades, numTrades == 1 ? "" : "s", - formatBalancesTbls(botClient.getBalance())); + balancesBuilder); if (numTrades < actions.length) { try { diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/BotProtocol.java b/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/BotProtocol.java index b090742a917..3fde8c2715b 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/BotProtocol.java +++ b/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/BotProtocol.java @@ -39,6 +39,7 @@ import static bisq.apitest.scenario.bot.protocol.ProtocolStep.*; import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.checkIfShutdownCalled; +import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL; import static java.lang.String.format; import static java.lang.System.currentTimeMillis; import static java.util.Arrays.stream; @@ -50,7 +51,7 @@ import bisq.apitest.scenario.bot.BotClient; import bisq.apitest.scenario.bot.script.BashScriptGenerator; import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException; -import bisq.cli.TradeFormat; +import bisq.cli.table.builder.TableBuilder; @Slf4j public abstract class BotProtocol { @@ -133,7 +134,8 @@ protected void printBotProtocolStep() { try { var t = this.getBotClient().getTrade(trade.getTradeId()); if (t.getIsFiatSent()) { - log.info("Buyer has started payment for trade:\n{}", TradeFormat.format(t)); + log.info("Buyer has started payment for trade:\n{}", + new TableBuilder(TRADE_DETAIL_TBL, t).build().toString()); return t; } } catch (Exception ex) { @@ -167,7 +169,8 @@ protected void printBotProtocolStep() { try { var t = this.getBotClient().getTrade(trade.getTradeId()); if (t.getIsFiatReceived()) { - log.info("Seller has received payment for trade:\n{}", TradeFormat.format(t)); + log.info("Seller has received payment for trade:\n{}", + new TableBuilder(TRADE_DETAIL_TBL, t).build().toString()); return t; } } catch (Exception ex) { @@ -202,7 +205,7 @@ protected void printBotProtocolStep() { if (t.getIsPayoutPublished()) { log.info("Payout tx {} has been published for trade:\n{}", t.getPayoutTxId(), - TradeFormat.format(t)); + new TableBuilder(TRADE_DETAIL_TBL, t).build().toString()); return t; } } catch (Exception ex) { diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/MakerBotProtocol.java b/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/MakerBotProtocol.java index 3f757dbbd28..a5ce8f5bcaf 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/MakerBotProtocol.java +++ b/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/MakerBotProtocol.java @@ -16,8 +16,8 @@ import static bisq.apitest.scenario.bot.protocol.ProtocolStep.DONE; import static bisq.apitest.scenario.bot.protocol.ProtocolStep.WAIT_FOR_OFFER_TAKER; import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.checkIfShutdownCalled; -import static bisq.cli.OfferFormat.formatOfferTable; -import static java.util.Collections.singletonList; +import static bisq.cli.table.builder.TableType.OFFER_TBL; +import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL; @@ -26,7 +26,7 @@ import bisq.apitest.scenario.bot.RandomOffer; import bisq.apitest.scenario.bot.script.BashScriptGenerator; import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException; -import bisq.cli.TradeFormat; +import bisq.cli.table.builder.TableBuilder; @Slf4j public class MakerBotProtocol extends BotProtocol { @@ -65,7 +65,7 @@ public void run() { private final Supplier randomOffer = () -> { checkIfShutdownCalled("Interrupted before creating random offer."); OfferInfo offer = new RandomOffer(botClient, paymentAccount).create().getOffer(); - log.info("Created random {} offer\n{}", currencyCode, formatOfferTable(singletonList(offer), currencyCode)); + log.info("Created random {} offer\n{}", currencyCode, new TableBuilder(OFFER_TBL, offer).build()); return offer; }; @@ -98,7 +98,9 @@ public void run() { private Optional getNewTrade(String offerId) { try { var trade = botClient.getTrade(offerId); - log.info("Offer {} was taken, new trade:\n{}", offerId, TradeFormat.format(trade)); + log.info("Offer {} was taken, new trade:\n{}", + offerId, + new TableBuilder(TRADE_DETAIL_TBL, trade).build().toString()); return Optional.of(trade); } catch (Exception ex) { // Get trade will throw a non-fatal gRPC exception if not found. diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/TakerBotProtocol.java b/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/TakerBotProtocol.java index dd04b5c5bff..a0aba127d66 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/TakerBotProtocol.java +++ b/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/TakerBotProtocol.java @@ -17,7 +17,7 @@ import static bisq.apitest.scenario.bot.protocol.ProtocolStep.FIND_OFFER; import static bisq.apitest.scenario.bot.protocol.ProtocolStep.TAKE_OFFER; import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.checkIfShutdownCalled; -import static bisq.cli.OfferFormat.formatOfferTable; +import static bisq.cli.table.builder.TableType.OFFER_TBL; import static bisq.core.payment.payload.PaymentMethod.F2F_ID; @@ -26,6 +26,7 @@ import bisq.apitest.scenario.bot.BotClient; import bisq.apitest.scenario.bot.script.BashScriptGenerator; import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException; +import bisq.cli.table.builder.TableBuilder; @Slf4j public class TakerBotProtocol extends BotProtocol { @@ -64,7 +65,7 @@ public void run() { private final Supplier> firstOffer = () -> { var offers = botClient.getOffers(currencyCode); if (offers.size() > 0) { - log.info("Offers found:\n{}", formatOfferTable(offers, currencyCode)); + log.info("Offers found:\n{}", new TableBuilder(OFFER_TBL, offers).build()); OfferInfo offer = offers.get(0); log.info("Will take first offer {}", offer.getId()); return Optional.of(offer); diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index 4086b8cbcdf..361fb1daa34 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -35,14 +35,14 @@ import java.io.PrintStream; import java.io.PrintWriter; -import java.math.BigDecimal; - import java.util.Date; import java.util.List; import lombok.extern.slf4j.Slf4j; -import static bisq.cli.CurrencyFormat.*; +import static bisq.cli.CurrencyFormat.formatInternalFiatPrice; +import static bisq.cli.CurrencyFormat.formatTxFeeRateInfo; +import static bisq.cli.CurrencyFormat.toSatoshis; import static bisq.cli.Method.*; import static bisq.cli.opts.OptLabel.*; import static bisq.cli.table.builder.TableType.*; @@ -53,7 +53,6 @@ import static java.lang.System.err; import static java.lang.System.exit; import static java.lang.System.out; -import static java.math.BigDecimal.ZERO; @@ -345,10 +344,10 @@ public static void run(String[] args) { var minAmount = toSatoshis(opts.getMinAmount()); var useMarketBasedPrice = opts.isUsingMktPriceMargin(); var fixedPrice = opts.getFixedPrice(); - var marketPriceMargin = opts.getMktPriceMarginAsBigDecimal(); - var securityDeposit = isSwap ? 0.00 : toSecurityDepositAsPct(opts.getSecurityDeposit()); + var marketPriceMarginPct = opts.getMktPriceMarginPct(); + var securityDepositPct = isSwap ? 0.00d : opts.getSecurityDepositPct(); var makerFeeCurrencyCode = opts.getMakerFeeCurrencyCode(); - var triggerPrice = 0; // Cannot be defined until offer is in book. + var triggerPrice = "0"; // Cannot be defined until the new offer is added to book. OfferInfo offer; if (isSwap) { offer = client.createBsqSwapOffer(direction, @@ -362,8 +361,8 @@ public static void run(String[] args) { minAmount, useMarketBasedPrice, fixedPrice, - marketPriceMargin.doubleValue(), - securityDeposit, + marketPriceMarginPct, + securityDepositPct, paymentAcctId, makerFeeCurrencyCode, triggerPrice); @@ -387,14 +386,14 @@ public static void run(String[] args) { var opts = new EditOfferOptionParser(args).parse(); var fixedPrice = opts.getFixedPrice(); var isUsingMktPriceMargin = opts.isUsingMktPriceMargin(); - var marketPriceMargin = opts.getMktPriceMarginAsBigDecimal(); - var triggerPrice = toInternalTriggerPrice(client, offerId, opts.getTriggerPriceAsBigDecimal()); + var marketPriceMarginPct = opts.getMktPriceMarginPct(); + var triggerPrice = opts.getTriggerPrice(); var enable = opts.getEnableAsSignedInt(); var editOfferType = opts.getOfferEditType(); client.editOffer(offerId, fixedPrice, isUsingMktPriceMargin, - marketPriceMargin.doubleValue(), + marketPriceMarginPct, triggerPrice, enable, editOfferType); @@ -517,7 +516,7 @@ public static void run(String[] args) { ? client.getOpenTrades() : client.getTradeHistory(category); if (trades.isEmpty()) { - out.println(format("no %s trades found", category.name().toLowerCase())); + out.printf("no %s trades found%n", category.name().toLowerCase()); } else { var tableType = category.equals(OPEN) ? OPEN_TRADES_TBL @@ -789,25 +788,6 @@ private static long toLong(String param) { } } - private static long toInternalTriggerPrice(GrpcClient client, - String offerId, - BigDecimal unscaledTriggerPrice) { - if (unscaledTriggerPrice.compareTo(ZERO) >= 0) { - // Unfortunately, the EditOffer proto triggerPrice field was declared as - // a long instead of a string, so the CLI has to look at the offer to know - // how to scale the trigger-price (for a fiat or altcoin offer) param sent - // to the server in its 'editoffer' request. That means a preliminary round - // trip to the server: a 'getmyoffer' request. - var offer = client.getOffer(offerId); - if (offer.getCounterCurrencyCode().equals("BTC")) - return toInternalCryptoCurrencyPrice(unscaledTriggerPrice); - else - return toInternalFiatPrice(unscaledTriggerPrice); - } else { - return 0L; - } - } - private static File saveFileToDisk(String prefix, @SuppressWarnings("SameParameterValue") String suffix, String text) { diff --git a/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java b/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java deleted file mode 100644 index e71dde5eb1d..00000000000 --- a/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java +++ /dev/null @@ -1,81 +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 . - */ - -package bisq.cli; - -import static com.google.common.base.Strings.padEnd; -import static com.google.common.base.Strings.padStart; - -@Deprecated -class ColumnHeaderConstants { - - // For inserting 2 spaces between column headers. - static final String COL_HEADER_DELIMITER = " "; - - // Table column header format specs, right padded with two spaces. In some cases - // such as COL_HEADER_CREATION_DATE, COL_HEADER_VOLUME and COL_HEADER_UUID, the - // expected max data string length is accounted for. In others, column header - // lengths are expected to be greater than any column value length. - static final String COL_HEADER_ADDRESS = padEnd("%-3s Address", 52, ' '); - static final String COL_HEADER_AMOUNT = "BTC(min - max)"; - static final String COL_HEADER_AVAILABLE_BALANCE = "Available Balance"; - static final String COL_HEADER_AVAILABLE_CONFIRMED_BALANCE = "Available Confirmed Balance"; - static final String COL_HEADER_UNCONFIRMED_CHANGE_BALANCE = "Unconfirmed Change Balance"; - static final String COL_HEADER_RESERVED_BALANCE = "Reserved Balance"; - static final String COL_HEADER_TOTAL_AVAILABLE_BALANCE = "Total Available Balance"; - static final String COL_HEADER_LOCKED_BALANCE = "Locked Balance"; - static final String COL_HEADER_LOCKED_FOR_VOTING_BALANCE = "Locked For Voting Balance"; - static final String COL_HEADER_LOCKUP_BONDS_BALANCE = "Lockup Bonds Balance"; - static final String COL_HEADER_UNLOCKING_BONDS_BALANCE = "Unlocking Bonds Balance"; - static final String COL_HEADER_UNVERIFIED_BALANCE = "Unverified Balance"; - static final String COL_HEADER_CONFIRMATIONS = "Confirmations"; - static final String COL_HEADER_IS_USED_ADDRESS = "Is Used"; - static final String COL_HEADER_CREATION_DATE = padEnd("Creation Date (UTC)", 20, ' '); - static final String COL_HEADER_CURRENCY = "Currency"; - static final String COL_HEADER_DIRECTION = "Buy/Sell"; - static final String COL_HEADER_ENABLED = "Enabled"; - static final String COL_HEADER_NAME = "Name"; - static final String COL_HEADER_PAYMENT_METHOD = "Payment Method"; - static final String COL_HEADER_PRICE = "Price in %-3s for 1 BTC"; - static final String COL_HEADER_PRICE_OF_ALTCOIN = "Price in BTC for 1 %-3s"; - static final String COL_HEADER_TRADE_AMOUNT = padStart("Amount(%-3s)", 12, ' '); - static final String COL_HEADER_TRADE_BSQ_BUYER_ADDRESS = "BSQ Buyer Address"; - static final String COL_HEADER_TRADE_BUYER_COST = padEnd("Buyer Cost(%-3s)", 15, ' '); - static final String COL_HEADER_TRADE_DEPOSIT_CONFIRMED = "Deposit Confirmed"; - static final String COL_HEADER_TRADE_DEPOSIT_PUBLISHED = "Deposit Published"; - static final String COL_HEADER_TRADE_PAYMENT_SENT = padEnd("%-3s Sent", 8, ' '); - static final String COL_HEADER_TRADE_PAYMENT_RECEIVED = padEnd("%-3s Received", 12, ' '); - static final String COL_HEADER_TRADE_PAYOUT_PUBLISHED = "Payout Published"; - static final String COL_HEADER_TRADE_WITHDRAWN = "Withdrawn"; - static final String COL_HEADER_TRADE_ROLE = "My Role"; - static final String COL_HEADER_TRADE_SHORT_ID = "ID"; - static final String COL_HEADER_TRADE_TX_FEE = padEnd("Tx Fee(BTC)", 12, ' '); - static final String COL_HEADER_TRADE_MAKER_FEE = padEnd("Maker Fee(%-3s)", 12, ' '); // "Maker Fee(%-3s)"; - static final String COL_HEADER_TRADE_TAKER_FEE = padEnd("Taker Fee(%-3s)", 12, ' '); // "Taker Fee(%-3s)"; - static final String COL_HEADER_TRIGGER_PRICE = "Trigger Price(%-3s)"; - static final String COL_HEADER_TX_ID = "Tx ID"; - static final String COL_HEADER_TX_INPUT_SUM = "Tx Inputs (BTC)"; - static final String COL_HEADER_TX_OUTPUT_SUM = "Tx Outputs (BTC)"; - static final String COL_HEADER_TX_FEE = "Tx Fee (BTC)"; - static final String COL_HEADER_TX_SIZE = "Tx Size (Bytes)"; - static final String COL_HEADER_TX_IS_CONFIRMED = "Is Confirmed"; - static final String COL_HEADER_TX_MEMO = "Memo"; - - static final String COL_HEADER_VOLUME = padEnd("%-3s(min - max)", 15, ' '); - - static final String COL_HEADER_UUID = padEnd("ID", 52, ' '); -} diff --git a/cli/src/main/java/bisq/cli/CurrencyFormat.java b/cli/src/main/java/bisq/cli/CurrencyFormat.java index a9d87146199..16836d7eee4 100644 --- a/cli/src/main/java/bisq/cli/CurrencyFormat.java +++ b/cli/src/main/java/bisq/cli/CurrencyFormat.java @@ -33,14 +33,17 @@ import static java.math.RoundingMode.HALF_UP; import static java.math.RoundingMode.UNNECESSARY; +/** + * Utility for formatting amounts, volumes and fees; there is no i18n support in the CLI. + */ @VisibleForTesting public class CurrencyFormat { - // Use the US locale for all DecimalFormat objects. + // Use the US locale as a base for all DecimalFormats, but commas should be omitted from number strings. private static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = DecimalFormatSymbols.getInstance(Locale.US); - // Format numbers in US locale for CLI console. - private static final NumberFormat FRIENDLY_NUMBER_FORMAT = NumberFormat.getInstance(Locale.US); + // Use the US locale as a base for all NumberFormats, but commas should be omitted from number strings. + private static final NumberFormat US_LOCALE_NUMBER_FORMAT = NumberFormat.getInstance(Locale.US); // Formats numbers for internal use, i.e., grpc request parameters. private static final DecimalFormat INTERNAL_FIAT_DECIMAL_FORMAT = new DecimalFormat("##############0.0000"); @@ -52,30 +55,25 @@ public class CurrencyFormat { static final BigDecimal BSQ_SATOSHI_DIVISOR = new BigDecimal(100); static final DecimalFormat BSQ_FORMAT = new DecimalFormat("###,###,###,##0.00", DECIMAL_FORMAT_SYMBOLS); - static final DecimalFormat SEND_BSQ_FORMAT = new DecimalFormat("###########0.00", DECIMAL_FORMAT_SYMBOLS); - static final BigDecimal SECURITY_DEPOSIT_MULTIPLICAND = new BigDecimal("0.01"); + public static String formatSatoshis(String sats) { + //noinspection BigDecimalMethodWithoutRoundingCalled + return SATOSHI_FORMAT.format(new BigDecimal(sats).divide(SATOSHI_DIVISOR)); + } @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") public static String formatSatoshis(long sats) { - return SATOSHI_FORMAT.format(BigDecimal.valueOf(sats).divide(SATOSHI_DIVISOR)); + return SATOSHI_FORMAT.format(new BigDecimal(sats).divide(SATOSHI_DIVISOR)); } @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") public static String formatBtc(long sats) { - return BTC_FORMAT.format(BigDecimal.valueOf(sats).divide(SATOSHI_DIVISOR)); + return BTC_FORMAT.format(new BigDecimal(sats).divide(SATOSHI_DIVISOR)); } @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") public static String formatBsq(long sats) { - return BSQ_FORMAT.format(BigDecimal.valueOf(sats).divide(BSQ_SATOSHI_DIVISOR)); - } - - public static String formatBsqAmount(long bsqSats) { - FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(2); - FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(2); - FRIENDLY_NUMBER_FORMAT.setRoundingMode(HALF_UP); - return SEND_BSQ_FORMAT.format((double) bsqSats / SATOSHI_DIVISOR.doubleValue()); + return BSQ_FORMAT.format(new BigDecimal(sats).divide(BSQ_SATOSHI_DIVISOR)); } public static String formatTxFeeRateInfo(TxFeeRateInfo txFeeRateInfo) { @@ -90,27 +88,6 @@ public static String formatTxFeeRateInfo(TxFeeRateInfo txFeeRateInfo) { formatFeeSatoshis(txFeeRateInfo.getMinFeeServiceRate())); } - @Deprecated - public static String formatAmountRange(long minAmount, long amount) { - return minAmount != amount - ? formatSatoshis(minAmount) + " - " + formatSatoshis(amount) - : formatSatoshis(amount); - } - - @Deprecated - public static String formatVolumeRange(long minVolume, long volume) { - return minVolume != volume - ? formatFiatVolume(minVolume) + " - " + formatFiatVolume(volume) - : formatFiatVolume(volume); - } - - @Deprecated - public static String formatCryptoCurrencyVolumeRange(long minVolume, long volume) { - return minVolume != volume - ? formatCryptoCurrencyVolume(minVolume) + " - " + formatCryptoCurrencyVolume(volume) - : formatCryptoCurrencyVolume(volume); - } - public static String formatInternalFiatPrice(BigDecimal price) { INTERNAL_FIAT_DECIMAL_FORMAT.setMinimumFractionDigits(4); INTERNAL_FIAT_DECIMAL_FORMAT.setMaximumFractionDigits(4); @@ -118,50 +95,23 @@ public static String formatInternalFiatPrice(BigDecimal price) { } public static String formatInternalFiatPrice(double price) { - FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(4); - FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(4); - return FRIENDLY_NUMBER_FORMAT.format(price); + US_LOCALE_NUMBER_FORMAT.setMinimumFractionDigits(4); + US_LOCALE_NUMBER_FORMAT.setMaximumFractionDigits(4); + return US_LOCALE_NUMBER_FORMAT.format(price); } public static String formatPrice(long price) { - FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(4); - FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(4); - FRIENDLY_NUMBER_FORMAT.setRoundingMode(UNNECESSARY); - return FRIENDLY_NUMBER_FORMAT.format((double) price / 10_000); - } - - public static String formatCryptoCurrencyPrice(long price) { - FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(8); - FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(8); - FRIENDLY_NUMBER_FORMAT.setRoundingMode(UNNECESSARY); - return FRIENDLY_NUMBER_FORMAT.format((double) price / SATOSHI_DIVISOR.doubleValue()); + US_LOCALE_NUMBER_FORMAT.setMinimumFractionDigits(4); + US_LOCALE_NUMBER_FORMAT.setMaximumFractionDigits(4); + US_LOCALE_NUMBER_FORMAT.setRoundingMode(UNNECESSARY); + return US_LOCALE_NUMBER_FORMAT.format((double) price / 10_000); } public static String formatFiatVolume(long volume) { - FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(0); - FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(0); - FRIENDLY_NUMBER_FORMAT.setRoundingMode(HALF_UP); - return FRIENDLY_NUMBER_FORMAT.format((double) volume / 10_000); - } - - public static String formatCryptoCurrencyVolume(long volume) { - int defaultPrecision = 2; - return formatCryptoCurrencyVolume(volume, defaultPrecision); - } - - public static String formatCryptoCurrencyVolume(long volume, int precision) { - FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(precision); - FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(precision); - FRIENDLY_NUMBER_FORMAT.setRoundingMode(HALF_UP); - return FRIENDLY_NUMBER_FORMAT.format((double) volume / SATOSHI_DIVISOR.doubleValue()); - } - - public static long toInternalFiatPrice(BigDecimal fiatPrice) { - return fiatPrice.multiply(new BigDecimal(10_000)).longValue(); - } - - public static long toInternalCryptoCurrencyPrice(BigDecimal altcoinPrice) { - return altcoinPrice.multiply(new BigDecimal(100_000_000)).longValue(); + US_LOCALE_NUMBER_FORMAT.setMinimumFractionDigits(0); + US_LOCALE_NUMBER_FORMAT.setMaximumFractionDigits(0); + US_LOCALE_NUMBER_FORMAT.setRoundingMode(HALF_UP); + return US_LOCALE_NUMBER_FORMAT.format((double) volume / 10_000); } public static long toSatoshis(String btc) { @@ -175,15 +125,6 @@ public static long toSatoshis(String btc) { } } - public static double toSecurityDepositAsPct(String securityDepositInput) { - try { - return new BigDecimal(securityDepositInput) - .multiply(SECURITY_DEPOSIT_MULTIPLICAND).doubleValue(); - } catch (NumberFormatException e) { - throw new IllegalArgumentException(format("'%s' is not a number", securityDepositInput)); - } - } - public static String formatFeeSatoshis(long sats) { return BTC_TX_FEE_FORMAT.format(BigDecimal.valueOf(sats)); } diff --git a/cli/src/main/java/bisq/cli/DirectionFormat.java b/cli/src/main/java/bisq/cli/DirectionFormat.java deleted file mode 100644 index c56d89467b1..00000000000 --- a/cli/src/main/java/bisq/cli/DirectionFormat.java +++ /dev/null @@ -1,61 +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 . - */ - -package bisq.cli; - -import bisq.proto.grpc.OfferInfo; - -import java.util.List; -import java.util.function.Function; - -import static bisq.cli.ColumnHeaderConstants.COL_HEADER_DIRECTION; -import static java.lang.String.format; -import static protobuf.OfferDirection.BUY; -import static protobuf.OfferDirection.SELL; - -@Deprecated -class DirectionFormat { - - static int getLongestDirectionColWidth(List offers) { - if (offers.isEmpty() || offers.get(0).getBaseCurrencyCode().equals("BTC")) - return COL_HEADER_DIRECTION.length(); - else - return 18; // .e.g., "Sell BSQ (Buy BTC)".length() - } - - static final Function directionFormat = (offer) -> { - String baseCurrencyCode = offer.getBaseCurrencyCode(); - boolean isCryptoCurrencyOffer = !baseCurrencyCode.equals("BTC"); - if (!isCryptoCurrencyOffer) { - return baseCurrencyCode; - } else { - // Return "Sell BSQ (Buy BTC)", or "Buy BSQ (Sell BTC)". - String direction = offer.getDirection(); - String mirroredDirection = getMirroredDirection(direction); - Function mixedCase = (word) -> word.charAt(0) + word.substring(1).toLowerCase(); - return format("%s %s (%s %s)", - mixedCase.apply(mirroredDirection), - baseCurrencyCode, - mixedCase.apply(direction), - offer.getCounterCurrencyCode()); - } - }; - - static String getMirroredDirection(String directionAsString) { - return directionAsString.equalsIgnoreCase(BUY.name()) ? SELL.name() : BUY.name(); - } -} diff --git a/cli/src/main/java/bisq/cli/GrpcClient.java b/cli/src/main/java/bisq/cli/GrpcClient.java index 897599c66fa..bf9b20888da 100644 --- a/cli/src/main/java/bisq/cli/GrpcClient.java +++ b/cli/src/main/java/bisq/cli/GrpcClient.java @@ -162,7 +162,7 @@ public OfferInfo createFixedPricedOffer(String direction, long amount, long minAmount, String fixedPrice, - double securityDeposit, + double securityDepositPct, String paymentAcctId, String makerFeeCurrencyCode) { return offersServiceRequest.createOffer(direction, @@ -172,29 +172,29 @@ public OfferInfo createFixedPricedOffer(String direction, false, fixedPrice, 0.00, - securityDeposit, + securityDepositPct, paymentAcctId, makerFeeCurrencyCode, - 0 /* no trigger price */); + "0" /* no trigger price */); } public OfferInfo createMarketBasedPricedOffer(String direction, String currencyCode, long amount, long minAmount, - double marketPriceMargin, - double securityDeposit, + double marketPriceMarginPct, + double securityDepositPct, String paymentAcctId, String makerFeeCurrencyCode, - long triggerPrice) { + String triggerPrice) { return offersServiceRequest.createOffer(direction, currencyCode, amount, minAmount, true, "0", - marketPriceMargin, - securityDeposit, + marketPriceMarginPct, + securityDepositPct, paymentAcctId, makerFeeCurrencyCode, triggerPrice); @@ -206,19 +206,19 @@ public OfferInfo createOffer(String direction, long minAmount, boolean useMarketBasedPrice, String fixedPrice, - double marketPriceMargin, - double securityDeposit, + double marketPriceMarginPct, + double securityDepositPct, String paymentAcctId, String makerFeeCurrencyCode, - long triggerPrice) { + String triggerPrice) { return offersServiceRequest.createOffer(direction, currencyCode, amount, minAmount, useMarketBasedPrice, fixedPrice, - marketPriceMargin, - securityDeposit, + marketPriceMarginPct, + securityDepositPct, paymentAcctId, makerFeeCurrencyCode, triggerPrice); @@ -236,24 +236,24 @@ public void editOfferPriceMargin(String offerId, double marketPriceMargin) { offersServiceRequest.editOfferPriceMargin(offerId, marketPriceMargin); } - public void editOfferTriggerPrice(String offerId, long triggerPrice) { + public void editOfferTriggerPrice(String offerId, String triggerPrice) { offersServiceRequest.editOfferTriggerPrice(offerId, triggerPrice); } public void editOffer(String offerId, - String priceAsString, + String price, boolean useMarketBasedPrice, - double marketPriceMargin, - long triggerPrice, + double marketPriceMarginPct, + String triggerPrice, int enable, EditType editType) { // Take care when using this method directly: // useMarketBasedPrice = true if margin based offer, false for fixed priced offer // scaledPriceString fmt = ######.#### offersServiceRequest.editOffer(offerId, - priceAsString, + price, useMarketBasedPrice, - marketPriceMargin, + marketPriceMarginPct, triggerPrice, enable, editType); diff --git a/cli/src/main/java/bisq/cli/OfferFormat.java b/cli/src/main/java/bisq/cli/OfferFormat.java deleted file mode 100644 index 9aea2d91673..00000000000 --- a/cli/src/main/java/bisq/cli/OfferFormat.java +++ /dev/null @@ -1,309 +0,0 @@ -package bisq.cli; - -import bisq.proto.grpc.OfferInfo; - -import com.google.common.annotations.VisibleForTesting; - -import java.util.List; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import static bisq.cli.ColumnHeaderConstants.*; -import static bisq.cli.CurrencyFormat.*; -import static bisq.cli.DirectionFormat.directionFormat; -import static bisq.cli.DirectionFormat.getLongestDirectionColWidth; -import static bisq.cli.TableFormat.formatTimestamp; -import static bisq.cli.TableFormat.getLongestColumnSize; -import static com.google.common.base.Strings.padEnd; -import static com.google.common.base.Strings.padStart; -import static java.lang.String.format; - -@Deprecated -@VisibleForTesting -public class OfferFormat { - - public static String formatOfferTable(List offers, String currencyCode) { - if (offers == null || offers.isEmpty()) - throw new IllegalArgumentException(format("%s offer list is empty", currencyCode.toLowerCase())); - - String baseCurrencyCode = offers.get(0).getBaseCurrencyCode(); - boolean isMyOffer = offers.get(0).getIsMyOffer(); - return baseCurrencyCode.equalsIgnoreCase("BTC") - ? formatFiatOfferTable(offers, currencyCode, isMyOffer) - : formatCryptoCurrencyOfferTable(offers, baseCurrencyCode, isMyOffer); - } - - private static String formatFiatOfferTable(List offers, - String fiatCurrencyCode, - boolean isMyOffer) { - // Some column values might be longer than header, so we need to calculate them. - int amountColWith = getLongestAmountColWidth(offers); - int volumeColWidth = getLongestVolumeColWidth(offers); - int paymentMethodColWidth = getLongestPaymentMethodColWidth(offers); - // "Enabled" and "Trigger Price" columns are displayed for my offers only. - String enabledHeaderFormat = isMyOffer ? - COL_HEADER_ENABLED + COL_HEADER_DELIMITER - : ""; - String triggerPriceHeaderFormat = isMyOffer ? - // COL_HEADER_TRIGGER_PRICE includes %s -> fiatCurrencyCode - COL_HEADER_TRIGGER_PRICE + COL_HEADER_DELIMITER - : ""; - String headersFormat = enabledHeaderFormat - + COL_HEADER_DIRECTION + COL_HEADER_DELIMITER - // COL_HEADER_PRICE includes %s -> fiatCurrencyCode - + COL_HEADER_PRICE + COL_HEADER_DELIMITER - + padStart(COL_HEADER_AMOUNT, amountColWith, ' ') + COL_HEADER_DELIMITER - // COL_HEADER_VOLUME includes %s -> fiatCurrencyCode - + padStart(COL_HEADER_VOLUME, volumeColWidth, ' ') + COL_HEADER_DELIMITER - + triggerPriceHeaderFormat - + padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER - + COL_HEADER_CREATION_DATE + COL_HEADER_DELIMITER - + COL_HEADER_UUID.trim() + "%n"; - String headerLine = format(headersFormat, - fiatCurrencyCode.toUpperCase(), - fiatCurrencyCode.toUpperCase(), - // COL_HEADER_TRIGGER_PRICE includes %s -> fiatCurrencyCode - isMyOffer ? fiatCurrencyCode.toUpperCase() : ""); - String colDataFormat = getFiatOfferColDataFormat(isMyOffer, - amountColWith, - volumeColWidth, - paymentMethodColWidth); - return formattedFiatOfferTable(offers, isMyOffer, headerLine, colDataFormat); - } - - private static String formattedFiatOfferTable(List offers, - boolean isMyOffer, - String headerLine, - String colDataFormat) { - if (isMyOffer) { - return headerLine - + offers.stream() - .map(o -> format(colDataFormat, - formatEnabled(o), - o.getDirection(), - formatPrice(o.getPrice()), - formatAmountRange(o.getMinAmount(), o.getAmount()), - formatVolumeRange(o.getMinVolume(), o.getVolume()), - o.getTriggerPrice() == 0 ? "" : formatPrice(o.getTriggerPrice()), - o.getPaymentMethodShortName(), - formatTimestamp(o.getDate()), - o.getId())) - .collect(Collectors.joining("\n")); - } else { - return headerLine - + offers.stream() - .map(o -> format(colDataFormat, - o.getDirection(), - formatPrice(o.getPrice()), - formatAmountRange(o.getMinAmount(), o.getAmount()), - formatVolumeRange(o.getMinVolume(), o.getVolume()), - o.getPaymentMethodShortName(), - formatTimestamp(o.getDate()), - o.getId())) - .collect(Collectors.joining("\n")); - } - } - - private static String getFiatOfferColDataFormat(boolean isMyOffer, - int amountColWith, - int volumeColWidth, - int paymentMethodColWidth) { - if (isMyOffer) { - return "%-" + (COL_HEADER_ENABLED.length() + COL_HEADER_DELIMITER.length()) + "s" - + "%-" + (COL_HEADER_DIRECTION.length() + COL_HEADER_DELIMITER.length()) + "s" - + "%" + (COL_HEADER_PRICE.length() - 1) + "s" - + " %" + amountColWith + "s" - + " %" + (volumeColWidth - 1) + "s" - + " %" + (COL_HEADER_TRIGGER_PRICE.length() - 1) + "s" - + " %-" + paymentMethodColWidth + "s" - + " %-" + (COL_HEADER_CREATION_DATE.length()) + "s" - + " %-" + COL_HEADER_UUID.length() + "s"; - } else { - return "%-" + (COL_HEADER_DIRECTION.length() + COL_HEADER_DELIMITER.length()) + "s" - + "%" + (COL_HEADER_PRICE.length() - 1) + "s" - + " %" + amountColWith + "s" - + " %" + (volumeColWidth - 1) + "s" - + " %-" + paymentMethodColWidth + "s" - + " %-" + (COL_HEADER_CREATION_DATE.length()) + "s" - + " %-" + COL_HEADER_UUID.length() + "s"; - } - } - - private static String formatCryptoCurrencyOfferTable(List offers, - String cryptoCurrencyCode, - boolean isMyOffer) { - // Some column values might be longer than header, so we need to calculate them. - int directionColWidth = getLongestDirectionColWidth(offers); - int amountColWith = getLongestAmountColWidth(offers); - int volumeColWidth = getLongestCryptoCurrencyVolumeColWidth(offers); - int paymentMethodColWidth = getLongestPaymentMethodColWidth(offers); - // "Enabled" column is displayed for my offers only. - String enabledHeaderFormat = isMyOffer ? - COL_HEADER_ENABLED + COL_HEADER_DELIMITER - : ""; - Supplier shouldShowTriggerPrice = () -> isMyOffer && !cryptoCurrencyCode.equalsIgnoreCase("BSQ"); - String triggerPriceHeaderFormat = shouldShowTriggerPrice.get() - ? COL_HEADER_TRIGGER_PRICE + COL_HEADER_DELIMITER - : ""; - // TODO use memoize function to avoid duplicate the formatting done above? - String headersFormat = enabledHeaderFormat - + padEnd(COL_HEADER_DIRECTION, directionColWidth, ' ') + COL_HEADER_DELIMITER - + COL_HEADER_PRICE_OF_ALTCOIN + COL_HEADER_DELIMITER // includes %s -> cryptoCurrencyCode - + padStart(COL_HEADER_AMOUNT, amountColWith, ' ') + COL_HEADER_DELIMITER - // COL_HEADER_VOLUME includes %s -> cryptoCurrencyCode - + padStart(COL_HEADER_VOLUME, volumeColWidth, ' ') + COL_HEADER_DELIMITER - + triggerPriceHeaderFormat // COL_HEADER_TRIGGER_PRICE includes %s -> cryptoCurrencyCode - + padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER - + COL_HEADER_CREATION_DATE + COL_HEADER_DELIMITER - + COL_HEADER_UUID.trim() + "%n"; - String headerLine = format(headersFormat, - cryptoCurrencyCode.toUpperCase(), - cryptoCurrencyCode.toUpperCase(), - shouldShowTriggerPrice.get() ? cryptoCurrencyCode.toUpperCase() : ""); - String colDataFormat = getCryptoCurrencyOfferColDataFormat(isMyOffer, - shouldShowTriggerPrice.get(), - directionColWidth, - amountColWith, - volumeColWidth, - paymentMethodColWidth); - if (isMyOffer) { - if (shouldShowTriggerPrice.get()) { - // Is my non-BSQ altcoin offer. Show ENABLED and TRIGGER_PRICE data. - return headerLine - + offers.stream() - .map(o -> format(colDataFormat, - formatEnabled(o), - directionFormat.apply(o), - formatCryptoCurrencyPrice(o.getPrice()), - formatAmountRange(o.getMinAmount(), o.getAmount()), - formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()), - o.getTriggerPrice() == 0 ? "" : formatCryptoCurrencyPrice(o.getTriggerPrice()), - o.getPaymentMethodShortName(), - formatTimestamp(o.getDate()), - o.getId())) - .collect(Collectors.joining("\n")); - } else { - // Is my BSQ altcoin offer. Show ENABLED, but not TRIGGER_PRICE data. - return headerLine - + offers.stream() - .map(o -> format(colDataFormat, - formatEnabled(o), - directionFormat.apply(o), - formatCryptoCurrencyPrice(o.getPrice()), - formatAmountRange(o.getMinAmount(), o.getAmount()), - formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()), - o.getPaymentMethodShortName(), - formatTimestamp(o.getDate()), - o.getId())) - .collect(Collectors.joining("\n")); - } - } else { - // Not my offer. Do not show ENABLED and TRIGGER_PRICE cols. - return headerLine - + offers.stream() - .map(o -> format(colDataFormat, - directionFormat.apply(o), - formatCryptoCurrencyPrice(o.getPrice()), - formatAmountRange(o.getMinAmount(), o.getAmount()), - formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()), - o.getPaymentMethodShortName(), - formatTimestamp(o.getDate()), - o.getId())) - .collect(Collectors.joining("\n")); - } - } - - private static String getCryptoCurrencyOfferColDataFormat(boolean isMyOffer, - boolean shouldShowTriggerPrice, - int directionColWidth, - int amountColWith, - int volumeColWidth, - int paymentMethodColWidth) { - if (isMyOffer) { - return getMyCryptoOfferColDataFormat(shouldShowTriggerPrice, - directionColWidth, - amountColWith, - volumeColWidth, - paymentMethodColWidth); - } else { - // Not my offer. Do not show ENABLED and TRIGGER_PRICE cols. - return "%-" + directionColWidth + "s" - + "%" + (COL_HEADER_PRICE_OF_ALTCOIN.length() + 1) + "s" - + " %" + amountColWith + "s" - + " %" + (volumeColWidth - 1) + "s" - + " %-" + paymentMethodColWidth + "s" - + " %-" + (COL_HEADER_CREATION_DATE.length()) + "s" - + " %-" + COL_HEADER_UUID.length() + "s"; - } - } - - private static String getMyCryptoOfferColDataFormat(boolean shouldShowTriggerPrice, - int directionColWidth, - int amountColWith, - int volumeColWidth, - int paymentMethodColWidth) { - if (shouldShowTriggerPrice) { - // Is my non-BSQ altcoin offer. Show ENABLED and TRIGGER_PRICE cols. - return "%-" + (COL_HEADER_ENABLED.length() + COL_HEADER_DELIMITER.length()) + "s" - + "%-" + directionColWidth + "s" - + "%" + (COL_HEADER_PRICE_OF_ALTCOIN.length() + 1) + "s" - + " %" + amountColWith + "s" - + " %" + (volumeColWidth - 1) + "s" - + " %" + (COL_HEADER_TRIGGER_PRICE.length() - 1) + "s" - + " %-" + paymentMethodColWidth + "s" - + " %-" + (COL_HEADER_CREATION_DATE.length()) + "s" - + " %-" + COL_HEADER_UUID.length() + "s"; - } else { - // Is my BSQ altcoin offer. Show ENABLED, but not TRIGGER_PRICE col. - return "%-" + (COL_HEADER_ENABLED.length() + COL_HEADER_DELIMITER.length()) + "s" - + "%-" + directionColWidth + "s" - + "%" + (COL_HEADER_PRICE_OF_ALTCOIN.length() + 1) + "s" - + " %" + amountColWith + "s" - + " %" + (volumeColWidth - 1) + "s" - + " %-" + paymentMethodColWidth + "s" - + " %-" + (COL_HEADER_CREATION_DATE.length()) + "s" - + " %-" + COL_HEADER_UUID.length() + "s"; - } - } - - private static String formatEnabled(OfferInfo offerInfo) { - if (offerInfo.getIsMyOffer() && offerInfo.getIsMyPendingOffer()) - return "PENDING"; - else - return offerInfo.getIsActivated() ? "YES" : "NO"; - } - - private static int getLongestPaymentMethodColWidth(List offers) { - return getLongestColumnSize( - COL_HEADER_PAYMENT_METHOD.length(), - offers.stream() - .map(OfferInfo::getPaymentMethodShortName) - .collect(Collectors.toList())); - } - - private static int getLongestAmountColWidth(List offers) { - return getLongestColumnSize( - COL_HEADER_AMOUNT.length(), - offers.stream() - .map(o -> formatAmountRange(o.getMinAmount(), o.getAmount())) - .collect(Collectors.toList())); - } - - private static int getLongestVolumeColWidth(List offers) { - // Pad this col width by 1 space. - return 1 + getLongestColumnSize( - COL_HEADER_VOLUME.length(), - offers.stream() - .map(o -> formatVolumeRange(o.getMinVolume(), o.getVolume())) - .collect(Collectors.toList())); - } - - private static int getLongestCryptoCurrencyVolumeColWidth(List offers) { - // Pad this col width by 1 space. - return 1 + getLongestColumnSize( - COL_HEADER_VOLUME.length(), - offers.stream() - .map(o -> formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume())) - .collect(Collectors.toList())); - } -} diff --git a/cli/src/main/java/bisq/cli/TableFormat.java b/cli/src/main/java/bisq/cli/TableFormat.java deleted file mode 100644 index 37c3b36476c..00000000000 --- a/cli/src/main/java/bisq/cli/TableFormat.java +++ /dev/null @@ -1,156 +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 . - */ - -package bisq.cli; - -import bisq.proto.grpc.AddressBalanceInfo; -import bisq.proto.grpc.BalancesInfo; -import bisq.proto.grpc.BsqBalanceInfo; -import bisq.proto.grpc.BtcBalanceInfo; - -import protobuf.PaymentAccount; - -import com.google.common.annotations.VisibleForTesting; - -import java.text.SimpleDateFormat; - -import java.util.Date; -import java.util.List; -import java.util.TimeZone; -import java.util.stream.Collectors; - -import static bisq.cli.ColumnHeaderConstants.*; -import static bisq.cli.CurrencyFormat.formatBsq; -import static bisq.cli.CurrencyFormat.formatSatoshis; -import static com.google.common.base.Strings.padEnd; -import static java.lang.String.format; -import static java.util.Collections.max; -import static java.util.Comparator.comparing; -import static java.util.TimeZone.getTimeZone; - -@Deprecated -@VisibleForTesting -public class TableFormat { - - static final TimeZone TZ_UTC = getTimeZone("UTC"); - static final SimpleDateFormat DATE_FORMAT_ISO_8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - - public static String formatAddressBalanceTbl(List addressBalanceInfo) { - String headerFormatString = COL_HEADER_ADDRESS + COL_HEADER_DELIMITER - + COL_HEADER_AVAILABLE_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_CONFIRMATIONS + COL_HEADER_DELIMITER - + COL_HEADER_IS_USED_ADDRESS + COL_HEADER_DELIMITER + "\n"; - String headerLine = format(headerFormatString, "BTC"); - - String colDataFormat = "%-" + COL_HEADER_ADDRESS.length() + "s" // lt justify - + " %" + (COL_HEADER_AVAILABLE_BALANCE.length() - 1) + "s" // rt justify - + " %" + COL_HEADER_CONFIRMATIONS.length() + "d" // rt justify - + " %-" + COL_HEADER_IS_USED_ADDRESS.length() + "s"; // lt justify - return headerLine - + addressBalanceInfo.stream() - .map(info -> format(colDataFormat, - info.getAddress(), - formatSatoshis(info.getBalance()), - info.getNumConfirmations(), - info.getIsAddressUnused() ? "NO" : "YES")) - .collect(Collectors.joining("\n")); - } - - public static String formatBalancesTbls(BalancesInfo balancesInfo) { - return "BTC" + "\n" - + formatBtcBalanceInfoTbl(balancesInfo.getBtc()) + "\n" - + "BSQ" + "\n" - + formatBsqBalanceInfoTbl(balancesInfo.getBsq()); - } - - public static String formatBsqBalanceInfoTbl(BsqBalanceInfo bsqBalanceInfo) { - String headerLine = COL_HEADER_AVAILABLE_CONFIRMED_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_UNVERIFIED_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_UNCONFIRMED_CHANGE_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_LOCKED_FOR_VOTING_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_LOCKUP_BONDS_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_UNLOCKING_BONDS_BALANCE + COL_HEADER_DELIMITER + "\n"; - String colDataFormat = "%" + COL_HEADER_AVAILABLE_CONFIRMED_BALANCE.length() + "s" // rt justify - + " %" + (COL_HEADER_UNVERIFIED_BALANCE.length() + 1) + "s" // rt justify - + " %" + (COL_HEADER_UNCONFIRMED_CHANGE_BALANCE.length() + 1) + "s" // rt justify - + " %" + (COL_HEADER_LOCKED_FOR_VOTING_BALANCE.length() + 1) + "s" // rt justify - + " %" + (COL_HEADER_LOCKUP_BONDS_BALANCE.length() + 1) + "s" // rt justify - + " %" + (COL_HEADER_UNLOCKING_BONDS_BALANCE.length() + 1) + "s"; // rt justify - return headerLine + format(colDataFormat, - formatBsq(bsqBalanceInfo.getAvailableConfirmedBalance()), - formatBsq(bsqBalanceInfo.getUnverifiedBalance()), - formatBsq(bsqBalanceInfo.getUnconfirmedChangeBalance()), - formatBsq(bsqBalanceInfo.getLockedForVotingBalance()), - formatBsq(bsqBalanceInfo.getLockupBondsBalance()), - formatBsq(bsqBalanceInfo.getUnlockingBondsBalance())); - } - - public static String formatBtcBalanceInfoTbl(BtcBalanceInfo btcBalanceInfo) { - String headerLine = COL_HEADER_AVAILABLE_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_RESERVED_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_TOTAL_AVAILABLE_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_LOCKED_BALANCE + COL_HEADER_DELIMITER + "\n"; - String colDataFormat = "%" + COL_HEADER_AVAILABLE_BALANCE.length() + "s" // rt justify - + " %" + (COL_HEADER_RESERVED_BALANCE.length() + 1) + "s" // rt justify - + " %" + (COL_HEADER_TOTAL_AVAILABLE_BALANCE.length() + 1) + "s" // rt justify - + " %" + (COL_HEADER_LOCKED_BALANCE.length() + 1) + "s"; // rt justify - return headerLine + format(colDataFormat, - formatSatoshis(btcBalanceInfo.getAvailableBalance()), - formatSatoshis(btcBalanceInfo.getReservedBalance()), - formatSatoshis(btcBalanceInfo.getTotalAvailableBalance()), - formatSatoshis(btcBalanceInfo.getLockedBalance())); - } - - public static String formatPaymentAcctTbl(List paymentAccounts) { - // Some column values might be longer than header, so we need to calculate them. - int nameColWidth = getLongestColumnSize( - COL_HEADER_NAME.length(), - paymentAccounts.stream().map(PaymentAccount::getAccountName) - .collect(Collectors.toList())); - int paymentMethodColWidth = getLongestColumnSize( - COL_HEADER_PAYMENT_METHOD.length(), - paymentAccounts.stream().map(a -> a.getPaymentMethod().getId()) - .collect(Collectors.toList())); - String headerLine = padEnd(COL_HEADER_NAME, nameColWidth, ' ') + COL_HEADER_DELIMITER - + COL_HEADER_CURRENCY + COL_HEADER_DELIMITER - + padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER - + COL_HEADER_UUID + COL_HEADER_DELIMITER + "\n"; - String colDataFormat = "%-" + nameColWidth + "s" // left justify - + " %-" + COL_HEADER_CURRENCY.length() + "s" // left justify - + " %-" + paymentMethodColWidth + "s" // left justify - + " %-" + COL_HEADER_UUID.length() + "s"; // left justify - return headerLine - + paymentAccounts.stream() - .map(a -> format(colDataFormat, - a.getAccountName(), - a.getSelectedTradeCurrency().getCode(), - a.getPaymentMethod().getId(), - a.getId())) - .collect(Collectors.joining("\n")); - } - - // Return size of the longest string value, or the header.len, whichever is greater. - static int getLongestColumnSize(int headerLength, List strings) { - int longest = max(strings, comparing(String::length)).length(); - return Math.max(longest, headerLength); - } - - static String formatTimestamp(long timestamp) { - DATE_FORMAT_ISO_8601.setTimeZone(TZ_UTC); - return DATE_FORMAT_ISO_8601.format(new Date(timestamp)); - } -} diff --git a/cli/src/main/java/bisq/cli/TradeFormat.java b/cli/src/main/java/bisq/cli/TradeFormat.java deleted file mode 100644 index 4ac85497288..00000000000 --- a/cli/src/main/java/bisq/cli/TradeFormat.java +++ /dev/null @@ -1,222 +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 . - */ - -package bisq.cli; - -import bisq.proto.grpc.ContractInfo; -import bisq.proto.grpc.TradeInfo; - -import com.google.common.annotations.VisibleForTesting; - -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Supplier; - -import static bisq.cli.ColumnHeaderConstants.*; -import static bisq.cli.CurrencyFormat.*; -import static com.google.common.base.Strings.padEnd; - -@Deprecated -@VisibleForTesting -public class TradeFormat { - - private static final String YES = "YES"; - private static final String NO = "NO"; - - // TODO add String format(List trades) - - @VisibleForTesting - public static String format(TradeInfo tradeInfo) { - // Some column values might be longer than header, so we need to calculate them. - int shortIdColWidth = Math.max(COL_HEADER_TRADE_SHORT_ID.length(), tradeInfo.getShortId().length()); - int roleColWidth = Math.max(COL_HEADER_TRADE_ROLE.length(), tradeInfo.getRole().length()); - - // We only show taker fee under its header when user is the taker. - boolean isTaker = tradeInfo.getRole().toLowerCase().contains("taker"); - Supplier makerFeeHeader = () -> !isTaker ? - COL_HEADER_TRADE_MAKER_FEE + COL_HEADER_DELIMITER - : ""; - Supplier makerFeeHeaderSpec = () -> !isTaker ? - "%" + (COL_HEADER_TRADE_MAKER_FEE.length() + 2) + "s" - : ""; - Supplier takerFeeHeader = () -> isTaker ? - COL_HEADER_TRADE_TAKER_FEE + COL_HEADER_DELIMITER - : ""; - Supplier takerFeeHeaderSpec = () -> isTaker ? - "%" + (COL_HEADER_TRADE_TAKER_FEE.length() + 2) + "s" - : ""; - - boolean showBsqBuyerAddress = shouldShowBsqBuyerAddress(tradeInfo, isTaker); - Supplier bsqBuyerAddressHeader = () -> showBsqBuyerAddress ? COL_HEADER_TRADE_BSQ_BUYER_ADDRESS : ""; - Supplier bsqBuyerAddressHeaderSpec = () -> showBsqBuyerAddress ? "%s" : ""; - - String headersFormat = padEnd(COL_HEADER_TRADE_SHORT_ID, shortIdColWidth, ' ') + COL_HEADER_DELIMITER - + padEnd(COL_HEADER_TRADE_ROLE, roleColWidth, ' ') + COL_HEADER_DELIMITER - + priceHeader.apply(tradeInfo) + COL_HEADER_DELIMITER // includes %s -> currencyCode - + padEnd(COL_HEADER_TRADE_AMOUNT, 12, ' ') + COL_HEADER_DELIMITER - + padEnd(COL_HEADER_TRADE_TX_FEE, 12, ' ') + COL_HEADER_DELIMITER - + makerFeeHeader.get() - // maker or taker fee header, not both - + takerFeeHeader.get() - + COL_HEADER_TRADE_DEPOSIT_PUBLISHED + COL_HEADER_DELIMITER - + COL_HEADER_TRADE_DEPOSIT_CONFIRMED + COL_HEADER_DELIMITER - + COL_HEADER_TRADE_BUYER_COST + COL_HEADER_DELIMITER - + COL_HEADER_TRADE_PAYMENT_SENT + COL_HEADER_DELIMITER - + COL_HEADER_TRADE_PAYMENT_RECEIVED + COL_HEADER_DELIMITER - + COL_HEADER_TRADE_PAYOUT_PUBLISHED + COL_HEADER_DELIMITER - + COL_HEADER_TRADE_WITHDRAWN + COL_HEADER_DELIMITER - + bsqBuyerAddressHeader.get() - + "%n"; - - String counterCurrencyCode = tradeInfo.getOffer().getCounterCurrencyCode(); - String baseCurrencyCode = tradeInfo.getOffer().getBaseCurrencyCode(); - - String headerLine = String.format(headersFormat, - /* COL_HEADER_PRICE */ priceHeaderCurrencyCode.apply(tradeInfo), - /* COL_HEADER_TRADE_AMOUNT */ baseCurrencyCode, - /* COL_HEADER_TRADE_(M||T)AKER_FEE */ makerTakerFeeHeaderCurrencyCode.apply(tradeInfo, isTaker), - /* COL_HEADER_TRADE_BUYER_COST */ counterCurrencyCode, - /* COL_HEADER_TRADE_PAYMENT_SENT */ paymentStatusHeaderCurrencyCode.apply(tradeInfo), - /* COL_HEADER_TRADE_PAYMENT_RECEIVED */ paymentStatusHeaderCurrencyCode.apply(tradeInfo)); - - String colDataFormat = "%-" + shortIdColWidth + "s" // lt justify - + " %-" + (roleColWidth + COL_HEADER_DELIMITER.length()) + "s" // left - + "%" + (COL_HEADER_PRICE.length() - 1) + "s" // rt justify - + "%" + (COL_HEADER_TRADE_AMOUNT.length() + 1) + "s" // rt justify - + "%" + (COL_HEADER_TRADE_TX_FEE.length() + 1) + "s" // rt justify - + makerFeeHeaderSpec.get() // rt justify - // OR (one of them is an empty string) - + takerFeeHeaderSpec.get() // rt justify - + " %-" + COL_HEADER_TRADE_DEPOSIT_PUBLISHED.length() + "s" // lt justify - + " %-" + COL_HEADER_TRADE_DEPOSIT_CONFIRMED.length() + "s" // lt justify - + "%" + (COL_HEADER_TRADE_BUYER_COST.length() + 1) + "s" // rt justify - + " %-" + (COL_HEADER_TRADE_PAYMENT_SENT.length() - 1) + "s" // left - + " %-" + (COL_HEADER_TRADE_PAYMENT_RECEIVED.length() - 1) + "s" // left - + " %-" + COL_HEADER_TRADE_PAYOUT_PUBLISHED.length() + "s" // lt justify - + " %-" + (COL_HEADER_TRADE_WITHDRAWN.length() + 2) + "s" - + bsqBuyerAddressHeaderSpec.get(); - - return headerLine + formatTradeData(colDataFormat, tradeInfo, isTaker, showBsqBuyerAddress); - } - - private static String formatTradeData(String format, - TradeInfo tradeInfo, - boolean isTaker, - boolean showBsqBuyerAddress) { - return String.format(format, - tradeInfo.getShortId(), - tradeInfo.getRole(), - priceFormat.apply(tradeInfo), - amountFormat.apply(tradeInfo), - makerTakerMinerTxFeeFormat.apply(tradeInfo, isTaker), - makerTakerFeeFormat.apply(tradeInfo, isTaker), - tradeInfo.getIsDepositPublished() ? YES : NO, - tradeInfo.getIsDepositConfirmed() ? YES : NO, - tradeCostFormat.apply(tradeInfo), - tradeInfo.getIsFiatSent() ? YES : NO, - tradeInfo.getIsFiatReceived() ? YES : NO, - tradeInfo.getIsPayoutPublished() ? YES : NO, - tradeInfo.getIsWithdrawn() ? YES : NO, - bsqReceiveAddress.apply(tradeInfo, showBsqBuyerAddress)); - } - - private static final Function priceHeader = (t) -> - t.getOffer().getBaseCurrencyCode().equals("BTC") - ? COL_HEADER_PRICE - : COL_HEADER_PRICE_OF_ALTCOIN; - - private static final Function priceHeaderCurrencyCode = (t) -> - t.getOffer().getBaseCurrencyCode().equals("BTC") - ? t.getOffer().getCounterCurrencyCode() - : t.getOffer().getBaseCurrencyCode(); - - private static final BiFunction makerTakerFeeHeaderCurrencyCode = (t, isTaker) -> { - if (isTaker) { - return t.getIsCurrencyForTakerFeeBtc() ? "BTC" : "BSQ"; - } else { - return t.getOffer().getIsCurrencyForMakerFeeBtc() ? "BTC" : "BSQ"; - } - }; - - private static final Function paymentStatusHeaderCurrencyCode = (t) -> - t.getOffer().getBaseCurrencyCode().equals("BTC") - ? t.getOffer().getCounterCurrencyCode() - : t.getOffer().getBaseCurrencyCode(); - - private static final Function priceFormat = (t) -> - t.getOffer().getBaseCurrencyCode().equals("BTC") - ? formatPrice(t.getTradePrice()) - : formatCryptoCurrencyPrice(t.getTradePrice()); - - private static final Function amountFormat = (t) -> - t.getOffer().getBaseCurrencyCode().equals("BTC") - ? formatSatoshis(t.getTradeAmountAsLong()) - : formatCryptoCurrencyVolume(t.getTradeVolume()); - - private static final BiFunction makerTakerMinerTxFeeFormat = (t, isTaker) -> { - if (isTaker) { - return formatSatoshis(t.getTxFeeAsLong()); - } else { - return formatSatoshis(t.getOffer().getTxFee()); - } - }; - - private static final BiFunction makerTakerFeeFormat = (t, isTaker) -> { - if (isTaker) { - return t.getIsCurrencyForTakerFeeBtc() - ? formatSatoshis(t.getTakerFeeAsLong()) - : formatBsq(t.getTakerFeeAsLong()); - } else { - return t.getOffer().getIsCurrencyForMakerFeeBtc() - ? formatSatoshis(t.getOffer().getMakerFee()) - : formatBsq(t.getOffer().getMakerFee()); - } - }; - - private static final Function tradeCostFormat = (t) -> - t.getOffer().getBaseCurrencyCode().equals("BTC") - ? formatFiatVolume(t.getTradeVolume()) - : formatSatoshis(t.getTradeAmountAsLong()); - - private static final BiFunction bsqReceiveAddress = (t, showBsqBuyerAddress) -> { - if (showBsqBuyerAddress) { - ContractInfo contract = t.getContract(); - boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker(); - return isBuyerMakerAndSellerTaker // (is BTC buyer / maker) - ? contract.getTakerPaymentAccountPayload().getAddress() - : contract.getMakerPaymentAccountPayload().getAddress(); - } else { - return ""; - } - }; - - private static boolean shouldShowBsqBuyerAddress(TradeInfo tradeInfo, boolean isTaker) { - if (tradeInfo.getOffer().getBaseCurrencyCode().equals("BTC")) { - return false; - } else { - ContractInfo contract = tradeInfo.getContract(); - // Do not forget buyer and seller refer to BTC buyer and seller, not BSQ - // buyer and seller. If you are buying BSQ, you are the (BTC) seller. - boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker(); - if (isTaker) { - return !isBuyerMakerAndSellerTaker; - } else { - return isBuyerMakerAndSellerTaker; - } - } - } -} diff --git a/cli/src/main/java/bisq/cli/TransactionFormat.java b/cli/src/main/java/bisq/cli/TransactionFormat.java deleted file mode 100644 index fd0553c537a..00000000000 --- a/cli/src/main/java/bisq/cli/TransactionFormat.java +++ /dev/null @@ -1,60 +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 . - */ - -package bisq.cli; - -import bisq.proto.grpc.TxInfo; - -import com.google.common.annotations.VisibleForTesting; - -import static bisq.cli.ColumnHeaderConstants.*; -import static bisq.cli.CurrencyFormat.formatSatoshis; -import static com.google.common.base.Strings.padEnd; - -@Deprecated -@VisibleForTesting -public class TransactionFormat { - - public static String format(TxInfo txInfo) { - String headerLine = padEnd(COL_HEADER_TX_ID, txInfo.getTxId().length(), ' ') + COL_HEADER_DELIMITER - + COL_HEADER_TX_IS_CONFIRMED + COL_HEADER_DELIMITER - + COL_HEADER_TX_INPUT_SUM + COL_HEADER_DELIMITER - + COL_HEADER_TX_OUTPUT_SUM + COL_HEADER_DELIMITER - + COL_HEADER_TX_FEE + COL_HEADER_DELIMITER - + COL_HEADER_TX_SIZE + COL_HEADER_DELIMITER - + (txInfo.getMemo().isEmpty() ? "" : COL_HEADER_TX_MEMO + COL_HEADER_DELIMITER) - + "\n"; - - String colDataFormat = "%-" + txInfo.getTxId().length() + "s" - + " %" + COL_HEADER_TX_IS_CONFIRMED.length() + "s" - + " %" + COL_HEADER_TX_INPUT_SUM.length() + "s" - + " %" + COL_HEADER_TX_OUTPUT_SUM.length() + "s" - + " %" + COL_HEADER_TX_FEE.length() + "s" - + " %" + COL_HEADER_TX_SIZE.length() + "s" - + " %s"; - - return headerLine - + String.format(colDataFormat, - txInfo.getTxId(), - txInfo.getIsPending() ? "NO" : "YES", // pending=true means not confirmed - formatSatoshis(txInfo.getInputSum()), - formatSatoshis(txInfo.getOutputSum()), - formatSatoshis(txInfo.getFee()), - txInfo.getSize(), - txInfo.getMemo().isEmpty() ? "" : txInfo.getMemo()); - } -} diff --git a/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java b/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java index 2bccab1d098..025a208a8e3 100644 --- a/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java @@ -29,6 +29,7 @@ import lombok.Getter; import static bisq.cli.opts.OptLabel.OPT_HELP; +import static java.lang.String.format; @SuppressWarnings("unchecked") abstract class AbstractMethodOptionParser implements MethodOpts { @@ -65,6 +66,14 @@ public boolean isForHelp() { return options.has(helpOpt); } + protected void verifyStringIsValidDouble(String string) { + try { + Double.valueOf(string); + } catch (NumberFormatException ex) { + throw new IllegalArgumentException(format("%s is not a number", string)); + } + } + protected final Predicate> valueNotSpecified = (opt) -> !options.hasArgument(opt) || options.valueOf(opt).isEmpty(); diff --git a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java index 5c338434815..40881891c85 100644 --- a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java @@ -20,8 +20,6 @@ import joptsimple.OptionSpec; -import java.math.BigDecimal; - import static bisq.cli.opts.OptLabel.*; import static java.lang.Boolean.FALSE; import static joptsimple.internal.Strings.EMPTY; @@ -45,7 +43,7 @@ public class CreateOfferOptionParser extends AbstractMethodOptionParser implemen final OptionSpec minAmountOpt = parser.accepts(OPT_MIN_AMOUNT, "minimum amount of btc to buy or sell") .withOptionalArg(); - final OptionSpec mktPriceMarginOpt = parser.accepts(OPT_MKT_PRICE_MARGIN, "market btc price margin (%)") + final OptionSpec mktPriceMarginPctOpt = parser.accepts(OPT_MKT_PRICE_MARGIN, "market btc price margin (%)") .withOptionalArg() .defaultsTo("0.00"); @@ -53,7 +51,7 @@ public class CreateOfferOptionParser extends AbstractMethodOptionParser implemen .withOptionalArg() .defaultsTo("0"); - final OptionSpec securityDepositOpt = parser.accepts(OPT_SECURITY_DEPOSIT, "maker security deposit (%)") + final OptionSpec securityDepositPctOpt = parser.accepts(OPT_SECURITY_DEPOSIT, "maker security deposit (%)") .withRequiredArg(); final OptionSpec makerFeeCurrencyCodeOpt = parser.accepts(OPT_FEE_CURRENCY, "maker fee currency code (bsq|btc)") @@ -92,10 +90,10 @@ public CreateOfferOptionParser parse() { if (options.has(paymentAccountIdOpt)) throw new IllegalArgumentException("cannot use a payment account id in bsq swap offer"); - if (options.has(mktPriceMarginOpt)) + if (options.has(mktPriceMarginPctOpt)) throw new IllegalArgumentException("cannot use a market price margin in bsq swap offer"); - if (options.has(securityDepositOpt)) + if (options.has(securityDepositPctOpt)) throw new IllegalArgumentException("cannot use a security deposit in bsq swap offer"); if (!options.has(fixedPriceOpt) || options.valueOf(fixedPriceOpt).isEmpty()) @@ -105,17 +103,24 @@ public CreateOfferOptionParser parse() { if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty()) throw new IllegalArgumentException("no payment account id specified"); - if (!options.has(mktPriceMarginOpt) && !options.has(fixedPriceOpt)) + if (!options.has(mktPriceMarginPctOpt) && !options.has(fixedPriceOpt)) throw new IllegalArgumentException("no market price margin or fixed price specified"); - if (options.has(mktPriceMarginOpt) && options.valueOf(mktPriceMarginOpt).isEmpty()) - throw new IllegalArgumentException("no market price margin specified"); + if (options.has(mktPriceMarginPctOpt)) { + var mktPriceMarginPctString = options.valueOf(mktPriceMarginPctOpt); + if (mktPriceMarginPctString.isEmpty()) + throw new IllegalArgumentException("no market price margin specified"); + else + verifyStringIsValidDouble(mktPriceMarginPctString); + } if (options.has(fixedPriceOpt) && options.valueOf(fixedPriceOpt).isEmpty()) throw new IllegalArgumentException("no fixed price specified"); - if (!options.has(securityDepositOpt) || options.valueOf(securityDepositOpt).isEmpty()) + if (!options.has(securityDepositPctOpt) || options.valueOf(securityDepositPctOpt).isEmpty()) throw new IllegalArgumentException("no security deposit specified"); + else + verifyStringIsValidDouble(options.valueOf(securityDepositPctOpt)); } return this; @@ -142,24 +147,19 @@ public String getMinAmount() { } public boolean isUsingMktPriceMargin() { - return options.has(mktPriceMarginOpt); - } - - @SuppressWarnings("unused") - public String getMktPriceMargin() { - return isUsingMktPriceMargin() ? options.valueOf(mktPriceMarginOpt) : "0.00"; + return options.has(mktPriceMarginPctOpt); } - public BigDecimal getMktPriceMarginAsBigDecimal() { - return isUsingMktPriceMargin() ? new BigDecimal(options.valueOf(mktPriceMarginOpt)) : BigDecimal.ZERO; + public double getMktPriceMarginPct() { + return isUsingMktPriceMargin() ? Double.parseDouble(options.valueOf(mktPriceMarginPctOpt)) : 0.00d; } public String getFixedPrice() { return options.has(fixedPriceOpt) ? options.valueOf(fixedPriceOpt) : "0.00"; } - public String getSecurityDeposit() { - return options.valueOf(securityDepositOpt); + public double getSecurityDepositPct() { + return Double.valueOf(options.valueOf(securityDepositPctOpt)); } public String getMakerFeeCurrencyCode() { diff --git a/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java index 557a0ff77d4..0e0b2d61c5a 100644 --- a/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java @@ -22,14 +22,11 @@ import joptsimple.OptionSpec; -import java.math.BigDecimal; - import static bisq.cli.opts.OptLabel.OPT_ENABLE; import static bisq.cli.opts.OptLabel.OPT_FIXED_PRICE; import static bisq.cli.opts.OptLabel.OPT_MKT_PRICE_MARGIN; import static bisq.cli.opts.OptLabel.OPT_TRIGGER_PRICE; import static bisq.proto.grpc.EditOfferRequest.EditType.*; -import static java.lang.String.format; @@ -45,7 +42,7 @@ public class EditOfferOptionParser extends OfferIdOptionParser implements Method .withOptionalArg() .defaultsTo("0"); - final OptionSpec mktPriceMarginOpt = parser.accepts(OPT_MKT_PRICE_MARGIN, + final OptionSpec mktPriceMarginPctOpt = parser.accepts(OPT_MKT_PRICE_MARGIN, "market btc price margin (%)") .withOptionalArg() .defaultsTo("0.00"); @@ -75,7 +72,7 @@ public EditOfferOptionParser parse() { // Super class will short-circuit parsing if help option is present. boolean hasNoEditDetails = !options.has(fixedPriceOpt) - && !options.has(mktPriceMarginOpt) + && !options.has(mktPriceMarginPctOpt) && !options.has(triggerPriceOpt) && !options.has(enableOpt); if (hasNoEditDetails) @@ -92,7 +89,7 @@ public EditOfferOptionParser parse() { // A single enable opt is a valid opt combo. boolean enableOptIsOnlyOpt = !options.has(fixedPriceOpt) - && !options.has(mktPriceMarginOpt) + && !options.has(mktPriceMarginPctOpt) && !options.has(triggerPriceOpt); if (enableOptIsOnlyOpt) { offerEditType = ACTIVATION_STATE_ONLY; @@ -107,7 +104,7 @@ public EditOfferOptionParser parse() { String fixedPriceAsString = options.valueOf(fixedPriceOpt); verifyStringIsValidDouble(fixedPriceAsString); - boolean fixedPriceOptIsOnlyOpt = !options.has(mktPriceMarginOpt) + boolean fixedPriceOptIsOnlyOpt = !options.has(mktPriceMarginPctOpt) && !options.has(triggerPriceOpt) && !options.has(enableOpt); if (fixedPriceOptIsOnlyOpt) { @@ -116,7 +113,7 @@ public EditOfferOptionParser parse() { } boolean fixedPriceOptAndEnableOptAreOnlyOpts = options.has(enableOpt) - && !options.has(mktPriceMarginOpt) + && !options.has(mktPriceMarginPctOpt) && !options.has(triggerPriceOpt); if (fixedPriceOptAndEnableOptAreOnlyOpts) { offerEditType = FIXED_PRICE_AND_ACTIVATION_STATE; @@ -124,15 +121,15 @@ public EditOfferOptionParser parse() { } } - if (options.has(mktPriceMarginOpt)) { - if (valueNotSpecified.test(mktPriceMarginOpt)) + if (options.has(mktPriceMarginPctOpt)) { + if (valueNotSpecified.test(mktPriceMarginPctOpt)) throw new IllegalArgumentException("no mkt price margin specified"); - String priceMarginAsString = options.valueOf(mktPriceMarginOpt); - if (priceMarginAsString.isEmpty()) + String priceMarginPctAsString = options.valueOf(mktPriceMarginPctOpt); + if (priceMarginPctAsString.isEmpty()) throw new IllegalArgumentException("no market price margin specified"); - verifyStringIsValidDouble(priceMarginAsString); + verifyStringIsValidDouble(priceMarginPctAsString); boolean mktPriceMarginOptIsOnlyOpt = !options.has(triggerPriceOpt) && !options.has(fixedPriceOpt) @@ -160,7 +157,7 @@ public EditOfferOptionParser parse() { verifyStringIsValidDouble(triggerPriceAsString); - boolean triggerPriceOptIsOnlyOpt = !options.has(mktPriceMarginOpt) + boolean triggerPriceOptIsOnlyOpt = !options.has(mktPriceMarginPctOpt) && !options.has(fixedPriceOpt) && !options.has(enableOpt); if (triggerPriceOptIsOnlyOpt) { @@ -168,7 +165,7 @@ public EditOfferOptionParser parse() { return this; } - boolean triggerPriceOptAndEnableOptAreOnlyOpts = !options.has(mktPriceMarginOpt) + boolean triggerPriceOptAndEnableOptAreOnlyOpts = !options.has(mktPriceMarginPctOpt) && !options.has(fixedPriceOpt) && options.has(enableOpt); if (triggerPriceOptAndEnableOptAreOnlyOpts) { @@ -177,18 +174,18 @@ public EditOfferOptionParser parse() { } } - if (options.has(mktPriceMarginOpt) && options.has(fixedPriceOpt)) + if (options.has(mktPriceMarginPctOpt) && options.has(fixedPriceOpt)) throw new IllegalArgumentException("cannot specify market price margin and fixed price"); if (options.has(fixedPriceOpt) && options.has(triggerPriceOpt)) throw new IllegalArgumentException("trigger price cannot be set on fixed price offers"); - if (options.has(mktPriceMarginOpt) && options.has(triggerPriceOpt) && !options.has(enableOpt)) { + if (options.has(mktPriceMarginPctOpt) && options.has(triggerPriceOpt) && !options.has(enableOpt)) { offerEditType = MKT_PRICE_MARGIN_AND_TRIGGER_PRICE; return this; } - if (options.has(mktPriceMarginOpt) && options.has(triggerPriceOpt) && options.has(enableOpt)) { + if (options.has(mktPriceMarginPctOpt) && options.has(triggerPriceOpt) && options.has(enableOpt)) { offerEditType = MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE; return this; } @@ -215,23 +212,19 @@ public String getTriggerPrice() { } } - public BigDecimal getTriggerPriceAsBigDecimal() { - return new BigDecimal(getTriggerPrice()); - } - public String getMktPriceMargin() { if (offerEditType.equals(MKT_PRICE_MARGIN_ONLY) || offerEditType.equals(MKT_PRICE_MARGIN_AND_ACTIVATION_STATE) || offerEditType.equals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE) || offerEditType.equals(MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE)) { - return isUsingMktPriceMargin() ? options.valueOf(mktPriceMarginOpt) : "0.00"; + return isUsingMktPriceMargin() ? options.valueOf(mktPriceMarginPctOpt) : "0.00"; } else { return "0.00"; } } - public BigDecimal getMktPriceMarginAsBigDecimal() { - return new BigDecimal(options.valueOf(mktPriceMarginOpt)); + public double getMktPriceMarginPct() { + return Double.parseDouble(options.valueOf(mktPriceMarginPctOpt)); } public boolean isUsingMktPriceMargin() { @@ -264,12 +257,4 @@ public Boolean isEnable() { public EditOfferRequest.EditType getOfferEditType() { return offerEditType; } - - private void verifyStringIsValidDouble(String string) { - try { - Double.valueOf(string); - } catch (NumberFormatException ex) { - throw new IllegalArgumentException(format("%s is not a number", string)); - } - } } diff --git a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java index 7214d8bc0f8..65b62f05838 100644 --- a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java +++ b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java @@ -29,11 +29,8 @@ import bisq.proto.grpc.GetOffersRequest; import bisq.proto.grpc.OfferInfo; -import java.math.BigDecimal; - import java.util.ArrayList; import java.util.List; -import java.util.function.Function; import static bisq.cli.CryptoCurrencyUtil.apiDoesSupportCryptoCurrency; import static bisq.proto.grpc.EditOfferRequest.EditType.ACTIVATION_STATE_ONLY; @@ -52,12 +49,6 @@ public class OffersServiceRequest { - private final Function scaledPriceStringRequestFormat = (price) -> { - BigDecimal factor = new BigDecimal(10).pow(4); - //noinspection BigDecimalMethodWithoutRoundingCalled - return new BigDecimal(price).divide(factor).toPlainString(); - }; - private final GrpcStubs grpcStubs; public OffersServiceRequest(GrpcStubs grpcStubs) { @@ -91,7 +82,7 @@ public OfferInfo createFixedPricedOffer(String direction, long amount, long minAmount, String fixedPrice, - double securityDeposit, + double securityDepositPct, String paymentAcctId, String makerFeeCurrencyCode) { return createOffer(direction, @@ -101,33 +92,10 @@ public OfferInfo createFixedPricedOffer(String direction, false, fixedPrice, 0.00, - securityDeposit, - paymentAcctId, - makerFeeCurrencyCode, - 0 /* no trigger price */); - } - - @SuppressWarnings("unused") - public OfferInfo createMarketBasedPricedOffer(String direction, - String currencyCode, - long amount, - long minAmount, - double marketPriceMargin, - double securityDeposit, - String paymentAcctId, - String makerFeeCurrencyCode, - long triggerPrice) { - return createOffer(direction, - currencyCode, - amount, - minAmount, - true, - "0", - marketPriceMargin, - securityDeposit, + securityDepositPct, paymentAcctId, makerFeeCurrencyCode, - triggerPrice); + "0" /* no trigger price */); } public OfferInfo createOffer(String direction, @@ -136,11 +104,11 @@ public OfferInfo createOffer(String direction, long minAmount, boolean useMarketBasedPrice, String fixedPrice, - double marketPriceMargin, - double securityDeposit, + double marketPriceMarginPct, + double securityDepositPct, String paymentAcctId, String makerFeeCurrencyCode, - long triggerPrice) { + String triggerPrice) { var request = CreateOfferRequest.newBuilder() .setDirection(direction) .setCurrencyCode(currencyCode) @@ -148,8 +116,8 @@ public OfferInfo createOffer(String direction, .setMinAmount(minAmount) .setUseMarketBasedPrice(useMarketBasedPrice) .setPrice(fixedPrice) - .setMarketPriceMargin(marketPriceMargin) - .setBuyerSecurityDeposit(securityDeposit) + .setMarketPriceMarginPct(marketPriceMarginPct) + .setBuyerSecurityDepositPct(securityDepositPct) .setPaymentAccountId(paymentAcctId) .setMakerFeeCurrencyCode(makerFeeCurrencyCode) .setTriggerPrice(triggerPrice) @@ -159,13 +127,13 @@ public OfferInfo createOffer(String direction, public void editOfferActivationState(String offerId, int enable) { var offer = getMyOffer(offerId); - var scaledPriceString = offer.getUseMarketBasedPrice() + var offerPrice = offer.getUseMarketBasedPrice() ? "0.00" - : scaledPriceStringRequestFormat.apply(offer.getPrice()); + : offer.getPrice(); editOffer(offerId, - scaledPriceString, + offerPrice, offer.getUseMarketBasedPrice(), - offer.getMarketPriceMargin(), + offer.getMarketPriceMarginPct(), offer.getTriggerPrice(), enable, ACTIVATION_STATE_ONLY); @@ -176,29 +144,29 @@ public void editOfferFixedPrice(String offerId, String rawPriceString) { editOffer(offerId, rawPriceString, false, - offer.getMarketPriceMargin(), + offer.getMarketPriceMarginPct(), offer.getTriggerPrice(), offer.getIsActivated() ? 1 : 0, FIXED_PRICE_ONLY); } - public void editOfferPriceMargin(String offerId, double marketPriceMargin) { + public void editOfferPriceMargin(String offerId, double marketPriceMarginPct) { var offer = getMyOffer(offerId); editOffer(offerId, "0.00", true, - marketPriceMargin, + marketPriceMarginPct, offer.getTriggerPrice(), offer.getIsActivated() ? 1 : 0, MKT_PRICE_MARGIN_ONLY); } - public void editOfferTriggerPrice(String offerId, long triggerPrice) { + public void editOfferTriggerPrice(String offerId, String triggerPrice) { var offer = getMyOffer(offerId); editOffer(offerId, "0.00", offer.getUseMarketBasedPrice(), - offer.getMarketPriceMargin(), + offer.getMarketPriceMarginPct(), triggerPrice, offer.getIsActivated() ? 1 : 0, TRIGGER_PRICE_ONLY); @@ -207,8 +175,8 @@ public void editOfferTriggerPrice(String offerId, long triggerPrice) { public void editOffer(String offerId, String scaledPriceString, boolean useMarketBasedPrice, - double marketPriceMargin, - long triggerPrice, + double marketPriceMarginPct, + String triggerPrice, int enable, EditOfferRequest.EditType editType) { // Take care when using this method directly: @@ -218,7 +186,7 @@ public void editOffer(String offerId, .setId(offerId) .setPrice(scaledPriceString) .setUseMarketBasedPrice(useMarketBasedPrice) - .setMarketPriceMargin(marketPriceMargin) + .setMarketPriceMarginPct(marketPriceMarginPct) .setTriggerPrice(triggerPrice) .setEnable(enable) .setEditType(editType) diff --git a/cli/src/main/java/bisq/cli/table/builder/AbstractTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/AbstractTableBuilder.java index 1484e2c1f93..71a95538ae7 100644 --- a/cli/src/main/java/bisq/cli/table/builder/AbstractTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/AbstractTableBuilder.java @@ -31,6 +31,8 @@ */ abstract class AbstractTableBuilder { + protected final Predicate isFiatOffer = (o) -> o.getBaseCurrencyCode().equals("BTC"); + protected final TableType tableType; protected final List protos; @@ -42,6 +44,4 @@ abstract class AbstractTableBuilder { } public abstract Table build(); - - protected final Predicate isFiatOffer = (o) -> o.getBaseCurrencyCode().equals("BTC"); } diff --git a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java index 7c70d1d0653..c13852ac11d 100644 --- a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java @@ -20,10 +20,7 @@ import bisq.proto.grpc.ContractInfo; import bisq.proto.grpc.TradeInfo; -import java.text.DecimalFormat; - import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.List; import java.util.function.Function; @@ -33,16 +30,17 @@ import javax.annotation.Nullable; +import static bisq.cli.CurrencyFormat.formatSatoshis; import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_BUYER_DEPOSIT; import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_SELLER_DEPOSIT; import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL; +import static java.lang.String.format; import static protobuf.OfferDirection.SELL; import bisq.cli.table.column.Column; import bisq.cli.table.column.MixedTradeFeeColumn; -import bisq.cli.table.column.MixedVolumeColumn; abstract class AbstractTradeListBuilder extends AbstractTableBuilder { @@ -55,15 +53,15 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder { protected final Column colCreateDate; @Nullable protected final Column colMarket; - protected final Column colPrice; + protected final Column colPrice; @Nullable protected final Column colPriceDeviation; @Nullable protected final Column colCurrency; @Nullable - protected final Column colAmountInBtc; + protected final Column colAmount; @Nullable - protected final MixedVolumeColumn colMixedAmount; + protected final Column colMixedAmount; @Nullable protected final Column colMinerTxFee; @Nullable @@ -94,7 +92,8 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder { @Nullable protected final Column colBisqTradeFee; @Nullable - protected final Column colTradeCost; + protected final Column colTradeCost; + //protected final Column colTradeCost; @Nullable protected final Column colIsPaymentSent; @Nullable @@ -124,7 +123,7 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder { this.colPrice = colSupplier.priceColumn.get(); this.colPriceDeviation = colSupplier.priceDeviationColumn.get(); this.colCurrency = colSupplier.currencyColumn.get(); - this.colAmountInBtc = colSupplier.amountInBtcColumn.get(); + this.colAmount = colSupplier.amountColumn.get(); this.colMixedAmount = colSupplier.mixedAmountColumn.get(); this.colMinerTxFee = colSupplier.minerTxFeeColumn.get(); this.colMixedTradeFee = colSupplier.mixedTradeFeeColumn.get(); @@ -145,6 +144,7 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder { this.colTradeCost = colSupplier.tradeCostColumn.get(); this.colIsPaymentSent = colSupplier.paymentSentColumn.get(); this.colIsPaymentReceived = colSupplier.paymentReceivedColumn.get(); + //noinspection ConstantConditions this.colAltcoinReceiveAddressColumn = colSupplier.altcoinReceiveAddressColumn.get(); // BSQ swap trade detail specific columns @@ -167,6 +167,7 @@ protected void validate() { private final Supplier isTradeDetailTblBuilder = () -> tableType.equals(TRADE_DETAIL_TBL); protected final Predicate isFiatTrade = (t) -> isFiatOffer.test(t.getOffer()); + protected final Predicate isBsqTrade = (t) -> !isFiatOffer.test(t.getOffer()) && t.getOffer().getBaseCurrencyCode().equals("BSQ"); protected final Predicate isBsqSwapTrade = (t) -> t.getOffer().getIsBsqSwapOffer(); protected final Predicate isMyOffer = (t) -> t.getOffer().getIsMyOffer(); protected final Predicate isTaker = (t) -> t.getRole().toLowerCase().contains("taker"); @@ -180,15 +181,28 @@ protected void validate() { // Column Value Functions - protected final Function toAmount = (t) -> - isFiatTrade.test(t) - ? t.getTradeAmountAsLong() - : t.getTradeVolume(); + // Altcoin volumes from server are string representations of decimals. + // Converting them to longs ("sats") requires shifting the decimal points + // to left: 2 for BSQ, 8 for other altcoins. + protected final Function toAltcoinTradeVolumeAsLong = (t) -> + isBsqTrade.test(t) + ? new BigDecimal(t.getTradeVolume()).movePointRight(2).longValue() + : new BigDecimal(t.getTradeVolume()).movePointRight(8).longValue(); - protected final Function toTradeVolume = (t) -> + protected final Function toTradeVolumeAsString = (t) -> isFiatTrade.test(t) ? t.getTradeVolume() - : t.getTradeAmountAsLong(); + : formatSatoshis(t.getTradeAmountAsLong()); + + protected final Function toTradeVolumeAsLong = (t) -> + isFiatTrade.test(t) + ? Long.parseLong(t.getTradeVolume()) + : toAltcoinTradeVolumeAsLong.apply(t); + + protected final Function toTradeAmount = (t) -> + isFiatTrade.test(t) + ? t.getTradeAmountAsLong() + : toTradeVolumeAsLong.apply(t); protected final Function toMarket = (t) -> t.getOffer().getBaseCurrencyCode() + "/" @@ -199,19 +213,9 @@ protected void validate() { ? t.getOffer().getCounterCurrencyCode() : t.getOffer().getBaseCurrencyCode(); - - protected final Function toDisplayedVolumePrecision = (t) -> { - if (isFiatTrade.test(t)) { - return 0; - } else { - String currencyCode = toPaymentCurrencyCode.apply(t); - return currencyCode.equalsIgnoreCase("BSQ") ? 2 : 8; - } - }; - protected final Function toPriceDeviation = (t) -> t.getOffer().getUseMarketBasedPrice() - ? formatToPercent(t.getOffer().getMarketPriceMargin()) + ? format("%.2f%s", t.getOffer().getMarketPriceMarginPct(), "%") : "N/A"; protected final Function toMyMinerTxFee = (t) -> { @@ -300,38 +304,4 @@ protected void validate() { return ""; } }; - - // TODO Move to bisq/cli/CurrencyFormat.java ? - - public static String formatToPercent(double value) { - DecimalFormat decimalFormat = new DecimalFormat("#.##"); - decimalFormat.setMinimumFractionDigits(2); - decimalFormat.setMaximumFractionDigits(2); - return formatToPercent(value, decimalFormat); - } - - public static String formatToPercent(double value, DecimalFormat decimalFormat) { - return decimalFormat.format(roundDouble(value * 100.0, 2)).replace(",", ".") + "%"; - } - - public static double roundDouble(double value, int precision) { - return roundDouble(value, precision, RoundingMode.HALF_UP); - } - - @SuppressWarnings("SameParameterValue") - public static double roundDouble(double value, int precision, RoundingMode roundingMode) { - if (precision < 0) - throw new IllegalArgumentException(); - if (!Double.isFinite(value)) - throw new IllegalArgumentException("Expected a finite double, but found " + value); - - try { - BigDecimal bd = BigDecimal.valueOf(value); - bd = bd.setScale(precision, roundingMode); - return bd.doubleValue(); - } catch (Throwable t) { - t.printStackTrace(); // TODO throw pretty exception for CLI console - return 0; - } - } } diff --git a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java index 261cddc1117..999b864abc0 100644 --- a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java @@ -24,8 +24,8 @@ import bisq.cli.table.Table; -import bisq.cli.table.column.MixedPriceColumn; +@SuppressWarnings("ConstantConditions") class ClosedTradeTableBuilder extends AbstractTradeListBuilder { ClosedTradeTableBuilder(List protos) { @@ -37,10 +37,10 @@ public Table build() { return new Table(colTradeId, colCreateDate.asStringColumn(), colMarket, - colPrice.asStringColumn(), + colPrice.justify(), colPriceDeviation.justify(), - colAmountInBtc.asStringColumn(), - colMixedAmount.asStringColumn(), + colAmount.asStringColumn(), + colMixedAmount.justify(), colCurrency, colMinerTxFee.asStringColumn(), colMixedTradeFee.asStringColumn(), @@ -51,14 +51,14 @@ public Table build() { } private void populateColumns() { - trades.stream().forEachOrdered(t -> { + trades.forEach(t -> { colTradeId.addRow(t.getTradeId()); colCreateDate.addRow(t.getDate()); colMarket.addRow(toMarket.apply(t)); - ((MixedPriceColumn) colPrice).addRow(t.getTradePrice(), isFiatTrade.test(t)); + colPrice.addRow(t.getTradePrice()); colPriceDeviation.addRow(toPriceDeviation.apply(t)); - colAmountInBtc.addRow(t.getTradeAmountAsLong()); - colMixedAmount.addRow(t.getTradeVolume(), toDisplayedVolumePrecision.apply(t)); + colAmount.addRow(t.getTradeAmountAsLong()); + colMixedAmount.addRow(t.getTradeVolume()); colCurrency.addRow(toPaymentCurrencyCode.apply(t)); colMinerTxFee.addRow(toMyMinerTxFee.apply(t)); diff --git a/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java index 13b4399070a..6a10519154f 100644 --- a/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java @@ -24,11 +24,11 @@ import bisq.cli.table.Table; -import bisq.cli.table.column.MixedPriceColumn; /** * Builds a {@code bisq.cli.table.Table} from a list of {@code bisq.proto.grpc.TradeInfo} objects. */ +@SuppressWarnings("ConstantConditions") class FailedTradeTableBuilder extends AbstractTradeListBuilder { FailedTradeTableBuilder(List protos) { @@ -40,9 +40,9 @@ public Table build() { return new Table(colTradeId, colCreateDate.asStringColumn(), colMarket, - colPrice.asStringColumn(), - colAmountInBtc.asStringColumn(), - colMixedAmount.asStringColumn(), + colPrice.justify(), + colAmount.asStringColumn(), + colMixedAmount.justify(), colCurrency, colOfferType, colRole, @@ -50,13 +50,13 @@ public Table build() { } private void populateColumns() { - trades.stream().forEachOrdered(t -> { + trades.forEach(t -> { colTradeId.addRow(t.getTradeId()); colCreateDate.addRow(t.getDate()); colMarket.addRow(toMarket.apply(t)); - ((MixedPriceColumn) colPrice).addRow(t.getTradePrice(), isFiatTrade.test(t)); - colAmountInBtc.addRow(t.getTradeAmountAsLong()); - colMixedAmount.addRow(t.getTradeVolume(), toDisplayedVolumePrecision.apply(t)); + colPrice.addRow(t.getTradePrice()); + colAmount.addRow(t.getTradeAmountAsLong()); + colMixedAmount.addRow(t.getTradeVolume()); colCurrency.addRow(toPaymentCurrencyCode.apply(t)); colOfferType.addRow(toOfferType.apply(t)); colRole.addRow(t.getRole()); diff --git a/cli/src/main/java/bisq/cli/table/builder/OfferTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/OfferTableBuilder.java index 08d19c09106..3ec7aa23c86 100644 --- a/cli/src/main/java/bisq/cli/table/builder/OfferTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/OfferTableBuilder.java @@ -28,13 +28,9 @@ import static bisq.cli.table.builder.TableBuilderConstants.*; import static bisq.cli.table.builder.TableType.OFFER_TBL; -import static bisq.cli.table.column.AltcoinColumn.DISPLAY_MODE.ALTCOIN_OFFER_VOLUME; -import static bisq.cli.table.column.AltcoinColumn.DISPLAY_MODE.ALTCOIN_TRIGGER_PRICE; import static bisq.cli.table.column.Column.JUSTIFICATION.LEFT; import static bisq.cli.table.column.Column.JUSTIFICATION.NONE; import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT; -import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.TRIGGER_PRICE; -import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.VOLUME; import static bisq.cli.table.column.ZippedStringColumns.DUPLICATION_MODE.EXCLUDE_DUPLICATES; import static java.lang.String.format; import static protobuf.OfferDirection.BUY; @@ -43,9 +39,7 @@ import bisq.cli.table.Table; -import bisq.cli.table.column.AltcoinColumn; import bisq.cli.table.column.Column; -import bisq.cli.table.column.FiatColumn; import bisq.cli.table.column.Iso8601DateTimeColumn; import bisq.cli.table.column.SatoshiColumn; import bisq.cli.table.column.StringColumn; @@ -79,17 +73,16 @@ public Table build() { @SuppressWarnings("ConstantConditions") public Table buildFiatOfferTable(List offers) { @Nullable - Column colEnabled = enabledColumn.get(); // Not boolean: YES, NO, or PENDING - Column colFiatPrice = new FiatColumn(format(COL_HEADER_DETAILED_PRICE, fiatTradeCurrency.get())); - Column colFiatVolume = new FiatColumn(format("Temp Volume (%s)", fiatTradeCurrency.get()), NONE, VOLUME); - Column colMinFiatVolume = new FiatColumn(format("Temp Min Volume (%s)", fiatTradeCurrency.get()), NONE, VOLUME); + Column colEnabled = enabledColumn.get(); // Not boolean: "YES", "NO", or "PENDING" + Column colFiatPrice = new StringColumn(format(COL_HEADER_DETAILED_PRICE, fiatTradeCurrency.get()), RIGHT); + Column colVolume = new StringColumn(format("Temp Volume (%s)", fiatTradeCurrency.get()), NONE); + Column colMinVolume = new StringColumn(format("Temp Min Volume (%s)", fiatTradeCurrency.get()), NONE); @Nullable - Column colTriggerPrice = fiatTriggerPriceColumn.get(); + Column colTriggerPrice = fiatTriggerPriceColumn.get(); // Populate columns with offer info. - //noinspection SimplifyStreamApiCallChains - offers.stream().forEachOrdered(o -> { + offers.forEach(o -> { if (colEnabled != null) colEnabled.addRow(toEnabled.apply(o)); @@ -97,11 +90,11 @@ public Table buildFiatOfferTable(List offers) { colFiatPrice.addRow(o.getPrice()); colMinAmount.addRow(o.getMinAmount()); colAmount.addRow(o.getAmount()); - colMinFiatVolume.addRow(o.getMinVolume()); - colFiatVolume.addRow(o.getVolume()); + colVolume.addRow(o.getVolume()); + colMinVolume.addRow(o.getMinVolume()); if (colTriggerPrice != null) - colTriggerPrice.addRow(o.getTriggerPrice()); + colTriggerPrice.addRow(toBlankOrNonZeroValue.apply(o.getTriggerPrice())); colPaymentMethod.addRow(o.getPaymentMethodShortName()); colCreateDate.addRow(o.getDate()); @@ -113,24 +106,24 @@ public Table buildFiatOfferTable(List offers) { new ZippedStringColumns(format(COL_HEADER_VOLUME_RANGE, fiatTradeCurrency.get()), RIGHT, " - ", - colMinFiatVolume.asStringColumn(), - colFiatVolume.asStringColumn()); + colMinVolume.asStringColumn(), + colVolume.asStringColumn()); // Define and return the table instance with populated columns. if (isShowingMyOffers.get()) { return new Table(colEnabled.asStringColumn(), colDirection, - colFiatPrice.asStringColumn(), + colFiatPrice.justify(), amountRange.asStringColumn(EXCLUDE_DUPLICATES), volumeRange.asStringColumn(EXCLUDE_DUPLICATES), - colTriggerPrice.asStringColumn(), + colTriggerPrice.justify(), colPaymentMethod, colCreateDate.asStringColumn(), colOfferId); } else { return new Table(colDirection, - colFiatPrice.asStringColumn(), + colFiatPrice.justify(), amountRange.asStringColumn(EXCLUDE_DUPLICATES), volumeRange.asStringColumn(EXCLUDE_DUPLICATES), colPaymentMethod, @@ -143,20 +136,15 @@ public Table buildFiatOfferTable(List offers) { public Table buildCryptoCurrencyOfferTable(List offers) { @Nullable Column colEnabled = enabledColumn.get(); // Not boolean: YES, NO, or PENDING - Column colBtcPrice = new SatoshiColumn(format(COL_HEADER_DETAILED_PRICE_OF_ALTCOIN, altcoinTradeCurrency.get())); - Column colBtcVolume = new AltcoinColumn(format("Temp Volume (%s)", altcoinTradeCurrency.get()), - NONE, - ALTCOIN_OFFER_VOLUME); - Column colMinBtcVolume = new AltcoinColumn(format("Temp Min Volume (%s)", altcoinTradeCurrency.get()), - NONE, - ALTCOIN_OFFER_VOLUME); + Column colBtcPrice = new StringColumn(format(COL_HEADER_DETAILED_PRICE_OF_ALTCOIN, altcoinTradeCurrency.get()), RIGHT); + Column colVolume = new StringColumn(format("Temp Volume (%s)", altcoinTradeCurrency.get()), NONE); + Column colMinVolume = new StringColumn(format("Temp Min Volume (%s)", altcoinTradeCurrency.get()), NONE); @Nullable - Column colTriggerPrice = altcoinTriggerPriceColumn.get(); + Column colTriggerPrice = altcoinTriggerPriceColumn.get(); // Populate columns with offer info. - //noinspection SimplifyStreamApiCallChains - offers.stream().forEachOrdered(o -> { + offers.forEach(o -> { if (colEnabled != null) colEnabled.addRow(toEnabled.apply(o)); @@ -164,11 +152,11 @@ public Table buildCryptoCurrencyOfferTable(List offers) { colBtcPrice.addRow(o.getPrice()); colAmount.addRow(o.getAmount()); colMinAmount.addRow(o.getMinAmount()); - colBtcVolume.addRow(o.getMinVolume()); - colMinBtcVolume.addRow(o.getVolume()); + colVolume.addRow(o.getVolume()); + colMinVolume.addRow(o.getMinVolume()); if (colTriggerPrice != null) - colTriggerPrice.addRow(o.getTriggerPrice()); + colTriggerPrice.addRow(toBlankOrNonZeroValue.apply(o.getTriggerPrice())); colPaymentMethod.addRow(o.getPaymentMethodShortName()); colCreateDate.addRow(o.getDate()); @@ -180,8 +168,8 @@ public Table buildCryptoCurrencyOfferTable(List offers) { new ZippedStringColumns(format(COL_HEADER_VOLUME_RANGE, altcoinTradeCurrency.get()), RIGHT, " - ", - colBtcVolume.asStringColumn(), - colMinBtcVolume.asStringColumn()); + colMinVolume.asStringColumn(), + colVolume.asStringColumn()); // Define and return the table instance with populated columns. @@ -189,7 +177,7 @@ public Table buildCryptoCurrencyOfferTable(List offers) { if (isShowingBsqOffers.get()) { return new Table(colEnabled.asStringColumn(), colDirection, - colBtcPrice.asStringColumn(), + colBtcPrice.justify(), amountRange.asStringColumn(EXCLUDE_DUPLICATES), volumeRange.asStringColumn(EXCLUDE_DUPLICATES), colPaymentMethod, @@ -198,17 +186,17 @@ public Table buildCryptoCurrencyOfferTable(List offers) { } else { return new Table(colEnabled.asStringColumn(), colDirection, - colBtcPrice.asStringColumn(), + colBtcPrice.justify(), amountRange.asStringColumn(EXCLUDE_DUPLICATES), volumeRange.asStringColumn(EXCLUDE_DUPLICATES), - colTriggerPrice.asStringColumn(), + colTriggerPrice.justify(), colPaymentMethod, colCreateDate.asStringColumn(), colOfferId); } } else { return new Table(colDirection, - colBtcPrice.asStringColumn(), + colBtcPrice.justify(), amountRange.asStringColumn(EXCLUDE_DUPLICATES), volumeRange.asStringColumn(EXCLUDE_DUPLICATES), colPaymentMethod, @@ -217,6 +205,7 @@ public Table buildCryptoCurrencyOfferTable(List offers) { } } + private final Function toBlankOrNonZeroValue = (s) -> s.trim().equals("0") ? "" : s; private final Supplier firstOfferInList = () -> (OfferInfo) protos.get(0); private final Supplier isShowingMyOffers = () -> firstOfferInList.get().getIsMyOffer(); private final Supplier isShowingFiatOffers = () -> isFiatOffer.test(firstOfferInList.get()); @@ -231,14 +220,14 @@ public Table buildCryptoCurrencyOfferTable(List offers) { ? new StringColumn(COL_HEADER_ENABLED, LEFT) : null; @Nullable - private final Supplier fiatTriggerPriceColumn = () -> + private final Supplier fiatTriggerPriceColumn = () -> isShowingMyOffers.get() - ? new FiatColumn(format(COL_HEADER_TRIGGER_PRICE, fiatTradeCurrency.get()), RIGHT, TRIGGER_PRICE) + ? new StringColumn(format(COL_HEADER_TRIGGER_PRICE, fiatTradeCurrency.get()), RIGHT) : null; @Nullable - private final Supplier altcoinTriggerPriceColumn = () -> + private final Supplier altcoinTriggerPriceColumn = () -> isShowingMyOffers.get() && !isShowingBsqOffers.get() - ? new AltcoinColumn(format(COL_HEADER_TRIGGER_PRICE, altcoinTradeCurrency.get()), RIGHT, ALTCOIN_TRIGGER_PRICE) + ? new StringColumn(format(COL_HEADER_TRIGGER_PRICE, altcoinTradeCurrency.get()), RIGHT) : null; private final Function toEnabled = (o) -> { diff --git a/cli/src/main/java/bisq/cli/table/builder/OpenTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/OpenTradeTableBuilder.java index b9c3f7df5da..63a960d9b84 100644 --- a/cli/src/main/java/bisq/cli/table/builder/OpenTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/OpenTradeTableBuilder.java @@ -24,11 +24,11 @@ import bisq.cli.table.Table; -import bisq.cli.table.column.MixedPriceColumn; /** * Builds a {@code bisq.cli.table.Table} from a list of {@code bisq.proto.grpc.TradeInfo} objects. */ +@SuppressWarnings("ConstantConditions") class OpenTradeTableBuilder extends AbstractTradeListBuilder { OpenTradeTableBuilder(List protos) { @@ -40,22 +40,22 @@ public Table build() { return new Table(colTradeId, colCreateDate.asStringColumn(), colMarket, - colPrice.asStringColumn(), - colAmountInBtc.asStringColumn(), - colMixedAmount.asStringColumn(), + colPrice.justify(), + colAmount.asStringColumn(), + colMixedAmount.justify(), colCurrency, colPaymentMethod, colRole); } private void populateColumns() { - trades.stream().forEachOrdered(t -> { + trades.forEach(t -> { colTradeId.addRow(t.getTradeId()); colCreateDate.addRow(t.getDate()); colMarket.addRow(toMarket.apply(t)); - ((MixedPriceColumn) colPrice).addRow(t.getTradePrice(), isFiatTrade.test(t)); - colAmountInBtc.addRow(t.getTradeAmountAsLong()); - colMixedAmount.addRow(t.getTradeVolume(), toDisplayedVolumePrecision.apply(t)); + colPrice.addRow(t.getTradePrice()); + colAmount.addRow(t.getTradeAmountAsLong()); + colMixedAmount.addRow(t.getTradeVolume()); colCurrency.addRow(toPaymentCurrencyCode.apply(t)); colPaymentMethod.addRow(t.getOffer().getPaymentMethodShortName()); colRole.addRow(t.getRole()); diff --git a/cli/src/main/java/bisq/cli/table/builder/TradeDetailTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/TradeDetailTableBuilder.java index 9d577ca1f6d..14dbef9bfb6 100644 --- a/cli/src/main/java/bisq/cli/table/builder/TradeDetailTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/TradeDetailTableBuilder.java @@ -77,12 +77,12 @@ private void populateBisqV1TradeColumns(TradeInfo trade) { colTradeId.addRow(trade.getShortId()); colRole.addRow(trade.getRole()); colPrice.addRow(trade.getTradePrice()); - colAmountInBtc.addRow(toAmount.apply(trade)); + colAmount.addRow(toTradeAmount.apply(trade)); colMinerTxFee.addRow(toMyMinerTxFee.apply(trade)); colBisqTradeFee.addRow(toMyMakerOrTakerFee.apply(trade)); colIsDepositPublished.addRow(trade.getIsDepositPublished()); colIsDepositConfirmed.addRow(trade.getIsDepositConfirmed()); - colTradeCost.addRow(toTradeVolume.apply(trade)); + colTradeCost.addRow(toTradeVolumeAsString.apply(trade)); colIsPaymentSent.addRow(trade.getIsFiatSent()); colIsPaymentReceived.addRow(trade.getIsFiatReceived()); colIsPayoutPublished.addRow(trade.getIsPayoutPublished()); @@ -95,10 +95,11 @@ private void populateBsqSwapTradeColumns(TradeInfo trade) { colTradeId.addRow(trade.getShortId()); colRole.addRow(trade.getRole()); colPrice.addRow(trade.getTradePrice()); - colAmountInBtc.addRow(toAmount.apply(trade)); + colAmount.addRow(toTradeAmount.apply(trade)); colMinerTxFee.addRow(toMyMinerTxFee.apply(trade)); colBisqTradeFee.addRow(toMyMakerOrTakerFee.apply(trade)); - colTradeCost.addRow(toTradeVolume.apply(trade)); + + colTradeCost.addRow(toTradeVolumeAsString.apply(trade)); var isCompleted = isCompletedBsqSwap.test(trade); status.addRow(isCompleted ? "COMPLETED" : "PENDING"); @@ -118,13 +119,13 @@ private List> getBisqV1TradeColumnList() { List> columns = new ArrayList<>() {{ add(colTradeId); add(colRole); - add(colPrice.asStringColumn()); - add(colAmountInBtc.asStringColumn()); + add(colPrice.justify()); + add(colAmount.asStringColumn()); add(colMinerTxFee.asStringColumn()); add(colBisqTradeFee.asStringColumn()); add(colIsDepositPublished.asStringColumn()); add(colIsDepositConfirmed.asStringColumn()); - add(colTradeCost.asStringColumn()); + add(colTradeCost.justify()); add(colIsPaymentSent.asStringColumn()); add(colIsPaymentReceived.asStringColumn()); add(colIsPayoutPublished.asStringColumn()); @@ -141,11 +142,11 @@ private List> getBsqSwapTradeColumnList(boolean isCompleted) { List> columns = new ArrayList<>() {{ add(colTradeId); add(colRole); - add(colPrice.asStringColumn()); - add(colAmountInBtc.asStringColumn()); + add(colPrice.justify()); + add(colAmount.asStringColumn()); add(colMinerTxFee.asStringColumn()); add(colBisqTradeFee.asStringColumn()); - add(colTradeCost.asStringColumn()); + add(colTradeCost.justify()); add(status); }}; diff --git a/cli/src/main/java/bisq/cli/table/builder/TradeTableColumnSupplier.java b/cli/src/main/java/bisq/cli/table/builder/TradeTableColumnSupplier.java index 9f3155fa2c1..ec9c51e2fec 100644 --- a/cli/src/main/java/bisq/cli/table/builder/TradeTableColumnSupplier.java +++ b/cli/src/main/java/bisq/cli/table/builder/TradeTableColumnSupplier.java @@ -36,24 +36,21 @@ import static bisq.cli.table.builder.TableType.FAILED_TRADES_TBL; import static bisq.cli.table.builder.TableType.OPEN_TRADES_TBL; import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL; -import static bisq.cli.table.column.AltcoinColumn.DISPLAY_MODE.ALTCOIN_OFFER_VOLUME; +import static bisq.cli.table.column.AltcoinVolumeColumn.DISPLAY_MODE.ALTCOIN_VOLUME; +import static bisq.cli.table.column.AltcoinVolumeColumn.DISPLAY_MODE.BSQ_VOLUME; import static bisq.cli.table.column.Column.JUSTIFICATION.LEFT; import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT; -import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.VOLUME; import static java.lang.String.format; -import bisq.cli.table.column.AltcoinColumn; +import bisq.cli.table.column.AltcoinVolumeColumn; import bisq.cli.table.column.BooleanColumn; import bisq.cli.table.column.BtcColumn; import bisq.cli.table.column.Column; -import bisq.cli.table.column.FiatColumn; import bisq.cli.table.column.Iso8601DateTimeColumn; import bisq.cli.table.column.LongColumn; -import bisq.cli.table.column.MixedPriceColumn; import bisq.cli.table.column.MixedTradeFeeColumn; -import bisq.cli.table.column.MixedVolumeColumn; import bisq.cli.table.column.SatoshiColumn; import bisq.cli.table.column.StringColumn; @@ -98,18 +95,16 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { ? null : new StringColumn(COL_HEADER_MARKET); - private final Function> toDetailedPriceColumn = (t) -> { + private final Function> toDetailedPriceColumn = (t) -> { String colHeader = isFiatTrade.test(t) ? format(COL_HEADER_DETAILED_PRICE, t.getOffer().getCounterCurrencyCode()) : format(COL_HEADER_DETAILED_PRICE_OF_ALTCOIN, t.getOffer().getBaseCurrencyCode()); - return isFiatTrade.test(t) - ? new FiatColumn(colHeader) - : new AltcoinColumn(colHeader); + return new StringColumn(colHeader, RIGHT); }; - final Supplier> priceColumn = () -> isTradeDetailTblBuilder.get() + final Supplier> priceColumn = () -> isTradeDetailTblBuilder.get() ? toDetailedPriceColumn.apply(firstRow.get()) - : new MixedPriceColumn(COL_HEADER_PRICE); + : new StringColumn(COL_HEADER_PRICE, RIGHT); final Supplier> priceDeviationColumn = () -> isTradeDetailTblBuilder.get() ? null @@ -122,18 +117,21 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { private final Function> toDetailedAmountColumn = (t) -> { String headerCurrencyCode = t.getOffer().getBaseCurrencyCode(); String colHeader = format(COL_HEADER_DETAILED_AMOUNT, headerCurrencyCode); + AltcoinVolumeColumn.DISPLAY_MODE displayMode = headerCurrencyCode.equals("BSQ") ? BSQ_VOLUME : ALTCOIN_VOLUME; return isFiatTrade.test(t) ? new SatoshiColumn(colHeader) - : new AltcoinColumn(colHeader, ALTCOIN_OFFER_VOLUME); + : new AltcoinVolumeColumn(colHeader, displayMode); }; - final Supplier> amountInBtcColumn = () -> isTradeDetailTblBuilder.get() + // Can be fiat, btc or altcoin amount represented as longs. Placing the decimal + // in the displayed string representation is done in the Column implementation. + final Supplier> amountColumn = () -> isTradeDetailTblBuilder.get() ? toDetailedAmountColumn.apply(firstRow.get()) : new BtcColumn(COL_HEADER_AMOUNT_IN_BTC); - final Supplier mixedAmountColumn = () -> isTradeDetailTblBuilder.get() + final Supplier mixedAmountColumn = () -> isTradeDetailTblBuilder.get() ? null - : new MixedVolumeColumn(COL_HEADER_AMOUNT); + : new StringColumn(COL_HEADER_AMOUNT, RIGHT); final Supplier> minerTxFeeColumn = () -> isTradeDetailTblBuilder.get() || isClosedTradeTblBuilder.get() ? new SatoshiColumn(COL_HEADER_TX_FEE) @@ -248,14 +246,12 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { } }; - final Supplier> tradeCostColumn = () -> { + final Supplier> tradeCostColumn = () -> { if (isTradeDetailTblBuilder.get()) { TradeInfo t = firstRow.get(); String headerCurrencyCode = t.getOffer().getCounterCurrencyCode(); String colHeader = format(COL_HEADER_TRADE_BUYER_COST, headerCurrencyCode); - return isFiatTrade.test(t) - ? new FiatColumn(colHeader, VOLUME) - : new SatoshiColumn(colHeader); + return new StringColumn(colHeader, RIGHT); } else { return null; } diff --git a/cli/src/main/java/bisq/cli/table/column/AltcoinColumn.java b/cli/src/main/java/bisq/cli/table/column/AltcoinVolumeColumn.java similarity index 66% rename from cli/src/main/java/bisq/cli/table/column/AltcoinColumn.java rename to cli/src/main/java/bisq/cli/table/column/AltcoinVolumeColumn.java index 4c59eb03324..0db6c381f4b 100644 --- a/cli/src/main/java/bisq/cli/table/column/AltcoinColumn.java +++ b/cli/src/main/java/bisq/cli/table/column/AltcoinVolumeColumn.java @@ -17,41 +17,33 @@ package bisq.cli.table.column; +import java.math.BigDecimal; + import java.util.function.BiFunction; import java.util.stream.IntStream; -import static bisq.cli.CurrencyFormat.formatCryptoCurrencyPrice; -import static bisq.cli.CurrencyFormat.formatCryptoCurrencyVolume; -import static bisq.cli.table.column.AltcoinColumn.DISPLAY_MODE.ALTCOIN_PRICE; import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT; /** - * For displaying altcoin values as volume, price, or optional trigger price - * with appropriate precision. + * For displaying altcoin volume with appropriate precision. */ -public class AltcoinColumn extends LongColumn { +public class AltcoinVolumeColumn extends LongColumn { public enum DISPLAY_MODE { - ALTCOIN_OFFER_VOLUME, - ALTCOIN_PRICE, - ALTCOIN_TRIGGER_PRICE + ALTCOIN_VOLUME, + BSQ_VOLUME, } private final DISPLAY_MODE displayMode; - // The default AltcoinColumn JUSTIFICATION is RIGHT. - // The default AltcoinColumn DISPLAY_MODE is ALTCOIN_PRICE. - public AltcoinColumn(String name) { - this(name, RIGHT, ALTCOIN_PRICE); - } - - public AltcoinColumn(String name, DISPLAY_MODE displayMode) { + // The default AltcoinVolumeColumn JUSTIFICATION is RIGHT. + public AltcoinVolumeColumn(String name, DISPLAY_MODE displayMode) { this(name, RIGHT, displayMode); } - public AltcoinColumn(String name, - JUSTIFICATION justification, - DISPLAY_MODE displayMode) { + public AltcoinVolumeColumn(String name, + JUSTIFICATION justification, + DISPLAY_MODE displayMode) { super(name, justification); this.displayMode = displayMode; } @@ -86,11 +78,10 @@ public StringColumn asStringColumn() { private final BiFunction toFormattedString = (value, displayMode) -> { switch (displayMode) { - case ALTCOIN_OFFER_VOLUME: - return value > 0 ? formatCryptoCurrencyVolume(value) : ""; - case ALTCOIN_PRICE: - case ALTCOIN_TRIGGER_PRICE: - return value > 0 ? formatCryptoCurrencyPrice(value) : ""; + case ALTCOIN_VOLUME: + return value > 0 ? new BigDecimal(value).movePointLeft(8).toString() : ""; + case BSQ_VOLUME: + return value > 0 ? new BigDecimal(value).movePointLeft(2).toString() : ""; default: throw new IllegalStateException("invalid display mode: " + displayMode); } diff --git a/cli/src/main/java/bisq/cli/table/column/FiatColumn.java b/cli/src/main/java/bisq/cli/table/column/FiatColumn.java index e9b940ee9d7..d115ab82fe2 100644 --- a/cli/src/main/java/bisq/cli/table/column/FiatColumn.java +++ b/cli/src/main/java/bisq/cli/table/column/FiatColumn.java @@ -22,19 +22,16 @@ import static bisq.cli.CurrencyFormat.formatFiatVolume; import static bisq.cli.CurrencyFormat.formatPrice; import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT; -import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.PRICE; -import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.TRIGGER_PRICE; +import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.FIAT_PRICE; /** - * For displaying fiat values as volume, price, or optional trigger price - * with appropriate precision. + * For displaying fiat volume or price with appropriate precision. */ public class FiatColumn extends LongColumn { public enum DISPLAY_MODE { - PRICE, - TRIGGER_PRICE, - VOLUME + FIAT_PRICE, + FIAT_VOLUME } private final DISPLAY_MODE displayMode; @@ -42,7 +39,7 @@ public enum DISPLAY_MODE { // The default FiatColumn JUSTIFICATION is RIGHT. // The default FiatColumn DISPLAY_MODE is PRICE. public FiatColumn(String name) { - this(name, RIGHT, PRICE); + this(name, RIGHT, FIAT_PRICE); } public FiatColumn(String name, DISPLAY_MODE displayMode) { @@ -60,11 +57,7 @@ public FiatColumn(String name, public void addRow(Long value) { rows.add(value); - String s; - if (displayMode.equals(TRIGGER_PRICE)) - s = value > 0 ? formatPrice(value) : ""; - else - s = displayMode.equals(PRICE) ? formatPrice(value) : formatFiatVolume(value); + String s = displayMode.equals(FIAT_PRICE) ? formatPrice(value) : formatFiatVolume(value); stringColumn.addRow(s); diff --git a/cli/src/main/java/bisq/cli/table/column/MixedPriceColumn.java b/cli/src/main/java/bisq/cli/table/column/MixedPriceColumn.java deleted file mode 100644 index d8beaf80feb..00000000000 --- a/cli/src/main/java/bisq/cli/table/column/MixedPriceColumn.java +++ /dev/null @@ -1,57 +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 . - */ - -package bisq.cli.table.column; - -import static bisq.cli.CurrencyFormat.formatPrice; -import static bisq.cli.CurrencyFormat.formatSatoshis; -import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT; - -/** - * For displaying a mix of fiat and altcoin prices with appropriate precision. - */ -public class MixedPriceColumn extends LongColumn { - - public MixedPriceColumn(String name) { - super(name, RIGHT); - } - - @Override - public void addRow(Long value) { - throw new UnsupportedOperationException("use public void addRow(Long value, boolean isFiat) instead"); - } - - public void addRow(Long value, boolean isFiat) { - rows.add(value); - - String s = isFiat ? formatPrice(value) : formatSatoshis(value); - stringColumn.addRow(s); - - if (isNewMaxWidth.test(s)) - maxWidth = s.length(); - } - - @Override - public String getRowAsFormattedString(int rowIndex) { - return getRow(rowIndex).toString(); - } - - @Override - public StringColumn asStringColumn() { - return stringColumn.justify(); - } -} diff --git a/cli/src/main/java/bisq/cli/table/column/MixedVolumeColumn.java b/cli/src/main/java/bisq/cli/table/column/MixedVolumeColumn.java deleted file mode 100644 index 1de2a24ecc9..00000000000 --- a/cli/src/main/java/bisq/cli/table/column/MixedVolumeColumn.java +++ /dev/null @@ -1,73 +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 . - */ - -package bisq.cli.table.column; - -import static bisq.cli.CurrencyFormat.formatCryptoCurrencyVolume; -import static bisq.cli.CurrencyFormat.formatFiatVolume; -import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT; - -/** - * For displaying a mix of fiat and altcoin volumes with appropriate precision. - */ -public class MixedVolumeColumn extends LongColumn { - - public MixedVolumeColumn(String name) { - super(name, RIGHT); - } - - @Override - public void addRow(Long value) { - throw new UnsupportedOperationException("use public void addRow(Long value, boolean isAltcoinVolume) instead"); - } - - @Deprecated - public void addRow(Long value, boolean isAltcoinVolume) { - rows.add(value); - - String s = isAltcoinVolume - ? formatCryptoCurrencyVolume(value) - : formatFiatVolume(value); - stringColumn.addRow(s); - - if (isNewMaxWidth.test(s)) - maxWidth = s.length(); - } - - public void addRow(Long value, int displayPrecision) { - rows.add(value); - - boolean isAltcoinVolume = displayPrecision > 0; - String s = isAltcoinVolume - ? formatCryptoCurrencyVolume(value, displayPrecision) - : formatFiatVolume(value); - stringColumn.addRow(s); - - if (isNewMaxWidth.test(s)) - maxWidth = s.length(); - } - - @Override - public String getRowAsFormattedString(int rowIndex) { - return getRow(rowIndex).toString(); - } - - @Override - public StringColumn asStringColumn() { - return stringColumn.justify(); - } -} diff --git a/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java b/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java index 818b4c9d26e..562214bdaff 100644 --- a/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java +++ b/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java @@ -126,7 +126,7 @@ public void testValidCreateOfferOpts() { "--" + OPT_DIRECTION + "=" + "BUY", "--" + OPT_CURRENCY_CODE + "=" + "EUR", "--" + OPT_AMOUNT + "=" + "0.125", - "--" + OPT_MKT_PRICE_MARGIN + "=" + "0.0", + "--" + OPT_MKT_PRICE_MARGIN + "=" + "3.15", "--" + OPT_SECURITY_DEPOSIT + "=" + "25.0" }; CreateOfferOptionParser parser = new CreateOfferOptionParser(args).parse(); @@ -134,8 +134,8 @@ public void testValidCreateOfferOpts() { assertEquals("BUY", parser.getDirection()); assertEquals("EUR", parser.getCurrencyCode()); assertEquals("0.125", parser.getAmount()); - assertEquals("0.0", parser.getMktPriceMargin()); - assertEquals("25.0", parser.getSecurityDeposit()); + assertEquals(3.15d, parser.getMktPriceMarginPct()); + assertEquals(25.0, parser.getSecurityDepositPct()); } // createoffer (bsq swap) opt parser tests diff --git a/cli/src/test/java/bisq/cli/table/AddressCliOutputDiffTest.java b/cli/src/test/java/bisq/cli/table/AddressCliOutputDiffTest.java index 2f8c8542cff..be5fd9de87e 100644 --- a/cli/src/test/java/bisq/cli/table/AddressCliOutputDiffTest.java +++ b/cli/src/test/java/bisq/cli/table/AddressCliOutputDiffTest.java @@ -11,7 +11,6 @@ import bisq.cli.AbstractCliTest; -import bisq.cli.TableFormat; import bisq.cli.table.builder.TableBuilder; @SuppressWarnings("unused") @@ -30,11 +29,13 @@ public AddressCliOutputDiffTest() { private void getFundingAddresses() { var fundingAddresses = aliceClient.getFundingAddresses(); if (fundingAddresses.size() > 0) { - var oldTbl = TableFormat.formatAddressBalanceTbl(fundingAddresses); + // TableFormat class had been deprecated, then deleted on 17-Feb-2022, but + // these diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = TableFormat.formatAddressBalanceTbl(fundingAddresses); var newTbl = new TableBuilder(ADDRESS_BALANCE_TBL, fundingAddresses).build().toString(); - printOldTbl(oldTbl); + // printOldTbl(oldTbl); printNewTbl(newTbl); - checkDiffsIgnoreWhitespace(oldTbl, newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); } else { err.println("no funding addresses found"); } @@ -52,10 +53,12 @@ private void getAddressBalance() { private void getAddressBalance(String address) { var addressBalance = singletonList(aliceClient.getAddressBalance(address)); - var oldTbl = TableFormat.formatAddressBalanceTbl(addressBalance); + // TableFormat class had been deprecated, then deleted on 17-Feb-2022, but these + // diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = TableFormat.formatAddressBalanceTbl(addressBalance); var newTbl = new TableBuilder(ADDRESS_BALANCE_TBL, addressBalance).build().toString(); - printOldTbl(oldTbl); + // printOldTbl(oldTbl); printNewTbl(newTbl); - checkDiffsIgnoreWhitespace(oldTbl, newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); } } diff --git a/cli/src/test/java/bisq/cli/table/GetBalanceCliOutputDiffTest.java b/cli/src/test/java/bisq/cli/table/GetBalanceCliOutputDiffTest.java index 0fa6d11c3da..d59e87004c2 100644 --- a/cli/src/test/java/bisq/cli/table/GetBalanceCliOutputDiffTest.java +++ b/cli/src/test/java/bisq/cli/table/GetBalanceCliOutputDiffTest.java @@ -6,7 +6,6 @@ import bisq.cli.AbstractCliTest; -import bisq.cli.TableFormat; import bisq.cli.table.builder.TableBuilder; @SuppressWarnings("unused") @@ -24,19 +23,21 @@ public GetBalanceCliOutputDiffTest() { private void getBtcBalance() { var balance = aliceClient.getBtcBalances(); - var oldTbl = TableFormat.formatBtcBalanceInfoTbl(balance); + // TableFormat class had been deprecated, then deleted on 17-Feb-2022, but these + // diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = TableFormat.formatBtcBalanceInfoTbl(balance); var newTbl = new TableBuilder(BTC_BALANCE_TBL, balance).build().toString(); - printOldTbl(oldTbl); + // printOldTbl(oldTbl); printNewTbl(newTbl); - checkDiffsIgnoreWhitespace(oldTbl, newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); } private void getBsqBalance() { var balance = aliceClient.getBsqBalances(); - var oldTbl = TableFormat.formatBsqBalanceInfoTbl(balance); + // var oldTbl = TableFormat.formatBsqBalanceInfoTbl(balance); var newTbl = new TableBuilder(BSQ_BALANCE_TBL, balance).build().toString(); - printOldTbl(oldTbl); + // printOldTbl(oldTbl); printNewTbl(newTbl); - checkDiffsIgnoreWhitespace(oldTbl, newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); } } diff --git a/cli/src/test/java/bisq/cli/table/GetOffersCliOutputDiffTest.java b/cli/src/test/java/bisq/cli/table/GetOffersCliOutputDiffTest.java index d97c9f8ded2..f5b85bdc62b 100644 --- a/cli/src/test/java/bisq/cli/table/GetOffersCliOutputDiffTest.java +++ b/cli/src/test/java/bisq/cli/table/GetOffersCliOutputDiffTest.java @@ -13,7 +13,6 @@ import bisq.cli.AbstractCliTest; -import bisq.cli.OfferFormat; import bisq.cli.table.builder.TableBuilder; @SuppressWarnings("unused") @@ -116,11 +115,13 @@ private void printAndCheckDiffs(List offers, log.warn("No {} {} offers to print.", direction, currencyCode); } else { log.info("Checking for diffs in {} {} offers.", direction, currencyCode); - var oldTbl = OfferFormat.formatOfferTable(offers, currencyCode); + // OfferFormat class had been deprecated, then deleted on 17-Feb-2022, but + // these diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = OfferFormat.formatOfferTable(offers, currencyCode); var newTbl = new TableBuilder(OFFER_TBL, offers).build().toString(); - printOldTbl(oldTbl); + // printOldTbl(oldTbl); printNewTbl(newTbl); - checkDiffsIgnoreWhitespace(oldTbl, newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); } } } diff --git a/cli/src/test/java/bisq/cli/table/GetTradeCliOutputDiffTest.java b/cli/src/test/java/bisq/cli/table/GetTradeCliOutputDiffTest.java index 8516033ee67..25c6ceed5a4 100644 --- a/cli/src/test/java/bisq/cli/table/GetTradeCliOutputDiffTest.java +++ b/cli/src/test/java/bisq/cli/table/GetTradeCliOutputDiffTest.java @@ -9,7 +9,6 @@ import bisq.cli.AbstractCliTest; import bisq.cli.GrpcClient; -import bisq.cli.TradeFormat; import bisq.cli.table.builder.TableBuilder; @SuppressWarnings("unused") @@ -43,10 +42,12 @@ private void getBobsTrade() { private void getTrade(GrpcClient client) { var trade = client.getTrade(tradeId); - var oldTbl = TradeFormat.format(trade); + // TradeFormat class had been deprecated, then deleted on 17-Feb-2022, but these + // diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = TradeFormat.format(trade); var newTbl = new TableBuilder(TRADE_DETAIL_TBL, trade).build().toString(); - printOldTbl(oldTbl); + // printOldTbl(oldTbl); printNewTbl(newTbl); - checkDiffsIgnoreWhitespace(oldTbl, newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); } } diff --git a/cli/src/test/java/bisq/cli/table/GetTransactionCliOutputDiffTest.java b/cli/src/test/java/bisq/cli/table/GetTransactionCliOutputDiffTest.java index 13fb639b6ed..d72749cd245 100644 --- a/cli/src/test/java/bisq/cli/table/GetTransactionCliOutputDiffTest.java +++ b/cli/src/test/java/bisq/cli/table/GetTransactionCliOutputDiffTest.java @@ -7,7 +7,6 @@ import bisq.cli.AbstractCliTest; -import bisq.cli.TransactionFormat; import bisq.cli.table.builder.TableBuilder; @SuppressWarnings("unused") @@ -31,11 +30,12 @@ public GetTransactionCliOutputDiffTest(String transactionId) { private void getTransaction() { var tx = aliceClient.getTransaction(transactionId); - var oldTbl = TransactionFormat.format(tx); + // TransactionFormat class had been deprecated, then deleted on 17-Feb-2022, but + // these diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = TransactionFormat.format(tx); var newTbl = new TableBuilder(TRANSACTION_TBL, tx).build().toString(); - printOldTbl(oldTbl); + // printOldTbl(oldTbl); printNewTbl(newTbl); - // Should show 1 (OK) diff due to new 'Is Confirmed' column being left justified (fixed). - checkDiffsIgnoreWhitespace(oldTbl, newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); } } diff --git a/cli/src/test/java/bisq/cli/table/PaymentAccountsCliOutputDiffTest.java b/cli/src/test/java/bisq/cli/table/PaymentAccountsCliOutputDiffTest.java index bf1ca88a1c4..30dda73d086 100644 --- a/cli/src/test/java/bisq/cli/table/PaymentAccountsCliOutputDiffTest.java +++ b/cli/src/test/java/bisq/cli/table/PaymentAccountsCliOutputDiffTest.java @@ -2,7 +2,6 @@ import lombok.extern.slf4j.Slf4j; -import static bisq.cli.TableFormat.formatPaymentAcctTbl; import static bisq.cli.table.builder.TableType.PAYMENT_ACCOUNT_TBL; @@ -26,11 +25,13 @@ public PaymentAccountsCliOutputDiffTest() { private void getPaymentAccounts() { var paymentAccounts = aliceClient.getPaymentAccounts(); if (paymentAccounts.size() > 0) { - var oldTbl = formatPaymentAcctTbl(paymentAccounts); + // The formatPaymentAcctTbl method had been deprecated, then deleted on 17-Feb-2022, + // but these diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = formatPaymentAcctTbl(paymentAccounts); var newTbl = new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccounts).build().toString(); - printOldTbl(oldTbl); + // printOldTbl(oldTbl); printNewTbl(newTbl); - checkDiffsIgnoreWhitespace(oldTbl, newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); } else { log.warn("no payment accounts found"); } diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index 5740d47b88f..9953a828c2b 100644 --- a/core/src/main/java/bisq/core/api/CoreApi.java +++ b/core/src/main/java/bisq/core/api/CoreApi.java @@ -30,7 +30,6 @@ import bisq.core.trade.model.TradeModel; import bisq.core.trade.model.bisq_v1.Trade; import bisq.core.trade.model.bsq_swap.BsqSwapTrade; -import bisq.core.trade.statistics.TradeStatistics3; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.common.app.Version; @@ -47,7 +46,6 @@ import com.google.common.util.concurrent.FutureCallback; -import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; @@ -194,24 +192,24 @@ public void createAndPlaceBsqSwapOffer(String directionAsString, public void createAndPlaceOffer(String currencyCode, String directionAsString, - String priceAsString, + String price, boolean useMarketBasedPrice, double marketPriceMargin, long amountAsLong, long minAmountAsLong, - double buyerSecurityDeposit, - long triggerPrice, + double buyerSecurityDepositPct, + String triggerPrice, String paymentAccountId, String makerFeeCurrencyCode, Consumer resultHandler) { coreOffersService.createAndPlaceOffer(currencyCode, directionAsString, - priceAsString, + useMarketBasedPrice ? "0" : price, useMarketBasedPrice, marketPriceMargin, amountAsLong, minAmountAsLong, - buyerSecurityDeposit, + buyerSecurityDepositPct, triggerPrice, paymentAccountId, makerFeeCurrencyCode, @@ -219,14 +217,14 @@ public void createAndPlaceOffer(String currencyCode, } public void editOffer(String offerId, - String priceAsString, + String price, boolean useMarketBasedPrice, double marketPriceMargin, - long triggerPrice, + String triggerPrice, int enable, EditType editType) { coreOffersService.editOffer(offerId, - priceAsString, + price, useMarketBasedPrice, marketPriceMargin, triggerPrice, @@ -442,10 +440,6 @@ public void removeWalletPassword(String password) { walletsService.removeWalletPassword(password); } - public List getTradeStatistics() { - return new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet()); - } - public int getNumConfirmationsForMostRecentTransaction(String addressString) { return walletsService.getNumConfirmationsForMostRecentTransaction(addressString); } diff --git a/core/src/main/java/bisq/core/api/CoreOffersService.java b/core/src/main/java/bisq/core/api/CoreOffersService.java index f321ad17d0c..4fdd8495738 100644 --- a/core/src/main/java/bisq/core/api/CoreOffersService.java +++ b/core/src/main/java/bisq/core/api/CoreOffersService.java @@ -66,6 +66,7 @@ import static bisq.core.offer.OpenOffer.State.AVAILABLE; import static bisq.core.offer.OpenOffer.State.DEACTIVATED; import static bisq.core.payment.PaymentAccountUtil.isPaymentAccountValidForOffer; +import static bisq.core.util.PriceUtil.getMarketPriceAsLong; import static bisq.proto.grpc.EditOfferRequest.EditType; import static bisq.proto.grpc.EditOfferRequest.EditType.FIXED_PRICE_AND_ACTIVATION_STATE; import static bisq.proto.grpc.EditOfferRequest.EditType.FIXED_PRICE_ONLY; @@ -258,7 +259,7 @@ void createAndPlaceBsqSwapOffer(String directionAsString, String offerId = getRandomOfferId(); OfferDirection direction = OfferDirection.valueOf(directionAsString.toUpperCase()); Coin amount = Coin.valueOf(amountAsLong); - Coin minAmount = Coin.valueOf(minAmountAsLong); + Coin minAmount = minAmountAsLong == 0 ? amount : Coin.valueOf(minAmountAsLong); Price price = Price.valueOf(currencyCode, priceStringToLong(priceAsString, currencyCode)); openBsqSwapOfferService.requestNewOffer(offerId, direction, @@ -275,8 +276,8 @@ void createAndPlaceOffer(String currencyCode, double marketPriceMargin, long amountAsLong, long minAmountAsLong, - double buyerSecurityDeposit, - long triggerPrice, + double buyerSecurityDepositPct, + String triggerPrice, String paymentAccountId, String makerFeeCurrencyCode, Consumer resultHandler) { @@ -293,8 +294,22 @@ void createAndPlaceOffer(String currencyCode, OfferDirection direction = OfferDirection.valueOf(directionAsString.toUpperCase()); Price price = Price.valueOf(upperCaseCurrencyCode, priceStringToLong(priceAsString, upperCaseCurrencyCode)); Coin amount = Coin.valueOf(amountAsLong); - Coin minAmount = Coin.valueOf(minAmountAsLong); + Coin minAmount = minAmountAsLong == 0 ? amount : Coin.valueOf(minAmountAsLong); Coin useDefaultTxFee = Coin.ZERO; + + // Almost ready to call createOfferService.createAndGetOffer(), but first: + // + // For the buyer security deposit parameter, API clients pass a double as a + // percent literal, e.g., #.## (%), where "1.00 means 1% of the trade amount". + // Desktop (UI) clients convert the percent literal string input before passing + // a representation of a pct as a decimal, e.g., 0.##. + // See bisq.desktop.main.offer.bisq_v1.MutableOfferDataModel, where + // "Pct value of buyer security deposit, e.g., 0.01 means 1% of trade amount." + // + // The API client's percent literal is transformed now, to make sure the double + // passed into createOfferService.createAndGetOffer() is correctly scaled. + double scaledBuyerSecurityDepositPct = exactMultiply(buyerSecurityDepositPct, 0.01); + Offer offer = createOfferService.createAndGetOffer(offerId, direction, upperCaseCurrencyCode, @@ -304,7 +319,7 @@ void createAndPlaceOffer(String currencyCode, useDefaultTxFee, useMarketBasedPrice, exactMultiply(marketPriceMargin, 0.01), - buyerSecurityDeposit, + scaledBuyerSecurityDepositPct, paymentAccount); verifyPaymentAccountIsValidForNewOffer(offer, paymentAccount); @@ -313,7 +328,7 @@ void createAndPlaceOffer(String currencyCode, boolean useSavingsWallet = true; //noinspection ConstantConditions placeOffer(offer, - buyerSecurityDeposit, + scaledBuyerSecurityDepositPct, triggerPrice, useSavingsWallet, transaction -> resultHandler.accept(offer)); @@ -321,15 +336,15 @@ void createAndPlaceOffer(String currencyCode, // Edit a placed offer. void editOffer(String offerId, - String editedPriceAsString, + String editedPrice, boolean editedUseMarketBasedPrice, double editedMarketPriceMargin, - long editedTriggerPrice, + String editedTriggerPrice, int editedEnable, EditType editType) { OpenOffer openOffer = getMyOpenOffer(offerId); var validator = new EditOfferValidator(openOffer, - editedPriceAsString, + editedPrice, editedUseMarketBasedPrice, editedMarketPriceMargin, editedTriggerPrice, @@ -346,7 +361,7 @@ void editOffer(String offerId, : editedEnable > 0 ? AVAILABLE : DEACTIVATED; OfferPayload editedPayload = getMergedOfferPayload(validator, openOffer, - editedPriceAsString, + editedPrice, editedMarketPriceMargin, editType); Offer editedOffer = new Offer(editedPayload); @@ -356,8 +371,9 @@ void editOffer(String offerId, openOfferManager.editOpenOfferStart(openOffer, () -> log.info("EditOpenOfferStart: offer {}", openOffer.getId()), log::error); + long triggerPriceAsLong = getMarketPriceAsLong(editedTriggerPrice, editedOffer.getCurrencyCode()); openOfferManager.editOpenOfferPublish(editedOffer, - editedTriggerPrice, + triggerPriceAsLong, newOfferState, () -> log.info("EditOpenOfferPublish: offer {}", openOffer.getId()), log::error); @@ -381,14 +397,15 @@ private void placeBsqSwapOffer(Offer offer, Runnable resultHandler) { } private void placeOffer(Offer offer, - double buyerSecurityDeposit, - long triggerPrice, + double buyerSecurityDepositPct, + String triggerPrice, boolean useSavingsWallet, Consumer resultHandler) { + var triggerPriceAsLong = getMarketPriceAsLong(triggerPrice, offer.getCurrencyCode()); openOfferManager.placeOffer(offer, - buyerSecurityDeposit, + buyerSecurityDepositPct, useSavingsWallet, - triggerPrice, + triggerPriceAsLong, resultHandler::accept, log::error); diff --git a/core/src/main/java/bisq/core/api/EditOfferValidator.java b/core/src/main/java/bisq/core/api/EditOfferValidator.java index 34515da5548..e6123e14bd8 100644 --- a/core/src/main/java/bisq/core/api/EditOfferValidator.java +++ b/core/src/main/java/bisq/core/api/EditOfferValidator.java @@ -14,6 +14,7 @@ import static bisq.proto.grpc.EditOfferRequest.EditType.*; import static java.lang.String.format; +import static java.math.BigDecimal.ZERO; @Slf4j class EditOfferValidator { @@ -49,10 +50,10 @@ class EditOfferValidator { private final OpenOffer currentlyOpenOffer; - private final String newPriceAsString; + private final String newPrice; private final boolean newIsUseMarketBasedPrice; private final double newMarketPriceMargin; - private final long newTriggerPrice; + private final String newTriggerPrice; private final int newEnable; private final EditOfferRequest.EditType editType; @@ -60,14 +61,14 @@ class EditOfferValidator { private final boolean isZeroEditedTriggerPrice; EditOfferValidator(OpenOffer currentlyOpenOffer, - String newPriceAsString, + String newPrice, boolean newIsUseMarketBasedPrice, double newMarketPriceMargin, - long newTriggerPrice, + String newTriggerPrice, int newEnable, EditOfferRequest.EditType editType) { this.currentlyOpenOffer = currentlyOpenOffer; - this.newPriceAsString = newPriceAsString; + this.newPrice = newPrice.isBlank() ? "0" : newPrice; // The client cannot determine what offer.isUseMarketBasedPrice should be // when editType = ACTIVATION_STATE_ONLY. Override newIsUseMarketBasedPrice // param for the ACTIVATION_STATE_ONLY case. @@ -77,12 +78,12 @@ class EditOfferValidator { ? currentlyOpenOffer.getOffer().isUseMarketBasedPrice() : newIsUseMarketBasedPrice; this.newMarketPriceMargin = newMarketPriceMargin; - this.newTriggerPrice = newTriggerPrice; + this.newTriggerPrice = newTriggerPrice.isBlank() ? "0" : newTriggerPrice; this.newEnable = newEnable; this.editType = editType; - this.isZeroEditedFixedPriceString = new BigDecimal(newPriceAsString).doubleValue() == 0; - this.isZeroEditedTriggerPrice = newTriggerPrice == 0; + this.isZeroEditedFixedPriceString = new BigDecimal(this.newPrice).doubleValue() == 0; + this.isZeroEditedTriggerPrice = new BigDecimal(this.newTriggerPrice).equals(ZERO); } EditOfferValidator validate() { @@ -123,7 +124,7 @@ public String toString() { return "EditOfferValidator{" + "\n" + " offer=" + offer.getId() + "\n" + ", offer.payloadBase.price=" + offer.getOfferPayloadBase().getPrice() + "\n" + - ", newPriceAsString=" + (isEditingPrice ? newPriceAsString : "N/A") + "\n" + + ", newPrice=" + (isEditingPrice ? newPrice : "N/A") + "\n" + ", offer.useMarketBasedPrice=" + offer.isUseMarketBasedPrice() + "\n" + ", newUseMarketBasedPrice=" + newIsUseMarketBasedPrice + "\n" + ", offer.marketPriceMargin=" + offer.getMarketPriceMargin() + "\n" + @@ -182,7 +183,7 @@ private void validateEditedMarketPriceMargin() { throw new IllegalStateException( format("programmer error: cannot set fixed price (%s)" + " in mkt price margin based offer with id '%s'", - newPriceAsString, + newPrice, currentlyOpenOffer.getId())); } @@ -195,7 +196,7 @@ private void validateEditedTriggerPrice() { + " in fixed price offer with id '%s'", currentlyOpenOffer.getId())); - if (newTriggerPrice < 0) + if (new BigDecimal(newTriggerPrice).compareTo(ZERO) < 0) throw new IllegalStateException( format("programmer error: cannot set trigger price to a negative value" + " in offer with id '%s'", diff --git a/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java b/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java index d44af9b0356..f63a70028cc 100644 --- a/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java @@ -39,7 +39,7 @@ public static TradeInfo toCanceledTradeInfo(OpenOffer myCanceledOpenOffer) { Offer offer = myCanceledOpenOffer.getOffer(); OfferInfo offerInfo = toMyOfferInfo(offer); - return new TradeInfoV1Builder() // TODO May need to use BsqSwapTradeInfoBuilder? + return new TradeInfoV1Builder() .withOffer(offerInfo) .withTradeId(myCanceledOpenOffer.getId()) .withShortId(myCanceledOpenOffer.getShortId()) @@ -52,8 +52,8 @@ public static TradeInfo toCanceledTradeInfo(OpenOffer myCanceledOpenOffer) { .withDepositTxId("") // Ignored .withPayoutTxId("") // Ignored .withTradeAmountAsLong(0) // Ignored - .withTradePrice(offer.getPrice().getValue()) - .withTradeVolume(0) // Ignored + .withTradePrice(offerInfo.getPrice()) + .withTradeVolume("") // Ignored .withTradingPeerNodeAddress("") // Ignored .withState("") // Ignored .withPhase("") // Ignored diff --git a/core/src/main/java/bisq/core/api/model/OfferInfo.java b/core/src/main/java/bisq/core/api/model/OfferInfo.java index 7e4c8c49b13..de155b86291 100644 --- a/core/src/main/java/bisq/core/api/model/OfferInfo.java +++ b/core/src/main/java/bisq/core/api/model/OfferInfo.java @@ -18,16 +18,22 @@ package bisq.core.api.model; import bisq.core.api.model.builder.OfferInfoBuilder; +import bisq.core.monetary.Price; import bisq.core.offer.Offer; import bisq.core.offer.OpenOffer; import bisq.core.util.coin.CoinUtil; import bisq.common.Payload; +import java.util.Optional; + import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; +import static bisq.common.util.MathUtils.exactMultiply; +import static bisq.core.util.PriceUtil.reformatMarketPrice; +import static bisq.core.util.VolumeUtil.formatVolume; import static java.util.Objects.requireNonNull; @EqualsAndHashCode @@ -41,19 +47,19 @@ public class OfferInfo implements Payload { private final String id; private final String direction; - private final long price; + private final String price; private final boolean useMarketBasedPrice; - private final double marketPriceMargin; + private final double marketPriceMarginPct; private final long amount; private final long minAmount; - private final long volume; - private final long minVolume; + private final String volume; + private final String minVolume; private final long txFee; private final long makerFee; private final String offerFeePaymentTxId; private final long buyerSecurityDeposit; private final long sellerSecurityDeposit; - private final long triggerPrice; + private final String triggerPrice; private final boolean isCurrencyForMakerFeeBtc; private final String paymentAccountId; private final String paymentMethodId; @@ -78,7 +84,7 @@ public OfferInfo(OfferInfoBuilder builder) { this.direction = builder.getDirection(); this.price = builder.getPrice(); this.useMarketBasedPrice = builder.isUseMarketBasedPrice(); - this.marketPriceMargin = builder.getMarketPriceMargin(); + this.marketPriceMarginPct = builder.getMarketPriceMarginPct(); this.amount = builder.getAmount(); this.minAmount = builder.getMinAmount(); this.volume = builder.getVolume(); @@ -129,23 +135,39 @@ public static OfferInfo toMyPendingOfferInfo(Offer myNewOffer) { public static OfferInfo toMyOfferInfo(OpenOffer openOffer) { // An OpenOffer is always my offer. + var currencyCode = openOffer.getOffer().getCurrencyCode(); + Optional optionalTriggerPrice = openOffer.getTriggerPrice() > 0 + ? Optional.of(Price.valueOf(currencyCode, openOffer.getTriggerPrice())) + : Optional.empty(); + var preciseTriggerPrice = optionalTriggerPrice + .map(value -> reformatMarketPrice(value.toPlainString(), currencyCode)) + .orElse("0"); return getBuilder(openOffer.getOffer(), true) - .withTriggerPrice(openOffer.getTriggerPrice()) + .withTriggerPrice(preciseTriggerPrice) .withIsActivated(!openOffer.isDeactivated()) .build(); } private static OfferInfoBuilder getBuilder(Offer offer, boolean isMyOffer) { + // OfferInfo protos are passed to API client, and some field + // values are converted to displayable, unambiguous form. + var currencyCode = offer.getCurrencyCode(); + var preciseOfferPrice = reformatMarketPrice( + requireNonNull(offer.getPrice()).toPlainString(), + currencyCode); + var marketPriceMarginAsPctLiteral = exactMultiply(offer.getMarketPriceMargin(), 100); + var roundedVolume = formatVolume(requireNonNull(offer.getVolume())); + var roundedMinVolume = formatVolume(requireNonNull(offer.getMinVolume())); return new OfferInfoBuilder() .withId(offer.getId()) .withDirection(offer.getDirection().name()) - .withPrice(requireNonNull(offer.getPrice()).getValue()) + .withPrice(preciseOfferPrice) .withUseMarketBasedPrice(offer.isUseMarketBasedPrice()) - .withMarketPriceMargin(offer.getMarketPriceMargin()) + .withMarketPriceMarginPct(marketPriceMarginAsPctLiteral) .withAmount(offer.getAmount().value) .withMinAmount(offer.getMinAmount().value) - .withVolume(requireNonNull(offer.getVolume()).getValue()) - .withMinVolume(requireNonNull(offer.getMinVolume()).getValue()) + .withVolume(roundedVolume) + .withMinVolume(roundedMinVolume) .withMakerFee(getMakerFee(offer, isMyOffer)) .withTxFee(offer.getTxFee().value) .withOfferFeePaymentTxId(offer.getOfferFeePaymentTxId()) @@ -189,17 +211,17 @@ public bisq.proto.grpc.OfferInfo toProtoMessage() { .setDirection(direction) .setPrice(price) .setUseMarketBasedPrice(useMarketBasedPrice) - .setMarketPriceMargin(marketPriceMargin) + .setMarketPriceMarginPct(marketPriceMarginPct) .setAmount(amount) .setMinAmount(minAmount) - .setVolume(volume) - .setMinVolume(minVolume) + .setVolume(volume == null ? "0" : volume) + .setMinVolume(minVolume == null ? "0" : minVolume) .setMakerFee(makerFee) .setTxFee(txFee) .setOfferFeePaymentTxId(isBsqSwapOffer ? "" : offerFeePaymentTxId) .setBuyerSecurityDeposit(buyerSecurityDeposit) .setSellerSecurityDeposit(sellerSecurityDeposit) - .setTriggerPrice(triggerPrice) + .setTriggerPrice(triggerPrice == null ? "0" : triggerPrice) .setIsCurrencyForMakerFeeBtc(isCurrencyForMakerFeeBtc) .setPaymentAccountId(paymentAccountId) .setPaymentMethodId(paymentMethodId) @@ -226,7 +248,7 @@ public static OfferInfo fromProto(bisq.proto.grpc.OfferInfo proto) { .withDirection(proto.getDirection()) .withPrice(proto.getPrice()) .withUseMarketBasedPrice(proto.getUseMarketBasedPrice()) - .withMarketPriceMargin(proto.getMarketPriceMargin()) + .withMarketPriceMarginPct(proto.getMarketPriceMarginPct()) .withAmount(proto.getAmount()) .withMinAmount(proto.getMinAmount()) .withVolume(proto.getVolume()) diff --git a/core/src/main/java/bisq/core/api/model/TradeInfo.java b/core/src/main/java/bisq/core/api/model/TradeInfo.java index 0bf0d791916..62df346206b 100644 --- a/core/src/main/java/bisq/core/api/model/TradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java @@ -25,6 +25,9 @@ import bisq.common.Payload; +import java.util.function.BiFunction; +import java.util.function.Function; + import lombok.EqualsAndHashCode; import lombok.Getter; @@ -34,6 +37,8 @@ import static bisq.core.api.model.PaymentAccountPayloadInfo.toPaymentAccountPayloadInfo; import static bisq.core.offer.OfferDirection.BUY; import static bisq.core.offer.OfferDirection.SELL; +import static bisq.core.util.PriceUtil.reformatMarketPrice; +import static bisq.core.util.VolumeUtil.formatVolume; import static java.util.Objects.requireNonNull; @EqualsAndHashCode @@ -44,6 +49,23 @@ public class TradeInfo implements Payload { // lighter weight TradeInfo proto wrapper instead, containing just enough fields to // view and interact with trades. + private static final BiFunction toOfferInfo = (tradeModel, isMyOffer) -> + isMyOffer ? toMyOfferInfo(tradeModel.getOffer()) : toOfferInfo(tradeModel.getOffer()); + + private static final Function toPeerNodeAddress = (tradeModel) -> + tradeModel.getTradingPeerNodeAddress() == null + ? "" + : tradeModel.getTradingPeerNodeAddress().getFullAddress(); + + private static final Function toRoundedVolume = (tradeModel) -> + tradeModel.getVolume() == null + ? "" + : formatVolume(requireNonNull(tradeModel.getVolume())); + + private static final Function toPreciseTradePrice = (tradeModel) -> + reformatMarketPrice(requireNonNull(tradeModel.getPrice()).toPlainString(), + tradeModel.getOffer().getCurrencyCode()); + // Bisq v1 trade protocol fields (some are in common with the BSQ Swap protocol). private final OfferInfo offer; private final String tradeId; @@ -57,8 +79,8 @@ public class TradeInfo implements Payload { private final String depositTxId; private final String payoutTxId; private final long tradeAmountAsLong; - private final long tradePrice; - private final long tradeVolume; + private final String tradePrice; + private final String tradeVolume; private final String tradingPeerNodeAddress; private final String state; private final String phase; @@ -133,14 +155,12 @@ public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, boolean isMyOffer, int numConfirmations, String closingStatus) { - OfferInfo offerInfo = isMyOffer ? toMyOfferInfo(bsqSwapTrade.getOffer()) : toOfferInfo(bsqSwapTrade.getOffer()); + var offerInfo = toOfferInfo.apply(bsqSwapTrade, isMyOffer); // A BSQ Swap miner tx fee is paid in full by the BTC seller (buying BSQ). // The BTC buyer's payout = tradeamount minus his share of miner fee. var isBtcSeller = (isMyOffer && bsqSwapTrade.getOffer().getDirection().equals(SELL)) || (!isMyOffer && bsqSwapTrade.getOffer().getDirection().equals(BUY)); - var txFeeInBtc = isBtcSeller - ? bsqSwapTrade.getTxFee().value - : 0L; + var txFeeInBtc = isBtcSeller ? bsqSwapTrade.getTxFee().value : 0L; // A BSQ Swap trade fee is paid in full by the BTC buyer (selling BSQ). // The transferred BSQ (payout) is reduced by the peer's trade fee. var takerFeeInBsq = !isMyOffer && bsqSwapTrade.getOffer().getDirection().equals(SELL) @@ -157,9 +177,9 @@ public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, .withTakerFeeAsLong(takerFeeInBsq) // N/A for bsq-swaps: .withTakerFeeTxId(""), .withDepositTxId(""), .withPayoutTxId("") .withTradeAmountAsLong(bsqSwapTrade.getAmountAsLong()) - .withTradePrice(bsqSwapTrade.getPrice().getValue()) - .withTradeVolume(bsqSwapTrade.getVolume() == null ? 0 : bsqSwapTrade.getVolume().getValue()) - .withTradingPeerNodeAddress(requireNonNull(bsqSwapTrade.getTradingPeerNodeAddress().getFullAddress())) + .withTradePrice(toPreciseTradePrice.apply(bsqSwapTrade)) + .withTradeVolume(toRoundedVolume.apply(bsqSwapTrade)) + .withTradingPeerNodeAddress(toPeerNodeAddress.apply(bsqSwapTrade)) .withState(bsqSwapTrade.getTradeState().name()) .withPhase(bsqSwapTrade.getTradePhase().name()) // N/A for bsq-swaps: .withTradePeriodState(""), .withIsDepositPublished(false), .withIsDepositConfirmed(false) @@ -194,7 +214,7 @@ private static TradeInfo toTradeInfo(Trade trade, contractInfo = ContractInfo.emptyContract.get(); } - OfferInfo offerInfo = isMyOffer ? toMyOfferInfo(trade.getOffer()) : toOfferInfo(trade.getOffer()); + var offerInfo = toOfferInfo.apply(trade, isMyOffer); return new TradeInfoV1Builder() .withOffer(offerInfo) .withTradeId(trade.getId()) @@ -208,9 +228,9 @@ private static TradeInfo toTradeInfo(Trade trade, .withDepositTxId(trade.getDepositTxId()) .withPayoutTxId(trade.getPayoutTxId()) .withTradeAmountAsLong(trade.getAmountAsLong()) - .withTradePrice(trade.getPrice().getValue()) - .withTradeVolume(trade.getVolume() == null ? 0 : trade.getVolume().getValue()) - .withTradingPeerNodeAddress(requireNonNull(trade.getTradingPeerNodeAddress().getFullAddress())) + .withTradePrice(toPreciseTradePrice.apply(trade)) + .withTradeVolume(toRoundedVolume.apply(trade)) + .withTradingPeerNodeAddress(toPeerNodeAddress.apply(trade)) .withState(trade.getTradeState().name()) .withPhase(trade.getTradePhase().name()) .withTradePeriodState(trade.getTradePeriodState().name()) @@ -246,8 +266,8 @@ public bisq.proto.grpc.TradeInfo toProtoMessage() { .setDepositTxId(depositTxId == null ? "" : depositTxId) .setPayoutTxId(payoutTxId == null ? "" : payoutTxId) .setTradeAmountAsLong(tradeAmountAsLong) - .setTradePrice(tradePrice) - .setTradeVolume(tradeVolume) + .setTradePrice(tradePrice == null ? "" : tradePrice) + .setTradeVolume(tradeVolume == null ? "" : tradeVolume) .setTradingPeerNodeAddress(tradingPeerNodeAddress) .setState(state == null ? "" : state) .setPhase(phase == null ? "" : phase) diff --git a/core/src/main/java/bisq/core/api/model/builder/OfferInfoBuilder.java b/core/src/main/java/bisq/core/api/model/builder/OfferInfoBuilder.java index 3773b465f72..02e41e3fc49 100644 --- a/core/src/main/java/bisq/core/api/model/builder/OfferInfoBuilder.java +++ b/core/src/main/java/bisq/core/api/model/builder/OfferInfoBuilder.java @@ -32,19 +32,19 @@ public final class OfferInfoBuilder { private String id; private String direction; - private long price; + private String price; private boolean useMarketBasedPrice; - private double marketPriceMargin; + private double marketPriceMarginPct; private long amount; private long minAmount; - private long volume; - private long minVolume; + private String volume; + private String minVolume; private long txFee; private long makerFee; private String offerFeePaymentTxId; private long buyerSecurityDeposit; private long sellerSecurityDeposit; - private long triggerPrice; + private String triggerPrice; private boolean isCurrencyForMakerFeeBtc; private String paymentAccountId; private String paymentMethodId; @@ -72,7 +72,7 @@ public OfferInfoBuilder withDirection(String direction) { return this; } - public OfferInfoBuilder withPrice(long price) { + public OfferInfoBuilder withPrice(String price) { this.price = price; return this; } @@ -82,8 +82,8 @@ public OfferInfoBuilder withUseMarketBasedPrice(boolean useMarketBasedPrice) { return this; } - public OfferInfoBuilder withMarketPriceMargin(double useMarketBasedPrice) { - this.marketPriceMargin = useMarketBasedPrice; + public OfferInfoBuilder withMarketPriceMarginPct(double marketPriceMarginPct) { + this.marketPriceMarginPct = marketPriceMarginPct; return this; } @@ -97,12 +97,12 @@ public OfferInfoBuilder withMinAmount(long minAmount) { return this; } - public OfferInfoBuilder withVolume(long volume) { + public OfferInfoBuilder withVolume(String volume) { this.volume = volume; return this; } - public OfferInfoBuilder withMinVolume(long minVolume) { + public OfferInfoBuilder withMinVolume(String minVolume) { this.minVolume = minVolume; return this; } @@ -132,7 +132,7 @@ public OfferInfoBuilder withSellerSecurityDeposit(long sellerSecurityDeposit) { return this; } - public OfferInfoBuilder withTriggerPrice(long triggerPrice) { + public OfferInfoBuilder withTriggerPrice(String triggerPrice) { this.triggerPrice = triggerPrice; return this; } diff --git a/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java b/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java index 3fcc29eff8c..db9cb78621e 100644 --- a/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java +++ b/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java @@ -44,8 +44,8 @@ public final class TradeInfoV1Builder { private String depositTxId; private String payoutTxId; private long tradeAmountAsLong; - private long tradePrice; - private long tradeVolume; + private String tradePrice; + private String tradeVolume; private String tradingPeerNodeAddress; private String state; private String phase; @@ -120,12 +120,12 @@ public TradeInfoV1Builder withTradeAmountAsLong(long tradeAmountAsLong) { return this; } - public TradeInfoV1Builder withTradePrice(long tradePrice) { + public TradeInfoV1Builder withTradePrice(String tradePrice) { this.tradePrice = tradePrice; return this; } - public TradeInfoV1Builder withTradeVolume(long tradeVolume) { + public TradeInfoV1Builder withTradeVolume(String tradeVolume) { this.tradeVolume = tradeVolume; return this; } diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcGetTradeStatisticsService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcGetTradeStatisticsService.java deleted file mode 100644 index 553ff1b30f5..00000000000 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcGetTradeStatisticsService.java +++ /dev/null @@ -1,72 +0,0 @@ -package bisq.daemon.grpc; - -import bisq.core.api.CoreApi; -import bisq.core.trade.statistics.TradeStatistics3; - -import bisq.proto.grpc.GetTradeStatisticsReply; -import bisq.proto.grpc.GetTradeStatisticsRequest; - -import io.grpc.ServerInterceptor; -import io.grpc.stub.StreamObserver; - -import javax.inject.Inject; - -import java.util.HashMap; -import java.util.Optional; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor; -import static bisq.proto.grpc.GetTradeStatisticsGrpc.GetTradeStatisticsImplBase; -import static bisq.proto.grpc.GetTradeStatisticsGrpc.getGetTradeStatisticsMethod; -import static java.util.concurrent.TimeUnit.SECONDS; - - - -import bisq.daemon.grpc.interceptor.CallRateMeteringInterceptor; -import bisq.daemon.grpc.interceptor.GrpcCallRateMeter; - -@Slf4j -class GrpcGetTradeStatisticsService extends GetTradeStatisticsImplBase { - - private final CoreApi coreApi; - private final GrpcExceptionHandler exceptionHandler; - - @Inject - public GrpcGetTradeStatisticsService(CoreApi coreApi, GrpcExceptionHandler exceptionHandler) { - this.coreApi = coreApi; - this.exceptionHandler = exceptionHandler; - } - - @Override - public void getTradeStatistics(GetTradeStatisticsRequest req, - StreamObserver responseObserver) { - try { - var tradeStatistics = coreApi.getTradeStatistics().stream() - .map(TradeStatistics3::toProtoTradeStatistics3) - .collect(Collectors.toList()); - - var reply = GetTradeStatisticsReply.newBuilder().addAllTradeStatistics(tradeStatistics).build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } catch (Throwable cause) { - exceptionHandler.handleException(log, cause, responseObserver); - } - } - - final ServerInterceptor[] interceptors() { - Optional rateMeteringInterceptor = rateMeteringInterceptor(); - return rateMeteringInterceptor.map(serverInterceptor -> - new ServerInterceptor[]{serverInterceptor}).orElseGet(() -> new ServerInterceptor[0]); - } - - final Optional rateMeteringInterceptor() { - return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass()) - .or(() -> Optional.of(CallRateMeteringInterceptor.valueOf( - new HashMap<>() {{ - put(getGetTradeStatisticsMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); - }} - ))); - } -} diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java index f49881e1713..18bb17bf048 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java @@ -125,9 +125,8 @@ public void getOffer(GetOfferRequest req, try { String offerId = req.getId(); Optional myOpenOffer = coreApi.findMyOpenOffer(offerId); - OfferInfo offerInfo = myOpenOffer.isPresent() - ? toMyOfferInfo(myOpenOffer.get()) - : toOfferInfo(coreApi.getOffer(offerId)); + OfferInfo offerInfo = myOpenOffer.map(OfferInfo::toMyOfferInfo) + .orElseGet(() -> toOfferInfo(coreApi.getOffer(offerId))); var reply = GetOfferReply.newBuilder() .setOffer(offerInfo.toProtoMessage()) .build(); @@ -278,10 +277,10 @@ public void createOffer(CreateOfferRequest req, req.getDirection(), req.getPrice(), req.getUseMarketBasedPrice(), - req.getMarketPriceMargin(), + req.getMarketPriceMarginPct(), req.getAmount(), req.getMinAmount(), - req.getBuyerSecurityDeposit(), + req.getBuyerSecurityDepositPct(), req.getTriggerPrice(), req.getPaymentAccountId(), req.getMakerFeeCurrencyCode(), @@ -307,7 +306,7 @@ public void editOffer(EditOfferRequest req, coreApi.editOffer(req.getId(), req.getPrice(), req.getUseMarketBasedPrice(), - req.getMarketPriceMargin(), + req.getMarketPriceMarginPct(), req.getTriggerPrice(), req.getEnable(), req.getEditType()); diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcServer.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcServer.java index f031539e067..9b109560447 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcServer.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcServer.java @@ -56,7 +56,6 @@ public GrpcServer(CoreContext coreContext, GrpcPriceService priceService, GrpcShutdownService shutdownService, GrpcVersionService versionService, - GrpcGetTradeStatisticsService tradeStatisticsService, GrpcTradesService tradesService, GrpcWalletsService walletsService) { this.server = ServerBuilder.forPort(config.apiPort) @@ -67,7 +66,6 @@ public GrpcServer(CoreContext coreContext, .addService(interceptForward(paymentAccountsService, paymentAccountsService.interceptors())) .addService(interceptForward(priceService, priceService.interceptors())) .addService(shutdownService) - .addService(interceptForward(tradeStatisticsService, tradeStatisticsService.interceptors())) .addService(interceptForward(tradesService, tradesService.interceptors())) .addService(interceptForward(versionService, versionService.interceptors())) .addService(interceptForward(walletsService, walletsService.interceptors())) diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java index 9331e154cef..904d406e04f 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java @@ -20,6 +20,7 @@ import bisq.core.api.CoreApi; import bisq.core.api.model.CanceledTradeInfo; import bisq.core.api.model.TradeInfo; +import bisq.core.offer.Offer; import bisq.core.offer.OpenOffer; import bisq.core.trade.model.TradeModel; import bisq.core.trade.model.bisq_v1.Trade; @@ -93,31 +94,37 @@ public void takeOffer(TakeOfferRequest req, responseObserver, exceptionHandler, log); - - if (coreApi.isBsqSwapOffer(req.getOfferId(), false)) { - coreApi.takeBsqSwapOffer(req.getOfferId(), - bsqSwapTrade -> { - var reply = buildTakeOfferReply(bsqSwapTrade); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - }, - errorMessage -> { - if (!errorMessageHandler.isErrorHandled()) - errorMessageHandler.handleErrorMessage(errorMessage); - }); - } else { - coreApi.takeOffer(req.getOfferId(), - req.getPaymentAccountId(), - req.getTakerFeeCurrencyCode(), - trade -> { - var reply = buildTakeOfferReply(trade); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - }, - errorMessage -> { - if (!errorMessageHandler.isErrorHandled()) - errorMessageHandler.handleErrorMessage(errorMessage); - }); + try { + // Make sure the offer exists before trying to take it. + Offer offer = coreApi.getOffer(req.getOfferId()); + + if (offer.isBsqSwapOffer()) { + coreApi.takeBsqSwapOffer(offer.getId(), + bsqSwapTrade -> { + var reply = buildTakeOfferReply(bsqSwapTrade); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + }, + errorMessage -> { + if (!errorMessageHandler.isErrorHandled()) + errorMessageHandler.handleErrorMessage(errorMessage); + }); + } else { + coreApi.takeOffer(offer.getId(), + req.getPaymentAccountId(), + req.getTakerFeeCurrencyCode(), + trade -> { + var reply = buildTakeOfferReply(trade); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + }, + errorMessage -> { + if (!errorMessageHandler.isErrorHandled()) + errorMessageHandler.handleErrorMessage(errorMessage); + }); + } + } catch (Throwable cause) { + exceptionHandler.handleException(log, cause, responseObserver); } } diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index f39ce446bb5..eaeb95fac6b 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -47,11 +47,11 @@ service Help { } message GetMethodHelpRequest { - string methodName = 1; // CLI command name. + string methodName = 1; // The CLI command name. } message GetMethodHelpReply { - string methodHelp = 1; // Man page for CLI command. + string methodHelp = 1; // The man page for the CLI command. } /* @@ -96,7 +96,7 @@ service Offers { // Edit an open offer. rpc EditOffer (EditOfferRequest) returns (EditOfferReply) { } - // Cancel (remove) an open offer. + // Cancel an open offer; remove it from the offer book. rpc CancelOffer (CancelOfferRequest) returns (CancelOfferReply) { } } @@ -108,10 +108,10 @@ message GetOfferCategoryRequest { message GetOfferCategoryReply { enum OfferCategory { - UNKNOWN = 0; - FIAT = 1; - ALTCOIN = 2; - BSQ_SWAP = 3; + UNKNOWN = 0; // An invalid offer category probably indicates a software bug. + FIAT = 1; // Indicates offer is to BUY or SELL BTC with a fiat currency. + ALTCOIN = 2; // Indicates offer is to BUY or SELL BTC with an altcoin. + BSQ_SWAP = 3; // Indicates offer is to swap BTC for BSQ. } OfferCategory offerCategory = 1; } @@ -172,7 +172,6 @@ message GetMyBsqSwapOffersReply { repeated OfferInfo bsqSwapOffers = 1; // The returned list of user's open BSQ swap offers. } -// TODO: Change some numeric fields to string to prevent loss of precision and/or ambiguity. message CreateBsqSwapOfferRequest { // The new BSQ swap offer's BUY (BTC) or SELL (BTC) direction. string direction = 1; @@ -188,7 +187,6 @@ message CreateBsqSwapOfferReply { OfferInfo bsqSwapOffer = 1; // The newly created BSQ swap offer. } -// TODO: Change some numeric fields to string to prevent loss of precision and/or ambiguity. message CreateOfferRequest { // The new offer's fiat or altcoin currency code. string currencyCode = 1; @@ -200,20 +198,17 @@ message CreateOfferRequest { // Whether the offer price is fixed, or market price margin based. bool useMarketBasedPrice = 4; // The offer's market price margin as a percentage above or below the current market BTC price, e.g., 2.50 represents 2.5%. - double marketPriceMargin = 5; + double marketPriceMarginPct = 5; // The amount of BTC to be traded, in satoshis. uint64 amount = 6; // The minimum amount of BTC to be traded, in satoshis. uint64 minAmount = 7; - // For a new BUY BTC offer, the offer maker's security deposit as a percentage of the BTC amount to be traded, e.g., 0.15 represents 15%. - // TODO: This parameter (where 0.## represents ##%) conflicts with marketPriceMargin (where #.## literally represents #.##%). - // Backward compat breaking change to buyerSecurityDeposit is needed to make it consistent with marketPriceMargin (or vice-versa). - double buyerSecurityDeposit = 8; - // For fiat, market price margin based offer, the current market BTC price at which the offer is automatically disabled. - // The parameter type is a long, representing the fiat price to 4 decimal places, but without the decimal. - // For example trigger price in EUR of 43749.3940 would be passed as 437493940. - // TODO: This should be a string type: "43749.3940", and converted to a long on the server. - uint64 triggerPrice = 9; + // A BUY BTC offer maker's security deposit as a percentage of the BTC amount to be traded, e.g., 15.00 represents 15%. + double buyerSecurityDepositPct = 8; + // A market price margin based offer's trigger price is the market BTC price at which the offer is automatically disabled. + // Disabled offers are never automatically enabled, they must be manually re-enabled. + // A zero value indicates trigger price is not set. Trigger price does not apply to fixed price offers. + string triggerPrice = 9; // The unique identifier of the payment account used to create the new offer, and send or receive trade payment. string paymentAccountId = 10; // The offer maker's trade fee currency: BTC or BSQ. @@ -232,18 +227,20 @@ message EditOfferRequest { string price = 2; // Whether the offer price is fixed, or market price margin based. bool useMarketBasedPrice = 3; - // The offer's new market price margin as a percentage above or below the current market BTC price. - double marketPriceMargin = 4; - // For market price margin based offer, the current market BTC price at which the offer is automatically disabled. - uint64 triggerPrice = 5; + // An offer's new market price margin as a percentage above or below the current market BTC price. + double marketPriceMarginPct = 4; + // A market price margin based offer's trigger price is the market BTC price at which the offer is automatically disabled. + // Disabled offers are never automatically enabled, they must be manually re-enabled. + // A zero value indicates trigger price is not set. Trigger price does not apply to fixed price offers. + string triggerPrice = 5; // Whether the offer's activation state should be changed (disable or enable), or left alone. // Send a signed int, not a bool (with default=false). // -1 = do not change activation state // 0 = disable // 1 = enable sint32 enable = 6; - // The EditType determines and constricts what offer details can - // be modified by the request, simplifying param validation. + // The EditType determines and constricts what offer details can be modified by the request, simplifying param + // validation. (The CLI need to infer this detail from 'editoffer' command options, other clients do not.) enum EditType { // Edit only the offer's activation state (enabled or disabled). ACTIVATION_STATE_ONLY = 0; @@ -280,44 +277,34 @@ message CancelOfferReply { // OfferInfo describes an offer to a client. It is derived from the heavier // Offer object in the daemon, which holds too much state to be sent to clients. -// TODO: Change some numeric fields to string to prevent loss of precision and/or ambiguity. message OfferInfo { // The offer's unique identifier. string id = 1; // The offer's BUY (BTC) or SELL (BTC) direction. string direction = 2; - // For fiat offers: a long representing the BTC price of the offer to 4 decimal places. - // A USD fiat price of 45000.4321 USD is represented as 450004321. - // For altcoin offers: a long representing the BTC price of the offer in satoshis. - // An altcoin price of five hundred thousand satoshis is represented as 500000. - // TODO: Change to string type. - uint64 price = 3; + // For fiat offers: the fiat price for 1 BTC to 4 decimal places, e.g., 45000 EUR is "45000.0000". + // For altcoin offers: the altcoin price for 1 BTC to 8 decimal places, e.g., 0.00005 BTC is "0.00005000". + string price = 3; // Whether the offer price is fixed, or market price margin based. bool useMarketBasedPrice = 4; - // The offer's market price margin above or below the current market BTC price, represented as a decimal. - // 5% is represented as 0.05. - // TODO: Change to string type, and make consistent with Create & Edit Offer's marketPriceMargin params. - double marketPriceMargin = 5; + // The offer's market price margin above or below the current market BTC price, e.g., 5.00 represents 5%. + double marketPriceMarginPct = 5; // The offer's BTC amount in satoshis. Ten million satoshis is represented as 10000000. uint64 amount = 6; // The offer's minimum BTC amount in satoshis. One million satoshis is represented as 1000000. uint64 minAmount = 7; - // A long representing the rounded volume of currency to be traded for BTC. - // For EUR fiat offers, a volume of 29500.000 EUR would be represented as 29500000. - // TODO: Seems to be a bug in the volume, missing one trailing decimal. - // Price has 4 "ghost" decimal places. Volume has only 3 "ghost" decimal places, e.g., - // EUR PRICE: 295001234 - // EUR VOL: 29500000 - uint64 volume = 8; - // A long representing the minimum, rounded volume of currency to be traded for BTC. - // TODO: Resolve the problem mentioned above, in the volume field description. - uint64 minVolume = 9; + // The rounded volume of currency to be traded for BTC. + // Fiat volume is rounded to whole currency units (no cents). Altcoin volume is rounded to 2 decimal places. + string volume = 8; + // The rounded, minimum volume of currency to be traded for BTC. + // Fiat volume is rounded to whole currency units (no cents). Altcoin volume is rounded to 2 decimal places. + string minVolume = 9; // A long representing the BTC buyer's security deposit in satoshis. uint64 buyerSecurityDeposit = 10; - // A long representing a market price margin based offer's trigger price: the current market BTC price at - // which the offer is automatically disabled. For fiat offers, a trigger price of 40000.5000 is represented - // as 400005000. - uint64 triggerPrice = 11; + // A market price margin based offer's trigger price is the market BTC price at which the offer is automatically disabled. + // Disabled offers are never automatically enabled, they must be manually re-enabled. + // A zero value indicates trigger price is not set. Trigger price does not apply to fixed price offers. + string triggerPrice = 11; // Whether the offer maker paid the trading fee in BTC or not (BSQ). bool isCurrencyForMakerFeeBtc = 12; // The unique identifier of the payment account used to create the offer. @@ -374,7 +361,7 @@ message AvailabilityResultWithDescription { * The PaymentAccounts service provides rpc methods for creating fiat and crypto currency payment accounts. */ service PaymentAccounts { - // Create a fiat payment account, providing details in a json form. + // Create a fiat payment account, providing details in a json form generated by rpc method GetPaymentAccountForm. rpc CreatePaymentAccount (CreatePaymentAccountRequest) returns (CreatePaymentAccountReply) { } // Get list of all saved fiat payment accounts. @@ -383,7 +370,7 @@ service PaymentAccounts { // Get list of all supported Bisq payment methods. rpc GetPaymentMethods (GetPaymentMethodsRequest) returns (GetPaymentMethodsReply) { } - // Get a json template file for a supported Bisq payment method. + // Get a json template file for a supported Bisq payment method. Fill in the form and call rpc method CreatePaymentAccount. rpc GetPaymentAccountForm (GetPaymentAccountFormRequest) returns (GetPaymentAccountFormReply) { } // Create a crypto currency (altcoin) payment account. @@ -395,51 +382,52 @@ service PaymentAccounts { } message CreatePaymentAccountRequest { - string paymentAccountForm = 1; + string paymentAccountForm = 1; // File path of filled json payment account form. } message CreatePaymentAccountReply { - PaymentAccount paymentAccount = 1; + PaymentAccount paymentAccount = 1; // The new payment account. } message GetPaymentAccountsRequest { } message GetPaymentAccountsReply { - repeated PaymentAccount paymentAccounts = 1; + repeated PaymentAccount paymentAccounts = 1; // All user's saved payment accounts. } message GetPaymentMethodsRequest { } message GetPaymentMethodsReply { - repeated PaymentMethod paymentMethods = 1; + repeated PaymentMethod paymentMethods = 1; // Ids of all supported Bisq fiat payment methods. } message GetPaymentAccountFormRequest { - string paymentMethodId = 1; + string paymentMethodId = 1; // Payment method id determining content of the requested payment account form. } message GetPaymentAccountFormReply { + // An empty payment account json form to be filled out and passed to rpc method CreatePaymentAccount. string paymentAccountFormJson = 1; } message CreateCryptoCurrencyPaymentAccountRequest { - string accountName = 1; - string currencyCode = 2; - string address = 3; - bool tradeInstant = 4; + string accountName = 1; // The name of the altcoin payment account. Uniqueness is not enforced. + string currencyCode = 2; // The altcoin currency code. + string address = 3; // The altcoin receiving address. + bool tradeInstant = 4; // Whether the altcoin payment account is an instant account or not. } message CreateCryptoCurrencyPaymentAccountReply { - PaymentAccount paymentAccount = 1; + PaymentAccount paymentAccount = 1; // The new altcoin payment account. } message GetCryptoCurrencyPaymentMethodsRequest { } message GetCryptoCurrencyPaymentMethodsReply { - repeated PaymentMethod paymentMethods = 1; + repeated PaymentMethod paymentMethods = 1; // Ids of all supported Bisq altcoin payment methods. } service Price { @@ -449,27 +437,11 @@ service Price { } message MarketPriceRequest { - string currencyCode = 1; + string currencyCode = 1; // The three letter currency code. } message MarketPriceReply { - double price = 1; -} - -/* -* GetTradeStatistics service is not implemented. It's stub will be remove from the gRPC daemon. -*/ -service GetTradeStatistics { - // Not implemented. - rpc GetTradeStatistics (GetTradeStatisticsRequest) returns (GetTradeStatisticsReply) { - } -} - -message GetTradeStatisticsRequest { -} - -message GetTradeStatisticsReply { - repeated TradeStatistics3 TradeStatistics = 1; + double price = 1; // The most recently available market price. } service ShutdownServer { @@ -481,7 +453,6 @@ service ShutdownServer { message StopRequest { } - message StopReply { } @@ -489,22 +460,22 @@ message StopReply { * The Trades service provides rpc methods for taking, executing, and listing trades. */ service Trades { - // Get an open trade with a trade-id. + // Get a currently open trade. rpc GetTrade (GetTradeRequest) returns (GetTradeReply) { } - // Get all open or historical trades. + // Get currently open, or historical trades (closed or failed). rpc GetTrades (GetTradesRequest) returns (GetTradesReply) { } // Take an open offer. rpc TakeOffer (TakeOfferRequest) returns (TakeOfferReply) { } - // Send a 'payment started' message to a trading peer (seller). + // Send a 'payment started' message to a trading peer (the BTC seller). rpc ConfirmPaymentStarted (ConfirmPaymentStartedRequest) returns (ConfirmPaymentStartedReply) { } - // Send a 'payment received' message to a trading peer (buyer). + // Send a 'payment received' message to a trading peer (the BTC buyer). rpc ConfirmPaymentReceived (ConfirmPaymentReceivedRequest) returns (ConfirmPaymentReceivedReply) { } - // Close a completed trade (moves it to trade history). + // Close a completed trade; move it to trade history. rpc CloseTrade (CloseTradeRequest) returns (CloseTradeReply) { } // Fail an open trade. @@ -519,170 +490,204 @@ service Trades { } message TakeOfferRequest { - string offerId = 1; - string paymentAccountId = 2; - string takerFeeCurrencyCode = 3; + string offerId = 1; // The unique identifier of the offer being taken. + string paymentAccountId = 2; // The unique identifier of the payment account used to take offer.. + string takerFeeCurrencyCode = 3; // The code of the currency (BSQ or BTC) used to pay the taker's Bisq trade fee. } message TakeOfferReply { - TradeInfo trade = 1; - AvailabilityResultWithDescription failureReason = 2; + TradeInfo trade = 1; // The new trade. + AvailabilityResultWithDescription failureReason = 2; // The reason the offer could not be taken. } message ConfirmPaymentStartedRequest { - string tradeId = 1; + string tradeId = 1; // The unique identifier of the open trade. } message ConfirmPaymentStartedReply { } message ConfirmPaymentReceivedRequest { - string tradeId = 1; + string tradeId = 1; // The unique identifier of the open trade. } message ConfirmPaymentReceivedReply { } message GetTradeRequest { - string tradeId = 1; + string tradeId = 1; // The unique identifier of the trade. } message GetTradeReply { - TradeInfo trade = 1; + TradeInfo trade = 1; // The unique identifier of the trade. } message GetTradesRequest { + // Rpc method GetTrades parameter determining what category of trade list is is being requested. enum Category { - OPEN = 0; - CLOSED = 1; - FAILED = 2; + OPEN = 0; // Get all currently open trades. + CLOSED = 1; // Get all completed trades. + FAILED = 2; // Get all failed trades. } Category category = 1; } message GetTradesReply { - repeated TradeInfo trades = 1; + repeated TradeInfo trades = 1; // All trades for GetTradesRequest.Category. } message CloseTradeRequest { - string tradeId = 1; + string tradeId = 1; // The unique identifier of the trade. } message CloseTradeReply { } message FailTradeRequest { - string tradeId = 1; + string tradeId = 1; // The unique identifier of the trade. } message FailTradeReply { } message UnFailTradeRequest { - string tradeId = 1; + string tradeId = 1; // The unique identifier of the trade. } message UnFailTradeReply { } message WithdrawFundsRequest { - string tradeId = 1; - string address = 2; - string memo = 3; + string tradeId = 1; // The unique identifier of the trade. + string address = 2; // The receiver's bitcoin wallet address. + string memo = 3; // An optional memo saved with the sent btc transaction. } message WithdrawFundsReply { } message TradeInfo { + // The original offer. OfferInfo offer = 1; + // The unique identifier of the trade. string tradeId = 2; + // An abbreviation of unique identifier of the trade. It cannot be used as parameter to rpc methods GetTrade, + // ConfirmPaymentStarted, CloseTrade, etc., but it may be useful while interacting with support or trading peers. string shortId = 3; + // The creation date of the trade as a long: the number of milliseconds that have elapsed since January 1, 1970. uint64 date = 4; + // A brief description of the user's role in the trade, i.e., an offer maker or taker, a BTC buyer or seller. string role = 5; + // Whether the offer taker's Bisq trade fee was paid in BTC or not (BSQ). bool isCurrencyForTakerFeeBtc = 6; + // The bitcoin miner transaction fee in satoshis. uint64 txFeeAsLong = 7; + // The offer taker's Bisq trade fee in satoshis. uint64 takerFeeAsLong = 8; + // The bitcoin transaction id for offer taker's Bisq trade fee. string takerFeeTxId = 9; + // The bitcoin transaction id for the offer taker's security deposit. string depositTxId = 10; + // The bitcoin transaction id for trade payout. string payoutTxId = 11; + // The trade payout amount in satoshis. uint64 tradeAmountAsLong = 12; - uint64 tradePrice = 13; + // For fiat trades: the fiat price for 1 BTC to 4 decimal places, e.g., 41000.50 EUR is "41000.5000". + // For altcoin trades: the altcoin price for 1 BTC to 8 decimal places, e.g., 0.5 BTC is "0.50000000". + string tradePrice = 13; + // The trading peer's node address. string tradingPeerNodeAddress = 14; + // The internal state of the trade. (TODO Needs more explanation.) string state = 15; + // The internal phase of the trade. (TODO Needs more explanation.) string phase = 16; + // How much of the trade protocol's time limit has elapsed. (TODO Needs more explanation.) string tradePeriodState = 17; + // Whether the trade's security deposit bitcoin transaction has been broadcast, or not. bool isDepositPublished = 18; + // Whether the trade's security deposit bitcoin transaction has been confirmed at least once, or not. bool isDepositConfirmed = 19; + // Whether the trade's 'start payment' message has been sent by the BTC buyer, or not. + // (TODO Rename field to isPaymentSent because payment could be made in altcoin.) bool isFiatSent = 20; + // Whether the trade's 'payment received' message has been sent by the BTC seller, or not. + // (TODO Rename field to isPaymentReceived because payment could be made in altcoin.) bool isFiatReceived = 21; + // Whether the trade's payout bitcoin transaction has been confirmed at least once, or not. bool isPayoutPublished = 22; + // Whether the trade's payout has been completed and the trade is now closed, or not. + // (TODO Rename field to isClosed, or isCompleted because payment could be made in altcoin.) bool isWithdrawn = 23; + // The entire trade contract as a json string. string contractAsJson = 24; + // The summary of the trade contract. ContractInfo contract = 25; - uint64 tradeVolume = 26; + // The volume of currency traded for BTC. + string tradeVolume = 26; + // The details specific to the BSQ swap trade. If the trade is not a BSQ swap, this field should be ignored. BsqSwapTradeInfo bsqSwapTradeInfo = 28; // Needed by open/closed/failed trade list items. string closingStatus = 29; } message ContractInfo { - string buyerNodeAddress = 1; - string sellerNodeAddress = 2; - string mediatorNodeAddress = 3; - string refundAgentNodeAddress = 4; - bool isBuyerMakerAndSellerTaker = 5; - string makerAccountId = 6; - string takerAccountId = 7; - PaymentAccountPayloadInfo makerPaymentAccountPayload = 8; - PaymentAccountPayloadInfo takerPaymentAccountPayload = 9; - string makerPayoutAddressString = 10; - string takerPayoutAddressString = 11; - uint64 lockTime = 12; + string buyerNodeAddress = 1; // The BTC buyer peer's node address. + string sellerNodeAddress = 2; // The BTC seller peer's node address. + string mediatorNodeAddress = 3; // If the trade was disputed, the Bisq mediator's node address. + string refundAgentNodeAddress = 4; // If a trade refund was requested, the Bisq refund agent's node address. + bool isBuyerMakerAndSellerTaker = 5; // Whether the BTC buyer created the original offer, or not. + string makerAccountId = 6; // The offer maker's payment account id. + string takerAccountId = 7; // The offer taker's payment account id. + PaymentAccountPayloadInfo makerPaymentAccountPayload = 8; // A summary of the offer maker's payment account. + PaymentAccountPayloadInfo takerPaymentAccountPayload = 9; // A summary of the offer taker's payment account. + string makerPayoutAddressString = 10; // The offer maker's BTC payout address. + string takerPayoutAddressString = 11; // The offer taker's BTC payout address. + uint64 lockTime = 12; // TODO } /* * BSQ Swap protocol specific fields not common to Bisq v1 trade protocol fields. */ message BsqSwapTradeInfo { - string txId = 1; - uint64 bsqTradeAmount = 2; - uint64 btcTradeAmount = 3; - uint64 bsqMakerTradeFee = 4; - uint64 bsqTakerTradeFee = 5; - uint64 txFeePerVbyte = 6; - string makerBsqAddress = 7; - string makerBtcAddress = 8; - string takerBsqAddress = 9; - string takerBtcAddress = 10; - uint64 numConfirmations = 11; - string errorMessage = 12; - uint64 payout = 13; - uint64 swapPeerPayout = 14; + string txId = 1; // The BSQ swap's bitcoin transaction id. + uint64 bsqTradeAmount = 2; // The amount of BSQ swapped in satoshis. + uint64 btcTradeAmount = 3; // The amount of BTC swapped in satoshis. + uint64 bsqMakerTradeFee = 4; // The swap offer maker's BSQ trade fee. + uint64 bsqTakerTradeFee = 5; // The swap offer taker's BSQ trade fee. + uint64 txFeePerVbyte = 6; // The swap transaction's bitcoin transaction id. + string makerBsqAddress = 7; // The swap offer maker's BSQ wallet address. + string makerBtcAddress = 8; // The swap offer maker's BTC wallet address. + string takerBsqAddress = 9; // The swap offer taker's BSQ wallet address. + string takerBtcAddress = 10; // The swap offer taker's BTC wallet address. + uint64 numConfirmations = 11; // The confirmations count for the completed swap's bitcoin transaction. + string errorMessage = 12; // An explanation for a failure to complete the swap. + uint64 payout = 13; // The amount of the user's payout in satoshis. (TODO explanation about miner fee vs trade fee) + uint64 swapPeerPayout = 14; // The amount of the peer's payout in satoshis. (TODO explanation about miner fee vs trade fee) } message PaymentAccountPayloadInfo { - string id = 1; - string paymentMethodId = 2; - string address = 3; + string id = 1; // The unique identifier of the payment account. + string paymentMethodId = 2; // The unique identifier of the payment method. + string address = 3; // The optional altcoin wallet address associated with the (altcoin) payment account. } message TxFeeRateInfo { - bool useCustomTxFeeRate = 1; - uint64 customTxFeeRate = 2; - uint64 feeServiceRate = 3; + bool useCustomTxFeeRate = 1; // Whether the daemon's custom btc transaction fee rate preference is set, or not. + uint64 customTxFeeRate = 2; // The daemon's custom btc transaction fee rate preference, in sats/byte. + uint64 feeServiceRate = 3; // The Bisq network's most recently available btc transaction fee rate, in sats/byte. + // The date of the most recent Bisq network fee rate request as a long: the number of milliseconds that have elapsed since January 1, 1970. uint64 lastFeeServiceRequestTs = 4; - uint64 minFeeServiceRate = 5; + uint64 minFeeServiceRate = 5; // The Bisq network's minimum btc transaction fee rate, in sats/byte. } message TxInfo { - string txId = 1; - uint64 inputSum = 2; - uint64 outputSum = 3; - uint64 fee = 4; - int32 size = 5; - bool isPending = 6; - string memo = 7; + string txId = 1; // The bitcoin transaction id. + uint64 inputSum = 2; // The sum of the bitcoin transaction's input values in satoshis. + uint64 outputSum = 3; // The sum of the bitcoin transaction's output values in satoshis. + uint64 fee = 4; // The bitcoin transaction's miner fee in satoshis. + int32 size = 5; // The bitcoin transaction's size in bytes. + bool isPending = 6; // Whether the bitcoin transaction has been confirmed at least one time, or not. + string memo = 7; // An optional memo associated with the bitcoin transaction. } /* @@ -691,7 +696,7 @@ message TxInfo { * an encryption password on a a wallet, and unlocking / locking an encrypted wallet. */ service Wallets { - // Get current BSQ and BTC balances. + // Get the Bisq wallet's current BSQ and BTC balances. rpc GetBalances (GetBalancesRequest) returns (GetBalancesReply) { } // Get BTC balance for a wallet address. @@ -700,95 +705,113 @@ service Wallets { // Get an unused BSQ wallet address. rpc GetUnusedBsqAddress (GetUnusedBsqAddressRequest) returns (GetUnusedBsqAddressReply) { } - // Send BSQ to an address. + // Send an amount of BSQ to an external address. rpc SendBsq (SendBsqRequest) returns (SendBsqReply) { } - // Send BSQ to an address. + // Send an amount of BTC to an external address. rpc SendBtc (SendBtcRequest) returns (SendBtcReply) { } - // Verify a specific amount of BSQ was received by a BSQ wallet address. (TODO change method name?) + // Verify a specific amount of BSQ was received by a BSQ wallet address. + // This is a problematic way of verifying BSQ payment has been received for a v1 trade protocol BSQ-BTC trade, + // which has been solved by the introduction of BSQ swap trades, which use a different, unused BSQ address for each trade. rpc VerifyBsqSentToAddress (VerifyBsqSentToAddressRequest) returns (VerifyBsqSentToAddressReply) { } - // Get most recently available BTC network tx fee, or custom fee rate if set. + // Get the Bisq network's most recently available bitcoin miner transaction fee rate, or custom fee rate if set. rpc GetTxFeeRate (GetTxFeeRateRequest) returns (GetTxFeeRateReply) { } - // Set custom tx fee rate. + // Set the Bisq daemon's custom bitcoin miner transaction fee rate, in sats/byte.. rpc SetTxFeeRatePreference (SetTxFeeRatePreferenceRequest) returns (SetTxFeeRatePreferenceReply) { } - // Remove custom tx fee rate, revert to using BTC network tx fee rate. + // Remove the custom bitcoin miner transaction fee rate; revert to the Bisq network's bitcoin miner transaction fee rate. rpc UnsetTxFeeRatePreference (UnsetTxFeeRatePreferenceRequest) returns (UnsetTxFeeRatePreferenceReply) { } - // Get a BTC tx with a transaction-id. + // Get a bitcoin transaction summary. rpc GetTransaction (GetTransactionRequest) returns (GetTransactionReply) { } - // Get all BTC receiving address in the wallet. + // Get all bitcoin receiving addresses in the Bisq BTC wallet. rpc GetFundingAddresses (GetFundingAddressesRequest) returns (GetFundingAddressesReply) { } - // Set wallet encryption password. + // Set the Bisq wallet's encryption password. rpc SetWalletPassword (SetWalletPasswordRequest) returns (SetWalletPasswordReply) { } - // Remove wallet encryption password. + // Remove the encryption password from the Bisq wallet. rpc RemoveWalletPassword (RemoveWalletPasswordRequest) returns (RemoveWalletPasswordReply) { } - // Lock unlocked, encrypted wallet. + // Lock an encrypted Bisq wallet before the UnlockWallet rpc method's timeout period has expired. rpc LockWallet (LockWalletRequest) returns (LockWalletReply) { } - // Unlock encrypted wallet before executing wallet sensitive methods: - // createoffer, takeoffer, getbalances, etc. + // Unlock a Bisq encrypted wallet before calling wallet sensitive rpc methods: CreateOffer, TakeOffer, GetBalances, + // etc., for a timeout period in seconds. An unlocked wallet will automatically lock itself after the timeout + // period has expired, or a LockWallet request has been made, whichever is first. An unlocked wallet's timeout + // setting can be overridden by subsequent UnlockWallet calls. rpc UnlockWallet (UnlockWalletRequest) returns (UnlockWalletReply) { } } message GetBalancesRequest { - string currencyCode = 1; + string currencyCode = 1; // The Bisq wallet currency (BSQ or BTC) for the balances request. } message GetBalancesReply { - BalancesInfo balances = 1; + BalancesInfo balances = 1; // The summary of Bisq wallet's BSQ and BTC balances. } message GetAddressBalanceRequest { - string address = 1; + string address = 1; // The BTC wallet address being queried. } message GetAddressBalanceReply { - AddressBalanceInfo addressBalanceInfo = 1; + AddressBalanceInfo addressBalanceInfo = 1; // The BTC wallet address with its balance summary. } message GetUnusedBsqAddressRequest { } message GetUnusedBsqAddressReply { - string address = 1; + string address = 1; // The BSQ wallet's unused address. } message SendBsqRequest { + // The external BSQ wallet address. string address = 1; + // The amount being sent to the external BSQ wallet address, as a string in "#######,##" format. string amount = 2; + // An optional bitcoin miner transaction fee rate, in sats/byte. If not defined, Bisq will revert + // to the custom transaction fee rate preference, if set, else the common Bisq network fee rate. string txFeeRate = 3; } message SendBsqReply { + // The summary of a bitcoin transaction. (BSQ is a colored coin, and transacted on the bitcoin blockchain.) TxInfo txInfo = 1; } message SendBtcRequest { + // The external bitcoin address. string address = 1; + // The amount of BTC to send to the external address, as a string in "##.########" (BTC unit) format. string amount = 2; + // An optional bitcoin miner transaction fee rate, in sats/byte. If not defined, Bisq will revert + // to the custom transaction fee rate preference, if set, else the common Bisq network fee rate. string txFeeRate = 3; + // An optional memo associated with the bitcoin transaction. string memo = 4; } message SendBtcReply { - TxInfo txInfo = 1; + TxInfo txInfo = 1; // The summary of a bitcoin transaction. } message VerifyBsqSentToAddressRequest { - string address = 1; - string amount = 2; + string address = 1; // The internal BSQ wallet address. + string amount = 2; // The amount supposedly sent to the BSQ wallet address, as a string in "#######,##" format. } message VerifyBsqSentToAddressReply { + // Whether a specific BSQ wallet address has received a specific amount of BSQ. If the same address has received + // the same amount of BSQ more than once, a true value does not indicate payment has been made for a v1 protocol + // BSQ-BTC trade. This BSQ payment verification problem is solved with BSQ swaps, which use a different BSQ + // address for each swap transaction. bool isAmountReceived = 1; } @@ -796,7 +819,7 @@ message GetTxFeeRateRequest { } message GetTxFeeRateReply { - TxFeeRateInfo txFeeRateInfo = 1; + TxFeeRateInfo txFeeRateInfo = 1; // The summary of the most recently available bitcoin transaction fee rates. } message SetTxFeeRatePreferenceRequest { @@ -804,14 +827,14 @@ message SetTxFeeRatePreferenceRequest { } message SetTxFeeRatePreferenceReply { - TxFeeRateInfo txFeeRateInfo = 1; + TxFeeRateInfo txFeeRateInfo = 1; // The summary of the most recently available bitcoin transaction fee rates. } message UnsetTxFeeRatePreferenceRequest { } message UnsetTxFeeRatePreferenceReply { - TxFeeRateInfo txFeeRateInfo = 1; + TxFeeRateInfo txFeeRateInfo = 1; // The summary of the most recently available bitcoin transaction fee rates. } message GetTransactionRequest { @@ -819,26 +842,26 @@ message GetTransactionRequest { } message GetTransactionReply { - TxInfo txInfo = 1; + TxInfo txInfo = 1; // The summary of a bitcoin transaction. } message GetFundingAddressesRequest { } message GetFundingAddressesReply { - repeated AddressBalanceInfo addressBalanceInfo = 1; + repeated AddressBalanceInfo addressBalanceInfo = 1; // The list of BTC wallet addresses with their balances. } message SetWalletPasswordRequest { - string password = 1; - string newPassword = 2; + string password = 1; // The new password for encrypting an unencrypted Bisq wallet. + string newPassword = 2; // The new password for encrypting an already encrypted Bisq wallet (a password override). } message SetWalletPasswordReply { } message RemoveWalletPasswordRequest { - string password = 1; + string password = 1; // The Bisq wallet's current encryption password. } message RemoveWalletPasswordReply { @@ -851,43 +874,57 @@ message LockWalletReply { } message UnlockWalletRequest { - string password = 1; - uint64 timeout = 2; + string password = 1; // The Bisq wallet's encryption password. + uint64 timeout = 2; // The Bisq wallet's unlock time period, in seconds. } message UnlockWalletReply { } -/* Field names are shortened for readability's sake, i.e., -* balancesInfo.getBtc().getAvailableBalance() is cleaner than -* balancesInfo.getBtcBalanceInfo().getAvailableBalance(). -*/ message BalancesInfo { - BsqBalanceInfo bsq = 1; - BtcBalanceInfo btc = 2; + BsqBalanceInfo bsq = 1; // BSQ wallet balance information. + BtcBalanceInfo btc = 2; // BTC wallet balance information. } message BsqBalanceInfo { + // The BSQ amount currently available to send to other addresses at the user's discretion, in satoshis. uint64 availableConfirmedBalance = 1; + // The BSQ amount currently being used in a send transaction, in satoshis. Unverified BSQ balances are not spendable, + // but are often quickly returned to the availableConfirmedBalance as soon as the send transaction has been broadcast. + // (TODO Clarify meaning of unverifiedBalance.) uint64 unverifiedBalance = 2; + // The BSQ transaction change amount tied up in an unconfirmed transaction, remaining unspendable until the change + // is returned to the availableConfirmedBalance. + // (TODO Clarify meaning of unverifiedBalance.) uint64 unconfirmedChangeBalance = 3; + // The locked BSQ amount held by DAO voting transaction. uint64 lockedForVotingBalance = 4; + // The locked BSQ amount held by DAO bonding transaction. uint64 lockupBondsBalance = 5; + // The BSQ amount received during a time-based DAO bonding release transaction. + // (TODO Clarify meaning of unlockingBondsBalance.) uint64 unlockingBondsBalance = 6; } message BtcBalanceInfo { + // The BTC amount currently available to send to other addresses at the user's discretion, in satoshis. uint64 availableBalance = 1; + // The BTC amount currently reserved to cover open offers' security deposits, and BTC sellers' payout amounts, + // in satoshis. Reserved funds are not spendable, but are recoverable by users. When a user cancels an offer + // funds reserved for that offer are returned to the availableBalance. uint64 reservedBalance = 2; + // The sum of availableBalance + reservedBalance, in satoshis. uint64 totalAvailableBalance = 3; + // The BTC amount being locked to cover the security deposits and BTC seller's pending trade payouts. Locked + // funds are not recoverable until a trade is completed, when security deposits are returned to the availableBalance. uint64 lockedBalance = 4; } message AddressBalanceInfo { - string address = 1; - int64 balance = 2; - int64 numConfirmations = 3; - bool isAddressUnused = 4; + string address = 1; // The bitcoin wallet address. + int64 balance = 2; // The address' BTC balance in satoshis. + int64 numConfirmations = 3; // The number of confirmations for the most recent transaction referencing the output address. + bool isAddressUnused = 4; // Whether the bitcoin address has ever been used, or not. } service GetVersion { @@ -900,5 +937,5 @@ message GetVersionRequest { } message GetVersionReply { - string version = 1; + string version = 1; // The version of the Bisq software release. }