Adjust API 'gettrade' for Bsq swaps

- Made several adjustments to CLI's 'gettrade' output related code
  so it can show single trade details for either Bisq v1 trades, or
  BSQ swap trades.

- Did minor refactoring of API's core to retrieve # tx confirmations
  for an addresses and transactions.

- Show # of tx confirmations in bsq swap trade detail.
This commit is contained in:
ghubstan 2021-11-14 13:48:12 -03:00
parent fc53ca48c1
commit 4ca878a8e1
No known key found for this signature in database
GPG key ID: E35592D6800A861E
13 changed files with 275 additions and 96 deletions

View file

@ -111,21 +111,39 @@ public class BsqSwapTradeTest extends AbstractTradeTest {
var swapTrade = bobClient.takeBsqSwapOffer(availableSwapOffer.getId(),
bobsBsqSwapAcct.getId(),
BISQ_FEE_CURRENCY_CODE);
log.debug("BsqSwap Trade at PREPARATION: {}", swapTrade);
tradeId = swapTrade.getTradeId(); // Cache the tradeId for following test case(s).
log.debug("BsqSwap Trade at PREPARATION:\n{}", toTradeDetailTable.apply(swapTrade));
assertEquals(PREPARATION.name(), swapTrade.getState());
genBtcBlocksThenWait(1, 3_000);
swapTrade = getBsqSwapTrade(bobClient, swapTrade.getTradeId());
log.debug("BsqSwap Trade at COMPLETION: {}", swapTrade);
log.debug("BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(swapTrade));
assertEquals(COMPLETED.name(), swapTrade.getState());
}
@Test
@Order(4)
public void testCompletedSwapTxConfirmations() {
sleep(2_000); // Wait for TX confirmation to happen on node.
var alicesTrade = getBsqSwapTrade(aliceClient, tradeId);
log.debug("Alice's BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(alicesTrade));
assertEquals(1, alicesTrade.getBsqSwapTradeInfo().getNumConfirmations());
var bobsTrade = getBsqSwapTrade(bobClient, tradeId);
log.debug("Bob's BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(bobsTrade));
assertEquals(1, bobsTrade.getBsqSwapTradeInfo().getNumConfirmations());
genBtcBlocksThenWait(1, 2_000);
bobsTrade = getBsqSwapTrade(bobClient, tradeId);
log.debug("Bob's BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(bobsTrade));
assertEquals(2, bobsTrade.getBsqSwapTradeInfo().getNumConfirmations());
}
@Test
@Order(5)
public void testGetBalancesAfterTrade() {
genBtcBlocksThenWait(1, 5_000);
var alicesBalances = aliceClient.getBalances();
log.debug("Alice's After Trade Balance:\n{}", formatBalancesTbls(alicesBalances));
var bobsBalances = bobClient.getBalances();

View file

@ -102,6 +102,15 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
@Nullable
protected final Column<String> colAltcoinReceiveAddressColumn;
// BSQ swap trade detail specific columns
@Nullable
protected final Column<String> status;
@Nullable
protected final Column<String> colTxId;
@Nullable
protected final Column<Long> colNumConfirmations;
AbstractTradeListBuilder(TableType tableType, List<?> protos) {
super(tableType, protos);
validate();
@ -125,7 +134,9 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
this.colRole = colSupplier.roleColumn.get();
this.colOfferType = colSupplier.offerTypeColumn.get();
this.colStatusDescription = colSupplier.statusDescriptionColumn.get();
// Trade detail specific columns
// Trade detail specific columns, some in common with BSQ swap trades detail.
this.colIsDepositPublished = colSupplier.depositPublishedColumn.get();
this.colIsDepositConfirmed = colSupplier.depositConfirmedColumn.get();
this.colIsPayoutPublished = colSupplier.payoutPublishedColumn.get();
@ -135,6 +146,12 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
this.colIsPaymentSent = colSupplier.paymentSentColumn.get();
this.colIsPaymentReceived = colSupplier.paymentReceivedColumn.get();
this.colAltcoinReceiveAddressColumn = colSupplier.altcoinReceiveAddressColumn.get();
// BSQ swap trade detail specific columns
this.status = colSupplier.bsqSwapStatusColumn.get();
this.colTxId = colSupplier.bsqSwapTxIdColumn.get();
this.colNumConfirmations = colSupplier.numConfirmationsColumn.get();
}
protected void validate() {
@ -149,9 +166,8 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
// 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> isBsqSwapTrade = (t) -> t.getOffer().getIsBsqSwapOffer();
protected final Predicate<TradeInfo> isTaker = (t) -> t.getRole().toLowerCase().contains("taker");
// Column Value Functions
@ -185,7 +201,6 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
}
};
// TODO Move to TradeUtil ?
protected final Function<TradeInfo, String> toPriceDeviation = (t) ->
t.getOffer().getUseMarketBasedPrice()
? formatToPercent(t.getOffer().getMarketPriceMargin())
@ -196,8 +211,6 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
? t.getTxFeeAsLong()
: t.getOffer().getTxFee();
// TODO Move to TradeUtil ?
protected final BiFunction<TradeInfo, Boolean, Long> toTradeFeeBsq = (t, isMyOffer) -> {
if (isMyOffer) {
return t.getOffer().getIsCurrencyForMakerFeeBtc()
@ -210,7 +223,6 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
}
};
// TODO Move to TradeUtil ?
protected final BiFunction<TradeInfo, Boolean, Long> toTradeFeeBtc = (t, isMyOffer) -> {
if (isMyOffer) {
return t.getOffer().getIsCurrencyForMakerFeeBtc()
@ -223,12 +235,18 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
}
};
protected final Function<TradeInfo, Long> toMyMakerOrTakerFee = (t) ->
isTaker.test(t)
protected final Function<TradeInfo, Long> toMyMakerOrTakerFee = (t) -> {
if (isBsqSwapTrade.test(t)) {
return isTaker.test(t)
? t.getBsqSwapTradeInfo().getBsqTakerTradeFee()
: t.getBsqSwapTradeInfo().getBsqMakerTradeFee();
} else {
return 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();
@ -267,7 +285,7 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
}
};
// TODO Stuff to move into bisq/cli/CurrencyFormat.java ?
// TODO Move to bisq/cli/CurrencyFormat.java ?
public static String formatToPercent(double value) {
DecimalFormat decimalFormat = new DecimalFormat("#.##");

View file

@ -35,6 +35,7 @@ class TableBuilderConstants {
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_BSQ_SWAP_TRADE_ROLE = "My BSQ Swap Role";
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";
@ -65,8 +66,6 @@ class TableBuilderConstants {
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";

View file

@ -17,10 +17,16 @@
package bisq.cli.table.builder;
import bisq.proto.grpc.TradeInfo;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL;
import static java.lang.String.format;
import static protobuf.BsqSwapTrade.State.COMPLETED;
import static protobuf.BsqSwapTrade.State.PREPARATION;
@ -30,8 +36,12 @@ import bisq.cli.table.column.Column;
/**
* Builds a {@code bisq.cli.table.Table} from a {@code bisq.proto.grpc.TradeInfo} object.
*/
@SuppressWarnings("ConstantConditions")
class TradeDetailTableBuilder extends AbstractTradeListBuilder {
private final Predicate<TradeInfo> isPendingBsqSwap = (t) -> t.getState().equals(PREPARATION.name());
private final Predicate<TradeInfo> isCompletedBsqSwap = (t) -> t.getState().equals(COMPLETED.name());
TradeDetailTableBuilder(List<?> protos) {
super(TRADE_DETAIL_TBL, protos);
}
@ -41,33 +51,70 @@ class TradeDetailTableBuilder extends AbstractTradeListBuilder {
* @return Table containing one row
*/
public Table build() {
populateColumns();
List<Column<?>> columns = defineColumnList();
// A trade detail table only has one row.
var trade = trades.get(0);
populateColumns(trade);
List<Column<?>> columns = defineColumnList(trade);
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 void populateColumns(TradeInfo trade) {
if (isBsqSwapTrade.test(trade)) {
var isPending = isPendingBsqSwap.test(trade);
var isCompleted = isCompletedBsqSwap.test(trade);
if (isPending == isCompleted)
throw new IllegalStateException(
format("programmer error: trade must be either pending or completed, is pending=%s and completed=%s",
isPending,
isCompleted));
populateBsqSwapTradeColumns(trade);
} else {
populateBisqV1TradeColumns(trade);
}
}
private List<Column<?>> defineColumnList() {
private void populateBisqV1TradeColumns(TradeInfo trade) {
colTradeId.addRow(trade.getShortId());
colRole.addRow(trade.getRole());
colPrice.addRow(trade.getTradePrice());
colAmountInBtc.addRow(toAmount.apply(trade));
colMinerTxFee.addRow(toMyMinerTxFee.apply(trade));
colBisqTradeFee.addRow(toMyMakerOrTakerFee.apply(trade));
colIsDepositPublished.addRow(trade.getIsDepositPublished());
colIsDepositConfirmed.addRow(trade.getIsDepositConfirmed());
colTradeCost.addRow(toTradeVolume.apply(trade));
colIsPaymentSent.addRow(trade.getIsFiatSent());
colIsPaymentReceived.addRow(trade.getIsFiatReceived());
colIsPayoutPublished.addRow(trade.getIsPayoutPublished());
colIsFundsWithdrawn.addRow(trade.getIsWithdrawn());
if (colAltcoinReceiveAddressColumn != null)
colAltcoinReceiveAddressColumn.addRow(toAltcoinReceiveAddress.apply(trade));
}
private void populateBsqSwapTradeColumns(TradeInfo trade) {
colTradeId.addRow(trade.getShortId());
colRole.addRow(trade.getRole());
colPrice.addRow(trade.getTradePrice());
colAmountInBtc.addRow(toAmount.apply(trade));
colMinerTxFee.addRow(toMyMinerTxFee.apply(trade));
colBisqTradeFee.addRow(toMyMakerOrTakerFee.apply(trade));
colTradeCost.addRow(toTradeVolume.apply(trade));
var isCompleted = isCompletedBsqSwap.test(trade);
status.addRow(isCompleted ? "COMPLETED" : "PENDING");
if (isCompleted) {
colTxId.addRow(trade.getBsqSwapTradeInfo().getTxId());
colNumConfirmations.addRow(trade.getBsqSwapTradeInfo().getNumConfirmations());
}
}
private List<Column<?>> defineColumnList(TradeInfo trade) {
return isBsqSwapTrade.test(trade)
? getBsqSwapTradeColumnList(isCompletedBsqSwap.test(trade))
: getBisqV1TradeColumnList();
}
private List<Column<?>> getBisqV1TradeColumnList() {
List<Column<?>> columns = new ArrayList<>() {{
add(colTradeId);
add(colRole);
@ -89,4 +136,25 @@ class TradeDetailTableBuilder extends AbstractTradeListBuilder {
return columns;
}
private List<Column<?>> getBsqSwapTradeColumnList(boolean isCompleted) {
List<Column<?>> columns = new ArrayList<>() {{
add(colTradeId);
add(colRole);
add(colPrice.asStringColumn());
add(colAmountInBtc.asStringColumn());
add(colMinerTxFee.asStringColumn());
add(colBisqTradeFee.asStringColumn());
add(colTradeCost.asStringColumn());
add(status);
}};
if (isCompleted)
columns.add(colTxId);
if (!colNumConfirmations.isEmpty())
columns.add(colNumConfirmations.asStringColumn());
return columns;
}
}

View file

@ -40,6 +40,7 @@ import static bisq.cli.table.column.AltcoinColumn.DISPLAY_MODE.ALTCOIN_OFFER_VOL
import static bisq.cli.table.column.Column.JUSTIFICATION.LEFT;
import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT;
import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.VOLUME;
import static java.lang.String.format;
@ -49,6 +50,7 @@ import bisq.cli.table.column.BtcColumn;
import bisq.cli.table.column.Column;
import bisq.cli.table.column.FiatColumn;
import bisq.cli.table.column.Iso8601DateTimeColumn;
import bisq.cli.table.column.LongColumn;
import bisq.cli.table.column.MixedPriceColumn;
import bisq.cli.table.column.MixedTradeFeeColumn;
import bisq.cli.table.column.MixedVolumeColumn;
@ -79,7 +81,10 @@ class TradeTableColumnSupplier {
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> isBsqSwapTrade = (t) -> t.getOffer().getIsBsqSwapOffer();
private final Predicate<TradeInfo> isTaker = (t) -> t.getRole().toLowerCase().contains("taker");
private final Supplier<Boolean> isSwapTradeDetail = () ->
isTradeDetailTblBuilder.get() && isBsqSwapTrade.test(firstRow.get());
final Supplier<StringColumn> tradeIdColumn = () -> isTradeDetailTblBuilder.get()
? new StringColumn(COL_HEADER_TRADE_SHORT_ID)
@ -95,8 +100,8 @@ class TradeTableColumnSupplier {
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());
? format(COL_HEADER_DETAILED_PRICE, t.getOffer().getCounterCurrencyCode())
: format(COL_HEADER_DETAILED_PRICE_OF_ALTCOIN, t.getOffer().getBaseCurrencyCode());
return isFiatTrade.test(t)
? new FiatColumn(colHeader)
: new AltcoinColumn(colHeader);
@ -116,7 +121,7 @@ class TradeTableColumnSupplier {
private final Function<TradeInfo, Column<Long>> toDetailedAmountColumn = (t) -> {
String headerCurrencyCode = t.getOffer().getBaseCurrencyCode();
String colHeader = String.format(COL_HEADER_DETAILED_AMOUNT, headerCurrencyCode);
String colHeader = format(COL_HEADER_DETAILED_AMOUNT, headerCurrencyCode);
return isFiatTrade.test(t)
? new SatoshiColumn(colHeader)
: new AltcoinColumn(colHeader, ALTCOIN_OFFER_VOLUME);
@ -142,10 +147,14 @@ class TradeTableColumnSupplier {
? null
: new StringColumn(COL_HEADER_PAYMENT_METHOD, LEFT);
final Supplier<StringColumn> roleColumn = () ->
isTradeDetailTblBuilder.get() || isOpenTradeTblBuilder.get() || isFailedTradeTblBuilder.get()
final Supplier<StringColumn> roleColumn = () -> {
if (isSwapTradeDetail.get())
return new StringColumn(COL_HEADER_BSQ_SWAP_TRADE_ROLE);
else
return 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)
@ -161,21 +170,42 @@ class TradeTableColumnSupplier {
private final Function<String, Column<Boolean>> toBooleanColumn = BooleanColumn::new;
final Supplier<Column<Boolean>> depositPublishedColumn = () -> isTradeDetailTblBuilder.get()
final Supplier<Column<Boolean>> depositPublishedColumn = () -> {
if (isSwapTradeDetail.get())
return null;
else
return isTradeDetailTblBuilder.get()
? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_PUBLISHED)
: null;
};
final Supplier<Column<Boolean>> depositConfirmedColumn = () -> isTradeDetailTblBuilder.get()
final Supplier<Column<Boolean>> depositConfirmedColumn = () -> {
if (isSwapTradeDetail.get())
return null;
else
return isTradeDetailTblBuilder.get()
? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_CONFIRMED)
: null;
final Supplier<Column<Boolean>> payoutPublishedColumn = () -> isTradeDetailTblBuilder.get()
};
final Supplier<Column<Boolean>> payoutPublishedColumn = () -> {
if (isSwapTradeDetail.get())
return null;
else
return isTradeDetailTblBuilder.get()
? toBooleanColumn.apply(COL_HEADER_TRADE_PAYOUT_PUBLISHED)
: null;
};
final Supplier<Column<Boolean>> fundsWithdrawnColumn = () -> isTradeDetailTblBuilder.get()
final Supplier<Column<Boolean>> fundsWithdrawnColumn = () -> {
if (isSwapTradeDetail.get())
return null;
else
return isTradeDetailTblBuilder.get()
? toBooleanColumn.apply(COL_HEADER_TRADE_WITHDRAWN)
: null;
};
final Supplier<Column<Long>> bisqTradeDetailFeeColumn = () -> {
if (isTradeDetailTblBuilder.get()) {
@ -184,8 +214,8 @@ class TradeTableColumnSupplier {
? 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);
? format(COL_HEADER_TRADE_TAKER_FEE, headerCurrencyCode)
: format(COL_HEADER_TRADE_MAKER_FEE, headerCurrencyCode);
boolean isBsqSatoshis = headerCurrencyCode.equals("BSQ");
return new SatoshiColumn(colHeader, isBsqSatoshis);
} else {
@ -201,7 +231,7 @@ class TradeTableColumnSupplier {
final Supplier<Column<Boolean>> paymentSentColumn = () -> {
if (isTradeDetailTblBuilder.get()) {
String headerCurrencyCode = toPaymentCurrencyCode.apply(firstRow.get());
String colHeader = String.format(COL_HEADER_TRADE_PAYMENT_SENT, headerCurrencyCode);
String colHeader = format(COL_HEADER_TRADE_PAYMENT_SENT, headerCurrencyCode);
return new BooleanColumn(colHeader);
} else {
return null;
@ -211,7 +241,7 @@ class TradeTableColumnSupplier {
final Supplier<Column<Boolean>> paymentReceivedColumn = () -> {
if (isTradeDetailTblBuilder.get()) {
String headerCurrencyCode = toPaymentCurrencyCode.apply(firstRow.get());
String colHeader = String.format(COL_HEADER_TRADE_PAYMENT_RECEIVED, headerCurrencyCode);
String colHeader = format(COL_HEADER_TRADE_PAYMENT_RECEIVED, headerCurrencyCode);
return new BooleanColumn(colHeader);
} else {
return null;
@ -222,7 +252,7 @@ class TradeTableColumnSupplier {
if (isTradeDetailTblBuilder.get()) {
TradeInfo t = firstRow.get();
String headerCurrencyCode = t.getOffer().getCounterCurrencyCode();
String colHeader = String.format(COL_HEADER_TRADE_BUYER_COST, headerCurrencyCode);
String colHeader = format(COL_HEADER_TRADE_BUYER_COST, headerCurrencyCode);
return isFiatTrade.test(t)
? new FiatColumn(colHeader, VOLUME)
: new SatoshiColumn(colHeader);
@ -231,6 +261,18 @@ class TradeTableColumnSupplier {
}
};
final Supplier<Column<String>> bsqSwapTxIdColumn = () -> isSwapTradeDetail.get()
? new StringColumn(COL_HEADER_TX_ID)
: null;
final Supplier<Column<String>> bsqSwapStatusColumn = () -> isSwapTradeDetail.get()
? new StringColumn(COL_HEADER_STATUS)
: null;
final Supplier<Column<Long>> numConfirmationsColumn = () -> isSwapTradeDetail.get()
? new LongColumn(COL_HEADER_CONFIRMATIONS)
: null;
final Predicate<TradeInfo> showAltCoinBuyerAddress = (t) -> {
if (isFiatTrade.test(t)) {
return false;
@ -251,7 +293,7 @@ class TradeTableColumnSupplier {
TradeInfo t = firstRow.get();
if (showAltCoinBuyerAddress.test(t)) {
String headerCurrencyCode = toPaymentCurrencyCode.apply(t);
String colHeader = String.format(COL_HEADER_TRADE_ALTCOIN_BUYER_ADDRESS, headerCurrencyCode);
String colHeader = format(COL_HEADER_TRADE_ALTCOIN_BUYER_ADDRESS, headerCurrencyCode);
return new StringColumn(colHeader);
} else {
return null;

View file

@ -401,6 +401,10 @@ public class CoreApi {
return walletsService.getTransaction(txId);
}
public int getTransactionConfirmations(String txId) {
return walletsService.getTransactionConfirmations(txId);
}
public void setWalletPassword(String password, String newPassword) {
walletsService.setWalletPassword(password, newPassword);
}

View file

@ -85,6 +85,7 @@ import javax.annotation.Nullable;
import static bisq.core.btc.wallet.Restrictions.getMinNonDustOutput;
import static bisq.core.util.ParsingUtils.parseToCoin;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.SECONDS;
@Singleton
@ -324,7 +325,7 @@ class CoreWalletsService {
for (TransactionOutput txOut : spendableBsqTxOutputs) {
if (isTxOutputAddressMatch.test(txOut) && isTxOutputValueMatch.test(txOut)) {
log.info("\t\tTx {} output has matching address {} and value {}.",
txOut.getParentTransaction().getTxId(),
requireNonNull(txOut.getParentTransaction()).getTxId(),
address,
txOut.getValue().toPlainString());
numMatches++;
@ -346,6 +347,7 @@ class CoreWalletsService {
@SuppressWarnings({"unchecked", "Convert2MethodRef"})
ListenableFuture<Void> future =
(ListenableFuture<Void>) executor.submit(() -> feeService.requestFees());
//noinspection NullableProblems
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Void ignored) {
@ -393,23 +395,11 @@ class CoreWalletsService {
}
Transaction getTransaction(String txId) {
if (txId.length() != 64)
throw new IllegalArgumentException(format("%s is not a transaction id", txId));
try {
Transaction tx = btcWalletService.getTransaction(txId);
if (tx == null)
throw new IllegalArgumentException(format("tx with id %s not found", txId));
else
return tx;
} catch (IllegalArgumentException ex) {
log.error("", ex);
throw new IllegalArgumentException(
format("could not get transaction with id %s%ncause: %s",
txId,
ex.getMessage().toLowerCase()));
return getTransactionWithId(txId);
}
int getTransactionConfirmations(String txId) {
return getTransactionWithId(txId).getConfidence().getDepthInBlocks();
}
int getNumConfirmationsForMostRecentTransaction(String addressString) {
@ -654,12 +644,32 @@ class CoreWalletsService {
return addressEntry.get();
}
private Transaction getTransactionWithId(String txId) {
if (txId.length() != 64)
throw new IllegalArgumentException(format("%s is not a transaction id", txId));
try {
Transaction tx = btcWalletService.getTransaction(txId);
if (tx == null)
throw new IllegalArgumentException(format("tx with id %s not found", txId));
else
return tx;
} catch (IllegalArgumentException ex) {
log.error("", ex);
throw new IllegalArgumentException(
format("could not get transaction with id %s%ncause: %s",
txId,
ex.getMessage().toLowerCase()));
}
}
/**
* Memoization stores the results of expensive function calls and returns
* the cached result when the same input occurs again.
*
* Resulting LoadingCache is used by calling `.get(input I)` or
* `.getUnchecked(input I)`, depending on whether or not `f` can return null.
* `.getUnchecked(input I)`, depending on whether `f` can return null.
* That's because CacheLoader throws an exception on null output from `f`.
*/
private static <I, O> LoadingCache<I, O> memoize(Function<I, O> f) {

View file

@ -39,6 +39,7 @@ public class BsqSwapTradeInfo implements Payload {
private final String makerBtcAddress;
private final String takerBsqAddress;
private final String takerBtcAddress;
private final long numConfirmations;
private final String errorMessage;
public BsqSwapTradeInfo(BsqSwapTradeInfoBuilder builder) {
@ -52,10 +53,13 @@ public class BsqSwapTradeInfo implements Payload {
this.makerBtcAddress = builder.getMakerBtcAddress();
this.takerBsqAddress = builder.getTakerBsqAddress();
this.takerBtcAddress = builder.getTakerBtcAddress();
this.numConfirmations = builder.getNumConfirmations();
this.errorMessage = builder.getErrorMessage();
}
public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, boolean wasMyOffer) {
public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade,
boolean wasMyOffer,
int numConfirmations) {
var protocolModel = trade.getBsqSwapProtocolModel();
var swapPeer = protocolModel.getTradePeer();
var makerBsqAddress = wasMyOffer ? protocolModel.getBsqAddress() : swapPeer.getBsqAddress();
@ -73,6 +77,7 @@ public class BsqSwapTradeInfo implements Payload {
.withMakerBtcAddress(makerBtcAddress)
.withTakerBsqAddress(takerBsqAddress)
.withTakerBtcAddress(takerBtcAddress)
.withNumConfirmations(numConfirmations)
.withErrorMessage(trade.getErrorMessage())
.build();
}
@ -94,7 +99,8 @@ public class BsqSwapTradeInfo implements Payload {
.setTakerBsqAddress(takerBsqAddress != null ? takerBsqAddress : "")
.setMakerBtcAddress(makerBtcAddress != null ? makerBtcAddress : "")
.setTakerBtcAddress(takerBtcAddress != null ? takerBtcAddress : "")
.setErrorMessage(errorMessage != null ? errorMessage : "")
.setTakerBtcAddress(takerBtcAddress != null ? takerBtcAddress : "")
.setNumConfirmations(numConfirmations)
.build();
}
@ -110,6 +116,7 @@ public class BsqSwapTradeInfo implements Payload {
.withMakerBtcAddress(proto.getMakerBtcAddress())
.withTakerBsqAddress(proto.getTakerBsqAddress())
.withTakerBtcAddress(proto.getTakerBtcAddress())
.withNumConfirmations(proto.getNumConfirmations())
.withErrorMessage(proto.getErrorMessage())
.build();
}
@ -127,6 +134,7 @@ public class BsqSwapTradeInfo implements Payload {
", makerBtcAddress='" + makerBtcAddress + '\'' +
", takerBsqAddress='" + takerBsqAddress + '\'' +
", takerBtcAddress='" + takerBtcAddress + '\'' +
", numConfirmations='" + numConfirmations + '\'' +
", errorMessage='" + errorMessage + '\'' +
'}';
}

View file

@ -24,8 +24,6 @@ import bisq.core.util.coin.CoinUtil;
import bisq.common.Payload;
import java.util.Objects;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@ -141,13 +139,13 @@ public class OfferInfo implements Payload {
return new OfferInfoBuilder()
.withId(offer.getId())
.withDirection(offer.getDirection().name())
.withPrice(Objects.requireNonNull(offer.getPrice()).getValue())
.withPrice(requireNonNull(offer.getPrice()).getValue())
.withUseMarketBasedPrice(offer.isUseMarketBasedPrice())
.withMarketPriceMargin(offer.getMarketPriceMargin())
.withAmount(offer.getAmount().value)
.withMinAmount(offer.getMinAmount().value)
.withVolume(Objects.requireNonNull(offer.getVolume()).getValue())
.withMinVolume(Objects.requireNonNull(offer.getMinVolume()).getValue())
.withVolume(requireNonNull(offer.getVolume()).getValue())
.withMinVolume(requireNonNull(offer.getMinVolume()).getValue())
.withMakerFee(getMakerFee(offer, isMyOffer))
.withTxFee(offer.getTxFee().value)
.withOfferFeePaymentTxId(offer.getOfferFeePaymentTxId())

View file

@ -104,7 +104,7 @@ public class TradeInfo implements Payload {
public static TradeInfo toNewTradeInfo(BsqSwapTrade trade, String role) {
// Always called by the taker, isMyOffer=false.
return toTradeInfo(trade, role, false);
return toTradeInfo(trade, role, false, 0);
}
public static TradeInfo toNewTradeInfo(Trade trade) {
@ -116,12 +116,15 @@ public class TradeInfo implements Payload {
if (tradeModel instanceof Trade)
return toTradeInfo((Trade) tradeModel, role, isMyOffer);
else if (tradeModel instanceof BsqSwapTrade)
return toTradeInfo((BsqSwapTrade) tradeModel, role, isMyOffer);
return toTradeInfo(tradeModel, role, isMyOffer);
else
throw new IllegalStateException("unsupported trade type: " + tradeModel.getClass().getSimpleName());
}
public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, String role, boolean isMyOffer) {
public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade,
String role,
boolean isMyOffer,
int numConfirmations) {
OfferInfo offerInfo = isMyOffer ? toMyOfferInfo(bsqSwapTrade.getOffer()) : toOfferInfo(bsqSwapTrade.getOffer());
TradeInfo tradeInfo = new TradeInfoV1Builder()
.withOffer(offerInfo)
@ -143,7 +146,7 @@ public class TradeInfo implements Payload {
// N/A: .withIsFiatSent(false), .withIsFiatReceived(false), .withIsPayoutPublished(false)
// N/A: .withIsWithdrawn(false), .withContractAsJson(""), .withContract(null)
.build();
tradeInfo.bsqSwapTradeInfo = toBsqSwapTradeInfo(bsqSwapTrade, isMyOffer);
tradeInfo.bsqSwapTradeInfo = toBsqSwapTradeInfo(bsqSwapTrade, isMyOffer, numConfirmations);
return tradeInfo;
}

View file

@ -43,6 +43,7 @@ public final class BsqSwapTradeInfoBuilder {
private String makerBtcAddress;
private String takerBsqAddress;
private String takerBtcAddress;
private long numConfirmations;
private String errorMessage;
public BsqSwapTradeInfoBuilder withTxId(String txId) {
@ -95,6 +96,11 @@ public final class BsqSwapTradeInfoBuilder {
return this;
}
public BsqSwapTradeInfoBuilder withNumConfirmations(long numConfirmations) {
this.numConfirmations = numConfirmations;
return this;
}
public BsqSwapTradeInfoBuilder withErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
return this;

View file

@ -78,7 +78,11 @@ class GrpcTradesService extends TradesImplBase {
var bsqSwapTrade = coreApi.getBsqSwapTrade(req.getTradeId());
boolean wasMyOffer = coreApi.isMyOffer(bsqSwapTrade.getOffer().getId());
String role = coreApi.getBsqSwapTradeRole(req.getTradeId());
var tradeInfo = toTradeInfo(bsqSwapTrade, role, wasMyOffer);
var numConfirmations = coreApi.getTransactionConfirmations(bsqSwapTrade.getTxId());
var tradeInfo = toTradeInfo(bsqSwapTrade,
role,
wasMyOffer,
numConfirmations);
var reply = GetBsqSwapTradeReply.newBuilder()
.setBsqSwapTrade(tradeInfo.toProtoMessage())
.build();

View file

@ -524,7 +524,8 @@ message BsqSwapTradeInfo {
string makerBtcAddress = 8;
string takerBsqAddress = 9;
string takerBtcAddress = 10;
string errorMessage = 11;
uint64 numConfirmations = 11;
string errorMessage = 12;
}
message PaymentAccountPayloadInfo {