diff --git a/apitest/src/test/java/bisq/apitest/method/MethodTest.java b/apitest/src/test/java/bisq/apitest/method/MethodTest.java
index 95918189424..791e22ef6c2 100644
--- a/apitest/src/test/java/bisq/apitest/method/MethodTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/MethodTest.java
@@ -39,6 +39,7 @@
import bisq.proto.grpc.GetPaymentAccountsRequest;
import bisq.proto.grpc.GetPaymentMethodsRequest;
import bisq.proto.grpc.GetTradeRequest;
+import bisq.proto.grpc.GetTransactionRequest;
import bisq.proto.grpc.GetTxFeeRateRequest;
import bisq.proto.grpc.GetUnusedBsqAddressRequest;
import bisq.proto.grpc.KeepFundsRequest;
@@ -432,6 +433,11 @@ protected final TxFeeRateInfo unsetTxFeeRate(BisqAppConfig bisqAppConfig) {
grpcStubs(bisqAppConfig).walletsService.unsetTxFeeRatePreference(req).getTxFeeRateInfo());
}
+ protected final TxInfo getTransaction(BisqAppConfig bisqAppConfig, String txId) {
+ var req = GetTransactionRequest.newBuilder().setTxId(txId).build();
+ return grpcStubs(bisqAppConfig).walletsService.getTransaction(req).getTxInfo();
+ }
+
// Static conveniences for test methods and test case fixture setups.
protected static RegisterDisputeAgentRequest createRegisterDisputeAgentRequest(String disputeAgentType) {
diff --git a/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java b/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java
index 9795eec28c4..ac66cc7993e 100644
--- a/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/payment/CreatePaymentAccountTest.java
@@ -746,7 +746,12 @@ public void testCreateTransferwiseAccount(TestInfo testInfo) {
String jsonString = getCompletedFormAsJsonString();
TransferwiseAccount paymentAccount = (TransferwiseAccount) createPaymentAccount(alicedaemon, jsonString);
verifyUserPayloadHasPaymentAccountWithId(paymentAccount.getId());
- verifyAccountTradeCurrencies(getAllTransferwiseCurrencies(), paymentAccount);
+ verifyUserPayloadHasPaymentAccountWithId(paymentAccount.getId());
+ // As per commit 88f26f93241af698ae689bf081205d0f9dc929fa
+ // Do not autofill all currencies by default but keep all unselected.
+ // verifyAccountTradeCurrencies(getAllTransferwiseCurrencies(), paymentAccount);
+ // TODO uncomment after master/merge
+ // assertEquals(0, paymentAccount.getTradeCurrencies().size());
verifyCommonFormEntries(paymentAccount);
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL), paymentAccount.getEmail());
log.debug("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount);
diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java
index 69445657416..ce5bceff7a1 100644
--- a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java
@@ -23,7 +23,6 @@
import lombok.extern.slf4j.Slf4j;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
@@ -33,6 +32,7 @@
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.apitest.config.BisqAppConfig.bobdaemon;
import static bisq.cli.CurrencyFormat.formatSatoshis;
+import static bisq.cli.TransactionFormat.format;
import static bisq.core.trade.Trade.Phase.*;
import static bisq.core.trade.Trade.State.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -42,7 +42,7 @@
import static protobuf.Offer.State.OFFER_FEE_PAID;
import static protobuf.OpenOffer.State.AVAILABLE;
-@Disabled
+// @Disabled
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TakeSellBTCOfferTest extends AbstractTradeTest {
@@ -52,6 +52,8 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
// Maker and Taker fees are in BTC.
private static final String TRADE_FEE_CURRENCY_CODE = "btc";
+ private static final String WITHDRAWAL_TX_MEMO = "Bob's trade withdrawal";
+
@Test
@Order(1)
public void testTakeAlicesSellOffer(final TestInfo testInfo) {
@@ -147,7 +149,7 @@ public void testBobsBtcWithdrawalToExternalAddress(final TestInfo testInfo) {
logTrade(log, testInfo, "Bob's view before withdrawing funds to external wallet", trade);
String toAddress = bitcoinCli.getNewBtcAddress();
- withdrawFunds(bobdaemon, tradeId, toAddress, "to whom it may concern");
+ withdrawFunds(bobdaemon, tradeId, toAddress, WITHDRAWAL_TX_MEMO);
genBtcBlocksThenWait(1, 2250);
@@ -162,4 +164,19 @@ public void testBobsBtcWithdrawalToExternalAddress(final TestInfo testInfo) {
testName(testInfo),
formatSatoshis(currentBalance.getAvailableBalance()));
}
+
+ @Test
+ @Order(5)
+ public void testGetTradeWithdrawalTx(final TestInfo testInfo) {
+ var trade = getTrade(bobdaemon, tradeId);
+ var withdrawalTxId = trade.getWithdrawalTxId();
+ assertNotNull(withdrawalTxId);
+
+ var txInfo = getTransaction(bobdaemon, withdrawalTxId);
+ assertEquals(WITHDRAWAL_TX_MEMO, txInfo.getMemo());
+
+ log.debug("{} Trade withdrawal Tx:\n{}",
+ testName(testInfo),
+ format(txInfo));
+ }
}
diff --git a/apitest/src/test/java/bisq/apitest/method/wallet/BtcWalletTest.java b/apitest/src/test/java/bisq/apitest/method/wallet/BtcWalletTest.java
index 7af31dcb3fc..90c46a3c814 100644
--- a/apitest/src/test/java/bisq/apitest/method/wallet/BtcWalletTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/wallet/BtcWalletTest.java
@@ -1,6 +1,7 @@
package bisq.apitest.method.wallet;
import bisq.proto.grpc.BtcBalanceInfo;
+import bisq.proto.grpc.TxInfo;
import lombok.extern.slf4j.Slf4j;
@@ -20,6 +21,7 @@
import static bisq.cli.TableFormat.formatBtcBalanceInfoTbl;
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.assertTrue;
import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
@@ -32,6 +34,8 @@
@TestMethodOrder(OrderAnnotation.class)
public class BtcWalletTest extends MethodTest {
+ private static final String TX_MEMO = "tx memo";
+
// All api tests depend on the DAO / regtest environment, and Bob & Alice's wallets
// are initialized with 10 BTC during the scaffolding setup.
private static final bisq.core.api.model.BtcBalanceInfo INITIAL_BTC_BALANCES =
@@ -99,15 +103,23 @@ public void testAliceSendBTCToBob(TestInfo testInfo) {
String bobsBtcAddress = getUnusedBtcAddress(bobdaemon);
log.debug("Sending 5.5 BTC From Alice to Bob @ {}", bobsBtcAddress);
- sendBtc(alicedaemon,
+ TxInfo txInfo = sendBtc(alicedaemon,
bobsBtcAddress,
"5.50",
"100",
- "to whom it may concern");
+ TX_MEMO);
+ assertTrue(txInfo.getIsPending());
+
+ // Note that the memo is not set on the tx yet.
+ assertTrue(txInfo.getMemo().isEmpty());
genBtcBlocksThenWait(1, 3000);
- BtcBalanceInfo alicesBalances = getBtcBalances(alicedaemon);
+ // Fetch the tx and check for confirmation and memo.
+ txInfo = getTransaction(alicedaemon, txInfo.getTxId());
+ assertFalse(txInfo.getIsPending());
+ assertEquals(TX_MEMO, txInfo.getMemo());
+ BtcBalanceInfo alicesBalances = getBtcBalances(alicedaemon);
log.debug("{} Alice's BTC Balances:\n{}",
testName(testInfo),
formatBtcBalanceInfoTbl(alicesBalances));
diff --git a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java
index 4c07452abc6..410b72ca6cb 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java
@@ -60,5 +60,6 @@ public void testTakeSellBTCOffer(final TestInfo testInfo) {
test.testBobsConfirmPaymentStarted(testInfo);
test.testAlicesConfirmPaymentReceived(testInfo);
test.testBobsBtcWithdrawalToExternalAddress(testInfo);
+ test.testGetTradeWithdrawalTx(testInfo);
}
}
diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java
index b9cb4ede0fd..4853e3a76b3 100644
--- a/cli/src/main/java/bisq/cli/CliMain.java
+++ b/cli/src/main/java/bisq/cli/CliMain.java
@@ -31,6 +31,7 @@
import bisq.proto.grpc.GetPaymentAccountsRequest;
import bisq.proto.grpc.GetPaymentMethodsRequest;
import bisq.proto.grpc.GetTradeRequest;
+import bisq.proto.grpc.GetTransactionRequest;
import bisq.proto.grpc.GetTxFeeRateRequest;
import bisq.proto.grpc.GetUnusedBsqAddressRequest;
import bisq.proto.grpc.GetVersionRequest;
@@ -116,6 +117,7 @@ private enum Method {
gettxfeerate,
settxfeerate,
unsettxfeerate,
+ gettransaction,
lockwallet,
unlockwallet,
removewalletpassword,
@@ -275,7 +277,10 @@ public static void run(String[] args) {
.build();
var reply = walletsService.sendBsq(request);
TxInfo txInfo = reply.getTxInfo();
- out.printf("%s bsq sent to %s in tx %s%n", amount, address, txInfo.getId());
+ out.printf("%s bsq sent to %s in tx %s%n",
+ amount,
+ address,
+ txInfo.getTxId());
return;
}
case sendbtc: {
@@ -305,7 +310,10 @@ public static void run(String[] args) {
.build();
var reply = walletsService.sendBtc(request);
TxInfo txInfo = reply.getTxInfo();
- out.printf("%s btc sent to %s in tx %s%n", amount, address, txInfo.getId());
+ out.printf("%s btc sent to %s in tx %s%n",
+ amount,
+ address,
+ txInfo.getTxId());
return;
}
case gettxfeerate: {
@@ -332,6 +340,18 @@ public static void run(String[] args) {
out.println(formatTxFeeRateInfo(reply.getTxFeeRateInfo()));
return;
}
+ case gettransaction: {
+ if (nonOptionArgs.size() < 2)
+ throw new IllegalArgumentException("no tx id specified");
+
+ var txId = nonOptionArgs.get(1);
+ var request = GetTransactionRequest.newBuilder()
+ .setTxId(txId)
+ .build();
+ var reply = walletsService.getTransaction(request);
+ out.println(TransactionFormat.format(reply.getTxInfo()));
+ return;
+ }
case createoffer: {
if (nonOptionArgs.size() < 9)
throw new IllegalArgumentException("incorrect parameter count,"
@@ -441,7 +461,7 @@ public static void run(String[] args) {
return;
}
case gettrade: {
- // TODO make short-id a valid argument
+ // TODO make short-id a valid argument?
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("incorrect parameter count, "
+ " expecting trade id [,showcontract = true|false]");
@@ -720,6 +740,7 @@ private static void printHelp(OptionParser parser, PrintStream stream) {
stream.format(rowFormat, "gettxfeerate", "", "Get current tx fee rate in sats/byte");
stream.format(rowFormat, "settxfeerate", "satoshis (per byte)", "Set custom tx fee rate in sats/byte");
stream.format(rowFormat, "unsettxfeerate", "", "Unset custom tx fee rate");
+ stream.format(rowFormat, "gettransaction", "transaction id", "Get transaction with id");
stream.format(rowFormat, "createoffer", "payment acct id, buy | sell, currency code, \\", "Create and place an offer");
stream.format(rowFormat, "", "amount (btc), min amount, use mkt based price, \\", "");
stream.format(rowFormat, "", "fixed price (btc) | mkt price margin (%), security deposit (%) \\", "");
diff --git a/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java b/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java
index 59b6230a2eb..e81e407d7b9 100644
--- a/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java
+++ b/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java
@@ -59,6 +59,16 @@ class ColumnHeaderConstants {
static final String COL_HEADER_TRADE_SHORT_ID = "ID";
static final String COL_HEADER_TRADE_TX_FEE = "Tx Fee(%-3s)";
static final String COL_HEADER_TRADE_TAKER_FEE = "Taker Fee(%-3s)";
+ static final String COL_HEADER_TRADE_WITHDRAWAL_TX_ID = "Withdrawal TX ID";
+
+ 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 = padEnd("%-3s(min - max)", 15, ' ');
static final String COL_HEADER_UUID = padEnd("ID", 52, ' ');
}
diff --git a/cli/src/main/java/bisq/cli/TradeFormat.java b/cli/src/main/java/bisq/cli/TradeFormat.java
index 2a28c1dccc4..0c4d4d29498 100644
--- a/cli/src/main/java/bisq/cli/TradeFormat.java
+++ b/cli/src/main/java/bisq/cli/TradeFormat.java
@@ -58,6 +58,7 @@ public static String format(TradeInfo tradeInfo) {
+ COL_HEADER_TRADE_FIAT_RECEIVED + COL_HEADER_DELIMITER
+ COL_HEADER_TRADE_PAYOUT_PUBLISHED + COL_HEADER_DELIMITER
+ COL_HEADER_TRADE_WITHDRAWN + COL_HEADER_DELIMITER
+ + (tradeInfo.getIsWithdrawn() ? COL_HEADER_TRADE_WITHDRAWAL_TX_ID + COL_HEADER_DELIMITER : "")
+ "%n";
String counterCurrencyCode = tradeInfo.getOffer().getCounterCurrencyCode();
@@ -66,18 +67,20 @@ public static String format(TradeInfo tradeInfo) {
? String.format(headersFormat, counterCurrencyCode, baseCurrencyCode, baseCurrencyCode, baseCurrencyCode)
: String.format(headersFormat, counterCurrencyCode, baseCurrencyCode, baseCurrencyCode);
- String colDataFormat = "%-" + shortIdColWidth + "s" // left justify
- + " %-" + (roleColWidth + COL_HEADER_DELIMITER.length()) + "s" // left justify
- + "%" + (COL_HEADER_PRICE.length() - 1) + "s" // right justify
- + "%" + (COL_HEADER_TRADE_AMOUNT.length() + 1) + "s" // right justify
- + "%" + (COL_HEADER_TRADE_TX_FEE.length() + 1) + "s" // right justify
- + takerFeeHeader.get() // right justify
- + " %-" + COL_HEADER_TRADE_DEPOSIT_PUBLISHED.length() + "s" // left justify
- + " %-" + COL_HEADER_TRADE_DEPOSIT_CONFIRMED.length() + "s" // left justify
- + " %-" + COL_HEADER_TRADE_FIAT_SENT.length() + "s" // left justify
- + " %-" + COL_HEADER_TRADE_FIAT_RECEIVED.length() + "s" // left justify
- + " %-" + COL_HEADER_TRADE_PAYOUT_PUBLISHED.length() + "s" // left justify
- + " %-" + COL_HEADER_TRADE_WITHDRAWN.length() + "s"; // left justify
+
+ String colDataFormat = "%-" + shortIdColWidth + "s" // lt justify
+ + " %-" + (roleColWidth + COL_HEADER_DELIMITER.length()) + "s" // left
+ + "%" + (COL_HEADER_PRICE.length() - 1) + "s" // rt justify
+ + "%" + (COL_HEADER_TRADE_AMOUNT.length() + 1) + "s" // rt justify
+ + "%" + (COL_HEADER_TRADE_TX_FEE.length() + 1) + "s" // rt justify
+ + takerFeeHeader.get() // rt justify
+ + " %-" + COL_HEADER_TRADE_DEPOSIT_PUBLISHED.length() + "s" // lt justify
+ + " %-" + COL_HEADER_TRADE_DEPOSIT_CONFIRMED.length() + "s" // lt justify
+ + " %-" + COL_HEADER_TRADE_FIAT_SENT.length() + "s" // lt justify
+ + " %-" + COL_HEADER_TRADE_FIAT_RECEIVED.length() + "s" // lt justify
+ + " %-" + COL_HEADER_TRADE_PAYOUT_PUBLISHED.length() + "s" // lt justify
+ + " %-" + COL_HEADER_TRADE_WITHDRAWN.length() + "s" // lt justify
+ + " %-" + COL_HEADER_TRADE_WITHDRAWAL_TX_ID.length() + "s"; // left
return headerLine +
(isTaker
@@ -97,7 +100,8 @@ private static String formatTradeForMaker(String format, TradeInfo tradeInfo) {
tradeInfo.getIsFiatSent() ? "YES" : "NO",
tradeInfo.getIsFiatReceived() ? "YES" : "NO",
tradeInfo.getIsPayoutPublished() ? "YES" : "NO",
- tradeInfo.getIsWithdrawn() ? "YES" : "NO");
+ tradeInfo.getIsWithdrawn() ? "YES" : "NO",
+ tradeInfo.getIsWithdrawn() ? tradeInfo.getWithdrawalTxId() : "");
}
private static String formatTradeForTaker(String format, TradeInfo tradeInfo) {
@@ -113,6 +117,7 @@ private static String formatTradeForTaker(String format, TradeInfo tradeInfo) {
tradeInfo.getIsFiatSent() ? "YES" : "NO",
tradeInfo.getIsFiatReceived() ? "YES" : "NO",
tradeInfo.getIsPayoutPublished() ? "YES" : "NO",
- tradeInfo.getIsWithdrawn() ? "YES" : "NO");
+ tradeInfo.getIsWithdrawn() ? "YES" : "NO",
+ tradeInfo.getIsWithdrawn() ? tradeInfo.getWithdrawalTxId() : "");
}
}
diff --git a/cli/src/main/java/bisq/cli/TransactionFormat.java b/cli/src/main/java/bisq/cli/TransactionFormat.java
new file mode 100644
index 00000000000..608c2fcb71f
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/TransactionFormat.java
@@ -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 .
+ */
+
+package bisq.cli;
+
+import bisq.proto.grpc.TxInfo;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import static bisq.cli.ColumnHeaderConstants.*;
+import static bisq.cli.CurrencyFormat.formatSatoshis;
+import static com.google.common.base.Strings.padEnd;
+
+@VisibleForTesting
+public class TransactionFormat {
+
+ public static String format(TxInfo txInfo) {
+ String headerLine = padEnd(COL_HEADER_TX_ID, txInfo.getTxId().length(), ' ') + COL_HEADER_DELIMITER
+ + COL_HEADER_TX_IS_CONFIRMED + COL_HEADER_DELIMITER
+ + COL_HEADER_TX_INPUT_SUM + COL_HEADER_DELIMITER
+ + COL_HEADER_TX_OUTPUT_SUM + COL_HEADER_DELIMITER
+ + COL_HEADER_TX_FEE + COL_HEADER_DELIMITER
+ + COL_HEADER_TX_SIZE + COL_HEADER_DELIMITER
+ + (txInfo.getMemo().isEmpty() ? "" : COL_HEADER_TX_MEMO + COL_HEADER_DELIMITER)
+ + "\n";
+
+ String colDataFormat = "%-" + txInfo.getTxId().length() + "s"
+ + " %" + COL_HEADER_TX_IS_CONFIRMED.length() + "s"
+ + " %" + COL_HEADER_TX_INPUT_SUM.length() + "s"
+ + " %" + COL_HEADER_TX_OUTPUT_SUM.length() + "s"
+ + " %" + COL_HEADER_TX_FEE.length() + "s"
+ + " %" + COL_HEADER_TX_SIZE.length() + "s"
+ + " %s";
+
+ return headerLine
+ + String.format(colDataFormat,
+ txInfo.getTxId(),
+ txInfo.getIsPending() ? "NO" : "YES", // pending=true means not confirmed
+ formatSatoshis(txInfo.getInputSum()),
+ formatSatoshis(txInfo.getOutputSum()),
+ formatSatoshis(txInfo.getFee()),
+ txInfo.getSize(),
+ txInfo.getMemo().isEmpty() ? "" : txInfo.getMemo());
+ }
+}
diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java
index dbba310f5dd..6709bf42ff1 100644
--- a/core/src/main/java/bisq/core/api/CoreApi.java
+++ b/core/src/main/java/bisq/core/api/CoreApi.java
@@ -279,6 +279,10 @@ public TxFeeRateInfo getMostRecentTxFeeRateInfo() {
return walletsService.getMostRecentTxFeeRateInfo();
}
+ public Transaction getTransaction(String txId) {
+ return walletsService.getTransaction(txId);
+ }
+
public void setWalletPassword(String password, String newPassword) {
walletsService.setWalletPassword(password, newPassword);
}
diff --git a/core/src/main/java/bisq/core/api/CoreWalletsService.java b/core/src/main/java/bisq/core/api/CoreWalletsService.java
index 2bbd585f8dd..c107259ff98 100644
--- a/core/src/main/java/bisq/core/api/CoreWalletsService.java
+++ b/core/src/main/java/bisq/core/api/CoreWalletsService.java
@@ -331,6 +331,26 @@ TxFeeRateInfo getMostRecentTxFeeRateInfo() {
feeService.getLastRequest());
}
+ 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()));
+ }
+ }
+
int getNumConfirmationsForMostRecentTransaction(String addressString) {
Address address = getAddressEntry(addressString).getAddress();
TransactionConfidence confidence = btcWalletService.getConfidenceForAddress(address);
diff --git a/core/src/main/java/bisq/core/api/model/TradeInfo.java b/core/src/main/java/bisq/core/api/model/TradeInfo.java
index 1a717a7672e..ec9880d5c69 100644
--- a/core/src/main/java/bisq/core/api/model/TradeInfo.java
+++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java
@@ -47,6 +47,7 @@ public class TradeInfo implements Payload {
private final String takerFeeTxId;
private final String depositTxId;
private final String payoutTxId;
+ private final String withdrawalTxId;
private final long tradeAmountAsLong;
private final long tradePrice;
private final String tradingPeerNodeAddress;
@@ -73,6 +74,7 @@ public TradeInfo(TradeInfoBuilder builder) {
this.takerFeeTxId = builder.takerFeeTxId;
this.depositTxId = builder.depositTxId;
this.payoutTxId = builder.payoutTxId;
+ this.withdrawalTxId = builder.withdrawalTxId;
this.tradeAmountAsLong = builder.tradeAmountAsLong;
this.tradePrice = builder.tradePrice;
this.tradingPeerNodeAddress = builder.tradingPeerNodeAddress;
@@ -106,6 +108,7 @@ public static TradeInfo toTradeInfo(Trade trade, String role) {
.withTakerFeeTxId(trade.getTakerFeeTxId())
.withDepositTxId(trade.getDepositTxId())
.withPayoutTxId(trade.getPayoutTxId())
+ .withWithdrawalTxId(trade.getWithdrawalTxId())
.withTradeAmountAsLong(trade.getTradeAmountAsLong())
.withTradePrice(trade.getTradePrice().getValue())
.withTradingPeerNodeAddress(Objects.requireNonNull(
@@ -141,6 +144,7 @@ public bisq.proto.grpc.TradeInfo toProtoMessage() {
.setTakerFeeTxId(takerFeeTxId == null ? "" : takerFeeTxId)
.setDepositTxId(depositTxId == null ? "" : depositTxId)
.setPayoutTxId(payoutTxId == null ? "" : payoutTxId)
+ .setWithdrawalTxId(withdrawalTxId == null ? "" : withdrawalTxId)
.setTradeAmountAsLong(tradeAmountAsLong)
.setTradePrice(tradePrice)
.setTradingPeerNodeAddress(tradingPeerNodeAddress)
@@ -180,6 +184,7 @@ public static class TradeInfoBuilder {
private String takerFeeTxId;
private String depositTxId;
private String payoutTxId;
+ private String withdrawalTxId;
private long tradeAmountAsLong;
private long tradePrice;
private String tradingPeerNodeAddress;
@@ -249,6 +254,11 @@ public TradeInfoBuilder withPayoutTxId(String payoutTxId) {
return this;
}
+ public TradeInfoBuilder withWithdrawalTxId(String withdrawalTxId) {
+ this.withdrawalTxId = withdrawalTxId;
+ return this;
+ }
+
public TradeInfoBuilder withTradeAmountAsLong(long tradeAmountAsLong) {
this.tradeAmountAsLong = tradeAmountAsLong;
return this;
@@ -332,6 +342,7 @@ public String toString() {
", takerFeeTxId='" + takerFeeTxId + '\'' + "\n" +
", depositTxId='" + depositTxId + '\'' + "\n" +
", payoutTxId='" + payoutTxId + '\'' + "\n" +
+ ", withdrawalTxId='" + withdrawalTxId + '\'' + "\n" +
", tradeAmountAsLong='" + tradeAmountAsLong + '\'' + "\n" +
", tradePrice='" + tradePrice + '\'' + "\n" +
", tradingPeerNodeAddress='" + tradingPeerNodeAddress + '\'' + "\n" +
diff --git a/core/src/main/java/bisq/core/api/model/TxInfo.java b/core/src/main/java/bisq/core/api/model/TxInfo.java
index f1b24f6b0fa..16d8f5fc108 100644
--- a/core/src/main/java/bisq/core/api/model/TxInfo.java
+++ b/core/src/main/java/bisq/core/api/model/TxInfo.java
@@ -28,26 +28,42 @@
@Getter
public class TxInfo implements Payload {
- private final String id;
+ // The client cannot see an instance of an org.bitcoinj.core.Transaction. We use the
+ // lighter weight TxInfo proto wrapper instead, containing just enough fields to
+ // view some transaction details. A block explorer or bitcoin-core client can be
+ // used to see more detail.
+
+ private final String txId;
+ private final long inputSum;
private final long outputSum;
private final long fee;
private final int size;
+ private final boolean isPending;
+ private final String memo;
- public TxInfo(String id, long outputSum, long fee, int size) {
- this.id = id;
- this.outputSum = outputSum;
- this.fee = fee;
- this.size = size;
+ public TxInfo(TxInfo.TxInfoBuilder builder) {
+ this.txId = builder.txId;
+ this.inputSum = builder.inputSum;
+ this.outputSum = builder.outputSum;
+ this.fee = builder.fee;
+ this.size = builder.size;
+ this.isPending = builder.isPending;
+ this.memo = builder.memo;
}
public static TxInfo toTxInfo(Transaction transaction) {
if (transaction == null)
throw new IllegalStateException("server created a null transaction");
- return new TxInfo(transaction.getTxId().toString(),
- transaction.getOutputSum().value,
- transaction.getFee().value,
- transaction.getMessageSize());
+ return new TxInfo.TxInfoBuilder()
+ .withTxId(transaction.getTxId().toString())
+ .withInputSum(transaction.getInputSum().value)
+ .withOutputSum(transaction.getOutputSum().value)
+ .withFee(transaction.getFee().value)
+ .withSize(transaction.getMessageSize())
+ .withIsPending(transaction.isPending())
+ .withMemo(transaction.getMemo())
+ .build();
}
//////////////////////////////////////////////////////////////////////////////////////
@@ -57,28 +73,88 @@ public static TxInfo toTxInfo(Transaction transaction) {
@Override
public bisq.proto.grpc.TxInfo toProtoMessage() {
return bisq.proto.grpc.TxInfo.newBuilder()
- .setId(id)
+ .setTxId(txId)
+ .setInputSum(inputSum)
.setOutputSum(outputSum)
.setFee(fee)
.setSize(size)
+ .setIsPending(isPending)
+ .setMemo(memo == null ? "" : memo)
.build();
}
@SuppressWarnings("unused")
public static TxInfo fromProto(bisq.proto.grpc.TxInfo proto) {
- return new TxInfo(proto.getId(),
- proto.getOutputSum(),
- proto.getFee(),
- proto.getSize());
+ return new TxInfo.TxInfoBuilder()
+ .withTxId(proto.getTxId())
+ .withInputSum(proto.getInputSum())
+ .withOutputSum(proto.getOutputSum())
+ .withFee(proto.getFee())
+ .withSize(proto.getSize())
+ .withIsPending(proto.getIsPending())
+ .withMemo(proto.getMemo())
+ .build();
+ }
+
+ public static class TxInfoBuilder {
+ private String txId;
+ private long inputSum;
+ private long outputSum;
+ private long fee;
+ private int size;
+ private boolean isPending;
+ private String memo;
+
+ public TxInfo.TxInfoBuilder withTxId(String txId) {
+ this.txId = txId;
+ return this;
+ }
+
+ public TxInfo.TxInfoBuilder withInputSum(long inputSum) {
+ this.inputSum = inputSum;
+ return this;
+ }
+
+ public TxInfo.TxInfoBuilder withOutputSum(long outputSum) {
+ this.outputSum = outputSum;
+ return this;
+ }
+
+ public TxInfo.TxInfoBuilder withFee(long fee) {
+ this.fee = fee;
+ return this;
+ }
+
+ public TxInfo.TxInfoBuilder withSize(int size) {
+ this.size = size;
+ return this;
+ }
+
+ public TxInfo.TxInfoBuilder withIsPending(boolean isPending) {
+ this.isPending = isPending;
+ return this;
+ }
+
+ public TxInfo.TxInfoBuilder withMemo(String memo) {
+ this.memo = memo;
+ return this;
+ }
+
+ public TxInfo build() {
+ return new TxInfo(this);
+ }
}
@Override
public String toString() {
return "TxInfo{" + "\n" +
- " id='" + id + '\'' + "\n" +
- ", outputSum=" + outputSum + " sats" + "\n" +
- ", fee=" + fee + " sats" + "\n" +
- ", size=" + size + " bytes" + "\n" +
+ " txId='" + txId + '\'' + "\n" +
+ ", inputSum=" + inputSum + "\n" +
+ ", outputSum=" + outputSum + "\n" +
+ ", fee=" + fee + "\n" +
+ ", size=" + size + "\n" +
+ ", isPending=" + isPending + "\n" +
+ ", memo='" + memo + '\'' + "\n" +
'}';
}
}
diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcWalletsService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcWalletsService.java
index c7bbb4623da..51b30cb1871 100644
--- a/daemon/src/main/java/bisq/daemon/grpc/GrpcWalletsService.java
+++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcWalletsService.java
@@ -29,6 +29,8 @@
import bisq.proto.grpc.GetBalancesRequest;
import bisq.proto.grpc.GetFundingAddressesReply;
import bisq.proto.grpc.GetFundingAddressesRequest;
+import bisq.proto.grpc.GetTransactionReply;
+import bisq.proto.grpc.GetTransactionRequest;
import bisq.proto.grpc.GetTxFeeRateReply;
import bisq.proto.grpc.GetTxFeeRateRequest;
import bisq.proto.grpc.GetUnusedBsqAddressReply;
@@ -280,6 +282,23 @@ public void unsetTxFeeRatePreference(UnsetTxFeeRatePreferenceRequest req,
}
}
+ @Override
+ public void getTransaction(GetTransactionRequest req,
+ StreamObserver responseObserver) {
+ try {
+ Transaction tx = coreApi.getTransaction(req.getTxId());
+ var reply = GetTransactionReply.newBuilder()
+ .setTxInfo(toTxInfo(tx).toProtoMessage())
+ .build();
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ } catch (IllegalStateException | IllegalArgumentException cause) {
+ var ex = new StatusRuntimeException(Status.UNKNOWN.withDescription(cause.getMessage()));
+ responseObserver.onError(ex);
+ throw ex;
+ }
+ }
+
@Override
public void setWalletPassword(SetWalletPasswordRequest req,
StreamObserver responseObserver) {
diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto
index a6e30cdb473..81795112e55 100644
--- a/proto/src/main/proto/grpc.proto
+++ b/proto/src/main/proto/grpc.proto
@@ -273,19 +273,20 @@ message TradeInfo {
string takerFeeTxId = 9;
string depositTxId = 10;
string payoutTxId = 11;
- uint64 tradeAmountAsLong = 12;
- uint64 tradePrice = 13;
- string tradingPeerNodeAddress = 14;
- string state = 15;
- string phase = 16;
- string tradePeriodState = 17;
- bool isDepositPublished = 18;
- bool isDepositConfirmed = 19;
- bool isFiatSent = 20;
- bool isFiatReceived = 21;
- bool isPayoutPublished = 22;
- bool isWithdrawn = 23;
- string contractAsJson = 24;
+ string withdrawalTxId = 12;
+ uint64 tradeAmountAsLong = 13;
+ uint64 tradePrice = 14;
+ string tradingPeerNodeAddress = 15;
+ string state = 16;
+ string phase = 17;
+ string tradePeriodState = 18;
+ bool isDepositPublished = 19;
+ bool isDepositConfirmed = 20;
+ bool isFiatSent = 21;
+ bool isFiatReceived = 22;
+ bool isPayoutPublished = 23;
+ bool isWithdrawn = 24;
+ string contractAsJson = 25;
}
///////////////////////////////////////////////////////////////////////////////////////////
@@ -300,10 +301,13 @@ message TxFeeRateInfo {
}
message TxInfo {
- string id = 1;
- uint64 outputSum = 2;
- uint64 fee = 3;
- int32 size = 4;
+ string txId = 1;
+ uint64 inputSum = 2;
+ uint64 outputSum = 3;
+ uint64 fee = 4;
+ int32 size = 5;
+ bool isPending = 6;
+ string memo = 7;
}
///////////////////////////////////////////////////////////////////////////////////////////
@@ -325,7 +329,9 @@ service Wallets {
}
rpc SetTxFeeRatePreference (SetTxFeeRatePreferenceRequest) returns (SetTxFeeRatePreferenceReply) {
}
- rpc unsetTxFeeRatePreference (UnsetTxFeeRatePreferenceRequest) returns (UnsetTxFeeRatePreferenceReply) {
+ rpc UnsetTxFeeRatePreference (UnsetTxFeeRatePreferenceRequest) returns (UnsetTxFeeRatePreferenceReply) {
+ }
+ rpc GetTransaction (GetTransactionRequest) returns (GetTransactionReply) {
}
rpc GetFundingAddresses (GetFundingAddressesRequest) returns (GetFundingAddressesReply) {
}
@@ -405,6 +411,14 @@ message UnsetTxFeeRatePreferenceReply {
TxFeeRateInfo txFeeRateInfo = 1;
}
+message GetTransactionRequest {
+ string txId = 1;
+}
+
+message GetTransactionReply {
+ TxInfo txInfo = 1;
+}
+
message GetFundingAddressesRequest {
}