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.
This commit is contained in:
ghubstan 2021-04-15 14:23:32 -03:00
parent e96da16df8
commit 126885d570
No known key found for this signature in database
GPG Key ID: E35592D6800A861E
17 changed files with 1200 additions and 213 deletions

View File

@ -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);
}
}
}

View File

@ -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<String> 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<Boolean> 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;

View File

@ -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<Enum<?>[], 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));
}

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<OfferInfo> 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<OfferInfo> 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);
}
}

View File

@ -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());
}

View File

@ -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());

View File

@ -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());
}

View File

@ -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(),

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<TradeInfo> 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<TradeInfo> 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);
}
}
}

View File

@ -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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<TradeInfo> 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<TradeInfo> 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);
}
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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);
}
}