Test API XMR support

This commit is contained in:
ghubstan 2021-12-02 19:11:29 -03:00
parent a45c5fa61f
commit 743ef93750
No known key found for this signature in database
GPG key ID: E35592D6800A861E
11 changed files with 998 additions and 24 deletions

View file

@ -35,6 +35,7 @@ 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.ApiTestConfig.XMR;
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.apitest.config.BisqAppConfig.arbdaemon;
import static bisq.apitest.config.BisqAppConfig.bobdaemon;
@ -65,6 +66,9 @@ public abstract class AbstractOfferTest extends MethodTest {
protected static PaymentAccount alicesLegacyBsqAcct;
protected static PaymentAccount bobsLegacyBsqAcct;
protected static PaymentAccount alicesXmrAcct;
protected static PaymentAccount bobsXmrAcct;
@BeforeAll
public static void setUp() {
startSupportingApps(true,
@ -96,11 +100,16 @@ public abstract class AbstractOfferTest extends MethodTest {
return priceAsBigDecimal.multiply(factor).longValue();
};
protected final BiFunction<Double, Double, Long> calcPriceAsLong = (base, delta) -> {
protected final BiFunction<Double, Double, Long> calcFiatTriggerPriceAsLong = (base, delta) -> {
var priceAsDouble = new BigDecimal(base).add(new BigDecimal(delta)).doubleValue();
return Double.valueOf(exactMultiply(priceAsDouble, 10_000)).longValue();
};
protected final BiFunction<Double, Double, Long> calcAltcoinTriggerPriceAsLong = (base, delta) -> {
var priceAsDouble = new BigDecimal(base).add(new BigDecimal(delta)).doubleValue();
return Double.valueOf(exactMultiply(priceAsDouble, 100_000_000)).longValue();
};
protected final BiFunction<Double, Double, String> calcPriceAsString = (base, delta) -> {
var priceAsBigDecimal = new BigDecimal(Double.toString(base))
.add(new BigDecimal(Double.toString(delta)));
@ -113,6 +122,7 @@ public abstract class AbstractOfferTest extends MethodTest {
protected final Function<List<OfferInfo>, String> toOffersTable = (offers) ->
new TableBuilder(OFFER_TBL, offers).build().toString();
@SuppressWarnings("ConstantConditions")
public static void initSwapPaymentAccounts() {
// A bot may not know what the default 'BSQ Swap' account name is,
// but API test cases do: the value of the i18n property 'BSQ_SWAP'.
@ -132,6 +142,20 @@ public abstract class AbstractOfferTest extends MethodTest {
false);
}
@SuppressWarnings("ConstantConditions")
public static void createXmrPaymentAccounts() {
alicesXmrAcct = aliceClient.createCryptoCurrencyPaymentAccount("Alice's XMR Account",
XMR,
"44G4jWmSvTEfifSUZzTDnJVLPvYATmq9XhhtDqUof1BGCLceG82EQsVYG9Q9GN4bJcjbAJEc1JD1m5G7iK4UPZqACubV4Mq",
false);
log.debug("Alices XMR Account: {}", alicesXmrAcct);
bobsXmrAcct = bobClient.createCryptoCurrencyPaymentAccount("Bob's XMR Account",
XMR,
"4BDRhdSBKZqAXs3PuNTbMtaXBNqFj5idC2yMVnQj8Rm61AyKY8AxLTt9vGRJ8pwcG4EtpyD8YpGqdZWCZ2VZj6yVBN2RVKs",
false);
log.debug("Bob's XMR Account: {}", bobsXmrAcct);
}
@AfterAll
public static void tearDown() {
tearDownScaffold();

View file

@ -245,7 +245,7 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
@Test
@Order(5)
public void testGetAllMyBsqOffers() {
List<OfferInfo> offers = aliceClient.getMyBsqOffersSortedByDate();
List<OfferInfo> offers = aliceClient.getMyCryptoCurrencyOffersSortedByDate(BSQ);
log.debug("ALL ALICE'S BSQ OFFERS:\n{}", toOffersTable.apply(offers));
assertEquals(4, offers.size());
log.debug("ALICE'S BALANCES\n{}", formatBalancesTbls(aliceClient.getBalances()));
@ -254,7 +254,7 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
@Test
@Order(6)
public void testGetAvailableBsqOffers() {
List<OfferInfo> offers = bobClient.getBsqOffersSortedByDate();
List<OfferInfo> offers = bobClient.getCryptoCurrencyOffersSortedByDate(BSQ);
log.debug("ALL BOB'S AVAILABLE BSQ OFFERS:\n{}", toOffersTable.apply(offers));
assertEquals(4, offers.size());
log.debug("BOB'S BALANCES\n{}", formatBalancesTbls(bobClient.getBalances()));

View file

@ -0,0 +1,282 @@
/*
* 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.apitest.config.ApiTestConfig.XMR;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static protobuf.OfferDirection.BUY;
import static protobuf.OfferDirection.SELL;
@SuppressWarnings("ConstantConditions")
@Disabled
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CreateXMROffersTest extends AbstractOfferTest {
private static final String MAKER_FEE_CURRENCY_CODE = BSQ;
@BeforeAll
public static void setUp() {
AbstractOfferTest.setUp();
createXmrPaymentAccounts();
}
@Test
@Order(1)
public void testCreateFixedPriceBuy1BTCFor200KXMROffer() {
// 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(),
XMR,
100_000_000L,
75_000_000L,
"0.005", // FIXED PRICE IN BTC (satoshis) FOR 1 XMR
getDefaultBuyerSecurityDepositAsPercent(),
alicesXmrAcct.getId(),
MAKER_FEE_CURRENCY_CODE);
log.debug("Sell XMR (Buy BTC) offer:\n{}", toOfferTable.apply(newOffer));
assertTrue(newOffer.getIsMyOffer());
assertTrue(newOffer.getIsMyPendingOffer());
String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
assertEquals(BUY.name(), newOffer.getDirection());
assertFalse(newOffer.getUseMarketBasedPrice());
assertEquals(500_000L, newOffer.getPrice());
assertEquals(100_000_000L, newOffer.getAmount());
assertEquals(75_000_000L, newOffer.getMinAmount());
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(XMR, newOffer.getBaseCurrencyCode());
assertEquals(BTC, newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
genBtcBlockAndWaitForOfferPreparation();
newOffer = aliceClient.getMyOffer(newOfferId);
assertTrue(newOffer.getIsMyOffer());
assertFalse(newOffer.getIsMyPendingOffer());
assertEquals(newOfferId, newOffer.getId());
assertEquals(BUY.name(), newOffer.getDirection());
assertFalse(newOffer.getUseMarketBasedPrice());
assertEquals(500_000L, newOffer.getPrice());
assertEquals(100_000_000L, newOffer.getAmount());
assertEquals(75_000_000L, newOffer.getMinAmount());
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(XMR, newOffer.getBaseCurrencyCode());
assertEquals(BTC, newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
}
@Test
@Order(2)
public void testCreateFixedPriceSell1BTCFor200KXMROffer() {
// Alice places an offer to SELL BTC for XMR.
var newOffer = aliceClient.createFixedPricedOffer(SELL.name(),
XMR,
100_000_000L,
50_000_000L,
"0.005", // FIXED PRICE IN BTC (satoshis) FOR 1 XMR
getDefaultBuyerSecurityDepositAsPercent(),
alicesXmrAcct.getId(),
MAKER_FEE_CURRENCY_CODE);
log.debug("Buy XMR (Sell BTC) offer:\n{}", toOfferTable.apply(newOffer));
assertTrue(newOffer.getIsMyOffer());
assertTrue(newOffer.getIsMyPendingOffer());
String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
assertEquals(SELL.name(), newOffer.getDirection());
assertFalse(newOffer.getUseMarketBasedPrice());
assertEquals(500_000L, newOffer.getPrice());
assertEquals(100_000_000L, newOffer.getAmount());
assertEquals(50_000_000L, newOffer.getMinAmount());
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(XMR, newOffer.getBaseCurrencyCode());
assertEquals(BTC, newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
genBtcBlockAndWaitForOfferPreparation();
newOffer = aliceClient.getMyOffer(newOfferId);
assertTrue(newOffer.getIsMyOffer());
assertFalse(newOffer.getIsMyPendingOffer());
assertEquals(newOfferId, newOffer.getId());
assertEquals(SELL.name(), newOffer.getDirection());
assertFalse(newOffer.getUseMarketBasedPrice());
assertEquals(500_000L, newOffer.getPrice());
assertEquals(100_000_000L, newOffer.getAmount());
assertEquals(50_000_000L, newOffer.getMinAmount());
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(XMR, newOffer.getBaseCurrencyCode());
assertEquals(BTC, newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
}
@Test
@Order(3)
public void testCreatePriceMarginBasedBuy1BTCOfferWithTriggerPrice() {
double priceMarginPctInput = 1.00;
double mktPriceAsDouble = aliceClient.getBtcPrice(XMR);
long triggerPriceAsLong = calcAltcoinTriggerPriceAsLong.apply(mktPriceAsDouble, -0.001);
var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
XMR,
100_000_000L,
75_000_000L,
priceMarginPctInput,
getDefaultBuyerSecurityDepositAsPercent(),
alicesXmrAcct.getId(),
MAKER_FEE_CURRENCY_CODE,
triggerPriceAsLong);
log.debug("Pending Sell XMR (Buy BTC) offer:\n{}", toOfferTable.apply(newOffer));
assertTrue(newOffer.getIsMyOffer());
assertTrue(newOffer.getIsMyPendingOffer());
String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
assertEquals(BUY.name(), newOffer.getDirection());
assertTrue(newOffer.getUseMarketBasedPrice());
// There is no trigger price while offer is pending.
assertEquals(0, newOffer.getTriggerPrice());
assertEquals(100_000_000L, newOffer.getAmount());
assertEquals(75_000_000L, newOffer.getMinAmount());
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(XMR, newOffer.getBaseCurrencyCode());
assertEquals(BTC, newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
genBtcBlockAndWaitForOfferPreparation();
newOffer = aliceClient.getMyOffer(newOfferId);
log.debug("Available Sell XMR (Buy BTC) offer:\n{}", toOfferTable.apply(newOffer));
assertTrue(newOffer.getIsMyOffer());
assertFalse(newOffer.getIsMyPendingOffer());
assertEquals(newOfferId, newOffer.getId());
assertEquals(BUY.name(), newOffer.getDirection());
assertTrue(newOffer.getUseMarketBasedPrice());
// The trigger price should exist on the prepared offer.
assertEquals(triggerPriceAsLong, newOffer.getTriggerPrice());
assertEquals(100_000_000L, newOffer.getAmount());
assertEquals(75_000_000L, newOffer.getMinAmount());
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(XMR, newOffer.getBaseCurrencyCode());
assertEquals(BTC, newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
}
@Test
@Order(4)
public void testCreatePriceMarginBasedSell1BTCOffer() {
// Alice places an offer to SELL BTC for XMR.
double priceMarginPctInput = 0.50;
var newOffer = aliceClient.createMarketBasedPricedOffer(SELL.name(),
XMR,
100_000_000L,
50_000_000L,
priceMarginPctInput,
getDefaultBuyerSecurityDepositAsPercent(),
alicesXmrAcct.getId(),
MAKER_FEE_CURRENCY_CODE,
NO_TRIGGER_PRICE);
log.debug("Buy XMR (Sell BTC) offer:\n{}", toOfferTable.apply(newOffer));
assertTrue(newOffer.getIsMyOffer());
assertTrue(newOffer.getIsMyPendingOffer());
String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
assertEquals(SELL.name(), newOffer.getDirection());
assertTrue(newOffer.getUseMarketBasedPrice());
assertEquals(100_000_000L, newOffer.getAmount());
assertEquals(50_000_000L, newOffer.getMinAmount());
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(XMR, newOffer.getBaseCurrencyCode());
assertEquals(BTC, newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
genBtcBlockAndWaitForOfferPreparation();
newOffer = aliceClient.getMyOffer(newOfferId);
assertTrue(newOffer.getIsMyOffer());
assertFalse(newOffer.getIsMyPendingOffer());
assertEquals(newOfferId, newOffer.getId());
assertEquals(SELL.name(), newOffer.getDirection());
assertTrue(newOffer.getUseMarketBasedPrice());
assertEquals(100_000_000L, newOffer.getAmount());
assertEquals(50_000_000L, newOffer.getMinAmount());
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(XMR, newOffer.getBaseCurrencyCode());
assertEquals(BTC, newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
}
@Test
@Order(5)
public void testGetAllMyXMROffers() {
List<OfferInfo> offers = aliceClient.getMyCryptoCurrencyOffersSortedByDate(XMR);
log.debug("All of Alice's XMR offers:\n{}", toOffersTable.apply(offers));
assertEquals(4, offers.size());
log.debug("Alice's balances\n{}", formatBalancesTbls(aliceClient.getBalances()));
}
@Test
@Order(6)
public void testGetAvailableXMROffers() {
List<OfferInfo> offers = bobClient.getCryptoCurrencyOffersSortedByDate(XMR);
log.debug("All of Bob's available XMR offers:\n{}", toOffersTable.apply(offers));
assertEquals(4, offers.size());
log.debug("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

@ -31,16 +31,12 @@ import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterAll;
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.apitest.config.ApiTestConfig.EUR;
import static bisq.apitest.config.ApiTestConfig.USD;
import static bisq.apitest.config.ApiTestConfig.*;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
import static bisq.proto.grpc.EditOfferRequest.EditType.*;
import static java.lang.String.format;
@ -49,7 +45,7 @@ import static protobuf.OfferDirection.BUY;
import static protobuf.OfferDirection.SELL;
@SuppressWarnings("ALL")
@Disabled
// @Disabled
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class EditOfferTest extends AbstractOfferTest {
@ -106,7 +102,7 @@ public class EditOfferTest extends AbstractOfferTest {
// Edit the offer's trigger price, nothing else.
var mktPrice = aliceClient.getBtcPrice(EUR);
var delta = 5_000.00;
var newTriggerPriceAsLong = calcPriceAsLong.apply(mktPrice, delta);
var newTriggerPriceAsLong = calcFiatTriggerPriceAsLong.apply(mktPrice, delta);
aliceClient.editOfferTriggerPrice(originalOffer.getId(), newTriggerPriceAsLong);
sleep(2_500); // Wait for offer book re-entry.
@ -261,7 +257,7 @@ public class EditOfferTest extends AbstractOfferTest {
PaymentAccount paymentAcct = getOrCreatePaymentAccount("US");
var originalMktPriceMargin = new BigDecimal("0.0").doubleValue();
var mktPriceAsDouble = aliceClient.getBtcPrice(USD);
var originalTriggerPriceAsLong = calcPriceAsLong.apply(mktPriceAsDouble, -5_000.0000);
var originalTriggerPriceAsLong = calcFiatTriggerPriceAsLong.apply(mktPriceAsDouble, -5_000.0000);
OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
USD,
paymentAcct.getId(),
@ -275,7 +271,7 @@ public class EditOfferTest extends AbstractOfferTest {
// Edit the offer's price margin and trigger price, and deactivate it.
var newMktPriceMargin = new BigDecimal("0.1").doubleValue();
var newTriggerPriceAsLong = calcPriceAsLong.apply(mktPriceAsDouble, -2_000.0000);
var newTriggerPriceAsLong = calcFiatTriggerPriceAsLong.apply(mktPriceAsDouble, -2_000.0000);
aliceClient.editOffer(originalOffer.getId(),
"0.00",
originalOffer.getUseMarketBasedPrice(),
@ -341,9 +337,8 @@ public class EditOfferTest extends AbstractOfferTest {
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
aliceClient.editOfferTriggerPrice(originalOffer.getId(), newTriggerPrice));
String expectedExceptionMessage =
format("UNKNOWN: programmer error: cannot set a trigger price (%s) in"
format("UNKNOWN: programmer error: cannot set a trigger price in"
+ " fixed price offer with id '%s'",
newTriggerPrice,
originalOffer.getId());
assertEquals(expectedExceptionMessage, exception.getMessage());
}
@ -364,7 +359,7 @@ public class EditOfferTest extends AbstractOfferTest {
// Change the offer to mkt price based and set a trigger price.
var newMktPriceMargin = new BigDecimal("0.05").doubleValue();
var delta = 200_000.0000; // trigger price on buy offer is 200K above mkt price
var newTriggerPriceAsLong = calcPriceAsLong.apply(mktPriceAsDouble, delta);
var newTriggerPriceAsLong = calcFiatTriggerPriceAsLong.apply(mktPriceAsDouble, delta);
aliceClient.editOffer(originalOffer.getId(),
"0.00",
true,
@ -391,7 +386,7 @@ public class EditOfferTest extends AbstractOfferTest {
double mktPriceAsDouble = aliceClient.getBtcPrice("GBP");
var originalMktPriceMargin = new BigDecimal("0.25").doubleValue();
var delta = 1_000.0000; // trigger price on sell offer is 1K below mkt price
var originalTriggerPriceAsLong = calcPriceAsLong.apply(mktPriceAsDouble, delta);
var originalTriggerPriceAsLong = calcFiatTriggerPriceAsLong.apply(mktPriceAsDouble, delta);
var originalOffer = createMktPricedOfferForEdit(SELL.name(),
"GBP",
paymentAcct.getId(),
@ -441,7 +436,7 @@ public class EditOfferTest extends AbstractOfferTest {
ACTIVATE_OFFER,
MKT_PRICE_MARGIN_ONLY));
String expectedExceptionMessage = format("UNKNOWN: cannot set mkt price margin or"
+ " trigger price on fixed price altcoin offer with id '%s'",
+ " trigger price on fixed price bsq offer with id '%s'",
originalOffer.getId());
assertEquals(expectedExceptionMessage, exception.getMessage());
}
@ -459,7 +454,7 @@ public class EditOfferTest extends AbstractOfferTest {
BSQ);
log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer));
genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry.
var newTriggerPriceAsLong = calcPriceAsLong.apply(0.00005, 0.00);
var newTriggerPriceAsLong = calcFiatTriggerPriceAsLong.apply(0.00005, 0.00);
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
aliceClient.editOffer(originalOffer.getId(),
"0.00",
@ -469,7 +464,7 @@ public class EditOfferTest extends AbstractOfferTest {
ACTIVATE_OFFER,
TRIGGER_PRICE_ONLY));
String expectedExceptionMessage = format("UNKNOWN: cannot set mkt price margin or"
+ " trigger price on fixed price altcoin offer with id '%s'",
+ " trigger price on fixed price bsq offer with id '%s'",
originalOffer.getId());
assertEquals(expectedExceptionMessage, exception.getMessage());
}
@ -572,8 +567,261 @@ public class EditOfferTest extends AbstractOfferTest {
assertEquals(0, editedOffer.getTriggerPrice());
}
// Edit XMR Offers
@Test
@Order(18)
public void testChangePriceMarginBasedXmrOfferWithTriggerPriceToFixedPricedAndDeactivateIt() {
createXmrPaymentAccounts();
double mktPriceAsDouble = aliceClient.getBtcPrice(XMR);
long triggerPriceAsLong = calcAltcoinTriggerPriceAsLong.apply(mktPriceAsDouble, 0.001);
OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
XMR,
alicesXmrAcct.getId(),
0.0,
triggerPriceAsLong);
log.debug("Pending XMR offer:\n{}", toOfferTable.apply(originalOffer));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
originalOffer = aliceClient.getMyOffer(originalOffer.getId());
log.debug("Original XMR offer:\n{}", toOfferTable.apply(originalOffer));
String newFixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, -0.001);
aliceClient.editOffer(originalOffer.getId(),
newFixedPriceAsString,
false,
0.00,
0,
DEACTIVATE_OFFER,
FIXED_PRICE_AND_ACTIVATION_STATE);
// Wait for edited offer to be removed from offer-book, edited & not re-published.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
log.debug("Edited XMR offer:\n{}", toOfferTable.apply(editedOffer));
assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice());
assertFalse(editedOffer.getUseMarketBasedPrice());
assertEquals(0.00, editedOffer.getMarketPriceMargin());
assertEquals(0, editedOffer.getTriggerPrice());
assertFalse(editedOffer.getIsActivated());
doSanityCheck(originalOffer, editedOffer);
}
@Test
@Order(19)
public void testEditTriggerPriceOnPriceMarginBasedXmrOffer() {
createXmrPaymentAccounts();
double mktPriceMargin = -0.075d;
OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
XMR,
alicesXmrAcct.getId(),
mktPriceMargin,
NO_TRIGGER_PRICE);
log.debug("Pending XMR offer:\n{}", toOfferTable.apply(originalOffer));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
originalOffer = aliceClient.getMyOffer(originalOffer.getId());
log.info("Original XMR offer:\n{}", toOfferTable.apply(originalOffer));
double mktPriceAsDouble = aliceClient.getBtcPrice(XMR);
long triggerPriceAsLong = calcAltcoinTriggerPriceAsLong.apply(mktPriceAsDouble, 0.001);
aliceClient.editOffer(originalOffer.getId(),
"0",
true,
mktPriceMargin,
triggerPriceAsLong,
ACTIVATE_OFFER,
TRIGGER_PRICE_ONLY);
// Wait for edited offer to be removed from offer-book, edited & not re-published.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
log.info("Edited XMR offer:\n{}", toOfferTable.apply(editedOffer));
assertTrue(editedOffer.getUseMarketBasedPrice());
assertEquals(scaledDownMktPriceMargin.apply(mktPriceMargin), editedOffer.getMarketPriceMargin());
assertEquals(triggerPriceAsLong, editedOffer.getTriggerPrice());
assertTrue(editedOffer.getIsActivated());
doSanityCheck(originalOffer, editedOffer);
}
@Test
@Order(20)
public void testChangeFixedPricedXmrOfferToPriceMarginBasedOfferWithTriggerPrice() {
createXmrPaymentAccounts();
double mktPriceAsDouble = aliceClient.getBtcPrice(XMR);
String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 0.00);
OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
XMR,
100_000_000L,
50_000_000L,
fixedPriceAsString, // FIXED PRICE IN BTC (satoshis) FOR 1 XMR
getDefaultBuyerSecurityDepositAsPercent(),
alicesXmrAcct.getId(),
BSQ);
log.debug("Pending XMR offer:\n{}", toOfferTable.apply(originalOffer));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
originalOffer = aliceClient.getMyOffer(originalOffer.getId());
log.debug("Original XMR offer:\n{}", toOfferTable.apply(originalOffer));
// Change the offer to mkt price based and set a trigger price.
var newMktPriceMargin = new BigDecimal("0.05").doubleValue();
var delta = -0.00100000;
var newTriggerPriceAsLong = calcAltcoinTriggerPriceAsLong.apply(mktPriceAsDouble, delta);
aliceClient.editOffer(originalOffer.getId(),
"0.00",
true,
newMktPriceMargin,
newTriggerPriceAsLong,
ACTIVATE_OFFER,
MKT_PRICE_MARGIN_AND_TRIGGER_PRICE);
// Wait for edited offer to be removed from offer-book, edited, and re-published.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
log.debug("Edited XMR offer:\n{}", toOfferTable.apply(editedOffer));
assertTrue(editedOffer.getUseMarketBasedPrice());
assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice());
assertTrue(editedOffer.getIsActivated());
doSanityCheck(originalOffer, editedOffer);
}
@Test
@Order(21)
public void testEditTriggerPriceOnFixedPriceXmrOfferShouldThrowException() {
createXmrPaymentAccounts();
OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
XMR,
100_000_000L,
25_000_000L,
"0.007", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
getDefaultBuyerSecurityDepositAsPercent(),
alicesXmrAcct.getId(),
BSQ);
log.debug("Original XMR offer:\n{}", toOfferTable.apply(originalOffer));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
var newTriggerPriceAsLong = calcAltcoinTriggerPriceAsLong.apply(0.007, 0.001);
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
aliceClient.editOffer(originalOffer.getId(),
"0.00",
false,
0.1,
newTriggerPriceAsLong,
ACTIVATE_OFFER,
TRIGGER_PRICE_ONLY));
String expectedExceptionMessage = format("UNKNOWN: programmer error: cannot set a trigger price"
+ " in fixed price offer with id '%s'",
originalOffer.getId());
assertEquals(expectedExceptionMessage, exception.getMessage());
}
@Test
@Order(22)
public void testEditFixedPriceOnXmrOffer() {
createXmrPaymentAccounts();
String fixedPriceAsString = "0.008"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
XMR,
100_000_000L,
100_000_000L,
fixedPriceAsString,
getDefaultBuyerSecurityDepositAsPercent(),
alicesXmrAcct.getId(),
BSQ);
log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
String newFixedPriceAsString = "0.009";
aliceClient.editOffer(originalOffer.getId(),
newFixedPriceAsString,
false,
0.0,
0,
ACTIVATE_OFFER,
FIXED_PRICE_ONLY);
// Wait for edited offer to be edited and removed from offer-book.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
log.debug("Edited XMR offer:\n{}", toOfferTable.apply(editedOffer));
assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice());
assertTrue(editedOffer.getIsActivated());
assertMarketBasedPriceFieldsAreIgnored(editedOffer);
doSanityCheck(originalOffer, editedOffer);
}
@Test
@Order(23)
public void testDisableXmrOffer() {
createXmrPaymentAccounts();
String fixedPriceAsString = "0.008"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
XMR,
100_000_000L,
50_000_000L,
fixedPriceAsString,
getDefaultBuyerSecurityDepositAsPercent(),
alicesXmrAcct.getId(),
BSQ);
log.debug("Original XMR offer:\n{}", toOfferTable.apply(originalOffer));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
aliceClient.editOffer(originalOffer.getId(),
fixedPriceAsString,
false,
0.0,
0,
DEACTIVATE_OFFER,
ACTIVATION_STATE_ONLY);
// Wait for edited offer to be removed from offer-book.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
log.debug("Edited XMR offer:\n{}", toOfferTable.apply(editedOffer));
assertFalse(editedOffer.getIsActivated());
assertEquals(scaledUpAltcoinOfferPrice.apply(fixedPriceAsString), editedOffer.getPrice());
assertMarketBasedPriceFieldsAreIgnored(editedOffer);
doSanityCheck(originalOffer, editedOffer);
}
@Test
@Order(24)
public void testEditFixedPriceAndDisableXmrOffer() {
createXmrPaymentAccounts();
String fixedPriceAsString = "0.004"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
XMR,
100_000_000L,
100_000_000L,
fixedPriceAsString,
getDefaultBuyerSecurityDepositAsPercent(),
alicesXmrAcct.getId(),
BSQ);
log.debug("Original XMR offer:\n{}", toOfferTable.apply(originalOffer));
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
String newFixedPriceAsString = "0.000045";
aliceClient.editOffer(originalOffer.getId(),
newFixedPriceAsString,
false,
0.0,
0,
DEACTIVATE_OFFER,
FIXED_PRICE_AND_ACTIVATION_STATE);
// Wait for edited offer to be edited and removed from offer-book.
genBtcBlocksThenWait(1, 2500);
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
log.debug("Edited XMR offer:\n{}", toOfferTable.apply(editedOffer));
assertFalse(editedOffer.getIsActivated());
assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice());
assertMarketBasedPriceFieldsAreIgnored(editedOffer);
doSanityCheck(originalOffer, editedOffer);
}
// Edit BSQ Swap Offers (should always be blocked)
@Test
@Order(25)
public void testEditBsqSwapOfferShouldThrowException() {
var originalOffer = aliceClient.createBsqSwapOffer(SELL.name(),
1_250_000L,
@ -650,6 +898,12 @@ public class EditOfferTest extends AbstractOfferTest {
assertEquals(originalOffer.getSellerSecurityDeposit(), editedOffer.getSellerSecurityDeposit());
}
private void assertMarketBasedPriceFieldsAreIgnored(OfferInfo editedOffer) {
assertFalse(editedOffer.getUseMarketBasedPrice());
assertEquals(0.00, editedOffer.getMarketPriceMargin());
assertEquals(0, editedOffer.getTriggerPrice());
}
private PaymentAccount getOrCreatePaymentAccount(String countryCode) {
if (paymentAcctCache.containsKey(countryCode)) {
return paymentAcctCache.get(countryCode);

View file

@ -48,6 +48,15 @@ public class AbstractTradeTest extends AbstractOfferTest {
EXPECTED_PROTOCOL_STATUS.init();
}
protected final TradeInfo takeAlicesOffer(String offerId,
String paymentAccountId,
String takerFeeCurrencyCode) {
return takeAlicesOffer(offerId,
paymentAccountId,
takerFeeCurrencyCode,
true);
}
protected final TradeInfo takeAlicesOffer(String offerId,
String paymentAccountId,
String takerFeeCurrencyCode,
@ -241,7 +250,7 @@ public class AbstractTradeTest extends AbstractOfferTest {
if (log.isDebugEnabled()) {
log.debug(format("%s %s%n%s",
testName(testInfo),
description.toUpperCase(),
description,
new TableBuilder(TRADE_DETAIL_TBL, trade).build()));
}
}

View file

@ -98,7 +98,7 @@ public class TakeBuyBSQOfferTest extends AbstractTradeTest {
tradeId = trade.getTradeId();
genBtcBlocksThenWait(1, 2_500);
alicesBsqOffers = aliceClient.getMyBsqOffersSortedByDate();
alicesBsqOffers = aliceClient.getMyCryptoCurrencyOffersSortedByDate(BSQ);
assertEquals(0, alicesBsqOffers.size());
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId());

View file

@ -0,0 +1,175 @@
/*
* 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 io.grpc.StatusRuntimeException;
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.XMR;
import static bisq.cli.table.builder.TableType.OFFER_TBL;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
import static bisq.core.trade.model.bisq_v1.Trade.Phase.PAYOUT_PUBLISHED;
import static bisq.core.trade.model.bisq_v1.Trade.State.BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG;
import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.fail;
import static protobuf.Offer.State.OFFER_FEE_PAID;
import static protobuf.OfferDirection.SELL;
import bisq.apitest.method.offer.AbstractOfferTest;
import bisq.cli.table.builder.TableBuilder;
@Disabled
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TakeBuyXMROfferTest extends AbstractTradeTest {
// Alice is maker / xmr buyer (btc seller), Bob is taker / xmr 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();
createXmrPaymentAccounts();
EXPECTED_PROTOCOL_STATUS.init();
}
@Test
@Order(1)
public void testTakeAlicesSellBTCForXMROffer(final TestInfo testInfo) {
try {
// Alice is going to BUY XMR, but the Offer direction = SELL because it is a
// BTC trade; Alice will SELL BTC for XMR. Bob will send Alice XMR.
// Confused me, but just need to remember there are only BTC offers.
var btcTradeDirection = SELL.name();
var alicesOffer = aliceClient.createFixedPricedOffer(btcTradeDirection,
XMR,
15_000_000L,
7_500_000L,
"0.00455500", // FIXED PRICE IN BTC (satoshis) FOR 1 XMR
getDefaultBuyerSecurityDepositAsPercent(),
alicesXmrAcct.getId(),
TRADE_FEE_CURRENCY_CODE);
log.debug("Alice's Buy XMR (Sell BTC) offer:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build());
genBtcBlocksThenWait(1, 5000);
var offerId = alicesOffer.getId();
assertFalse(alicesOffer.getIsCurrencyForMakerFeeBtc());
var alicesXmrOffers = aliceClient.getMyCryptoCurrencyOffers(btcTradeDirection, XMR);
assertEquals(1, alicesXmrOffers.size());
var trade = takeAlicesOffer(offerId, bobsXmrAcct.getId(), TRADE_FEE_CURRENCY_CODE);
alicesXmrOffers = aliceClient.getMyCryptoCurrencyOffersSortedByDate(XMR);
assertEquals(0, alicesXmrOffers.size());
genBtcBlocksThenWait(1, 2_500);
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId());
trade = bobClient.getTrade(tradeId);
verifyTakerDepositConfirmed(trade);
logTrade(log, testInfo, "Alice's Maker/Buyer View", aliceClient.getTrade(tradeId));
logTrade(log, testInfo, "Bob's Taker/Seller View", bobClient.getTrade(tradeId));
} catch (StatusRuntimeException e) {
fail(e);
}
}
@Test
@Order(2)
public void testBobsConfirmPaymentStarted(final TestInfo testInfo) {
try {
var trade = bobClient.getTrade(tradeId);
verifyTakerDepositConfirmed(trade);
log.debug("Bob sends XMR payment to Alice for trade {}", trade.getTradeId());
bobClient.confirmPaymentStarted(trade.getTradeId());
sleep(3500);
waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, bobClient, tradeId);
logTrade(log, testInfo, "Alice's Maker/Buyer View (Payment Sent)", aliceClient.getTrade(tradeId));
logTrade(log, testInfo, "Bob's Taker/Seller View (Payment Sent)", bobClient.getTrade(tradeId));
} catch (StatusRuntimeException e) {
fail(e);
}
}
@Test
@Order(3)
public void testAlicesConfirmPaymentReceived(final TestInfo testInfo) {
try {
waitForSellerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);
sleep(2_000);
var trade = aliceClient.getTrade(tradeId);
// If we were trading BSQ, Alice would verify payment has been sent to her
// Bisq / BSQ wallet, but we can do no such checks for XMR payments.
// All XMR transfers are done outside Bisq.
log.debug("Alice verifies XMR payment was received from Bob, for trade {}", trade.getTradeId());
aliceClient.confirmPaymentReceived(trade.getTradeId());
sleep(3_000);
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 Maker/Buyer View (Payment Received)", aliceClient.getTrade(tradeId));
logTrade(log, testInfo, "Bob's Taker/Seller View (Payment Received)", bobClient.getTrade(tradeId));
} catch (StatusRuntimeException e) {
fail(e);
}
}
@Test
@Order(4)
public void testKeepFunds(final TestInfo testInfo) {
try {
genBtcBlocksThenWait(1, 1_000);
var trade = bobClient.getTrade(tradeId);
logTrade(log, testInfo, "Alice's view before keeping funds", trade);
aliceClient.keepFunds(tradeId);
bobClient.keepFunds(tradeId);
genBtcBlocksThenWait(1, 1_000);
trade = bobClient.getTrade(tradeId);
EXPECTED_PROTOCOL_STATUS.setState(BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG)
.setPhase(PAYOUT_PUBLISHED);
verifyExpectedProtocolStatus(trade);
logTrade(log, testInfo, "Alice's Maker/Buyer View (Done)", aliceClient.getTrade(tradeId));
logTrade(log, testInfo, "Bob's Taker/Seller View (Done)", bobClient.getTrade(tradeId));
logBalances(log, testInfo);
} catch (StatusRuntimeException e) {
fail(e);
}
}
}

View file

@ -0,0 +1,183 @@
/*
* 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 io.grpc.StatusRuntimeException;
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.BTC;
import static bisq.apitest.config.ApiTestConfig.XMR;
import static bisq.cli.table.builder.TableType.OFFER_TBL;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
import static bisq.core.trade.model.bisq_v1.Trade.Phase.PAYOUT_PUBLISHED;
import static bisq.core.trade.model.bisq_v1.Trade.Phase.WITHDRAWN;
import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG;
import static bisq.core.trade.model.bisq_v1.Trade.State.WITHDRAW_COMPLETED;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static protobuf.OfferDirection.BUY;
import bisq.apitest.method.offer.AbstractOfferTest;
import bisq.cli.table.builder.TableBuilder;
@Disabled
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TakeSellXMROfferTest extends AbstractTradeTest {
// Alice is maker / xmr seller (btc buyer), Bob is taker / xmr 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();
createXmrPaymentAccounts();
EXPECTED_PROTOCOL_STATUS.init();
}
@Test
@Order(1)
public void testTakeAlicesBuyBTCForXMROffer(final TestInfo testInfo) {
try {
// Alice is going to SELL XMR, but the Offer direction = BUY because it is a
// BTC trade; Alice will BUY BTC for XMR. Alice will send Bob XMR.
// Confused me, but just need to remember there are only BTC offers.
var btcTradeDirection = BUY.name();
double priceMarginPctInput = 1.50;
var alicesOffer = aliceClient.createMarketBasedPricedOffer(btcTradeDirection,
XMR,
20_000_000L,
10_500_000L,
priceMarginPctInput,
getDefaultBuyerSecurityDepositAsPercent(),
alicesXmrAcct.getId(),
TRADE_FEE_CURRENCY_CODE,
NO_TRIGGER_PRICE);
log.debug("Alice's SELL XMR (Buy BTC) offer:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build());
genBtcBlocksThenWait(1, 4000);
var offerId = alicesOffer.getId();
assertTrue(alicesOffer.getIsCurrencyForMakerFeeBtc());
var alicesXmrOffers = aliceClient.getMyCryptoCurrencyOffers(btcTradeDirection, XMR);
assertEquals(1, alicesXmrOffers.size());
var trade = takeAlicesOffer(offerId, bobsXmrAcct.getId(), TRADE_FEE_CURRENCY_CODE);
alicesXmrOffers = aliceClient.getMyCryptoCurrencyOffersSortedByDate(XMR);
assertEquals(0, alicesXmrOffers.size());
genBtcBlocksThenWait(1, 2_500);
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId());
trade = bobClient.getTrade(tradeId);
verifyTakerDepositConfirmed(trade);
logTrade(log, testInfo, "Alice's Maker/Seller View", aliceClient.getTrade(tradeId));
logTrade(log, testInfo, "Bob's Taker/Buyer View", bobClient.getTrade(tradeId));
} catch (StatusRuntimeException e) {
fail(e);
}
}
@Test
@Order(2)
public void testAlicesConfirmPaymentStarted(final TestInfo testInfo) {
try {
var trade = aliceClient.getTrade(tradeId);
waitForDepositConfirmation(log, testInfo, aliceClient, trade.getTradeId());
log.debug("Alice sends XMR payment to Bob for trade {}", trade.getTradeId());
aliceClient.confirmPaymentStarted(trade.getTradeId());
sleep(3500);
waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);
logTrade(log, testInfo, "Alice's Maker/Seller View (Payment Sent)", aliceClient.getTrade(tradeId));
logTrade(log, testInfo, "Bob's Taker/Buyer View (Payment Sent)", bobClient.getTrade(tradeId));
} catch (StatusRuntimeException e) {
fail(e);
}
}
@Test
@Order(3)
public void testBobsConfirmPaymentReceived(final TestInfo testInfo) {
try {
waitForSellerSeesPaymentInitiatedMessage(log, testInfo, bobClient, tradeId);
var trade = bobClient.getTrade(tradeId);
sleep(2_000);
// If we were trading BSQ, Bob would verify payment has been sent to his
// Bisq / BSQ wallet, but we can do no such checks for XMR payments.
// All XMR transfers are done outside Bisq.
log.debug("Bob verifies XMR payment was received from Alice, for trade {}", trade.getTradeId());
bobClient.confirmPaymentReceived(trade.getTradeId());
sleep(3_000);
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 Maker/Seller View (Payment Received)", aliceClient.getTrade(tradeId));
logTrade(log, testInfo, "Bob's Taker/Buyer View (Payment Received)", bobClient.getTrade(tradeId));
} catch (StatusRuntimeException e) {
fail(e);
}
}
@Test
@Order(4)
public void testAlicesBtcWithdrawalToExternalAddress(final TestInfo testInfo) {
try {
genBtcBlocksThenWait(1, 1_000);
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);
bobClient.keepFunds(tradeId);
genBtcBlocksThenWait(1, 1_000);
trade = aliceClient.getTrade(tradeId);
EXPECTED_PROTOCOL_STATUS.setState(WITHDRAW_COMPLETED)
.setPhase(WITHDRAWN)
.setWithdrawn(true);
verifyExpectedProtocolStatus(trade);
logTrade(log, testInfo, "Alice's Maker/Seller View (Done)", aliceClient.getTrade(tradeId));
logTrade(log, testInfo, "Bob's Taker/Buyer View (Done)", bobClient.getTrade(tradeId));
logBalances(log, testInfo);
} catch (StatusRuntimeException e) {
fail(e);
}
}
}

View file

@ -59,7 +59,7 @@ public class LongRunningOfferDeactivationTest extends AbstractOfferTest {
public void testSellOfferAutoDisable(final TestInfo testInfo) {
PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, "US");
double mktPriceAsDouble = aliceClient.getBtcPrice("USD");
long triggerPrice = calcPriceAsLong.apply(mktPriceAsDouble, -50.0000);
long triggerPrice = calcFiatTriggerPriceAsLong.apply(mktPriceAsDouble, -50.0000);
log.info("Current USD mkt price = {} Trigger Price = {}", mktPriceAsDouble, formatPrice(triggerPrice));
OfferInfo offer = aliceClient.createMarketBasedPricedOffer(SELL.name(),
"USD",
@ -107,7 +107,7 @@ public class LongRunningOfferDeactivationTest extends AbstractOfferTest {
public void testBuyOfferAutoDisable(final TestInfo testInfo) {
PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, "US");
double mktPriceAsDouble = aliceClient.getBtcPrice("USD");
long triggerPrice = calcPriceAsLong.apply(mktPriceAsDouble, 50.0000);
long triggerPrice = calcFiatTriggerPriceAsLong.apply(mktPriceAsDouble, 50.0000);
log.info("Current USD mkt price = {} Trigger Price = {}", mktPriceAsDouble, formatPrice(triggerPrice));
OfferInfo offer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
"USD",

View file

@ -33,6 +33,7 @@ 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.CreateXMROffersTest;
import bisq.apitest.method.offer.EditOfferTest;
import bisq.apitest.method.offer.ValidateCreateOfferTest;
@ -90,6 +91,19 @@ public class OfferTest extends AbstractOfferTest {
@Test
@Order(6)
public void testCreateXMROffers() {
CreateXMROffersTest test = new CreateXMROffersTest();
CreateXMROffersTest.createXmrPaymentAccounts();
test.testCreateFixedPriceBuy1BTCFor200KXMROffer();
test.testCreateFixedPriceSell1BTCFor200KXMROffer();
test.testCreatePriceMarginBasedBuy1BTCOfferWithTriggerPrice();
test.testCreatePriceMarginBasedSell1BTCOffer();
test.testGetAllMyXMROffers();
test.testGetAvailableXMROffers();
}
@Test
@Order(7)
public void testCreateBSQSwapOffers() {
BsqSwapOfferTest test = new BsqSwapOfferTest();
test.testAliceCreateBsqSwapBuyOffer1();
@ -101,7 +115,7 @@ public class OfferTest extends AbstractOfferTest {
}
@Test
@Order(7)
@Order(8)
public void testEditOffer() {
EditOfferTest test = new EditOfferTest();
// Edit fiat offer tests
@ -124,5 +138,14 @@ public class OfferTest extends AbstractOfferTest {
test.testEditFixedPriceOnBsqOffer();
test.testDisableBsqOffer();
test.testEditFixedPriceAndDisableBsqOffer();
// Edit xmr offer tests
test.testChangePriceMarginBasedXmrOfferWithTriggerPriceToFixedPricedAndDeactivateIt();
test.testChangeFixedPricedXmrOfferToPriceMarginBasedOfferWithTriggerPrice();
test.testEditTriggerPriceOnFixedPriceXmrOfferShouldThrowException();
test.testEditFixedPriceOnXmrOffer();
test.testDisableXmrOffer();
test.testEditFixedPriceAndDisableXmrOffer();
// Edit bsq swap offer tests
test.testEditBsqSwapOfferShouldThrowException();
}
}

View file

@ -33,8 +33,10 @@ import bisq.apitest.method.trade.BsqSwapTradeTest;
import bisq.apitest.method.trade.TakeBuyBSQOfferTest;
import bisq.apitest.method.trade.TakeBuyBTCOfferTest;
import bisq.apitest.method.trade.TakeBuyBTCOfferWithNationalBankAcctTest;
import bisq.apitest.method.trade.TakeBuyXMROfferTest;
import bisq.apitest.method.trade.TakeSellBSQOfferTest;
import bisq.apitest.method.trade.TakeSellBTCOfferTest;
import bisq.apitest.method.trade.TakeSellXMROfferTest;
@Slf4j
@ -99,6 +101,28 @@ public class TradeTest extends AbstractTradeTest {
@Test
@Order(6)
public void testTakeBuyXMROffer(final TestInfo testInfo) {
TakeBuyXMROfferTest test = new TakeBuyXMROfferTest();
TakeBuyXMROfferTest.createXmrPaymentAccounts();
test.testTakeAlicesSellBTCForXMROffer(testInfo);
test.testBobsConfirmPaymentStarted(testInfo);
test.testAlicesConfirmPaymentReceived(testInfo);
test.testKeepFunds(testInfo);
}
@Test
@Order(7)
public void testTakeSellXMROffer(final TestInfo testInfo) {
TakeSellXMROfferTest test = new TakeSellXMROfferTest();
TakeBuyXMROfferTest.createXmrPaymentAccounts();
test.testTakeAlicesBuyBTCForXMROffer(testInfo);
test.testAlicesConfirmPaymentStarted(testInfo);
test.testBobsConfirmPaymentReceived(testInfo);
test.testAlicesBtcWithdrawalToExternalAddress(testInfo);
}
@Test
@Order(8)
public void testBsqSwapTradeTest(final TestInfo testInfo) {
BsqSwapTradeTest test = new BsqSwapTradeTest();
test.testGetBalancesBeforeTrade();