From 126885d57087cde472f6239c3ade828bdb04a672 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 15 Apr 2021 14:23:32 -0300 Subject: [PATCH] Test BSQ/BTC trade pair - Added CreateBSQOffersTest, TakeBuyBSQOfferTest, TakeSellBSQOfferTest. The first tests bsq offer creation, the second & third test bsq trade protocol, and includes bsq payment verification checks. - Made test dispute agent registration a test harness opt. It was a test case option, now it happens by default. - Moved some global test constants into ApiTestConfig. Adjusted affected test cases. --- .../src/main/java/bisq/apitest/Scaffold.java | 22 +- .../bisq/apitest/config/ApiTestConfig.java | 17 + .../java/bisq/apitest/method/MethodTest.java | 33 +- .../method/RegisterDisputeAgentsTest.java | 3 + .../method/offer/AbstractOfferTest.java | 20 +- .../apitest/method/offer/CancelOfferTest.java | 6 +- .../method/offer/CreateBSQOffersTest.java | 252 ++++++++++++++ .../offer/CreateOfferUsingFixedPriceTest.java | 101 +++--- ...CreateOfferUsingMarketPriceMarginTest.java | 130 ++++---- .../method/offer/ValidateCreateOfferTest.java | 18 +- .../method/trade/AbstractTradeTest.java | 52 ++- .../method/trade/TakeBuyBSQOfferTest.java | 304 +++++++++++++++++ .../method/trade/TakeBuyBTCOfferTest.java | 51 ++- .../method/trade/TakeSellBSQOfferTest.java | 309 ++++++++++++++++++ .../method/trade/TakeSellBTCOfferTest.java | 57 ++-- .../java/bisq/apitest/scenario/OfferTest.java | 14 + .../java/bisq/apitest/scenario/TradeTest.java | 24 ++ 17 files changed, 1200 insertions(+), 213 deletions(-) create mode 100644 apitest/src/test/java/bisq/apitest/method/offer/CreateBSQOffersTest.java create mode 100644 apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBSQOfferTest.java create mode 100644 apitest/src/test/java/bisq/apitest/method/trade/TakeSellBSQOfferTest.java diff --git a/apitest/src/main/java/bisq/apitest/Scaffold.java b/apitest/src/main/java/bisq/apitest/Scaffold.java index a556a426b3..d518c0bcb0 100644 --- a/apitest/src/main/java/bisq/apitest/Scaffold.java +++ b/apitest/src/main/java/bisq/apitest/Scaffold.java @@ -42,10 +42,14 @@ import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind; +import static bisq.apitest.config.ApiTestConfig.MEDIATOR; +import static bisq.apitest.config.ApiTestConfig.REFUND_AGENT; import static bisq.apitest.config.BisqAppConfig.*; +import static bisq.common.app.DevEnv.DEV_PRIVILEGE_PRIV_KEY; import static java.lang.String.format; import static java.lang.System.exit; import static java.lang.System.out; +import static java.net.InetAddress.getLoopbackAddress; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; @@ -58,6 +62,7 @@ import bisq.apitest.linux.BashCommand; import bisq.apitest.linux.BisqProcess; import bisq.apitest.linux.BitcoinDaemon; import bisq.apitest.linux.LinuxProcess; +import bisq.cli.GrpcClient; @Slf4j public class Scaffold { @@ -146,6 +151,8 @@ public class Scaffold { // Verify each startup task's future is done. verifyStartupCompleted(); + + maybeRegisterDisputeAgents(); return this; } @@ -287,7 +294,7 @@ public class Scaffold { Files.copy(srcPath, destPath, REPLACE_EXISTING); String chmod700Perms = "rwx------"; Files.setPosixFilePermissions(destPath, PosixFilePermissions.fromString(chmod700Perms)); - log.info("Installed {} with perms {}.", destPath.toString(), chmod700Perms); + log.info("Installed {} with perms {}.", destPath, chmod700Perms); } catch (IOException e) { e.printStackTrace(); } @@ -309,7 +316,7 @@ public class Scaffold { Path destPath = Paths.get(dataDir, testRateMeteringFile.getName()); String chmod700Perms = "rwx------"; Files.setPosixFilePermissions(destPath, PosixFilePermissions.fromString(chmod700Perms)); - log.info("Installed {} with perms {}.", destPath.toString(), chmod700Perms); + log.info("Installed {} with perms {}.", destPath, chmod700Perms); } private void installShutdownHook() { @@ -448,4 +455,15 @@ public class Scaffold { if (Utilities.isWindows()) throw new IllegalStateException("ApiTest not supported on Windows"); } + + private void maybeRegisterDisputeAgents() { + if (config.hasSupportingApp(arbdaemon.name()) && config.registerDisputeAgents) { + log.info("Option --registerDisputeAgents=true, registering dispute agents in arbdaemon ..."); + GrpcClient arbClient = new GrpcClient(getLoopbackAddress().getHostAddress(), + arbdaemon.apiPort, + config.apiPassword); + arbClient.registerDisputeAgent(MEDIATOR, DEV_PRIVILEGE_PRIV_KEY); + arbClient.registerDisputeAgent(REFUND_AGENT, DEV_PRIVILEGE_PRIV_KEY); + } + } } diff --git a/apitest/src/main/java/bisq/apitest/config/ApiTestConfig.java b/apitest/src/main/java/bisq/apitest/config/ApiTestConfig.java index bffa009935..44897a2da0 100644 --- a/apitest/src/main/java/bisq/apitest/config/ApiTestConfig.java +++ b/apitest/src/main/java/bisq/apitest/config/ApiTestConfig.java @@ -54,6 +54,13 @@ import static joptsimple.internal.Strings.EMPTY; @Slf4j public class ApiTestConfig { + // Global constants + public static final String BSQ = "BSQ"; + public static final String BTC = "BTC"; + public static final String ARBITRATOR = "arbitrator"; + public static final String MEDIATOR = "mediator"; + public static final String REFUND_AGENT = "refundagent"; + // Option name constants static final String HELP = "help"; static final String BASH_PATH = "bashPath"; @@ -73,6 +80,7 @@ public class ApiTestConfig { static final String SUPPORTING_APPS = "supportingApps"; static final String CALL_RATE_METERING_CONFIG_PATH = "callRateMeteringConfigPath"; static final String ENABLE_BISQ_DEBUGGING = "enableBisqDebugging"; + static final String REGISTER_DISPUTE_AGENTS = "registerDisputeAgents"; // Default values for certain options static final String DEFAULT_CONFIG_FILE_NAME = "apitest.properties"; @@ -105,6 +113,7 @@ public class ApiTestConfig { public final List supportingApps; public final String callRateMeteringConfigPath; public final boolean enableBisqDebugging; + public final boolean registerDisputeAgents; // Immutable system configurations set in the constructor. public final String bitcoinDatadir; @@ -242,6 +251,13 @@ public class ApiTestConfig { .withRequiredArg() .ofType(Boolean.class) .defaultsTo(false); + + ArgumentAcceptingOptionSpec registerDisputeAgentsOpt = + parser.accepts(REGISTER_DISPUTE_AGENTS, + "Register dispute agents in arbitration daemon") + .withRequiredArg() + .ofType(Boolean.class) + .defaultsTo(true); try { CompositeOptionSet options = new CompositeOptionSet(); @@ -299,6 +315,7 @@ public class ApiTestConfig { this.supportingApps = asList(options.valueOf(supportingAppsOpt).split(",")); this.callRateMeteringConfigPath = options.valueOf(callRateMeteringConfigPathOpt); this.enableBisqDebugging = options.valueOf(enableBisqDebuggingOpt); + this.registerDisputeAgents = options.valueOf(registerDisputeAgentsOpt); // Assign values to special-case static fields. BASH_PATH_VALUE = bashPath; diff --git a/apitest/src/test/java/bisq/apitest/method/MethodTest.java b/apitest/src/test/java/bisq/apitest/method/MethodTest.java index c475c909ec..51308d8ccd 100644 --- a/apitest/src/test/java/bisq/apitest/method/MethodTest.java +++ b/apitest/src/test/java/bisq/apitest/method/MethodTest.java @@ -30,7 +30,6 @@ import java.io.PrintWriter; import java.util.function.Function; import java.util.stream.Collectors; -import static bisq.common.app.DevEnv.DEV_PRIVILEGE_PRIV_KEY; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.stream; import static org.junit.jupiter.api.Assertions.fail; @@ -42,28 +41,21 @@ import bisq.cli.GrpcClient; public class MethodTest extends ApiTestCase { - protected static final String ARBITRATOR = "arbitrator"; - protected static final String MEDIATOR = "mediator"; - protected static final String REFUND_AGENT = "refundagent"; - protected static final CoreProtoResolver CORE_PROTO_RESOLVER = new CoreProtoResolver(); private static final Function[], String> toNameList = (enums) -> stream(enums).map(Enum::name).collect(Collectors.joining(",")); public static void startSupportingApps(File callRateMeteringConfigFile, - boolean registerDisputeAgents, boolean generateBtcBlock, Enum... supportingApps) { startSupportingApps(callRateMeteringConfigFile, - registerDisputeAgents, generateBtcBlock, false, supportingApps); } public static void startSupportingApps(File callRateMeteringConfigFile, - boolean registerDisputeAgents, boolean generateBtcBlock, boolean startSupportingAppsInDebugMode, Enum... supportingApps) { @@ -73,23 +65,20 @@ public class MethodTest extends ApiTestCase { "--callRateMeteringConfigPath", callRateMeteringConfigFile.getAbsolutePath(), "--enableBisqDebugging", startSupportingAppsInDebugMode ? "true" : "false" }); - doPostStartup(registerDisputeAgents, generateBtcBlock); + doPostStartup(generateBtcBlock); } catch (Exception ex) { fail(ex); } } - public static void startSupportingApps(boolean registerDisputeAgents, - boolean generateBtcBlock, + public static void startSupportingApps(boolean generateBtcBlock, Enum... supportingApps) { - startSupportingApps(registerDisputeAgents, - generateBtcBlock, + startSupportingApps(generateBtcBlock, false, supportingApps); } - public static void startSupportingApps(boolean registerDisputeAgents, - boolean generateBtcBlock, + public static void startSupportingApps(boolean generateBtcBlock, boolean startSupportingAppsInDebugMode, Enum... supportingApps) { try { @@ -100,18 +89,13 @@ public class MethodTest extends ApiTestCase { "--callRateMeteringConfigPath", callRateMeteringConfigFile.getAbsolutePath(), "--enableBisqDebugging", startSupportingAppsInDebugMode ? "true" : "false" }); - doPostStartup(registerDisputeAgents, generateBtcBlock); + doPostStartup(generateBtcBlock); } catch (Exception ex) { fail(ex); } } - protected static void doPostStartup(boolean registerDisputeAgents, - boolean generateBtcBlock) { - if (registerDisputeAgents) { - registerDisputeAgents(); - } - + protected static void doPostStartup(boolean generateBtcBlock) { // Generate 1 regtest block for alice's and/or bob's wallet to // show 10 BTC balance, and allow time for daemons parse the new block. if (generateBtcBlock) @@ -159,11 +143,6 @@ public class MethodTest extends ApiTestCase { // Static conveniences for test methods and test case fixture setups. - protected static void registerDisputeAgents() { - arbClient.registerDisputeAgent(MEDIATOR, DEV_PRIVILEGE_PRIV_KEY); - arbClient.registerDisputeAgent(REFUND_AGENT, DEV_PRIVILEGE_PRIV_KEY); - } - protected static String encodeToHex(String s) { return Utilities.bytesAsHexString(s.getBytes(UTF_8)); } diff --git a/apitest/src/test/java/bisq/apitest/method/RegisterDisputeAgentsTest.java b/apitest/src/test/java/bisq/apitest/method/RegisterDisputeAgentsTest.java index 9b3a2e5f0b..b5011ffced 100644 --- a/apitest/src/test/java/bisq/apitest/method/RegisterDisputeAgentsTest.java +++ b/apitest/src/test/java/bisq/apitest/method/RegisterDisputeAgentsTest.java @@ -29,6 +29,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind; +import static bisq.apitest.config.ApiTestConfig.ARBITRATOR; +import static bisq.apitest.config.ApiTestConfig.MEDIATOR; +import static bisq.apitest.config.ApiTestConfig.REFUND_AGENT; import static bisq.apitest.config.BisqAppConfig.arbdaemon; import static bisq.apitest.config.BisqAppConfig.seednode; import static bisq.common.app.DevEnv.DEV_PRIVILEGE_PRIV_KEY; 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 c28a51de42..f0e95dd25f 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java @@ -19,6 +19,8 @@ package bisq.apitest.method.offer; import bisq.core.monetary.Altcoin; +import protobuf.PaymentAccount; + import org.bitcoinj.utils.Fiat; import java.math.BigDecimal; @@ -30,6 +32,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind; +import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.apitest.config.BisqAppConfig.alicedaemon; import static bisq.apitest.config.BisqAppConfig.arbdaemon; import static bisq.apitest.config.BisqAppConfig.bobdaemon; @@ -49,10 +52,13 @@ public abstract class AbstractOfferTest extends MethodTest { @Setter protected static boolean isLongRunningTest; + protected static PaymentAccount alicesBsqAcct; + protected static PaymentAccount bobsBsqAcct; + @BeforeAll public static void setUp() { startSupportingApps(true, - true, + false, bitcoind, seednode, arbdaemon, @@ -60,6 +66,18 @@ public abstract class AbstractOfferTest extends MethodTest { bobdaemon); } + + public static void createBsqPaymentAccounts() { + alicesBsqAcct = aliceClient.createCryptoCurrencyPaymentAccount("Alice's BSQ Account", + BSQ, + aliceClient.getUnusedBsqAddress(), + false); + bobsBsqAcct = bobClient.createCryptoCurrencyPaymentAccount("Bob's BSQ Account", + BSQ, + bobClient.getUnusedBsqAddress(), + false); + } + protected double getScaledOfferPrice(double offerPrice, String currencyCode) { int precision = isCryptoCurrency(currencyCode) ? Altcoin.SMALLEST_UNIT_EXPONENT : Fiat.SMALLEST_UNIT_EXPONENT; return scaleDownByPowerOf10(offerPrice, precision); 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 166d853c90..fe21e4aa8f 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java @@ -32,15 +32,17 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; 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.OfferPayload.Direction.BUY; @Disabled @Slf4j @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class CancelOfferTest extends AbstractOfferTest { - private static final String DIRECTION = "buy"; + private static final String DIRECTION = BUY.name(); private static final String CURRENCY_CODE = "cad"; private static final int MAX_OFFERS = 3; @@ -52,7 +54,7 @@ public class CancelOfferTest extends AbstractOfferTest { 0.00, getDefaultBuyerSecurityDepositAsPercent(), paymentAccountId, - "bsq"); + BSQ); }; @Test diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CreateBSQOffersTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CreateBSQOffersTest.java new file mode 100644 index 0000000000..ba4f8ce47b --- /dev/null +++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateBSQOffersTest.java @@ -0,0 +1,252 @@ +/* + * 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.apitest.method.offer; + +import bisq.proto.grpc.OfferInfo; + +import java.util.List; + +import lombok.extern.slf4j.Slf4j; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import static bisq.apitest.config.ApiTestConfig.BSQ; +import static bisq.apitest.config.ApiTestConfig.BTC; +import static bisq.cli.TableFormat.formatBalancesTbls; +import static bisq.cli.TableFormat.formatOfferTable; +import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static protobuf.OfferPayload.Direction.BUY; +import static protobuf.OfferPayload.Direction.SELL; + +@Disabled +@Slf4j +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class CreateBSQOffersTest extends AbstractOfferTest { + + private static final String MAKER_FEE_CURRENCY_CODE = BSQ; + + @BeforeAll + public static void setUp() { + AbstractOfferTest.setUp(); + createBsqPaymentAccounts(); + } + + @Test + @Order(1) + public void testCreateBuy1BTCFor20KBSQOffer() { + // Remember alt coin trades are BTC trades. When placing an offer, you are + // offering to buy or sell BTC, not BSQ, XMR, etc. In this test case, + // Alice places an offer to BUY BTC with BSQ. + var newOffer = aliceClient.createFixedPricedOffer(BUY.name(), + BSQ, + 100_000_000L, + 100_000_000L, + "0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ + getDefaultBuyerSecurityDepositAsPercent(), + alicesBsqAcct.getId(), + MAKER_FEE_CURRENCY_CODE); + log.info("Sell BSQ (Buy BTC) OFFER:\n{}", formatOfferTable(singletonList(newOffer), BSQ)); + String newOfferId = newOffer.getId(); + assertNotEquals("", newOfferId); + assertEquals(BUY.name(), newOffer.getDirection()); + assertFalse(newOffer.getUseMarketBasedPrice()); + assertEquals(5_000, newOffer.getPrice()); + assertEquals(100_000_000L, newOffer.getAmount()); + assertEquals(100_000_000L, newOffer.getMinAmount()); + assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); + assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); + assertEquals(BSQ, newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getCounterCurrencyCode()); + assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); + + genBtcBlockAndWaitForOfferPreparation(); + + newOffer = aliceClient.getMyOffer(newOfferId); + assertEquals(newOfferId, newOffer.getId()); + assertEquals(BUY.name(), newOffer.getDirection()); + assertFalse(newOffer.getUseMarketBasedPrice()); + assertEquals(5_000, newOffer.getPrice()); + assertEquals(100_000_000L, newOffer.getAmount()); + assertEquals(100_000_000L, newOffer.getMinAmount()); + assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); + assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); + assertEquals(BSQ, newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getCounterCurrencyCode()); + assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); + } + + @Test + @Order(2) + public void testCreateSell1BTCFor20KBSQOffer() { + // Alice places an offer to SELL BTC for BSQ. + var newOffer = aliceClient.createFixedPricedOffer(SELL.name(), + BSQ, + 100_000_000L, + 100_000_000L, + "0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ + getDefaultBuyerSecurityDepositAsPercent(), + alicesBsqAcct.getId(), + MAKER_FEE_CURRENCY_CODE); + log.info("SELL 20K BSQ OFFER:\n{}", formatOfferTable(singletonList(newOffer), BSQ)); + String newOfferId = newOffer.getId(); + assertNotEquals("", newOfferId); + assertEquals(SELL.name(), newOffer.getDirection()); + assertFalse(newOffer.getUseMarketBasedPrice()); + assertEquals(5_000, newOffer.getPrice()); + assertEquals(100_000_000L, newOffer.getAmount()); + assertEquals(100_000_000L, newOffer.getMinAmount()); + assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); + assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); + assertEquals(BSQ, newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getCounterCurrencyCode()); + assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); + + genBtcBlockAndWaitForOfferPreparation(); + + newOffer = aliceClient.getMyOffer(newOfferId); + assertEquals(newOfferId, newOffer.getId()); + assertEquals(SELL.name(), newOffer.getDirection()); + assertFalse(newOffer.getUseMarketBasedPrice()); + assertEquals(5_000, newOffer.getPrice()); + assertEquals(100_000_000L, newOffer.getAmount()); + assertEquals(100_000_000L, newOffer.getMinAmount()); + assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); + assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); + assertEquals(BSQ, newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getCounterCurrencyCode()); + assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); + } + + @Test + @Order(3) + public void testCreateBuyBTCWith1To2KBSQOffer() { + // Alice places an offer to BUY 0.05 - 0.10 BTC with BSQ. + var newOffer = aliceClient.createFixedPricedOffer(BUY.name(), + BSQ, + 10_000_000L, + 5_000_000L, + "0.00005", // FIXED PRICE IN BTC sats FOR 1 BSQ + getDefaultBuyerSecurityDepositAsPercent(), + alicesBsqAcct.getId(), + MAKER_FEE_CURRENCY_CODE); + log.info("BUY 1-2K BSQ OFFER:\n{}", formatOfferTable(singletonList(newOffer), BSQ)); + String newOfferId = newOffer.getId(); + assertNotEquals("", newOfferId); + assertEquals(BUY.name(), newOffer.getDirection()); + assertFalse(newOffer.getUseMarketBasedPrice()); + assertEquals(5_000, newOffer.getPrice()); + assertEquals(10_000_000L, newOffer.getAmount()); + assertEquals(5_000_000L, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); + assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); + assertEquals(BSQ, newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getCounterCurrencyCode()); + assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); + + genBtcBlockAndWaitForOfferPreparation(); + + newOffer = aliceClient.getMyOffer(newOfferId); + assertEquals(newOfferId, newOffer.getId()); + assertEquals(BUY.name(), newOffer.getDirection()); + assertFalse(newOffer.getUseMarketBasedPrice()); + assertEquals(5_000, newOffer.getPrice()); + assertEquals(10_000_000L, newOffer.getAmount()); + assertEquals(5_000_000L, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); + assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); + assertEquals(BSQ, newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getCounterCurrencyCode()); + assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); + } + + @Test + @Order(4) + public void testCreateSellBTCFor5To10KBSQOffer() { + // Alice places an offer to SELL 0.25 - 0.50 BTC for BSQ. + var newOffer = aliceClient.createFixedPricedOffer(SELL.name(), + BSQ, + 50_000_000L, + 25_000_000L, + "0.00005", // FIXED PRICE IN BTC sats FOR 1 BSQ + getDefaultBuyerSecurityDepositAsPercent(), + alicesBsqAcct.getId(), + MAKER_FEE_CURRENCY_CODE); + log.info("SELL 5-10K BSQ OFFER:\n{}", formatOfferTable(singletonList(newOffer), BSQ)); + String newOfferId = newOffer.getId(); + assertNotEquals("", newOfferId); + assertEquals(SELL.name(), newOffer.getDirection()); + assertFalse(newOffer.getUseMarketBasedPrice()); + assertEquals(5_000, newOffer.getPrice()); + assertEquals(50_000_000L, newOffer.getAmount()); + assertEquals(25_000_000L, newOffer.getMinAmount()); + assertEquals(7_500_000, newOffer.getBuyerSecurityDeposit()); + assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); + assertEquals(BSQ, newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getCounterCurrencyCode()); + assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); + + genBtcBlockAndWaitForOfferPreparation(); + + newOffer = aliceClient.getMyOffer(newOfferId); + assertEquals(newOfferId, newOffer.getId()); + assertEquals(SELL.name(), newOffer.getDirection()); + assertFalse(newOffer.getUseMarketBasedPrice()); + assertEquals(5_000, newOffer.getPrice()); + assertEquals(50_000_000L, newOffer.getAmount()); + assertEquals(25_000_000L, newOffer.getMinAmount()); + assertEquals(7_500_000, newOffer.getBuyerSecurityDeposit()); + assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); + assertEquals(BSQ, newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getCounterCurrencyCode()); + assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); + } + + @Test + @Order(5) + public void testGetAllMyBsqOffers() { + List offers = aliceClient.getMyBsqOffersSortedByDate(); + log.info("ALL ALICE'S BSQ OFFERS:\n{}", formatOfferTable(offers, BSQ)); + assertEquals(4, offers.size()); + log.info("ALICE'S BALANCES\n{}", formatBalancesTbls(aliceClient.getBalances())); + } + + @Test + @Order(6) + public void testGetAvailableBsqOffers() { + List offers = bobClient.getBsqOffersSortedByDate(); + log.info("ALL BOB'S AVAILABLE BSQ OFFERS:\n{}", formatOfferTable(offers, BSQ)); + assertEquals(4, offers.size()); + log.info("BOB'S BALANCES\n{}", formatBalancesTbls(bobClient.getBalances())); + } + + 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); + } +} 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 2e8b2962b2..081c6feadc 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingFixedPriceTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingFixedPriceTest.java @@ -27,53 +27,60 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +import static bisq.apitest.config.ApiTestConfig.BSQ; +import static bisq.apitest.config.ApiTestConfig.BTC; +import static bisq.cli.TableFormat.formatOfferTable; import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; +import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static protobuf.OfferPayload.Direction.BUY; +import static protobuf.OfferPayload.Direction.SELL; @Disabled @Slf4j @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { - private static final String MAKER_FEE_CURRENCY_CODE = "bsq"; + private static final String MAKER_FEE_CURRENCY_CODE = BSQ; @Test @Order(1) public void testCreateAUDBTCBuyOfferUsingFixedPrice16000() { PaymentAccount audAccount = createDummyF2FAccount(aliceClient, "AU"); - var newOffer = aliceClient.createFixedPricedOffer("buy", + var newOffer = aliceClient.createFixedPricedOffer(BUY.name(), "aud", - 10000000L, - 10000000L, + 10_000_000L, + 10_000_000L, "36000", getDefaultBuyerSecurityDepositAsPercent(), audAccount.getId(), MAKER_FEE_CURRENCY_CODE); + log.info("OFFER #1:\n{}", formatOfferTable(singletonList(newOffer), "AUD")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); - assertEquals("BUY", newOffer.getDirection()); + assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(360000000, newOffer.getPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(360_000_000, newOffer.getPrice()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(10_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(audAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("AUD", newOffer.getCounterCurrencyCode()); assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); - assertEquals("BUY", newOffer.getDirection()); + assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(360000000, newOffer.getPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(360_000_000, newOffer.getPrice()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(10_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(audAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("AUD", newOffer.getCounterCurrencyCode()); assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); } @@ -82,37 +89,38 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { @Order(2) public void testCreateUSDBTCBuyOfferUsingFixedPrice100001234() { PaymentAccount usdAccount = createDummyF2FAccount(aliceClient, "US"); - var newOffer = aliceClient.createFixedPricedOffer("buy", + var newOffer = aliceClient.createFixedPricedOffer(BUY.name(), "usd", - 10000000L, - 10000000L, + 10_000_000L, + 10_000_000L, "30000.1234", getDefaultBuyerSecurityDepositAsPercent(), usdAccount.getId(), MAKER_FEE_CURRENCY_CODE); + log.info("OFFER #2:\n{}", formatOfferTable(singletonList(newOffer), "USD")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); - assertEquals("BUY", newOffer.getDirection()); + assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(300001234, newOffer.getPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(300_001_234, newOffer.getPrice()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(10_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("USD", newOffer.getCounterCurrencyCode()); assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); - assertEquals("BUY", newOffer.getDirection()); + assertEquals(BUY.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(300001234, newOffer.getPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(300_001_234, newOffer.getPrice()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(10_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("USD", newOffer.getCounterCurrencyCode()); assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); } @@ -121,37 +129,38 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { @Order(3) public void testCreateEURBTCSellOfferUsingFixedPrice95001234() { PaymentAccount eurAccount = createDummyF2FAccount(aliceClient, "FR"); - var newOffer = aliceClient.createFixedPricedOffer("sell", + var newOffer = aliceClient.createFixedPricedOffer(SELL.name(), "eur", - 10000000L, - 10000000L, + 10_000_000L, + 5_000_000L, "29500.1234", getDefaultBuyerSecurityDepositAsPercent(), eurAccount.getId(), MAKER_FEE_CURRENCY_CODE); + log.info("OFFER #3:\n{}", formatOfferTable(singletonList(newOffer), "EUR")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); - assertEquals("SELL", newOffer.getDirection()); + assertEquals(SELL.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(295001234, newOffer.getPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(295_001_234, newOffer.getPrice()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(5_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(eurAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("EUR", newOffer.getCounterCurrencyCode()); assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); - assertEquals("SELL", newOffer.getDirection()); + assertEquals(SELL.name(), newOffer.getDirection()); assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(295001234, newOffer.getPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(295_001_234, newOffer.getPrice()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(5_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(eurAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("EUR", newOffer.getCounterCurrencyCode()); assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); } 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 4dad4152e1..94c2519d91 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java @@ -31,15 +31,19 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +import static bisq.apitest.config.ApiTestConfig.BTC; +import static bisq.cli.TableFormat.formatOfferTable; import static bisq.common.util.MathUtils.scaleDownByPowerOf10; import static bisq.common.util.MathUtils.scaleUpByPowerOf10; import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static java.lang.Math.abs; import static java.lang.String.format; +import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static protobuf.OfferPayload.Direction.BUY; +import static protobuf.OfferPayload.Direction.SELL; @Disabled @Slf4j @@ -50,42 +54,43 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { private static final double MKT_PRICE_MARGIN_ERROR_TOLERANCE = 0.0050; // 0.50% private static final double MKT_PRICE_MARGIN_WARNING_TOLERANCE = 0.0001; // 0.01% - private static final String MAKER_FEE_CURRENCY_CODE = "btc"; + private static final String MAKER_FEE_CURRENCY_CODE = BTC; @Test @Order(1) public void testCreateUSDBTCBuyOffer5PctPriceMargin() { PaymentAccount usdAccount = createDummyF2FAccount(aliceClient, "US"); double priceMarginPctInput = 5.00; - var newOffer = aliceClient.createMarketBasedPricedOffer("buy", + var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(), "usd", - 10000000L, - 10000000L, + 10_000_000L, + 10_000_000L, priceMarginPctInput, getDefaultBuyerSecurityDepositAsPercent(), usdAccount.getId(), MAKER_FEE_CURRENCY_CODE); + log.info("OFFER #1:\n{}", formatOfferTable(singletonList(newOffer), "usd")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); - assertEquals("BUY", newOffer.getDirection()); + assertEquals(BUY.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(10_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("USD", newOffer.getCounterCurrencyCode()); assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); - assertEquals("BUY", newOffer.getDirection()); + assertEquals(BUY.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(10_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("USD", newOffer.getCounterCurrencyCode()); assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); @@ -97,51 +102,36 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { public void testCreateNZDBTCBuyOfferMinus2PctPriceMargin() { PaymentAccount nzdAccount = createDummyF2FAccount(aliceClient, "NZ"); double priceMarginPctInput = -2.00; - /* - var req = CreateOfferRequest.newBuilder() - .setPaymentAccountId(nzdAccount.getId()) - .setDirection("buy") - .setCurrencyCode("nzd") - .setAmount(10000000) - .setMinAmount(10000000) - .setUseMarketBasedPrice(true) - .setMarketPriceMargin(priceMarginPctInput) - .setPrice("0") - .setBuyerSecurityDeposit(getDefaultBuyerSecurityDepositAsPercent()) - .setMakerFeeCurrencyCode(MAKER_FEE_CURRENCY_CODE) - .build(); - var newOffer = aliceStubs.offersService.createOffer(req).getOffer(); - - */ - var newOffer = aliceClient.createMarketBasedPricedOffer("buy", + var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(), "nzd", - 10000000L, - 10000000L, + 10_000_000L, + 10_000_000L, priceMarginPctInput, getDefaultBuyerSecurityDepositAsPercent(), nzdAccount.getId(), MAKER_FEE_CURRENCY_CODE); + log.info("OFFER #2:\n{}", formatOfferTable(singletonList(newOffer), "nzd")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); - assertEquals("BUY", newOffer.getDirection()); + assertEquals(BUY.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(10_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(nzdAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("NZD", newOffer.getCounterCurrencyCode()); assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); - assertEquals("BUY", newOffer.getDirection()); + assertEquals(BUY.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(10_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(nzdAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("NZD", newOffer.getCounterCurrencyCode()); assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); @@ -153,35 +143,36 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { public void testCreateGBPBTCSellOfferMinus1Point5PctPriceMargin() { PaymentAccount gbpAccount = createDummyF2FAccount(aliceClient, "GB"); double priceMarginPctInput = -1.5; - var newOffer = aliceClient.createMarketBasedPricedOffer("sell", + var newOffer = aliceClient.createMarketBasedPricedOffer(SELL.name(), "gbp", - 10000000L, - 10000000L, + 10_000_000L, + 5_000_000L, priceMarginPctInput, getDefaultBuyerSecurityDepositAsPercent(), gbpAccount.getId(), MAKER_FEE_CURRENCY_CODE); + log.info("OFFER #3:\n{}", formatOfferTable(singletonList(newOffer), "gbp")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); - assertEquals("SELL", newOffer.getDirection()); + assertEquals(SELL.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(5_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(gbpAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("GBP", newOffer.getCounterCurrencyCode()); assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); - assertEquals("SELL", newOffer.getDirection()); + assertEquals(SELL.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(5_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(gbpAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("GBP", newOffer.getCounterCurrencyCode()); assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); @@ -193,35 +184,36 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { public void testCreateBRLBTCSellOffer6Point55PctPriceMargin() { PaymentAccount brlAccount = createDummyF2FAccount(aliceClient, "BR"); double priceMarginPctInput = 6.55; - var newOffer = aliceClient.createMarketBasedPricedOffer("sell", + var newOffer = aliceClient.createMarketBasedPricedOffer(SELL.name(), "brl", - 10000000L, - 10000000L, + 10_000_000L, + 5_000_000L, priceMarginPctInput, getDefaultBuyerSecurityDepositAsPercent(), brlAccount.getId(), MAKER_FEE_CURRENCY_CODE); + log.info("OFFER #4:\n{}", formatOfferTable(singletonList(newOffer), "brl")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); - assertEquals("SELL", newOffer.getDirection()); + assertEquals(SELL.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(5_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(brlAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("BRL", newOffer.getCounterCurrencyCode()); assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); - assertEquals("SELL", newOffer.getDirection()); + assertEquals(SELL.name(), newOffer.getDirection()); assertTrue(newOffer.getUseMarketBasedPrice()); - assertEquals(10000000, newOffer.getAmount()); - assertEquals(10000000, newOffer.getMinAmount()); - assertEquals(1500000, newOffer.getBuyerSecurityDeposit()); + assertEquals(10_000_000, newOffer.getAmount()); + assertEquals(5_000_000, newOffer.getMinAmount()); + assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); assertEquals(brlAccount.getId(), newOffer.getPaymentAccountId()); - assertEquals("BTC", newOffer.getBaseCurrencyCode()); + assertEquals(BTC, newOffer.getBaseCurrencyCode()); assertEquals("BRL", newOffer.getCounterCurrencyCode()); assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); 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 65f8c83f60..33626ee6c3 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/ValidateCreateOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/ValidateCreateOfferTest.java @@ -29,10 +29,13 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +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; +import static protobuf.OfferPayload.Direction.BUY; @Disabled @Slf4j @@ -45,16 +48,15 @@ public class ValidateCreateOfferTest extends AbstractOfferTest { PaymentAccount usdAccount = createDummyF2FAccount(aliceClient, "US"); @SuppressWarnings("ResultOfMethodCallIgnored") Throwable exception = assertThrows(StatusRuntimeException.class, () -> - aliceClient.createFixedPricedOffer("buy", + aliceClient.createFixedPricedOffer(BUY.name(), "usd", 100000000000L, // exceeds amount limit 100000000000L, "10000.0000", getDefaultBuyerSecurityDepositAsPercent(), usdAccount.getId(), - "bsq")); - assertEquals("UNKNOWN: An error occurred at task: ValidateOffer", - exception.getMessage()); + BSQ)); + assertEquals("UNKNOWN: An error occurred at task: ValidateOffer", exception.getMessage()); } @Test @@ -63,14 +65,14 @@ public class ValidateCreateOfferTest extends AbstractOfferTest { PaymentAccount chfAccount = createDummyF2FAccount(aliceClient, "ch"); @SuppressWarnings("ResultOfMethodCallIgnored") Throwable exception = assertThrows(StatusRuntimeException.class, () -> - aliceClient.createFixedPricedOffer("buy", + aliceClient.createFixedPricedOffer(BUY.name(), "eur", 10000000L, 10000000L, "40000.0000", getDefaultBuyerSecurityDepositAsPercent(), chfAccount.getId(), - "btc")); + BTC)); String expectedError = format("UNKNOWN: cannot create EUR offer with payment account %s", chfAccount.getId()); assertEquals(expectedError, exception.getMessage()); } @@ -81,14 +83,14 @@ public class ValidateCreateOfferTest extends AbstractOfferTest { PaymentAccount audAccount = createDummyF2FAccount(aliceClient, "au"); @SuppressWarnings("ResultOfMethodCallIgnored") Throwable exception = assertThrows(StatusRuntimeException.class, () -> - aliceClient.createFixedPricedOffer("buy", + aliceClient.createFixedPricedOffer(BUY.name(), "cad", 10000000L, 10000000L, "63000.0000", getDefaultBuyerSecurityDepositAsPercent(), audAccount.getId(), - "btc")); + BTC)); String expectedError = format("UNKNOWN: cannot create CAD offer with payment account %s", audAccount.getId()); assertEquals(expectedError, exception.getMessage()); } 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 161d2f1283..4c4a6b3453 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java @@ -9,13 +9,16 @@ import org.slf4j.Logger; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.TestInfo; +import static bisq.cli.CurrencyFormat.formatBsqAmount; import static bisq.cli.TradeFormat.format; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; import bisq.apitest.method.offer.AbstractOfferTest; +import bisq.cli.GrpcClient; public class AbstractTradeTest extends AbstractOfferTest { @@ -59,11 +62,58 @@ public class AbstractTradeTest extends AbstractOfferTest { assertEquals(EXPECTED_PROTOCOL_STATUS.isWithdrawn, trade.getIsWithdrawn()); } + protected final void sendBsqPayment(Logger log, + GrpcClient grpcClient, + TradeInfo trade) { + var contract = trade.getContract(); + String receiverAddress = contract.getIsBuyerMakerAndSellerTaker() + ? contract.getTakerPaymentAccountPayload().getAddress() + : contract.getMakerPaymentAccountPayload().getAddress(); + String sendBsqAmount = formatBsqAmount(trade.getOffer().getVolume()); + log.info("Sending {} BSQ to address {}", sendBsqAmount, receiverAddress); + grpcClient.sendBsq(receiverAddress, sendBsqAmount, ""); + } + + protected final void verifyBsqPaymentHasBeenReceived(Logger log, + GrpcClient grpcClient, + TradeInfo trade) { + var contract = trade.getContract(); + var bsqSats = trade.getOffer().getVolume(); + var receiveAmountAsString = formatBsqAmount(bsqSats); + var address = contract.getIsBuyerMakerAndSellerTaker() + ? contract.getTakerPaymentAccountPayload().getAddress() + : contract.getMakerPaymentAccountPayload().getAddress(); + boolean receivedBsqSatoshis = grpcClient.verifyBsqSentToAddress(address, receiveAmountAsString); + if (receivedBsqSatoshis) + log.info("Payment of {} BSQ was received to address {} for trade with id {}.", + receiveAmountAsString, + address, + trade.getTradeId()); + else + fail(String.format("Payment of %s BSQ was was not sent to address %s for trade with id %s.", + receiveAmountAsString, + address, + trade.getTradeId())); + } + protected final void logTrade(Logger log, TestInfo testInfo, String description, TradeInfo trade) { - if (log.isDebugEnabled()) { + logTrade(log, testInfo, description, trade, false); + } + + protected final void logTrade(Logger log, + TestInfo testInfo, + String description, + TradeInfo trade, + boolean force) { + if (force) + log.info(String.format("%s %s%n%s", + testName(testInfo), + description.toUpperCase(), + format(trade))); + else if (log.isDebugEnabled()) { log.debug(String.format("%s %s%n%s", testName(testInfo), description.toUpperCase(), diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBSQOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBSQOfferTest.java new file mode 100644 index 0000000000..fc365931d5 --- /dev/null +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBSQOfferTest.java @@ -0,0 +1,304 @@ +/* + * 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.apitest.method.trade; + +import bisq.proto.grpc.TradeInfo; + +import io.grpc.StatusRuntimeException; + +import java.util.function.Predicate; + +import lombok.extern.slf4j.Slf4j; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.TestMethodOrder; + +import static bisq.apitest.config.ApiTestConfig.BSQ; +import static bisq.cli.TableFormat.formatBalancesTbls; +import static bisq.cli.TableFormat.formatOfferTable; +import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; +import static bisq.core.trade.Trade.Phase.DEPOSIT_CONFIRMED; +import static bisq.core.trade.Trade.Phase.FIAT_SENT; +import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED; +import static bisq.core.trade.Trade.State.BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG; +import static bisq.core.trade.Trade.State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN; +import static bisq.core.trade.Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG; +import static bisq.core.trade.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG; +import static java.lang.String.format; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; +import static protobuf.Offer.State.OFFER_FEE_PAID; +import static protobuf.OfferPayload.Direction.SELL; + + + +import bisq.apitest.method.offer.AbstractOfferTest; + +// https://github.com/ghubstan/bisq/blob/master/cli/src/main/java/bisq/cli/TradeFormat.java + +@Disabled +@Slf4j +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TakeBuyBSQOfferTest extends AbstractTradeTest { + + // Alice is maker / bsq buyer (btc seller), Bob is taker / bsq seller (btc buyer). + + // Maker and Taker fees are in BSQ. + private static final String TRADE_FEE_CURRENCY_CODE = BSQ; + + @BeforeAll + public static void setUp() { + AbstractOfferTest.setUp(); + createBsqPaymentAccounts(); + EXPECTED_PROTOCOL_STATUS.init(); + } + + @Test + @Order(1) + public void testTakeAlicesSellBTCForBSQOffer(final TestInfo testInfo) { + try { + // Alice is going to BUY BSQ, but the Offer direction = SELL because it is a + // BTC trade; Alice will SELL BTC for BSQ. Bob will send Alice BSQ. + // Confused me, but just need to remember there are only BTC offers. + var btcTradeDirection = SELL.name(); + var alicesOffer = aliceClient.createFixedPricedOffer(btcTradeDirection, + BSQ, + 15_000_000L, + 7_500_000L, + "0.000035", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ + getDefaultBuyerSecurityDepositAsPercent(), + alicesBsqAcct.getId(), + TRADE_FEE_CURRENCY_CODE); + log.info("ALICE'S BUY BSQ (SELL BTC) OFFER:\n{}", formatOfferTable(singletonList(alicesOffer), BSQ)); + genBtcBlocksThenWait(1, 5000); + var offerId = alicesOffer.getId(); + assertFalse(alicesOffer.getIsCurrencyForMakerFeeBtc()); + + var alicesBsqOffers = aliceClient.getMyCryptoCurrencyOffers(btcTradeDirection, BSQ); + assertEquals(1, alicesBsqOffers.size()); + + var trade = takeAlicesOffer(offerId, bobsBsqAcct.getId(), TRADE_FEE_CURRENCY_CODE); + assertNotNull(trade); + assertEquals(offerId, trade.getTradeId()); + assertFalse(trade.getIsCurrencyForTakerFeeBtc()); + // Cache the trade id for the other tests. + tradeId = trade.getTradeId(); + + genBtcBlocksThenWait(1, 6000); + alicesBsqOffers = aliceClient.getMyBsqOffersSortedByDate(); + assertEquals(0, alicesBsqOffers.size()); + + for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { + trade = bobClient.getTrade(trade.getTradeId()); + + if (!trade.getIsDepositConfirmed()) { + log.warn("Bob still waiting on trade {} tx {}: DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN, attempt # {}", + trade.getShortId(), + trade.getDepositTxId(), + i); + genBtcBlocksThenWait(1, 4000); + continue; + } else { + EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN) + .setPhase(DEPOSIT_CONFIRMED) + .setDepositPublished(true) + .setDepositConfirmed(true); + verifyExpectedProtocolStatus(trade); + logTrade(log, testInfo, "Bob's view after taking offer and deposit confirmed", trade); + break; + } + } + + genBtcBlocksThenWait(1, 2500); + + if (!trade.getIsDepositConfirmed()) { + fail(format("INVALID_PHASE for Bob's trade %s in STATE=%s PHASE=%s, deposit tx was never confirmed.", + trade.getShortId(), + trade.getState(), + trade.getPhase())); + } + + logTrade(log, testInfo, "Alice's Maker/Buyer View", aliceClient.getTrade(tradeId), true); + logTrade(log, testInfo, "Bob's Taker/Seller View", bobClient.getTrade(tradeId), true); + + } catch (StatusRuntimeException e) { + fail(e); + } + } + + @Test + @Order(2) + public void testBobsConfirmPaymentStarted(final TestInfo testInfo) { + try { + var trade = bobClient.getTrade(tradeId); + + Predicate tradeStateAndPhaseCorrect = (t) -> + t.getState().equals(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN.name()) + && t.getPhase().equals(DEPOSIT_CONFIRMED.name()); + + for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { + if (!tradeStateAndPhaseCorrect.test(trade)) { + log.warn("INVALID_PHASE for Bob's trade {} in STATE={} PHASE={}, cannot send payment started msg yet.", + trade.getShortId(), + trade.getState(), + trade.getPhase()); + sleep(10_000); + trade = bobClient.getTrade(tradeId); + continue; + } else { + break; + } + } + + if (!tradeStateAndPhaseCorrect.test(trade)) { + fail(format("INVALID_PHASE for Bob's trade %s in STATE=%s PHASE=%s, could not send payment started msg.", + trade.getShortId(), + trade.getState(), + trade.getPhase())); + } + + sendBsqPayment(log, bobClient, trade); + genBtcBlocksThenWait(1, 2500); + bobClient.confirmPaymentStarted(trade.getTradeId()); + sleep(6000); + + for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { + trade = aliceClient.getTrade(tradeId); + + if (!trade.getIsFiatSent()) { + log.warn("Alice still waiting for trade {} SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG, attempt # {}", + trade.getShortId(), + i); + sleep(5000); + continue; + } else { + // Warning: trade.getOffer().getState() might be AVAILABLE, not OFFER_FEE_PAID. + EXPECTED_PROTOCOL_STATUS.setState(SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) + .setPhase(FIAT_SENT) + .setFiatSent(true); + verifyExpectedProtocolStatus(trade); + logTrade(log, testInfo, "Alice's view after confirming fiat payment received", trade); + break; + } + } + + logTrade(log, testInfo, "Alice's Maker/Buyer View (Payment Sent)", aliceClient.getTrade(tradeId), true); + logTrade(log, testInfo, "Bob's Taker/Seller View (Payment Sent)", bobClient.getTrade(tradeId), true); + + } catch (StatusRuntimeException e) { + fail(e); + } + } + + @Test + @Order(3) + public void testAlicesConfirmPaymentReceived(final TestInfo testInfo) { + try { + var trade = aliceClient.getTrade(tradeId); + + Predicate tradeStateAndPhaseCorrect = (t) -> + t.getState().equals(SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG.name()) + && (t.getPhase().equals(PAYOUT_PUBLISHED.name()) || t.getPhase().equals(FIAT_SENT.name())); + + for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { + if (!tradeStateAndPhaseCorrect.test(trade)) { + log.warn("INVALID_PHASE for Alice's trade {} in STATE={} PHASE={}, cannot confirm payment received yet.", + trade.getShortId(), + trade.getState(), + trade.getPhase()); + sleep(1000 * 10); + trade = aliceClient.getTrade(tradeId); + continue; + } else { + break; + } + } + + if (!tradeStateAndPhaseCorrect.test(trade)) { + fail(format("INVALID_PHASE for Alice's trade %s in STATE=%s PHASE=%s, cannot confirm payment received.", + trade.getShortId(), + trade.getState(), + trade.getPhase())); + } + + sleep(2000); + verifyBsqPaymentHasBeenReceived(log, aliceClient, trade); + + aliceClient.confirmPaymentReceived(trade.getTradeId()); + sleep(3000); + + trade = aliceClient.getTrade(tradeId); + assertEquals(OFFER_FEE_PAID.name(), trade.getOffer().getState()); + EXPECTED_PROTOCOL_STATUS.setState(SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG) + .setPhase(PAYOUT_PUBLISHED) + .setPayoutPublished(true) + .setFiatReceived(true); + verifyExpectedProtocolStatus(trade); + logTrade(log, testInfo, "Alice's view after confirming fiat payment received", trade); + + logTrade(log, testInfo, "Alice's Maker/Buyer View (Payment Received)", aliceClient.getTrade(tradeId), true); + logTrade(log, testInfo, "Bob's Taker/Seller View (Payment Received)", bobClient.getTrade(tradeId), true); + + } catch (StatusRuntimeException e) { + fail(e); + } + } + + @Test + @Order(4) + public void testBobsKeepFunds(final TestInfo testInfo) { + try { + genBtcBlocksThenWait(1, 1000); + + var trade = bobClient.getTrade(tradeId); + logTrade(log, testInfo, "Alice's view before keeping funds", trade); + + bobClient.keepFunds(tradeId); + genBtcBlocksThenWait(1, 1000); + + trade = bobClient.getTrade(tradeId); + EXPECTED_PROTOCOL_STATUS.setState(BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG) + .setPhase(PAYOUT_PUBLISHED); + verifyExpectedProtocolStatus(trade); + logTrade(log, testInfo, "Alice's view after keeping funds", trade); + + logTrade(log, testInfo, "Alice's Maker/Buyer View (Done)", aliceClient.getTrade(tradeId), true); + logTrade(log, testInfo, "Bob's Taker/Seller View (Done)", bobClient.getTrade(tradeId), true); + + var alicesBalances = aliceClient.getBalances(); + log.info("{} Alice's Current Balance:\n{}", + testName(testInfo), + formatBalancesTbls(alicesBalances)); + var bobsBalances = bobClient.getBalances(); + log.info("{} Bob's Current Balance:\n{}", + testName(testInfo), + formatBalancesTbls(bobsBalances)); + + } catch (StatusRuntimeException e) { + fail(e); + } + } +} 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 8c2f96fecb..93d9b1b9c8 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java @@ -19,7 +19,6 @@ package bisq.apitest.method.trade; import bisq.core.payment.PaymentAccount; -import bisq.proto.grpc.BtcBalanceInfo; import bisq.proto.grpc.TradeInfo; import io.grpc.StatusRuntimeException; @@ -35,10 +34,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.TestMethodOrder; -import static bisq.cli.CurrencyFormat.formatSatoshis; +import static bisq.apitest.config.ApiTestConfig.BSQ; +import static bisq.cli.TableFormat.formatBalancesTbls; import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static bisq.core.trade.Trade.Phase.DEPOSIT_CONFIRMED; -import static bisq.core.trade.Trade.Phase.DEPOSIT_PUBLISHED; import static bisq.core.trade.Trade.Phase.FIAT_SENT; import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED; import static bisq.core.trade.Trade.State.*; @@ -48,12 +47,9 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import static protobuf.Offer.State.OFFER_FEE_PAID; +import static protobuf.OfferPayload.Direction.BUY; import static protobuf.OpenOffer.State.AVAILABLE; - - -import bisq.cli.TradeFormat; - @Disabled @Slf4j @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @@ -62,17 +58,17 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest { // Alice is maker/buyer, Bob is taker/seller. // Maker and Taker fees are in BSQ. - private static final String TRADE_FEE_CURRENCY_CODE = "bsq"; + private static final String TRADE_FEE_CURRENCY_CODE = BSQ; @Test @Order(1) public void testTakeAlicesBuyOffer(final TestInfo testInfo) { try { PaymentAccount alicesUsdAccount = createDummyF2FAccount(aliceClient, "US"); - var alicesOffer = aliceClient.createMarketBasedPricedOffer("buy", + var alicesOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(), "usd", - 12500000, - 12500000, // min-amount = amount + 12_500_000L, + 12_500_000L, // min-amount = amount 0.00, getDefaultBuyerSecurityDepositAsPercent(), alicesUsdAccount.getId(), @@ -83,7 +79,7 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest { // Wait for Alice's AddToOfferBook task. // Wait times vary; my logs show >= 2 second delay. sleep(3000); // TODO loop instead of hard code wait time - var alicesUsdOffers = aliceClient.getMyOffersSortedByDate("buy", "usd"); + var alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), "usd"); assertEquals(1, alicesUsdOffers.size()); PaymentAccount bobsUsdAccount = createDummyF2FAccount(bobClient, "US"); @@ -95,18 +91,9 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest { tradeId = trade.getTradeId(); genBtcBlocksThenWait(1, 4000); - alicesUsdOffers = aliceClient.getMyOffersSortedByDate("buy", "usd"); + alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), "usd"); assertEquals(0, alicesUsdOffers.size()); - if (!isLongRunningTest) { - trade = bobClient.getTrade(trade.getTradeId()); - EXPECTED_PROTOCOL_STATUS.setState(SELLER_PUBLISHED_DEPOSIT_TX) - .setPhase(DEPOSIT_PUBLISHED) - .setDepositPublished(true); - verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Bob's view after taking offer and sending deposit", trade); - } - genBtcBlocksThenWait(1, 2500); for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { @@ -117,14 +104,15 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest { trade.getShortId(), trade.getDepositTxId(), i); - sleep(5000); + genBtcBlocksThenWait(1, 4000); continue; } else { EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN) .setPhase(DEPOSIT_CONFIRMED) + .setDepositPublished(true) .setDepositConfirmed(true); verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Bob's view after deposit is confirmed", trade); + logTrade(log, testInfo, "Bob's view after deposit is confirmed", trade, true); break; } } @@ -269,11 +257,18 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest { .setPhase(PAYOUT_PUBLISHED); verifyExpectedProtocolStatus(trade); logTrade(log, testInfo, "Alice's view after keeping funds", trade); - BtcBalanceInfo currentBalance = aliceClient.getBtcBalances(); - log.info("{} Alice's current available balance: {} BTC. Last trade:\n{}", + + logTrade(log, testInfo, "Alice's Maker/Buyer View (Done)", aliceClient.getTrade(tradeId), true); + logTrade(log, testInfo, "Bob's Taker/Seller View (Done)", bobClient.getTrade(tradeId), true); + + var alicesBalances = aliceClient.getBalances(); + log.info("{} Alice's Current Balance:\n{}", testName(testInfo), - formatSatoshis(currentBalance.getAvailableBalance()), - TradeFormat.format(aliceClient.getTrade(tradeId))); + formatBalancesTbls(alicesBalances)); + var bobsBalances = bobClient.getBalances(); + log.info("{} Bob's Current Balance:\n{}", + testName(testInfo), + formatBalancesTbls(bobsBalances)); } catch (StatusRuntimeException e) { fail(e); } diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBSQOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBSQOfferTest.java new file mode 100644 index 0000000000..786601e6fa --- /dev/null +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBSQOfferTest.java @@ -0,0 +1,309 @@ +/* + * 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.apitest.method.trade; + +import bisq.proto.grpc.TradeInfo; + +import io.grpc.StatusRuntimeException; + +import java.util.function.Predicate; + +import lombok.extern.slf4j.Slf4j; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.TestMethodOrder; + +import static bisq.apitest.config.ApiTestConfig.BSQ; +import static bisq.apitest.config.ApiTestConfig.BTC; +import static bisq.cli.TableFormat.formatBalancesTbls; +import static bisq.cli.TableFormat.formatOfferTable; +import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; +import static bisq.core.trade.Trade.Phase.DEPOSIT_CONFIRMED; +import static bisq.core.trade.Trade.Phase.FIAT_SENT; +import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED; +import static bisq.core.trade.Trade.Phase.WITHDRAWN; +import static bisq.core.trade.Trade.State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN; +import static bisq.core.trade.Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG; +import static bisq.core.trade.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG; +import static bisq.core.trade.Trade.State.WITHDRAW_COMPLETED; +import static java.lang.String.format; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static protobuf.OfferPayload.Direction.BUY; + + + +import bisq.apitest.method.offer.AbstractOfferTest; + +@Disabled +@Slf4j +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TakeSellBSQOfferTest extends AbstractTradeTest { + + // Alice is maker / bsq seller (btc buyer), Bob is taker / bsq buyer (btc seller). + + // Maker and Taker fees are in BTC. + private static final String TRADE_FEE_CURRENCY_CODE = BTC; + + private static final String WITHDRAWAL_TX_MEMO = "Bob's trade withdrawal"; + + @BeforeAll + public static void setUp() { + AbstractOfferTest.setUp(); + createBsqPaymentAccounts(); + EXPECTED_PROTOCOL_STATUS.init(); + } + + @Test + @Order(1) + public void testTakeAlicesBuyBTCForBSQOffer(final TestInfo testInfo) { + try { + // Alice is going to SELL BSQ, but the Offer direction = BUY because it is a + // BTC trade; Alice will BUY BTC for BSQ. Alice will send Bob BSQ. + // Confused me, but just need to remember there are only BTC offers. + var btcTradeDirection = BUY.name(); + var alicesOffer = aliceClient.createFixedPricedOffer(btcTradeDirection, + BSQ, + 15_000_000L, + 7_500_000L, + "0.000035", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ + getDefaultBuyerSecurityDepositAsPercent(), + alicesBsqAcct.getId(), + TRADE_FEE_CURRENCY_CODE); + log.info("ALICE'S SELL BSQ (BUY BTC) OFFER:\n{}", formatOfferTable(singletonList(alicesOffer), BSQ)); + genBtcBlocksThenWait(1, 4000); + var offerId = alicesOffer.getId(); + assertTrue(alicesOffer.getIsCurrencyForMakerFeeBtc()); + + var alicesBsqOffers = aliceClient.getMyCryptoCurrencyOffers(btcTradeDirection, BSQ); + assertEquals(1, alicesBsqOffers.size()); + + var trade = takeAlicesOffer(offerId, bobsBsqAcct.getId(), TRADE_FEE_CURRENCY_CODE); + assertNotNull(trade); + assertEquals(offerId, trade.getTradeId()); + assertTrue(trade.getIsCurrencyForTakerFeeBtc()); + // Cache the trade id for the other tests. + tradeId = trade.getTradeId(); + + genBtcBlocksThenWait(1, 6000); + alicesBsqOffers = aliceClient.getMyBsqOffersSortedByDate(); + assertEquals(0, alicesBsqOffers.size()); + + for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { + trade = bobClient.getTrade(trade.getTradeId()); + + if (!trade.getIsDepositConfirmed()) { + log.warn("Bob still waiting on trade {} tx {}: DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN, attempt # {}", + trade.getShortId(), + trade.getDepositTxId(), + i); + genBtcBlocksThenWait(1, 4000); + continue; + } else { + EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN) + .setPhase(DEPOSIT_CONFIRMED) + .setDepositPublished(true) + .setDepositConfirmed(true); + verifyExpectedProtocolStatus(trade); + logTrade(log, testInfo, "Bob's view after taking offer and deposit confirmed", trade); + break; + } + } + + genBtcBlocksThenWait(1, 2500); + + if (!trade.getIsDepositConfirmed()) { + fail(format("INVALID_PHASE for Bob's trade %s in STATE=%s PHASE=%s, deposit tx was never confirmed.", + trade.getShortId(), + trade.getState(), + trade.getPhase())); + } + + logTrade(log, testInfo, "Alice's Maker/Seller View", aliceClient.getTrade(tradeId), true); + logTrade(log, testInfo, "Bob's Taker/Buyer View", bobClient.getTrade(tradeId), true); + + } catch (StatusRuntimeException e) { + fail(e); + } + } + + @Test + @Order(2) + public void testAlicesConfirmPaymentStarted(final TestInfo testInfo) { + try { + var trade = aliceClient.getTrade(tradeId); + + Predicate tradeStateAndPhaseCorrect = (t) -> + t.getState().equals(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN.name()) + && t.getPhase().equals(DEPOSIT_CONFIRMED.name()); + + for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { + if (!tradeStateAndPhaseCorrect.test(trade)) { + log.warn("INVALID_PHASE for Alice's trade {} in STATE={} PHASE={}, cannot send payment started msg yet.", + trade.getShortId(), + trade.getState(), + trade.getPhase()); + sleep(10_000); + trade = aliceClient.getTrade(tradeId); + continue; + } else { + break; + } + } + + if (!tradeStateAndPhaseCorrect.test(trade)) { + fail(format("INVALID_PHASE for Alice's trade %s in STATE=%s PHASE=%s, could not send payment started msg.", + trade.getShortId(), + trade.getState(), + trade.getPhase())); + } + + sendBsqPayment(log, aliceClient, trade); + genBtcBlocksThenWait(1, 2500); + aliceClient.confirmPaymentStarted(trade.getTradeId()); + sleep(6000); + + for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { + trade = bobClient.getTrade(tradeId); + + if (!trade.getIsFiatSent()) { + log.warn("Bob still waiting for trade {} SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG, attempt # {}", + trade.getShortId(), + i); + sleep(5000); + continue; + } else { + // Warning: trade.getOffer().getState() might be AVAILABLE, not OFFER_FEE_PAID. + EXPECTED_PROTOCOL_STATUS.setState(SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) + .setPhase(FIAT_SENT) + .setFiatSent(true); + verifyExpectedProtocolStatus(trade); + logTrade(log, testInfo, "Alice's view after confirming fiat payment received", trade); + break; + } + } + + logTrade(log, testInfo, "Alice's Maker/Seller View (Payment Sent)", aliceClient.getTrade(tradeId), true); + logTrade(log, testInfo, "Bob's Taker/Buyer View (Payment Sent)", bobClient.getTrade(tradeId), true); + + } catch (StatusRuntimeException e) { + fail(e); + } + } + + @Test + @Order(3) + public void testBobsConfirmPaymentReceived(final TestInfo testInfo) { + try { + var trade = bobClient.getTrade(tradeId); + + Predicate tradeStateAndPhaseCorrect = (t) -> + t.getState().equals(SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG.name()) + && (t.getPhase().equals(PAYOUT_PUBLISHED.name()) || t.getPhase().equals(FIAT_SENT.name())); + + for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { + if (!tradeStateAndPhaseCorrect.test(trade)) { + log.warn("INVALID_PHASE for Bob's trade {} in STATE={} PHASE={}, cannot confirm payment received yet.", + trade.getShortId(), + trade.getState(), + trade.getPhase()); + sleep(1000 * 10); + trade = bobClient.getTrade(tradeId); + continue; + } else { + break; + } + } + + if (!tradeStateAndPhaseCorrect.test(trade)) { + fail(format("INVALID_PHASE for Bob's trade %s in STATE=%s PHASE=%s, cannot confirm payment received.", + trade.getShortId(), + trade.getState(), + trade.getPhase())); + } + + sleep(2000); + verifyBsqPaymentHasBeenReceived(log, bobClient, trade); + + bobClient.confirmPaymentReceived(trade.getTradeId()); + sleep(3000); + + trade = bobClient.getTrade(tradeId); + // Warning: trade.getOffer().getState() might be AVAILABLE, not OFFER_FEE_PAID. + EXPECTED_PROTOCOL_STATUS.setState(SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG) + .setPhase(PAYOUT_PUBLISHED) + .setPayoutPublished(true) + .setFiatReceived(true); + verifyExpectedProtocolStatus(trade); + logTrade(log, testInfo, "Alice's view after confirming fiat payment received", trade); + + logTrade(log, testInfo, "Alice's Maker/Seller View (Payment Received)", aliceClient.getTrade(tradeId), true); + logTrade(log, testInfo, "Bob's Taker/Buyer View (Payment Received)", bobClient.getTrade(tradeId), true); + + } catch (StatusRuntimeException e) { + fail(e); + } + } + + @Test + @Order(4) + public void testAlicesBtcWithdrawalToExternalAddress(final TestInfo testInfo) { + try { + genBtcBlocksThenWait(1, 1000); + + var trade = aliceClient.getTrade(tradeId); + logTrade(log, testInfo, "Alice's view before withdrawing BTC funds to external wallet", trade); + + String toAddress = bitcoinCli.getNewBtcAddress(); + aliceClient.withdrawFunds(tradeId, toAddress, WITHDRAWAL_TX_MEMO); + + genBtcBlocksThenWait(1, 1000); + + trade = aliceClient.getTrade(tradeId); + EXPECTED_PROTOCOL_STATUS.setState(WITHDRAW_COMPLETED) + .setPhase(WITHDRAWN) + .setWithdrawn(true); + verifyExpectedProtocolStatus(trade); + logTrade(log, testInfo, "Alice's view after withdrawing funds to external wallet", trade); + + + logTrade(log, testInfo, "Alice's Maker/Seller View (Done)", aliceClient.getTrade(tradeId), true); + logTrade(log, testInfo, "Bob's Taker/Buyer View (Done)", bobClient.getTrade(tradeId), true); + + var alicesBalances = aliceClient.getBalances(); + log.info("{} Alice's Current Balance:\n{}", + testName(testInfo), + formatBalancesTbls(alicesBalances)); + var bobsBalances = bobClient.getBalances(); + log.info("{} Bob's Current Balance:\n{}", + testName(testInfo), + formatBalancesTbls(bobsBalances)); + + } catch (StatusRuntimeException e) { + fail(e); + } + } +} 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 09d27453d3..ece3432123 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java @@ -19,7 +19,6 @@ package bisq.apitest.method.trade; import bisq.core.payment.PaymentAccount; -import bisq.proto.grpc.BtcBalanceInfo; import bisq.proto.grpc.TradeInfo; import io.grpc.StatusRuntimeException; @@ -35,9 +34,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.TestMethodOrder; -import static bisq.cli.CurrencyFormat.formatSatoshis; +import static bisq.apitest.config.ApiTestConfig.BTC; +import static bisq.cli.TableFormat.formatBalancesTbls; import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; -import static bisq.core.trade.Trade.Phase.*; +import static bisq.core.trade.Trade.Phase.DEPOSIT_CONFIRMED; +import static bisq.core.trade.Trade.Phase.FIAT_SENT; +import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED; +import static bisq.core.trade.Trade.Phase.WITHDRAWN; import static bisq.core.trade.Trade.State.*; import static java.lang.String.format; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -45,12 +48,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static protobuf.Offer.State.OFFER_FEE_PAID; +import static protobuf.OfferPayload.Direction.SELL; import static protobuf.OpenOffer.State.AVAILABLE; - - -import bisq.cli.TradeFormat; - @Disabled @Slf4j @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @@ -59,7 +59,7 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest { // Alice is maker/seller, Bob is taker/buyer. // Maker and Taker fees are in BTC. - private static final String TRADE_FEE_CURRENCY_CODE = "btc"; + private static final String TRADE_FEE_CURRENCY_CODE = BTC; private static final String WITHDRAWAL_TX_MEMO = "Bob's trade withdrawal"; @@ -68,10 +68,10 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest { public void testTakeAlicesSellOffer(final TestInfo testInfo) { try { PaymentAccount alicesUsdAccount = createDummyF2FAccount(aliceClient, "US"); - var alicesOffer = aliceClient.createMarketBasedPricedOffer("sell", + var alicesOffer = aliceClient.createMarketBasedPricedOffer(SELL.name(), "usd", - 12500000L, - 12500000L, // min-amount = amount + 12_500_000L, + 12_500_000L, // min-amount = amount 0.00, getDefaultBuyerSecurityDepositAsPercent(), alicesUsdAccount.getId(), @@ -83,7 +83,7 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest { // Wait times vary; my logs show >= 2 second delay, but taking sell offers // seems to require more time to prepare. sleep(3000); // TODO loop instead of hard code wait time - var alicesUsdOffers = aliceClient.getMyOffersSortedByDate("sell", "usd"); + var alicesUsdOffers = aliceClient.getMyOffersSortedByDate(SELL.name(), "usd"); assertEquals(1, alicesUsdOffers.size()); PaymentAccount bobsUsdAccount = createDummyF2FAccount(bobClient, "US"); @@ -95,18 +95,9 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest { tradeId = trade.getTradeId(); genBtcBlocksThenWait(1, 4000); - var takeableUsdOffers = bobClient.getOffersSortedByDate("sell", "usd"); + var takeableUsdOffers = bobClient.getOffersSortedByDate(SELL.name(), "usd"); assertEquals(0, takeableUsdOffers.size()); - if (!isLongRunningTest) { - trade = bobClient.getTrade(trade.getTradeId()); - EXPECTED_PROTOCOL_STATUS.setState(BUYER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG) - .setPhase(DEPOSIT_PUBLISHED) - .setDepositPublished(true); - verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Bob's view after taking offer and sending deposit", trade); - } - genBtcBlocksThenWait(1, 2500); for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { @@ -117,14 +108,15 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest { trade.getShortId(), trade.getDepositTxId(), i); - sleep(5000); + genBtcBlocksThenWait(1, 4000); continue; } else { EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN) .setPhase(DEPOSIT_CONFIRMED) + .setDepositPublished(true) .setDepositConfirmed(true); verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Bob's view after deposit is confirmed", trade); + logTrade(log, testInfo, "Bob's view after deposit is confirmed", trade, true); break; } } @@ -265,12 +257,19 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest { .setPhase(WITHDRAWN) .setWithdrawn(true); verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Bob's view after withdrawing funds to external wallet", trade); - BtcBalanceInfo currentBalance = bobClient.getBtcBalances(); - log.info("{} Bob's current available balance: {} BTC. Last trade:\n{}", + logTrade(log, testInfo, "Bob's view after withdrawing BTC funds to external wallet", trade); + + logTrade(log, testInfo, "Alice's Maker/Buyer View (Done)", aliceClient.getTrade(tradeId), true); + logTrade(log, testInfo, "Bob's Taker/Seller View (Done)", bobClient.getTrade(tradeId), true); + + var alicesBalances = aliceClient.getBalances(); + log.info("{} Alice's Current Balance:\n{}", testName(testInfo), - formatSatoshis(currentBalance.getAvailableBalance()), - TradeFormat.format(bobClient.getTrade(tradeId))); + formatBalancesTbls(alicesBalances)); + var bobsBalances = bobClient.getBalances(); + log.info("{} Bob's Current Balance:\n{}", + testName(testInfo), + formatBalancesTbls(bobsBalances)); } catch (StatusRuntimeException e) { fail(e); } diff --git a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java index c82cbaef90..15c11e65b4 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java @@ -29,6 +29,7 @@ import org.junit.jupiter.api.TestMethodOrder; import bisq.apitest.method.offer.AbstractOfferTest; import bisq.apitest.method.offer.CancelOfferTest; +import bisq.apitest.method.offer.CreateBSQOffersTest; import bisq.apitest.method.offer.CreateOfferUsingFixedPriceTest; import bisq.apitest.method.offer.CreateOfferUsingMarketPriceMarginTest; import bisq.apitest.method.offer.ValidateCreateOfferTest; @@ -71,4 +72,17 @@ public class OfferTest extends AbstractOfferTest { test.testCreateGBPBTCSellOfferMinus1Point5PctPriceMargin(); test.testCreateBRLBTCSellOffer6Point55PctPriceMargin(); } + + @Test + @Order(5) + public void testCreateBSQOffersTest() { + CreateBSQOffersTest test = new CreateBSQOffersTest(); + CreateBSQOffersTest.createBsqPaymentAccounts(); + test.testCreateBuy1BTCFor20KBSQOffer(); + test.testCreateSell1BTCFor20KBSQOffer(); + test.testCreateBuyBTCWith1To2KBSQOffer(); + test.testCreateSellBTCFor5To10KBSQOffer(); + test.testGetAllMyBsqOffers(); + test.testGetAvailableBsqOffers(); + } } diff --git a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java index 4c07452abc..f4e93ad35a 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java @@ -29,7 +29,9 @@ import org.junit.jupiter.api.TestMethodOrder; import bisq.apitest.method.trade.AbstractTradeTest; +import bisq.apitest.method.trade.TakeBuyBSQOfferTest; import bisq.apitest.method.trade.TakeBuyBTCOfferTest; +import bisq.apitest.method.trade.TakeSellBSQOfferTest; import bisq.apitest.method.trade.TakeSellBTCOfferTest; @@ -61,4 +63,26 @@ public class TradeTest extends AbstractTradeTest { test.testAlicesConfirmPaymentReceived(testInfo); test.testBobsBtcWithdrawalToExternalAddress(testInfo); } + + @Test + @Order(3) + public void testTakeBuyBSQOffer(final TestInfo testInfo) { + TakeBuyBSQOfferTest test = new TakeBuyBSQOfferTest(); + TakeBuyBSQOfferTest.createBsqPaymentAccounts(); + test.testTakeAlicesSellBTCForBSQOffer(testInfo); + test.testBobsConfirmPaymentStarted(testInfo); + test.testAlicesConfirmPaymentReceived(testInfo); + test.testBobsKeepFunds(testInfo); + } + + @Test + @Order(4) + public void testTakeSellBSQOffer(final TestInfo testInfo) { + TakeSellBSQOfferTest test = new TakeSellBSQOfferTest(); + TakeSellBSQOfferTest.createBsqPaymentAccounts(); + test.testTakeAlicesBuyBTCForBSQOffer(testInfo); + test.testAlicesConfirmPaymentStarted(testInfo); + test.testBobsConfirmPaymentReceived(testInfo); + test.testAlicesBtcWithdrawalToExternalAddress(testInfo); + } }