mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Merge pull request #5812 from ghubstan/02-cli-console-formatting-api
Deprecate and replace hard-coded CLI console output formatters
This commit is contained in:
commit
1578f45ffc
@ -57,6 +57,9 @@ public class ApiTestConfig {
|
||||
// Global constants
|
||||
public static final String BSQ = "BSQ";
|
||||
public static final String BTC = "BTC";
|
||||
public static final String EUR = "EUR";
|
||||
public static final String USD = "USD";
|
||||
public static final String XMR = "XMR";
|
||||
public static final String ARBITRATOR = "arbitrator";
|
||||
public static final String MEDIATOR = "mediator";
|
||||
public static final String REFUND_AGENT = "refundagent";
|
||||
|
@ -24,6 +24,8 @@ import bisq.core.proto.CoreProtoResolver;
|
||||
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import bisq.proto.grpc.BalancesInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
@ -35,7 +37,11 @@ import org.slf4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.BSQ;
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.apitest.config.ApiTestRateMeterInterceptorConfig.getTestRateMeterInterceptorConfig;
|
||||
import static bisq.cli.table.builder.TableType.BSQ_BALANCE_TBL;
|
||||
import static bisq.cli.table.builder.TableType.BTC_BALANCE_TBL;
|
||||
import static java.lang.String.format;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.Arrays.stream;
|
||||
@ -46,6 +52,7 @@ import static org.junit.jupiter.api.Assertions.fail;
|
||||
import bisq.apitest.ApiTestCase;
|
||||
import bisq.apitest.linux.BashCommand;
|
||||
import bisq.cli.GrpcClient;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
public class MethodTest extends ApiTestCase {
|
||||
|
||||
@ -155,6 +162,15 @@ public class MethodTest extends ApiTestCase {
|
||||
return bisq.core.payment.PaymentAccount.fromProto(paymentAccount, CORE_PROTO_RESOLVER);
|
||||
}
|
||||
|
||||
public static String formatBalancesTbls(BalancesInfo allBalances) {
|
||||
StringBuilder balances = new StringBuilder(BTC).append("\n");
|
||||
balances.append(new TableBuilder(BTC_BALANCE_TBL, allBalances.getBtc()).build());
|
||||
balances.append("\n");
|
||||
balances.append(BSQ).append("\n");
|
||||
balances.append(new TableBuilder(BSQ_BALANCE_TBL, allBalances.getBsq()).build());
|
||||
return balances.toString();
|
||||
}
|
||||
|
||||
protected static String encodeToHex(String s) {
|
||||
return Utilities.bytesAsHexString(s.getBytes(UTF_8));
|
||||
}
|
||||
|
@ -17,10 +17,14 @@
|
||||
|
||||
package bisq.apitest.method.offer;
|
||||
|
||||
import bisq.proto.grpc.BsqSwapOfferInfo;
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import protobuf.PaymentAccount;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
@ -36,11 +40,13 @@ import static bisq.apitest.config.BisqAppConfig.alicedaemon;
|
||||
import static bisq.apitest.config.BisqAppConfig.arbdaemon;
|
||||
import static bisq.apitest.config.BisqAppConfig.bobdaemon;
|
||||
import static bisq.apitest.config.BisqAppConfig.seednode;
|
||||
import static bisq.cli.table.builder.TableType.OFFER_TBL;
|
||||
import static bisq.common.util.MathUtils.exactMultiply;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.method.MethodTest;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractOfferTest extends MethodTest {
|
||||
@ -52,8 +58,11 @@ public abstract class AbstractOfferTest extends MethodTest {
|
||||
@Setter
|
||||
protected static boolean isLongRunningTest;
|
||||
|
||||
protected static PaymentAccount alicesBsqAcct;
|
||||
protected static PaymentAccount bobsBsqAcct;
|
||||
protected static PaymentAccount alicesBsqSwapAcct;
|
||||
protected static PaymentAccount bobsBsqSwapAcct;
|
||||
// TODO Deprecate legacy BSQ accounts when no longer in use.
|
||||
protected static PaymentAccount alicesLegacyBsqAcct;
|
||||
protected static PaymentAccount bobsLegacyBsqAcct;
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
@ -64,17 +73,9 @@ public abstract class AbstractOfferTest extends MethodTest {
|
||||
arbdaemon,
|
||||
alicedaemon,
|
||||
bobdaemon);
|
||||
}
|
||||
|
||||
public static void createBsqSwapBsqPaymentAccounts() {
|
||||
alicesBsqAcct = aliceClient.createCryptoCurrencyPaymentAccount("Alice's BsqSwap Account",
|
||||
BSQ,
|
||||
aliceClient.getUnusedBsqAddress(), // TODO refactor, bsq address not needed for atom acct
|
||||
false);
|
||||
bobsBsqAcct = bobClient.createCryptoCurrencyPaymentAccount("Bob's BsqSwap Account",
|
||||
BSQ,
|
||||
bobClient.getUnusedBsqAddress(), // TODO refactor, bsq address not needed for atom acct
|
||||
false);
|
||||
initSwapPaymentAccounts();
|
||||
createLegacyBsqPaymentAccounts();
|
||||
}
|
||||
|
||||
// Mkt Price Margin value of offer returned from server is scaled down by 10^-2.
|
||||
@ -105,13 +106,31 @@ public abstract class AbstractOfferTest extends MethodTest {
|
||||
return priceAsBigDecimal.toPlainString();
|
||||
};
|
||||
|
||||
protected final Function<OfferInfo, String> toOfferTable = (offer) ->
|
||||
new TableBuilder(OFFER_TBL, offer).build().toString();
|
||||
|
||||
protected final Function<List<OfferInfo>, String> toOffersTable = (offers) ->
|
||||
new TableBuilder(OFFER_TBL, offers).build().toString();
|
||||
|
||||
// TODO
|
||||
protected final Function<BsqSwapOfferInfo, String> toBsqSwapOfferTable = (offer) ->
|
||||
new TableBuilder(OFFER_TBL, offer).build().toString();
|
||||
|
||||
|
||||
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'.
|
||||
alicesBsqSwapAcct = aliceClient.getPaymentAccount("BSQ Swap");
|
||||
bobsBsqSwapAcct = bobClient.getPaymentAccount("BSQ Swap");
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static void createBsqPaymentAccounts() {
|
||||
alicesBsqAcct = aliceClient.createCryptoCurrencyPaymentAccount("Alice's BSQ Account",
|
||||
public static void createLegacyBsqPaymentAccounts() {
|
||||
alicesLegacyBsqAcct = aliceClient.createCryptoCurrencyPaymentAccount("Alice's Legacy BSQ Account",
|
||||
BSQ,
|
||||
aliceClient.getUnusedBsqAddress(),
|
||||
false);
|
||||
bobsBsqAcct = bobClient.createCryptoCurrencyPaymentAccount("Bob's BSQ Account",
|
||||
bobsLegacyBsqAcct = bobClient.createCryptoCurrencyPaymentAccount("Bob's Legacy BSQ Account",
|
||||
BSQ,
|
||||
bobClient.getUnusedBsqAddress(),
|
||||
false);
|
||||
|
@ -31,7 +31,6 @@ 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 java.lang.String.format;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
@ -46,7 +45,6 @@ public class BsqSwapOfferTest extends AbstractOfferTest {
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
AbstractOfferTest.setUp();
|
||||
createBsqSwapBsqPaymentAccounts();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@ -115,7 +113,7 @@ public class BsqSwapOfferTest extends AbstractOfferTest {
|
||||
1_000_000L,
|
||||
1_000_000L,
|
||||
"0.00005",
|
||||
alicesBsqAcct.getId());
|
||||
alicesBsqSwapAcct.getId());
|
||||
log.debug("BsqSwap Sell BSQ (Buy BTC) OFFER:\n{}", bsqSwapOffer);
|
||||
var newOfferId = bsqSwapOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
|
@ -32,10 +32,7 @@ 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;
|
||||
@ -53,7 +50,6 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
AbstractOfferTest.setUp();
|
||||
createBsqPaymentAccounts();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -68,9 +64,9 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
100_000_000L,
|
||||
"0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesBsqAcct.getId(),
|
||||
alicesLegacyBsqAcct.getId(),
|
||||
MAKER_FEE_CURRENCY_CODE);
|
||||
log.info("Sell BSQ (Buy BTC) OFFER:\n{}", formatOfferTable(singletonList(newOffer), BSQ));
|
||||
log.debug("Sell BSQ (Buy BTC) OFFER:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsMyPendingOffer());
|
||||
|
||||
@ -82,7 +78,7 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
assertEquals(100_000_000L, newOffer.getAmount());
|
||||
assertEquals(100_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BSQ, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
@ -99,7 +95,7 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
assertEquals(100_000_000L, newOffer.getAmount());
|
||||
assertEquals(100_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BSQ, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
@ -115,9 +111,9 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
100_000_000L,
|
||||
"0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesBsqAcct.getId(),
|
||||
alicesLegacyBsqAcct.getId(),
|
||||
MAKER_FEE_CURRENCY_CODE);
|
||||
log.info("SELL 20K BSQ OFFER:\n{}", formatOfferTable(singletonList(newOffer), BSQ));
|
||||
log.debug("SELL 20K BSQ OFFER:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsMyPendingOffer());
|
||||
|
||||
@ -129,7 +125,7 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
assertEquals(100_000_000L, newOffer.getAmount());
|
||||
assertEquals(100_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BSQ, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
@ -146,7 +142,7 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
assertEquals(100_000_000L, newOffer.getAmount());
|
||||
assertEquals(100_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BSQ, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
@ -162,9 +158,9 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
5_000_000L,
|
||||
"0.00005", // FIXED PRICE IN BTC sats FOR 1 BSQ
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesBsqAcct.getId(),
|
||||
alicesLegacyBsqAcct.getId(),
|
||||
MAKER_FEE_CURRENCY_CODE);
|
||||
log.info("BUY 1-2K BSQ OFFER:\n{}", formatOfferTable(singletonList(newOffer), BSQ));
|
||||
log.debug("BUY 1-2K BSQ OFFER:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsMyPendingOffer());
|
||||
|
||||
@ -176,7 +172,7 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
assertEquals(10_000_000L, newOffer.getAmount());
|
||||
assertEquals(5_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BSQ, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
@ -193,7 +189,7 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
assertEquals(10_000_000L, newOffer.getAmount());
|
||||
assertEquals(5_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BSQ, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
@ -209,9 +205,9 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
25_000_000L,
|
||||
"0.00005", // FIXED PRICE IN BTC sats FOR 1 BSQ
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesBsqAcct.getId(),
|
||||
alicesLegacyBsqAcct.getId(),
|
||||
MAKER_FEE_CURRENCY_CODE);
|
||||
log.info("SELL 5-10K BSQ OFFER:\n{}", formatOfferTable(singletonList(newOffer), BSQ));
|
||||
log.debug("SELL 5-10K BSQ OFFER:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsMyPendingOffer());
|
||||
|
||||
@ -223,7 +219,7 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
assertEquals(50_000_000L, newOffer.getAmount());
|
||||
assertEquals(25_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(7_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BSQ, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
@ -240,7 +236,7 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
assertEquals(50_000_000L, newOffer.getAmount());
|
||||
assertEquals(25_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(7_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(alicesLegacyBsqAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BSQ, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
@ -250,18 +246,18 @@ public class CreateBSQOffersTest extends AbstractOfferTest {
|
||||
@Order(5)
|
||||
public void testGetAllMyBsqOffers() {
|
||||
List<OfferInfo> offers = aliceClient.getMyBsqOffersSortedByDate();
|
||||
log.info("ALL ALICE'S BSQ OFFERS:\n{}", formatOfferTable(offers, BSQ));
|
||||
log.debug("ALL ALICE'S BSQ OFFERS:\n{}", toOffersTable.apply(offers));
|
||||
assertEquals(4, offers.size());
|
||||
log.info("ALICE'S BALANCES\n{}", formatBalancesTbls(aliceClient.getBalances()));
|
||||
log.debug("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));
|
||||
log.debug("ALL BOB'S AVAILABLE BSQ OFFERS:\n{}", toOffersTable.apply(offers));
|
||||
assertEquals(4, offers.size());
|
||||
log.info("BOB'S BALANCES\n{}", formatBalancesTbls(bobClient.getBalances()));
|
||||
log.debug("BOB'S BALANCES\n{}", formatBalancesTbls(bobClient.getBalances()));
|
||||
}
|
||||
|
||||
private void genBtcBlockAndWaitForOfferPreparation() {
|
||||
|
@ -29,9 +29,9 @@ 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.apitest.config.ApiTestConfig.EUR;
|
||||
import static bisq.apitest.config.ApiTestConfig.USD;
|
||||
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;
|
||||
@ -58,7 +58,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
audAccount.getId(),
|
||||
MAKER_FEE_CURRENCY_CODE);
|
||||
log.info("OFFER #1:\n{}", formatOfferTable(singletonList(newOffer), "AUD"));
|
||||
log.debug("OFFER #1:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsMyPendingOffer());
|
||||
|
||||
@ -103,7 +103,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
usdAccount.getId(),
|
||||
MAKER_FEE_CURRENCY_CODE);
|
||||
log.info("OFFER #2:\n{}", formatOfferTable(singletonList(newOffer), "USD"));
|
||||
log.debug("OFFER #2:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsMyPendingOffer());
|
||||
|
||||
@ -117,7 +117,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("USD", newOffer.getCounterCurrencyCode());
|
||||
assertEquals(USD, newOffer.getCounterCurrencyCode());
|
||||
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
|
||||
newOffer = aliceClient.getMyOffer(newOfferId);
|
||||
@ -132,7 +132,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("USD", newOffer.getCounterCurrencyCode());
|
||||
assertEquals(USD, newOffer.getCounterCurrencyCode());
|
||||
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
}
|
||||
|
||||
@ -148,7 +148,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
eurAccount.getId(),
|
||||
MAKER_FEE_CURRENCY_CODE);
|
||||
log.info("OFFER #3:\n{}", formatOfferTable(singletonList(newOffer), "EUR"));
|
||||
log.debug("OFFER #3:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsMyPendingOffer());
|
||||
|
||||
@ -162,7 +162,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(eurAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("EUR", newOffer.getCounterCurrencyCode());
|
||||
assertEquals(EUR, newOffer.getCounterCurrencyCode());
|
||||
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
|
||||
newOffer = aliceClient.getMyOffer(newOfferId);
|
||||
@ -177,7 +177,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(eurAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("EUR", newOffer.getCounterCurrencyCode());
|
||||
assertEquals(EUR, newOffer.getCounterCurrencyCode());
|
||||
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ 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.apitest.config.ApiTestConfig.USD;
|
||||
import static bisq.common.util.MathUtils.roundDouble;
|
||||
import static bisq.common.util.MathUtils.scaleDownByPowerOf10;
|
||||
import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
|
||||
@ -47,7 +47,6 @@ import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
|
||||
import static java.lang.Math.abs;
|
||||
import static java.lang.String.format;
|
||||
import static java.math.RoundingMode.HALF_UP;
|
||||
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;
|
||||
@ -81,7 +80,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
||||
usdAccount.getId(),
|
||||
MAKER_FEE_CURRENCY_CODE,
|
||||
NO_TRIGGER_PRICE);
|
||||
log.info("OFFER #1:\n{}", formatOfferTable(singletonList(newOffer), "usd"));
|
||||
log.debug("OFFER #1:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsMyPendingOffer());
|
||||
|
||||
@ -94,7 +93,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("USD", newOffer.getCounterCurrencyCode());
|
||||
assertEquals(USD, newOffer.getCounterCurrencyCode());
|
||||
assertTrue(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
|
||||
newOffer = aliceClient.getMyOffer(newOfferId);
|
||||
@ -108,7 +107,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("USD", newOffer.getCounterCurrencyCode());
|
||||
assertEquals(USD, newOffer.getCounterCurrencyCode());
|
||||
assertTrue(newOffer.getIsCurrencyForMakerFeeBtc());
|
||||
|
||||
assertCalculatedPriceIsCorrect(newOffer, priceMarginPctInput);
|
||||
@ -128,7 +127,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
||||
nzdAccount.getId(),
|
||||
MAKER_FEE_CURRENCY_CODE,
|
||||
NO_TRIGGER_PRICE);
|
||||
log.info("OFFER #2:\n{}", formatOfferTable(singletonList(newOffer), "nzd"));
|
||||
log.debug("OFFER #2:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsMyPendingOffer());
|
||||
|
||||
@ -175,7 +174,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
||||
gbpAccount.getId(),
|
||||
MAKER_FEE_CURRENCY_CODE,
|
||||
NO_TRIGGER_PRICE);
|
||||
log.info("OFFER #3:\n{}", formatOfferTable(singletonList(newOffer), "gbp"));
|
||||
log.debug("OFFER #3:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsMyPendingOffer());
|
||||
|
||||
@ -222,7 +221,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
||||
brlAccount.getId(),
|
||||
MAKER_FEE_CURRENCY_CODE,
|
||||
NO_TRIGGER_PRICE);
|
||||
log.info("OFFER #4:\n{}", formatOfferTable(singletonList(newOffer), "brl"));
|
||||
log.debug("OFFER #4:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsMyPendingOffer());
|
||||
|
||||
@ -262,7 +261,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
||||
double mktPriceAsDouble = aliceClient.getBtcPrice("usd");
|
||||
BigDecimal mktPrice = new BigDecimal(Double.toString(mktPriceAsDouble));
|
||||
BigDecimal triggerPrice = mktPrice.add(new BigDecimal("1000.9999"));
|
||||
long triggerPriceAsLong = Price.parse("USD", triggerPrice.toString()).getValue();
|
||||
long triggerPriceAsLong = Price.parse(USD, triggerPrice.toString()).getValue();
|
||||
|
||||
var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
|
||||
"usd",
|
||||
@ -278,7 +277,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
||||
|
||||
genBtcBlocksThenWait(1, 4000); // give time to add to offer book
|
||||
newOffer = aliceClient.getMyOffer(newOffer.getId());
|
||||
log.info("OFFER #5:\n{}", formatOfferTable(singletonList(newOffer), "usd"));
|
||||
log.debug("OFFER #5:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsMyPendingOffer());
|
||||
assertEquals(triggerPriceAsLong, newOffer.getTriggerPrice());
|
||||
|
@ -38,11 +38,11 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.BSQ;
|
||||
import static bisq.cli.TableFormat.formatOfferTable;
|
||||
import static bisq.apitest.config.ApiTestConfig.EUR;
|
||||
import static bisq.apitest.config.ApiTestConfig.USD;
|
||||
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static bisq.proto.grpc.EditOfferRequest.EditType.*;
|
||||
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.assertThrows;
|
||||
@ -58,7 +58,6 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
|
||||
// Some test fixtures to reduce duplication.
|
||||
private static final Map<String, PaymentAccount> paymentAcctCache = new HashMap<>();
|
||||
private static final String DOLLAR = "USD";
|
||||
private static final String RUBLE = "RUB";
|
||||
private static final long AMOUNT = 10000000L;
|
||||
|
||||
@ -67,11 +66,11 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
public void testOfferDisableAndEnable() {
|
||||
PaymentAccount paymentAcct = getOrCreatePaymentAccount("DE");
|
||||
OfferInfo originalOffer = createMktPricedOfferForEdit(BUY.name(),
|
||||
"EUR",
|
||||
EUR,
|
||||
paymentAcct.getId(),
|
||||
0.0,
|
||||
NO_TRIGGER_PRICE);
|
||||
log.info("ORIGINAL EUR OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "EUR"));
|
||||
log.debug("ORIGINAL EUR OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
assertFalse(originalOffer.getIsActivated()); // Not activated until prep is done.
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
originalOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
@ -80,13 +79,13 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
aliceClient.editOfferActivationState(originalOffer.getId(), DEACTIVATE_OFFER);
|
||||
genBtcBlocksThenWait(1, 1500); // Wait for offer book removal.
|
||||
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED EUR OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "EUR"));
|
||||
log.debug("EDITED EUR OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
assertFalse(editedOffer.getIsActivated());
|
||||
// Re-enable offer
|
||||
aliceClient.editOfferActivationState(editedOffer.getId(), ACTIVATE_OFFER);
|
||||
genBtcBlocksThenWait(1, 1500); // Wait for offer book re-entry.
|
||||
editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED EUR OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "EUR"));
|
||||
log.debug("EDITED EUR OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
assertTrue(editedOffer.getIsActivated());
|
||||
|
||||
doSanityCheck(originalOffer, editedOffer);
|
||||
@ -97,24 +96,24 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
public void testEditTriggerPrice() {
|
||||
PaymentAccount paymentAcct = getOrCreatePaymentAccount("FI");
|
||||
OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
|
||||
"EUR",
|
||||
EUR,
|
||||
paymentAcct.getId(),
|
||||
0.0,
|
||||
NO_TRIGGER_PRICE);
|
||||
log.info("ORIGINAL EUR OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "EUR"));
|
||||
log.debug("ORIGINAL EUR OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
originalOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
assertEquals(0 /*no trigger price*/, originalOffer.getTriggerPrice());
|
||||
|
||||
// Edit the offer's trigger price, nothing else.
|
||||
var mktPrice = aliceClient.getBtcPrice("EUR");
|
||||
var mktPrice = aliceClient.getBtcPrice(EUR);
|
||||
var delta = 5_000.00;
|
||||
var newTriggerPriceAsLong = calcPriceAsLong.apply(mktPrice, delta);
|
||||
|
||||
aliceClient.editOfferTriggerPrice(originalOffer.getId(), newTriggerPriceAsLong);
|
||||
sleep(2500); // Wait for offer book re-entry.
|
||||
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED EUR OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "EUR"));
|
||||
log.debug("EDITED EUR OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice());
|
||||
assertTrue(editedOffer.getUseMarketBasedPrice());
|
||||
|
||||
@ -126,11 +125,11 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
public void testSetTriggerPriceToNegativeValueShouldThrowException() {
|
||||
PaymentAccount paymentAcct = getOrCreatePaymentAccount("FI");
|
||||
final OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
|
||||
"EUR",
|
||||
EUR,
|
||||
paymentAcct.getId(),
|
||||
0.0,
|
||||
NO_TRIGGER_PRICE);
|
||||
log.info("ORIGINAL EUR OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "EUR"));
|
||||
log.debug("ORIGINAL EUR OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
// Edit the offer's trigger price, set to -1, check error.
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
@ -147,18 +146,18 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
PaymentAccount paymentAcct = getOrCreatePaymentAccount("US");
|
||||
var originalMktPriceMargin = new BigDecimal("0.1").doubleValue();
|
||||
OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
|
||||
DOLLAR,
|
||||
USD,
|
||||
paymentAcct.getId(),
|
||||
originalMktPriceMargin,
|
||||
NO_TRIGGER_PRICE);
|
||||
log.info("ORIGINAL USD OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "USD"));
|
||||
log.debug("ORIGINAL USD OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin());
|
||||
// Edit the offer's price margin, nothing else.
|
||||
var newMktPriceMargin = new BigDecimal("0.5").doubleValue();
|
||||
aliceClient.editOfferPriceMargin(originalOffer.getId(), newMktPriceMargin);
|
||||
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED USD OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "USD"));
|
||||
log.debug("EDITED USD OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
|
||||
|
||||
doSanityCheck(originalOffer, editedOffer);
|
||||
@ -174,7 +173,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
RUBLE,
|
||||
paymentAcct.getId(),
|
||||
fixedPriceAsString);
|
||||
log.info("ORIGINAL RUB OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "RUB"));
|
||||
log.debug("ORIGINAL RUB OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
// Edit the offer's fixed price, nothing else.
|
||||
String editedFixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 100_000.0000);
|
||||
@ -182,7 +181,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
// Wait for edited offer to be removed from offer-book, edited, and re-published.
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED RUB OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "RUB"));
|
||||
log.debug("EDITED RUB OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
var expectedNewFixedPrice = scaledUpFiatOfferPrice.apply(new BigDecimal(editedFixedPriceAsString));
|
||||
assertEquals(expectedNewFixedPrice, editedOffer.getPrice());
|
||||
assertFalse(editedOffer.getUseMarketBasedPrice());
|
||||
@ -200,7 +199,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
RUBLE,
|
||||
paymentAcct.getId(),
|
||||
fixedPriceAsString);
|
||||
log.info("ORIGINAL RUB OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "RUB"));
|
||||
log.debug("ORIGINAL RUB OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
// Edit the offer's fixed price and deactivate it.
|
||||
String editedFixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 100_000.0000);
|
||||
@ -214,7 +213,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
// Wait for edited offer to be removed from offer-book, edited, and re-published.
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED RUB OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "RUB"));
|
||||
log.debug("EDITED RUB OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
var expectedNewFixedPrice = scaledUpFiatOfferPrice.apply(new BigDecimal(editedFixedPriceAsString));
|
||||
assertEquals(expectedNewFixedPrice, editedOffer.getPrice());
|
||||
assertFalse(editedOffer.getIsActivated());
|
||||
@ -229,11 +228,11 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
|
||||
var originalMktPriceMargin = new BigDecimal("0.0").doubleValue();
|
||||
OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
|
||||
DOLLAR,
|
||||
USD,
|
||||
paymentAcct.getId(),
|
||||
originalMktPriceMargin,
|
||||
0);
|
||||
log.info("ORIGINAL USD OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "USD"));
|
||||
log.debug("ORIGINAL USD OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
originalOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin());
|
||||
@ -250,7 +249,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
// Wait for edited offer to be removed from offer-book, edited, and re-published.
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED USD OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "USD"));
|
||||
log.debug("EDITED USD OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
|
||||
assertEquals(0, editedOffer.getTriggerPrice());
|
||||
assertFalse(editedOffer.getIsActivated());
|
||||
@ -264,15 +263,15 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
PaymentAccount paymentAcct = getOrCreatePaymentAccount("US");
|
||||
|
||||
var originalMktPriceMargin = new BigDecimal("0.0").doubleValue();
|
||||
var mktPriceAsDouble = aliceClient.getBtcPrice(DOLLAR);
|
||||
var mktPriceAsDouble = aliceClient.getBtcPrice(USD);
|
||||
var originalTriggerPriceAsLong = calcPriceAsLong.apply(mktPriceAsDouble, -5_000.0000);
|
||||
|
||||
OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
|
||||
DOLLAR,
|
||||
USD,
|
||||
paymentAcct.getId(),
|
||||
originalMktPriceMargin,
|
||||
originalTriggerPriceAsLong);
|
||||
log.info("ORIGINAL USD OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "USD"));
|
||||
log.debug("ORIGINAL USD OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
originalOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin());
|
||||
@ -291,7 +290,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
// Wait for edited offer to be removed from offer-book, edited, and re-published.
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED USD OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "USD"));
|
||||
log.debug("EDITED USD OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
|
||||
assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice());
|
||||
assertFalse(editedOffer.getIsActivated());
|
||||
@ -305,11 +304,11 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
PaymentAccount paymentAcct = getOrCreatePaymentAccount("US");
|
||||
var originalMktPriceMargin = new BigDecimal("0.0").doubleValue();
|
||||
final OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(),
|
||||
DOLLAR,
|
||||
USD,
|
||||
paymentAcct.getId(),
|
||||
originalMktPriceMargin,
|
||||
NO_TRIGGER_PRICE);
|
||||
log.info("ORIGINAL USD OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "USD"));
|
||||
log.debug("ORIGINAL USD OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
// Try to edit both the fixed price and mkt price margin.
|
||||
var newMktPriceMargin = new BigDecimal("0.25").doubleValue();
|
||||
@ -340,7 +339,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
RUBLE,
|
||||
paymentAcct.getId(),
|
||||
fixedPriceAsString);
|
||||
log.info("ORIGINAL RUB OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "RUB"));
|
||||
log.debug("ORIGINAL RUB OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
long newTriggerPrice = 1000000L;
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
@ -363,7 +362,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
"MXN",
|
||||
paymentAcct.getId(),
|
||||
fixedPriceAsString);
|
||||
log.info("ORIGINAL MXN OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "MXN"));
|
||||
log.debug("ORIGINAL MXN OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
|
||||
// Change the offer to mkt price based and set a trigger price.
|
||||
@ -380,7 +379,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
// Wait for edited offer to be removed from offer-book, edited, and re-published.
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED MXN OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "MXN"));
|
||||
log.debug("EDITED MXN OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
assertTrue(editedOffer.getUseMarketBasedPrice());
|
||||
assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin());
|
||||
assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice());
|
||||
@ -402,7 +401,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
paymentAcct.getId(),
|
||||
originalMktPriceMargin,
|
||||
originalTriggerPriceAsLong);
|
||||
log.info("ORIGINAL GBP OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "GBP"));
|
||||
log.debug("ORIGINAL GBP OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
|
||||
String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 0.00);
|
||||
@ -416,7 +415,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
// Wait for edited offer to be removed from offer-book, edited, and re-published.
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED GBP OFFER:\n{}", formatOfferTable(singletonList(editedOffer), "GBP"));
|
||||
log.debug("EDITED GBP OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
assertEquals(scaledUpFiatOfferPrice.apply(new BigDecimal(fixedPriceAsString)), editedOffer.getPrice());
|
||||
assertFalse(editedOffer.getUseMarketBasedPrice());
|
||||
assertEquals(0.00, editedOffer.getMarketPriceMargin());
|
||||
@ -427,16 +426,15 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
@Test
|
||||
@Order(13)
|
||||
public void testChangeFixedPricedBsqOfferToPriceMarginBasedOfferShouldThrowException() {
|
||||
createBsqPaymentAccounts();
|
||||
OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
|
||||
BSQ,
|
||||
100_000_000L,
|
||||
100_000_000L,
|
||||
"0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesBsqAcct.getId(),
|
||||
alicesLegacyBsqAcct.getId(),
|
||||
BSQ);
|
||||
log.info("ORIGINAL BSQ OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "BSQ"));
|
||||
log.debug("ORIGINAL BSQ OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
aliceClient.editOffer(originalOffer.getId(),
|
||||
@ -455,16 +453,15 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
@Test
|
||||
@Order(14)
|
||||
public void testEditTriggerPriceOnFixedPriceBsqOfferShouldThrowException() {
|
||||
createBsqPaymentAccounts();
|
||||
OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
|
||||
BSQ,
|
||||
100_000_000L,
|
||||
100_000_000L,
|
||||
"0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesBsqAcct.getId(),
|
||||
alicesLegacyBsqAcct.getId(),
|
||||
BSQ);
|
||||
log.info("ORIGINAL BSQ OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "BSQ"));
|
||||
log.debug("ORIGINAL BSQ OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
var newTriggerPriceAsLong = calcPriceAsLong.apply(0.00005, 0.00);
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
@ -484,7 +481,6 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
@Test
|
||||
@Order(15)
|
||||
public void testEditFixedPriceOnBsqOffer() {
|
||||
createBsqPaymentAccounts();
|
||||
String fixedPriceAsString = "0.00005"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
|
||||
final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
|
||||
BSQ,
|
||||
@ -492,9 +488,9 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
100_000_000L,
|
||||
fixedPriceAsString,
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesBsqAcct.getId(),
|
||||
alicesLegacyBsqAcct.getId(),
|
||||
BSQ);
|
||||
log.info("ORIGINAL BSQ OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "BSQ"));
|
||||
log.debug("ORIGINAL BSQ OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
String newFixedPriceAsString = "0.00003111";
|
||||
aliceClient.editOffer(originalOffer.getId(),
|
||||
@ -507,7 +503,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
// Wait for edited offer to be edited and removed from offer-book.
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED BSQ OFFER:\n{}", formatOfferTable(singletonList(editedOffer), BSQ));
|
||||
log.debug("EDITED BSQ OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice());
|
||||
assertTrue(editedOffer.getIsActivated());
|
||||
assertFalse(editedOffer.getUseMarketBasedPrice());
|
||||
@ -518,7 +514,6 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
@Test
|
||||
@Order(16)
|
||||
public void testDisableBsqOffer() {
|
||||
createBsqPaymentAccounts();
|
||||
String fixedPriceAsString = "0.00005"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
|
||||
final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
|
||||
BSQ,
|
||||
@ -526,9 +521,9 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
100_000_000L,
|
||||
fixedPriceAsString,
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesBsqAcct.getId(),
|
||||
alicesLegacyBsqAcct.getId(),
|
||||
BSQ);
|
||||
log.info("ORIGINAL BSQ OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "BSQ"));
|
||||
log.debug("ORIGINAL BSQ OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
aliceClient.editOffer(originalOffer.getId(),
|
||||
fixedPriceAsString,
|
||||
@ -540,7 +535,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
// Wait for edited offer to be removed from offer-book.
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED BSQ OFFER:\n{}", formatOfferTable(singletonList(editedOffer), BSQ));
|
||||
log.debug("EDITED BSQ OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
assertFalse(editedOffer.getIsActivated());
|
||||
assertEquals(scaledUpAltcoinOfferPrice.apply(fixedPriceAsString), editedOffer.getPrice());
|
||||
assertFalse(editedOffer.getUseMarketBasedPrice());
|
||||
@ -551,7 +546,6 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
@Test
|
||||
@Order(17)
|
||||
public void testEditFixedPriceAndDisableBsqOffer() {
|
||||
createBsqPaymentAccounts();
|
||||
String fixedPriceAsString = "0.00005"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
|
||||
final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(),
|
||||
BSQ,
|
||||
@ -559,9 +553,9 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
100_000_000L,
|
||||
fixedPriceAsString,
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesBsqAcct.getId(),
|
||||
alicesLegacyBsqAcct.getId(),
|
||||
BSQ);
|
||||
log.info("ORIGINAL BSQ OFFER:\n{}", formatOfferTable(singletonList(originalOffer), "BSQ"));
|
||||
log.debug("ORIGINAL BSQ OFFER:\n{}", toOfferTable.apply(originalOffer));
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
String newFixedPriceAsString = "0.000045";
|
||||
aliceClient.editOffer(originalOffer.getId(),
|
||||
@ -574,7 +568,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
||||
// Wait for edited offer to be edited and removed from offer-book.
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId());
|
||||
log.info("EDITED BSQ OFFER:\n{}", formatOfferTable(singletonList(editedOffer), BSQ));
|
||||
log.debug("EDITED BSQ OFFER:\n{}", toOfferTable.apply(editedOffer));
|
||||
assertFalse(editedOffer.getIsActivated());
|
||||
assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice());
|
||||
assertFalse(editedOffer.getUseMarketBasedPrice());
|
||||
|
@ -73,16 +73,21 @@ import org.junit.jupiter.api.TestInfo;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind;
|
||||
import static bisq.apitest.config.ApiTestConfig.EUR;
|
||||
import static bisq.apitest.config.ApiTestConfig.USD;
|
||||
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
|
||||
import static bisq.cli.TableFormat.formatPaymentAcctTbl;
|
||||
import static bisq.cli.table.builder.TableType.PAYMENT_ACCOUNT_TBL;
|
||||
import static bisq.core.locale.CurrencyUtil.*;
|
||||
import static bisq.core.payment.payload.PaymentMethod.*;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@SuppressWarnings({"OptionalGetWithoutIsPresent", "ConstantConditions"})
|
||||
@Disabled
|
||||
@Slf4j
|
||||
@ -226,7 +231,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
CashDepositAccount paymentAccount = (CashDepositAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("EUR", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(EUR, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_COUNTRY),
|
||||
Objects.requireNonNull(paymentAccount.getCountry()).code);
|
||||
@ -305,7 +310,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
ClearXchangeAccount paymentAccount = (ClearXchangeAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("USD", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(USD, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL_OR_MOBILE_NR), paymentAccount.getEmailOrMobileNr());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
|
||||
@ -378,7 +383,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
HalCashAccount paymentAccount = (HalCashAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("EUR", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(EUR, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_MOBILE_NR), paymentAccount.getMobileNr());
|
||||
print(paymentAccount);
|
||||
@ -463,7 +468,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
MoneyBeamAccount paymentAccount = (MoneyBeamAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("EUR", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(EUR, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_ACCOUNT_ID), paymentAccount.getAccountId());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SALT), paymentAccount.getSaltAsHex());
|
||||
@ -519,7 +524,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
PerfectMoneyAccount paymentAccount = (PerfectMoneyAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("USD", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(USD, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_ACCOUNT_NR), paymentAccount.getAccountNr());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SALT), paymentAccount.getSaltAsHex());
|
||||
@ -593,7 +598,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
PopmoneyAccount paymentAccount = (PopmoneyAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("USD", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(USD, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_ACCOUNT_ID), paymentAccount.getAccountId());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
|
||||
@ -712,7 +717,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_COUNTRY),
|
||||
Objects.requireNonNull(paymentAccount.getCountry()).code);
|
||||
verifyAccountSingleTradeCurrency("EUR", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(EUR, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_IBAN), paymentAccount.getIban());
|
||||
@ -743,7 +748,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_COUNTRY),
|
||||
Objects.requireNonNull(paymentAccount.getCountry()).code);
|
||||
verifyAccountSingleTradeCurrency("EUR", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(EUR, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_IBAN), paymentAccount.getIban());
|
||||
@ -813,7 +818,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "IT Swift Acct w/ DE Intermediary");
|
||||
String allFiatCodes = getCommaDelimitedFiatCurrencyCodes(getAllSortedFiatCurrencies());
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, allFiatCodes);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, "EUR");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, EUR);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BANK_SWIFT_CODE, "PASCITMMFIR");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BANK_COUNTRY_CODE, "IT");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BANK_NAME, "BANCA MONTE DEI PASCHI DI SIENA S.P.A.");
|
||||
@ -927,7 +932,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
add(getTradeCurrency("CAD").get());
|
||||
add(getTradeCurrency("HRK").get());
|
||||
add(getTradeCurrency("CZK").get());
|
||||
add(getTradeCurrency("EUR").get());
|
||||
add(getTradeCurrency(EUR).get());
|
||||
add(getTradeCurrency("HKD").get());
|
||||
add(getTradeCurrency("IDR").get());
|
||||
add(getTradeCurrency("JPY").get());
|
||||
@ -1048,7 +1053,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
USPostalMoneyOrderAccount paymentAccount = (USPostalMoneyOrderAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("USD", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(USD, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_POSTAL_ADDRESS), paymentAccount.getPostalAddress());
|
||||
@ -1096,7 +1101,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
WesternUnionAccount paymentAccount = (WesternUnionAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("USD", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(USD, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getFullName());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_CITY), paymentAccount.getCity());
|
||||
@ -1115,7 +1120,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
||||
private void print(PaymentAccount paymentAccount) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount);
|
||||
log.debug("\n{}", formatPaymentAcctTbl(singletonList(paymentAccount.toProtoMessage())));
|
||||
log.debug("\n{}", new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccount.toProtoMessage()).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package bisq.apitest.method.trade;
|
||||
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@ -9,16 +11,23 @@ import org.slf4j.Logger;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
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 static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.DEPOSIT_CONFIRMED;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.FIAT_SENT;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.PAYOUT_PUBLISHED;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.State.BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG;
|
||||
import static java.lang.String.format;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.method.offer.AbstractOfferTest;
|
||||
import bisq.cli.GrpcClient;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
public class AbstractTradeTest extends AbstractOfferTest {
|
||||
|
||||
@ -28,6 +37,7 @@ public class AbstractTradeTest extends AbstractOfferTest {
|
||||
protected static String tradeId;
|
||||
|
||||
protected final Supplier<Integer> maxTradeStateAndPhaseChecks = () -> isLongRunningTest ? 10 : 2;
|
||||
private final Function<GrpcClient, String> toUserName = (client) -> client.equals(aliceClient) ? "Alice" : "Bob";
|
||||
|
||||
@BeforeAll
|
||||
public static void initStaticFixtures() {
|
||||
@ -36,15 +46,128 @@ public class AbstractTradeTest extends AbstractOfferTest {
|
||||
|
||||
protected final TradeInfo takeAlicesOffer(String offerId,
|
||||
String paymentAccountId,
|
||||
String takerFeeCurrencyCode) {
|
||||
return bobClient.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode);
|
||||
String takerFeeCurrencyCode,
|
||||
boolean generateBtcBlock) {
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
var trade = bobClient.takeOffer(offerId,
|
||||
paymentAccountId,
|
||||
takerFeeCurrencyCode);
|
||||
assertNotNull(trade);
|
||||
assertEquals(offerId, trade.getTradeId());
|
||||
|
||||
if (takerFeeCurrencyCode.equals(BTC))
|
||||
assertTrue(trade.getIsCurrencyForTakerFeeBtc());
|
||||
else
|
||||
assertFalse(trade.getIsCurrencyForTakerFeeBtc());
|
||||
|
||||
// Cache the trade id for the other tests.
|
||||
tradeId = trade.getTradeId();
|
||||
|
||||
if (generateBtcBlock)
|
||||
genBtcBlocksThenWait(1, 6_000);
|
||||
|
||||
return trade;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected final TradeInfo takeBobsOffer(String offerId,
|
||||
String paymentAccountId,
|
||||
String takerFeeCurrencyCode) {
|
||||
return aliceClient.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode);
|
||||
protected final void waitForDepositConfirmation(Logger log,
|
||||
TestInfo testInfo,
|
||||
GrpcClient grpcClient,
|
||||
String tradeId) {
|
||||
Predicate<TradeInfo> isTradeInDepositConfirmedStateAndPhase = (t) ->
|
||||
t.getState().equals(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN.name())
|
||||
&& t.getPhase().equals(DEPOSIT_CONFIRMED.name());
|
||||
|
||||
String userName = toUserName.apply(grpcClient);
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
TradeInfo trade = grpcClient.getTrade(tradeId);
|
||||
if (!isTradeInDepositConfirmedStateAndPhase.test(trade)) {
|
||||
log.warn("{} still waiting on trade {} tx {}: DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN, attempt # {}",
|
||||
userName,
|
||||
trade.getShortId(),
|
||||
trade.getDepositTxId(),
|
||||
i);
|
||||
genBtcBlocksThenWait(1, 4_000);
|
||||
} else {
|
||||
EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN)
|
||||
.setPhase(DEPOSIT_CONFIRMED)
|
||||
.setDepositPublished(true)
|
||||
.setDepositConfirmed(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log,
|
||||
testInfo,
|
||||
userName + "'s view after deposit is confirmed",
|
||||
trade);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final void verifyTakerDepositConfirmed(TradeInfo trade) {
|
||||
if (!trade.getIsDepositConfirmed()) {
|
||||
fail(format("INVALID_PHASE for trade %s in STATE=%s PHASE=%s, deposit tx never confirmed.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
}
|
||||
|
||||
protected final void waitForBuyerSeesPaymentInitiatedMessage(Logger log,
|
||||
TestInfo testInfo,
|
||||
GrpcClient grpcClient,
|
||||
String tradeId) {
|
||||
String userName = toUserName.apply(grpcClient);
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
TradeInfo trade = grpcClient.getTrade(tradeId);
|
||||
if (!trade.getIsFiatSent()) {
|
||||
log.warn("{} still waiting for trade {} {}, attempt # {}",
|
||||
userName,
|
||||
trade.getShortId(),
|
||||
BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG,
|
||||
i);
|
||||
sleep(5_000);
|
||||
} else {
|
||||
// Do not check trade.getOffer().getState() here because
|
||||
// it might be AVAILABLE, not OFFER_FEE_PAID.
|
||||
EXPECTED_PROTOCOL_STATUS.setState(BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG)
|
||||
.setPhase(FIAT_SENT)
|
||||
.setFiatSent(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, userName + "'s view after confirming trade payment sent", trade);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final void waitForSellerSeesPaymentInitiatedMessage(Logger log,
|
||||
TestInfo testInfo,
|
||||
GrpcClient grpcClient,
|
||||
String tradeId) {
|
||||
Predicate<TradeInfo> isTradeInPaymentReceiptConfirmedStateAndPhase = (t) ->
|
||||
t.getState().equals(SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG.name()) &&
|
||||
(t.getPhase().equals(PAYOUT_PUBLISHED.name()) || t.getPhase().equals(FIAT_SENT.name()));
|
||||
String userName = toUserName.apply(grpcClient);
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
TradeInfo trade = grpcClient.getTrade(tradeId);
|
||||
if (!isTradeInPaymentReceiptConfirmedStateAndPhase.test(trade)) {
|
||||
log.warn("INVALID_PHASE for {}'s trade {} in STATE={} PHASE={}, cannot confirm payment received yet.",
|
||||
userName,
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase());
|
||||
sleep(10_000);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TradeInfo trade = grpcClient.getTrade(tradeId);
|
||||
if (!isTradeInPaymentReceiptConfirmedStateAndPhase.test(trade)) {
|
||||
fail(format("INVALID_PHASE for {}'s trade %s in STATE=%s PHASE=%s, cannot confirm payment received.",
|
||||
userName,
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
}
|
||||
|
||||
protected final void verifyExpectedProtocolStatus(TradeInfo trade) {
|
||||
@ -70,7 +193,7 @@ public class AbstractTradeTest extends AbstractOfferTest {
|
||||
? contract.getTakerPaymentAccountPayload().getAddress()
|
||||
: contract.getMakerPaymentAccountPayload().getAddress();
|
||||
String sendBsqAmount = formatBsqAmount(trade.getOffer().getVolume());
|
||||
log.info("Sending {} BSQ to address {}", sendBsqAmount, receiverAddress);
|
||||
log.debug("Sending {} BSQ to address {}", sendBsqAmount, receiverAddress);
|
||||
grpcClient.sendBsq(receiverAddress, sendBsqAmount, "");
|
||||
}
|
||||
|
||||
@ -85,39 +208,37 @@ public class AbstractTradeTest extends AbstractOfferTest {
|
||||
: contract.getMakerPaymentAccountPayload().getAddress();
|
||||
boolean receivedBsqSatoshis = grpcClient.verifyBsqSentToAddress(address, receiveAmountAsString);
|
||||
if (receivedBsqSatoshis)
|
||||
log.info("Payment of {} BSQ was received to address {} for trade with id {}.",
|
||||
log.debug("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.",
|
||||
fail(format("Payment of %s BSQ was was not sent to address %s for trade with id %s.",
|
||||
receiveAmountAsString,
|
||||
address,
|
||||
trade.getTradeId()));
|
||||
}
|
||||
|
||||
protected final void logBalances(Logger log, TestInfo testInfo) {
|
||||
var alicesBalances = aliceClient.getBalances();
|
||||
log.debug("{} Alice's Current Balances:\n{}",
|
||||
testName(testInfo),
|
||||
formatBalancesTbls(alicesBalances));
|
||||
var bobsBalances = bobClient.getBalances();
|
||||
log.debug("{} Bob's Current Balances:\n{}",
|
||||
testName(testInfo),
|
||||
formatBalancesTbls(bobsBalances));
|
||||
}
|
||||
|
||||
protected final void logTrade(Logger log,
|
||||
TestInfo testInfo,
|
||||
String description,
|
||||
TradeInfo trade) {
|
||||
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",
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(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(),
|
||||
format(trade)));
|
||||
new TableBuilder(TRADE_DETAIL_TBL, trade).build()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,6 @@ package bisq.apitest.method.trade;
|
||||
import bisq.proto.grpc.BsqSwapOfferInfo;
|
||||
import bisq.proto.grpc.BsqSwapTradeInfo;
|
||||
|
||||
import protobuf.BsqSwapTrade;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -38,11 +36,12 @@ 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 java.lang.String.format;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static protobuf.BsqSwapTrade.State.COMPLETED;
|
||||
import static protobuf.BsqSwapTrade.State.PREPARATION;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
|
||||
|
||||
@ -63,7 +62,6 @@ public class BsqSwapTradeTest extends AbstractOfferTest {
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
AbstractOfferTest.setUp();
|
||||
createBsqSwapBsqPaymentAccounts();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@ -75,9 +73,9 @@ public class BsqSwapTradeTest extends AbstractOfferTest {
|
||||
@Order(1)
|
||||
public void testGetBalancesBeforeTrade() {
|
||||
var alicesBalances = aliceClient.getBalances();
|
||||
log.info("Alice's Before Trade Balance:\n{}", formatBalancesTbls(alicesBalances));
|
||||
log.debug("Alice's Before Trade Balance:\n{}", formatBalancesTbls(alicesBalances));
|
||||
var bobsBalances = bobClient.getBalances();
|
||||
log.info("Bob's Before Trade Balance:\n{}", formatBalancesTbls(bobsBalances));
|
||||
log.debug("Bob's Before Trade Balance:\n{}", formatBalancesTbls(bobsBalances));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -87,7 +85,7 @@ public class BsqSwapTradeTest extends AbstractOfferTest {
|
||||
1_000_000L,
|
||||
1_000_000L,
|
||||
"0.00005",
|
||||
alicesBsqAcct.getId());
|
||||
alicesBsqSwapAcct.getId());
|
||||
log.debug("BsqSwap Sell BSQ (Buy BTC) OFFER:\n{}", bsqSwapOffer);
|
||||
var newOfferId = bsqSwapOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
@ -105,24 +103,25 @@ public class BsqSwapTradeTest extends AbstractOfferTest {
|
||||
public void testBobTakesBsqSwapOffer() {
|
||||
var bsqSwapOffer = getAvailableBsqSwapOffer();
|
||||
var bsqSwapTradeInfo = bobClient.takeBsqSwapOffer(bsqSwapOffer.getId(),
|
||||
bobsBsqAcct.getId(),
|
||||
bobsBsqSwapAcct.getId(),
|
||||
BISQ_FEE_CURRENCY_CODE);
|
||||
log.debug("Trade at t1: {}", bsqSwapTradeInfo);
|
||||
assertEquals(BsqSwapTrade.State.PREPARATION.name(), bsqSwapTradeInfo.getState());
|
||||
assertEquals(PREPARATION.name(), bsqSwapTradeInfo.getState());
|
||||
genBtcBlocksThenWait(1, 3_000);
|
||||
|
||||
bsqSwapTradeInfo = getBsqSwapTrade(bsqSwapTradeInfo.getTradeId());
|
||||
log.debug("Trade at t2: {}", bsqSwapTradeInfo);
|
||||
assertEquals(BsqSwapTrade.State.COMPLETED.name(), bsqSwapTradeInfo.getState());
|
||||
assertEquals(COMPLETED.name(), bsqSwapTradeInfo.getState());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testGetBalancesAfterTrade() {
|
||||
sleep(2_500); // Give wallet time to finish processing TX.
|
||||
var alicesBalances = aliceClient.getBalances();
|
||||
log.info("Alice's After Trade Balance:\n{}", formatBalancesTbls(alicesBalances));
|
||||
log.debug("Alice's After Trade Balance:\n{}", formatBalancesTbls(alicesBalances));
|
||||
var bobsBalances = bobClient.getBalances();
|
||||
log.info("Bob's After Trade Balance:\n{}", formatBalancesTbls(bobsBalances));
|
||||
log.debug("Bob's After Trade Balance:\n{}", formatBalancesTbls(bobsBalances));
|
||||
}
|
||||
|
||||
private BsqSwapOfferInfo getAvailableBsqSwapOffer() {
|
||||
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.method.trade;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.method.offer.AbstractOfferTest;
|
||||
|
||||
// @Disabled
|
||||
@Slf4j
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class BsqSwapTradeTestLoop extends AbstractOfferTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
AbstractOfferTest.setUp();
|
||||
createBsqSwapBsqPaymentAccounts();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testGetBalancesBeforeTrade() {
|
||||
BsqSwapTradeTest test = new BsqSwapTradeTest();
|
||||
runTradeLoop(test);
|
||||
}
|
||||
|
||||
private void runTradeLoop(BsqSwapTradeTest test) {
|
||||
// TODO Fix wallet inconsistency bugs after 2nd trades.
|
||||
for (int tradeCount = 1; tradeCount <= 2; tradeCount++) {
|
||||
log.warn("================================ Trade # {} ================================", tradeCount);
|
||||
test.testGetBalancesBeforeTrade();
|
||||
|
||||
test.testAliceCreateBsqSwapBuyOffer();
|
||||
genBtcBlocksThenWait(1, 8000);
|
||||
|
||||
test.testBobTakesBsqSwapOffer();
|
||||
genBtcBlocksThenWait(1, 8000);
|
||||
|
||||
test.testGetBalancesAfterTrade();
|
||||
}
|
||||
}
|
||||
}
|
@ -17,12 +17,8 @@
|
||||
|
||||
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;
|
||||
@ -34,18 +30,10 @@ 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.model.bisq_v1.Trade.Phase.DEPOSIT_CONFIRMED;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.FIAT_SENT;
|
||||
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.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG;
|
||||
import static bisq.core.trade.model.bisq_v1.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;
|
||||
@ -72,7 +60,6 @@ public class TakeBuyBSQOfferTest extends AbstractTradeTest {
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
AbstractOfferTest.setUp();
|
||||
createBsqPaymentAccounts();
|
||||
EXPECTED_PROTOCOL_STATUS.init();
|
||||
}
|
||||
|
||||
@ -90,9 +77,9 @@ public class TakeBuyBSQOfferTest extends AbstractTradeTest {
|
||||
7_500_000L,
|
||||
"0.000035", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesBsqAcct.getId(),
|
||||
alicesLegacyBsqAcct.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE);
|
||||
log.info("ALICE'S BUY BSQ (SELL BTC) OFFER:\n{}", formatOfferTable(singletonList(alicesOffer), BSQ));
|
||||
log.debug("ALICE'S BUY BSQ (SELL BTC) OFFER:\n{}", toOfferTable.apply(alicesOffer));
|
||||
genBtcBlocksThenWait(1, 5000);
|
||||
var offerId = alicesOffer.getId();
|
||||
assertFalse(alicesOffer.getIsCurrencyForMakerFeeBtc());
|
||||
@ -100,50 +87,27 @@ public class TakeBuyBSQOfferTest extends AbstractTradeTest {
|
||||
var alicesBsqOffers = aliceClient.getMyCryptoCurrencyOffers(btcTradeDirection, BSQ);
|
||||
assertEquals(1, alicesBsqOffers.size());
|
||||
|
||||
var trade = takeAlicesOffer(offerId, bobsBsqAcct.getId(), TRADE_FEE_CURRENCY_CODE);
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsLegacyBsqAcct.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
false);
|
||||
assertNotNull(trade);
|
||||
assertEquals(offerId, trade.getTradeId());
|
||||
assertFalse(trade.getIsCurrencyForTakerFeeBtc());
|
||||
// Cache the trade id for the other tests.
|
||||
tradeId = trade.getTradeId();
|
||||
|
||||
genBtcBlocksThenWait(1, 6000);
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
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);
|
||||
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId());
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
verifyTakerDepositConfirmed(trade);
|
||||
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);
|
||||
}
|
||||
@ -154,60 +118,14 @@ public class TakeBuyBSQOfferTest extends AbstractTradeTest {
|
||||
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()));
|
||||
}
|
||||
|
||||
verifyTakerDepositConfirmed(trade);
|
||||
sendBsqPayment(log, bobClient, trade);
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
@ -217,39 +135,12 @@ public class TakeBuyBSQOfferTest extends AbstractTradeTest {
|
||||
@Order(3)
|
||||
public void testAlicesConfirmPaymentReceived(final TestInfo testInfo) {
|
||||
try {
|
||||
waitForSellerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);
|
||||
sleep(2_000);
|
||||
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);
|
||||
|
||||
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)
|
||||
@ -257,11 +148,8 @@ public class TakeBuyBSQOfferTest extends AbstractTradeTest {
|
||||
.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);
|
||||
|
||||
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);
|
||||
}
|
||||
@ -269,34 +157,23 @@ public class TakeBuyBSQOfferTest extends AbstractTradeTest {
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testBobsKeepFunds(final TestInfo testInfo) {
|
||||
public void testKeepFunds(final TestInfo testInfo) {
|
||||
try {
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
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, 1000);
|
||||
genBtcBlocksThenWait(1, 1_000);
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
EXPECTED_PROTOCOL_STATUS.setState(BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG)
|
||||
.setPhase(PAYOUT_PUBLISHED);
|
||||
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));
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -19,12 +19,8 @@ package bisq.apitest.method.trade;
|
||||
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
@ -35,18 +31,14 @@ 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.apitest.config.ApiTestConfig.USD;
|
||||
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.DEPOSIT_CONFIRMED;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.FIAT_SENT;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.PAYOUT_PUBLISHED;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.State.*;
|
||||
import static java.lang.String.format;
|
||||
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.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static protobuf.Offer.State.OFFER_FEE_PAID;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
import static protobuf.OpenOffer.State.AVAILABLE;
|
||||
|
||||
@ -66,7 +58,7 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
|
||||
try {
|
||||
PaymentAccount alicesUsdAccount = createDummyF2FAccount(aliceClient, "US");
|
||||
var alicesOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
|
||||
"usd",
|
||||
USD,
|
||||
12_500_000L,
|
||||
12_500_000L, // min-amount = amount
|
||||
0.00,
|
||||
@ -78,53 +70,26 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
|
||||
assertFalse(alicesOffer.getIsCurrencyForMakerFeeBtc());
|
||||
|
||||
// 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.name(), "usd");
|
||||
// Wait times vary; my logs show >= 2-second delay.
|
||||
sleep(3_000); // TODO loop instead of hard code a wait time
|
||||
var alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), USD);
|
||||
assertEquals(1, alicesUsdOffers.size());
|
||||
|
||||
PaymentAccount bobsUsdAccount = createDummyF2FAccount(bobClient, "US");
|
||||
var trade = takeAlicesOffer(offerId, bobsUsdAccount.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, 4000);
|
||||
alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), "usd");
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsUsdAccount.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
false);
|
||||
sleep(2_500); // Allow available offer to be removed from offer book.
|
||||
alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), USD);
|
||||
assertEquals(0, alicesUsdOffers.size());
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId());
|
||||
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
|
||||
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 deposit is confirmed", trade, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -135,56 +100,10 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
|
||||
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 confirm payment started yet.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase());
|
||||
// fail("Bad trade state and phase.");
|
||||
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, could not confirm payment started.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
|
||||
waitForDepositConfirmation(log, testInfo, aliceClient, trade.getTradeId());
|
||||
aliceClient.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 {} BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG, attempt # {}",
|
||||
trade.getShortId(),
|
||||
i);
|
||||
sleep(5000);
|
||||
continue;
|
||||
} else {
|
||||
assertEquals(OFFER_FEE_PAID.name(), trade.getOffer().getState());
|
||||
EXPECTED_PROTOCOL_STATUS.setState(BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG)
|
||||
.setPhase(FIAT_SENT)
|
||||
.setFiatSent(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Alice's view after confirming fiat payment sent", trade);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sleep(6_000);
|
||||
waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
@ -194,37 +113,10 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
|
||||
@Order(3)
|
||||
public void testBobsConfirmPaymentReceived(final TestInfo testInfo) {
|
||||
try {
|
||||
waitForSellerSeesPaymentInitiatedMessage(log, testInfo, bobClient, tradeId);
|
||||
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());
|
||||
// fail("Bad trade state and phase.");
|
||||
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()));
|
||||
}
|
||||
|
||||
bobClient.confirmPaymentReceived(trade.getTradeId());
|
||||
sleep(3000);
|
||||
|
||||
sleep(3_000);
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
// Note: offer.state == available
|
||||
assertEquals(AVAILABLE.name(), trade.getOffer().getState());
|
||||
@ -234,7 +126,6 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
|
||||
.setFiatReceived(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Bob's view after confirming fiat payment received", trade);
|
||||
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
@ -242,34 +133,20 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testAlicesKeepFunds(final TestInfo testInfo) {
|
||||
public void testKeepFunds(final TestInfo testInfo) {
|
||||
try {
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
|
||||
genBtcBlocksThenWait(1, 1_000);
|
||||
var trade = aliceClient.getTrade(tradeId);
|
||||
logTrade(log, testInfo, "Alice's view before keeping funds", trade);
|
||||
|
||||
aliceClient.keepFunds(tradeId);
|
||||
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
|
||||
bobClient.keepFunds(tradeId);
|
||||
genBtcBlocksThenWait(1, 1_000);
|
||||
trade = aliceClient.getTrade(tradeId);
|
||||
EXPECTED_PROTOCOL_STATUS.setState(BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG)
|
||||
.setPhase(PAYOUT_PUBLISHED);
|
||||
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));
|
||||
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);
|
||||
}
|
||||
|
@ -37,12 +37,8 @@ package bisq.apitest.method.trade;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.payload.NationalBankAccountPayload;
|
||||
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
@ -53,13 +49,10 @@ 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.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.DEPOSIT_CONFIRMED;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.FIAT_SENT;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.PAYOUT_PUBLISHED;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.State.*;
|
||||
import static java.lang.String.format;
|
||||
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.*;
|
||||
import static protobuf.Offer.State.OFFER_FEE_PAID;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
@ -110,17 +103,15 @@ public class TakeBuyBTCOfferWithNationalBankAcctTest 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
|
||||
sleep(3_000); // TODO loop instead of hard code wait time
|
||||
var alicesOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), BRL);
|
||||
assertEquals(1, alicesOffers.size());
|
||||
|
||||
|
||||
var trade = takeAlicesOffer(offerId, bobsPaymentAccount.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();
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsPaymentAccount.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
false);
|
||||
|
||||
// Before generating a blk and confirming deposit tx, make sure there
|
||||
// are no bank acct details in the either side's contract.
|
||||
@ -148,36 +139,13 @@ public class TakeBuyBTCOfferWithNationalBankAcctTest extends AbstractTradeTest {
|
||||
genBtcBlocksThenWait(1, 4000);
|
||||
alicesOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), BRL);
|
||||
assertEquals(0, alicesOffers.size());
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId());
|
||||
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
|
||||
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);
|
||||
} 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, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -203,53 +171,14 @@ public class TakeBuyBTCOfferWithNationalBankAcctTest extends AbstractTradeTest {
|
||||
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 confirm payment started yet.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase());
|
||||
sleep(1000 * 10);
|
||||
trade = aliceClient.getTrade(tradeId);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tradeStateAndPhaseCorrect.test(trade)) {
|
||||
fail(format("INVALID_PHASE for Alice's trade %s in STATE=%s PHASE=%s, could not confirm payment started.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
|
||||
waitForDepositConfirmation(log, testInfo, aliceClient, trade.getTradeId());
|
||||
aliceClient.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 {} BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG, attempt # {}",
|
||||
trade.getShortId(),
|
||||
i);
|
||||
sleep(5000);
|
||||
} else {
|
||||
assertEquals(OFFER_FEE_PAID.name(), trade.getOffer().getState());
|
||||
EXPECTED_PROTOCOL_STATUS.setState(BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG)
|
||||
.setPhase(FIAT_SENT)
|
||||
.setFiatSent(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Alice's view after confirming fiat payment sent", trade);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sleep(6_000);
|
||||
waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);
|
||||
trade = aliceClient.getTrade(tradeId);
|
||||
assertEquals(OFFER_FEE_PAID.name(), trade.getOffer().getState());
|
||||
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);
|
||||
}
|
||||
@ -259,35 +188,10 @@ public class TakeBuyBTCOfferWithNationalBankAcctTest extends AbstractTradeTest {
|
||||
@Order(4)
|
||||
public void testBobsConfirmPaymentReceived(final TestInfo testInfo) {
|
||||
try {
|
||||
waitForSellerSeesPaymentInitiatedMessage(log, testInfo, bobClient, tradeId);
|
||||
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);
|
||||
} 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()));
|
||||
}
|
||||
|
||||
bobClient.confirmPaymentReceived(trade.getTradeId());
|
||||
sleep(3000);
|
||||
|
||||
sleep(3_000);
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
// Note: offer.state == available
|
||||
assertEquals(AVAILABLE.name(), trade.getOffer().getState());
|
||||
@ -297,7 +201,6 @@ public class TakeBuyBTCOfferWithNationalBankAcctTest extends AbstractTradeTest {
|
||||
.setFiatReceived(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Bob's view after confirming fiat payment received", trade);
|
||||
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
@ -305,34 +208,22 @@ public class TakeBuyBTCOfferWithNationalBankAcctTest extends AbstractTradeTest {
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void testAlicesKeepFunds(final TestInfo testInfo) {
|
||||
public void testKeepFunds(final TestInfo testInfo) {
|
||||
try {
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
genBtcBlocksThenWait(1, 1_000);
|
||||
|
||||
var trade = aliceClient.getTrade(tradeId);
|
||||
logTrade(log, testInfo, "Alice's view before keeping funds", trade);
|
||||
|
||||
aliceClient.keepFunds(tradeId);
|
||||
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
|
||||
bobClient.keepFunds(tradeId);
|
||||
genBtcBlocksThenWait(1, 1_000);
|
||||
trade = aliceClient.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));
|
||||
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);
|
||||
}
|
||||
|
@ -17,12 +17,8 @@
|
||||
|
||||
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;
|
||||
@ -35,21 +31,13 @@ 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.cli.table.builder.TableType.OFFER_TBL;
|
||||
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.DEPOSIT_CONFIRMED;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.FIAT_SENT;
|
||||
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.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG;
|
||||
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 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.OfferDirection.BUY;
|
||||
@ -57,6 +45,7 @@ import static protobuf.OfferDirection.BUY;
|
||||
|
||||
|
||||
import bisq.apitest.method.offer.AbstractOfferTest;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@Disabled
|
||||
@Slf4j
|
||||
@ -73,7 +62,6 @@ public class TakeSellBSQOfferTest extends AbstractTradeTest {
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
AbstractOfferTest.setUp();
|
||||
createBsqPaymentAccounts();
|
||||
EXPECTED_PROTOCOL_STATUS.init();
|
||||
}
|
||||
|
||||
@ -91,60 +79,27 @@ public class TakeSellBSQOfferTest extends AbstractTradeTest {
|
||||
7_500_000L,
|
||||
"0.000035", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesBsqAcct.getId(),
|
||||
alicesLegacyBsqAcct.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE);
|
||||
log.info("ALICE'S SELL BSQ (BUY BTC) OFFER:\n{}", formatOfferTable(singletonList(alicesOffer), BSQ));
|
||||
genBtcBlocksThenWait(1, 4000);
|
||||
log.debug("ALICE'S SELL BSQ (BUY BTC) OFFER:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build());
|
||||
genBtcBlocksThenWait(1, 4_000);
|
||||
var offerId = alicesOffer.getId();
|
||||
assertTrue(alicesOffer.getIsCurrencyForMakerFeeBtc());
|
||||
|
||||
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();
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsLegacyBsqAcct.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
false);
|
||||
sleep(2_500); // Allow available offer to be removed from offer book.
|
||||
alicesBsqOffers = aliceClient.getMyCryptoCurrencyOffersSortedByDate(BSQ);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
@ -155,60 +110,14 @@ public class TakeSellBSQOfferTest extends AbstractTradeTest {
|
||||
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()));
|
||||
}
|
||||
|
||||
waitForDepositConfirmation(log, testInfo, aliceClient, trade.getTradeId());
|
||||
sendBsqPayment(log, aliceClient, trade);
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
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);
|
||||
|
||||
sleep(6_000);
|
||||
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);
|
||||
}
|
||||
@ -218,39 +127,13 @@ public class TakeSellBSQOfferTest extends AbstractTradeTest {
|
||||
@Order(3)
|
||||
public void testBobsConfirmPaymentReceived(final TestInfo testInfo) {
|
||||
try {
|
||||
waitForSellerSeesPaymentInitiatedMessage(log, testInfo, bobClient, tradeId);
|
||||
|
||||
sleep(2_000);
|
||||
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);
|
||||
|
||||
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)
|
||||
@ -258,11 +141,8 @@ public class TakeSellBSQOfferTest extends AbstractTradeTest {
|
||||
.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);
|
||||
|
||||
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);
|
||||
}
|
||||
@ -272,36 +152,23 @@ public class TakeSellBSQOfferTest extends AbstractTradeTest {
|
||||
@Order(4)
|
||||
public void testAlicesBtcWithdrawalToExternalAddress(final TestInfo testInfo) {
|
||||
try {
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
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);
|
||||
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
|
||||
// Bob keeps funds.
|
||||
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 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));
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -19,12 +19,8 @@ package bisq.apitest.method.trade;
|
||||
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
@ -35,21 +31,17 @@ import org.junit.jupiter.api.TestInfo;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.cli.TableFormat.formatBalancesTbls;
|
||||
import static bisq.apitest.config.ApiTestConfig.USD;
|
||||
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.DEPOSIT_CONFIRMED;
|
||||
import static bisq.core.trade.model.bisq_v1.Trade.Phase.FIAT_SENT;
|
||||
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.*;
|
||||
import static java.lang.String.format;
|
||||
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.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.OfferDirection.SELL;
|
||||
import static protobuf.OpenOffer.State.AVAILABLE;
|
||||
|
||||
@Disabled
|
||||
@Slf4j
|
||||
@ -69,7 +61,7 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
|
||||
try {
|
||||
PaymentAccount alicesUsdAccount = createDummyF2FAccount(aliceClient, "US");
|
||||
var alicesOffer = aliceClient.createMarketBasedPricedOffer(SELL.name(),
|
||||
"usd",
|
||||
USD,
|
||||
12_500_000L,
|
||||
12_500_000L, // min-amount = amount
|
||||
0.00,
|
||||
@ -81,54 +73,26 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
|
||||
assertTrue(alicesOffer.getIsCurrencyForMakerFeeBtc());
|
||||
|
||||
// Wait for Alice's AddToOfferBook task.
|
||||
// Wait times vary; my logs show >= 2 second delay, but taking sell offers
|
||||
// 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.name(), "usd");
|
||||
sleep(3_000); // TODO loop instead of hard code a wait time
|
||||
var alicesUsdOffers = aliceClient.getMyOffersSortedByDate(SELL.name(), USD);
|
||||
assertEquals(1, alicesUsdOffers.size());
|
||||
|
||||
PaymentAccount bobsUsdAccount = createDummyF2FAccount(bobClient, "US");
|
||||
var trade = takeAlicesOffer(offerId, bobsUsdAccount.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, 4000);
|
||||
var takeableUsdOffers = bobClient.getOffersSortedByDate(SELL.name(), "usd");
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsUsdAccount.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
false);
|
||||
sleep(2_500); // Allow available offer to be removed from offer book.
|
||||
var takeableUsdOffers = bobClient.getOffersSortedByDate(SELL.name(), USD);
|
||||
assertEquals(0, takeableUsdOffers.size());
|
||||
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
|
||||
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 deposit is confirmed", trade, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -139,54 +103,10 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
|
||||
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 confirm payment started yet.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase());
|
||||
// fail("Bad trade state and phase.");
|
||||
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, could not confirm payment started.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
|
||||
verifyTakerDepositConfirmed(trade);
|
||||
bobClient.confirmPaymentStarted(tradeId);
|
||||
sleep(6000);
|
||||
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
|
||||
if (!trade.getIsFiatSent()) {
|
||||
log.warn("Bob still waiting for trade {} BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG, attempt # {}",
|
||||
trade.getShortId(),
|
||||
i);
|
||||
sleep(5000);
|
||||
continue;
|
||||
} else {
|
||||
// Note: offer.state == available
|
||||
assertEquals(AVAILABLE.name(), trade.getOffer().getState());
|
||||
EXPECTED_PROTOCOL_STATUS.setState(BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG)
|
||||
.setPhase(FIAT_SENT)
|
||||
.setFiatSent(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Bob's view after confirming fiat payment sent", trade);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sleep(6_000);
|
||||
waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, bobClient, tradeId);
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
@ -196,36 +116,11 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
|
||||
@Order(3)
|
||||
public void testAlicesConfirmPaymentReceived(final TestInfo testInfo) {
|
||||
try {
|
||||
waitForSellerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);
|
||||
|
||||
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());
|
||||
// fail("Bad trade state and phase.");
|
||||
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, could not confirm payment received.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
|
||||
aliceClient.confirmPaymentReceived(trade.getTradeId());
|
||||
sleep(3000);
|
||||
|
||||
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)
|
||||
@ -243,34 +138,22 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
|
||||
@Order(4)
|
||||
public void testBobsBtcWithdrawalToExternalAddress(final TestInfo testInfo) {
|
||||
try {
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
genBtcBlocksThenWait(1, 1_000);
|
||||
|
||||
var trade = bobClient.getTrade(tradeId);
|
||||
logTrade(log, testInfo, "Bob's view before withdrawing funds to external wallet", trade);
|
||||
|
||||
String toAddress = bitcoinCli.getNewBtcAddress();
|
||||
bobClient.withdrawFunds(tradeId, toAddress, WITHDRAWAL_TX_MEMO);
|
||||
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
|
||||
aliceClient.keepFunds(tradeId);
|
||||
genBtcBlocksThenWait(1, 1_000);
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
EXPECTED_PROTOCOL_STATUS.setState(WITHDRAW_COMPLETED)
|
||||
.setPhase(WITHDRAWN)
|
||||
.setWithdrawn(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
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),
|
||||
formatBalancesTbls(alicesBalances));
|
||||
var bobsBalances = bobClient.getBalances();
|
||||
log.info("{} Bob's Current Balance:\n{}",
|
||||
testName(testInfo),
|
||||
formatBalancesTbls(bobsBalances));
|
||||
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);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import static bisq.apitest.method.wallet.WalletTestUtil.ALICES_INITIAL_BSQ_BALAN
|
||||
import static bisq.apitest.method.wallet.WalletTestUtil.BOBS_INITIAL_BSQ_BALANCES;
|
||||
import static bisq.apitest.method.wallet.WalletTestUtil.bsqBalanceModel;
|
||||
import static bisq.apitest.method.wallet.WalletTestUtil.verifyBsqBalances;
|
||||
import static bisq.cli.TableFormat.formatBsqBalanceInfoTbl;
|
||||
import static bisq.cli.table.builder.TableType.BSQ_BALANCE_TBL;
|
||||
import static org.bitcoinj.core.NetworkParameters.ID_REGTEST;
|
||||
import static org.bitcoinj.core.NetworkParameters.PAYMENT_PROTOCOL_ID_REGTEST;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@ -36,6 +36,7 @@ import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import bisq.apitest.config.BisqAppConfig;
|
||||
import bisq.apitest.method.MethodTest;
|
||||
import bisq.cli.GrpcClient;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@Disabled
|
||||
@Slf4j
|
||||
@ -55,6 +56,7 @@ public class BsqWalletTest extends MethodTest {
|
||||
bobdaemon);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testGetUnusedBsqAddress() {
|
||||
@ -73,13 +75,13 @@ public class BsqWalletTest extends MethodTest {
|
||||
BsqBalanceInfo alicesBsqBalances = aliceClient.getBsqBalances();
|
||||
log.debug("{} -> Alice's BSQ Initial Balances -> \n{}",
|
||||
testName(testInfo),
|
||||
formatBsqBalanceInfoTbl(alicesBsqBalances));
|
||||
new TableBuilder(BSQ_BALANCE_TBL, alicesBsqBalances).build());
|
||||
verifyBsqBalances(ALICES_INITIAL_BSQ_BALANCES, alicesBsqBalances);
|
||||
|
||||
BsqBalanceInfo bobsBsqBalances = bobClient.getBsqBalances();
|
||||
log.debug("{} -> Bob's BSQ Initial Balances -> \n{}",
|
||||
testName(testInfo),
|
||||
formatBsqBalanceInfoTbl(bobsBsqBalances));
|
||||
new TableBuilder(BSQ_BALANCE_TBL, bobsBsqBalances).build());
|
||||
verifyBsqBalances(BOBS_INITIAL_BSQ_BALANCES, bobsBsqBalances);
|
||||
}
|
||||
|
||||
@ -100,19 +102,19 @@ public class BsqWalletTest extends MethodTest {
|
||||
alicedaemon);
|
||||
|
||||
verifyBsqBalances(bsqBalanceModel(150000000,
|
||||
2500050,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0),
|
||||
2500050,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0),
|
||||
bobsBsqBalances);
|
||||
|
||||
verifyBsqBalances(bsqBalanceModel(97499950,
|
||||
97499950,
|
||||
97499950,
|
||||
0,
|
||||
0,
|
||||
0),
|
||||
97499950,
|
||||
97499950,
|
||||
0,
|
||||
0,
|
||||
0),
|
||||
alicesBsqBalances);
|
||||
}
|
||||
|
||||
@ -124,7 +126,7 @@ public class BsqWalletTest extends MethodTest {
|
||||
genBtcBlocksThenWait(1, 4000);
|
||||
|
||||
BsqBalanceInfo alicesBsqBalances = aliceClient.getBalances().getBsq();
|
||||
BsqBalanceInfo bobsBsqBalances = waitForBsqNewAvailableBalance(bobClient, 150000000);
|
||||
BsqBalanceInfo bobsBsqBalances = waitForBsqNewAvailableConfirmedBalance(bobClient, 150000000);
|
||||
|
||||
log.debug("See Available Confirmed BSQ Balances...");
|
||||
printBobAndAliceBsqBalances(testInfo,
|
||||
@ -133,19 +135,19 @@ public class BsqWalletTest extends MethodTest {
|
||||
alicedaemon);
|
||||
|
||||
verifyBsqBalances(bsqBalanceModel(152500050,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0),
|
||||
bobsBsqBalances);
|
||||
|
||||
verifyBsqBalances(bsqBalanceModel(97499950,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0),
|
||||
alicesBsqBalances);
|
||||
}
|
||||
|
||||
@ -166,8 +168,8 @@ public class BsqWalletTest extends MethodTest {
|
||||
return bsqBalance;
|
||||
}
|
||||
|
||||
private BsqBalanceInfo waitForBsqNewAvailableBalance(GrpcClient grpcClient,
|
||||
long staleBalance) {
|
||||
private BsqBalanceInfo waitForBsqNewAvailableConfirmedBalance(GrpcClient grpcClient,
|
||||
long staleBalance) {
|
||||
BsqBalanceInfo bsqBalance = grpcClient.getBsqBalances();
|
||||
for (int numRequests = 1;
|
||||
numRequests <= 15 && bsqBalance.getAvailableConfirmedBalance() == staleBalance;
|
||||
@ -187,12 +189,12 @@ public class BsqWalletTest extends MethodTest {
|
||||
testName(testInfo),
|
||||
senderApp.equals(bobdaemon) ? "Sending" : "Receiving",
|
||||
SEND_BSQ_AMOUNT,
|
||||
formatBsqBalanceInfoTbl(bobsBsqBalances));
|
||||
new TableBuilder(BSQ_BALANCE_TBL, bobsBsqBalances).build());
|
||||
|
||||
log.debug("{} -> Alice's Balances After {} {} BSQ-> \n{}",
|
||||
testName(testInfo),
|
||||
senderApp.equals(alicedaemon) ? "Sending" : "Receiving",
|
||||
SEND_BSQ_AMOUNT,
|
||||
formatBsqBalanceInfoTbl(alicesBsqBalances));
|
||||
new TableBuilder(BSQ_BALANCE_TBL, alicesBsqBalances).build());
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,8 @@ import static bisq.apitest.config.BisqAppConfig.bobdaemon;
|
||||
import static bisq.apitest.config.BisqAppConfig.seednode;
|
||||
import static bisq.apitest.method.wallet.WalletTestUtil.INITIAL_BTC_BALANCES;
|
||||
import static bisq.apitest.method.wallet.WalletTestUtil.verifyBtcBalances;
|
||||
import static bisq.cli.TableFormat.formatAddressBalanceTbl;
|
||||
import static bisq.cli.TableFormat.formatBtcBalanceInfoTbl;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static bisq.cli.table.builder.TableType.ADDRESS_BALANCE_TBL;
|
||||
import static bisq.cli.table.builder.TableType.BTC_BALANCE_TBL;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@ -30,6 +29,7 @@ import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
|
||||
|
||||
import bisq.apitest.method.MethodTest;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@Disabled
|
||||
@Slf4j
|
||||
@ -54,10 +54,14 @@ public class BtcWalletTest extends MethodTest {
|
||||
// Bob & Alice's regtest Bisq wallets were initialized with 10 BTC.
|
||||
|
||||
BtcBalanceInfo alicesBalances = aliceClient.getBtcBalances();
|
||||
log.debug("{} Alice's BTC Balances:\n{}", testName(testInfo), formatBtcBalanceInfoTbl(alicesBalances));
|
||||
log.debug("{} Alice's BTC Balances:\n{}",
|
||||
testName(testInfo),
|
||||
new TableBuilder(BTC_BALANCE_TBL, alicesBalances).build());
|
||||
|
||||
BtcBalanceInfo bobsBalances = bobClient.getBtcBalances();
|
||||
log.debug("{} Bob's BTC Balances:\n{}", testName(testInfo), formatBtcBalanceInfoTbl(bobsBalances));
|
||||
log.debug("{} Bob's BTC Balances:\n{}",
|
||||
testName(testInfo),
|
||||
new TableBuilder(BTC_BALANCE_TBL, bobsBalances).build());
|
||||
|
||||
assertEquals(INITIAL_BTC_BALANCES.getAvailableBalance(), alicesBalances.getAvailableBalance());
|
||||
assertEquals(INITIAL_BTC_BALANCES.getAvailableBalance(), bobsBalances.getAvailableBalance());
|
||||
@ -76,7 +80,8 @@ public class BtcWalletTest extends MethodTest {
|
||||
|
||||
log.debug("{} -> Alice's Funded Address Balance -> \n{}",
|
||||
testName(testInfo),
|
||||
formatAddressBalanceTbl(singletonList(aliceClient.getAddressBalance(newAddress))));
|
||||
new TableBuilder(ADDRESS_BALANCE_TBL,
|
||||
aliceClient.getAddressBalance(newAddress)));
|
||||
|
||||
// New balance is 12.5 BTC
|
||||
btcBalanceInfo = aliceClient.getBtcBalances();
|
||||
@ -88,7 +93,7 @@ public class BtcWalletTest extends MethodTest {
|
||||
verifyBtcBalances(alicesExpectedBalances, btcBalanceInfo);
|
||||
log.debug("{} -> Alice's BTC Balances After Sending 2.5 BTC -> \n{}",
|
||||
testName(testInfo),
|
||||
formatBtcBalanceInfoTbl(btcBalanceInfo));
|
||||
new TableBuilder(BTC_BALANCE_TBL, btcBalanceInfo).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -115,7 +120,7 @@ public class BtcWalletTest extends MethodTest {
|
||||
BtcBalanceInfo alicesBalances = aliceClient.getBtcBalances();
|
||||
log.debug("{} Alice's BTC Balances:\n{}",
|
||||
testName(testInfo),
|
||||
formatBtcBalanceInfoTbl(alicesBalances));
|
||||
new TableBuilder(BTC_BALANCE_TBL, alicesBalances).build());
|
||||
bisq.core.api.model.BtcBalanceInfo alicesExpectedBalances =
|
||||
bisq.core.api.model.BtcBalanceInfo.valueOf(700000000,
|
||||
0,
|
||||
@ -126,7 +131,7 @@ public class BtcWalletTest extends MethodTest {
|
||||
BtcBalanceInfo bobsBalances = bobClient.getBtcBalances();
|
||||
log.debug("{} Bob's BTC Balances:\n{}",
|
||||
testName(testInfo),
|
||||
formatBtcBalanceInfoTbl(bobsBalances));
|
||||
new TableBuilder(BTC_BALANCE_TBL, bobsBalances).build());
|
||||
// The sendbtc tx weight and size randomly varies between two distinct values
|
||||
// (876 wu, 219 bytes, OR 880 wu, 220 bytes) from test run to test run, hence
|
||||
// the assertion of an available balance range [1549978000, 1549978100].
|
||||
|
@ -43,7 +43,6 @@ public class LongRunningBsqSwapTest extends AbstractOfferTest {
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
AbstractOfferTest.setUp();
|
||||
createBsqSwapBsqPaymentAccounts();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -71,7 +71,7 @@ public class LongRunningTradesTest extends AbstractTradeTest {
|
||||
test.testTakeAlicesBuyOffer(testInfo);
|
||||
test.testAlicesConfirmPaymentStarted(testInfo);
|
||||
test.testBobsConfirmPaymentReceived(testInfo);
|
||||
test.testAlicesKeepFunds(testInfo);
|
||||
test.testKeepFunds(testInfo);
|
||||
}
|
||||
|
||||
public void testTakeSellBTCOffer(final TestInfo testInfo) {
|
||||
|
@ -80,7 +80,6 @@ public class OfferTest extends AbstractOfferTest {
|
||||
@Order(5)
|
||||
public void testCreateBSQOffers() {
|
||||
CreateBSQOffersTest test = new CreateBSQOffersTest();
|
||||
CreateBSQOffersTest.createBsqPaymentAccounts();
|
||||
test.testCreateBuy1BTCFor20KBSQOffer();
|
||||
test.testCreateSell1BTCFor20KBSQOffer();
|
||||
test.testCreateBuyBTCWith1To2KBSQOffer();
|
||||
@ -93,7 +92,6 @@ public class OfferTest extends AbstractOfferTest {
|
||||
@Order(6)
|
||||
public void testCreateBSQSwapOffers() {
|
||||
BsqSwapOfferTest test = new BsqSwapOfferTest();
|
||||
BsqSwapOfferTest.createBsqSwapBsqPaymentAccounts();
|
||||
test.testAliceCreateBsqSwapBuyOffer1();
|
||||
test.testAliceCreateBsqSwapBuyOffer2();
|
||||
test.testAliceCreateBsqSwapBuyOffer3();
|
||||
|
@ -53,7 +53,7 @@ public class TradeTest extends AbstractTradeTest {
|
||||
test.testTakeAlicesBuyOffer(testInfo);
|
||||
test.testAlicesConfirmPaymentStarted(testInfo);
|
||||
test.testBobsConfirmPaymentReceived(testInfo);
|
||||
test.testAlicesKeepFunds(testInfo);
|
||||
test.testKeepFunds(testInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -70,11 +70,10 @@ public class TradeTest extends AbstractTradeTest {
|
||||
@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.testKeepFunds(testInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -85,14 +84,13 @@ public class TradeTest extends AbstractTradeTest {
|
||||
test.testBankAcctDetailsIncludedInContracts(testInfo);
|
||||
test.testAlicesConfirmPaymentStarted(testInfo);
|
||||
test.testBobsConfirmPaymentReceived(testInfo);
|
||||
test.testAlicesKeepFunds(testInfo);
|
||||
test.testKeepFunds(testInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void testTakeSellBSQOffer(final TestInfo testInfo) {
|
||||
TakeSellBSQOfferTest test = new TakeSellBSQOfferTest();
|
||||
TakeSellBSQOfferTest.createBsqPaymentAccounts();
|
||||
test.testTakeAlicesBuyBTCForBSQOffer(testInfo);
|
||||
test.testAlicesConfirmPaymentStarted(testInfo);
|
||||
test.testBobsConfirmPaymentReceived(testInfo);
|
||||
@ -103,7 +101,6 @@ public class TradeTest extends AbstractTradeTest {
|
||||
@Order(6)
|
||||
public void testBsqSwapTradeTest(final TestInfo testInfo) {
|
||||
BsqSwapTradeTest test = new BsqSwapTradeTest();
|
||||
test.createBsqSwapBsqPaymentAccounts();
|
||||
test.testAliceCreateBsqSwapBuyOffer();
|
||||
test.testBobTakesBsqSwapOffer();
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import static bisq.apitest.scenario.bot.protocol.ProtocolStep.DONE;
|
||||
import static bisq.apitest.scenario.bot.protocol.ProtocolStep.WAIT_FOR_OFFER_TAKER;
|
||||
import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.checkIfShutdownCalled;
|
||||
import static bisq.cli.TableFormat.formatOfferTable;
|
||||
import static bisq.cli.OfferFormat.formatOfferTable;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
|
||||
|
@ -17,7 +17,7 @@ import static bisq.apitest.scenario.bot.protocol.ProtocolStep.DONE;
|
||||
import static bisq.apitest.scenario.bot.protocol.ProtocolStep.FIND_OFFER;
|
||||
import static bisq.apitest.scenario.bot.protocol.ProtocolStep.TAKE_OFFER;
|
||||
import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.checkIfShutdownCalled;
|
||||
import static bisq.cli.TableFormat.formatOfferTable;
|
||||
import static bisq.cli.OfferFormat.formatOfferTable;
|
||||
import static bisq.core.payment.payload.PaymentMethod.F2F_ID;
|
||||
|
||||
|
||||
|
@ -34,6 +34,7 @@ configure(subprojects) {
|
||||
bcVersion = '1.63'
|
||||
bitcoinjVersion = '42bbae9'
|
||||
codecVersion = '1.13'
|
||||
cowwocVersion = '1.2'
|
||||
easybindVersion = '1.0.3'
|
||||
easyVersion = '4.0.1'
|
||||
findbugsVersion = '3.0.2'
|
||||
@ -395,6 +396,7 @@ configure(project(':cli')) {
|
||||
testAnnotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||
testCompileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||
testRuntime "javax.annotation:javax.annotation-api:$javaxAnnotationVersion"
|
||||
testImplementation "org.bitbucket.cowwoc:diff-match-patch:$cowwocVersion"
|
||||
}
|
||||
|
||||
test {
|
||||
|
@ -34,6 +34,8 @@ import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@ -41,13 +43,13 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.*;
|
||||
import static bisq.cli.Method.*;
|
||||
import static bisq.cli.TableFormat.*;
|
||||
import static bisq.cli.opts.OptLabel.*;
|
||||
import static bisq.cli.table.builder.TableType.*;
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.err;
|
||||
import static java.lang.System.exit;
|
||||
import static java.lang.System.out;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.math.BigDecimal.ZERO;
|
||||
|
||||
|
||||
|
||||
@ -76,6 +78,7 @@ import bisq.cli.opts.TakeOfferOptionParser;
|
||||
import bisq.cli.opts.UnlockWalletOptionParser;
|
||||
import bisq.cli.opts.VerifyBsqSentToAddressOptionParser;
|
||||
import bisq.cli.opts.WithdrawFundsOptionParser;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
/**
|
||||
* A command-line client for the Bisq gRPC API.
|
||||
@ -167,15 +170,19 @@ public class CliMain {
|
||||
var balances = client.getBalances(currencyCode);
|
||||
switch (currencyCode.toUpperCase()) {
|
||||
case "BSQ":
|
||||
out.println(formatBsqBalanceInfoTbl(balances.getBsq()));
|
||||
new TableBuilder(BSQ_BALANCE_TBL, balances.getBsq()).build().print(out);
|
||||
break;
|
||||
case "BTC":
|
||||
out.println(formatBtcBalanceInfoTbl(balances.getBtc()));
|
||||
new TableBuilder(BTC_BALANCE_TBL, balances.getBtc()).build().print(out);
|
||||
break;
|
||||
case "":
|
||||
default:
|
||||
out.println(formatBalancesTbls(balances));
|
||||
default: {
|
||||
out.println("BTC");
|
||||
new TableBuilder(BTC_BALANCE_TBL, balances.getBtc()).build().print(out);
|
||||
out.println("BSQ");
|
||||
new TableBuilder(BSQ_BALANCE_TBL, balances.getBsq()).build().print(out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -187,7 +194,7 @@ public class CliMain {
|
||||
}
|
||||
var address = opts.getAddress();
|
||||
var addressBalance = client.getAddressBalance(address);
|
||||
out.println(formatAddressBalanceTbl(singletonList(addressBalance)));
|
||||
new TableBuilder(ADDRESS_BALANCE_TBL, addressBalance).build().print(out);
|
||||
return;
|
||||
}
|
||||
case getbtcprice: {
|
||||
@ -207,7 +214,7 @@ public class CliMain {
|
||||
return;
|
||||
}
|
||||
var fundingAddresses = client.getFundingAddresses();
|
||||
out.println(formatAddressBalanceTbl(fundingAddresses));
|
||||
new TableBuilder(ADDRESS_BALANCE_TBL, fundingAddresses).build().print(out);
|
||||
return;
|
||||
}
|
||||
case getunusedbsqaddress: {
|
||||
@ -316,7 +323,7 @@ public class CliMain {
|
||||
}
|
||||
var txId = opts.getTxId();
|
||||
var tx = client.getTransaction(txId);
|
||||
out.println(TransactionFormat.format(tx));
|
||||
new TableBuilder(TRANSACTION_TBL, tx).build().print(out);
|
||||
return;
|
||||
}
|
||||
case createoffer: {
|
||||
@ -347,7 +354,7 @@ public class CliMain {
|
||||
paymentAcctId,
|
||||
makerFeeCurrencyCode,
|
||||
triggerPrice);
|
||||
out.println(formatOfferTable(singletonList(offer), currencyCode));
|
||||
new TableBuilder(OFFER_TBL, offer).build().print(out);
|
||||
return;
|
||||
}
|
||||
case editoffer: {
|
||||
@ -360,7 +367,7 @@ public class CliMain {
|
||||
var fixedPrice = opts.getFixedPrice();
|
||||
var isUsingMktPriceMargin = opts.isUsingMktPriceMargin();
|
||||
var marketPriceMargin = opts.getMktPriceMarginAsBigDecimal();
|
||||
var triggerPrice = toInternalFiatPrice(opts.getTriggerPriceAsBigDecimal());
|
||||
var triggerPrice = toInternalTriggerPrice(client, offerId, opts.getTriggerPriceAsBigDecimal());
|
||||
var enable = opts.getEnableAsSignedInt();
|
||||
var editOfferType = opts.getOfferEditType();
|
||||
client.editOffer(offerId,
|
||||
@ -392,7 +399,7 @@ public class CliMain {
|
||||
}
|
||||
var offerId = opts.getOfferId();
|
||||
var offer = client.getOffer(offerId);
|
||||
out.println(formatOfferTable(singletonList(offer), offer.getCounterCurrencyCode()));
|
||||
new TableBuilder(OFFER_TBL, offer).build().print(out);
|
||||
return;
|
||||
}
|
||||
case getmyoffer: {
|
||||
@ -403,7 +410,7 @@ public class CliMain {
|
||||
}
|
||||
var offerId = opts.getOfferId();
|
||||
var offer = client.getMyOffer(offerId);
|
||||
out.println(formatOfferTable(singletonList(offer), offer.getCounterCurrencyCode()));
|
||||
new TableBuilder(OFFER_TBL, offer).build().print(out);
|
||||
return;
|
||||
}
|
||||
case getoffers: {
|
||||
@ -418,7 +425,7 @@ public class CliMain {
|
||||
if (offers.isEmpty())
|
||||
out.printf("no %s %s offers found%n", direction, currencyCode);
|
||||
else
|
||||
out.println(formatOfferTable(offers, currencyCode));
|
||||
new TableBuilder(OFFER_TBL, offers).build().print(out);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -434,7 +441,7 @@ public class CliMain {
|
||||
if (offers.isEmpty())
|
||||
out.printf("no %s %s offers found%n", direction, currencyCode);
|
||||
else
|
||||
out.println(formatOfferTable(offers, currencyCode));
|
||||
new TableBuilder(OFFER_TBL, offers).build().print(out);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -464,7 +471,7 @@ public class CliMain {
|
||||
if (showContract)
|
||||
out.println(trade.getContractAsJson());
|
||||
else
|
||||
out.println(TradeFormat.format(trade));
|
||||
new TableBuilder(TRADE_DETAIL_TBL, trade).build().print(out);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -556,11 +563,12 @@ public class CliMain {
|
||||
}
|
||||
var paymentAccount = client.createPaymentAccount(jsonString);
|
||||
out.println("payment account saved");
|
||||
out.println(formatPaymentAcctTbl(singletonList(paymentAccount)));
|
||||
new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccount).build().print(out);
|
||||
return;
|
||||
}
|
||||
case createcryptopaymentacct: {
|
||||
var opts = new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse();
|
||||
var opts =
|
||||
new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
@ -574,7 +582,7 @@ public class CliMain {
|
||||
address,
|
||||
isTradeInstant);
|
||||
out.println("payment account saved");
|
||||
out.println(formatPaymentAcctTbl(singletonList(paymentAccount)));
|
||||
new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccount).build().print(out);
|
||||
return;
|
||||
}
|
||||
case getpaymentaccts: {
|
||||
@ -584,7 +592,7 @@ public class CliMain {
|
||||
}
|
||||
var paymentAccounts = client.getPaymentAccounts();
|
||||
if (paymentAccounts.size() > 0)
|
||||
out.println(formatPaymentAcctTbl(paymentAccounts));
|
||||
new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccounts).build().print(out);
|
||||
else
|
||||
out.println("no payment accounts are saved");
|
||||
|
||||
@ -708,6 +716,25 @@ public class CliMain {
|
||||
}
|
||||
}
|
||||
|
||||
private static long toInternalTriggerPrice(GrpcClient client,
|
||||
String offerId,
|
||||
BigDecimal unscaledTriggerPrice) {
|
||||
if (unscaledTriggerPrice.compareTo(ZERO) >= 0) {
|
||||
// Unfortunately, the EditOffer proto triggerPrice field was declared as
|
||||
// a long instead of a string, so the CLI has to look at the offer to know
|
||||
// how to scale the trigger-price (for a fiat or altcoin offer) param sent
|
||||
// to the server in its 'editoffer' request. That means a preliminary round
|
||||
// trip to the server: a 'getmyoffer' request.
|
||||
var offer = client.getMyOffer(offerId);
|
||||
if (offer.getCounterCurrencyCode().equals("BTC"))
|
||||
return toInternalCryptoCurrencyPrice(unscaledTriggerPrice);
|
||||
else
|
||||
return toInternalFiatPrice(unscaledTriggerPrice);
|
||||
} else {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
private static File saveFileToDisk(String prefix,
|
||||
@SuppressWarnings("SameParameterValue") String suffix,
|
||||
String text) {
|
||||
|
@ -20,6 +20,7 @@ package bisq.cli;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
|
||||
@Deprecated
|
||||
class ColumnHeaderConstants {
|
||||
|
||||
// For inserting 2 spaces between column headers.
|
||||
|
@ -20,15 +20,16 @@ package bisq.cli;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class CryptoCurrencyUtil {
|
||||
public class CryptoCurrencyUtil {
|
||||
|
||||
public static boolean isSupportedCryptoCurrency(String currencyCode) {
|
||||
public static boolean apiDoesSupportCryptoCurrency(String currencyCode) {
|
||||
return getSupportedCryptoCurrencies().contains(currencyCode.toUpperCase());
|
||||
}
|
||||
|
||||
public static List<String> getSupportedCryptoCurrencies() {
|
||||
final List<String> result = new ArrayList<>();
|
||||
result.add("BSQ");
|
||||
// result.add("XMR"); // TODO Uncomment when XMR support is added.
|
||||
result.sort(String::compareTo);
|
||||
return result;
|
||||
}
|
||||
|
@ -39,14 +39,15 @@ public class CurrencyFormat {
|
||||
// Use the US locale for all DecimalFormat objects.
|
||||
private static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = DecimalFormatSymbols.getInstance(Locale.US);
|
||||
|
||||
// Formats numbers in US locale, human friendly style.
|
||||
// Format numbers in US locale for CLI console.
|
||||
private static final NumberFormat FRIENDLY_NUMBER_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
|
||||
// Formats numbers for internal use, i.e., grpc request parameters.
|
||||
private static final DecimalFormat INTERNAL_FIAT_DECIMAL_FORMAT = new DecimalFormat("##############0.0000");
|
||||
|
||||
static final BigDecimal SATOSHI_DIVISOR = new BigDecimal(100_000_000);
|
||||
static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.00000000", DECIMAL_FORMAT_SYMBOLS);
|
||||
static final DecimalFormat SATOSHI_FORMAT = new DecimalFormat("###,##0.00000000", DECIMAL_FORMAT_SYMBOLS);
|
||||
static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.########", DECIMAL_FORMAT_SYMBOLS);
|
||||
static final DecimalFormat BTC_TX_FEE_FORMAT = new DecimalFormat("###,###,##0", DECIMAL_FORMAT_SYMBOLS);
|
||||
|
||||
static final BigDecimal BSQ_SATOSHI_DIVISOR = new BigDecimal(100);
|
||||
@ -57,6 +58,11 @@ public class CurrencyFormat {
|
||||
|
||||
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
|
||||
public static String formatSatoshis(long sats) {
|
||||
return SATOSHI_FORMAT.format(BigDecimal.valueOf(sats).divide(SATOSHI_DIVISOR));
|
||||
}
|
||||
|
||||
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
|
||||
public static String formatBtc(long sats) {
|
||||
return BTC_FORMAT.format(BigDecimal.valueOf(sats).divide(SATOSHI_DIVISOR));
|
||||
}
|
||||
|
||||
@ -84,22 +90,25 @@ public class CurrencyFormat {
|
||||
formatFeeSatoshis(txFeeRateInfo.getMinFeeServiceRate()));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String formatAmountRange(long minAmount, long amount) {
|
||||
return minAmount != amount
|
||||
? formatSatoshis(minAmount) + " - " + formatSatoshis(amount)
|
||||
: formatSatoshis(amount);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String formatVolumeRange(long minVolume, long volume) {
|
||||
return minVolume != volume
|
||||
? formatOfferVolume(minVolume) + " - " + formatOfferVolume(volume)
|
||||
: formatOfferVolume(volume);
|
||||
? formatFiatVolume(minVolume) + " - " + formatFiatVolume(volume)
|
||||
: formatFiatVolume(volume);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String formatCryptoCurrencyVolumeRange(long minVolume, long volume) {
|
||||
return minVolume != volume
|
||||
? formatCryptoCurrencyOfferVolume(minVolume) + " - " + formatCryptoCurrencyOfferVolume(volume)
|
||||
: formatCryptoCurrencyOfferVolume(volume);
|
||||
? formatCryptoCurrencyVolume(minVolume) + " - " + formatCryptoCurrencyVolume(volume)
|
||||
: formatCryptoCurrencyVolume(volume);
|
||||
}
|
||||
|
||||
public static String formatInternalFiatPrice(BigDecimal price) {
|
||||
@ -128,22 +137,31 @@ public class CurrencyFormat {
|
||||
return FRIENDLY_NUMBER_FORMAT.format((double) price / SATOSHI_DIVISOR.doubleValue());
|
||||
}
|
||||
|
||||
public static String formatOfferVolume(long volume) {
|
||||
public static String formatFiatVolume(long volume) {
|
||||
FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(0);
|
||||
FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(0);
|
||||
FRIENDLY_NUMBER_FORMAT.setRoundingMode(HALF_UP);
|
||||
return FRIENDLY_NUMBER_FORMAT.format((double) volume / 10_000);
|
||||
}
|
||||
|
||||
public static String formatCryptoCurrencyOfferVolume(long volume) {
|
||||
FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(2);
|
||||
FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(2);
|
||||
public static String formatCryptoCurrencyVolume(long volume) {
|
||||
int defaultPrecision = 2;
|
||||
return formatCryptoCurrencyVolume(volume, defaultPrecision);
|
||||
}
|
||||
|
||||
public static String formatCryptoCurrencyVolume(long volume, int precision) {
|
||||
FRIENDLY_NUMBER_FORMAT.setMinimumFractionDigits(precision);
|
||||
FRIENDLY_NUMBER_FORMAT.setMaximumFractionDigits(precision);
|
||||
FRIENDLY_NUMBER_FORMAT.setRoundingMode(HALF_UP);
|
||||
return FRIENDLY_NUMBER_FORMAT.format((double) volume / SATOSHI_DIVISOR.doubleValue());
|
||||
}
|
||||
|
||||
public static long toInternalFiatPrice(BigDecimal humanFriendlyFiatPrice) {
|
||||
return humanFriendlyFiatPrice.multiply(new BigDecimal(10_000)).longValue();
|
||||
public static long toInternalFiatPrice(BigDecimal fiatPrice) {
|
||||
return fiatPrice.multiply(new BigDecimal(10_000)).longValue();
|
||||
}
|
||||
|
||||
public static long toInternalCryptoCurrencyPrice(BigDecimal altcoinPrice) {
|
||||
return altcoinPrice.multiply(new BigDecimal(100_000_000)).longValue();
|
||||
}
|
||||
|
||||
public static long toSatoshis(String btc) {
|
||||
|
@ -27,6 +27,7 @@ import static java.lang.String.format;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
import static protobuf.OfferDirection.SELL;
|
||||
|
||||
@Deprecated
|
||||
class DirectionFormat {
|
||||
|
||||
static int getLongestDirectionColWidth(List<OfferInfo> offers) {
|
||||
|
@ -327,6 +327,10 @@ public final class GrpcClient {
|
||||
return offersServiceRequest.getMyOffersSortedByDate(currencyCode);
|
||||
}
|
||||
|
||||
public List<OfferInfo> getMyCryptoCurrencyOffersSortedByDate(String currencyCode) {
|
||||
return offersServiceRequest.getMyCryptoCurrencyOffersSortedByDate(currencyCode);
|
||||
}
|
||||
|
||||
public List<OfferInfo> getMyBsqOffersSortedByDate() {
|
||||
return offersServiceRequest.getMyBsqOffersSortedByDate();
|
||||
}
|
||||
@ -414,6 +418,10 @@ public final class GrpcClient {
|
||||
return paymentAccountsServiceRequest.getPaymentAccounts();
|
||||
}
|
||||
|
||||
public PaymentAccount getPaymentAccount(String accountName) {
|
||||
return paymentAccountsServiceRequest.getPaymentAccount(accountName);
|
||||
}
|
||||
|
||||
public PaymentAccount createCryptoCurrencyPaymentAccount(String accountName,
|
||||
String currencyCode,
|
||||
String address,
|
||||
|
309
cli/src/main/java/bisq/cli/OfferFormat.java
Normal file
309
cli/src/main/java/bisq/cli/OfferFormat.java
Normal file
@ -0,0 +1,309 @@
|
||||
package bisq.cli;
|
||||
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.cli.ColumnHeaderConstants.*;
|
||||
import static bisq.cli.CurrencyFormat.*;
|
||||
import static bisq.cli.DirectionFormat.directionFormat;
|
||||
import static bisq.cli.DirectionFormat.getLongestDirectionColWidth;
|
||||
import static bisq.cli.TableFormat.formatTimestamp;
|
||||
import static bisq.cli.TableFormat.getLongestColumnSize;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
import static java.lang.String.format;
|
||||
|
||||
@Deprecated
|
||||
@VisibleForTesting
|
||||
public class OfferFormat {
|
||||
|
||||
public static String formatOfferTable(List<OfferInfo> offers, String currencyCode) {
|
||||
if (offers == null || offers.isEmpty())
|
||||
throw new IllegalArgumentException(format("%s offer list is empty", currencyCode.toLowerCase()));
|
||||
|
||||
String baseCurrencyCode = offers.get(0).getBaseCurrencyCode();
|
||||
boolean isMyOffer = offers.get(0).getIsMyOffer();
|
||||
return baseCurrencyCode.equalsIgnoreCase("BTC")
|
||||
? formatFiatOfferTable(offers, currencyCode, isMyOffer)
|
||||
: formatCryptoCurrencyOfferTable(offers, baseCurrencyCode, isMyOffer);
|
||||
}
|
||||
|
||||
private static String formatFiatOfferTable(List<OfferInfo> offers,
|
||||
String fiatCurrencyCode,
|
||||
boolean isMyOffer) {
|
||||
// Some column values might be longer than header, so we need to calculate them.
|
||||
int amountColWith = getLongestAmountColWidth(offers);
|
||||
int volumeColWidth = getLongestVolumeColWidth(offers);
|
||||
int paymentMethodColWidth = getLongestPaymentMethodColWidth(offers);
|
||||
// "Enabled" and "Trigger Price" columns are displayed for my offers only.
|
||||
String enabledHeaderFormat = isMyOffer ?
|
||||
COL_HEADER_ENABLED + COL_HEADER_DELIMITER
|
||||
: "";
|
||||
String triggerPriceHeaderFormat = isMyOffer ?
|
||||
// COL_HEADER_TRIGGER_PRICE includes %s -> fiatCurrencyCode
|
||||
COL_HEADER_TRIGGER_PRICE + COL_HEADER_DELIMITER
|
||||
: "";
|
||||
String headersFormat = enabledHeaderFormat
|
||||
+ COL_HEADER_DIRECTION + COL_HEADER_DELIMITER
|
||||
// COL_HEADER_PRICE includes %s -> fiatCurrencyCode
|
||||
+ COL_HEADER_PRICE + COL_HEADER_DELIMITER
|
||||
+ padStart(COL_HEADER_AMOUNT, amountColWith, ' ') + COL_HEADER_DELIMITER
|
||||
// COL_HEADER_VOLUME includes %s -> fiatCurrencyCode
|
||||
+ padStart(COL_HEADER_VOLUME, volumeColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ triggerPriceHeaderFormat
|
||||
+ padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_CREATION_DATE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_UUID.trim() + "%n";
|
||||
String headerLine = format(headersFormat,
|
||||
fiatCurrencyCode.toUpperCase(),
|
||||
fiatCurrencyCode.toUpperCase(),
|
||||
// COL_HEADER_TRIGGER_PRICE includes %s -> fiatCurrencyCode
|
||||
isMyOffer ? fiatCurrencyCode.toUpperCase() : "");
|
||||
String colDataFormat = getFiatOfferColDataFormat(isMyOffer,
|
||||
amountColWith,
|
||||
volumeColWidth,
|
||||
paymentMethodColWidth);
|
||||
return formattedFiatOfferTable(offers, isMyOffer, headerLine, colDataFormat);
|
||||
}
|
||||
|
||||
private static String formattedFiatOfferTable(List<OfferInfo> offers,
|
||||
boolean isMyOffer,
|
||||
String headerLine,
|
||||
String colDataFormat) {
|
||||
if (isMyOffer) {
|
||||
return headerLine
|
||||
+ offers.stream()
|
||||
.map(o -> format(colDataFormat,
|
||||
formatEnabled(o),
|
||||
o.getDirection(),
|
||||
formatPrice(o.getPrice()),
|
||||
formatAmountRange(o.getMinAmount(), o.getAmount()),
|
||||
formatVolumeRange(o.getMinVolume(), o.getVolume()),
|
||||
o.getTriggerPrice() == 0 ? "" : formatPrice(o.getTriggerPrice()),
|
||||
o.getPaymentMethodShortName(),
|
||||
formatTimestamp(o.getDate()),
|
||||
o.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
} else {
|
||||
return headerLine
|
||||
+ offers.stream()
|
||||
.map(o -> format(colDataFormat,
|
||||
o.getDirection(),
|
||||
formatPrice(o.getPrice()),
|
||||
formatAmountRange(o.getMinAmount(), o.getAmount()),
|
||||
formatVolumeRange(o.getMinVolume(), o.getVolume()),
|
||||
o.getPaymentMethodShortName(),
|
||||
formatTimestamp(o.getDate()),
|
||||
o.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFiatOfferColDataFormat(boolean isMyOffer,
|
||||
int amountColWith,
|
||||
int volumeColWidth,
|
||||
int paymentMethodColWidth) {
|
||||
if (isMyOffer) {
|
||||
return "%-" + (COL_HEADER_ENABLED.length() + COL_HEADER_DELIMITER.length()) + "s"
|
||||
+ "%-" + (COL_HEADER_DIRECTION.length() + COL_HEADER_DELIMITER.length()) + "s"
|
||||
+ "%" + (COL_HEADER_PRICE.length() - 1) + "s"
|
||||
+ " %" + amountColWith + "s"
|
||||
+ " %" + (volumeColWidth - 1) + "s"
|
||||
+ " %" + (COL_HEADER_TRIGGER_PRICE.length() - 1) + "s"
|
||||
+ " %-" + paymentMethodColWidth + "s"
|
||||
+ " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s";
|
||||
} else {
|
||||
return "%-" + (COL_HEADER_DIRECTION.length() + COL_HEADER_DELIMITER.length()) + "s"
|
||||
+ "%" + (COL_HEADER_PRICE.length() - 1) + "s"
|
||||
+ " %" + amountColWith + "s"
|
||||
+ " %" + (volumeColWidth - 1) + "s"
|
||||
+ " %-" + paymentMethodColWidth + "s"
|
||||
+ " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s";
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatCryptoCurrencyOfferTable(List<OfferInfo> offers,
|
||||
String cryptoCurrencyCode,
|
||||
boolean isMyOffer) {
|
||||
// Some column values might be longer than header, so we need to calculate them.
|
||||
int directionColWidth = getLongestDirectionColWidth(offers);
|
||||
int amountColWith = getLongestAmountColWidth(offers);
|
||||
int volumeColWidth = getLongestCryptoCurrencyVolumeColWidth(offers);
|
||||
int paymentMethodColWidth = getLongestPaymentMethodColWidth(offers);
|
||||
// "Enabled" column is displayed for my offers only.
|
||||
String enabledHeaderFormat = isMyOffer ?
|
||||
COL_HEADER_ENABLED + COL_HEADER_DELIMITER
|
||||
: "";
|
||||
Supplier<Boolean> shouldShowTriggerPrice = () -> isMyOffer && !cryptoCurrencyCode.equalsIgnoreCase("BSQ");
|
||||
String triggerPriceHeaderFormat = shouldShowTriggerPrice.get()
|
||||
? COL_HEADER_TRIGGER_PRICE + COL_HEADER_DELIMITER
|
||||
: "";
|
||||
// TODO use memoize function to avoid duplicate the formatting done above?
|
||||
String headersFormat = enabledHeaderFormat
|
||||
+ padEnd(COL_HEADER_DIRECTION, directionColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_PRICE_OF_ALTCOIN + COL_HEADER_DELIMITER // includes %s -> cryptoCurrencyCode
|
||||
+ padStart(COL_HEADER_AMOUNT, amountColWith, ' ') + COL_HEADER_DELIMITER
|
||||
// COL_HEADER_VOLUME includes %s -> cryptoCurrencyCode
|
||||
+ padStart(COL_HEADER_VOLUME, volumeColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ triggerPriceHeaderFormat // COL_HEADER_TRIGGER_PRICE includes %s -> cryptoCurrencyCode
|
||||
+ padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_CREATION_DATE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_UUID.trim() + "%n";
|
||||
String headerLine = format(headersFormat,
|
||||
cryptoCurrencyCode.toUpperCase(),
|
||||
cryptoCurrencyCode.toUpperCase(),
|
||||
shouldShowTriggerPrice.get() ? cryptoCurrencyCode.toUpperCase() : "");
|
||||
String colDataFormat = getCryptoCurrencyOfferColDataFormat(isMyOffer,
|
||||
shouldShowTriggerPrice.get(),
|
||||
directionColWidth,
|
||||
amountColWith,
|
||||
volumeColWidth,
|
||||
paymentMethodColWidth);
|
||||
if (isMyOffer) {
|
||||
if (shouldShowTriggerPrice.get()) {
|
||||
// Is my non-BSQ altcoin offer. Show ENABLED and TRIGGER_PRICE data.
|
||||
return headerLine
|
||||
+ offers.stream()
|
||||
.map(o -> format(colDataFormat,
|
||||
formatEnabled(o),
|
||||
directionFormat.apply(o),
|
||||
formatCryptoCurrencyPrice(o.getPrice()),
|
||||
formatAmountRange(o.getMinAmount(), o.getAmount()),
|
||||
formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()),
|
||||
o.getTriggerPrice() == 0 ? "" : formatCryptoCurrencyPrice(o.getTriggerPrice()),
|
||||
o.getPaymentMethodShortName(),
|
||||
formatTimestamp(o.getDate()),
|
||||
o.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
} else {
|
||||
// Is my BSQ altcoin offer. Show ENABLED, but not TRIGGER_PRICE data.
|
||||
return headerLine
|
||||
+ offers.stream()
|
||||
.map(o -> format(colDataFormat,
|
||||
formatEnabled(o),
|
||||
directionFormat.apply(o),
|
||||
formatCryptoCurrencyPrice(o.getPrice()),
|
||||
formatAmountRange(o.getMinAmount(), o.getAmount()),
|
||||
formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()),
|
||||
o.getPaymentMethodShortName(),
|
||||
formatTimestamp(o.getDate()),
|
||||
o.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
} else {
|
||||
// Not my offer. Do not show ENABLED and TRIGGER_PRICE cols.
|
||||
return headerLine
|
||||
+ offers.stream()
|
||||
.map(o -> format(colDataFormat,
|
||||
directionFormat.apply(o),
|
||||
formatCryptoCurrencyPrice(o.getPrice()),
|
||||
formatAmountRange(o.getMinAmount(), o.getAmount()),
|
||||
formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()),
|
||||
o.getPaymentMethodShortName(),
|
||||
formatTimestamp(o.getDate()),
|
||||
o.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
private static String getCryptoCurrencyOfferColDataFormat(boolean isMyOffer,
|
||||
boolean shouldShowTriggerPrice,
|
||||
int directionColWidth,
|
||||
int amountColWith,
|
||||
int volumeColWidth,
|
||||
int paymentMethodColWidth) {
|
||||
if (isMyOffer) {
|
||||
return getMyCryptoOfferColDataFormat(shouldShowTriggerPrice,
|
||||
directionColWidth,
|
||||
amountColWith,
|
||||
volumeColWidth,
|
||||
paymentMethodColWidth);
|
||||
} else {
|
||||
// Not my offer. Do not show ENABLED and TRIGGER_PRICE cols.
|
||||
return "%-" + directionColWidth + "s"
|
||||
+ "%" + (COL_HEADER_PRICE_OF_ALTCOIN.length() + 1) + "s"
|
||||
+ " %" + amountColWith + "s"
|
||||
+ " %" + (volumeColWidth - 1) + "s"
|
||||
+ " %-" + paymentMethodColWidth + "s"
|
||||
+ " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s";
|
||||
}
|
||||
}
|
||||
|
||||
private static String getMyCryptoOfferColDataFormat(boolean shouldShowTriggerPrice,
|
||||
int directionColWidth,
|
||||
int amountColWith,
|
||||
int volumeColWidth,
|
||||
int paymentMethodColWidth) {
|
||||
if (shouldShowTriggerPrice) {
|
||||
// Is my non-BSQ altcoin offer. Show ENABLED and TRIGGER_PRICE cols.
|
||||
return "%-" + (COL_HEADER_ENABLED.length() + COL_HEADER_DELIMITER.length()) + "s"
|
||||
+ "%-" + directionColWidth + "s"
|
||||
+ "%" + (COL_HEADER_PRICE_OF_ALTCOIN.length() + 1) + "s"
|
||||
+ " %" + amountColWith + "s"
|
||||
+ " %" + (volumeColWidth - 1) + "s"
|
||||
+ " %" + (COL_HEADER_TRIGGER_PRICE.length() - 1) + "s"
|
||||
+ " %-" + paymentMethodColWidth + "s"
|
||||
+ " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s";
|
||||
} else {
|
||||
// Is my BSQ altcoin offer. Show ENABLED, but not TRIGGER_PRICE col.
|
||||
return "%-" + (COL_HEADER_ENABLED.length() + COL_HEADER_DELIMITER.length()) + "s"
|
||||
+ "%-" + directionColWidth + "s"
|
||||
+ "%" + (COL_HEADER_PRICE_OF_ALTCOIN.length() + 1) + "s"
|
||||
+ " %" + amountColWith + "s"
|
||||
+ " %" + (volumeColWidth - 1) + "s"
|
||||
+ " %-" + paymentMethodColWidth + "s"
|
||||
+ " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s";
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatEnabled(OfferInfo offerInfo) {
|
||||
if (offerInfo.getIsMyOffer() && offerInfo.getIsMyPendingOffer())
|
||||
return "PENDING";
|
||||
else
|
||||
return offerInfo.getIsActivated() ? "YES" : "NO";
|
||||
}
|
||||
|
||||
private static int getLongestPaymentMethodColWidth(List<OfferInfo> offers) {
|
||||
return getLongestColumnSize(
|
||||
COL_HEADER_PAYMENT_METHOD.length(),
|
||||
offers.stream()
|
||||
.map(OfferInfo::getPaymentMethodShortName)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static int getLongestAmountColWidth(List<OfferInfo> offers) {
|
||||
return getLongestColumnSize(
|
||||
COL_HEADER_AMOUNT.length(),
|
||||
offers.stream()
|
||||
.map(o -> formatAmountRange(o.getMinAmount(), o.getAmount()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static int getLongestVolumeColWidth(List<OfferInfo> offers) {
|
||||
// Pad this col width by 1 space.
|
||||
return 1 + getLongestColumnSize(
|
||||
COL_HEADER_VOLUME.length(),
|
||||
offers.stream()
|
||||
.map(o -> formatVolumeRange(o.getMinVolume(), o.getVolume()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static int getLongestCryptoCurrencyVolumeColWidth(List<OfferInfo> offers) {
|
||||
// Pad this col width by 1 space.
|
||||
return 1 + getLongestColumnSize(
|
||||
COL_HEADER_VOLUME.length(),
|
||||
offers.stream()
|
||||
.map(o -> formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ import bisq.proto.grpc.AddressBalanceInfo;
|
||||
import bisq.proto.grpc.BalancesInfo;
|
||||
import bisq.proto.grpc.BsqBalanceInfo;
|
||||
import bisq.proto.grpc.BtcBalanceInfo;
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import protobuf.PaymentAccount;
|
||||
|
||||
@ -35,16 +34,15 @@ import java.util.TimeZone;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.cli.ColumnHeaderConstants.*;
|
||||
import static bisq.cli.CurrencyFormat.*;
|
||||
import static bisq.cli.DirectionFormat.directionFormat;
|
||||
import static bisq.cli.DirectionFormat.getLongestDirectionColWidth;
|
||||
import static bisq.cli.CurrencyFormat.formatBsq;
|
||||
import static bisq.cli.CurrencyFormat.formatSatoshis;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Collections.max;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.TimeZone.getTimeZone;
|
||||
|
||||
@Deprecated
|
||||
@VisibleForTesting
|
||||
public class TableFormat {
|
||||
|
||||
@ -145,234 +143,13 @@ public class TableFormat {
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
public static String formatOfferTable(List<OfferInfo> offers, String currencyCode) {
|
||||
if (offers == null || offers.isEmpty())
|
||||
throw new IllegalArgumentException(format("%s offer list is empty", currencyCode.toLowerCase()));
|
||||
|
||||
String baseCurrencyCode = offers.get(0).getBaseCurrencyCode();
|
||||
boolean isMyOffer = offers.get(0).getIsMyOffer();
|
||||
return baseCurrencyCode.equalsIgnoreCase("BTC")
|
||||
? formatFiatOfferTable(offers, currencyCode, isMyOffer)
|
||||
: formatCryptoCurrencyOfferTable(offers, baseCurrencyCode, isMyOffer);
|
||||
}
|
||||
|
||||
private static String formatFiatOfferTable(List<OfferInfo> offers,
|
||||
String fiatCurrencyCode,
|
||||
boolean isMyOffer) {
|
||||
// Some column values might be longer than header, so we need to calculate them.
|
||||
int amountColWith = getLongestAmountColWidth(offers);
|
||||
int volumeColWidth = getLongestVolumeColWidth(offers);
|
||||
int paymentMethodColWidth = getLongestPaymentMethodColWidth(offers);
|
||||
// "Enabled" and "Trigger Price" columns are displayed for my offers only.
|
||||
String enabledHeaderFormat = isMyOffer ?
|
||||
COL_HEADER_ENABLED + COL_HEADER_DELIMITER
|
||||
: "";
|
||||
String triggerPriceHeaderFormat = isMyOffer ?
|
||||
// COL_HEADER_TRIGGER_PRICE includes %s -> fiatCurrencyCode
|
||||
COL_HEADER_TRIGGER_PRICE + COL_HEADER_DELIMITER
|
||||
: "";
|
||||
String headersFormat = enabledHeaderFormat
|
||||
+ COL_HEADER_DIRECTION + COL_HEADER_DELIMITER
|
||||
// COL_HEADER_PRICE includes %s -> fiatCurrencyCode
|
||||
+ COL_HEADER_PRICE + COL_HEADER_DELIMITER
|
||||
+ padStart(COL_HEADER_AMOUNT, amountColWith, ' ') + COL_HEADER_DELIMITER
|
||||
// COL_HEADER_VOLUME includes %s -> fiatCurrencyCode
|
||||
+ padStart(COL_HEADER_VOLUME, volumeColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ triggerPriceHeaderFormat
|
||||
+ padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_CREATION_DATE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_UUID.trim() + "%n";
|
||||
String headerLine = format(headersFormat,
|
||||
fiatCurrencyCode.toUpperCase(),
|
||||
fiatCurrencyCode.toUpperCase(),
|
||||
// COL_HEADER_TRIGGER_PRICE includes %s -> fiatCurrencyCode
|
||||
isMyOffer ? fiatCurrencyCode.toUpperCase() : "");
|
||||
String colDataFormat = getFiatOfferColDataFormat(isMyOffer,
|
||||
amountColWith,
|
||||
volumeColWidth,
|
||||
paymentMethodColWidth);
|
||||
return formattedFiatOfferTable(offers, isMyOffer, headerLine, colDataFormat);
|
||||
}
|
||||
|
||||
private static String formattedFiatOfferTable(List<OfferInfo> offers,
|
||||
boolean isMyOffer,
|
||||
String headerLine,
|
||||
String colDataFormat) {
|
||||
if (isMyOffer) {
|
||||
return headerLine
|
||||
+ offers.stream()
|
||||
.map(o -> format(colDataFormat,
|
||||
formatEnabled(o),
|
||||
o.getDirection(),
|
||||
formatPrice(o.getPrice()),
|
||||
formatAmountRange(o.getMinAmount(), o.getAmount()),
|
||||
formatVolumeRange(o.getMinVolume(), o.getVolume()),
|
||||
o.getTriggerPrice() == 0 ? "" : formatPrice(o.getTriggerPrice()),
|
||||
o.getPaymentMethodShortName(),
|
||||
formatTimestamp(o.getDate()),
|
||||
o.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
} else {
|
||||
return headerLine
|
||||
+ offers.stream()
|
||||
.map(o -> format(colDataFormat,
|
||||
o.getDirection(),
|
||||
formatPrice(o.getPrice()),
|
||||
formatAmountRange(o.getMinAmount(), o.getAmount()),
|
||||
formatVolumeRange(o.getMinVolume(), o.getVolume()),
|
||||
o.getPaymentMethodShortName(),
|
||||
formatTimestamp(o.getDate()),
|
||||
o.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFiatOfferColDataFormat(boolean isMyOffer,
|
||||
int amountColWith,
|
||||
int volumeColWidth,
|
||||
int paymentMethodColWidth) {
|
||||
if (isMyOffer) {
|
||||
return "%-" + (COL_HEADER_ENABLED.length() + COL_HEADER_DELIMITER.length()) + "s"
|
||||
+ "%-" + (COL_HEADER_DIRECTION.length() + COL_HEADER_DELIMITER.length()) + "s"
|
||||
+ "%" + (COL_HEADER_PRICE.length() - 1) + "s"
|
||||
+ " %" + amountColWith + "s"
|
||||
+ " %" + (volumeColWidth - 1) + "s"
|
||||
+ " %" + (COL_HEADER_TRIGGER_PRICE.length() - 1) + "s"
|
||||
+ " %-" + paymentMethodColWidth + "s"
|
||||
+ " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s";
|
||||
} else {
|
||||
return "%-" + (COL_HEADER_DIRECTION.length() + COL_HEADER_DELIMITER.length()) + "s"
|
||||
+ "%" + (COL_HEADER_PRICE.length() - 1) + "s"
|
||||
+ " %" + amountColWith + "s"
|
||||
+ " %" + (volumeColWidth - 1) + "s"
|
||||
+ " %-" + paymentMethodColWidth + "s"
|
||||
+ " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s";
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatCryptoCurrencyOfferTable(List<OfferInfo> offers,
|
||||
String cryptoCurrencyCode,
|
||||
boolean isMyOffer) {
|
||||
// Some column values might be longer than header, so we need to calculate them.
|
||||
int directionColWidth = getLongestDirectionColWidth(offers);
|
||||
int amountColWith = getLongestAmountColWidth(offers);
|
||||
int volumeColWidth = getLongestCryptoCurrencyVolumeColWidth(offers);
|
||||
int paymentMethodColWidth = getLongestPaymentMethodColWidth(offers);
|
||||
// "Enabled" column is displayed for my offers only.
|
||||
String enabledHeaderFormat = isMyOffer ?
|
||||
COL_HEADER_ENABLED + COL_HEADER_DELIMITER
|
||||
: "";
|
||||
// TODO use memoize function to avoid duplicate the formatting done above?
|
||||
String headersFormat = enabledHeaderFormat
|
||||
+ padEnd(COL_HEADER_DIRECTION, directionColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_PRICE_OF_ALTCOIN + COL_HEADER_DELIMITER // includes %s -> cryptoCurrencyCode
|
||||
+ padStart(COL_HEADER_AMOUNT, amountColWith, ' ') + COL_HEADER_DELIMITER
|
||||
// COL_HEADER_VOLUME includes %s -> cryptoCurrencyCode
|
||||
+ padStart(COL_HEADER_VOLUME, volumeColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_CREATION_DATE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_UUID.trim() + "%n";
|
||||
String headerLine = format(headersFormat,
|
||||
cryptoCurrencyCode.toUpperCase(),
|
||||
cryptoCurrencyCode.toUpperCase());
|
||||
String colDataFormat;
|
||||
if (isMyOffer) {
|
||||
colDataFormat = "%-" + (COL_HEADER_ENABLED.length() + COL_HEADER_DELIMITER.length()) + "s"
|
||||
+ "%-" + directionColWidth + "s"
|
||||
+ "%" + (COL_HEADER_PRICE_OF_ALTCOIN.length() + 1) + "s"
|
||||
+ " %" + amountColWith + "s"
|
||||
+ " %" + (volumeColWidth - 1) + "s"
|
||||
+ " %-" + paymentMethodColWidth + "s"
|
||||
+ " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s";
|
||||
} else {
|
||||
colDataFormat = "%-" + directionColWidth + "s"
|
||||
+ "%" + (COL_HEADER_PRICE_OF_ALTCOIN.length() + 1) + "s"
|
||||
+ " %" + amountColWith + "s"
|
||||
+ " %" + (volumeColWidth - 1) + "s"
|
||||
+ " %-" + paymentMethodColWidth + "s"
|
||||
+ " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s";
|
||||
}
|
||||
if (isMyOffer) {
|
||||
return headerLine
|
||||
+ offers.stream()
|
||||
.map(o -> format(colDataFormat,
|
||||
formatEnabled(o),
|
||||
directionFormat.apply(o),
|
||||
formatCryptoCurrencyPrice(o.getPrice()),
|
||||
formatAmountRange(o.getMinAmount(), o.getAmount()),
|
||||
formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()),
|
||||
o.getPaymentMethodShortName(),
|
||||
formatTimestamp(o.getDate()),
|
||||
o.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
} else {
|
||||
return headerLine
|
||||
+ offers.stream()
|
||||
.map(o -> format(colDataFormat,
|
||||
directionFormat.apply(o),
|
||||
formatCryptoCurrencyPrice(o.getPrice()),
|
||||
formatAmountRange(o.getMinAmount(), o.getAmount()),
|
||||
formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()),
|
||||
o.getPaymentMethodShortName(),
|
||||
formatTimestamp(o.getDate()),
|
||||
o.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static String formatEnabled(OfferInfo offerInfo) {
|
||||
if (offerInfo.getIsMyOffer() && offerInfo.getIsMyPendingOffer())
|
||||
return "PENDING";
|
||||
else
|
||||
return offerInfo.getIsActivated() ? "YES" : "NO";
|
||||
}
|
||||
|
||||
private static int getLongestPaymentMethodColWidth(List<OfferInfo> offers) {
|
||||
return getLongestColumnSize(
|
||||
COL_HEADER_PAYMENT_METHOD.length(),
|
||||
offers.stream()
|
||||
.map(OfferInfo::getPaymentMethodShortName)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static int getLongestAmountColWidth(List<OfferInfo> offers) {
|
||||
return getLongestColumnSize(
|
||||
COL_HEADER_AMOUNT.length(),
|
||||
offers.stream()
|
||||
.map(o -> formatAmountRange(o.getMinAmount(), o.getAmount()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static int getLongestVolumeColWidth(List<OfferInfo> offers) {
|
||||
// Pad this col width by 1 space.
|
||||
return 1 + getLongestColumnSize(
|
||||
COL_HEADER_VOLUME.length(),
|
||||
offers.stream()
|
||||
.map(o -> formatVolumeRange(o.getMinVolume(), o.getVolume()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static int getLongestCryptoCurrencyVolumeColWidth(List<OfferInfo> offers) {
|
||||
// Pad this col width by 1 space.
|
||||
return 1 + getLongestColumnSize(
|
||||
COL_HEADER_VOLUME.length(),
|
||||
offers.stream()
|
||||
.map(o -> formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
// Return size of the longest string value, or the header.len, whichever is greater.
|
||||
private static int getLongestColumnSize(int headerLength, List<String> strings) {
|
||||
static int getLongestColumnSize(int headerLength, List<String> strings) {
|
||||
int longest = max(strings, comparing(String::length)).length();
|
||||
return Math.max(longest, headerLength);
|
||||
}
|
||||
|
||||
private static String formatTimestamp(long timestamp) {
|
||||
static String formatTimestamp(long timestamp) {
|
||||
DATE_FORMAT_ISO_8601.setTimeZone(TZ_UTC);
|
||||
return DATE_FORMAT_ISO_8601.format(new Date(timestamp));
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import static bisq.cli.ColumnHeaderConstants.*;
|
||||
import static bisq.cli.CurrencyFormat.*;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
|
||||
@Deprecated
|
||||
@VisibleForTesting
|
||||
public class TradeFormat {
|
||||
|
||||
@ -164,7 +165,7 @@ public class TradeFormat {
|
||||
private static final Function<TradeInfo, String> amountFormat = (t) ->
|
||||
t.getOffer().getBaseCurrencyCode().equals("BTC")
|
||||
? formatSatoshis(t.getTradeAmountAsLong())
|
||||
: formatCryptoCurrencyOfferVolume(t.getTradeVolume());
|
||||
: formatCryptoCurrencyVolume(t.getTradeVolume());
|
||||
|
||||
private static final BiFunction<TradeInfo, Boolean, String> makerTakerMinerTxFeeFormat = (t, isTaker) -> {
|
||||
if (isTaker) {
|
||||
@ -188,7 +189,7 @@ public class TradeFormat {
|
||||
|
||||
private static final Function<TradeInfo, String> tradeCostFormat = (t) ->
|
||||
t.getOffer().getBaseCurrencyCode().equals("BTC")
|
||||
? formatOfferVolume(t.getTradeVolume())
|
||||
? formatFiatVolume(t.getTradeVolume())
|
||||
: formatSatoshis(t.getTradeAmountAsLong());
|
||||
|
||||
private static final BiFunction<TradeInfo, Boolean, String> bsqReceiveAddress = (t, showBsqBuyerAddress) -> {
|
||||
|
@ -25,6 +25,7 @@ import static bisq.cli.ColumnHeaderConstants.*;
|
||||
import static bisq.cli.CurrencyFormat.formatSatoshis;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
|
||||
@Deprecated
|
||||
@VisibleForTesting
|
||||
public class TransactionFormat {
|
||||
|
||||
|
@ -20,7 +20,10 @@ package bisq.cli.opts;
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.*;
|
||||
import static bisq.cli.opts.OptLabel.OPT_ACCOUNT_NAME;
|
||||
import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
|
||||
import static bisq.cli.opts.OptLabel.OPT_CURRENCY_CODE;
|
||||
import static bisq.cli.opts.OptLabel.OPT_TRADE_INSTANT;
|
||||
|
||||
public class CreateCryptoCurrencyPaymentAcctOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
@ -38,11 +41,6 @@ public class CreateCryptoCurrencyPaymentAcctOptionParser extends AbstractMethodO
|
||||
.ofType(boolean.class)
|
||||
.defaultsTo(Boolean.FALSE);
|
||||
|
||||
final OptionSpec<Boolean> tradeBsqSwapOpt = parser.accepts(OPT_TRADE_BSQ_SWAP, "create trade bsq swap account")
|
||||
.withOptionalArg()
|
||||
.ofType(boolean.class)
|
||||
.defaultsTo(Boolean.FALSE);
|
||||
|
||||
public CreateCryptoCurrencyPaymentAcctOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
@ -84,8 +82,4 @@ public class CreateCryptoCurrencyPaymentAcctOptionParser extends AbstractMethodO
|
||||
public boolean getIsTradeInstant() {
|
||||
return options.valueOf(tradeInstantOpt);
|
||||
}
|
||||
|
||||
public boolean getIsTradeBsqSwap() {
|
||||
return options.valueOf(tradeBsqSwapOpt);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,6 @@ public class OptLabel {
|
||||
public final static String OPT_REGISTRATION_KEY = "registration-key";
|
||||
public final static String OPT_SECURITY_DEPOSIT = "security-deposit";
|
||||
public final static String OPT_SHOW_CONTRACT = "show-contract";
|
||||
public final static String OPT_TRADE_BSQ_SWAP = "trade-bsq-swap";
|
||||
public final static String OPT_TRADE_ID = "trade-id";
|
||||
public final static String OPT_TRADE_INSTANT = "trade-instant";
|
||||
public final static String OPT_TIMEOUT = "timeout";
|
||||
|
@ -60,6 +60,7 @@ public class OffersServiceRequest {
|
||||
this.grpcStubs = grpcStubs;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public OfferInfo createFixedPricedOffer(String direction,
|
||||
String currencyCode,
|
||||
long amount,
|
||||
@ -81,6 +82,7 @@ public class OffersServiceRequest {
|
||||
0 /* no trigger price */);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public OfferInfo createMarketBasedPricedOffer(String direction,
|
||||
String currencyCode,
|
||||
long amount,
|
||||
@ -328,6 +330,13 @@ public class OffersServiceRequest {
|
||||
return sortOffersByDate(offers);
|
||||
}
|
||||
|
||||
public List<OfferInfo> getMyCryptoCurrencyOffersSortedByDate(String currencyCode) {
|
||||
ArrayList<OfferInfo> offers = new ArrayList<>();
|
||||
offers.addAll(getMyCryptoCurrencyOffers(BUY.name(), currencyCode));
|
||||
offers.addAll(getMyCryptoCurrencyOffers(SELL.name(), currencyCode));
|
||||
return sortOffersByDate(offers);
|
||||
}
|
||||
|
||||
public List<OfferInfo> getMyBsqOffersSortedByDate() {
|
||||
ArrayList<OfferInfo> offers = new ArrayList<>();
|
||||
offers.addAll(getMyCryptoCurrencyOffers(BUY.name(), "BSQ"));
|
||||
|
@ -29,6 +29,8 @@ import protobuf.PaymentMethod;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.GrpcStubs;
|
||||
@ -65,6 +67,22 @@ public class PaymentAccountsServiceRequest {
|
||||
return grpcStubs.paymentAccountsService.getPaymentAccounts(request).getPaymentAccountsList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first PaymentAccount found with the given name, or throws an
|
||||
* IllegalArgumentException if not found. This method should be used with care;
|
||||
* it will only return one PaymentAccount, and the account name must be an exact
|
||||
* match on the name argument.
|
||||
* @param accountName the name of the stored PaymentAccount to retrieve
|
||||
* @return PaymentAccount with given name
|
||||
*/
|
||||
public PaymentAccount getPaymentAccount(String accountName) {
|
||||
return getPaymentAccounts().stream()
|
||||
.filter(a -> a.getAccountName().equals(accountName)).findFirst()
|
||||
.orElseThrow(() ->
|
||||
new IllegalArgumentException(format("payment account with name '%s' not found",
|
||||
accountName)));
|
||||
}
|
||||
|
||||
public PaymentAccount createCryptoCurrencyPaymentAccount(String accountName,
|
||||
String currencyCode,
|
||||
String address,
|
||||
|
@ -74,6 +74,7 @@ public class TradesServiceRequest {
|
||||
var request = ConfirmPaymentStartedRequest.newBuilder()
|
||||
.setTradeId(tradeId)
|
||||
.build();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.tradesService.confirmPaymentStarted(request);
|
||||
}
|
||||
|
||||
@ -81,6 +82,7 @@ public class TradesServiceRequest {
|
||||
var request = ConfirmPaymentReceivedRequest.newBuilder()
|
||||
.setTradeId(tradeId)
|
||||
.build();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.tradesService.confirmPaymentReceived(request);
|
||||
}
|
||||
|
||||
@ -88,6 +90,7 @@ public class TradesServiceRequest {
|
||||
var request = KeepFundsRequest.newBuilder()
|
||||
.setTradeId(tradeId)
|
||||
.build();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.tradesService.keepFunds(request);
|
||||
}
|
||||
|
||||
@ -97,6 +100,7 @@ public class TradesServiceRequest {
|
||||
.setAddress(address)
|
||||
.setMemo(memo)
|
||||
.build();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.tradesService.withdrawFunds(request);
|
||||
}
|
||||
}
|
||||
|
@ -161,6 +161,7 @@ public class WalletsServiceRequest {
|
||||
|
||||
public void lockWallet() {
|
||||
var request = LockWalletRequest.newBuilder().build();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.walletsService.lockWallet(request);
|
||||
}
|
||||
|
||||
@ -168,18 +169,21 @@ public class WalletsServiceRequest {
|
||||
var request = UnlockWalletRequest.newBuilder()
|
||||
.setPassword(walletPassword)
|
||||
.setTimeout(timeout).build();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.walletsService.unlockWallet(request);
|
||||
}
|
||||
|
||||
public void removeWalletPassword(String walletPassword) {
|
||||
var request = RemoveWalletPasswordRequest.newBuilder()
|
||||
.setPassword(walletPassword).build();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.walletsService.removeWalletPassword(request);
|
||||
}
|
||||
|
||||
public void setWalletPassword(String walletPassword) {
|
||||
var request = SetWalletPasswordRequest.newBuilder()
|
||||
.setPassword(walletPassword).build();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.walletsService.setWalletPassword(request);
|
||||
}
|
||||
|
||||
@ -187,6 +191,7 @@ public class WalletsServiceRequest {
|
||||
var request = SetWalletPasswordRequest.newBuilder()
|
||||
.setPassword(oldWalletPassword)
|
||||
.setNewPassword(newWalletPassword).build();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.walletsService.setWalletPassword(request);
|
||||
}
|
||||
}
|
||||
|
155
cli/src/main/java/bisq/cli/table/Table.java
Normal file
155
cli/src/main/java/bisq/cli/table/Table.java
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.cli.table;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
import static java.lang.String.format;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.column.Column;
|
||||
|
||||
/**
|
||||
* A simple table of formatted data for the CLI's output console. A table must be
|
||||
* created with at least one populated column, and each column passed to the constructor
|
||||
* must contain the same number of rows. Null checking is omitted because tables are
|
||||
* populated by protobuf message fields which cannot be null.
|
||||
*
|
||||
* All data in a column has the same type: long, string, etc., but a table
|
||||
* may contain an arbitrary number of columns of any type. For output formatting
|
||||
* purposes, numeric and date columns should be transformed to a StringColumn type with
|
||||
* formatted and justified string values before being passed to the constructor.
|
||||
*
|
||||
* This is not a relational, rdbms table.
|
||||
*/
|
||||
public class Table {
|
||||
|
||||
public final Column<?>[] columns;
|
||||
public final int rowCount;
|
||||
|
||||
// Each printed column is delimited by two spaces.
|
||||
private final int columnDelimiterLength = 2;
|
||||
|
||||
/**
|
||||
* Default constructor. Takes populated Columns.
|
||||
*
|
||||
* @param columns containing the same number of rows
|
||||
*/
|
||||
public Table(Column<?>... columns) {
|
||||
this.columns = columns;
|
||||
this.rowCount = columns.length > 0 ? columns[0].rowCount() : 0;
|
||||
validateStructure();
|
||||
}
|
||||
|
||||
/**
|
||||
* Print table data to a PrintStream.
|
||||
*
|
||||
* @param printStream the target output stream
|
||||
*/
|
||||
public void print(PrintStream printStream) {
|
||||
printColumnNames(printStream);
|
||||
for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
||||
printRow(printStream, rowIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print table column names to a PrintStream.
|
||||
*
|
||||
* @param printStream the target output stream
|
||||
*/
|
||||
private void printColumnNames(PrintStream printStream) {
|
||||
IntStream.range(0, columns.length).forEachOrdered(colIndex -> {
|
||||
var c = columns[colIndex];
|
||||
var justifiedName = c.getJustification().equals(RIGHT)
|
||||
? padStart(c.getName(), c.getWidth(), ' ')
|
||||
: c.getName();
|
||||
var paddedWidth = colIndex == columns.length - 1
|
||||
? c.getName().length()
|
||||
: c.getWidth() + columnDelimiterLength;
|
||||
printStream.printf("%-" + paddedWidth + "s", justifiedName);
|
||||
});
|
||||
printStream.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a table row to a PrintStream.
|
||||
*
|
||||
* @param printStream the target output stream
|
||||
*/
|
||||
private void printRow(PrintStream printStream, int rowIndex) {
|
||||
IntStream.range(0, columns.length).forEachOrdered(colIndex -> {
|
||||
var c = columns[colIndex];
|
||||
var paddedWidth = colIndex == columns.length - 1
|
||||
? c.getWidth()
|
||||
: c.getWidth() + columnDelimiterLength;
|
||||
printStream.printf("%-" + paddedWidth + "s", c.getRow(rowIndex));
|
||||
if (colIndex == columns.length - 1)
|
||||
printStream.println();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the table's formatted output as a String.
|
||||
* @return String
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try (PrintStream ps = new PrintStream(baos, true, UTF_8)) {
|
||||
print(ps);
|
||||
}
|
||||
return baos.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the table has columns, and each column has the same number of rows.
|
||||
*/
|
||||
private void validateStructure() {
|
||||
if (columns.length == 0)
|
||||
throw new IllegalArgumentException("Table has no columns.");
|
||||
|
||||
if (columns[0].isEmpty())
|
||||
throw new IllegalArgumentException(
|
||||
format("Table's 1st column (%s) has no data.",
|
||||
columns[0].getName()));
|
||||
|
||||
IntStream.range(1, columns.length).forEachOrdered(colIndex -> {
|
||||
var c = columns[colIndex];
|
||||
|
||||
if (c.isEmpty())
|
||||
throw new IllegalStateException(
|
||||
format("Table column # %d (%s) does not have any data.",
|
||||
colIndex + 1,
|
||||
c.getName()));
|
||||
|
||||
if (this.rowCount != c.rowCount())
|
||||
throw new IllegalStateException(
|
||||
format("Table column # %d (%s) does not have same number of rows as 1st column.",
|
||||
colIndex + 1,
|
||||
c.getName()));
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.cli.table.builder;
|
||||
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.Table;
|
||||
|
||||
/**
|
||||
* Abstract superclass for TableBuilder implementations.
|
||||
*/
|
||||
abstract class AbstractTableBuilder {
|
||||
|
||||
protected final TableType tableType;
|
||||
protected final List<?> protos;
|
||||
|
||||
AbstractTableBuilder(TableType tableType, List<?> protos) {
|
||||
this.tableType = tableType;
|
||||
this.protos = protos;
|
||||
if (protos.isEmpty())
|
||||
throw new IllegalArgumentException("cannot build a table without rows");
|
||||
}
|
||||
|
||||
public abstract Table build();
|
||||
|
||||
protected final Predicate<OfferInfo> isFiatOffer = (o) -> o.getBaseCurrencyCode().equals("BTC");
|
||||
}
|
@ -0,0 +1,303 @@
|
||||
/*
|
||||
* 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.cli.table.builder;
|
||||
|
||||
import bisq.proto.grpc.ContractInfo;
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_BUYER_DEPOSIT;
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_SELLER_DEPOSIT;
|
||||
import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.column.Column;
|
||||
import bisq.cli.table.column.MixedTradeFeeColumn;
|
||||
import bisq.cli.table.column.MixedVolumeColumn;
|
||||
|
||||
abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
|
||||
|
||||
protected final List<TradeInfo> trades;
|
||||
|
||||
protected final TradeTableColumnSupplier colSupplier;
|
||||
|
||||
protected final Column<String> colTradeId;
|
||||
@Nullable
|
||||
protected final Column<Long> colCreateDate;
|
||||
@Nullable
|
||||
protected final Column<String> colMarket;
|
||||
protected final Column<Long> colPrice;
|
||||
@Nullable
|
||||
protected final Column<String> colPriceDeviation;
|
||||
@Nullable
|
||||
protected final Column<String> colCurrency;
|
||||
@Nullable
|
||||
protected final Column<Long> colAmountInBtc;
|
||||
@Nullable
|
||||
protected final MixedVolumeColumn colMixedAmount;
|
||||
@Nullable
|
||||
protected final Column<Long> colMinerTxFee;
|
||||
@Nullable
|
||||
protected final MixedTradeFeeColumn colMixedTradeFee;
|
||||
@Nullable
|
||||
protected final Column<Long> colBuyerDeposit;
|
||||
@Nullable
|
||||
protected final Column<Long> colSellerDeposit;
|
||||
@Nullable
|
||||
protected final Column<String> colPaymentMethod;
|
||||
@Nullable
|
||||
protected final Column<String> colRole;
|
||||
@Nullable
|
||||
protected final Column<String> colOfferType;
|
||||
@Nullable
|
||||
protected final Column<String> colStatusDescription;
|
||||
|
||||
// Trade detail tbl specific columns
|
||||
|
||||
@Nullable
|
||||
protected final Column<Boolean> colIsDepositPublished;
|
||||
@Nullable
|
||||
protected final Column<Boolean> colIsDepositConfirmed;
|
||||
@Nullable
|
||||
protected final Column<Boolean> colIsPayoutPublished;
|
||||
@Nullable
|
||||
protected final Column<Boolean> colIsFundsWithdrawn;
|
||||
@Nullable
|
||||
protected final Column<Long> colBisqTradeFee;
|
||||
@Nullable
|
||||
protected final Column<Long> colTradeCost;
|
||||
@Nullable
|
||||
protected final Column<Boolean> colIsPaymentSent;
|
||||
@Nullable
|
||||
protected final Column<Boolean> colIsPaymentReceived;
|
||||
@Nullable
|
||||
protected final Column<String> colAltcoinReceiveAddressColumn;
|
||||
|
||||
AbstractTradeListBuilder(TableType tableType, List<?> protos) {
|
||||
super(tableType, protos);
|
||||
validate();
|
||||
|
||||
this.trades = protos.stream().map(p -> (TradeInfo) p).collect(Collectors.toList());
|
||||
this.colSupplier = new TradeTableColumnSupplier(tableType, trades);
|
||||
|
||||
this.colTradeId = colSupplier.tradeIdColumn.get();
|
||||
this.colCreateDate = colSupplier.createDateColumn.get();
|
||||
this.colMarket = colSupplier.marketColumn.get();
|
||||
this.colPrice = colSupplier.priceColumn.get();
|
||||
this.colPriceDeviation = colSupplier.priceDeviationColumn.get();
|
||||
this.colCurrency = colSupplier.currencyColumn.get();
|
||||
this.colAmountInBtc = colSupplier.amountInBtcColumn.get();
|
||||
this.colMixedAmount = colSupplier.mixedAmountColumn.get();
|
||||
this.colMinerTxFee = colSupplier.minerTxFeeColumn.get();
|
||||
this.colMixedTradeFee = colSupplier.mixedTradeFeeColumn.get();
|
||||
this.colBuyerDeposit = colSupplier.toSecurityDepositColumn.apply(COL_HEADER_BUYER_DEPOSIT);
|
||||
this.colSellerDeposit = colSupplier.toSecurityDepositColumn.apply(COL_HEADER_SELLER_DEPOSIT);
|
||||
this.colPaymentMethod = colSupplier.paymentMethodColumn.get();
|
||||
this.colRole = colSupplier.roleColumn.get();
|
||||
this.colOfferType = colSupplier.offerTypeColumn.get();
|
||||
this.colStatusDescription = colSupplier.statusDescriptionColumn.get();
|
||||
// Trade detail specific columns
|
||||
this.colIsDepositPublished = colSupplier.depositPublishedColumn.get();
|
||||
this.colIsDepositConfirmed = colSupplier.depositConfirmedColumn.get();
|
||||
this.colIsPayoutPublished = colSupplier.payoutPublishedColumn.get();
|
||||
this.colIsFundsWithdrawn = colSupplier.fundsWithdrawnColumn.get();
|
||||
this.colBisqTradeFee = colSupplier.bisqTradeDetailFeeColumn.get();
|
||||
this.colTradeCost = colSupplier.tradeCostColumn.get();
|
||||
this.colIsPaymentSent = colSupplier.paymentSentColumn.get();
|
||||
this.colIsPaymentReceived = colSupplier.paymentReceivedColumn.get();
|
||||
this.colAltcoinReceiveAddressColumn = colSupplier.altcoinReceiveAddressColumn.get();
|
||||
}
|
||||
|
||||
protected void validate() {
|
||||
if (isTradeDetailTblBuilder.get()) {
|
||||
if (protos.size() != 1)
|
||||
throw new IllegalArgumentException("trade detail tbl can have only one row");
|
||||
} else if (protos.isEmpty()) {
|
||||
throw new IllegalArgumentException("trade tbl has no rows");
|
||||
}
|
||||
}
|
||||
|
||||
// Helper Functions
|
||||
|
||||
private final Supplier<Boolean> isTradeDetailTblBuilder = () -> tableType.equals(TRADE_DETAIL_TBL);
|
||||
|
||||
protected final Predicate<TradeInfo> isFiatTrade = (t) -> isFiatOffer.test(t.getOffer());
|
||||
|
||||
protected final Predicate<TradeInfo> isTaker = (t) -> t.getRole().toLowerCase().contains("taker");
|
||||
|
||||
// Column Value Functions
|
||||
|
||||
protected final Function<TradeInfo, Long> toAmount = (t) ->
|
||||
isFiatTrade.test(t)
|
||||
? t.getTradeAmountAsLong()
|
||||
: t.getTradeVolume();
|
||||
|
||||
protected final Function<TradeInfo, Long> toTradeVolume = (t) ->
|
||||
isFiatTrade.test(t)
|
||||
? t.getTradeVolume()
|
||||
: t.getTradeAmountAsLong();
|
||||
|
||||
protected final Function<TradeInfo, String> toMarket = (t) ->
|
||||
t.getOffer().getBaseCurrencyCode() + "/"
|
||||
+ t.getOffer().getCounterCurrencyCode();
|
||||
|
||||
protected final Function<TradeInfo, String> toPaymentCurrencyCode = (t) ->
|
||||
isFiatTrade.test(t)
|
||||
? t.getOffer().getCounterCurrencyCode()
|
||||
: t.getOffer().getBaseCurrencyCode();
|
||||
|
||||
|
||||
protected final Function<TradeInfo, Integer> toDisplayedVolumePrecision = (t) -> {
|
||||
if (isFiatTrade.test(t)) {
|
||||
return 0;
|
||||
} else {
|
||||
String currencyCode = toPaymentCurrencyCode.apply(t);
|
||||
return currencyCode.equalsIgnoreCase("BSQ") ? 2 : 8;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO Move to TradeUtil ?
|
||||
protected final Function<TradeInfo, String> toPriceDeviation = (t) ->
|
||||
t.getOffer().getUseMarketBasedPrice()
|
||||
? formatToPercent(t.getOffer().getMarketPriceMargin())
|
||||
: "N/A";
|
||||
|
||||
protected final Function<TradeInfo, Long> toMyMinerTxFee = (t) ->
|
||||
isTaker.test(t)
|
||||
? t.getTxFeeAsLong()
|
||||
: t.getOffer().getTxFee();
|
||||
|
||||
|
||||
// TODO Move to TradeUtil ?
|
||||
protected final BiFunction<TradeInfo, Boolean, Long> toTradeFeeBsq = (t, isMyOffer) -> {
|
||||
if (isMyOffer) {
|
||||
return t.getOffer().getIsCurrencyForMakerFeeBtc()
|
||||
? 0L // Maker paid BTC fee, return 0.
|
||||
: t.getOffer().getMakerFee();
|
||||
} else {
|
||||
return t.getIsCurrencyForTakerFeeBtc()
|
||||
? 0L // Taker paid BTC fee, return 0.
|
||||
: t.getTakerFeeAsLong();
|
||||
}
|
||||
};
|
||||
|
||||
// TODO Move to TradeUtil ?
|
||||
protected final BiFunction<TradeInfo, Boolean, Long> toTradeFeeBtc = (t, isMyOffer) -> {
|
||||
if (isMyOffer) {
|
||||
return t.getOffer().getIsCurrencyForMakerFeeBtc()
|
||||
? t.getOffer().getMakerFee()
|
||||
: 0L; // Maker paid BSQ fee, return 0.
|
||||
} else {
|
||||
return t.getIsCurrencyForTakerFeeBtc()
|
||||
? t.getTakerFeeAsLong()
|
||||
: 0L; // Taker paid BSQ fee, return 0.
|
||||
}
|
||||
};
|
||||
|
||||
protected final Function<TradeInfo, Long> toMyMakerOrTakerFee = (t) ->
|
||||
isTaker.test(t)
|
||||
? t.getTakerFeeAsLong()
|
||||
: t.getOffer().getMakerFee();
|
||||
|
||||
// TODO Move to TradeUtil ? SEE ClosedTradesViewModel # getDirectionLabel
|
||||
protected final Function<TradeInfo, String> toOfferType = (t) -> {
|
||||
if (isFiatTrade.test(t)) {
|
||||
return t.getOffer().getDirection() + " " + t.getOffer().getBaseCurrencyCode();
|
||||
} else {
|
||||
if (t.getOffer().getDirection().equals("BUY")) {
|
||||
return "SELL " + t.getOffer().getBaseCurrencyCode();
|
||||
} else {
|
||||
return "BUY " + t.getOffer().getBaseCurrencyCode();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected final Predicate<TradeInfo> showAltCoinBuyerAddress = (t) -> {
|
||||
if (isFiatTrade.test(t)) {
|
||||
return false;
|
||||
} else {
|
||||
ContractInfo contract = t.getContract();
|
||||
boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker();
|
||||
if (isTaker.test(t)) {
|
||||
return !isBuyerMakerAndSellerTaker;
|
||||
} else {
|
||||
return isBuyerMakerAndSellerTaker;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected final Function<TradeInfo, String> toAltcoinReceiveAddress = (t) -> {
|
||||
if (showAltCoinBuyerAddress.test(t)) {
|
||||
ContractInfo contract = t.getContract();
|
||||
boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker();
|
||||
return isBuyerMakerAndSellerTaker // (is BTC buyer / maker)
|
||||
? contract.getTakerPaymentAccountPayload().getAddress()
|
||||
: contract.getMakerPaymentAccountPayload().getAddress();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
// TODO Stuff to move into bisq/cli/CurrencyFormat.java ?
|
||||
|
||||
public static String formatToPercent(double value) {
|
||||
DecimalFormat decimalFormat = new DecimalFormat("#.##");
|
||||
decimalFormat.setMinimumFractionDigits(2);
|
||||
decimalFormat.setMaximumFractionDigits(2);
|
||||
return formatToPercent(value, decimalFormat);
|
||||
}
|
||||
|
||||
public static String formatToPercent(double value, DecimalFormat decimalFormat) {
|
||||
return decimalFormat.format(roundDouble(value * 100.0, 2)).replace(",", ".") + "%";
|
||||
}
|
||||
|
||||
public static double roundDouble(double value, int precision) {
|
||||
return roundDouble(value, precision, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public static double roundDouble(double value, int precision, RoundingMode roundingMode) {
|
||||
if (precision < 0)
|
||||
throw new IllegalArgumentException();
|
||||
if (!Double.isFinite(value))
|
||||
throw new IllegalArgumentException("Expected a finite double, but found " + value);
|
||||
|
||||
try {
|
||||
BigDecimal bd = BigDecimal.valueOf(value);
|
||||
bd = bd.setScale(precision, roundingMode);
|
||||
return bd.doubleValue();
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace(); // TODO throw pretty exception for CLI console
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package bisq.cli.table.builder;
|
||||
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.*;
|
||||
import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.column.BtcColumn;
|
||||
import bisq.cli.table.column.Column;
|
||||
import bisq.cli.table.column.Iso8601DateTimeColumn;
|
||||
import bisq.cli.table.column.MixedPriceColumn;
|
||||
import bisq.cli.table.column.MixedTradeFeeColumn;
|
||||
import bisq.cli.table.column.MixedVolumeColumn;
|
||||
import bisq.cli.table.column.SatoshiColumn;
|
||||
import bisq.cli.table.column.StringColumn;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.cli.table.Table} from one or more {@code bisq.proto.grpc.TradeInfo} objects.
|
||||
*/
|
||||
abstract class AbstractTradeTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
@Nullable
|
||||
protected final Column<String> colTradeId;
|
||||
@Nullable
|
||||
protected final Column<Long> colCreateDate;
|
||||
@Nullable
|
||||
protected final Column<String> colMarket;
|
||||
@Nullable
|
||||
protected final MixedPriceColumn colMixedPrice;
|
||||
@Nullable
|
||||
protected final Column<String> colPriceDeviation;
|
||||
@Nullable
|
||||
protected final Column<Long> colAmountInBtc;
|
||||
@Nullable
|
||||
protected final MixedVolumeColumn colMixedAmount;
|
||||
@Nullable
|
||||
protected final Column<String> colCurrency;
|
||||
@Nullable
|
||||
protected final MixedTradeFeeColumn colMixedTradeFee;
|
||||
@Nullable
|
||||
protected final Column<Long> colBuyerDeposit;
|
||||
@Nullable
|
||||
protected final Column<Long> colSellerDeposit;
|
||||
@Nullable
|
||||
protected final Column<String> colOfferType;
|
||||
@Nullable
|
||||
protected final Column<String> colStatus;
|
||||
protected final Column<Long> colMinerTxFee;
|
||||
|
||||
AbstractTradeTableBuilder(TableType tableType, List<?> protos) {
|
||||
super(tableType, protos);
|
||||
boolean isTradeDetail = tableType.equals(TRADE_DETAIL_TBL);
|
||||
this.colTradeId = isTradeDetail ? null : new StringColumn(COL_HEADER_TRADE_ID);
|
||||
this.colCreateDate = isTradeDetail ? null : new Iso8601DateTimeColumn(COL_HEADER_DATE_TIME);
|
||||
this.colMarket = isTradeDetail ? null : new StringColumn(COL_HEADER_MARKET);
|
||||
this.colMixedPrice = isTradeDetail ? null : new MixedPriceColumn(COL_HEADER_PRICE);
|
||||
this.colPriceDeviation = isTradeDetail ? null : new StringColumn(COL_HEADER_DEVIATION, RIGHT);
|
||||
this.colAmountInBtc = isTradeDetail ? null : new BtcColumn(COL_HEADER_AMOUNT_IN_BTC);
|
||||
this.colMixedAmount = isTradeDetail ? null : new MixedVolumeColumn(COL_HEADER_AMOUNT);
|
||||
this.colCurrency = isTradeDetail ? null : new StringColumn(COL_HEADER_CURRENCY);
|
||||
this.colMixedTradeFee = isTradeDetail ? null : new MixedTradeFeeColumn(COL_HEADER_TRADE_FEE);
|
||||
this.colBuyerDeposit = isTradeDetail ? null : new SatoshiColumn(COL_HEADER_BUYER_DEPOSIT);
|
||||
this.colSellerDeposit = isTradeDetail ? null : new SatoshiColumn(COL_HEADER_SELLER_DEPOSIT);
|
||||
this.colOfferType = isTradeDetail ? null : new StringColumn(COL_HEADER_OFFER_TYPE);
|
||||
this.colStatus = isTradeDetail ? null : new StringColumn(COL_HEADER_STATUS);
|
||||
this.colMinerTxFee = new SatoshiColumn(COL_HEADER_TX_FEE);
|
||||
}
|
||||
|
||||
protected final Predicate<TradeInfo> isFiatTrade = (t) -> isFiatOffer.test(t.getOffer());
|
||||
|
||||
protected final Predicate<TradeInfo> isTaker = (t) -> t.getRole().toLowerCase().contains("taker");
|
||||
|
||||
protected final Function<TradeInfo, String> toPaymentCurrencyCode = (t) ->
|
||||
isFiatTrade.test(t)
|
||||
? t.getOffer().getCounterCurrencyCode()
|
||||
: t.getOffer().getBaseCurrencyCode();
|
||||
|
||||
protected final Function<TradeInfo, Long> toAmount = (t) ->
|
||||
isFiatTrade.test(t)
|
||||
? t.getTradeAmountAsLong()
|
||||
: t.getTradeVolume();
|
||||
|
||||
protected final Function<TradeInfo, Long> toMinerTxFee = (t) ->
|
||||
isTaker.test(t)
|
||||
? t.getTxFeeAsLong()
|
||||
: t.getOffer().getTxFee();
|
||||
|
||||
protected final Function<TradeInfo, Long> toMakerTakerFee = (t) ->
|
||||
isTaker.test(t)
|
||||
? t.getTakerFeeAsLong()
|
||||
: t.getOffer().getMakerFee();
|
||||
|
||||
protected final Function<TradeInfo, Long> toTradeCost = (t) ->
|
||||
isFiatTrade.test(t)
|
||||
? t.getTradeVolume()
|
||||
: t.getTradeAmountAsLong();
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.cli.table.builder;
|
||||
|
||||
import bisq.proto.grpc.AddressBalanceInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_ADDRESS;
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_AVAILABLE_BALANCE;
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_CONFIRMATIONS;
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_IS_USED_ADDRESS;
|
||||
import static bisq.cli.table.builder.TableType.ADDRESS_BALANCE_TBL;
|
||||
import static java.lang.String.format;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.Table;
|
||||
import bisq.cli.table.column.BooleanColumn;
|
||||
import bisq.cli.table.column.Column;
|
||||
import bisq.cli.table.column.LongColumn;
|
||||
import bisq.cli.table.column.SatoshiColumn;
|
||||
import bisq.cli.table.column.StringColumn;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.cli.table.Table} from a List of
|
||||
* {@code bisq.proto.grpc.AddressBalanceInfo} objects.
|
||||
*/
|
||||
class AddressBalanceTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
// Default columns not dynamically generated with address info.
|
||||
private final Column<String> colAddress;
|
||||
private final Column<Long> colAvailableBalance;
|
||||
private final Column<Long> colConfirmations;
|
||||
private final Column<Boolean> colIsUsed;
|
||||
|
||||
AddressBalanceTableBuilder(List<?> protos) {
|
||||
super(ADDRESS_BALANCE_TBL, protos);
|
||||
colAddress = new StringColumn(format(COL_HEADER_ADDRESS, "BTC"));
|
||||
this.colAvailableBalance = new SatoshiColumn(COL_HEADER_AVAILABLE_BALANCE);
|
||||
this.colConfirmations = new LongColumn(COL_HEADER_CONFIRMATIONS);
|
||||
this.colIsUsed = new BooleanColumn(COL_HEADER_IS_USED_ADDRESS);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
List<AddressBalanceInfo> addresses = protos.stream()
|
||||
.map(a -> (AddressBalanceInfo) a)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Populate columns with address info.
|
||||
//noinspection SimplifyStreamApiCallChains
|
||||
addresses.stream().forEachOrdered(a -> {
|
||||
colAddress.addRow(a.getAddress());
|
||||
colAvailableBalance.addRow(a.getBalance());
|
||||
colConfirmations.addRow(a.getNumConfirmations());
|
||||
colIsUsed.addRow(!a.getIsAddressUnused());
|
||||
});
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
return new Table(colAddress,
|
||||
colAvailableBalance.asStringColumn(),
|
||||
colConfirmations.asStringColumn(),
|
||||
colIsUsed.asStringColumn());
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.cli.table.builder;
|
||||
|
||||
import bisq.proto.grpc.BsqBalanceInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.*;
|
||||
import static bisq.cli.table.builder.TableType.BSQ_BALANCE_TBL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.Table;
|
||||
import bisq.cli.table.column.Column;
|
||||
import bisq.cli.table.column.SatoshiColumn;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.cli.table.Table} from a
|
||||
* {@code bisq.proto.grpc.BsqBalanceInfo} object.
|
||||
*/
|
||||
class BsqBalanceTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
// Default columns not dynamically generated with bsq balance info.
|
||||
private final Column<Long> colAvailableConfirmedBalance;
|
||||
private final Column<Long> colUnverifiedBalance;
|
||||
private final Column<Long> colUnconfirmedChangeBalance;
|
||||
private final Column<Long> colLockedForVotingBalance;
|
||||
private final Column<Long> colLockupBondsBalance;
|
||||
private final Column<Long> colUnlockingBondsBalance;
|
||||
|
||||
BsqBalanceTableBuilder(List<?> protos) {
|
||||
super(BSQ_BALANCE_TBL, protos);
|
||||
this.colAvailableConfirmedBalance = new SatoshiColumn(COL_HEADER_AVAILABLE_CONFIRMED_BALANCE, true);
|
||||
this.colUnverifiedBalance = new SatoshiColumn(COL_HEADER_UNVERIFIED_BALANCE, true);
|
||||
this.colUnconfirmedChangeBalance = new SatoshiColumn(COL_HEADER_UNCONFIRMED_CHANGE_BALANCE, true);
|
||||
this.colLockedForVotingBalance = new SatoshiColumn(COL_HEADER_LOCKED_FOR_VOTING_BALANCE, true);
|
||||
this.colLockupBondsBalance = new SatoshiColumn(COL_HEADER_LOCKUP_BONDS_BALANCE, true);
|
||||
this.colUnlockingBondsBalance = new SatoshiColumn(COL_HEADER_UNLOCKING_BONDS_BALANCE, true);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
BsqBalanceInfo balance = (BsqBalanceInfo) protos.get(0);
|
||||
|
||||
// Populate columns with bsq balance info.
|
||||
|
||||
colAvailableConfirmedBalance.addRow(balance.getAvailableConfirmedBalance());
|
||||
colUnverifiedBalance.addRow(balance.getUnverifiedBalance());
|
||||
colUnconfirmedChangeBalance.addRow(balance.getUnconfirmedChangeBalance());
|
||||
colLockedForVotingBalance.addRow(balance.getLockedForVotingBalance());
|
||||
colLockupBondsBalance.addRow(balance.getLockupBondsBalance());
|
||||
colUnlockingBondsBalance.addRow(balance.getUnlockingBondsBalance());
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
|
||||
return new Table(colAvailableConfirmedBalance.asStringColumn(),
|
||||
colUnverifiedBalance.asStringColumn(),
|
||||
colUnconfirmedChangeBalance.asStringColumn(),
|
||||
colLockedForVotingBalance.asStringColumn(),
|
||||
colLockupBondsBalance.asStringColumn(),
|
||||
colUnlockingBondsBalance.asStringColumn());
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.cli.table.builder;
|
||||
|
||||
import bisq.proto.grpc.BtcBalanceInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_AVAILABLE_BALANCE;
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_LOCKED_BALANCE;
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_RESERVED_BALANCE;
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_TOTAL_AVAILABLE_BALANCE;
|
||||
import static bisq.cli.table.builder.TableType.BTC_BALANCE_TBL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.Table;
|
||||
import bisq.cli.table.column.Column;
|
||||
import bisq.cli.table.column.SatoshiColumn;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.cli.table.Table} from a
|
||||
* {@code bisq.proto.grpc.BtcBalanceInfo} object.
|
||||
*/
|
||||
class BtcBalanceTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
// Default columns not dynamically generated with btc balance info.
|
||||
private final Column<Long> colAvailableBalance;
|
||||
private final Column<Long> colReservedBalance;
|
||||
private final Column<Long> colTotalAvailableBalance;
|
||||
private final Column<Long> colLockedBalance;
|
||||
|
||||
BtcBalanceTableBuilder(List<?> protos) {
|
||||
super(BTC_BALANCE_TBL, protos);
|
||||
this.colAvailableBalance = new SatoshiColumn(COL_HEADER_AVAILABLE_BALANCE);
|
||||
this.colReservedBalance = new SatoshiColumn(COL_HEADER_RESERVED_BALANCE);
|
||||
this.colTotalAvailableBalance = new SatoshiColumn(COL_HEADER_TOTAL_AVAILABLE_BALANCE);
|
||||
this.colLockedBalance = new SatoshiColumn(COL_HEADER_LOCKED_BALANCE);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
BtcBalanceInfo balance = (BtcBalanceInfo) protos.get(0);
|
||||
|
||||
// Populate columns with btc balance info.
|
||||
|
||||
colAvailableBalance.addRow(balance.getAvailableBalance());
|
||||
colReservedBalance.addRow(balance.getReservedBalance());
|
||||
colTotalAvailableBalance.addRow(balance.getTotalAvailableBalance());
|
||||
colLockedBalance.addRow(balance.getLockedBalance());
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
|
||||
return new Table(colAvailableBalance.asStringColumn(),
|
||||
colReservedBalance.asStringColumn(),
|
||||
colTotalAvailableBalance.asStringColumn(),
|
||||
colLockedBalance.asStringColumn());
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package bisq.cli.table.builder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.cli.table.builder.TableType.FAILED_TRADE_TBL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.Table;
|
||||
import bisq.cli.table.column.MixedPriceColumn;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.cli.table.Table} from a list of {@code bisq.proto.grpc.TradeInfo} objects.
|
||||
*/
|
||||
class FailedTradeTableBuilder extends AbstractTradeListBuilder {
|
||||
|
||||
FailedTradeTableBuilder(List<?> protos) {
|
||||
super(FAILED_TRADE_TBL, protos);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
populateColumns();
|
||||
return new Table(colTradeId,
|
||||
colCreateDate.asStringColumn(),
|
||||
colMarket,
|
||||
colPrice.asStringColumn(),
|
||||
colAmountInBtc.asStringColumn(),
|
||||
colMixedAmount.asStringColumn(),
|
||||
colCurrency,
|
||||
colOfferType,
|
||||
colRole,
|
||||
colStatusDescription);
|
||||
}
|
||||
|
||||
private void populateColumns() {
|
||||
trades.stream().forEachOrdered(t -> {
|
||||
colTradeId.addRow(t.getTradeId());
|
||||
colCreateDate.addRow(t.getDate());
|
||||
colMarket.addRow(toMarket.apply(t));
|
||||
((MixedPriceColumn) colPrice).addRow(t.getTradePrice(), isFiatTrade.test(t));
|
||||
colAmountInBtc.addRow(t.getTradeAmountAsLong());
|
||||
colMixedAmount.addRow(t.getTradeVolume(), toDisplayedVolumePrecision.apply(t));
|
||||
colCurrency.addRow(toPaymentCurrencyCode.apply(t));
|
||||
colOfferType.addRow(toOfferType.apply(t));
|
||||
colRole.addRow(t.getRole());
|
||||
colStatusDescription.addRow("Failed");
|
||||
});
|
||||
}
|
||||
}
|
280
cli/src/main/java/bisq/cli/table/builder/OfferTableBuilder.java
Normal file
280
cli/src/main/java/bisq/cli/table/builder/OfferTableBuilder.java
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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.cli.table.builder;
|
||||
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.*;
|
||||
import static bisq.cli.table.builder.TableType.OFFER_TBL;
|
||||
import static bisq.cli.table.column.AltcoinColumn.DISPLAY_MODE.ALTCOIN_OFFER_VOLUME;
|
||||
import static bisq.cli.table.column.AltcoinColumn.DISPLAY_MODE.ALTCOIN_TRIGGER_PRICE;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.LEFT;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.NONE;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.TRIGGER_PRICE;
|
||||
import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.VOLUME;
|
||||
import static bisq.cli.table.column.ZippedStringColumns.DUPLICATION_MODE.EXCLUDE_DUPLICATES;
|
||||
import static java.lang.String.format;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
import static protobuf.OfferDirection.SELL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.Table;
|
||||
import bisq.cli.table.column.AltcoinColumn;
|
||||
import bisq.cli.table.column.Column;
|
||||
import bisq.cli.table.column.FiatColumn;
|
||||
import bisq.cli.table.column.Iso8601DateTimeColumn;
|
||||
import bisq.cli.table.column.SatoshiColumn;
|
||||
import bisq.cli.table.column.StringColumn;
|
||||
import bisq.cli.table.column.ZippedStringColumns;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.cli.table.Table} from a List of
|
||||
* {@code bisq.proto.grpc.OfferInfo} objects.
|
||||
*/
|
||||
class OfferTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
// Columns common to both fiat and cryptocurrency offers.
|
||||
private final Column<String> colOfferId = new StringColumn(COL_HEADER_UUID, LEFT);
|
||||
private final Column<String> colDirection = new StringColumn(COL_HEADER_DIRECTION, LEFT);
|
||||
private final Column<Long> colAmount = new SatoshiColumn("Temp Amount", NONE);
|
||||
private final Column<Long> colMinAmount = new SatoshiColumn("Temp Min Amount", NONE);
|
||||
private final Column<String> colPaymentMethod = new StringColumn(COL_HEADER_PAYMENT_METHOD, LEFT);
|
||||
private final Column<Long> colCreateDate = new Iso8601DateTimeColumn(COL_HEADER_CREATION_DATE);
|
||||
|
||||
OfferTableBuilder(List<?> protos) {
|
||||
super(OFFER_TBL, protos);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
List<OfferInfo> offers = protos.stream().map(p -> (OfferInfo) p).collect(Collectors.toList());
|
||||
return isShowingFiatOffers.get()
|
||||
? buildFiatOfferTable(offers)
|
||||
: buildCryptoCurrencyOfferTable(offers);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public Table buildFiatOfferTable(List<OfferInfo> offers) {
|
||||
@Nullable
|
||||
Column<String> colEnabled = enabledColumn.get(); // Not boolean: YES, NO, or PENDING
|
||||
Column<Long> colFiatPrice = new FiatColumn(format(COL_HEADER_DETAILED_PRICE, fiatTradeCurrency.get()));
|
||||
Column<Long> colFiatVolume = new FiatColumn(format("Temp Volume (%s)", fiatTradeCurrency.get()), NONE, VOLUME);
|
||||
Column<Long> colMinFiatVolume = new FiatColumn(format("Temp Min Volume (%s)", fiatTradeCurrency.get()), NONE, VOLUME);
|
||||
@Nullable
|
||||
Column<Long> colTriggerPrice = fiatTriggerPriceColumn.get();
|
||||
|
||||
// Populate columns with offer info.
|
||||
|
||||
//noinspection SimplifyStreamApiCallChains
|
||||
offers.stream().forEachOrdered(o -> {
|
||||
if (colEnabled != null)
|
||||
colEnabled.addRow(toEnabled.apply(o));
|
||||
|
||||
colDirection.addRow(o.getDirection());
|
||||
colFiatPrice.addRow(o.getPrice());
|
||||
colMinAmount.addRow(o.getMinAmount());
|
||||
colAmount.addRow(o.getAmount());
|
||||
colMinFiatVolume.addRow(o.getMinVolume());
|
||||
colFiatVolume.addRow(o.getVolume());
|
||||
|
||||
if (colTriggerPrice != null)
|
||||
colTriggerPrice.addRow(o.getTriggerPrice());
|
||||
|
||||
colPaymentMethod.addRow(o.getPaymentMethodShortName());
|
||||
colCreateDate.addRow(o.getDate());
|
||||
colOfferId.addRow(o.getId());
|
||||
});
|
||||
|
||||
ZippedStringColumns amountRange = zippedAmountRangeColumns.get();
|
||||
ZippedStringColumns volumeRange =
|
||||
new ZippedStringColumns(format(COL_HEADER_VOLUME_RANGE, fiatTradeCurrency.get()),
|
||||
RIGHT,
|
||||
" - ",
|
||||
colMinFiatVolume.asStringColumn(),
|
||||
colFiatVolume.asStringColumn());
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
|
||||
if (isShowingMyOffers.get()) {
|
||||
return new Table(colEnabled.asStringColumn(),
|
||||
colDirection,
|
||||
colFiatPrice.asStringColumn(),
|
||||
amountRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
volumeRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
colTriggerPrice.asStringColumn(),
|
||||
colPaymentMethod,
|
||||
colCreateDate.asStringColumn(),
|
||||
colOfferId);
|
||||
} else {
|
||||
return new Table(colDirection,
|
||||
colFiatPrice.asStringColumn(),
|
||||
amountRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
volumeRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
colPaymentMethod,
|
||||
colCreateDate.asStringColumn(),
|
||||
colOfferId);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public Table buildCryptoCurrencyOfferTable(List<OfferInfo> offers) {
|
||||
@Nullable
|
||||
Column<String> colEnabled = enabledColumn.get(); // Not boolean: YES, NO, or PENDING
|
||||
Column<Long> colBtcPrice = new SatoshiColumn(format(COL_HEADER_DETAILED_PRICE_OF_ALTCOIN, altcoinTradeCurrency.get()));
|
||||
Column<Long> colBtcVolume = new AltcoinColumn(format("Temp Volume (%s)", altcoinTradeCurrency.get()),
|
||||
NONE,
|
||||
ALTCOIN_OFFER_VOLUME);
|
||||
Column<Long> colMinBtcVolume = new AltcoinColumn(format("Temp Min Volume (%s)", altcoinTradeCurrency.get()),
|
||||
NONE,
|
||||
ALTCOIN_OFFER_VOLUME);
|
||||
@Nullable
|
||||
Column<Long> colTriggerPrice = altcoinTriggerPriceColumn.get();
|
||||
|
||||
// Populate columns with offer info.
|
||||
|
||||
//noinspection SimplifyStreamApiCallChains
|
||||
offers.stream().forEachOrdered(o -> {
|
||||
if (colEnabled != null)
|
||||
colEnabled.addRow(toEnabled.apply(o));
|
||||
|
||||
colDirection.addRow(directionFormat.apply(o));
|
||||
colBtcPrice.addRow(o.getPrice());
|
||||
colAmount.addRow(o.getAmount());
|
||||
colMinAmount.addRow(o.getMinAmount());
|
||||
colBtcVolume.addRow(o.getMinVolume());
|
||||
colMinBtcVolume.addRow(o.getVolume());
|
||||
|
||||
if (colTriggerPrice != null)
|
||||
colTriggerPrice.addRow(o.getTriggerPrice());
|
||||
|
||||
colPaymentMethod.addRow(o.getPaymentMethodShortName());
|
||||
colCreateDate.addRow(o.getDate());
|
||||
colOfferId.addRow(o.getId());
|
||||
});
|
||||
|
||||
ZippedStringColumns amountRange = zippedAmountRangeColumns.get();
|
||||
ZippedStringColumns volumeRange =
|
||||
new ZippedStringColumns(format(COL_HEADER_VOLUME_RANGE, altcoinTradeCurrency.get()),
|
||||
RIGHT,
|
||||
" - ",
|
||||
colBtcVolume.asStringColumn(),
|
||||
colMinBtcVolume.asStringColumn());
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
|
||||
if (isShowingMyOffers.get()) {
|
||||
if (isShowingBsqOffers.get()) {
|
||||
return new Table(colEnabled.asStringColumn(),
|
||||
colDirection,
|
||||
colBtcPrice.asStringColumn(),
|
||||
amountRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
volumeRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
colPaymentMethod,
|
||||
colCreateDate.asStringColumn(),
|
||||
colOfferId);
|
||||
} else {
|
||||
return new Table(colEnabled.asStringColumn(),
|
||||
colDirection,
|
||||
colBtcPrice.asStringColumn(),
|
||||
amountRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
volumeRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
colTriggerPrice.asStringColumn(),
|
||||
colPaymentMethod,
|
||||
colCreateDate.asStringColumn(),
|
||||
colOfferId);
|
||||
}
|
||||
} else {
|
||||
return new Table(colDirection,
|
||||
colBtcPrice.asStringColumn(),
|
||||
amountRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
volumeRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
colPaymentMethod,
|
||||
colCreateDate.asStringColumn(),
|
||||
colOfferId);
|
||||
}
|
||||
}
|
||||
|
||||
private final Supplier<OfferInfo> firstOfferInList = () -> (OfferInfo) protos.get(0);
|
||||
private final Supplier<Boolean> isShowingMyOffers = () -> firstOfferInList.get().getIsMyOffer();
|
||||
private final Supplier<Boolean> isShowingFiatOffers = () -> isFiatOffer.test(firstOfferInList.get());
|
||||
private final Supplier<String> fiatTradeCurrency = () -> firstOfferInList.get().getCounterCurrencyCode();
|
||||
private final Supplier<String> altcoinTradeCurrency = () -> firstOfferInList.get().getBaseCurrencyCode();
|
||||
private final Supplier<Boolean> isShowingBsqOffers = () ->
|
||||
!isFiatOffer.test(firstOfferInList.get()) && altcoinTradeCurrency.get().equals("BSQ");
|
||||
|
||||
@Nullable // Not a boolean column: YES, NO, or PENDING.
|
||||
private final Supplier<StringColumn> enabledColumn = () ->
|
||||
isShowingMyOffers.get()
|
||||
? new StringColumn(COL_HEADER_ENABLED, LEFT)
|
||||
: null;
|
||||
@Nullable
|
||||
private final Supplier<FiatColumn> fiatTriggerPriceColumn = () ->
|
||||
isShowingMyOffers.get()
|
||||
? new FiatColumn(format(COL_HEADER_TRIGGER_PRICE, fiatTradeCurrency.get()), RIGHT, TRIGGER_PRICE)
|
||||
: null;
|
||||
@Nullable
|
||||
private final Supplier<AltcoinColumn> altcoinTriggerPriceColumn = () ->
|
||||
isShowingMyOffers.get() && !isShowingBsqOffers.get()
|
||||
? new AltcoinColumn(format(COL_HEADER_TRIGGER_PRICE, altcoinTradeCurrency.get()), RIGHT, ALTCOIN_TRIGGER_PRICE)
|
||||
: null;
|
||||
|
||||
private final Function<OfferInfo, String> toEnabled = (o) -> {
|
||||
if (o.getIsMyOffer() && o.getIsMyPendingOffer())
|
||||
return "PENDING";
|
||||
else
|
||||
return o.getIsActivated() ? "YES" : "NO";
|
||||
};
|
||||
|
||||
private final Function<String, String> toMirroredDirection = (d) ->
|
||||
d.equalsIgnoreCase(BUY.name()) ? SELL.name() : BUY.name();
|
||||
|
||||
private final Function<OfferInfo, String> directionFormat = (o) -> {
|
||||
if (isFiatOffer.test(o)) {
|
||||
return o.getBaseCurrencyCode();
|
||||
} else {
|
||||
// Return "Sell BSQ (Buy BTC)", or "Buy BSQ (Sell BTC)".
|
||||
String direction = o.getDirection();
|
||||
String mirroredDirection = toMirroredDirection.apply(direction);
|
||||
Function<String, String> mixedCase = (word) -> word.charAt(0) + word.substring(1).toLowerCase();
|
||||
return format("%s %s (%s %s)",
|
||||
mixedCase.apply(mirroredDirection),
|
||||
o.getBaseCurrencyCode(),
|
||||
mixedCase.apply(direction),
|
||||
o.getCounterCurrencyCode());
|
||||
}
|
||||
};
|
||||
|
||||
private final Supplier<ZippedStringColumns> zippedAmountRangeColumns = () -> {
|
||||
if (colMinAmount.isEmpty() || colAmount.isEmpty())
|
||||
throw new IllegalStateException("amount columns must have data");
|
||||
|
||||
return new ZippedStringColumns(COL_HEADER_AMOUNT_RANGE,
|
||||
RIGHT,
|
||||
" - ",
|
||||
colMinAmount.asStringColumn(),
|
||||
colAmount.asStringColumn());
|
||||
};
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package bisq.cli.table.builder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.cli.table.builder.TableType.OPEN_TRADE_TBL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.Table;
|
||||
import bisq.cli.table.column.MixedPriceColumn;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.cli.table.Table} from a list of {@code bisq.proto.grpc.TradeInfo} objects.
|
||||
*/
|
||||
class OpenTradeTableBuilder extends AbstractTradeListBuilder {
|
||||
|
||||
OpenTradeTableBuilder(List<?> protos) {
|
||||
super(OPEN_TRADE_TBL, protos);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
populateColumns();
|
||||
return new Table(colTradeId,
|
||||
colCreateDate.asStringColumn(),
|
||||
colMarket,
|
||||
colPrice.asStringColumn(),
|
||||
colAmountInBtc.asStringColumn(),
|
||||
colMixedAmount.asStringColumn(),
|
||||
colCurrency,
|
||||
colPaymentMethod,
|
||||
colRole);
|
||||
}
|
||||
|
||||
private void populateColumns() {
|
||||
trades.stream().forEachOrdered(t -> {
|
||||
colTradeId.addRow(t.getTradeId());
|
||||
colCreateDate.addRow(t.getDate());
|
||||
colMarket.addRow(toMarket.apply(t));
|
||||
((MixedPriceColumn) colPrice).addRow(t.getTradePrice(), isFiatTrade.test(t));
|
||||
colAmountInBtc.addRow(t.getTradeAmountAsLong());
|
||||
colMixedAmount.addRow(t.getTradeVolume(), toDisplayedVolumePrecision.apply(t));
|
||||
colCurrency.addRow(toPaymentCurrencyCode.apply(t));
|
||||
colPaymentMethod.addRow(t.getOffer().getPaymentMethodShortName());
|
||||
colRole.addRow(t.getRole());
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.cli.table.builder;
|
||||
|
||||
import protobuf.PaymentAccount;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_CURRENCY;
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_NAME;
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_PAYMENT_METHOD;
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_UUID;
|
||||
import static bisq.cli.table.builder.TableType.PAYMENT_ACCOUNT_TBL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.Table;
|
||||
import bisq.cli.table.column.Column;
|
||||
import bisq.cli.table.column.StringColumn;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.cli.table.Table} from a List of
|
||||
* {@code protobuf.PaymentAccount} objects.
|
||||
*/
|
||||
class PaymentAccountTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
// Default columns not dynamically generated with payment account info.
|
||||
private final Column<String> colName;
|
||||
private final Column<String> colCurrency;
|
||||
private final Column<String> colPaymentMethod;
|
||||
private final Column<String> colId;
|
||||
|
||||
PaymentAccountTableBuilder(List<?> protos) {
|
||||
super(PAYMENT_ACCOUNT_TBL, protos);
|
||||
this.colName = new StringColumn(COL_HEADER_NAME);
|
||||
this.colCurrency = new StringColumn(COL_HEADER_CURRENCY);
|
||||
this.colPaymentMethod = new StringColumn(COL_HEADER_PAYMENT_METHOD);
|
||||
this.colId = new StringColumn(COL_HEADER_UUID);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
List<PaymentAccount> paymentAccounts = protos.stream()
|
||||
.map(a -> (PaymentAccount) a)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Populate columns with payment account info.
|
||||
//noinspection SimplifyStreamApiCallChains
|
||||
paymentAccounts.stream().forEachOrdered(a -> {
|
||||
colName.addRow(a.getAccountName());
|
||||
colCurrency.addRow(a.getSelectedTradeCurrency().getCode());
|
||||
colPaymentMethod.addRow(a.getPaymentMethod().getId());
|
||||
colId.addRow(a.getId());
|
||||
});
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
return new Table(colName, colCurrency, colPaymentMethod, colId);
|
||||
}
|
||||
}
|
70
cli/src/main/java/bisq/cli/table/builder/TableBuilder.java
Normal file
70
cli/src/main/java/bisq/cli/table/builder/TableBuilder.java
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.cli.table.builder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.Table;
|
||||
|
||||
/**
|
||||
* Table builder factory. It is not conventionally named TableBuilderFactory because
|
||||
* it has no static factory methods. The number of static fields and methods in the
|
||||
* {@code bisq.cli.table} are kept to a minimum in an effort o reduce class load time
|
||||
* in the session-less CLI.
|
||||
*/
|
||||
public class TableBuilder extends AbstractTableBuilder {
|
||||
|
||||
public TableBuilder(TableType tableType, Object proto) {
|
||||
this(tableType, singletonList(proto));
|
||||
}
|
||||
|
||||
public TableBuilder(TableType tableType, List<?> protos) {
|
||||
super(tableType, protos);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
switch (tableType) {
|
||||
case ADDRESS_BALANCE_TBL:
|
||||
return new AddressBalanceTableBuilder(protos).build();
|
||||
case BSQ_BALANCE_TBL:
|
||||
return new BsqBalanceTableBuilder(protos).build();
|
||||
case BTC_BALANCE_TBL:
|
||||
return new BtcBalanceTableBuilder(protos).build();
|
||||
case CLOSED_TRADE_TBL:
|
||||
throw new UnsupportedOperationException("TODO return new ClosedTradeTableBuilder(protos).build()");
|
||||
case FAILED_TRADE_TBL:
|
||||
throw new UnsupportedOperationException("TODO return new FailedTradeTableBuilder(protos).build()");
|
||||
case OFFER_TBL:
|
||||
return new OfferTableBuilder(protos).build();
|
||||
case OPEN_TRADE_TBL:
|
||||
throw new UnsupportedOperationException("TODO return new OpenTradeTableBuilder(protos).build()");
|
||||
case PAYMENT_ACCOUNT_TBL:
|
||||
return new PaymentAccountTableBuilder(protos).build();
|
||||
case TRADE_DETAIL_TBL:
|
||||
return new TradeDetailTableBuilder(protos).build();
|
||||
case TRANSACTION_TBL:
|
||||
return new TransactionTableBuilder(protos).build();
|
||||
default:
|
||||
throw new IllegalArgumentException("invalid cli table type " + tableType.name());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.cli.table.builder;
|
||||
|
||||
/**
|
||||
* Table column name constants.
|
||||
*/
|
||||
class TableBuilderConstants {
|
||||
static final String COL_HEADER_ADDRESS = "%-3s Address";
|
||||
static final String COL_HEADER_AMOUNT = "Amount";
|
||||
static final String COL_HEADER_AMOUNT_IN_BTC = "Amount in BTC";
|
||||
static final String COL_HEADER_AMOUNT_RANGE = "BTC(min - max)";
|
||||
static final String COL_HEADER_AVAILABLE_BALANCE = "Available Balance";
|
||||
static final String COL_HEADER_AVAILABLE_CONFIRMED_BALANCE = "Available Confirmed Balance";
|
||||
static final String COL_HEADER_UNCONFIRMED_CHANGE_BALANCE = "Unconfirmed Change Balance";
|
||||
static final String COL_HEADER_RESERVED_BALANCE = "Reserved Balance";
|
||||
static final String COL_HEADER_TOTAL_AVAILABLE_BALANCE = "Total Available Balance";
|
||||
static final String COL_HEADER_LOCKED_BALANCE = "Locked Balance";
|
||||
static final String COL_HEADER_LOCKED_FOR_VOTING_BALANCE = "Locked For Voting Balance";
|
||||
static final String COL_HEADER_LOCKUP_BONDS_BALANCE = "Lockup Bonds Balance";
|
||||
static final String COL_HEADER_UNLOCKING_BONDS_BALANCE = "Unlocking Bonds Balance";
|
||||
static final String COL_HEADER_UNVERIFIED_BALANCE = "Unverified Balance";
|
||||
static final String COL_HEADER_BUYER_DEPOSIT = "Buyer Deposit";
|
||||
static final String COL_HEADER_SELLER_DEPOSIT = "Seller Deposit";
|
||||
static final String COL_HEADER_CONFIRMATIONS = "Confirmations";
|
||||
static final String COL_HEADER_DEVIATION = "Deviation";
|
||||
static final String COL_HEADER_IS_USED_ADDRESS = "Is Used";
|
||||
static final String COL_HEADER_CREATION_DATE = "Creation Date (UTC)";
|
||||
static final String COL_HEADER_CURRENCY = "Currency";
|
||||
static final String COL_HEADER_DATE_TIME = "Date/Time (UTC)";
|
||||
static final String COL_HEADER_DETAILED_AMOUNT = "Amount(%-3s)";
|
||||
static final String COL_HEADER_DETAILED_PRICE = "Price in %-3s for 1 BTC";
|
||||
static final String COL_HEADER_DETAILED_PRICE_OF_ALTCOIN = "Price in BTC for 1 %-3s";
|
||||
static final String COL_HEADER_DIRECTION = "Buy/Sell";
|
||||
static final String COL_HEADER_ENABLED = "Enabled";
|
||||
static final String COL_HEADER_MARKET = "Market";
|
||||
static final String COL_HEADER_NAME = "Name";
|
||||
static final String COL_HEADER_OFFER_TYPE = "Offer Type";
|
||||
static final String COL_HEADER_PAYMENT_METHOD = "Payment Method";
|
||||
static final String COL_HEADER_PRICE = "Price";
|
||||
static final String COL_HEADER_STATUS = "Status";
|
||||
static final String COL_HEADER_TRADE_ALTCOIN_BUYER_ADDRESS = "%-3s Buyer Address";
|
||||
static final String COL_HEADER_TRADE_BUYER_COST = "Buyer Cost(%-3s)";
|
||||
static final String COL_HEADER_TRADE_DEPOSIT_CONFIRMED = "Deposit Confirmed";
|
||||
static final String COL_HEADER_TRADE_DEPOSIT_PUBLISHED = "Deposit Published";
|
||||
static final String COL_HEADER_TRADE_PAYMENT_SENT = "%-3s Sent";
|
||||
static final String COL_HEADER_TRADE_PAYMENT_RECEIVED = "%-3s Received";
|
||||
static final String COL_HEADER_TRADE_PAYOUT_PUBLISHED = "Payout Published";
|
||||
static final String COL_HEADER_TRADE_WITHDRAWN = "Withdrawn";
|
||||
static final String COL_HEADER_TRADE_ID = "Trade ID";
|
||||
static final String COL_HEADER_TRADE_ROLE = "My Role";
|
||||
static final String COL_HEADER_TRADE_SHORT_ID = "ID";
|
||||
@Deprecated
|
||||
static final String COL_HEADER_TRADE_TX_FEE = "Tx Fee(BTC)";
|
||||
static final String COL_HEADER_TRADE_MAKER_FEE = "Maker Fee(%-3s)";
|
||||
static final String COL_HEADER_TRADE_TAKER_FEE = "Taker Fee(%-3s)";
|
||||
static final String COL_HEADER_TRADE_FEE = "Trade Fee";
|
||||
static final String COL_HEADER_TRIGGER_PRICE = "Trigger Price(%-3s)";
|
||||
static final String COL_HEADER_TX_ID = "Tx ID";
|
||||
static final String COL_HEADER_TX_INPUT_SUM = "Tx Inputs (BTC)";
|
||||
static final String COL_HEADER_TX_OUTPUT_SUM = "Tx Outputs (BTC)";
|
||||
static final String COL_HEADER_TX_FEE = "Tx Fee (BTC)";
|
||||
static final String COL_HEADER_TX_SIZE = "Tx Size (Bytes)";
|
||||
static final String COL_HEADER_TX_IS_CONFIRMED = "Is Confirmed";
|
||||
static final String COL_HEADER_TX_MEMO = "Memo";
|
||||
static final String COL_HEADER_VOLUME_RANGE = "%-3s(min - max)";
|
||||
static final String COL_HEADER_UUID = "ID";
|
||||
}
|
35
cli/src/main/java/bisq/cli/table/builder/TableType.java
Normal file
35
cli/src/main/java/bisq/cli/table/builder/TableType.java
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.cli.table.builder;
|
||||
|
||||
/**
|
||||
* Used as param in TableBuilder constructor instead of inspecting
|
||||
* protos to find out what kind of CLI output table should be built.
|
||||
*/
|
||||
public enum TableType {
|
||||
ADDRESS_BALANCE_TBL,
|
||||
BSQ_BALANCE_TBL,
|
||||
BTC_BALANCE_TBL,
|
||||
CLOSED_TRADE_TBL,
|
||||
FAILED_TRADE_TBL,
|
||||
OFFER_TBL,
|
||||
OPEN_TRADE_TBL,
|
||||
PAYMENT_ACCOUNT_TBL,
|
||||
TRADE_DETAIL_TBL,
|
||||
TRANSACTION_TBL
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.cli.table.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.Table;
|
||||
import bisq.cli.table.column.Column;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.cli.table.Table} from a {@code bisq.proto.grpc.TradeInfo} object.
|
||||
*/
|
||||
class TradeDetailTableBuilder extends AbstractTradeListBuilder {
|
||||
|
||||
TradeDetailTableBuilder(List<?> protos) {
|
||||
super(TRADE_DETAIL_TBL, protos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a single row trade detail table.
|
||||
* @return Table containing one row
|
||||
*/
|
||||
public Table build() {
|
||||
populateColumns();
|
||||
List<Column<?>> columns = defineColumnList();
|
||||
return new Table(columns.toArray(new Column<?>[0]));
|
||||
}
|
||||
|
||||
private void populateColumns() {
|
||||
trades.stream().forEachOrdered(t -> {
|
||||
colTradeId.addRow(t.getShortId());
|
||||
colRole.addRow(t.getRole());
|
||||
colPrice.addRow(t.getTradePrice());
|
||||
colAmountInBtc.addRow(toAmount.apply(t));
|
||||
colMinerTxFee.addRow(toMyMinerTxFee.apply(t));
|
||||
colBisqTradeFee.addRow(toMyMakerOrTakerFee.apply(t));
|
||||
colIsDepositPublished.addRow(t.getIsDepositPublished());
|
||||
colIsDepositConfirmed.addRow(t.getIsDepositConfirmed());
|
||||
colTradeCost.addRow(toTradeVolume.apply(t));
|
||||
colIsPaymentSent.addRow(t.getIsFiatSent());
|
||||
colIsPaymentReceived.addRow(t.getIsFiatReceived());
|
||||
colIsPayoutPublished.addRow(t.getIsPayoutPublished());
|
||||
colIsFundsWithdrawn.addRow(t.getIsWithdrawn());
|
||||
|
||||
if (colAltcoinReceiveAddressColumn != null)
|
||||
colAltcoinReceiveAddressColumn.addRow(toAltcoinReceiveAddress.apply(t));
|
||||
});
|
||||
}
|
||||
|
||||
private List<Column<?>> defineColumnList() {
|
||||
List<Column<?>> columns = new ArrayList<>() {{
|
||||
add(colTradeId);
|
||||
add(colRole);
|
||||
add(colPrice.asStringColumn());
|
||||
add(colAmountInBtc.asStringColumn());
|
||||
add(colMinerTxFee.asStringColumn());
|
||||
add(colBisqTradeFee.asStringColumn());
|
||||
add(colIsDepositPublished.asStringColumn());
|
||||
add(colIsDepositConfirmed.asStringColumn());
|
||||
add(colTradeCost.asStringColumn());
|
||||
add(colIsPaymentSent.asStringColumn());
|
||||
add(colIsPaymentReceived.asStringColumn());
|
||||
add(colIsPayoutPublished.asStringColumn());
|
||||
add(colIsFundsWithdrawn.asStringColumn());
|
||||
}};
|
||||
|
||||
if (colAltcoinReceiveAddressColumn != null)
|
||||
columns.add(colAltcoinReceiveAddressColumn);
|
||||
|
||||
return columns;
|
||||
}
|
||||
}
|
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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.cli.table.builder;
|
||||
|
||||
import bisq.proto.grpc.ContractInfo;
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.*;
|
||||
import static bisq.cli.table.builder.TableType.CLOSED_TRADE_TBL;
|
||||
import static bisq.cli.table.builder.TableType.FAILED_TRADE_TBL;
|
||||
import static bisq.cli.table.builder.TableType.OPEN_TRADE_TBL;
|
||||
import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL;
|
||||
import static bisq.cli.table.column.AltcoinColumn.DISPLAY_MODE.ALTCOIN_OFFER_VOLUME;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.LEFT;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.VOLUME;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.column.AltcoinColumn;
|
||||
import bisq.cli.table.column.BooleanColumn;
|
||||
import bisq.cli.table.column.BtcColumn;
|
||||
import bisq.cli.table.column.Column;
|
||||
import bisq.cli.table.column.FiatColumn;
|
||||
import bisq.cli.table.column.Iso8601DateTimeColumn;
|
||||
import bisq.cli.table.column.MixedPriceColumn;
|
||||
import bisq.cli.table.column.MixedTradeFeeColumn;
|
||||
import bisq.cli.table.column.MixedVolumeColumn;
|
||||
import bisq.cli.table.column.SatoshiColumn;
|
||||
import bisq.cli.table.column.StringColumn;
|
||||
|
||||
/**
|
||||
* Convenience for supplying column definitions to
|
||||
* open/closed/failed/detail trade table builders.
|
||||
*/
|
||||
@Slf4j
|
||||
class TradeTableColumnSupplier {
|
||||
|
||||
@Getter
|
||||
private final TableType tableType;
|
||||
@Getter
|
||||
private final List<TradeInfo> trades;
|
||||
|
||||
public TradeTableColumnSupplier(TableType tableType, List<TradeInfo> trades) {
|
||||
this.tableType = tableType;
|
||||
this.trades = trades;
|
||||
}
|
||||
|
||||
private final Supplier<Boolean> isTradeDetailTblBuilder = () -> getTableType().equals(TRADE_DETAIL_TBL);
|
||||
private final Supplier<Boolean> isOpenTradeTblBuilder = () -> getTableType().equals(OPEN_TRADE_TBL);
|
||||
private final Supplier<Boolean> isClosedTradeTblBuilder = () -> getTableType().equals(CLOSED_TRADE_TBL);
|
||||
private final Supplier<Boolean> isFailedTradeTblBuilder = () -> getTableType().equals(FAILED_TRADE_TBL);
|
||||
private final Supplier<TradeInfo> firstRow = () -> getTrades().get(0);
|
||||
private final Predicate<OfferInfo> isFiatOffer = (o) -> o.getBaseCurrencyCode().equals("BTC");
|
||||
private final Predicate<TradeInfo> isFiatTrade = (t) -> isFiatOffer.test(t.getOffer());
|
||||
private final Predicate<TradeInfo> isTaker = (t) -> t.getRole().toLowerCase().contains("taker");
|
||||
|
||||
final Supplier<StringColumn> tradeIdColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? new StringColumn(COL_HEADER_TRADE_SHORT_ID)
|
||||
: new StringColumn(COL_HEADER_TRADE_ID);
|
||||
|
||||
final Supplier<Iso8601DateTimeColumn> createDateColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new Iso8601DateTimeColumn(COL_HEADER_DATE_TIME);
|
||||
|
||||
final Supplier<StringColumn> marketColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_MARKET);
|
||||
|
||||
private final Function<TradeInfo, Column<Long>> toDetailedPriceColumn = (t) -> {
|
||||
String colHeader = isFiatTrade.test(t)
|
||||
? String.format(COL_HEADER_DETAILED_PRICE, t.getOffer().getCounterCurrencyCode())
|
||||
: String.format(COL_HEADER_DETAILED_PRICE_OF_ALTCOIN, t.getOffer().getBaseCurrencyCode());
|
||||
return isFiatTrade.test(t)
|
||||
? new FiatColumn(colHeader)
|
||||
: new AltcoinColumn(colHeader);
|
||||
};
|
||||
|
||||
final Supplier<Column<Long>> priceColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? toDetailedPriceColumn.apply(firstRow.get())
|
||||
: new MixedPriceColumn(COL_HEADER_PRICE);
|
||||
|
||||
final Supplier<Column<String>> priceDeviationColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_DEVIATION, RIGHT);
|
||||
|
||||
final Supplier<StringColumn> currencyColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_CURRENCY);
|
||||
|
||||
private final Function<TradeInfo, Column<Long>> toDetailedAmountColumn = (t) -> {
|
||||
String headerCurrencyCode = t.getOffer().getBaseCurrencyCode();
|
||||
String colHeader = String.format(COL_HEADER_DETAILED_AMOUNT, headerCurrencyCode);
|
||||
return isFiatTrade.test(t)
|
||||
? new SatoshiColumn(colHeader)
|
||||
: new AltcoinColumn(colHeader, ALTCOIN_OFFER_VOLUME);
|
||||
};
|
||||
|
||||
final Supplier<Column<Long>> amountInBtcColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? toDetailedAmountColumn.apply(firstRow.get())
|
||||
: new BtcColumn(COL_HEADER_AMOUNT_IN_BTC);
|
||||
|
||||
final Supplier<MixedVolumeColumn> mixedAmountColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new MixedVolumeColumn(COL_HEADER_AMOUNT);
|
||||
|
||||
final Supplier<Column<Long>> minerTxFeeColumn = () -> isTradeDetailTblBuilder.get() || isClosedTradeTblBuilder.get()
|
||||
? new SatoshiColumn(COL_HEADER_TX_FEE)
|
||||
: null;
|
||||
|
||||
final Supplier<MixedTradeFeeColumn> mixedTradeFeeColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new MixedTradeFeeColumn(COL_HEADER_TRADE_FEE);
|
||||
|
||||
final Supplier<StringColumn> paymentMethodColumn = () -> isTradeDetailTblBuilder.get() || isClosedTradeTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_PAYMENT_METHOD, LEFT);
|
||||
|
||||
final Supplier<StringColumn> roleColumn = () ->
|
||||
isTradeDetailTblBuilder.get() || isOpenTradeTblBuilder.get() || isFailedTradeTblBuilder.get()
|
||||
? new StringColumn(COL_HEADER_TRADE_ROLE)
|
||||
: null;
|
||||
|
||||
final Function<String, Column<Long>> toSecurityDepositColumn = (name) -> isClosedTradeTblBuilder.get()
|
||||
? new SatoshiColumn(name)
|
||||
: null;
|
||||
|
||||
final Supplier<StringColumn> offerTypeColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_OFFER_TYPE);
|
||||
|
||||
final Supplier<StringColumn> statusDescriptionColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_STATUS);
|
||||
|
||||
private final Function<String, Column<Boolean>> toBooleanColumn = BooleanColumn::new;
|
||||
|
||||
final Supplier<Column<Boolean>> depositPublishedColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_PUBLISHED)
|
||||
: null;
|
||||
|
||||
final Supplier<Column<Boolean>> depositConfirmedColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_CONFIRMED)
|
||||
: null;
|
||||
|
||||
final Supplier<Column<Boolean>> payoutPublishedColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? toBooleanColumn.apply(COL_HEADER_TRADE_PAYOUT_PUBLISHED)
|
||||
: null;
|
||||
|
||||
final Supplier<Column<Boolean>> fundsWithdrawnColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? toBooleanColumn.apply(COL_HEADER_TRADE_WITHDRAWN)
|
||||
: null;
|
||||
|
||||
final Supplier<Column<Long>> bisqTradeDetailFeeColumn = () -> {
|
||||
if (isTradeDetailTblBuilder.get()) {
|
||||
TradeInfo t = firstRow.get();
|
||||
String headerCurrencyCode = isTaker.test(t)
|
||||
? t.getIsCurrencyForTakerFeeBtc() ? "BTC" : "BSQ"
|
||||
: t.getOffer().getIsCurrencyForMakerFeeBtc() ? "BTC" : "BSQ";
|
||||
String colHeader = isTaker.test(t)
|
||||
? String.format(COL_HEADER_TRADE_TAKER_FEE, headerCurrencyCode)
|
||||
: String.format(COL_HEADER_TRADE_MAKER_FEE, headerCurrencyCode);
|
||||
boolean isBsqSatoshis = headerCurrencyCode.equals("BSQ");
|
||||
return new SatoshiColumn(colHeader, isBsqSatoshis);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
final Function<TradeInfo, String> toPaymentCurrencyCode = (t) ->
|
||||
isFiatTrade.test(t)
|
||||
? t.getOffer().getCounterCurrencyCode()
|
||||
: t.getOffer().getBaseCurrencyCode();
|
||||
|
||||
final Supplier<Column<Boolean>> paymentSentColumn = () -> {
|
||||
if (isTradeDetailTblBuilder.get()) {
|
||||
String headerCurrencyCode = toPaymentCurrencyCode.apply(firstRow.get());
|
||||
String colHeader = String.format(COL_HEADER_TRADE_PAYMENT_SENT, headerCurrencyCode);
|
||||
return new BooleanColumn(colHeader);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
final Supplier<Column<Boolean>> paymentReceivedColumn = () -> {
|
||||
if (isTradeDetailTblBuilder.get()) {
|
||||
String headerCurrencyCode = toPaymentCurrencyCode.apply(firstRow.get());
|
||||
String colHeader = String.format(COL_HEADER_TRADE_PAYMENT_RECEIVED, headerCurrencyCode);
|
||||
return new BooleanColumn(colHeader);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
final Supplier<Column<Long>> tradeCostColumn = () -> {
|
||||
if (isTradeDetailTblBuilder.get()) {
|
||||
TradeInfo t = firstRow.get();
|
||||
String headerCurrencyCode = t.getOffer().getCounterCurrencyCode();
|
||||
String colHeader = String.format(COL_HEADER_TRADE_BUYER_COST, headerCurrencyCode);
|
||||
return isFiatTrade.test(t)
|
||||
? new FiatColumn(colHeader, VOLUME)
|
||||
: new SatoshiColumn(colHeader);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
final Predicate<TradeInfo> showAltCoinBuyerAddress = (t) -> {
|
||||
if (isFiatTrade.test(t)) {
|
||||
return false;
|
||||
} else {
|
||||
ContractInfo contract = t.getContract();
|
||||
boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker();
|
||||
if (isTaker.test(t)) {
|
||||
return !isBuyerMakerAndSellerTaker;
|
||||
} else {
|
||||
return isBuyerMakerAndSellerTaker;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
final Supplier<Column<String>> altcoinReceiveAddressColumn = () -> {
|
||||
if (isTradeDetailTblBuilder.get()) {
|
||||
TradeInfo t = firstRow.get();
|
||||
if (showAltCoinBuyerAddress.test(t)) {
|
||||
String headerCurrencyCode = toPaymentCurrencyCode.apply(t);
|
||||
String colHeader = String.format(COL_HEADER_TRADE_ALTCOIN_BUYER_ADDRESS, headerCurrencyCode);
|
||||
return new StringColumn(colHeader);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.cli.table.builder;
|
||||
|
||||
import bisq.proto.grpc.TxInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static bisq.cli.table.builder.TableBuilderConstants.*;
|
||||
import static bisq.cli.table.builder.TableType.TRANSACTION_TBL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.Table;
|
||||
import bisq.cli.table.column.BooleanColumn;
|
||||
import bisq.cli.table.column.Column;
|
||||
import bisq.cli.table.column.LongColumn;
|
||||
import bisq.cli.table.column.SatoshiColumn;
|
||||
import bisq.cli.table.column.StringColumn;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.cli.table.Table} from a {@code bisq.proto.grpc.TxInfo} object.
|
||||
*/
|
||||
class TransactionTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
// Default columns not dynamically generated with tx info.
|
||||
private final Column<String> colTxId;
|
||||
private final Column<Boolean> colIsConfirmed;
|
||||
private final Column<Long> colInputSum;
|
||||
private final Column<Long> colOutputSum;
|
||||
private final Column<Long> colTxFee;
|
||||
private final Column<Long> colTxSize;
|
||||
|
||||
TransactionTableBuilder(List<?> protos) {
|
||||
super(TRANSACTION_TBL, protos);
|
||||
this.colTxId = new StringColumn(COL_HEADER_TX_ID);
|
||||
this.colIsConfirmed = new BooleanColumn(COL_HEADER_TX_IS_CONFIRMED);
|
||||
this.colInputSum = new SatoshiColumn(COL_HEADER_TX_INPUT_SUM);
|
||||
this.colOutputSum = new SatoshiColumn(COL_HEADER_TX_OUTPUT_SUM);
|
||||
this.colTxFee = new SatoshiColumn(COL_HEADER_TX_FEE);
|
||||
this.colTxSize = new LongColumn(COL_HEADER_TX_SIZE);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
// TODO Add 'gettransactions' api method & show multiple tx in the console.
|
||||
// For now, a tx tbl is only one row.
|
||||
TxInfo tx = (TxInfo) protos.get(0);
|
||||
|
||||
// Declare the columns derived from tx info.
|
||||
|
||||
@Nullable
|
||||
Column<String> colMemo = tx.getMemo().isEmpty()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_TX_MEMO);
|
||||
|
||||
// Populate columns with tx info.
|
||||
|
||||
colTxId.addRow(tx.getTxId());
|
||||
colIsConfirmed.addRow(!tx.getIsPending());
|
||||
colInputSum.addRow(tx.getInputSum());
|
||||
colOutputSum.addRow(tx.getOutputSum());
|
||||
colTxFee.addRow(tx.getFee());
|
||||
colTxSize.addRow((long) tx.getSize());
|
||||
if (colMemo != null)
|
||||
colMemo.addRow(tx.getMemo());
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
|
||||
if (colMemo != null) {
|
||||
return new Table(colTxId,
|
||||
colIsConfirmed.asStringColumn(),
|
||||
colInputSum.asStringColumn(),
|
||||
colOutputSum.asStringColumn(),
|
||||
colTxFee.asStringColumn(),
|
||||
colTxSize.asStringColumn(),
|
||||
colMemo);
|
||||
} else {
|
||||
return new Table(colTxId,
|
||||
colIsConfirmed.asStringColumn(),
|
||||
colInputSum.asStringColumn(),
|
||||
colOutputSum.asStringColumn(),
|
||||
colTxFee.asStringColumn(),
|
||||
colTxSize.asStringColumn());
|
||||
}
|
||||
}
|
||||
}
|
86
cli/src/main/java/bisq/cli/table/column/AbstractColumn.java
Normal file
86
cli/src/main/java/bisq/cli/table/column/AbstractColumn.java
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
|
||||
/**
|
||||
* Partial implementation of the {@link Column} interface.
|
||||
*/
|
||||
abstract class AbstractColumn<C extends Column<T>, T> implements Column<T> {
|
||||
|
||||
// We create an encapsulated StringColumn up front to populate with formatted
|
||||
// strings in each this.addRow(Long value) call. But we will not know how
|
||||
// to justify the cached, formatted string until the column is fully populated.
|
||||
protected final StringColumn stringColumn;
|
||||
|
||||
// The name field is not final, so it can be re-set for column alignment.
|
||||
protected String name;
|
||||
protected final JUSTIFICATION justification;
|
||||
// The max width is not known until after column is fully populated.
|
||||
protected int maxWidth;
|
||||
|
||||
public AbstractColumn(String name, JUSTIFICATION justification) {
|
||||
this.name = name;
|
||||
this.justification = justification;
|
||||
this.stringColumn = this instanceof StringColumn ? null : new StringColumn(name, justification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JUSTIFICATION getJustification() {
|
||||
return this.justification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Column<T> justify() {
|
||||
if (this instanceof StringColumn && this.justification.equals(RIGHT))
|
||||
return this.justify();
|
||||
else
|
||||
return this; // no-op
|
||||
}
|
||||
|
||||
protected final String toJustifiedString(String s) {
|
||||
switch (justification) {
|
||||
case LEFT:
|
||||
return padEnd(s, maxWidth, ' ');
|
||||
case RIGHT:
|
||||
return padStart(s, maxWidth, ' ');
|
||||
case NONE:
|
||||
default:
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
98
cli/src/main/java/bisq/cli/table/column/AltcoinColumn.java
Normal file
98
cli/src/main/java/bisq/cli/table/column/AltcoinColumn.java
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.formatCryptoCurrencyPrice;
|
||||
import static bisq.cli.CurrencyFormat.formatCryptoCurrencyVolume;
|
||||
import static bisq.cli.table.column.AltcoinColumn.DISPLAY_MODE.ALTCOIN_PRICE;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying altcoin values as volume, price, or optional trigger price
|
||||
* with appropriate precision.
|
||||
*/
|
||||
public class AltcoinColumn extends LongColumn {
|
||||
|
||||
public enum DISPLAY_MODE {
|
||||
ALTCOIN_OFFER_VOLUME,
|
||||
ALTCOIN_PRICE,
|
||||
ALTCOIN_TRIGGER_PRICE
|
||||
}
|
||||
|
||||
private final DISPLAY_MODE displayMode;
|
||||
|
||||
// The default AltcoinColumn JUSTIFICATION is RIGHT.
|
||||
// The default AltcoinColumn DISPLAY_MODE is ALTCOIN_PRICE.
|
||||
public AltcoinColumn(String name) {
|
||||
this(name, RIGHT, ALTCOIN_PRICE);
|
||||
}
|
||||
|
||||
public AltcoinColumn(String name, DISPLAY_MODE displayMode) {
|
||||
this(name, RIGHT, displayMode);
|
||||
}
|
||||
|
||||
public AltcoinColumn(String name,
|
||||
JUSTIFICATION justification,
|
||||
DISPLAY_MODE displayMode) {
|
||||
super(name, justification);
|
||||
this.displayMode = displayMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
rows.add(value);
|
||||
|
||||
String s = toFormattedString.apply(value, displayMode);
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return toFormattedString.apply(getRow(rowIndex), displayMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
// We cached the formatted altcoin value strings, but we did
|
||||
// not know how much padding each string needed until now.
|
||||
IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> {
|
||||
String unjustified = stringColumn.getRow(rowIndex);
|
||||
String justified = stringColumn.toJustifiedString(unjustified);
|
||||
stringColumn.updateRow(rowIndex, justified);
|
||||
});
|
||||
return this.stringColumn;
|
||||
}
|
||||
|
||||
private final BiFunction<Long, DISPLAY_MODE, String> toFormattedString = (value, displayMode) -> {
|
||||
switch (displayMode) {
|
||||
case ALTCOIN_OFFER_VOLUME:
|
||||
return value > 0 ? formatCryptoCurrencyVolume(value) : "";
|
||||
case ALTCOIN_PRICE:
|
||||
case ALTCOIN_TRIGGER_PRICE:
|
||||
return value > 0 ? formatCryptoCurrencyPrice(value) : "";
|
||||
default:
|
||||
throw new IllegalStateException("invalid display mode: " + displayMode);
|
||||
}
|
||||
};
|
||||
}
|
131
cli/src/main/java/bisq/cli/table/column/BooleanColumn.java
Normal file
131
cli/src/main/java/bisq/cli/table/column/BooleanColumn.java
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.LEFT;
|
||||
|
||||
/**
|
||||
* For displaying boolean values as YES, NO, or user's choice for 'true' and 'false'.
|
||||
*/
|
||||
public class BooleanColumn extends AbstractColumn<BooleanColumn, Boolean> {
|
||||
|
||||
private static final String DEFAULT_TRUE_AS_STRING = "YES";
|
||||
private static final String DEFAULT_FALSE_AS_STRING = "NO";
|
||||
|
||||
private final List<Boolean> rows = new ArrayList<>();
|
||||
|
||||
private final Predicate<String> isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth;
|
||||
|
||||
private final String trueAsString;
|
||||
private final String falseAsString;
|
||||
|
||||
// The default BooleanColumn JUSTIFICATION is LEFT.
|
||||
// The default BooleanColumn True AsString value is YES.
|
||||
// The default BooleanColumn False AsString value is NO.
|
||||
public BooleanColumn(String name) {
|
||||
this(name, LEFT, DEFAULT_TRUE_AS_STRING, DEFAULT_FALSE_AS_STRING);
|
||||
}
|
||||
|
||||
// Use this constructor to override default LEFT justification.
|
||||
@SuppressWarnings("unused")
|
||||
public BooleanColumn(String name, JUSTIFICATION justification) {
|
||||
this(name, justification, DEFAULT_TRUE_AS_STRING, DEFAULT_FALSE_AS_STRING);
|
||||
}
|
||||
|
||||
// Use this constructor to override default true/false as string defaults.
|
||||
public BooleanColumn(String name, String trueAsString, String falseAsString) {
|
||||
this(name, LEFT, trueAsString, falseAsString);
|
||||
}
|
||||
|
||||
// Use this constructor to override default LEFT justification.
|
||||
public BooleanColumn(String name,
|
||||
JUSTIFICATION justification,
|
||||
String trueAsString,
|
||||
String falseAsString) {
|
||||
super(name, justification);
|
||||
this.trueAsString = trueAsString;
|
||||
this.falseAsString = falseAsString;
|
||||
this.maxWidth = name.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Boolean value) {
|
||||
rows.add(value);
|
||||
|
||||
// We do not know how much padding each StringColumn value needs until it has all the values.
|
||||
String s = asString(value);
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Boolean> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rowCount() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return rows.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getRow(int rowIndex) {
|
||||
return rows.get(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRow(int rowIndex, Boolean newValue) {
|
||||
rows.set(rowIndex, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return getRow(rowIndex)
|
||||
? trueAsString
|
||||
: falseAsString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
// We cached the formatted satoshi strings, but we did
|
||||
// not know how much padding each string needed until now.
|
||||
IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> {
|
||||
String unjustified = stringColumn.getRow(rowIndex);
|
||||
String justified = stringColumn.toJustifiedString(unjustified);
|
||||
stringColumn.updateRow(rowIndex, justified);
|
||||
});
|
||||
return stringColumn;
|
||||
}
|
||||
|
||||
private String asString(boolean value) {
|
||||
return value ? trueAsString : falseAsString;
|
||||
}
|
||||
}
|
48
cli/src/main/java/bisq/cli/table/column/BtcColumn.java
Normal file
48
cli/src/main/java/bisq/cli/table/column/BtcColumn.java
Normal file
@ -0,0 +1,48 @@
|
||||
package bisq.cli.table.column;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.formatBtc;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
import static java.util.Comparator.comparingInt;
|
||||
|
||||
public class BtcColumn extends SatoshiColumn {
|
||||
|
||||
public BtcColumn(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
rows.add(value);
|
||||
|
||||
String s = formatBtc(value);
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return formatBtc(getRow(rowIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
// We cached the formatted satoshi strings, but we did
|
||||
// not know how much zero padding each string needed until now.
|
||||
int maxColumnValueWidth = stringColumn.getRows().stream()
|
||||
.max(comparingInt(String::length))
|
||||
.get()
|
||||
.length();
|
||||
IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> {
|
||||
String btcString = stringColumn.getRow(rowIndex);
|
||||
if (btcString.length() < maxColumnValueWidth) {
|
||||
String paddedBtcString = padEnd(btcString, maxColumnValueWidth, '0');
|
||||
stringColumn.updateRow(rowIndex, paddedBtcString);
|
||||
}
|
||||
});
|
||||
return stringColumn.justify();
|
||||
}
|
||||
}
|
122
cli/src/main/java/bisq/cli/table/column/Column.java
Normal file
122
cli/src/main/java/bisq/cli/table/column/Column.java
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface Column<T> {
|
||||
|
||||
enum JUSTIFICATION {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
NONE
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column's name.
|
||||
*
|
||||
* @return name as String
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Sets the column name.
|
||||
*
|
||||
* @param name of the column
|
||||
*/
|
||||
void setName(String name);
|
||||
|
||||
/**
|
||||
* Add column value.
|
||||
*
|
||||
* @param value added to column's data (row)
|
||||
*/
|
||||
void addRow(T value);
|
||||
|
||||
/**
|
||||
* Returns the column data.
|
||||
*
|
||||
* @return rows as List<T>
|
||||
*/
|
||||
List<T> getRows();
|
||||
|
||||
/**
|
||||
* Returns the maximum width of the column name, or longest,
|
||||
* formatted string value -- whichever is greater.
|
||||
*
|
||||
* @return width of the populated column as int
|
||||
*/
|
||||
int getWidth();
|
||||
|
||||
/**
|
||||
* Returns the number of rows in the column.
|
||||
*
|
||||
* @return number of rows in the column as int.
|
||||
*/
|
||||
int rowCount();
|
||||
|
||||
/**
|
||||
* Returns true if the column has no data.
|
||||
*
|
||||
* @return true if empty, false if not
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Returns the column value (data) at given row index.
|
||||
*
|
||||
* @return value object
|
||||
*/
|
||||
T getRow(int rowIndex);
|
||||
|
||||
/**
|
||||
* Update an existing value at the given row index to a new value.
|
||||
*
|
||||
* @param rowIndex row index of value to be updated
|
||||
* @param newValue new value
|
||||
*/
|
||||
void updateRow(int rowIndex, T newValue);
|
||||
|
||||
/**
|
||||
* Returns the row value as a formatted String.
|
||||
*
|
||||
* @return a row value as formatted String
|
||||
*/
|
||||
String getRowAsFormattedString(int rowIndex);
|
||||
|
||||
/**
|
||||
* Return the column with all of its data as a StringColumn with all of its
|
||||
* formatted string data.
|
||||
*
|
||||
* @return StringColumn
|
||||
*/
|
||||
StringColumn asStringColumn();
|
||||
|
||||
/**
|
||||
* Convenience for justifying populated StringColumns before being displayed.
|
||||
* Is only useful for StringColumn instances.
|
||||
*/
|
||||
Column<T> justify();
|
||||
|
||||
/**
|
||||
* Returns JUSTIFICATION value (RIGHT|LEFT|NONE) for the column.
|
||||
*
|
||||
* @return column JUSTIFICATION
|
||||
*/
|
||||
JUSTIFICATION getJustification();
|
||||
}
|
93
cli/src/main/java/bisq/cli/table/column/DoubleColumn.java
Normal file
93
cli/src/main/java/bisq/cli/table/column/DoubleColumn.java
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying Double values.
|
||||
*/
|
||||
public class DoubleColumn extends NumberColumn<DoubleColumn, Double> {
|
||||
|
||||
protected final List<Double> rows = new ArrayList<>();
|
||||
|
||||
protected final Predicate<String> isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth;
|
||||
|
||||
// The default DoubleColumn JUSTIFICATION is RIGHT.
|
||||
public DoubleColumn(String name) {
|
||||
this(name, RIGHT);
|
||||
}
|
||||
|
||||
public DoubleColumn(String name, JUSTIFICATION justification) {
|
||||
super(name, justification);
|
||||
this.maxWidth = name.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Double value) {
|
||||
rows.add(value);
|
||||
|
||||
String s = String.valueOf(value);
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Double> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rowCount() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return rows.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getRow(int rowIndex) {
|
||||
return rows.get(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRow(int rowIndex, Double newValue) {
|
||||
rows.set(rowIndex, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
String s = String.valueOf(getRow(rowIndex));
|
||||
return toJustifiedString(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
IntStream.range(0, rows.size()).forEachOrdered(rowIndex ->
|
||||
stringColumn.addRow(getRowAsFormattedString(rowIndex)));
|
||||
|
||||
return stringColumn;
|
||||
}
|
||||
}
|
91
cli/src/main/java/bisq/cli/table/column/FiatColumn.java
Normal file
91
cli/src/main/java/bisq/cli/table/column/FiatColumn.java
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.formatFiatVolume;
|
||||
import static bisq.cli.CurrencyFormat.formatPrice;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.PRICE;
|
||||
import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.TRIGGER_PRICE;
|
||||
|
||||
/**
|
||||
* For displaying fiat values as volume, price, or optional trigger price
|
||||
* with appropriate precision.
|
||||
*/
|
||||
public class FiatColumn extends LongColumn {
|
||||
|
||||
public enum DISPLAY_MODE {
|
||||
PRICE,
|
||||
TRIGGER_PRICE,
|
||||
VOLUME
|
||||
}
|
||||
|
||||
private final DISPLAY_MODE displayMode;
|
||||
|
||||
// The default FiatColumn JUSTIFICATION is RIGHT.
|
||||
// The default FiatColumn DISPLAY_MODE is PRICE.
|
||||
public FiatColumn(String name) {
|
||||
this(name, RIGHT, PRICE);
|
||||
}
|
||||
|
||||
public FiatColumn(String name, DISPLAY_MODE displayMode) {
|
||||
this(name, RIGHT, displayMode);
|
||||
}
|
||||
|
||||
public FiatColumn(String name,
|
||||
JUSTIFICATION justification,
|
||||
DISPLAY_MODE displayMode) {
|
||||
super(name, justification);
|
||||
this.displayMode = displayMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
rows.add(value);
|
||||
|
||||
String s;
|
||||
if (displayMode.equals(TRIGGER_PRICE))
|
||||
s = value > 0 ? formatPrice(value) : "";
|
||||
else
|
||||
s = displayMode.equals(PRICE) ? formatPrice(value) : formatFiatVolume(value);
|
||||
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return getRow(rowIndex).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
// We cached the formatted fiat price strings, but we did
|
||||
// not know how much padding each string needed until now.
|
||||
IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> {
|
||||
String unjustified = stringColumn.getRow(rowIndex);
|
||||
String justified = stringColumn.toJustifiedString(unjustified);
|
||||
stringColumn.updateRow(rowIndex, justified);
|
||||
});
|
||||
return this.stringColumn;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.LEFT;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
import static java.util.TimeZone.getTimeZone;
|
||||
|
||||
/**
|
||||
* For displaying (long) timestamp values as ISO-8601 dates in UTC time zone.
|
||||
*/
|
||||
public class Iso8601DateTimeColumn extends LongColumn {
|
||||
|
||||
protected final SimpleDateFormat iso8601DateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
|
||||
// The default Iso8601DateTimeColumn JUSTIFICATION is LEFT.
|
||||
public Iso8601DateTimeColumn(String name) {
|
||||
this(name, LEFT);
|
||||
}
|
||||
|
||||
public Iso8601DateTimeColumn(String name, JUSTIFICATION justification) {
|
||||
super(name, justification);
|
||||
iso8601DateFormat.setTimeZone(getTimeZone("UTC"));
|
||||
this.maxWidth = Math.max(name.length(), String.valueOf(currentTimeMillis()).length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
long time = getRow(rowIndex);
|
||||
return justification.equals(LEFT)
|
||||
? padEnd(iso8601DateFormat.format(new Date(time)), maxWidth, ' ')
|
||||
: padStart(iso8601DateFormat.format(new Date(time)), maxWidth, ' ');
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
IntStream.range(0, rows.size()).forEachOrdered(rowIndex ->
|
||||
stringColumn.addRow(getRowAsFormattedString(rowIndex)));
|
||||
|
||||
return stringColumn;
|
||||
}
|
||||
}
|
93
cli/src/main/java/bisq/cli/table/column/LongColumn.java
Normal file
93
cli/src/main/java/bisq/cli/table/column/LongColumn.java
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying Long values.
|
||||
*/
|
||||
public class LongColumn extends NumberColumn<LongColumn, Long> {
|
||||
|
||||
protected final List<Long> rows = new ArrayList<>();
|
||||
|
||||
protected final Predicate<String> isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth;
|
||||
|
||||
// The default LongColumn JUSTIFICATION is RIGHT.
|
||||
public LongColumn(String name) {
|
||||
this(name, RIGHT);
|
||||
}
|
||||
|
||||
public LongColumn(String name, JUSTIFICATION justification) {
|
||||
super(name, justification);
|
||||
this.maxWidth = name.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
rows.add(value);
|
||||
|
||||
String s = String.valueOf(value);
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rowCount() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return rows.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getRow(int rowIndex) {
|
||||
return rows.get(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRow(int rowIndex, Long newValue) {
|
||||
rows.set(rowIndex, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
String s = String.valueOf(getRow(rowIndex));
|
||||
return toJustifiedString(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
IntStream.range(0, rows.size()).forEachOrdered(rowIndex ->
|
||||
stringColumn.addRow(getRowAsFormattedString(rowIndex)));
|
||||
|
||||
return stringColumn;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.formatPrice;
|
||||
import static bisq.cli.CurrencyFormat.formatSatoshis;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying a mix of fiat and altcoin prices with appropriate precision.
|
||||
*/
|
||||
public class MixedPriceColumn extends LongColumn {
|
||||
|
||||
public MixedPriceColumn(String name) {
|
||||
super(name, RIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
throw new UnsupportedOperationException("use public void addRow(Long value, boolean isFiat) instead");
|
||||
}
|
||||
|
||||
public void addRow(Long value, boolean isFiat) {
|
||||
rows.add(value);
|
||||
|
||||
String s = isFiat ? formatPrice(value) : formatSatoshis(value);
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return getRow(rowIndex).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
return stringColumn.justify();
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.formatBsq;
|
||||
import static bisq.cli.CurrencyFormat.formatSatoshis;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying a mix of BSQ and BTC trade fees with appropriate precision.
|
||||
*/
|
||||
public class MixedTradeFeeColumn extends LongColumn {
|
||||
|
||||
public MixedTradeFeeColumn(String name) {
|
||||
super(name, RIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
throw new UnsupportedOperationException("use public void addRow(Long value, boolean isBsq) instead");
|
||||
}
|
||||
|
||||
public void addRow(Long value, boolean isBsq) {
|
||||
rows.add(value);
|
||||
|
||||
String s = isBsq
|
||||
? formatBsq(value) + " BSQ"
|
||||
: formatSatoshis(value) + " BTC";
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return getRow(rowIndex).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
return stringColumn.justify();
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.cli.table.column;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.formatCryptoCurrencyVolume;
|
||||
import static bisq.cli.CurrencyFormat.formatFiatVolume;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying a mix of fiat and altcoin volumes with appropriate precision.
|
||||
*/
|
||||
public class MixedVolumeColumn extends LongColumn {
|
||||
|
||||
public MixedVolumeColumn(String name) {
|
||||
super(name, RIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
throw new UnsupportedOperationException("use public void addRow(Long value, boolean isAltcoinVolume) instead");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void addRow(Long value, boolean isAltcoinVolume) {
|
||||
rows.add(value);
|
||||
|
||||
String s = isAltcoinVolume
|
||||
? formatCryptoCurrencyVolume(value)
|
||||
: formatFiatVolume(value);
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
public void addRow(Long value, int displayPrecision) {
|
||||
rows.add(value);
|
||||
|
||||
boolean isAltcoinVolume = displayPrecision > 0;
|
||||
String s = isAltcoinVolume
|
||||
? formatCryptoCurrencyVolume(value, displayPrecision)
|
||||
: formatFiatVolume(value);
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return getRow(rowIndex).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
return stringColumn.justify();
|
||||
}
|
||||
}
|
32
cli/src/main/java/bisq/cli/table/column/NumberColumn.java
Normal file
32
cli/src/main/java/bisq/cli/table/column/NumberColumn.java
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.cli.table.column;
|
||||
|
||||
/**
|
||||
* Abstract superclass for numeric Columns.
|
||||
*
|
||||
* @param <C> the subclass column's type (LongColumn, IntegerColumn, ...)
|
||||
* @param <T> the subclass column's numeric Java type (Long, Integer, ...)
|
||||
*/
|
||||
abstract class NumberColumn<C extends NumberColumn<C, T>,
|
||||
T extends Number> extends AbstractColumn<C, T> implements Column<T> {
|
||||
|
||||
public NumberColumn(String name, JUSTIFICATION justification) {
|
||||
super(name, justification);
|
||||
}
|
||||
}
|
72
cli/src/main/java/bisq/cli/table/column/SatoshiColumn.java
Normal file
72
cli/src/main/java/bisq/cli/table/column/SatoshiColumn.java
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.formatBsq;
|
||||
import static bisq.cli.CurrencyFormat.formatSatoshis;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying BTC or BSQ satoshi values with appropriate precision.
|
||||
*/
|
||||
public class SatoshiColumn extends LongColumn {
|
||||
|
||||
protected final boolean isBsqSatoshis;
|
||||
|
||||
// The default SatoshiColumn JUSTIFICATION is RIGHT.
|
||||
public SatoshiColumn(String name) {
|
||||
this(name, RIGHT, false);
|
||||
}
|
||||
|
||||
public SatoshiColumn(String name, boolean isBsqSatoshis) {
|
||||
this(name, RIGHT, isBsqSatoshis);
|
||||
}
|
||||
|
||||
public SatoshiColumn(String name, JUSTIFICATION justification) {
|
||||
this(name, justification, false);
|
||||
}
|
||||
|
||||
public SatoshiColumn(String name, JUSTIFICATION justification, boolean isBsqSatoshis) {
|
||||
super(name, justification);
|
||||
this.isBsqSatoshis = isBsqSatoshis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
rows.add(value);
|
||||
|
||||
// We do not know how much padding each StringColumn value needs until it has all the values.
|
||||
String s = isBsqSatoshis ? formatBsq(value) : formatSatoshis(value);
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return isBsqSatoshis
|
||||
? formatBsq(getRow(rowIndex))
|
||||
: formatSatoshis(getRow(rowIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
return stringColumn.justify();
|
||||
}
|
||||
}
|
102
cli/src/main/java/bisq/cli/table/column/StringColumn.java
Normal file
102
cli/src/main/java/bisq/cli/table/column/StringColumn.java
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.LEFT;
|
||||
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying justified string values.
|
||||
*/
|
||||
public class StringColumn extends AbstractColumn<StringColumn, String> {
|
||||
|
||||
private final List<String> rows = new ArrayList<>();
|
||||
|
||||
private final Predicate<String> isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth;
|
||||
|
||||
// The default StringColumn JUSTIFICATION is LEFT.
|
||||
public StringColumn(String name) {
|
||||
this(name, LEFT);
|
||||
}
|
||||
|
||||
// Use this constructor to override default LEFT justification.
|
||||
public StringColumn(String name, JUSTIFICATION justification) {
|
||||
super(name, justification);
|
||||
this.maxWidth = name.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(String value) {
|
||||
rows.add(value);
|
||||
if (isNewMaxWidth.test(value))
|
||||
maxWidth = value.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rowCount() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return rows.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRow(int rowIndex) {
|
||||
return rows.get(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRow(int rowIndex, String newValue) {
|
||||
rows.set(rowIndex, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return getRow(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn justify() {
|
||||
if (justification.equals(RIGHT)) {
|
||||
IntStream.range(0, getRows().size()).forEach(rowIndex -> {
|
||||
String unjustified = getRow(rowIndex);
|
||||
String justified = toJustifiedString(unjustified);
|
||||
updateRow(rowIndex, justified);
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
130
cli/src/main/java/bisq/cli/table/column/ZippedStringColumns.java
Normal file
130
cli/src/main/java/bisq/cli/table/column/ZippedStringColumns.java
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.cli.table.column;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static bisq.cli.table.column.ZippedStringColumns.DUPLICATION_MODE.EXCLUDE_DUPLICATES;
|
||||
import static bisq.cli.table.column.ZippedStringColumns.DUPLICATION_MODE.INCLUDE_DUPLICATES;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.column.Column.JUSTIFICATION;
|
||||
|
||||
/**
|
||||
* For zipping multiple StringColumns into a single StringColumn.
|
||||
* Useful for displaying amount and volume range values.
|
||||
*/
|
||||
public class ZippedStringColumns {
|
||||
|
||||
public enum DUPLICATION_MODE {
|
||||
EXCLUDE_DUPLICATES,
|
||||
INCLUDE_DUPLICATES
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final JUSTIFICATION justification;
|
||||
private final String delimiter;
|
||||
private final StringColumn[] columns;
|
||||
|
||||
public ZippedStringColumns(String name,
|
||||
JUSTIFICATION justification,
|
||||
String delimiter,
|
||||
StringColumn... columns) {
|
||||
this.name = name;
|
||||
this.justification = justification;
|
||||
this.delimiter = delimiter;
|
||||
this.columns = columns;
|
||||
validateColumnData();
|
||||
}
|
||||
|
||||
public StringColumn asStringColumn(DUPLICATION_MODE duplicationMode) {
|
||||
StringColumn stringColumn = new StringColumn(name, justification);
|
||||
|
||||
buildRows(stringColumn, duplicationMode);
|
||||
|
||||
// Re-set the column name field to its justified value, in case any of the column
|
||||
// values are longer than the name passed to this constructor.
|
||||
stringColumn.setName(stringColumn.toJustifiedString(name));
|
||||
|
||||
return stringColumn;
|
||||
}
|
||||
|
||||
private void buildRows(StringColumn stringColumn, DUPLICATION_MODE duplicationMode) {
|
||||
// Populate the StringColumn with unjustified zipped values; we cannot justify
|
||||
// the zipped values until stringColumn knows its final maxWidth.
|
||||
IntStream.range(0, columns[0].getRows().size()).forEach(rowIndex -> {
|
||||
String row = buildRow(rowIndex, duplicationMode);
|
||||
stringColumn.addRow(row);
|
||||
});
|
||||
|
||||
formatRows(stringColumn);
|
||||
}
|
||||
|
||||
private String buildRow(int rowIndex, DUPLICATION_MODE duplicationMode) {
|
||||
StringBuilder rowBuilder = new StringBuilder();
|
||||
@Nullable
|
||||
List<String> processedValues = duplicationMode.equals(EXCLUDE_DUPLICATES)
|
||||
? new ArrayList<>()
|
||||
: null;
|
||||
IntStream.range(0, columns.length).forEachOrdered(colIndex -> {
|
||||
// For each column @ rowIndex ...
|
||||
var value = columns[colIndex].getRows().get(rowIndex);
|
||||
if (duplicationMode.equals(INCLUDE_DUPLICATES)) {
|
||||
if (rowBuilder.length() > 0)
|
||||
rowBuilder.append(delimiter);
|
||||
|
||||
rowBuilder.append(value);
|
||||
} else if (!processedValues.contains(value)) {
|
||||
if (rowBuilder.length() > 0)
|
||||
rowBuilder.append(delimiter);
|
||||
|
||||
rowBuilder.append(value);
|
||||
processedValues.add(value);
|
||||
}
|
||||
});
|
||||
return rowBuilder.toString();
|
||||
}
|
||||
|
||||
private void formatRows(StringColumn stringColumn) {
|
||||
// Now we can justify the zipped string values in the new StringColumn.
|
||||
IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> {
|
||||
String unjustified = stringColumn.getRow(rowIndex);
|
||||
String justified = stringColumn.toJustifiedString(unjustified);
|
||||
stringColumn.updateRow(rowIndex, justified);
|
||||
});
|
||||
}
|
||||
|
||||
private void validateColumnData() {
|
||||
if (columns.length == 0)
|
||||
throw new IllegalStateException("cannot zip columns because they do not have any data");
|
||||
|
||||
StringColumn firstColumn = columns[0];
|
||||
if (firstColumn.getRows().isEmpty())
|
||||
throw new IllegalStateException("1st column has no data");
|
||||
|
||||
IntStream.range(1, columns.length).forEach(colIndex -> {
|
||||
if (columns[colIndex].getRows().size() != firstColumn.getRows().size())
|
||||
throw new IllegalStateException("columns do not have same number of rows");
|
||||
});
|
||||
}
|
||||
}
|
96
cli/src/test/java/bisq/cli/AbstractCliTest.java
Normal file
96
cli/src/test/java/bisq/cli/AbstractCliTest.java
Normal file
@ -0,0 +1,96 @@
|
||||
package bisq.cli;
|
||||
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_HOST;
|
||||
import static bisq.cli.opts.OptLabel.OPT_PASSWORD;
|
||||
import static bisq.cli.opts.OptLabel.OPT_PORT;
|
||||
import static org.bitbucket.cowwoc.diffmatchpatch.DiffMatchPatch.Operation.DELETE;
|
||||
import static org.bitbucket.cowwoc.diffmatchpatch.DiffMatchPatch.Operation.INSERT;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.opts.ArgumentList;
|
||||
import org.bitbucket.cowwoc.diffmatchpatch.DiffMatchPatch;
|
||||
|
||||
/**
|
||||
* Parent class for CLI smoke tests. Useful for examining the format of the console
|
||||
* output, and checking for diffs while making changes to console output formatters.
|
||||
*
|
||||
* Tests that create offers or trades should not be run on mainnet.
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractCliTest {
|
||||
|
||||
static final String PASSWORD_OPT = "--password=xyz"; // Both daemons' password.
|
||||
static final String ALICE_PORT_OPT = "--port=" + 9998; // Alice's daemon port.
|
||||
static final String BOB_PORT_OPT = "--port=" + 9999; // Bob's daemon port.
|
||||
static final String[] BASE_ALICE_CLIENT_OPTS = new String[]{PASSWORD_OPT, ALICE_PORT_OPT};
|
||||
static final String[] BASE_BOB_CLIENT_OPTS = new String[]{PASSWORD_OPT, BOB_PORT_OPT};
|
||||
|
||||
protected final GrpcClient aliceClient;
|
||||
protected final GrpcClient bobClient;
|
||||
|
||||
public AbstractCliTest() {
|
||||
this.aliceClient = getGrpcClient(BASE_ALICE_CLIENT_OPTS);
|
||||
this.bobClient = getGrpcClient(BASE_BOB_CLIENT_OPTS);
|
||||
}
|
||||
|
||||
protected GrpcClient getGrpcClient(String[] args) {
|
||||
var parser = new OptionParser();
|
||||
var hostOpt = parser.accepts(OPT_HOST, "rpc server hostname or ip")
|
||||
.withRequiredArg()
|
||||
.defaultsTo("localhost");
|
||||
var portOpt = parser.accepts(OPT_PORT, "rpc server port")
|
||||
.withRequiredArg()
|
||||
.ofType(Integer.class)
|
||||
.defaultsTo(9998);
|
||||
var passwordOpt = parser.accepts(OPT_PASSWORD, "rpc server password")
|
||||
.withRequiredArg();
|
||||
|
||||
OptionSet options = parser.parse(new ArgumentList(args).getCLIArguments());
|
||||
var host = options.valueOf(hostOpt);
|
||||
var port = options.valueOf(portOpt);
|
||||
var password = options.valueOf(passwordOpt);
|
||||
if (password == null)
|
||||
throw new IllegalArgumentException("missing required 'password' option");
|
||||
|
||||
return new GrpcClient(host, port, password);
|
||||
}
|
||||
|
||||
protected void checkDiffsIgnoreWhitespace(String oldOutput, String newOutput) {
|
||||
Predicate<DiffMatchPatch.Operation> isInsertOrDelete = (operation) ->
|
||||
operation.equals(INSERT) || operation.equals(DELETE);
|
||||
Predicate<String> isWhitespace = (text) -> text.trim().isEmpty();
|
||||
boolean hasNonWhitespaceDiffs = false;
|
||||
if (!oldOutput.equals(newOutput)) {
|
||||
DiffMatchPatch dmp = new DiffMatchPatch();
|
||||
LinkedList<DiffMatchPatch.Diff> diff = dmp.diffMain(oldOutput, newOutput, true);
|
||||
for (DiffMatchPatch.Diff d : diff) {
|
||||
if (isInsertOrDelete.test(d.operation) && !isWhitespace.test(d.text)) {
|
||||
hasNonWhitespaceDiffs = true;
|
||||
log.error(">>> DIFF {}", d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNonWhitespaceDiffs)
|
||||
log.error("FAIL: There were diffs");
|
||||
else
|
||||
log.info("PASS: No diffs");
|
||||
}
|
||||
|
||||
protected void printOldTbl(String tbl) {
|
||||
log.info("OLD Console OUT:\n{}", tbl);
|
||||
}
|
||||
|
||||
protected void printNewTbl(String tbl) {
|
||||
log.info("NEW Console OUT:\n{}", tbl);
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package bisq.cli.table;
|
||||
|
||||
import bisq.proto.grpc.AddressBalanceInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.cli.table.builder.TableType.ADDRESS_BALANCE_TBL;
|
||||
import static java.lang.System.err;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.AbstractCliTest;
|
||||
import bisq.cli.TableFormat;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class AddressCliOutputDiffTest extends AbstractCliTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
AddressCliOutputDiffTest test = new AddressCliOutputDiffTest();
|
||||
test.getFundingAddresses();
|
||||
test.getAddressBalance();
|
||||
}
|
||||
|
||||
public AddressCliOutputDiffTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
private void getFundingAddresses() {
|
||||
var fundingAddresses = aliceClient.getFundingAddresses();
|
||||
if (fundingAddresses.size() > 0) {
|
||||
var oldTbl = TableFormat.formatAddressBalanceTbl(fundingAddresses);
|
||||
var newTbl = new TableBuilder(ADDRESS_BALANCE_TBL, fundingAddresses).build().toString();
|
||||
printOldTbl(oldTbl);
|
||||
printNewTbl(newTbl);
|
||||
checkDiffsIgnoreWhitespace(oldTbl, newTbl);
|
||||
} else {
|
||||
err.println("no funding addresses found");
|
||||
}
|
||||
}
|
||||
|
||||
private void getAddressBalance() {
|
||||
List<AddressBalanceInfo> addresses = aliceClient.getFundingAddresses();
|
||||
int numAddresses = addresses.size();
|
||||
// Check output for last 2 addresses.
|
||||
for (int i = numAddresses - 2; i < addresses.size(); i++) {
|
||||
var addressBalanceInfo = addresses.get(i);
|
||||
getAddressBalance(addressBalanceInfo.getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
private void getAddressBalance(String address) {
|
||||
var addressBalance = singletonList(aliceClient.getAddressBalance(address));
|
||||
var oldTbl = TableFormat.formatAddressBalanceTbl(addressBalance);
|
||||
var newTbl = new TableBuilder(ADDRESS_BALANCE_TBL, addressBalance).build().toString();
|
||||
printOldTbl(oldTbl);
|
||||
printNewTbl(newTbl);
|
||||
checkDiffsIgnoreWhitespace(oldTbl, newTbl);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package bisq.cli.table;
|
||||
|
||||
import static bisq.cli.table.builder.TableType.BSQ_BALANCE_TBL;
|
||||
import static bisq.cli.table.builder.TableType.BTC_BALANCE_TBL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.AbstractCliTest;
|
||||
import bisq.cli.TableFormat;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class GetBalanceCliOutputDiffTest extends AbstractCliTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
GetBalanceCliOutputDiffTest test = new GetBalanceCliOutputDiffTest();
|
||||
test.getBtcBalance();
|
||||
test.getBsqBalance();
|
||||
}
|
||||
|
||||
public GetBalanceCliOutputDiffTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
private void getBtcBalance() {
|
||||
var balance = aliceClient.getBtcBalances();
|
||||
var oldTbl = TableFormat.formatBtcBalanceInfoTbl(balance);
|
||||
var newTbl = new TableBuilder(BTC_BALANCE_TBL, balance).build().toString();
|
||||
printOldTbl(oldTbl);
|
||||
printNewTbl(newTbl);
|
||||
checkDiffsIgnoreWhitespace(oldTbl, newTbl);
|
||||
}
|
||||
|
||||
private void getBsqBalance() {
|
||||
var balance = aliceClient.getBsqBalances();
|
||||
var oldTbl = TableFormat.formatBsqBalanceInfoTbl(balance);
|
||||
var newTbl = new TableBuilder(BSQ_BALANCE_TBL, balance).build().toString();
|
||||
printOldTbl(oldTbl);
|
||||
printNewTbl(newTbl);
|
||||
checkDiffsIgnoreWhitespace(oldTbl, newTbl);
|
||||
}
|
||||
}
|
126
cli/src/test/java/bisq/cli/table/GetOffersCliOutputDiffTest.java
Normal file
126
cli/src/test/java/bisq/cli/table/GetOffersCliOutputDiffTest.java
Normal file
@ -0,0 +1,126 @@
|
||||
package bisq.cli.table;
|
||||
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.cli.table.builder.TableType.OFFER_TBL;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
import static protobuf.OfferDirection.SELL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.AbstractCliTest;
|
||||
import bisq.cli.OfferFormat;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Slf4j
|
||||
public class GetOffersCliOutputDiffTest extends AbstractCliTest {
|
||||
|
||||
// "My" offers are always Alice's offers.
|
||||
// "Available" offers are always Alice's offers available to Bob.
|
||||
|
||||
public static void main(String[] args) {
|
||||
GetOffersCliOutputDiffTest test = new GetOffersCliOutputDiffTest();
|
||||
|
||||
test.getMyBuyUsdOffers();
|
||||
test.getMySellUsdOffers();
|
||||
test.getAvailableBuyUsdOffers();
|
||||
test.getAvailableSellUsdOffers();
|
||||
|
||||
/*
|
||||
// TODO Uncomment when XMR support is added.
|
||||
test.getMyBuyXmrOffers();
|
||||
test.getMySellXmrOffers();
|
||||
test.getAvailableBuyXmrOffers();
|
||||
test.getAvailableSellXmrOffers();
|
||||
*/
|
||||
|
||||
test.getMyBuyBsqOffers();
|
||||
test.getMySellBsqOffers();
|
||||
test.getAvailableBuyBsqOffers();
|
||||
test.getAvailableSellBsqOffers();
|
||||
}
|
||||
|
||||
public GetOffersCliOutputDiffTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
private void getMyBuyUsdOffers() {
|
||||
var myOffers = aliceClient.getMyOffers(BUY.name(), "USD");
|
||||
printAndCheckDiffs(myOffers, BUY.name(), "USD");
|
||||
}
|
||||
|
||||
private void getMySellUsdOffers() {
|
||||
var myOffers = aliceClient.getMyOffers(SELL.name(), "USD");
|
||||
printAndCheckDiffs(myOffers, SELL.name(), "USD");
|
||||
}
|
||||
|
||||
private void getAvailableBuyUsdOffers() {
|
||||
var offers = bobClient.getOffers(BUY.name(), "USD");
|
||||
printAndCheckDiffs(offers, BUY.name(), "USD");
|
||||
}
|
||||
|
||||
private void getAvailableSellUsdOffers() {
|
||||
var offers = bobClient.getOffers(SELL.name(), "USD");
|
||||
printAndCheckDiffs(offers, SELL.name(), "USD");
|
||||
}
|
||||
|
||||
private void getMyBuyXmrOffers() {
|
||||
var myOffers = aliceClient.getMyOffers(BUY.name(), "XMR");
|
||||
printAndCheckDiffs(myOffers, BUY.name(), "XMR");
|
||||
}
|
||||
|
||||
private void getMySellXmrOffers() {
|
||||
var myOffers = aliceClient.getMyOffers(SELL.name(), "XMR");
|
||||
printAndCheckDiffs(myOffers, SELL.name(), "XMR");
|
||||
}
|
||||
|
||||
private void getAvailableBuyXmrOffers() {
|
||||
var offers = bobClient.getOffers(BUY.name(), "XMR");
|
||||
printAndCheckDiffs(offers, BUY.name(), "XMR");
|
||||
}
|
||||
|
||||
private void getAvailableSellXmrOffers() {
|
||||
var offers = bobClient.getOffers(SELL.name(), "XMR");
|
||||
printAndCheckDiffs(offers, SELL.name(), "XMR");
|
||||
}
|
||||
|
||||
private void getMyBuyBsqOffers() {
|
||||
var myOffers = aliceClient.getMyOffers(BUY.name(), "BSQ");
|
||||
printAndCheckDiffs(myOffers, BUY.name(), "BSQ");
|
||||
}
|
||||
|
||||
private void getMySellBsqOffers() {
|
||||
var myOffers = aliceClient.getMyOffers(SELL.name(), "BSQ");
|
||||
printAndCheckDiffs(myOffers, SELL.name(), "BSQ");
|
||||
}
|
||||
|
||||
private void getAvailableBuyBsqOffers() {
|
||||
var offers = bobClient.getOffers(BUY.name(), "BSQ");
|
||||
printAndCheckDiffs(offers, BUY.name(), "BSQ");
|
||||
}
|
||||
|
||||
private void getAvailableSellBsqOffers() {
|
||||
var offers = bobClient.getOffers(SELL.name(), "BSQ");
|
||||
printAndCheckDiffs(offers, SELL.name(), "BSQ");
|
||||
}
|
||||
|
||||
private void printAndCheckDiffs(List<OfferInfo> offers,
|
||||
String direction,
|
||||
String currencyCode) {
|
||||
if (offers.isEmpty()) {
|
||||
log.warn("No {} {} offers to print.", direction, currencyCode);
|
||||
} else {
|
||||
log.info("Checking for diffs in {} {} offers.", direction, currencyCode);
|
||||
var oldTbl = OfferFormat.formatOfferTable(offers, currencyCode);
|
||||
var newTbl = new TableBuilder(OFFER_TBL, offers).build().toString();
|
||||
printOldTbl(oldTbl);
|
||||
printNewTbl(newTbl);
|
||||
checkDiffsIgnoreWhitespace(oldTbl, newTbl);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package bisq.cli.table;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL;
|
||||
import static java.lang.System.out;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.AbstractCliTest;
|
||||
import bisq.cli.GrpcClient;
|
||||
import bisq.cli.TradeFormat;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Slf4j
|
||||
public class GetTradeCliOutputDiffTest extends AbstractCliTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length == 0)
|
||||
throw new IllegalStateException("Need a single trade-id program argument.");
|
||||
|
||||
GetTradeCliOutputDiffTest test = new GetTradeCliOutputDiffTest(args[0]);
|
||||
test.getAlicesTrade();
|
||||
out.println();
|
||||
test.getBobsTrade();
|
||||
}
|
||||
|
||||
private final String tradeId;
|
||||
|
||||
public GetTradeCliOutputDiffTest(String tradeId) {
|
||||
super();
|
||||
this.tradeId = tradeId;
|
||||
}
|
||||
|
||||
private void getAlicesTrade() {
|
||||
getTrade(aliceClient);
|
||||
}
|
||||
|
||||
private void getBobsTrade() {
|
||||
getTrade(bobClient);
|
||||
}
|
||||
|
||||
private void getTrade(GrpcClient client) {
|
||||
var trade = client.getTrade(tradeId);
|
||||
var oldTbl = TradeFormat.format(trade);
|
||||
var newTbl = new TableBuilder(TRADE_DETAIL_TBL, trade).build().toString();
|
||||
printOldTbl(oldTbl);
|
||||
printNewTbl(newTbl);
|
||||
checkDiffsIgnoreWhitespace(oldTbl, newTbl);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package bisq.cli.table;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.cli.table.builder.TableType.TRANSACTION_TBL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.AbstractCliTest;
|
||||
import bisq.cli.TransactionFormat;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Slf4j
|
||||
public class GetTransactionCliOutputDiffTest extends AbstractCliTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length == 0)
|
||||
throw new IllegalStateException("Need a single transaction-id program argument.");
|
||||
|
||||
GetTransactionCliOutputDiffTest test = new GetTransactionCliOutputDiffTest(args[0]);
|
||||
test.getTransaction();
|
||||
}
|
||||
|
||||
private final String transactionId;
|
||||
|
||||
public GetTransactionCliOutputDiffTest(String transactionId) {
|
||||
super();
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
|
||||
private void getTransaction() {
|
||||
var tx = aliceClient.getTransaction(transactionId);
|
||||
var oldTbl = TransactionFormat.format(tx);
|
||||
var newTbl = new TableBuilder(TRANSACTION_TBL, tx).build().toString();
|
||||
printOldTbl(oldTbl);
|
||||
printNewTbl(newTbl);
|
||||
// Should show 1 (OK) diff due to new 'Is Confirmed' column being left justified (fixed).
|
||||
checkDiffsIgnoreWhitespace(oldTbl, newTbl);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package bisq.cli.table;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.cli.TableFormat.formatPaymentAcctTbl;
|
||||
import static bisq.cli.table.builder.TableType.PAYMENT_ACCOUNT_TBL;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.AbstractCliTest;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Slf4j
|
||||
public class PaymentAccountsCliOutputDiffTest extends AbstractCliTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
PaymentAccountsCliOutputDiffTest test = new PaymentAccountsCliOutputDiffTest();
|
||||
test.getPaymentAccounts();
|
||||
}
|
||||
|
||||
public PaymentAccountsCliOutputDiffTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
private void getPaymentAccounts() {
|
||||
var paymentAccounts = aliceClient.getPaymentAccounts();
|
||||
if (paymentAccounts.size() > 0) {
|
||||
var oldTbl = formatPaymentAcctTbl(paymentAccounts);
|
||||
var newTbl = new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccounts).build().toString();
|
||||
printOldTbl(oldTbl);
|
||||
printNewTbl(newTbl);
|
||||
checkDiffsIgnoreWhitespace(oldTbl, newTbl);
|
||||
} else {
|
||||
log.warn("no payment accounts found");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -240,13 +240,11 @@ public class CoreApi {
|
||||
public PaymentAccount createCryptoCurrencyPaymentAccount(String accountName,
|
||||
String currencyCode,
|
||||
String address,
|
||||
boolean tradeInstant,
|
||||
boolean isBsqSwap) {
|
||||
boolean tradeInstant) {
|
||||
return paymentAccountsService.createCryptoCurrencyPaymentAccount(accountName,
|
||||
currencyCode,
|
||||
address,
|
||||
tradeInstant,
|
||||
isBsqSwap);
|
||||
tradeInstant);
|
||||
}
|
||||
|
||||
public List<PaymentMethod> getCryptoCurrencyPaymentMethods() {
|
||||
|
@ -103,8 +103,7 @@ class CorePaymentAccountsService {
|
||||
PaymentAccount createCryptoCurrencyPaymentAccount(String accountName,
|
||||
String currencyCode,
|
||||
String address,
|
||||
boolean tradeInstant,
|
||||
boolean isBsqSwap) {
|
||||
boolean tradeInstant) {
|
||||
String bsqCode = currencyCode.toUpperCase();
|
||||
if (!bsqCode.equals("BSQ"))
|
||||
throw new IllegalArgumentException("api does not currently support " + currencyCode + " accounts");
|
||||
@ -112,25 +111,15 @@ class CorePaymentAccountsService {
|
||||
// Validate the BSQ address string but ignore the return value.
|
||||
coreWalletsService.getValidBsqAddress(address);
|
||||
|
||||
// TODO Split into 2 methods: createAtomicPaymentAccount(), createCryptoCurrencyPaymentAccount().
|
||||
PaymentAccount cryptoCurrencyAccount;
|
||||
if (isBsqSwap) {
|
||||
cryptoCurrencyAccount = PaymentAccountFactory.getPaymentAccount(PaymentMethod.BSQ_SWAP);
|
||||
} else {
|
||||
cryptoCurrencyAccount = tradeInstant
|
||||
? (InstantCryptoCurrencyAccount) PaymentAccountFactory.getPaymentAccount(PaymentMethod.BLOCK_CHAINS_INSTANT)
|
||||
: (CryptoCurrencyAccount) PaymentAccountFactory.getPaymentAccount(PaymentMethod.BLOCK_CHAINS);
|
||||
}
|
||||
AssetAccount cryptoCurrencyAccount = tradeInstant
|
||||
? (InstantCryptoCurrencyAccount) PaymentAccountFactory.getPaymentAccount(PaymentMethod.BLOCK_CHAINS_INSTANT)
|
||||
: (CryptoCurrencyAccount) PaymentAccountFactory.getPaymentAccount(PaymentMethod.BLOCK_CHAINS);
|
||||
cryptoCurrencyAccount.init();
|
||||
cryptoCurrencyAccount.setAccountName(accountName);
|
||||
if (!isBsqSwap) {
|
||||
((AssetAccount) cryptoCurrencyAccount).setAddress(address);
|
||||
}
|
||||
|
||||
cryptoCurrencyAccount.setAddress(address);
|
||||
Optional<CryptoCurrency> cryptoCurrency = getCryptoCurrency(bsqCode);
|
||||
cryptoCurrency.ifPresent(cryptoCurrencyAccount::setSingleTradeCurrency);
|
||||
user.addPaymentAccount(cryptoCurrencyAccount);
|
||||
accountAgeWitnessService.publishMyAccountAgeWitness(cryptoCurrencyAccount.getPaymentAccountPayload());
|
||||
log.info("Saved crypto payment account with id {} and payment method {}.",
|
||||
cryptoCurrencyAccount.getId(),
|
||||
cryptoCurrencyAccount.getPaymentAccountPayload().getPaymentMethodId());
|
||||
|
@ -136,8 +136,7 @@ class GrpcPaymentAccountsService extends PaymentAccountsImplBase {
|
||||
PaymentAccount paymentAccount = coreApi.createCryptoCurrencyPaymentAccount(req.getAccountName(),
|
||||
req.getCurrencyCode(),
|
||||
req.getAddress(),
|
||||
req.getTradeInstant(),
|
||||
req.getIsBsqSwap());
|
||||
req.getTradeInstant());
|
||||
var reply = CreateCryptoCurrencyPaymentAccountReply.newBuilder()
|
||||
.setPaymentAccount(paymentAccount.toProtoMessage())
|
||||
.build();
|
||||
|
@ -684,8 +684,7 @@ class OfferBookViewModel extends ActivatableViewModel {
|
||||
return coreApi.createCryptoCurrencyPaymentAccount(DisplayUtils.createAssetsAccountName("BSQ", unusedBsqAddressAsString),
|
||||
"BSQ",
|
||||
unusedBsqAddressAsString,
|
||||
isInstantPaymentMethod(offer),
|
||||
false);
|
||||
isInstantPaymentMethod(offer));
|
||||
}
|
||||
|
||||
public void setOfferActionHandler(OfferView.OfferActionHandler offerActionHandler) {
|
||||
|
@ -311,7 +311,6 @@ message CreateCryptoCurrencyPaymentAccountRequest {
|
||||
string currencyCode = 2;
|
||||
string address = 3;
|
||||
bool tradeInstant = 4;
|
||||
bool isBsqSwap = 5;
|
||||
}
|
||||
|
||||
message CreateCryptoCurrencyPaymentAccountReply {
|
||||
|
Loading…
Reference in New Issue
Block a user