mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-08-14 09:25:37 -04:00
Haveno
This commit is contained in:
parent
8a38081c04
commit
a22edd60f8
241 changed files with 10631 additions and 4905 deletions
|
@ -36,7 +36,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
@Slf4j
|
||||
public class BisqAppMain extends BisqExecutable {
|
||||
|
||||
public static final String DEFAULT_APP_NAME = "Bisq";
|
||||
public static final String DEFAULT_APP_NAME = "Haveno";
|
||||
|
||||
private BisqApp application;
|
||||
|
||||
|
|
|
@ -28,8 +28,6 @@ import bisq.desktop.components.AutoTooltipLabel;
|
|||
import bisq.desktop.components.AutoTooltipToggleButton;
|
||||
import bisq.desktop.components.BusyAnimation;
|
||||
import bisq.desktop.main.account.AccountView;
|
||||
import bisq.desktop.main.dao.DaoView;
|
||||
import bisq.desktop.main.funds.FundsView;
|
||||
import bisq.desktop.main.market.MarketView;
|
||||
import bisq.desktop.main.market.offerbook.OfferBookChartView;
|
||||
import bisq.desktop.main.offer.BuyOfferView;
|
||||
|
@ -180,12 +178,12 @@ public class MainView extends InitializableView<StackPane, MainViewModel>
|
|||
ToggleButton buyButton = new NavButton(BuyOfferView.class, Res.get("mainView.menu.buyBtc").toUpperCase());
|
||||
ToggleButton sellButton = new NavButton(SellOfferView.class, Res.get("mainView.menu.sellBtc").toUpperCase());
|
||||
ToggleButton portfolioButton = new NavButton(PortfolioView.class, Res.get("mainView.menu.portfolio").toUpperCase());
|
||||
ToggleButton fundsButton = new NavButton(FundsView.class, Res.get("mainView.menu.funds").toUpperCase());
|
||||
// ToggleButton fundsButton = new NavButton(FundsView.class, Res.get("mainView.menu.funds").toUpperCase());
|
||||
|
||||
ToggleButton supportButton = new NavButton(SupportView.class, Res.get("mainView.menu.support"));
|
||||
ToggleButton settingsButton = new NavButton(SettingsView.class, Res.get("mainView.menu.settings"));
|
||||
ToggleButton accountButton = new NavButton(AccountView.class, Res.get("mainView.menu.account"));
|
||||
ToggleButton daoButton = new NavButton(DaoView.class, Res.get("mainView.menu.dao"));
|
||||
// ToggleButton daoButton = new NavButton(DaoView.class, Res.get("mainView.menu.dao"));
|
||||
|
||||
JFXBadge portfolioButtonWithBadge = new JFXBadge(portfolioButton);
|
||||
JFXBadge supportButtonWithBadge = new JFXBadge(supportButton);
|
||||
|
@ -208,17 +206,17 @@ public class MainView extends InitializableView<StackPane, MainViewModel>
|
|||
sellButton.fire();
|
||||
} else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT4, keyEvent)) {
|
||||
portfolioButton.fire();
|
||||
} else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT5, keyEvent)) {
|
||||
fundsButton.fire();
|
||||
// } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT5, keyEvent)) {
|
||||
// fundsButton.fire();
|
||||
} else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT6, keyEvent)) {
|
||||
supportButton.fire();
|
||||
} else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT7, keyEvent)) {
|
||||
settingsButton.fire();
|
||||
} else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT8, keyEvent)) {
|
||||
accountButton.fire();
|
||||
} else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT9, keyEvent)) {
|
||||
if (daoButton.isVisible())
|
||||
daoButton.fire();
|
||||
// } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT9, keyEvent)) {
|
||||
// if (daoButton.isVisible())
|
||||
// daoButton.fire();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -315,14 +313,14 @@ public class MainView extends InitializableView<StackPane, MainViewModel>
|
|||
});
|
||||
|
||||
HBox primaryNav = new HBox(marketButton, getNavigationSeparator(), buyButton, getNavigationSeparator(),
|
||||
sellButton, getNavigationSeparator(), portfolioButtonWithBadge, getNavigationSeparator(), fundsButton);
|
||||
sellButton, getNavigationSeparator(), portfolioButtonWithBadge, getNavigationSeparator());
|
||||
|
||||
primaryNav.setAlignment(Pos.CENTER_LEFT);
|
||||
primaryNav.getStyleClass().add("nav-primary");
|
||||
HBox.setHgrow(primaryNav, Priority.SOMETIMES);
|
||||
|
||||
HBox secondaryNav = new HBox(supportButtonWithBadge, getNavigationSpacer(), settingsButtonWithBadge,
|
||||
getNavigationSpacer(), accountButton, getNavigationSpacer(), daoButton);
|
||||
getNavigationSpacer(), accountButton, getNavigationSpacer());
|
||||
secondaryNav.getStyleClass().add("nav-secondary");
|
||||
HBox.setHgrow(secondaryNav, Priority.SOMETIMES);
|
||||
|
||||
|
@ -364,7 +362,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel>
|
|||
baseApplicationContainer.setBottom(createFooter());
|
||||
|
||||
setupBadge(portfolioButtonWithBadge, model.getNumPendingTrades(), model.getShowPendingTradesNotification());
|
||||
setupBadge(supportButtonWithBadge, model.getNumOpenSupportTickets(), model.getShowOpenSupportTicketsNotification());
|
||||
// setupBadge(supportButtonWithBadge, model.getNumOpenSupportTickets(), model.getShowOpenSupportTicketsNotification());
|
||||
setupBadge(settingsButtonWithBadge, new SimpleStringProperty(Res.get("shared.new")), model.getShowSettingsUpdatesNotification());
|
||||
|
||||
navigation.addListener((viewPath, data) -> {
|
||||
|
|
|
@ -38,8 +38,8 @@ import bisq.core.dao.governance.bond.BondState;
|
|||
import bisq.core.dao.governance.bond.reputation.MyBondedReputation;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.coin.BsqFormatter;
|
||||
import bisq.core.util.ParsingUtils;
|
||||
import bisq.core.util.coin.BsqFormatter;
|
||||
import bisq.core.util.validation.HexStringValidator;
|
||||
import bisq.core.util.validation.IntegerValidator;
|
||||
|
||||
|
|
|
@ -24,18 +24,17 @@ import bisq.desktop.components.TitledGroupBg;
|
|||
import bisq.core.offer.availability.tasks.ProcessOfferAvailabilityResponse;
|
||||
import bisq.core.offer.availability.tasks.SendOfferAvailabilityRequest;
|
||||
import bisq.core.offer.placeoffer.tasks.AddToOfferBook;
|
||||
import bisq.core.offer.placeoffer.tasks.CreateMakerFeeTx;
|
||||
import bisq.core.offer.placeoffer.tasks.ValidateOffer;
|
||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||
import bisq.core.trade.protocol.tasks.SetupDepositTxsListener;
|
||||
import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
|
||||
import bisq.core.trade.protocol.tasks.buyer.BuyerCreateAndSignPayoutTx;
|
||||
import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDelayedPayoutTxSignatureRequest;
|
||||
import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDepositTxAndDelayedPayoutTxMessage;
|
||||
import bisq.core.trade.protocol.tasks.buyer.BuyerProcessPayoutTxPublishedMessage;
|
||||
import bisq.core.trade.protocol.tasks.buyer.BuyerSendCounterCurrencyTransferStartedMessage;
|
||||
import bisq.core.trade.protocol.tasks.buyer.BuyerSendsDelayedPayoutTxSignatureResponse;
|
||||
import bisq.core.trade.protocol.tasks.buyer.BuyerSetupDepositTxListener;
|
||||
import bisq.core.trade.protocol.tasks.buyer.BuyerSetupPayoutTxListener;
|
||||
import bisq.core.trade.protocol.tasks.buyer.BuyerSignPayoutTx;
|
||||
import bisq.core.trade.protocol.tasks.buyer.BuyerSignsDelayedPayoutTx;
|
||||
import bisq.core.trade.protocol.tasks.buyer.BuyerVerifiesFinalDelayedPayoutTx;
|
||||
import bisq.core.trade.protocol.tasks.buyer.BuyerVerifiesPreparedDelayedPayoutTx;
|
||||
|
@ -45,11 +44,12 @@ import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerCreatesDepositT
|
|||
import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerSendsDepositTxMessage;
|
||||
import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerSignsDepositTx;
|
||||
import bisq.core.trade.protocol.tasks.maker.MakerCreateAndSignContract;
|
||||
import bisq.core.trade.protocol.tasks.maker.MakerCreateFeeTx;
|
||||
import bisq.core.trade.protocol.tasks.maker.MakerProcessesInputsForDepositTxRequest;
|
||||
import bisq.core.trade.protocol.tasks.maker.MakerRemovesOpenOffer;
|
||||
import bisq.core.trade.protocol.tasks.maker.MakerSetsLockTime;
|
||||
import bisq.core.trade.protocol.tasks.maker.MakerSetupDepositTxsListener;
|
||||
import bisq.core.trade.protocol.tasks.maker.MakerVerifyTakerFeePayment;
|
||||
import bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx;
|
||||
import bisq.core.trade.protocol.tasks.seller.SellerCreatesDelayedPayoutTx;
|
||||
import bisq.core.trade.protocol.tasks.seller.SellerFinalizesDelayedPayoutTx;
|
||||
import bisq.core.trade.protocol.tasks.seller.SellerProcessCounterCurrencyTransferStartedMessage;
|
||||
|
@ -58,19 +58,19 @@ import bisq.core.trade.protocol.tasks.seller.SellerPublishesDepositTx;
|
|||
import bisq.core.trade.protocol.tasks.seller.SellerPublishesTradeStatistics;
|
||||
import bisq.core.trade.protocol.tasks.seller.SellerSendDelayedPayoutTxSignatureRequest;
|
||||
import bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage;
|
||||
import bisq.core.trade.protocol.tasks.seller.SellerSendsDepositTxAndDelayedPayoutTxMessage;
|
||||
import bisq.core.trade.protocol.tasks.seller.SellerSignAndFinalizePayoutTx;
|
||||
import bisq.core.trade.protocol.tasks.seller.SellerSignAndPublishPayoutTx;
|
||||
import bisq.core.trade.protocol.tasks.seller.SellerSignsDelayedPayoutTx;
|
||||
import bisq.core.trade.protocol.tasks.seller_as_maker.SellerAsMakerCreatesUnsignedDepositTx;
|
||||
import bisq.core.trade.protocol.tasks.seller_as_maker.SellerAsMakerFinalizesDepositTx;
|
||||
import bisq.core.trade.protocol.tasks.seller_as_maker.SellerAsMakerProcessDepositTxMessage;
|
||||
import bisq.core.trade.protocol.tasks.seller_as_maker.SellerAsMakerSendsInputsForDepositTxResponse;
|
||||
import bisq.core.trade.protocol.tasks.seller_as_taker.SellerAsTakerCreatesDepositTxInputs;
|
||||
import bisq.core.trade.protocol.tasks.seller_as_taker.SellerAsTakerSignsDepositTx;
|
||||
import bisq.core.trade.protocol.tasks.taker.CreateTakerFeeTx;
|
||||
import bisq.core.trade.protocol.tasks.taker.TakerCreateFeeTx;
|
||||
import bisq.core.trade.protocol.tasks.taker.TakerProcessesInputsForDepositTxResponse;
|
||||
import bisq.core.trade.protocol.tasks.taker.TakerProcessesMakerDepositTxMessage;
|
||||
import bisq.core.trade.protocol.tasks.taker.TakerPublishFeeTx;
|
||||
import bisq.core.trade.protocol.tasks.taker.TakerSendInputsForDepositTxRequest;
|
||||
import bisq.core.trade.protocol.tasks.taker.TakerSetupDepositTxsListener;
|
||||
import bisq.core.trade.protocol.tasks.taker.TakerVerifyAndSignContract;
|
||||
import bisq.core.trade.protocol.tasks.taker.TakerVerifyMakerFeePayment;
|
||||
|
||||
|
@ -118,7 +118,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
addGroup("PlaceOfferProtocol",
|
||||
FXCollections.observableArrayList(Arrays.asList(
|
||||
ValidateOffer.class,
|
||||
CreateMakerFeeTx.class,
|
||||
MakerCreateFeeTx.class,
|
||||
AddToOfferBook.class)
|
||||
));
|
||||
|
||||
|
@ -127,7 +127,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
FXCollections.observableArrayList(Arrays.asList(
|
||||
ApplyFilter.class,
|
||||
TakerVerifyMakerFeePayment.class,
|
||||
CreateTakerFeeTx.class,
|
||||
TakerCreateFeeTx.class, // TODO (woodser): rename to TakerCreateFeeTx
|
||||
SellerAsTakerCreatesDepositTxInputs.class,
|
||||
TakerSendInputsForDepositTxRequest.class,
|
||||
|
||||
|
@ -143,7 +143,8 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
SellerProcessDelayedPayoutTxSignatureResponse.class,
|
||||
SellerSignsDelayedPayoutTx.class,
|
||||
SellerFinalizesDelayedPayoutTx.class,
|
||||
SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
|
||||
//SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
|
||||
TakerProcessesMakerDepositTxMessage.class,
|
||||
SellerPublishesDepositTx.class,
|
||||
SellerPublishesTradeStatistics.class,
|
||||
|
||||
|
@ -153,8 +154,8 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
|
||||
ApplyFilter.class,
|
||||
TakerVerifyMakerFeePayment.class,
|
||||
SellerSignAndFinalizePayoutTx.class,
|
||||
SellerBroadcastPayoutTx.class,
|
||||
SellerSignAndPublishPayoutTx.class,
|
||||
//SellerBroadcastPayoutTx.class, // TODO (woodser): removed from main pipeline; debug view?
|
||||
SellerSendPayoutTxPublishedMessage.class
|
||||
|
||||
)
|
||||
|
@ -168,7 +169,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
MakerSetsLockTime.class,
|
||||
MakerCreateAndSignContract.class,
|
||||
BuyerAsMakerCreatesAndSignsDepositTx.class,
|
||||
BuyerSetupDepositTxListener.class,
|
||||
MakerSetupDepositTxsListener.class,
|
||||
BuyerAsMakerSendsInputsForDepositTxResponse.class,
|
||||
|
||||
BuyerProcessDelayedPayoutTxSignatureRequest.class,
|
||||
|
@ -182,7 +183,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
|
||||
ApplyFilter.class,
|
||||
MakerVerifyTakerFeePayment.class,
|
||||
BuyerSignPayoutTx.class,
|
||||
BuyerCreateAndSignPayoutTx.class,
|
||||
BuyerSetupPayoutTxListener.class,
|
||||
BuyerSendCounterCurrencyTransferStartedMessage.class,
|
||||
|
||||
|
@ -195,7 +196,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
FXCollections.observableArrayList(Arrays.asList(
|
||||
ApplyFilter.class,
|
||||
TakerVerifyMakerFeePayment.class,
|
||||
CreateTakerFeeTx.class,
|
||||
TakerCreateFeeTx.class,
|
||||
BuyerAsTakerCreatesDepositTxInputs.class,
|
||||
TakerSendInputsForDepositTxRequest.class,
|
||||
|
||||
|
@ -205,7 +206,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
TakerVerifyAndSignContract.class,
|
||||
TakerPublishFeeTx.class,
|
||||
BuyerAsTakerSignsDepositTx.class,
|
||||
BuyerSetupDepositTxListener.class,
|
||||
TakerSetupDepositTxsListener.class,
|
||||
BuyerAsTakerSendsDepositTxMessage.class,
|
||||
|
||||
BuyerProcessDelayedPayoutTxSignatureRequest.class,
|
||||
|
@ -218,7 +219,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
|
||||
ApplyFilter.class,
|
||||
TakerVerifyMakerFeePayment.class,
|
||||
BuyerSignPayoutTx.class,
|
||||
BuyerCreateAndSignPayoutTx.class,
|
||||
BuyerSetupPayoutTxListener.class,
|
||||
BuyerSendCounterCurrencyTransferStartedMessage.class,
|
||||
|
||||
|
@ -234,8 +235,9 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
MakerCreateAndSignContract.class,
|
||||
SellerAsMakerCreatesUnsignedDepositTx.class,
|
||||
SellerAsMakerSendsInputsForDepositTxResponse.class,
|
||||
SetupDepositTxsListener.class,
|
||||
|
||||
SellerAsMakerProcessDepositTxMessage.class,
|
||||
//SellerAsMakerProcessDepositTxMessage.class,
|
||||
MakerRemovesOpenOffer.class,
|
||||
SellerAsMakerFinalizesDepositTx.class,
|
||||
SellerCreatesDelayedPayoutTx.class,
|
||||
|
@ -244,7 +246,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
SellerProcessDelayedPayoutTxSignatureResponse.class,
|
||||
SellerSignsDelayedPayoutTx.class,
|
||||
SellerFinalizesDelayedPayoutTx.class,
|
||||
SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
|
||||
//SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
|
||||
SellerPublishesDepositTx.class,
|
||||
SellerPublishesTradeStatistics.class,
|
||||
|
||||
|
@ -254,8 +256,8 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
|
||||
ApplyFilter.class,
|
||||
MakerVerifyTakerFeePayment.class,
|
||||
SellerSignAndFinalizePayoutTx.class,
|
||||
SellerBroadcastPayoutTx.class,
|
||||
SellerSignAndPublishPayoutTx.class,
|
||||
//SellerBroadcastPayoutTx.class, // TODO (woodser): removed from main pipeline; debug view?
|
||||
SellerSendPayoutTxPublishedMessage.class
|
||||
)
|
||||
));
|
||||
|
|
|
@ -136,7 +136,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
|||
@Override
|
||||
public void initialize() {
|
||||
|
||||
paymentLabelString = Res.get("funds.deposit.fundBisqWallet");
|
||||
paymentLabelString = Res.get("funds.deposit.fundHavenoWallet");
|
||||
addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address")));
|
||||
balanceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.balanceWithCur", Res.getBaseCurrencyCode())));
|
||||
confirmationsColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.confirmations")));
|
||||
|
|
|
@ -17,20 +17,17 @@
|
|||
|
||||
package bisq.desktop.main.funds.locked;
|
||||
|
||||
import bisq.desktop.components.AutoTooltipLabel;
|
||||
import bisq.desktop.util.DisplayUtils;
|
||||
|
||||
import bisq.core.btc.listeners.BalanceListener;
|
||||
import bisq.core.btc.model.AddressEntry;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.WalletService;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
|
@ -67,22 +64,24 @@ class LockedListItem {
|
|||
this.btcWalletService = btcWalletService;
|
||||
this.formatter = formatter;
|
||||
|
||||
if (trade.getDepositTx() != null && !trade.getDepositTx().getOutputs().isEmpty()) {
|
||||
address = WalletService.getAddressFromOutput(trade.getDepositTx().getOutput(0));
|
||||
addressString = address != null ? address.toString() : "";
|
||||
} else {
|
||||
address = null;
|
||||
addressString = "";
|
||||
}
|
||||
balanceLabel = new AutoTooltipLabel();
|
||||
balanceListener = new BalanceListener(address) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateBalance();
|
||||
}
|
||||
};
|
||||
btcWalletService.addBalanceListener(balanceListener);
|
||||
updateBalance();
|
||||
throw new RuntimeException("Cannot listen to multisig deposits in xmr without exchanging multisig info");
|
||||
|
||||
// if (trade.getDepositTx() != null && !trade.getDepositTx().getOutputs().isEmpty()) {
|
||||
// address = WalletService.getAddressFromOutput(trade.getDepositTx().getOutput(0));
|
||||
// addressString = address != null ? address.toString() : "";
|
||||
// } else {
|
||||
// address = null;
|
||||
// addressString = "";
|
||||
// }
|
||||
// balanceLabel = new AutoTooltipLabel();
|
||||
// balanceListener = new BalanceListener(address) {
|
||||
// @Override
|
||||
// public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
// updateBalance();
|
||||
// }
|
||||
// };
|
||||
// btcWalletService.addBalanceListener(balanceListener);
|
||||
// updateBalance();
|
||||
}
|
||||
|
||||
LockedListItem() {
|
||||
|
|
|
@ -17,25 +17,27 @@
|
|||
|
||||
package bisq.desktop.main.funds.transactions;
|
||||
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.trade.Tradable;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
class DisplayedTransactions extends ObservableListDecorator<TransactionsListItem> {
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final TradableRepository tradableRepository;
|
||||
private final TransactionListItemFactory transactionListItemFactory;
|
||||
private final TransactionAwareTradableFactory transactionAwareTradableFactory;
|
||||
|
||||
DisplayedTransactions(BtcWalletService btcWalletService, TradableRepository tradableRepository,
|
||||
DisplayedTransactions(XmrWalletService xmrWalletService, TradableRepository tradableRepository,
|
||||
TransactionListItemFactory transactionListItemFactory,
|
||||
TransactionAwareTradableFactory transactionAwareTradableFactory) {
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.tradableRepository = tradableRepository;
|
||||
this.transactionListItemFactory = transactionListItemFactory;
|
||||
this.transactionAwareTradableFactory = transactionAwareTradableFactory;
|
||||
|
@ -49,13 +51,13 @@ class DisplayedTransactions extends ObservableListDecorator<TransactionsListItem
|
|||
}
|
||||
|
||||
private List<TransactionsListItem> getTransactionListItems() {
|
||||
Set<Transaction> transactions = btcWalletService.getTransactions(false);
|
||||
List<MoneroTxWallet> transactions = xmrWalletService.getTransactions(false);
|
||||
return transactions.stream()
|
||||
.map(this::convertTransactionToListItem)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private TransactionsListItem convertTransactionToListItem(Transaction transaction) {
|
||||
private TransactionsListItem convertTransactionToListItem(MoneroTxWallet transaction) {
|
||||
Set<Tradable> tradables = tradableRepository.getAll();
|
||||
|
||||
TransactionAwareTradable maybeTradable = tradables.stream()
|
||||
|
|
|
@ -17,31 +17,31 @@
|
|||
|
||||
package bisq.desktop.main.funds.transactions;
|
||||
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class DisplayedTransactionsFactory {
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final TradableRepository tradableRepository;
|
||||
private final TransactionListItemFactory transactionListItemFactory;
|
||||
private final TransactionAwareTradableFactory transactionAwareTradableFactory;
|
||||
|
||||
@Inject
|
||||
DisplayedTransactionsFactory(BtcWalletService btcWalletService,
|
||||
DisplayedTransactionsFactory(XmrWalletService xmrWalletService,
|
||||
TradableRepository tradableRepository,
|
||||
TransactionListItemFactory transactionListItemFactory,
|
||||
TransactionAwareTradableFactory transactionAwareTradableFactory) {
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.tradableRepository = tradableRepository;
|
||||
this.transactionListItemFactory = transactionListItemFactory;
|
||||
this.transactionAwareTradableFactory = transactionAwareTradableFactory;
|
||||
}
|
||||
|
||||
DisplayedTransactions create() {
|
||||
return new DisplayedTransactions(btcWalletService, tradableRepository, transactionListItemFactory,
|
||||
return new DisplayedTransactions(xmrWalletService, tradableRepository, transactionListItemFactory,
|
||||
transactionAwareTradableFactory);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,9 @@ package bisq.desktop.main.funds.transactions;
|
|||
|
||||
import bisq.core.trade.Tradable;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
class DummyTransactionAwareTradable implements TransactionAwareTradable {
|
||||
private final Tradable delegate;
|
||||
|
@ -29,7 +31,7 @@ class DummyTransactionAwareTradable implements TransactionAwareTradable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isRelatedToTransaction(Transaction transaction) {
|
||||
public boolean isRelatedToTransaction(MoneroTxWallet transaction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,9 @@ import bisq.core.offer.Offer;
|
|||
import bisq.core.offer.OpenOffer;
|
||||
import bisq.core.trade.Tradable;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
class TransactionAwareOpenOffer implements TransactionAwareTradable {
|
||||
private final OpenOffer delegate;
|
||||
|
@ -30,11 +32,11 @@ class TransactionAwareOpenOffer implements TransactionAwareTradable {
|
|||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public boolean isRelatedToTransaction(Transaction transaction) {
|
||||
public boolean isRelatedToTransaction(MoneroTxWallet transaction) {
|
||||
Offer offer = delegate.getOffer();
|
||||
String paymentTxId = offer.getOfferFeePaymentTxId();
|
||||
|
||||
String txId = transaction.getTxId().toString();
|
||||
String txId = transaction.getHash();
|
||||
|
||||
return paymentTxId.equals(txId);
|
||||
}
|
||||
|
|
|
@ -19,10 +19,12 @@ package bisq.desktop.main.funds.transactions;
|
|||
|
||||
import bisq.core.trade.Tradable;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
interface TransactionAwareTradable {
|
||||
boolean isRelatedToTransaction(Transaction transaction);
|
||||
boolean isRelatedToTransaction(MoneroTxWallet transaction);
|
||||
|
||||
Tradable asTradable();
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
package bisq.desktop.main.funds.transactions;
|
||||
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.offer.OpenOffer;
|
||||
import bisq.core.support.dispute.arbitration.ArbitrationManager;
|
||||
import bisq.core.support.dispute.refund.RefundManager;
|
||||
|
@ -34,17 +34,17 @@ import javax.inject.Singleton;
|
|||
public class TransactionAwareTradableFactory {
|
||||
private final ArbitrationManager arbitrationManager;
|
||||
private final RefundManager refundManager;
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final PubKeyRing pubKeyRing;
|
||||
|
||||
@Inject
|
||||
TransactionAwareTradableFactory(ArbitrationManager arbitrationManager,
|
||||
RefundManager refundManager,
|
||||
BtcWalletService btcWalletService,
|
||||
XmrWalletService xmrWalletService,
|
||||
PubKeyRing pubKeyRing) {
|
||||
this.arbitrationManager = arbitrationManager;
|
||||
this.refundManager = refundManager;
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.pubKeyRing = pubKeyRing;
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ public class TransactionAwareTradableFactory {
|
|||
return new TransactionAwareTrade((Trade) delegate,
|
||||
arbitrationManager,
|
||||
refundManager,
|
||||
btcWalletService,
|
||||
xmrWalletService,
|
||||
pubKeyRing);
|
||||
} else {
|
||||
return new DummyTransactionAwareTradable(delegate);
|
||||
|
|
|
@ -17,79 +17,81 @@
|
|||
|
||||
package bisq.desktop.main.funds.transactions;
|
||||
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.support.dispute.Dispute;
|
||||
import bisq.core.support.dispute.arbitration.ArbitrationManager;
|
||||
import bisq.core.support.dispute.refund.RefundManager;
|
||||
import bisq.core.trade.Contract;
|
||||
import bisq.core.trade.Tradable;
|
||||
import bisq.core.trade.Trade;
|
||||
|
||||
import bisq.common.crypto.PubKeyRing;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionOutput;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
|
||||
@Slf4j
|
||||
class TransactionAwareTrade implements TransactionAwareTradable {
|
||||
private final Trade trade;
|
||||
private final ArbitrationManager arbitrationManager;
|
||||
private final RefundManager refundManager;
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final PubKeyRing pubKeyRing;
|
||||
|
||||
TransactionAwareTrade(Trade trade,
|
||||
ArbitrationManager arbitrationManager,
|
||||
RefundManager refundManager,
|
||||
BtcWalletService btcWalletService,
|
||||
XmrWalletService xmrWalletService,
|
||||
PubKeyRing pubKeyRing) {
|
||||
this.trade = trade;
|
||||
this.arbitrationManager = arbitrationManager;
|
||||
this.refundManager = refundManager;
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.pubKeyRing = pubKeyRing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRelatedToTransaction(Transaction transaction) {
|
||||
Sha256Hash hash = transaction.getTxId();
|
||||
String txId = hash.toString();
|
||||
public boolean isRelatedToTransaction(MoneroTxWallet transaction) {
|
||||
String txId = transaction.getHash();
|
||||
|
||||
boolean isTakerOfferFeeTx = txId.equals(trade.getTakerFeeTxId());
|
||||
boolean isOfferFeeTx = isOfferFeeTx(txId);
|
||||
boolean isDepositTx = isDepositTx(hash);
|
||||
boolean isPayoutTx = isPayoutTx(hash);
|
||||
boolean isMakerDepositTx = isMakerDepositTx(txId);
|
||||
boolean isTakerDepositTx = isTakerDepositTx(txId);
|
||||
boolean isPayoutTx = isPayoutTx(txId);
|
||||
boolean isDisputedPayoutTx = isDisputedPayoutTx(txId);
|
||||
boolean isDelayedPayoutTx = transaction.getLockTime() != 0 && isDelayedPayoutTx(txId);
|
||||
boolean isRefundPayoutTx = isRefundPayoutTx(txId);
|
||||
|
||||
return isTakerOfferFeeTx || isOfferFeeTx || isDepositTx || isPayoutTx ||
|
||||
isDisputedPayoutTx || isDelayedPayoutTx || isRefundPayoutTx;
|
||||
return isTakerOfferFeeTx || isOfferFeeTx || isMakerDepositTx || isTakerDepositTx ||
|
||||
isPayoutTx || isDisputedPayoutTx;
|
||||
}
|
||||
|
||||
private boolean isPayoutTx(Sha256Hash txId) {
|
||||
return Optional.ofNullable(trade.getPayoutTx())
|
||||
.map(Transaction::getTxId)
|
||||
.map(hash -> hash.equals(txId))
|
||||
.orElse(false);
|
||||
private boolean isPayoutTx(String txId) {
|
||||
return Optional.ofNullable(trade.getPayoutTx())
|
||||
.map(MoneroTxWallet::getHash)
|
||||
.map(hash -> hash.equals(txId))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
private boolean isDepositTx(Sha256Hash txId) {
|
||||
return Optional.ofNullable(trade.getDepositTx())
|
||||
.map(Transaction::getTxId)
|
||||
.map(hash -> hash.equals(txId))
|
||||
.orElse(false);
|
||||
private boolean isMakerDepositTx(String txId) {
|
||||
return Optional.ofNullable(trade.getMakerDepositTx())
|
||||
.map(MoneroTxWallet::getHash)
|
||||
.map(hash -> hash.equals(txId))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
private boolean isTakerDepositTx(String txId) {
|
||||
return Optional.ofNullable(trade.getTakerDepositTx())
|
||||
.map(MoneroTxWallet::getHash)
|
||||
.map(hash -> hash.equals(txId))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
private boolean isOfferFeeTx(String txId) {
|
||||
|
@ -117,59 +119,59 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
});
|
||||
}
|
||||
|
||||
boolean isDelayedPayoutTx(String txId) {
|
||||
Transaction transaction = btcWalletService.getTransaction(txId);
|
||||
if (transaction == null)
|
||||
return false;
|
||||
|
||||
if (transaction.getLockTime() == 0)
|
||||
return false;
|
||||
|
||||
if (transaction.getInputs() == null)
|
||||
return false;
|
||||
|
||||
return transaction.getInputs().stream()
|
||||
.anyMatch(input -> {
|
||||
TransactionOutput connectedOutput = input.getConnectedOutput();
|
||||
if (connectedOutput == null) {
|
||||
return false;
|
||||
}
|
||||
Transaction parentTransaction = connectedOutput.getParentTransaction();
|
||||
if (parentTransaction == null) {
|
||||
return false;
|
||||
}
|
||||
return isDepositTx(parentTransaction.getTxId());
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isRefundPayoutTx(String txId) {
|
||||
String tradeId = trade.getId();
|
||||
ObservableList<Dispute> disputes = refundManager.getDisputesAsObservableList();
|
||||
|
||||
boolean isAnyDisputeRelatedToThis = refundManager.getDisputedTradeIds().contains(tradeId);
|
||||
|
||||
if (isAnyDisputeRelatedToThis) {
|
||||
Transaction tx = btcWalletService.getTransaction(txId);
|
||||
if (tx != null) {
|
||||
for (TransactionOutput txo : tx.getOutputs()) {
|
||||
if (btcWalletService.isTransactionOutputMine(txo)) {
|
||||
try {
|
||||
Address receiverAddress = txo.getScriptPubKey().getToAddress(btcWalletService.getParams());
|
||||
Contract contract = checkNotNull(trade.getContract());
|
||||
String myPayoutAddressString = contract.isMyRoleBuyer(pubKeyRing) ?
|
||||
contract.getBuyerPayoutAddressString() :
|
||||
contract.getSellerPayoutAddressString();
|
||||
if (receiverAddress != null && myPayoutAddressString.equals(receiverAddress.toString())) {
|
||||
return true;
|
||||
}
|
||||
} catch (RuntimeException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// boolean isDelayedPayoutTx(String txId) {
|
||||
// Transaction transaction = btcWalletService.getTransaction(txId);
|
||||
// if (transaction == null)
|
||||
// return false;
|
||||
//
|
||||
// if (transaction.getLockTime() == 0)
|
||||
// return false;
|
||||
//
|
||||
// if (transaction.getInputs() == null)
|
||||
// return false;
|
||||
//
|
||||
// return transaction.getInputs().stream()
|
||||
// .anyMatch(input -> {
|
||||
// TransactionOutput connectedOutput = input.getConnectedOutput();
|
||||
// if (connectedOutput == null) {
|
||||
// return false;
|
||||
// }
|
||||
// Transaction parentTransaction = connectedOutput.getParentTransaction();
|
||||
// if (parentTransaction == null) {
|
||||
// return false;
|
||||
// }
|
||||
// return isDepositTx(parentTransaction.getTxId());
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// private boolean isRefundPayoutTx(String txId) {
|
||||
// String tradeId = trade.getId();
|
||||
// ObservableList<Dispute> disputes = refundManager.getDisputesAsObservableList();
|
||||
//
|
||||
// boolean isAnyDisputeRelatedToThis = refundManager.getDisputedTradeIds().contains(tradeId);
|
||||
//
|
||||
// if (isAnyDisputeRelatedToThis) {
|
||||
// Transaction tx = btcWalletService.getTransaction(txId);
|
||||
// if (tx != null) {
|
||||
// for (TransactionOutput txo : tx.getOutputs()) {
|
||||
// if (btcWalletService.isTransactionOutputMine(txo)) {
|
||||
// try {
|
||||
// Address receiverAddress = txo.getScriptPubKey().getToAddress(btcWalletService.getParams());
|
||||
// Contract contract = checkNotNull(trade.getContract());
|
||||
// String myPayoutAddressString = contract.isMyRoleBuyer(pubKeyRing) ?
|
||||
// contract.getBuyerPayoutAddressString() :
|
||||
// contract.getSellerPayoutAddressString();
|
||||
// if (receiverAddress != null && myPayoutAddressString.equals(receiverAddress.toString())) {
|
||||
// return true;
|
||||
// }
|
||||
// } catch (RuntimeException ignore) {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public Tradable asTradable() {
|
||||
|
|
|
@ -18,14 +18,12 @@
|
|||
package bisq.desktop.main.funds.transactions;
|
||||
|
||||
import bisq.core.btc.wallet.BsqWalletService;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.dao.DaoFacade;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
@ -33,30 +31,34 @@ import javax.inject.Singleton;
|
|||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
|
||||
@Singleton
|
||||
public class TransactionListItemFactory {
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final BsqWalletService bsqWalletService;
|
||||
private final DaoFacade daoFacade;
|
||||
private final CoinFormatter formatter;
|
||||
private final Preferences preferences;
|
||||
|
||||
@Inject
|
||||
TransactionListItemFactory(BtcWalletService btcWalletService,
|
||||
TransactionListItemFactory(XmrWalletService xmrWalletService,
|
||||
BsqWalletService bsqWalletService,
|
||||
DaoFacade daoFacade,
|
||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
|
||||
Preferences preferences) {
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.bsqWalletService = bsqWalletService;
|
||||
this.daoFacade = daoFacade;
|
||||
this.formatter = formatter;
|
||||
this.preferences = preferences;
|
||||
}
|
||||
|
||||
TransactionsListItem create(Transaction transaction, @Nullable TransactionAwareTradable tradable) {
|
||||
TransactionsListItem create(MoneroTxWallet transaction, @Nullable TransactionAwareTradable tradable) {
|
||||
return new TransactionsListItem(transaction,
|
||||
btcWalletService,
|
||||
xmrWalletService,
|
||||
bsqWalletService,
|
||||
tradable,
|
||||
daoFacade,
|
||||
|
|
|
@ -18,34 +18,21 @@
|
|||
package bisq.desktop.main.funds.transactions;
|
||||
|
||||
import bisq.desktop.components.indicator.TxConfidenceIndicator;
|
||||
import bisq.desktop.util.DisplayUtils;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
|
||||
import bisq.core.btc.listeners.TxConfidenceListener;
|
||||
import bisq.core.btc.wallet.BsqWalletService;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.WalletService;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.dao.DaoFacade;
|
||||
import bisq.core.dao.state.model.blockchain.TxType;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.offer.OpenOffer;
|
||||
import bisq.core.trade.Tradable;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
import org.bitcoinj.core.TransactionOutput;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
|
||||
import javafx.scene.control.Tooltip;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import lombok.Getter;
|
||||
|
@ -53,9 +40,13 @@ import lombok.extern.slf4j.Slf4j;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
@Slf4j
|
||||
class TransactionsListItem {
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final CoinFormatter formatter;
|
||||
private String dateString;
|
||||
private final Date date;
|
||||
|
@ -88,218 +79,220 @@ class TransactionsListItem {
|
|||
// used at exportCSV
|
||||
TransactionsListItem() {
|
||||
date = null;
|
||||
btcWalletService = null;
|
||||
xmrWalletService = null;
|
||||
txId = null;
|
||||
formatter = null;
|
||||
isDustAttackTx = false;
|
||||
lazyFieldsSupplier = null;
|
||||
}
|
||||
|
||||
TransactionsListItem(Transaction transaction,
|
||||
BtcWalletService btcWalletService,
|
||||
TransactionsListItem(MoneroTxWallet transaction,
|
||||
XmrWalletService xmrWalletService,
|
||||
BsqWalletService bsqWalletService,
|
||||
TransactionAwareTradable transactionAwareTradable,
|
||||
DaoFacade daoFacade,
|
||||
CoinFormatter formatter,
|
||||
long ignoreDustThreshold) {
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.formatter = formatter;
|
||||
this.memo = transaction.getMemo();
|
||||
|
||||
txId = transaction.getTxId().toString();
|
||||
|
||||
Optional<Tradable> optionalTradable = Optional.ofNullable(transactionAwareTradable)
|
||||
.map(TransactionAwareTradable::asTradable);
|
||||
|
||||
Coin valueSentToMe = btcWalletService.getValueSentToMeForTransaction(transaction);
|
||||
Coin valueSentFromMe = btcWalletService.getValueSentFromMeForTransaction(transaction);
|
||||
|
||||
// TODO check and refactor
|
||||
boolean txFeeForBsqPayment = false;
|
||||
boolean withdrawalFromBSQWallet = false;
|
||||
if (valueSentToMe.isZero()) {
|
||||
amountAsCoin = valueSentFromMe.multiply(-1);
|
||||
for (TransactionOutput output : transaction.getOutputs()) {
|
||||
if (!btcWalletService.isTransactionOutputMine(output)) {
|
||||
received = false;
|
||||
if (WalletService.isOutputScriptConvertibleToAddress(output)) {
|
||||
addressString = WalletService.getAddressStringFromOutput(output);
|
||||
if (bsqWalletService.isTransactionOutputMine(output)) {
|
||||
txFeeForBsqPayment = true;
|
||||
} else {
|
||||
direction = Res.get("funds.tx.direction.sentTo");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (valueSentFromMe.isZero()) {
|
||||
amountAsCoin = valueSentToMe;
|
||||
direction = Res.get("funds.tx.direction.receivedWith");
|
||||
received = true;
|
||||
for (TransactionOutput output : transaction.getOutputs()) {
|
||||
if (btcWalletService.isTransactionOutputMine(output) &&
|
||||
WalletService.isOutputScriptConvertibleToAddress(output)) {
|
||||
addressString = WalletService.getAddressStringFromOutput(output);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
amountAsCoin = valueSentToMe.subtract(valueSentFromMe);
|
||||
boolean outgoing = false;
|
||||
for (TransactionOutput output : transaction.getOutputs()) {
|
||||
if (!btcWalletService.isTransactionOutputMine(output)) {
|
||||
if (WalletService.isOutputScriptConvertibleToAddress(output)) {
|
||||
addressString = WalletService.getAddressStringFromOutput(output);
|
||||
if (bsqWalletService.isTransactionOutputMine(output)) {
|
||||
outgoing = false;
|
||||
txFeeForBsqPayment = true;
|
||||
|
||||
Optional<TxType> txTypeOptional = daoFacade.getOptionalTxType(txId);
|
||||
if (txTypeOptional.isPresent()) {
|
||||
if (txTypeOptional.get().equals(TxType.COMPENSATION_REQUEST))
|
||||
details = Res.get("funds.tx.compensationRequestTxFee");
|
||||
else if (txTypeOptional.get().equals(TxType.REIMBURSEMENT_REQUEST))
|
||||
details = Res.get("funds.tx.reimbursementRequestTxFee");
|
||||
else
|
||||
details = Res.get("funds.tx.daoTxFee");
|
||||
}
|
||||
} else {
|
||||
outgoing = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
addressString = WalletService.getAddressStringFromOutput(output);
|
||||
outgoing = (valueSentToMe.getValue() < valueSentFromMe.getValue());
|
||||
if (!outgoing) {
|
||||
direction = Res.get("funds.tx.direction.receivedWith");
|
||||
received = true;
|
||||
withdrawalFromBSQWallet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (outgoing) {
|
||||
direction = Res.get("funds.tx.direction.sentTo");
|
||||
received = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (txFeeForBsqPayment) {
|
||||
// direction = Res.get("funds.tx.txFeePaymentForBsqTx");
|
||||
direction = Res.get("funds.tx.direction.sentTo");
|
||||
//addressString = "";
|
||||
}
|
||||
|
||||
if (optionalTradable.isPresent()) {
|
||||
tradable = optionalTradable.get();
|
||||
detailsAvailable = true;
|
||||
String tradeId = tradable.getShortId();
|
||||
if (tradable instanceof OpenOffer) {
|
||||
details = Res.get("funds.tx.createOfferFee", tradeId);
|
||||
} else if (tradable instanceof Trade) {
|
||||
Trade trade = (Trade) tradable;
|
||||
TransactionAwareTrade transactionAwareTrade = (TransactionAwareTrade) transactionAwareTradable;
|
||||
if (trade.getTakerFeeTxId() != null && trade.getTakerFeeTxId().equals(txId)) {
|
||||
details = Res.get("funds.tx.takeOfferFee", tradeId);
|
||||
} else {
|
||||
Offer offer = trade.getOffer();
|
||||
String offerFeePaymentTxID = offer.getOfferFeePaymentTxId();
|
||||
if (offerFeePaymentTxID != null && offerFeePaymentTxID.equals(txId)) {
|
||||
details = Res.get("funds.tx.createOfferFee", tradeId);
|
||||
} else if (trade.getDepositTx() != null &&
|
||||
trade.getDepositTx().getTxId().equals(Sha256Hash.wrap(txId))) {
|
||||
details = Res.get("funds.tx.multiSigDeposit", tradeId);
|
||||
} else if (trade.getPayoutTx() != null &&
|
||||
trade.getPayoutTx().getTxId().equals(Sha256Hash.wrap(txId))) {
|
||||
details = Res.get("funds.tx.multiSigPayout", tradeId);
|
||||
|
||||
if (amountAsCoin.isZero()) {
|
||||
initialTxConfidenceVisibility = false;
|
||||
}
|
||||
} else {
|
||||
Trade.DisputeState disputeState = trade.getDisputeState();
|
||||
if (disputeState == Trade.DisputeState.DISPUTE_CLOSED) {
|
||||
if (valueSentToMe.isPositive()) {
|
||||
details = Res.get("funds.tx.disputePayout", tradeId);
|
||||
} else {
|
||||
details = Res.get("funds.tx.disputeLost", tradeId);
|
||||
initialTxConfidenceVisibility = false;
|
||||
}
|
||||
} else if (disputeState == Trade.DisputeState.REFUND_REQUEST_CLOSED ||
|
||||
disputeState == Trade.DisputeState.REFUND_REQUESTED ||
|
||||
disputeState == Trade.DisputeState.REFUND_REQUEST_STARTED_BY_PEER) {
|
||||
if (valueSentToMe.isPositive()) {
|
||||
details = Res.get("funds.tx.refund", tradeId);
|
||||
} else {
|
||||
// We have spent the deposit tx outputs to the Bisq donation address to enable
|
||||
// the refund process (refund agent -> reimbursement). As the funds have left our wallet
|
||||
// already when funding the deposit tx we show 0 BTC as amount.
|
||||
// Confirmation is not known from the BitcoinJ side (not 100% clear why) as no funds
|
||||
// left our wallet nor we received funds. So we set indicator invisible.
|
||||
amountAsCoin = Coin.ZERO;
|
||||
details = Res.get("funds.tx.collateralForRefund", tradeId);
|
||||
initialTxConfidenceVisibility = false;
|
||||
}
|
||||
} else {
|
||||
if (transactionAwareTrade.isDelayedPayoutTx(txId)) {
|
||||
details = Res.get("funds.tx.timeLockedPayoutTx", tradeId);
|
||||
initialTxConfidenceVisibility = false;
|
||||
} else {
|
||||
details = Res.get("funds.tx.unknown", tradeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (amountAsCoin.isZero()) {
|
||||
details = Res.get("funds.tx.noFundsFromDispute");
|
||||
initialTxConfidenceVisibility = false;
|
||||
} else if (withdrawalFromBSQWallet) {
|
||||
details = Res.get("funds.tx.withdrawnFromBSQWallet");
|
||||
} else if (!txFeeForBsqPayment) {
|
||||
details = received ? Res.get("funds.tx.receivedFunds") : Res.get("funds.tx.withdrawnFromWallet");
|
||||
} else if (details.isEmpty()) {
|
||||
details = Res.get("funds.tx.txFeePaymentForBsqTx");
|
||||
}
|
||||
}
|
||||
// Use tx.getIncludedInBestChainAt() when available, otherwise use tx.getUpdateTime()
|
||||
date = transaction.getIncludedInBestChainAt() != null ? transaction.getIncludedInBestChainAt() : transaction.getUpdateTime();
|
||||
dateString = DisplayUtils.formatDateTime(date);
|
||||
|
||||
isDustAttackTx = received && valueSentToMe.value < ignoreDustThreshold;
|
||||
if (isDustAttackTx) {
|
||||
details = Res.get("funds.tx.dustAttackTx");
|
||||
}
|
||||
|
||||
// confidence
|
||||
lazyFieldsSupplier = Suppliers.memoize(() -> new LazyFields() {{
|
||||
txConfidenceIndicator = new TxConfidenceIndicator();
|
||||
txConfidenceIndicator.setId("funds-confidence");
|
||||
tooltip = new Tooltip(Res.get("shared.notUsedYet"));
|
||||
txConfidenceIndicator.setProgress(0);
|
||||
txConfidenceIndicator.setTooltip(tooltip);
|
||||
txConfidenceIndicator.setVisible(initialTxConfidenceVisibility);
|
||||
|
||||
TransactionConfidence confidence = transaction.getConfidence();
|
||||
GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator);
|
||||
confirmations = confidence.getDepthInBlocks();
|
||||
}});
|
||||
|
||||
txConfidenceListener = new TxConfidenceListener(txId) {
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
||||
GUIUtil.updateConfidence(confidence, lazy().tooltip, lazy().txConfidenceIndicator);
|
||||
confirmations = confidence.getDepthInBlocks();
|
||||
}
|
||||
};
|
||||
btcWalletService.addTxConfidenceListener(txConfidenceListener);
|
||||
throw new RuntimeException("TransactionsListItem needs updated to use XMR wallet");
|
||||
// this.btcWalletService = btcWalletService;
|
||||
// this.formatter = formatter;
|
||||
// this.memo = transaction.getMemo();
|
||||
//
|
||||
// txId = transaction.getTxId().toString();
|
||||
//
|
||||
// Optional<Tradable> optionalTradable = Optional.ofNullable(transactionAwareTradable)
|
||||
// .map(TransactionAwareTradable::asTradable);
|
||||
//
|
||||
// Coin valueSentToMe = btcWalletService.getValueSentToMeForTransaction(transaction);
|
||||
// Coin valueSentFromMe = btcWalletService.getValueSentFromMeForTransaction(transaction);
|
||||
//
|
||||
// // TODO check and refactor
|
||||
// boolean txFeeForBsqPayment = false;
|
||||
// boolean withdrawalFromBSQWallet = false;
|
||||
// if (valueSentToMe.isZero()) {
|
||||
// amountAsCoin = valueSentFromMe.multiply(-1);
|
||||
// for (TransactionOutput output : transaction.getOutputs()) {
|
||||
// if (!btcWalletService.isTransactionOutputMine(output)) {
|
||||
// received = false;
|
||||
// if (WalletService.isOutputScriptConvertibleToAddress(output)) {
|
||||
// addressString = WalletService.getAddressStringFromOutput(output);
|
||||
// if (bsqWalletService.isTransactionOutputMine(output)) {
|
||||
// txFeeForBsqPayment = true;
|
||||
// } else {
|
||||
// direction = Res.get("funds.tx.direction.sentTo");
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else if (valueSentFromMe.isZero()) {
|
||||
// amountAsCoin = valueSentToMe;
|
||||
// direction = Res.get("funds.tx.direction.receivedWith");
|
||||
// received = true;
|
||||
// for (TransactionOutput output : transaction.getOutputs()) {
|
||||
// if (btcWalletService.isTransactionOutputMine(output) &&
|
||||
// WalletService.isOutputScriptConvertibleToAddress(output)) {
|
||||
// addressString = WalletService.getAddressStringFromOutput(output);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// amountAsCoin = valueSentToMe.subtract(valueSentFromMe);
|
||||
// boolean outgoing = false;
|
||||
// for (TransactionOutput output : transaction.getOutputs()) {
|
||||
// if (!btcWalletService.isTransactionOutputMine(output)) {
|
||||
// if (WalletService.isOutputScriptConvertibleToAddress(output)) {
|
||||
// addressString = WalletService.getAddressStringFromOutput(output);
|
||||
// if (bsqWalletService.isTransactionOutputMine(output)) {
|
||||
// outgoing = false;
|
||||
// txFeeForBsqPayment = true;
|
||||
//
|
||||
// Optional<TxType> txTypeOptional = daoFacade.getOptionalTxType(txId);
|
||||
// if (txTypeOptional.isPresent()) {
|
||||
// if (txTypeOptional.get().equals(TxType.COMPENSATION_REQUEST))
|
||||
// details = Res.get("funds.tx.compensationRequestTxFee");
|
||||
// else if (txTypeOptional.get().equals(TxType.REIMBURSEMENT_REQUEST))
|
||||
// details = Res.get("funds.tx.reimbursementRequestTxFee");
|
||||
// else
|
||||
// details = Res.get("funds.tx.daoTxFee");
|
||||
// }
|
||||
// } else {
|
||||
// outgoing = true;
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// } else {
|
||||
// addressString = WalletService.getAddressStringFromOutput(output);
|
||||
// outgoing = (valueSentToMe.getValue() < valueSentFromMe.getValue());
|
||||
// if (!outgoing) {
|
||||
// direction = Res.get("funds.tx.direction.receivedWith");
|
||||
// received = true;
|
||||
// withdrawalFromBSQWallet = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (outgoing) {
|
||||
// direction = Res.get("funds.tx.direction.sentTo");
|
||||
// received = false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (txFeeForBsqPayment) {
|
||||
// // direction = Res.get("funds.tx.txFeePaymentForBsqTx");
|
||||
// direction = Res.get("funds.tx.direction.sentTo");
|
||||
// //addressString = "";
|
||||
// }
|
||||
//
|
||||
// if (optionalTradable.isPresent()) {
|
||||
// tradable = optionalTradable.get();
|
||||
// detailsAvailable = true;
|
||||
// String tradeId = tradable.getShortId();
|
||||
// if (tradable instanceof OpenOffer) {
|
||||
// details = Res.get("funds.tx.createOfferFee", tradeId);
|
||||
// } else if (tradable instanceof Trade) {
|
||||
// Trade trade = (Trade) tradable;
|
||||
// TransactionAwareTrade transactionAwareTrade = (TransactionAwareTrade) transactionAwareTradable;
|
||||
// if (trade.getTakerFeeTxId() != null && trade.getTakerFeeTxId().equals(txId)) {
|
||||
// details = Res.get("funds.tx.takeOfferFee", tradeId);
|
||||
// } else {
|
||||
// Offer offer = trade.getOffer();
|
||||
// String offerFeePaymentTxID = offer.getOfferFeePaymentTxId();
|
||||
// if (offerFeePaymentTxID != null && offerFeePaymentTxID.equals(txId)) {
|
||||
// details = Res.get("funds.tx.createOfferFee", tradeId);
|
||||
// } else if (trade.getDepositTx() != null &&
|
||||
// trade.getDepositTx().getTxId().equals(Sha256Hash.wrap(txId))) {
|
||||
// details = Res.get("funds.tx.multiSigDeposit", tradeId);
|
||||
// } else if (trade.getPayoutTx() != null &&
|
||||
// trade.getPayoutTx().getTxId().equals(Sha256Hash.wrap(txId))) {
|
||||
// details = Res.get("funds.tx.multiSigPayout", tradeId);
|
||||
//
|
||||
// if (amountAsCoin.isZero()) {
|
||||
// initialTxConfidenceVisibility = false;
|
||||
// }
|
||||
// } else {
|
||||
// Trade.DisputeState disputeState = trade.getDisputeState();
|
||||
// if (disputeState == Trade.DisputeState.DISPUTE_CLOSED) {
|
||||
// if (valueSentToMe.isPositive()) {
|
||||
// details = Res.get("funds.tx.disputePayout", tradeId);
|
||||
// } else {
|
||||
// details = Res.get("funds.tx.disputeLost", tradeId);
|
||||
// initialTxConfidenceVisibility = false;
|
||||
// }
|
||||
// } else if (disputeState == Trade.DisputeState.REFUND_REQUEST_CLOSED ||
|
||||
// disputeState == Trade.DisputeState.REFUND_REQUESTED ||
|
||||
// disputeState == Trade.DisputeState.REFUND_REQUEST_STARTED_BY_PEER) {
|
||||
// if (valueSentToMe.isPositive()) {
|
||||
// details = Res.get("funds.tx.refund", tradeId);
|
||||
// } else {
|
||||
// // We have spent the deposit tx outputs to the Bisq donation address to enable
|
||||
// // the refund process (refund agent -> reimbursement). As the funds have left our wallet
|
||||
// // already when funding the deposit tx we show 0 BTC as amount.
|
||||
// // Confirmation is not known from the BitcoinJ side (not 100% clear why) as no funds
|
||||
// // left our wallet nor we received funds. So we set indicator invisible.
|
||||
// amountAsCoin = Coin.ZERO;
|
||||
// details = Res.get("funds.tx.collateralForRefund", tradeId);
|
||||
// initialTxConfidenceVisibility = false;
|
||||
// }
|
||||
// } else {
|
||||
// if (transactionAwareTrade.isDelayedPayoutTx(txId)) {
|
||||
// details = Res.get("funds.tx.timeLockedPayoutTx", tradeId);
|
||||
// initialTxConfidenceVisibility = false;
|
||||
// } else {
|
||||
// details = Res.get("funds.tx.unknown", tradeId);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// if (amountAsCoin.isZero()) {
|
||||
// details = Res.get("funds.tx.noFundsFromDispute");
|
||||
// initialTxConfidenceVisibility = false;
|
||||
// } else if (withdrawalFromBSQWallet) {
|
||||
// details = Res.get("funds.tx.withdrawnFromBSQWallet");
|
||||
// } else if (!txFeeForBsqPayment) {
|
||||
// details = received ? Res.get("funds.tx.receivedFunds") : Res.get("funds.tx.withdrawnFromWallet");
|
||||
// } else if (details.isEmpty()) {
|
||||
// details = Res.get("funds.tx.txFeePaymentForBsqTx");
|
||||
// }
|
||||
// }
|
||||
// // Use tx.getIncludedInBestChainAt() when available, otherwise use tx.getUpdateTime()
|
||||
// date = transaction.getIncludedInBestChainAt() != null ? transaction.getIncludedInBestChainAt() : transaction.getUpdateTime();
|
||||
// dateString = DisplayUtils.formatDateTime(date);
|
||||
//
|
||||
// isDustAttackTx = received && valueSentToMe.value < ignoreDustThreshold;
|
||||
// if (isDustAttackTx) {
|
||||
// details = Res.get("funds.tx.dustAttackTx");
|
||||
// }
|
||||
//
|
||||
// // confidence
|
||||
// lazyFieldsSupplier = Suppliers.memoize(() -> new LazyFields() {{
|
||||
// txConfidenceIndicator = new TxConfidenceIndicator();
|
||||
// txConfidenceIndicator.setId("funds-confidence");
|
||||
// tooltip = new Tooltip(Res.get("shared.notUsedYet"));
|
||||
// txConfidenceIndicator.setProgress(0);
|
||||
// txConfidenceIndicator.setTooltip(tooltip);
|
||||
// txConfidenceIndicator.setVisible(initialTxConfidenceVisibility);
|
||||
//
|
||||
// TransactionConfidence confidence = transaction.getConfidence();
|
||||
// GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator);
|
||||
// confirmations = confidence.getDepthInBlocks();
|
||||
// }});
|
||||
//
|
||||
// txConfidenceListener = new TxConfidenceListener(txId) {
|
||||
// @Override
|
||||
// public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
||||
// GUIUtil.updateConfidence(confidence, lazy().tooltip, lazy().txConfidenceIndicator);
|
||||
// confirmations = confidence.getDepthInBlocks();
|
||||
// }
|
||||
// };
|
||||
// btcWalletService.addTxConfidenceListener(txConfidenceListener);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
btcWalletService.removeTxConfidenceListener(txConfidenceListener);
|
||||
// TODO (woodser): remove wallet listener
|
||||
//xmrWalletService.removeTxConfidenceListener(txConfidenceListener);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,26 +19,26 @@ package bisq.desktop.main.funds.withdrawal;
|
|||
|
||||
import bisq.desktop.components.AutoTooltipLabel;
|
||||
|
||||
import bisq.core.btc.listeners.BalanceListener;
|
||||
import bisq.core.btc.model.AddressEntry;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.listeners.XmrBalanceListener;
|
||||
import bisq.core.btc.model.XmrAddressEntry;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
class WithdrawalListItem {
|
||||
private final BalanceListener balanceListener;
|
||||
private final XmrBalanceListener balanceListener;
|
||||
private final Label balanceLabel;
|
||||
private final AddressEntry addressEntry;
|
||||
private final BtcWalletService walletService;
|
||||
private final XmrAddressEntry addressEntry;
|
||||
private final XmrWalletService walletService;
|
||||
private final CoinFormatter formatter;
|
||||
private Coin balance;
|
||||
private final String addressString;
|
||||
|
@ -46,7 +46,7 @@ class WithdrawalListItem {
|
|||
@Getter
|
||||
private boolean isSelected;
|
||||
|
||||
public WithdrawalListItem(AddressEntry addressEntry, BtcWalletService walletService,
|
||||
public WithdrawalListItem(XmrAddressEntry addressEntry, XmrWalletService walletService,
|
||||
CoinFormatter formatter) {
|
||||
this.addressEntry = addressEntry;
|
||||
this.walletService = walletService;
|
||||
|
@ -55,9 +55,9 @@ class WithdrawalListItem {
|
|||
|
||||
// balance
|
||||
balanceLabel = new AutoTooltipLabel();
|
||||
balanceListener = new BalanceListener(getAddress()) {
|
||||
balanceListener = new XmrBalanceListener(addressEntry.getAccountIndex()) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
public void onBalanceChanged(BigInteger balance) {
|
||||
updateBalance();
|
||||
}
|
||||
};
|
||||
|
@ -71,7 +71,7 @@ class WithdrawalListItem {
|
|||
}
|
||||
|
||||
private void updateBalance() {
|
||||
balance = walletService.getBalanceForAddress(addressEntry.getAddress());
|
||||
balance = walletService.getBalanceForAccount(addressEntry.getAccountIndex());
|
||||
if (balance != null)
|
||||
balanceLabel.setText(formatter.formatCoin(this.balance));
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ class WithdrawalListItem {
|
|||
return Res.getWithCol("shared.offerId") + " " + addressEntry.getShortOfferId();
|
||||
else if (addressEntry.isTrade())
|
||||
return Res.getWithCol("shared.tradeId") + " " + addressEntry.getShortOfferId();
|
||||
else if (addressEntry.getContext() == AddressEntry.Context.ARBITRATOR)
|
||||
else if (addressEntry.getContext() == XmrAddressEntry.Context.ARBITRATOR)
|
||||
return Res.get("funds.withdrawal.arbitrationFee");
|
||||
else
|
||||
return "-";
|
||||
|
@ -102,11 +102,7 @@ class WithdrawalListItem {
|
|||
return addressEntry.hashCode();
|
||||
}
|
||||
|
||||
private Address getAddress() {
|
||||
return addressEntry.getAddress();
|
||||
}
|
||||
|
||||
public AddressEntry getAddressEntry() {
|
||||
public XmrAddressEntry getAddressEntry() {
|
||||
return addressEntry;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,23 +26,16 @@ import bisq.desktop.components.HyperlinkWithIcon;
|
|||
import bisq.desktop.components.InputTextField;
|
||||
import bisq.desktop.components.TitledGroupBg;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.main.overlays.windows.TxDetails;
|
||||
import bisq.desktop.main.overlays.windows.WalletPasswordWindow;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
import bisq.desktop.util.Layout;
|
||||
|
||||
import bisq.core.btc.exceptions.AddressEntryException;
|
||||
import bisq.core.btc.exceptions.InsufficientFundsException;
|
||||
import bisq.core.btc.listeners.BalanceListener;
|
||||
import bisq.core.btc.model.AddressEntry;
|
||||
import bisq.core.btc.listeners.XmrBalanceListener;
|
||||
import bisq.core.btc.setup.WalletsSetup;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.Restrictions;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.TradeManager;
|
||||
import bisq.core.user.DontShowAgainLookup;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
import bisq.core.util.ParsingUtils;
|
||||
|
@ -51,15 +44,11 @@ import bisq.core.util.validation.BtcAddressValidator;
|
|||
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.util.Tuple3;
|
||||
import bisq.common.util.Tuple4;
|
||||
|
||||
import org.bitcoinj.core.AddressFormatException;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
@ -101,18 +90,14 @@ import javafx.util.Callback;
|
|||
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static bisq.desktop.util.FormBuilder.*;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@FxmlView
|
||||
public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
|
@ -128,7 +113,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
private Label amountLabel;
|
||||
private TextField amountTextField, withdrawFromTextField, withdrawToTextField, withdrawMemoTextField, transactionFeeInputTextField;
|
||||
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final TradeManager tradeManager;
|
||||
private final P2PService p2PService;
|
||||
private final WalletsSetup walletsSetup;
|
||||
|
@ -139,7 +124,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
private final ObservableList<WithdrawalListItem> observableList = FXCollections.observableArrayList();
|
||||
private final SortedList<WithdrawalListItem> sortedList = new SortedList<>(observableList);
|
||||
private final Set<WithdrawalListItem> selectedItems = new HashSet<>();
|
||||
private BalanceListener balanceListener;
|
||||
private XmrBalanceListener balanceListener;
|
||||
private Set<String> fromAddresses = new HashSet<>();
|
||||
private Coin totalAvailableAmountOfSelectedItems = Coin.ZERO;
|
||||
private Coin amountAsCoin = Coin.ZERO;
|
||||
|
@ -160,7 +145,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
private WithdrawalView(BtcWalletService btcWalletService,
|
||||
private WithdrawalView(XmrWalletService xmrWalletService,
|
||||
TradeManager tradeManager,
|
||||
P2PService p2PService,
|
||||
WalletsSetup walletsSetup,
|
||||
|
@ -169,7 +154,8 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
BtcAddressValidator btcAddressValidator,
|
||||
WalletPasswordWindow walletPasswordWindow,
|
||||
FeeService feeService) {
|
||||
this.btcWalletService = btcWalletService;
|
||||
// throw new RuntimeException("WithdrawalView needs updated to use XMR wallet");
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.tradeManager = tradeManager;
|
||||
this.p2PService = p2PService;
|
||||
this.walletsSetup = walletsSetup;
|
||||
|
@ -295,9 +281,9 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
balanceColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
tableView.getSortOrder().add(balanceColumn);
|
||||
|
||||
balanceListener = new BalanceListener() {
|
||||
balanceListener = new XmrBalanceListener() {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
public void onBalanceChanged(BigInteger balance) {
|
||||
updateList();
|
||||
}
|
||||
};
|
||||
|
@ -346,7 +332,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
|
||||
amountTextField.textProperty().addListener(amountListener);
|
||||
amountTextField.focusedProperty().addListener(amountFocusListener);
|
||||
btcWalletService.addBalanceListener(balanceListener);
|
||||
xmrWalletService.addBalanceListener(balanceListener);
|
||||
feeToggleGroup.selectedToggleProperty().addListener(feeToggleGroupListener);
|
||||
inputsToggleGroup.selectedToggleProperty().addListener(inputsToggleGroupListener);
|
||||
|
||||
|
@ -371,7 +357,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
protected void deactivate() {
|
||||
sortedList.comparatorProperty().unbind();
|
||||
observableList.forEach(WithdrawalListItem::cleanup);
|
||||
btcWalletService.removeBalanceListener(balanceListener);
|
||||
xmrWalletService.removeBalanceListener(balanceListener);
|
||||
amountTextField.textProperty().removeListener(amountListener);
|
||||
amountTextField.focusedProperty().removeListener(amountFocusListener);
|
||||
feeToggleGroup.selectedToggleProperty().removeListener(feeToggleGroupListener);
|
||||
|
@ -388,107 +374,108 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void onWithdraw() {
|
||||
if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, walletsSetup)) {
|
||||
try {
|
||||
final String withdrawToAddress = withdrawToTextField.getText();
|
||||
final Coin sendersAmount;
|
||||
|
||||
// We do not know sendersAmount if senderPaysFee is true. We repeat fee calculation after first attempt if senderPaysFee is true.
|
||||
Transaction feeEstimationTransaction = btcWalletService.getFeeEstimationTransactionForMultipleAddresses(fromAddresses, amountAsCoin);
|
||||
if (feeExcluded && feeEstimationTransaction != null) {
|
||||
feeEstimationTransaction = btcWalletService.getFeeEstimationTransactionForMultipleAddresses(fromAddresses, amountAsCoin.add(feeEstimationTransaction.getFee()));
|
||||
}
|
||||
checkNotNull(feeEstimationTransaction, "feeEstimationTransaction must not be null");
|
||||
|
||||
Coin dust = btcWalletService.getDust(feeEstimationTransaction);
|
||||
Coin fee = feeEstimationTransaction.getFee().add(dust);
|
||||
Coin receiverAmount;
|
||||
// amountAsCoin is what the user typed into the withdrawal field.
|
||||
// this can be interpreted as either the senders amount or receivers amount depending
|
||||
// on a radio button "fee excluded / fee included".
|
||||
// therefore we calculate the actual sendersAmount and receiverAmount as follows:
|
||||
if (feeExcluded) {
|
||||
receiverAmount = amountAsCoin;
|
||||
sendersAmount = receiverAmount.add(fee);
|
||||
} else {
|
||||
sendersAmount = amountAsCoin.add(dust); // sendersAmount bumped up to UTXO size when dust is in play
|
||||
receiverAmount = sendersAmount.subtract(fee);
|
||||
}
|
||||
if (dust.isPositive()) {
|
||||
log.info("Dust output ({} satoshi) was detected, the dust amount has been added to the fee (was {}, now {})",
|
||||
dust.value,
|
||||
feeEstimationTransaction.getFee(),
|
||||
fee.value);
|
||||
}
|
||||
|
||||
if (areInputsValid(sendersAmount)) {
|
||||
int txVsize = feeEstimationTransaction.getVsize();
|
||||
log.info("Fee for tx with size {}: {} " + Res.getBaseCurrencyCode() + "", txVsize, fee.toPlainString());
|
||||
|
||||
if (receiverAmount.isPositive()) {
|
||||
double vkb = txVsize / 1000d;
|
||||
|
||||
String messageText = Res.get("shared.sendFundsDetailsWithFee",
|
||||
formatter.formatCoinWithCode(sendersAmount),
|
||||
withdrawFromTextField.getText(),
|
||||
withdrawToAddress,
|
||||
formatter.formatCoinWithCode(fee),
|
||||
Double.parseDouble(transactionFeeInputTextField.getText()),
|
||||
vkb,
|
||||
formatter.formatCoinWithCode(receiverAmount));
|
||||
if (dust.isPositive()) {
|
||||
messageText = Res.get("shared.sendFundsDetailsDust",
|
||||
dust.value, dust.value > 1 ? "s" : "")
|
||||
+ messageText;
|
||||
}
|
||||
|
||||
new Popup().headLine(Res.get("funds.withdrawal.confirmWithdrawalRequest"))
|
||||
.confirmation(messageText)
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.onAction(() -> doWithdraw(sendersAmount, fee, new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
|
||||
if (transaction != null) {
|
||||
String key = "showTransactionSent";
|
||||
if (DontShowAgainLookup.showAgain(key)) {
|
||||
new TxDetails(transaction.getTxId().toString(), withdrawToAddress, formatter.formatCoinWithCode(sendersAmount))
|
||||
.dontShowAgainId(key)
|
||||
.show();
|
||||
}
|
||||
log.debug("onWithdraw onSuccess tx ID:{}", transaction.getTxId().toString());
|
||||
} else {
|
||||
log.error("onWithdraw transaction is null");
|
||||
}
|
||||
|
||||
List<Trade> trades = new ArrayList<>(tradeManager.getObservableList());
|
||||
trades.stream()
|
||||
.filter(Trade::isPayoutPublished)
|
||||
.forEach(trade -> btcWalletService.getAddressEntry(trade.getId(), AddressEntry.Context.TRADE_PAYOUT)
|
||||
.ifPresent(addressEntry -> {
|
||||
if (btcWalletService.getBalanceForAddress(addressEntry.getAddress()).isZero())
|
||||
tradeManager.onTradeCompleted(trade);
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable t) {
|
||||
log.error("onWithdraw onFailure");
|
||||
}
|
||||
}))
|
||||
.closeButtonText(Res.get("shared.cancel"))
|
||||
.show();
|
||||
} else {
|
||||
new Popup().warning(Res.get("portfolio.pending.step5_buyer.amountTooLow")).show();
|
||||
}
|
||||
}
|
||||
} catch (InsufficientFundsException e) {
|
||||
new Popup().warning(Res.get("funds.withdrawal.warn.amountExceeds") + "\n\nError message:\n" + e.getMessage()).show();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.toString());
|
||||
new Popup().warning(e.toString()).show();
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("WithdrawalView.onWithdraw() not updated to XMR");
|
||||
// if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, walletsSetup)) {
|
||||
// try {
|
||||
// final String withdrawToAddress = withdrawToTextField.getText();
|
||||
// final Coin sendersAmount;
|
||||
//
|
||||
// // We do not know sendersAmount if senderPaysFee is true. We repeat fee calculation after first attempt if senderPaysFee is true.
|
||||
// Transaction feeEstimationTransaction = btcWalletService.getFeeEstimationTransactionForMultipleAddresses(fromAddresses, amountAsCoin);
|
||||
// if (feeExcluded && feeEstimationTransaction != null) {
|
||||
// feeEstimationTransaction = btcWalletService.getFeeEstimationTransactionForMultipleAddresses(fromAddresses, amountAsCoin.add(feeEstimationTransaction.getFee()));
|
||||
// }
|
||||
// checkNotNull(feeEstimationTransaction, "feeEstimationTransaction must not be null");
|
||||
//
|
||||
// Coin dust = btcWalletService.getDust(feeEstimationTransaction);
|
||||
// Coin fee = feeEstimationTransaction.getFee().add(dust);
|
||||
// Coin receiverAmount;
|
||||
// // amountAsCoin is what the user typed into the withdrawal field.
|
||||
// // this can be interpreted as either the senders amount or receivers amount depending
|
||||
// // on a radio button "fee excluded / fee included".
|
||||
// // therefore we calculate the actual sendersAmount and receiverAmount as follows:
|
||||
// if (feeExcluded) {
|
||||
// receiverAmount = amountAsCoin;
|
||||
// sendersAmount = receiverAmount.add(fee);
|
||||
// } else {
|
||||
// sendersAmount = amountAsCoin.add(dust); // sendersAmount bumped up to UTXO size when dust is in play
|
||||
// receiverAmount = sendersAmount.subtract(fee);
|
||||
// }
|
||||
// if (dust.isPositive()) {
|
||||
// log.info("Dust output ({} satoshi) was detected, the dust amount has been added to the fee (was {}, now {})",
|
||||
// dust.value,
|
||||
// feeEstimationTransaction.getFee(),
|
||||
// fee.value);
|
||||
// }
|
||||
//
|
||||
// if (areInputsValid(sendersAmount)) {
|
||||
// int txVsize = feeEstimationTransaction.getVsize();
|
||||
// log.info("Fee for tx with size {}: {} " + Res.getBaseCurrencyCode() + "", txVsize, fee.toPlainString());
|
||||
//
|
||||
// if (receiverAmount.isPositive()) {
|
||||
// double vkb = txVsize / 1000d;
|
||||
//
|
||||
// String messageText = Res.get("shared.sendFundsDetailsWithFee",
|
||||
// formatter.formatCoinWithCode(sendersAmount),
|
||||
// withdrawFromTextField.getText(),
|
||||
// withdrawToAddress,
|
||||
// formatter.formatCoinWithCode(fee),
|
||||
// Double.parseDouble(transactionFeeInputTextField.getText()),
|
||||
// vkb,
|
||||
// formatter.formatCoinWithCode(receiverAmount));
|
||||
// if (dust.isPositive()) {
|
||||
// messageText = Res.get("shared.sendFundsDetailsDust",
|
||||
// dust.value, dust.value > 1 ? "s" : "")
|
||||
// + messageText;
|
||||
// }
|
||||
//
|
||||
// new Popup().headLine(Res.get("funds.withdrawal.confirmWithdrawalRequest"))
|
||||
// .confirmation(messageText)
|
||||
// .actionButtonText(Res.get("shared.yes"))
|
||||
// .onAction(() -> doWithdraw(sendersAmount, fee, new FutureCallback<>() {
|
||||
// @Override
|
||||
// public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
|
||||
// if (transaction != null) {
|
||||
// String key = "showTransactionSent";
|
||||
// if (DontShowAgainLookup.showAgain(key)) {
|
||||
// new TxDetails(transaction.getTxId().toString(), withdrawToAddress, formatter.formatCoinWithCode(sendersAmount))
|
||||
// .dontShowAgainId(key)
|
||||
// .show();
|
||||
// }
|
||||
// log.debug("onWithdraw onSuccess tx ID:{}", transaction.getTxId().toString());
|
||||
// } else {
|
||||
// log.error("onWithdraw transaction is null");
|
||||
// }
|
||||
//
|
||||
// List<Trade> trades = new ArrayList<>(tradeManager.getObservableList());
|
||||
// trades.stream()
|
||||
// .filter(Trade::isPayoutPublished)
|
||||
// .forEach(trade -> btcWalletService.getAddressEntry(trade.getId(), AddressEntry.Context.TRADE_PAYOUT)
|
||||
// .ifPresent(addressEntry -> {
|
||||
// if (btcWalletService.getBalanceForAddress(addressEntry.getAddress()).isZero())
|
||||
// tradeManager.onTradeCompleted(trade);
|
||||
// }));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onFailure(@NotNull Throwable t) {
|
||||
// log.error("onWithdraw onFailure");
|
||||
// }
|
||||
// }))
|
||||
// .closeButtonText(Res.get("shared.cancel"))
|
||||
// .show();
|
||||
// } else {
|
||||
// new Popup().warning(Res.get("portfolio.pending.step5_buyer.amountTooLow")).show();
|
||||
// }
|
||||
// }
|
||||
// } catch (InsufficientFundsException e) {
|
||||
// new Popup().warning(Res.get("funds.withdrawal.warn.amountExceeds") + "\n\nError message:\n" + e.getMessage()).show();
|
||||
// } catch (Throwable e) {
|
||||
// e.printStackTrace();
|
||||
// log.error(e.toString());
|
||||
// new Popup().warning(e.toString()).show();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private void selectForWithdrawal(WithdrawalListItem item) {
|
||||
|
@ -546,55 +533,58 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void updateList() {
|
||||
//throw new RuntimeException("WithdrawalView.updateList() needs updated to use XMR");
|
||||
observableList.forEach(WithdrawalListItem::cleanup);
|
||||
observableList.setAll(btcWalletService.getAddressEntriesForAvailableBalanceStream()
|
||||
.map(addressEntry -> new WithdrawalListItem(addressEntry, btcWalletService, formatter))
|
||||
observableList.setAll(xmrWalletService.getAddressEntriesForAvailableBalanceStream()
|
||||
.map(addressEntry -> new WithdrawalListItem(addressEntry, xmrWalletService, formatter))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
updateInputSelection();
|
||||
}
|
||||
|
||||
private void doWithdraw(Coin amount, Coin fee, FutureCallback<Transaction> callback) {
|
||||
if (btcWalletService.isEncrypted()) {
|
||||
UserThread.runAfter(() -> walletPasswordWindow.onAesKey(aesKey ->
|
||||
sendFunds(amount, fee, aesKey, callback))
|
||||
.show(), 300, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
sendFunds(amount, fee, null, callback);
|
||||
}
|
||||
throw new RuntimeException("WithdrawalView.doWithdraw() not updated to XMR");
|
||||
// if (xmrWalletService.isEncrypted()) {
|
||||
// UserThread.runAfter(() -> walletPasswordWindow.onAesKey(aesKey ->
|
||||
// sendFunds(amount, fee, aesKey, callback))
|
||||
// .show(), 300, TimeUnit.MILLISECONDS);
|
||||
// } else {
|
||||
// sendFunds(amount, fee, null, callback);
|
||||
// }
|
||||
}
|
||||
|
||||
private void sendFunds(Coin amount, Coin fee, KeyParameter aesKey, FutureCallback<Transaction> callback) {
|
||||
try {
|
||||
String memo = withdrawMemoTextField.getText();
|
||||
if (memo.isEmpty()) {
|
||||
memo = null;
|
||||
}
|
||||
Transaction transaction = btcWalletService.sendFundsForMultipleAddresses(fromAddresses,
|
||||
withdrawToTextField.getText(),
|
||||
amount,
|
||||
fee,
|
||||
null,
|
||||
aesKey,
|
||||
memo,
|
||||
callback);
|
||||
|
||||
reset();
|
||||
updateList();
|
||||
} catch (AddressFormatException e) {
|
||||
new Popup().warning(Res.get("validation.btc.invalidAddress")).show();
|
||||
} catch (Wallet.DustySendRequested e) {
|
||||
new Popup().warning(Res.get("validation.amountBelowDust",
|
||||
formatter.formatCoinWithCode(Restrictions.getMinNonDustOutput()))).show();
|
||||
} catch (AddressEntryException e) {
|
||||
new Popup().error(e.getMessage()).show();
|
||||
} catch (InsufficientMoneyException e) {
|
||||
log.warn(e.getMessage());
|
||||
new Popup().warning(Res.get("funds.withdrawal.notEnoughFunds") + "\n\nError message:\n" + e.getMessage()).show();
|
||||
} catch (Throwable e) {
|
||||
log.warn(e.toString());
|
||||
new Popup().warning(e.toString()).show();
|
||||
}
|
||||
throw new RuntimeException("WithdrawalView.sendFunds() not updated to XMR");
|
||||
// try {
|
||||
// String memo = withdrawMemoTextField.getText();
|
||||
// if (memo.isEmpty()) {
|
||||
// memo = null;
|
||||
// }
|
||||
// Transaction transaction = btcWalletService.sendFundsForMultipleAddresses(fromAddresses,
|
||||
// withdrawToTextField.getText(),
|
||||
// amount,
|
||||
// fee,
|
||||
// null,
|
||||
// aesKey,
|
||||
// memo,
|
||||
// callback);
|
||||
//
|
||||
// reset();
|
||||
// updateList();
|
||||
// } catch (AddressFormatException e) {
|
||||
// new Popup().warning(Res.get("validation.btc.invalidAddress")).show();
|
||||
// } catch (Wallet.DustySendRequested e) {
|
||||
// new Popup().warning(Res.get("validation.amountBelowDust",
|
||||
// formatter.formatCoinWithCode(Restrictions.getMinNonDustOutput()))).show();
|
||||
// } catch (AddressEntryException e) {
|
||||
// new Popup().error(e.getMessage()).show();
|
||||
// } catch (InsufficientMoneyException e) {
|
||||
// log.warn(e.getMessage());
|
||||
// new Popup().warning(Res.get("funds.withdrawal.notEnoughFunds") + "\n\nError message:\n" + e.getMessage()).show();
|
||||
// } catch (Throwable e) {
|
||||
// log.warn(e.toString());
|
||||
// new Popup().warning(e.toString()).show();
|
||||
// }
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
|
|
|
@ -23,12 +23,12 @@ import bisq.desktop.util.GUIUtil;
|
|||
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.btc.TxFeeEstimationService;
|
||||
import bisq.core.btc.listeners.BalanceListener;
|
||||
import bisq.core.btc.listeners.BsqBalanceListener;
|
||||
import bisq.core.btc.model.AddressEntry;
|
||||
import bisq.core.btc.listeners.XmrBalanceListener;
|
||||
import bisq.core.btc.model.XmrAddressEntry;
|
||||
import bisq.core.btc.wallet.BsqWalletService;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.Restrictions;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.CurrencyUtil;
|
||||
import bisq.core.locale.TradeCurrency;
|
||||
import bisq.core.monetary.Price;
|
||||
|
@ -58,7 +58,6 @@ import bisq.common.util.Tuple2;
|
|||
import bisq.common.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
|
@ -81,6 +80,8 @@ import javafx.collections.FXCollections;
|
|||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.SetChangeListener;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
|
@ -98,6 +99,7 @@ import static java.util.Comparator.comparing;
|
|||
public abstract class MutableOfferDataModel extends OfferDataModel implements BsqBalanceListener {
|
||||
private final CreateOfferService createOfferService;
|
||||
protected final OpenOfferManager openOfferManager;
|
||||
private final XmrWalletService xmrWalletService;
|
||||
private final BsqWalletService bsqWalletService;
|
||||
private final Preferences preferences;
|
||||
protected final User user;
|
||||
|
@ -109,7 +111,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
private final CoinFormatter btcFormatter;
|
||||
private final Navigation navigation;
|
||||
private final String offerId;
|
||||
private final BalanceListener btcBalanceListener;
|
||||
private final XmrBalanceListener xmrBalanceListener;
|
||||
private final SetChangeListener<PaymentAccount> paymentAccountsChangeListener;
|
||||
|
||||
protected OfferPayload.Direction direction;
|
||||
|
@ -152,7 +154,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
public MutableOfferDataModel(CreateOfferService createOfferService,
|
||||
OpenOfferManager openOfferManager,
|
||||
OfferUtil offerUtil,
|
||||
BtcWalletService btcWalletService,
|
||||
XmrWalletService xmrWalletService,
|
||||
BsqWalletService bsqWalletService,
|
||||
Preferences preferences,
|
||||
User user,
|
||||
|
@ -163,8 +165,9 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
||||
TradeStatisticsManager tradeStatisticsManager,
|
||||
Navigation navigation) {
|
||||
super(btcWalletService, offerUtil);
|
||||
super(xmrWalletService, offerUtil);
|
||||
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.createOfferService = createOfferService;
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.bsqWalletService = bsqWalletService;
|
||||
|
@ -180,14 +183,14 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
|
||||
offerId = createOfferService.getRandomOfferId();
|
||||
shortOfferId = Utilities.getShortId(offerId);
|
||||
addressEntry = btcWalletService.getOrCreateAddressEntry(offerId, AddressEntry.Context.OFFER_FUNDING);
|
||||
addressEntry = xmrWalletService.getOrCreateAddressEntry(offerId, XmrAddressEntry.Context.OFFER_FUNDING);
|
||||
|
||||
useMarketBasedPrice.set(preferences.isUsePercentageBasedPrice());
|
||||
buyerSecurityDeposit.set(Restrictions.getMinBuyerSecurityDepositAsPercent());
|
||||
|
||||
btcBalanceListener = new BalanceListener(getAddressEntry().getAddress()) {
|
||||
xmrBalanceListener = new XmrBalanceListener(getAddressEntry().getAccountIndex()) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
public void onBalanceChanged(BigInteger balance) {
|
||||
updateBalance();
|
||||
}
|
||||
};
|
||||
|
@ -211,13 +214,13 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
}
|
||||
|
||||
private void addListeners() {
|
||||
btcWalletService.addBalanceListener(btcBalanceListener);
|
||||
xmrWalletService.addBalanceListener(xmrBalanceListener);
|
||||
bsqWalletService.addBsqBalanceListener(this);
|
||||
user.getPaymentAccountsAsObservable().addListener(paymentAccountsChangeListener);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
btcWalletService.removeBalanceListener(btcBalanceListener);
|
||||
xmrWalletService.removeBalanceListener(xmrBalanceListener);
|
||||
bsqWalletService.removeBsqBalanceListener(this);
|
||||
user.getPaymentAccountsAsObservable().removeListener(paymentAccountsChangeListener);
|
||||
}
|
||||
|
@ -482,7 +485,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
return direction == OfferPayload.Direction.BUY;
|
||||
}
|
||||
|
||||
AddressEntry getAddressEntry() {
|
||||
XmrAddressEntry getAddressEntry() {
|
||||
return addressEntry;
|
||||
}
|
||||
|
||||
|
@ -618,7 +621,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
}
|
||||
|
||||
void swapTradeToSavings() {
|
||||
btcWalletService.resetAddressEntriesForOpenOffer(offerId);
|
||||
xmrWalletService.resetAddressEntriesForOpenOffer(offerId);
|
||||
}
|
||||
|
||||
private void fillPaymentAccounts() {
|
||||
|
|
|
@ -1282,8 +1282,9 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
|
|||
|
||||
@NotNull
|
||||
private String getBitcoinURI() {
|
||||
return GUIUtil.getBitcoinURI(addressTextField.getAddress(), model.getDataModel().getMissingCoin().get(),
|
||||
model.getPaymentLabel());
|
||||
return "TODO"; // TODO (woodser): wallet.createPaymentUri();
|
||||
// return GUIUtil.getBitcoinURI(addressTextField.getAddress(), model.getDataModel().getMissingCoin().get(),
|
||||
// model.getPaymentLabel());
|
||||
}
|
||||
|
||||
private void addAmountPriceFields() {
|
||||
|
|
|
@ -19,8 +19,8 @@ package bisq.desktop.main.offer;
|
|||
|
||||
import bisq.desktop.common.model.ActivatableDataModel;
|
||||
|
||||
import bisq.core.btc.model.AddressEntry;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.model.XmrAddressEntry;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.offer.OfferUtil;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
@ -41,7 +41,7 @@ import static bisq.core.util.coin.CoinUtil.minCoin;
|
|||
* needed in that UI element.
|
||||
*/
|
||||
public abstract class OfferDataModel extends ActivatableDataModel {
|
||||
protected final BtcWalletService btcWalletService;
|
||||
protected final XmrWalletService xmrWalletService;
|
||||
protected final OfferUtil offerUtil;
|
||||
|
||||
@Getter
|
||||
|
@ -56,18 +56,18 @@ public abstract class OfferDataModel extends ActivatableDataModel {
|
|||
protected final BooleanProperty showWalletFundedNotification = new SimpleBooleanProperty();
|
||||
@Getter
|
||||
protected Coin totalAvailableBalance;
|
||||
protected AddressEntry addressEntry;
|
||||
protected XmrAddressEntry addressEntry;
|
||||
protected boolean useSavingsWallet;
|
||||
|
||||
public OfferDataModel(BtcWalletService btcWalletService, OfferUtil offerUtil) {
|
||||
this.btcWalletService = btcWalletService;
|
||||
public OfferDataModel(XmrWalletService xmrWalletService, OfferUtil offerUtil) {
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.offerUtil = offerUtil;
|
||||
}
|
||||
|
||||
protected void updateBalance() {
|
||||
Coin tradeWalletBalance = btcWalletService.getBalanceForAddress(addressEntry.getAddress());
|
||||
Coin tradeWalletBalance = xmrWalletService.getBalanceForAccount(addressEntry.getAccountIndex());
|
||||
if (useSavingsWallet) {
|
||||
Coin savingWalletBalance = btcWalletService.getSavingWalletBalance();
|
||||
Coin savingWalletBalance = xmrWalletService.getSavingWalletBalance();
|
||||
totalAvailableBalance = savingWalletBalance.add(tradeWalletBalance);
|
||||
if (totalToPayAsCoin.get() != null) {
|
||||
balance.set(minCoin(totalToPayAsCoin.get(), totalAvailableBalance));
|
||||
|
|
|
@ -26,7 +26,7 @@ import bisq.desktop.main.offer.MutableOfferDataModel;
|
|||
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.btc.wallet.BsqWalletService;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.offer.CreateOfferService;
|
||||
import bisq.core.offer.OfferUtil;
|
||||
import bisq.core.offer.OpenOfferManager;
|
||||
|
@ -55,7 +55,7 @@ class CreateOfferDataModel extends MutableOfferDataModel {
|
|||
public CreateOfferDataModel(CreateOfferService createOfferService,
|
||||
OpenOfferManager openOfferManager,
|
||||
OfferUtil offerUtil,
|
||||
BtcWalletService btcWalletService,
|
||||
XmrWalletService xmrWalletService,
|
||||
BsqWalletService bsqWalletService,
|
||||
Preferences preferences,
|
||||
User user,
|
||||
|
@ -69,7 +69,7 @@ class CreateOfferDataModel extends MutableOfferDataModel {
|
|||
super(createOfferService,
|
||||
openOfferManager,
|
||||
offerUtil,
|
||||
btcWalletService,
|
||||
xmrWalletService,
|
||||
bsqWalletService,
|
||||
preferences,
|
||||
user,
|
||||
|
|
|
@ -25,11 +25,11 @@ import bisq.desktop.util.GUIUtil;
|
|||
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.btc.TxFeeEstimationService;
|
||||
import bisq.core.btc.listeners.BalanceListener;
|
||||
import bisq.core.btc.model.AddressEntry;
|
||||
import bisq.core.btc.listeners.XmrBalanceListener;
|
||||
import bisq.core.btc.model.XmrAddressEntry;
|
||||
import bisq.core.btc.wallet.BsqWalletService;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.Restrictions;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.filter.FilterManager;
|
||||
import bisq.core.locale.CurrencyUtil;
|
||||
import bisq.core.locale.Res;
|
||||
|
@ -56,8 +56,6 @@ import bisq.network.p2p.P2PService;
|
|||
import bisq.common.util.Tuple2;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
|
@ -69,6 +67,8 @@ import javafx.beans.property.SimpleObjectProperty;
|
|||
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import lombok.Getter;
|
||||
|
@ -111,7 +111,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
private final ObjectProperty<Coin> amount = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Volume> volume = new SimpleObjectProperty<>();
|
||||
|
||||
private BalanceListener balanceListener;
|
||||
private XmrBalanceListener balanceListener;
|
||||
private PaymentAccount paymentAccount;
|
||||
private boolean isTabSelected;
|
||||
Price tradePrice;
|
||||
|
@ -134,7 +134,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
TakeOfferDataModel(TradeManager tradeManager,
|
||||
OfferBook offerBook,
|
||||
OfferUtil offerUtil,
|
||||
BtcWalletService btcWalletService,
|
||||
XmrWalletService xmrWalletService,
|
||||
BsqWalletService bsqWalletService,
|
||||
User user, FeeService feeService,
|
||||
MempoolService mempoolService,
|
||||
|
@ -146,7 +146,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
Navigation navigation,
|
||||
P2PService p2PService
|
||||
) {
|
||||
super(btcWalletService, offerUtil);
|
||||
super(xmrWalletService, offerUtil);
|
||||
|
||||
this.tradeManager = tradeManager;
|
||||
this.offerBook = offerBook;
|
||||
|
@ -207,7 +207,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
void initWithData(Offer offer) {
|
||||
this.offer = offer;
|
||||
tradePrice = offer.getPrice();
|
||||
addressEntry = btcWalletService.getOrCreateAddressEntry(offer.getId(), AddressEntry.Context.OFFER_FUNDING);
|
||||
addressEntry = xmrWalletService.getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.OFFER_FUNDING);
|
||||
checkNotNull(addressEntry, "addressEntry must not be null");
|
||||
|
||||
ObservableList<PaymentAccount> possiblePaymentAccounts = getPossiblePaymentAccounts();
|
||||
|
@ -265,11 +265,40 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
calculateVolume();
|
||||
calculateTotalToPay();
|
||||
|
||||
balanceListener = new BalanceListener(addressEntry.getAddress()) {
|
||||
balanceListener = new XmrBalanceListener(addressEntry.getAccountIndex()) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateBalance();
|
||||
public void onBalanceChanged(BigInteger balance) {
|
||||
updateBalance();
|
||||
}
|
||||
|
||||
// public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
// updateBalance();
|
||||
//
|
||||
// /*if (isMainNet.get()) {
|
||||
// SettableFuture<Coin> future = blockchainService.requestFee(tx.getHashAsString());
|
||||
// Futures.addCallback(future, new FutureCallback<Coin>() {
|
||||
// public void onSuccess(Coin fee) {
|
||||
// UserThread.execute(() -> setFeeFromFundingTx(fee));
|
||||
// }
|
||||
//
|
||||
// public void onFailure(@NotNull Throwable throwable) {
|
||||
// UserThread.execute(() -> new Popup<>()
|
||||
// .warning("We did not get a response for the request of the mining fee used " +
|
||||
// "in the funding transaction.\n\n" +
|
||||
// "Are you sure you used a sufficiently high fee of at least " +
|
||||
// formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) + "?")
|
||||
// .actionButtonText("Yes, I used a sufficiently high fee.")
|
||||
// .onAction(() -> setFeeFromFundingTx(FeePolicy.getMinRequiredFeeForFundingTx()))
|
||||
// .closeButtonText("No. Let's cancel that payment.")
|
||||
// .onClose(() -> setFeeFromFundingTx(Coin.NEGATIVE_SATOSHI))
|
||||
// .show());
|
||||
// }
|
||||
// });
|
||||
// } else {
|
||||
// setFeeFromFundingTx(FeePolicy.getMinRequiredFeeForFundingTx());
|
||||
// isFeeFromFundingTxSufficient.set(feeFromFundingTx.compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0);
|
||||
// }*/
|
||||
// }
|
||||
};
|
||||
|
||||
offer.resetState();
|
||||
|
@ -301,7 +330,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
offerBook.removeOffer(checkNotNull(offer), tradeManager);
|
||||
}
|
||||
|
||||
btcWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
||||
//xmrWalletService.resetAddressEntriesForOpenOffer(offer.getId()); // TODO (woodser): this removes address entries for reserved trades before completion. how doesn't this delete the multisig address entry in bisq before completion?
|
||||
}
|
||||
|
||||
|
||||
|
@ -335,7 +364,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
tradeManager.onTakeOffer(amount.get(),
|
||||
txFeeFromFeeService,
|
||||
getTakerFee(),
|
||||
isCurrencyForTakerFeeBtc(),
|
||||
tradePrice.getValue(),
|
||||
fundsNeededForTrade,
|
||||
offer,
|
||||
|
@ -363,7 +391,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
// So that would require more thoughts how to deal with all those cases.
|
||||
public void estimateTxVsize() {
|
||||
int txVsize = 0;
|
||||
if (btcWalletService.getBalance(Wallet.BalanceType.AVAILABLE).isPositive()) {
|
||||
if (xmrWalletService.getWallet().getUnlockedBalance(0).compareTo(new BigInteger("0")) > 0) {
|
||||
Coin fundsNeededForTrade = getFundsNeededForTrade();
|
||||
if (isBuyOffer())
|
||||
fundsNeededForTrade = fundsNeededForTrade.add(amount.get());
|
||||
|
@ -469,11 +497,11 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void addListeners() {
|
||||
btcWalletService.addBalanceListener(balanceListener);
|
||||
xmrWalletService.addBalanceListener(balanceListener);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
btcWalletService.removeBalanceListener(balanceListener);
|
||||
xmrWalletService.removeBalanceListener(balanceListener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -554,7 +582,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
|
||||
public void swapTradeToSavings() {
|
||||
log.debug("swapTradeToSavings, offerId={}", offer.getId());
|
||||
btcWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
||||
xmrWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
||||
}
|
||||
|
||||
// We use the sum of the vsize of the trade fee and the deposit tx to get an average.
|
||||
|
@ -648,7 +676,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
return txFeeFromFeeService; //feeService.getTxFee(169);
|
||||
}
|
||||
|
||||
public AddressEntry getAddressEntry() {
|
||||
public XmrAddressEntry getAddressEntry() {
|
||||
return addressEntry;
|
||||
}
|
||||
|
||||
|
|
|
@ -1086,9 +1086,10 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
|
||||
@NotNull
|
||||
private String getBitcoinURI() {
|
||||
return GUIUtil.getBitcoinURI(model.dataModel.getAddressEntry().getAddressString(),
|
||||
model.dataModel.getMissingCoin().get(),
|
||||
model.getPaymentLabel());
|
||||
return "TODO";
|
||||
// return GUIUtil.getBitcoinURI(model.dataModel.getAddressEntry().getAddressString(),
|
||||
// model.dataModel.getMissingCoin().get(),
|
||||
// model.getPaymentLabel());
|
||||
}
|
||||
|
||||
private void addAmountPriceFields() {
|
||||
|
|
|
@ -233,11 +233,11 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
showTransactionPublishedScreen.set(false);
|
||||
dataModel.onTakeOffer(trade -> {
|
||||
this.trade = trade;
|
||||
takeOfferCompleted.set(true);
|
||||
trade.stateProperty().addListener(tradeStateListener);
|
||||
applyTradeState();
|
||||
trade.errorMessageProperty().addListener(tradeErrorListener);
|
||||
applyTradeErrorMessage(trade.getErrorMessage());
|
||||
takeOfferCompleted.set(true);
|
||||
});
|
||||
|
||||
updateButtonDisableState();
|
||||
|
@ -448,13 +448,16 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
}
|
||||
|
||||
private void applyTradeState() {
|
||||
if (trade.isDepositPublished()) {
|
||||
if (trade.getDepositTx() != null) {
|
||||
if (trade.isTakerFeePublished()) {
|
||||
if (trade.getTakerFeeTxId() != null) {
|
||||
if (takeOfferSucceededHandler != null)
|
||||
takeOfferSucceededHandler.run();
|
||||
|
||||
showTransactionPublishedScreen.set(true);
|
||||
updateSpinnerInfo();
|
||||
} else {
|
||||
final String msg = "trade.getTakerFeeTxId() must not be null.";
|
||||
DevEnv.logErrorAndThrowIfDevMode(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ import bisq.core.payment.payload.PaymentMethod;
|
|||
import bisq.core.support.dispute.Dispute;
|
||||
import bisq.core.support.dispute.DisputeList;
|
||||
import bisq.core.support.dispute.DisputeManager;
|
||||
import bisq.core.support.dispute.agent.DisputeAgentLookupMap;
|
||||
import bisq.core.support.dispute.arbitration.ArbitrationManager;
|
||||
import bisq.core.support.dispute.mediation.MediationManager;
|
||||
import bisq.core.support.dispute.refund.RefundManager;
|
||||
|
@ -201,15 +200,17 @@ public class ContractWindow extends Overlay<ContractWindow> {
|
|||
title = Res.get("shared.selectedArbitrator");
|
||||
break;
|
||||
case MEDIATION:
|
||||
agentKeyBaseUserName = DisputeAgentLookupMap.getKeyBaseUserName(contract.getMediatorNodeAddress().getFullAddress());
|
||||
title = Res.get("shared.selectedMediator");
|
||||
break;
|
||||
throw new RuntimeException("Mediation type not adapted to XMR");
|
||||
// agentKeyBaseUserName = DisputeAgentLookupMap.getKeyBaseUserName(contract.getMediatorNodeAddress().getFullAddress());
|
||||
// title = Res.get("shared.selectedMediator");
|
||||
// break;
|
||||
case TRADE:
|
||||
break;
|
||||
case REFUND:
|
||||
agentKeyBaseUserName = DisputeAgentLookupMap.getKeyBaseUserName(contract.getRefundAgentNodeAddress().getFullAddress());
|
||||
title = Res.get("shared.selectedRefundAgent");
|
||||
break;
|
||||
throw new RuntimeException("Refund type not adapted to XMR");
|
||||
//agentKeyBaseUserName = DisputeAgentLookupMap.getKeyBaseUserName(contract.getRefundAgentNodeAddress().getFullAddress());
|
||||
//title = Res.get("shared.selectedRefundAgent");
|
||||
//break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,7 +248,7 @@ public class ContractWindow extends Overlay<ContractWindow> {
|
|||
}
|
||||
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.makerFeeTxId"), offer.getOfferFeePaymentTxId());
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.takerFeeTxId"), contract.getTakerFeeTxID());
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.takerFeeTxId"), "TAKER FEE TX ID NOT PART OF CONTRACT"); // TODO (woodser): should taker fee tx id be part of contract?
|
||||
|
||||
if (dispute.getDepositTxSerialized() != null)
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.depositTransactionId"), dispute.getDepositTxId());
|
||||
|
@ -273,8 +274,6 @@ public class ContractWindow extends Overlay<ContractWindow> {
|
|||
viewContractButton.setOnAction(e -> {
|
||||
TextArea textArea = new BisqTextArea();
|
||||
String contractAsJson = dispute.getContractAsJson();
|
||||
contractAsJson += "\n\nBuyerMultiSigPubKeyHex: " + Utils.HEX.encode(contract.getBuyerMultiSigPubKey());
|
||||
contractAsJson += "\nSellerMultiSigPubKeyHex: " + Utils.HEX.encode(contract.getSellerMultiSigPubKey());
|
||||
textArea.setText(contractAsJson);
|
||||
textArea.setPrefHeight(50);
|
||||
textArea.setEditable(false);
|
||||
|
|
|
@ -23,35 +23,25 @@ import bisq.desktop.components.BisqTextArea;
|
|||
import bisq.desktop.components.InputTextField;
|
||||
import bisq.desktop.main.overlays.Overlay;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.main.support.dispute.DisputeSummaryVerification;
|
||||
import bisq.desktop.util.DisplayUtils;
|
||||
import bisq.desktop.util.Layout;
|
||||
|
||||
import bisq.core.btc.TxFeeEstimationService;
|
||||
import bisq.core.btc.exceptions.TransactionVerificationException;
|
||||
import bisq.core.btc.exceptions.TxBroadcastException;
|
||||
import bisq.core.btc.exceptions.WalletException;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.Restrictions;
|
||||
import bisq.core.btc.model.XmrAddressEntry;
|
||||
import bisq.core.btc.wallet.TradeWalletService;
|
||||
import bisq.core.btc.wallet.TxBroadcaster;
|
||||
import bisq.core.dao.DaoFacade;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.provider.mempool.MempoolService;
|
||||
import bisq.core.support.SupportType;
|
||||
import bisq.core.support.dispute.Dispute;
|
||||
import bisq.core.support.dispute.DisputeList;
|
||||
import bisq.core.support.dispute.DisputeManager;
|
||||
import bisq.core.support.dispute.DisputeResult;
|
||||
import bisq.core.support.dispute.arbitration.ArbitrationManager;
|
||||
import bisq.core.support.dispute.mediation.MediationManager;
|
||||
import bisq.core.support.dispute.refund.RefundManager;
|
||||
import bisq.core.trade.Contract;
|
||||
import bisq.core.trade.TradeDataValidation;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
import bisq.core.util.ParsingUtils;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
import bisq.core.util.coin.CoinUtil;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.app.DevEnv;
|
||||
|
@ -59,12 +49,11 @@ import bisq.common.handlers.ResultHandler;
|
|||
import bisq.common.util.Tuple2;
|
||||
import bisq.common.util.Tuple3;
|
||||
|
||||
import org.bitcoinj.core.AddressFormatException;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Button;
|
||||
|
@ -85,28 +74,30 @@ import javafx.geometry.Insets;
|
|||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.desktop.util.FormBuilder.*;
|
||||
import static bisq.desktop.util.FormBuilder.add2ButtonsWithBox;
|
||||
import static bisq.desktop.util.FormBuilder.addConfirmationLabelLabel;
|
||||
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
|
||||
import static bisq.desktop.util.FormBuilder.addTopLabelWithVBox;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
||||
|
||||
import monero.wallet.MoneroWallet;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
@Slf4j
|
||||
public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||
private final CoinFormatter formatter;
|
||||
private final ArbitrationManager arbitrationManager;
|
||||
private final MediationManager mediationManager;
|
||||
private final RefundManager refundManager;
|
||||
private final TradeWalletService tradeWalletService;
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final TxFeeEstimationService txFeeEstimationService;
|
||||
private final MempoolService mempoolService;
|
||||
private final DaoFacade daoFacade;
|
||||
private final XmrWalletService walletService;
|
||||
private final TradeWalletService tradeWalletService; // TODO (woodser): remove for xmr or adapt to get/create multisig wallets for tx creation utils
|
||||
private Dispute dispute;
|
||||
private Optional<Runnable> finalizeDisputeHandlerOptional = Optional.empty();
|
||||
private ToggleGroup tradeAmountToggleGroup, reasonToggleGroup;
|
||||
|
@ -122,13 +113,12 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
// Dispute object of other trade peer. The dispute field is the one from which we opened the close dispute window.
|
||||
private Optional<Dispute> peersDisputeOptional;
|
||||
private String role;
|
||||
private Label delayedPayoutTxStatus;
|
||||
private TextArea summaryNotesTextArea;
|
||||
|
||||
private ChangeListener<Boolean> customRadioButtonSelectedListener;
|
||||
private ChangeListener<Toggle> reasonToggleSelectionListener;
|
||||
private InputTextField buyerPayoutAmountInputTextField, sellerPayoutAmountInputTextField;
|
||||
private ChangeListener<Boolean> buyerPayoutAmountListener, sellerPayoutAmountListener;
|
||||
private ChangeListener<String> buyerPayoutAmountListener, sellerPayoutAmountListener;
|
||||
private CheckBox isLoserPublisherCheckBox;
|
||||
private ChangeListener<Toggle> tradeAmountToggleGroupListener;
|
||||
|
||||
|
@ -139,22 +129,16 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
|
||||
@Inject
|
||||
public DisputeSummaryWindow(@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
|
||||
ArbitrationManager arbitrationManager,
|
||||
MediationManager mediationManager,
|
||||
RefundManager refundManager,
|
||||
TradeWalletService tradeWalletService,
|
||||
BtcWalletService btcWalletService,
|
||||
TxFeeEstimationService txFeeEstimationService,
|
||||
MempoolService mempoolService,
|
||||
DaoFacade daoFacade) {
|
||||
XmrWalletService walletService,
|
||||
TradeWalletService tradeWalletService) {
|
||||
|
||||
this.formatter = formatter;
|
||||
this.arbitrationManager = arbitrationManager;
|
||||
this.mediationManager = mediationManager;
|
||||
this.refundManager = refundManager;
|
||||
this.walletService = walletService;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.txFeeEstimationService = txFeeEstimationService;
|
||||
this.mempoolService = mempoolService;
|
||||
this.daoFacade = daoFacade;
|
||||
|
||||
type = Type.Confirmation;
|
||||
}
|
||||
|
@ -166,7 +150,6 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
width = 1150;
|
||||
createGridPane();
|
||||
addContent();
|
||||
checkDelayedPayoutTransaction();
|
||||
display();
|
||||
|
||||
if (DevEnv.isDevMode()) {
|
||||
|
@ -176,6 +159,12 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
}
|
||||
}
|
||||
|
||||
public DisputeSummaryWindow onFinalizeDispute(Runnable finalizeDisputeHandler) {
|
||||
this.finalizeDisputeHandlerOptional = Optional.of(finalizeDisputeHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Protected
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -288,14 +277,24 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
addConfirmationLabelLabel(gridPane, rowIndex, Res.get("shared.tradeId"), dispute.getShortTradeId(),
|
||||
Layout.TWICE_FIRST_ROW_DISTANCE);
|
||||
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("disputeSummaryWindow.openDate"), DisplayUtils.formatDateTime(dispute.getOpeningDate()));
|
||||
role = dispute.getRoleString();
|
||||
if (dispute.isDisputeOpenerIsMaker()) {
|
||||
if (dispute.isDisputeOpenerIsBuyer())
|
||||
role = Res.get("support.buyerOfferer");
|
||||
else
|
||||
role = Res.get("support.sellerOfferer");
|
||||
} else {
|
||||
if (dispute.isDisputeOpenerIsBuyer())
|
||||
role = Res.get("support.buyerTaker");
|
||||
else
|
||||
role = Res.get("support.sellerTaker");
|
||||
}
|
||||
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("disputeSummaryWindow.role"), role);
|
||||
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.tradeAmount"),
|
||||
formatter.formatCoinWithCode(contract.getTradeAmount()));
|
||||
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.tradePrice"),
|
||||
FormattingUtils.formatPrice(contract.getTradePrice()));
|
||||
FormattingUtils.formatPrice(contract.getTradePrice()));
|
||||
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.tradeVolume"),
|
||||
DisplayUtils.formatVolumeWithCode(contract.getTradeVolume()));
|
||||
DisplayUtils.formatVolumeWithCode(contract.getTradeVolume()));
|
||||
String securityDeposit = Res.getWithColAndCap("shared.buyer") +
|
||||
" " +
|
||||
formatter.formatCoinWithCode(contract.getOfferPayload().getBuyerSecurityDeposit()) +
|
||||
|
@ -304,26 +303,6 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
" " +
|
||||
formatter.formatCoinWithCode(contract.getOfferPayload().getSellerSecurityDeposit());
|
||||
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), securityDeposit);
|
||||
|
||||
boolean isMediationDispute = getDisputeManager(dispute) instanceof MediationManager;
|
||||
if (isMediationDispute) {
|
||||
if (dispute.getTradePeriodEnd().getTime() > 0) {
|
||||
String status = DisplayUtils.formatDateTime(dispute.getTradePeriodEnd());
|
||||
Label tradePeriodEnd = addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("disputeSummaryWindow.tradePeriodEnd"), status).second;
|
||||
if (dispute.getTradePeriodEnd().toInstant().isAfter(Instant.now())) {
|
||||
tradePeriodEnd.getStyleClass().add("alert"); // highlight field when the trade period is still active
|
||||
}
|
||||
}
|
||||
if (dispute.getExtraDataMap() != null && dispute.getExtraDataMap().size() > 0) {
|
||||
String extraDataSummary = "";
|
||||
for (Map.Entry<String, String> entry : dispute.getExtraDataMap().entrySet()) {
|
||||
extraDataSummary += "[" + entry.getKey() + ":" + entry.getValue() + "] ";
|
||||
}
|
||||
addConfirmationLabelLabelWithCopyIcon(gridPane, ++rowIndex, Res.get("disputeSummaryWindow.extraInfo"), extraDataSummary);
|
||||
}
|
||||
} else {
|
||||
delayedPayoutTxStatus = addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("disputeSummaryWindow.delayedPayoutStatus"), "Checking...").second;
|
||||
}
|
||||
}
|
||||
|
||||
private void addTradeAmountPayoutControls() {
|
||||
|
@ -355,16 +334,16 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
tradeAmountToggleGroupListener = (observable, oldValue, newValue) -> applyPayoutAmounts(newValue);
|
||||
tradeAmountToggleGroup.selectedToggleProperty().addListener(tradeAmountToggleGroupListener);
|
||||
|
||||
buyerPayoutAmountListener = (observable, oldValue, newValue) -> applyCustomAmounts(buyerPayoutAmountInputTextField, oldValue, newValue);
|
||||
sellerPayoutAmountListener = (observable, oldValue, newValue) -> applyCustomAmounts(sellerPayoutAmountInputTextField, oldValue, newValue);
|
||||
buyerPayoutAmountListener = (observable1, oldValue1, newValue1) -> applyCustomAmounts(buyerPayoutAmountInputTextField);
|
||||
sellerPayoutAmountListener = (observable1, oldValue1, newValue1) -> applyCustomAmounts(sellerPayoutAmountInputTextField);
|
||||
|
||||
customRadioButtonSelectedListener = (observable, oldValue, newValue) -> {
|
||||
buyerPayoutAmountInputTextField.setEditable(newValue);
|
||||
sellerPayoutAmountInputTextField.setEditable(newValue);
|
||||
|
||||
if (newValue) {
|
||||
buyerPayoutAmountInputTextField.focusedProperty().addListener(buyerPayoutAmountListener);
|
||||
sellerPayoutAmountInputTextField.focusedProperty().addListener(sellerPayoutAmountListener);
|
||||
buyerPayoutAmountInputTextField.textProperty().addListener(buyerPayoutAmountListener);
|
||||
sellerPayoutAmountInputTextField.textProperty().addListener(sellerPayoutAmountListener);
|
||||
} else {
|
||||
removePayoutAmountListeners();
|
||||
}
|
||||
|
@ -374,10 +353,10 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
|
||||
private void removePayoutAmountListeners() {
|
||||
if (buyerPayoutAmountInputTextField != null && buyerPayoutAmountListener != null)
|
||||
buyerPayoutAmountInputTextField.focusedProperty().removeListener(buyerPayoutAmountListener);
|
||||
buyerPayoutAmountInputTextField.textProperty().removeListener(buyerPayoutAmountListener);
|
||||
|
||||
if (sellerPayoutAmountInputTextField != null && sellerPayoutAmountListener != null)
|
||||
sellerPayoutAmountInputTextField.focusedProperty().removeListener(sellerPayoutAmountListener);
|
||||
sellerPayoutAmountInputTextField.textProperty().removeListener(sellerPayoutAmountListener);
|
||||
|
||||
}
|
||||
|
||||
|
@ -405,66 +384,36 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
}
|
||||
}
|
||||
|
||||
private void applyCustomAmounts(InputTextField inputTextField, boolean oldFocusValue, boolean newFocusValue) {
|
||||
// We only apply adjustments at focus out, otherwise we cannot enter certain values if we update at each
|
||||
// keystroke.
|
||||
if (!oldFocusValue || newFocusValue) {
|
||||
return;
|
||||
}
|
||||
private void applyCustomAmounts(InputTextField inputTextField) {
|
||||
// // We only apply adjustments at focus out, otherwise we cannot enter certain values if we update at each
|
||||
// // keystroke.
|
||||
// if (!oldFocusValue || newFocusValue) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
Contract contract = dispute.getContract();
|
||||
boolean isMediationDispute = getDisputeManager(dispute) instanceof MediationManager;
|
||||
// At mediation we require a min. payout to the losing party to keep incentive for the trader to accept the
|
||||
// mediated payout. For Refund agent cases we do not have that restriction.
|
||||
Coin minRefundAtDispute = isMediationDispute ? Restrictions.getMinRefundAtMediatedDispute() : Coin.ZERO;
|
||||
|
||||
Offer offer = new Offer(contract.getOfferPayload());
|
||||
Coin totalAvailable = contract.getTradeAmount()
|
||||
Coin available = contract.getTradeAmount()
|
||||
.add(offer.getBuyerSecurityDeposit())
|
||||
.add(offer.getSellerSecurityDeposit());
|
||||
Coin availableForPayout = totalAvailable.subtract(minRefundAtDispute);
|
||||
|
||||
Coin enteredAmount = ParsingUtils.parseToCoin(inputTextField.getText(), formatter);
|
||||
if (enteredAmount.compareTo(minRefundAtDispute) < 0) {
|
||||
enteredAmount = minRefundAtDispute;
|
||||
inputTextField.setText(formatter.formatCoin(enteredAmount));
|
||||
if (enteredAmount.compareTo(available) > 0) {
|
||||
enteredAmount = available;
|
||||
Coin finalEnteredAmount = enteredAmount;
|
||||
inputTextField.setText(formatter.formatCoin(finalEnteredAmount));
|
||||
}
|
||||
if (enteredAmount.isPositive() && !Restrictions.isAboveDust(enteredAmount)) {
|
||||
enteredAmount = Restrictions.getMinNonDustOutput();
|
||||
inputTextField.setText(formatter.formatCoin(enteredAmount));
|
||||
}
|
||||
if (enteredAmount.compareTo(availableForPayout) > 0) {
|
||||
enteredAmount = availableForPayout;
|
||||
inputTextField.setText(formatter.formatCoin(enteredAmount));
|
||||
}
|
||||
Coin counterPartAsCoin = totalAvailable.subtract(enteredAmount);
|
||||
Coin counterPartAsCoin = available.subtract(enteredAmount);
|
||||
String formattedCounterPartAmount = formatter.formatCoin(counterPartAsCoin);
|
||||
Coin buyerAmount;
|
||||
Coin sellerAmount;
|
||||
if (inputTextField == buyerPayoutAmountInputTextField) {
|
||||
buyerAmount = enteredAmount;
|
||||
sellerAmount = counterPartAsCoin;
|
||||
Coin sellerAmountFromField = ParsingUtils.parseToCoin(sellerPayoutAmountInputTextField.getText(), formatter);
|
||||
Coin totalAmountFromFields = enteredAmount.add(sellerAmountFromField);
|
||||
// RefundAgent can enter less then available
|
||||
if (isMediationDispute ||
|
||||
totalAmountFromFields.compareTo(totalAvailable) > 0) {
|
||||
sellerPayoutAmountInputTextField.setText(formattedCounterPartAmount);
|
||||
} else {
|
||||
sellerAmount = sellerAmountFromField;
|
||||
}
|
||||
sellerPayoutAmountInputTextField.setText(formattedCounterPartAmount);
|
||||
} else {
|
||||
sellerAmount = enteredAmount;
|
||||
buyerAmount = counterPartAsCoin;
|
||||
Coin buyerAmountFromField = ParsingUtils.parseToCoin(buyerPayoutAmountInputTextField.getText(), formatter);
|
||||
Coin totalAmountFromFields = enteredAmount.add(buyerAmountFromField);
|
||||
// RefundAgent can enter less then available
|
||||
if (isMediationDispute ||
|
||||
totalAmountFromFields.compareTo(totalAvailable) > 0) {
|
||||
buyerPayoutAmountInputTextField.setText(formattedCounterPartAmount);
|
||||
} else {
|
||||
buyerAmount = buyerAmountFromField;
|
||||
}
|
||||
buyerPayoutAmountInputTextField.setText(formattedCounterPartAmount);
|
||||
}
|
||||
|
||||
disputeResult.setBuyerPayoutAmount(buyerAmount);
|
||||
|
@ -632,100 +581,138 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
}
|
||||
|
||||
private void addButtons(Contract contract) {
|
||||
Tuple3<Button, Button, HBox> tuple = add2ButtonsWithBox(gridPane, ++rowIndex,
|
||||
Res.get("disputeSummaryWindow.close.button"),
|
||||
Res.get("shared.cancel"), 15, true);
|
||||
Button closeTicketButton = tuple.first;
|
||||
closeTicketButton.disableProperty().bind(Bindings.createBooleanBinding(
|
||||
() -> tradeAmountToggleGroup.getSelectedToggle() == null
|
||||
|| summaryNotesTextArea.getText() == null
|
||||
|| summaryNotesTextArea.getText().length() == 0
|
||||
|| !isPayoutAmountValid(),
|
||||
tradeAmountToggleGroup.selectedToggleProperty(),
|
||||
summaryNotesTextArea.textProperty(),
|
||||
buyerPayoutAmountInputTextField.textProperty(),
|
||||
sellerPayoutAmountInputTextField.textProperty()));
|
||||
Tuple3<Button, Button, HBox> tuple = add2ButtonsWithBox(gridPane, ++rowIndex,
|
||||
Res.get("disputeSummaryWindow.close.button"),
|
||||
Res.get("shared.cancel"), 15, true);
|
||||
Button closeTicketButton = tuple.first;
|
||||
closeTicketButton.disableProperty().bind(Bindings.createBooleanBinding(
|
||||
() -> tradeAmountToggleGroup.getSelectedToggle() == null
|
||||
|| summaryNotesTextArea.getText() == null
|
||||
|| summaryNotesTextArea.getText().length() == 0
|
||||
|| !isPayoutAmountValid(),
|
||||
tradeAmountToggleGroup.selectedToggleProperty(),
|
||||
summaryNotesTextArea.textProperty(),
|
||||
buyerPayoutAmountInputTextField.textProperty(),
|
||||
sellerPayoutAmountInputTextField.textProperty()));
|
||||
|
||||
Button cancelButton = tuple.second;
|
||||
Button cancelButton = tuple.second;
|
||||
|
||||
closeTicketButton.setOnAction(e -> {
|
||||
if (dispute.getDepositTxSerialized() == null) {
|
||||
log.warn("dispute.getDepositTxSerialized is null");
|
||||
closeTicketButton.setOnAction(e -> {
|
||||
|
||||
// TODO (woodser): create disputed payout tx after showing payout tx confirmation, within doCloseIfValid() (see upstream/master)
|
||||
if (!dispute.isMediationDispute()) {
|
||||
try {
|
||||
System.out.println(disputeResult);
|
||||
XmrAddressEntry arbitratorAddressEntry = walletService.getArbitratorAddressEntry();
|
||||
MoneroWallet multisigWallet = walletService.getOrCreateMultisigWallet(dispute.getTradeId());
|
||||
System.out.println("Arbitrator payout address entry: " + arbitratorAddressEntry.getAddressString());
|
||||
//dispute.getContract().getArbitratorPubKeyRing(); // TODO: support arbitrator pub key ring in contract?
|
||||
//disputeResult.setArbitratorPubKey(arbitratorAddressEntry.getPubKey());
|
||||
|
||||
// TODO (woodser): don't send signed tx if opener is not co-signer?
|
||||
// // determine if opener is co-signer
|
||||
// boolean openerIsWinner = (contract.getBuyerPubKeyRing().equals(dispute.getTraderPubKeyRing()) && disputeResult.getWinner() == Winner.BUYER) || (contract.getSellerPubKeyRing().equals(dispute.getTraderPubKeyRing()) && disputeResult.getWinner() == Winner.SELLER);
|
||||
// boolean openerIsCosigner = openerIsWinner || disputeResult.isLoserPublisher();
|
||||
// if (!openerIsCosigner) throw new RuntimeException("Need to query non-opener for updated multisig hex before creating tx");
|
||||
|
||||
// arbitrator creates and signs dispute payout tx if dispute is in context of opener, otherwise opener's peer must request payout tx by providing updated multisig hex
|
||||
boolean isOpener = dispute.isOpener();
|
||||
System.out.println("Is dispute opener: " + isOpener);
|
||||
if (isOpener) {
|
||||
MoneroTxWallet arbitratorPayoutTx = ArbitrationManager.arbitratorCreatesDisputedPayoutTx(contract, dispute, disputeResult, multisigWallet);
|
||||
System.out.println("Created arbitrator-signed payout tx: " + arbitratorPayoutTx);
|
||||
if (arbitratorPayoutTx != null) disputeResult.setArbitratorSignedPayoutTxHex(arbitratorPayoutTx.getTxSet().getMultisigTxHex());
|
||||
}
|
||||
|
||||
// send arbitrator's updated multisig hex with dispute result
|
||||
disputeResult.setArbitratorUpdatedMultisigHex(multisigWallet.getMultisigHex());
|
||||
} catch (AddressFormatException e2) {
|
||||
log.error("Error at close dispute", e2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (dispute.getSupportType() == SupportType.REFUND &&
|
||||
peersDisputeOptional.isPresent() &&
|
||||
!peersDisputeOptional.get().isClosed()) {
|
||||
showPayoutTxConfirmation(contract,
|
||||
disputeResult,
|
||||
() -> doCloseIfValid(closeTicketButton));
|
||||
} else {
|
||||
doCloseIfValid(closeTicketButton);
|
||||
}
|
||||
});
|
||||
// // TODO (woodser): handle with showPayoutTxConfirmation() / doCloseIfValid() in order to have confirmation window (see upstream/master)
|
||||
doClose(closeTicketButton);
|
||||
|
||||
cancelButton.setOnAction(e -> {
|
||||
dispute.setDisputeResult(disputeResult);
|
||||
checkNotNull(getDisputeManager(dispute)).requestPersistence();
|
||||
hide();
|
||||
});
|
||||
// if (dispute.getDepositTxSerialized() == null) {
|
||||
// log.warn("dispute.getDepositTxSerialized is null");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (dispute.getSupportType() == SupportType.REFUND &&
|
||||
// peersDisputeOptional.isPresent() &&
|
||||
// !peersDisputeOptional.get().isClosed()) {
|
||||
// showPayoutTxConfirmation(contract,
|
||||
// disputeResult,
|
||||
// () -> doCloseIfValid(closeTicketButton));
|
||||
// } else {
|
||||
// doCloseIfValid(closeTicketButton);
|
||||
// }
|
||||
});
|
||||
|
||||
cancelButton.setOnAction(e -> {
|
||||
dispute.setDisputeResult(disputeResult);
|
||||
checkNotNull(getDisputeManager(dispute)).requestPersistence();
|
||||
hide();
|
||||
});
|
||||
}
|
||||
|
||||
private void showPayoutTxConfirmation(Contract contract, DisputeResult disputeResult, ResultHandler resultHandler) {
|
||||
Coin buyerPayoutAmount = disputeResult.getBuyerPayoutAmount();
|
||||
String buyerPayoutAddressString = contract.getBuyerPayoutAddressString();
|
||||
Coin sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
|
||||
String sellerPayoutAddressString = contract.getSellerPayoutAddressString();
|
||||
Coin outputAmount = buyerPayoutAmount.add(sellerPayoutAmount);
|
||||
Tuple2<Coin, Integer> feeTuple = txFeeEstimationService.getEstimatedFeeAndTxVsize(outputAmount, btcWalletService);
|
||||
Coin fee = feeTuple.first;
|
||||
Integer txVsize = feeTuple.second;
|
||||
double feePerVbyte = CoinUtil.getFeePerVbyte(fee, txVsize);
|
||||
double vkb = txVsize / 1000d;
|
||||
Coin inputAmount = outputAmount.add(fee);
|
||||
String buyerDetails = "";
|
||||
if (buyerPayoutAmount.isPositive()) {
|
||||
buyerDetails = Res.get("disputeSummaryWindow.close.txDetails.buyer",
|
||||
formatter.formatCoinWithCode(buyerPayoutAmount),
|
||||
buyerPayoutAddressString);
|
||||
}
|
||||
String sellerDetails = "";
|
||||
if (sellerPayoutAmount.isPositive()) {
|
||||
sellerDetails = Res.get("disputeSummaryWindow.close.txDetails.seller",
|
||||
formatter.formatCoinWithCode(sellerPayoutAmount),
|
||||
sellerPayoutAddressString);
|
||||
}
|
||||
if (outputAmount.isPositive()) {
|
||||
new Popup().width(900)
|
||||
.headLine(Res.get("disputeSummaryWindow.close.txDetails.headline"))
|
||||
.confirmation(Res.get("disputeSummaryWindow.close.txDetails",
|
||||
formatter.formatCoinWithCode(inputAmount),
|
||||
buyerDetails,
|
||||
sellerDetails,
|
||||
formatter.formatCoinWithCode(fee),
|
||||
feePerVbyte,
|
||||
vkb))
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.onAction(() -> {
|
||||
doPayout(buyerPayoutAmount,
|
||||
sellerPayoutAmount,
|
||||
fee,
|
||||
buyerPayoutAddressString,
|
||||
sellerPayoutAddressString,
|
||||
resultHandler);
|
||||
})
|
||||
.closeButtonText(Res.get("shared.cancel"))
|
||||
.show();
|
||||
} else {
|
||||
// No payout will be made
|
||||
new Popup().headLine(Res.get("disputeSummaryWindow.close.noPayout.headline"))
|
||||
.confirmation(Res.get("disputeSummaryWindow.close.noPayout.text"))
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.onAction(resultHandler::handleResult)
|
||||
.closeButtonText(Res.get("shared.cancel"))
|
||||
.show();
|
||||
}
|
||||
throw new RuntimeException("DisputeSummaryWindow.showPayoutTxConfimration() needs updated for XMR");
|
||||
// Coin buyerPayoutAmount = disputeResult.getBuyerPayoutAmount();
|
||||
// String buyerPayoutAddressString = contract.getBuyerPayoutAddressString();
|
||||
// Coin sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
|
||||
// String sellerPayoutAddressString = contract.getSellerPayoutAddressString();
|
||||
// Coin outputAmount = buyerPayoutAmount.add(sellerPayoutAmount);
|
||||
// Tuple2<Coin, Integer> feeTuple = txFeeEstimationService.getEstimatedFeeAndTxSize(outputAmount, feeService, btcWalletService);
|
||||
// Coin fee = feeTuple.first;
|
||||
// Integer txSize = feeTuple.second;
|
||||
// double feePerByte = CoinUtil.getFeePerByte(fee, txSize);
|
||||
// double kb = txSize / 1000d;
|
||||
// Coin inputAmount = outputAmount.add(fee);
|
||||
// String buyerDetails = "";
|
||||
// if (buyerPayoutAmount.isPositive()) {
|
||||
// buyerDetails = Res.get("disputeSummaryWindow.close.txDetails.buyer",
|
||||
// formatter.formatCoinWithCode(buyerPayoutAmount),
|
||||
// buyerPayoutAddressString);
|
||||
// }
|
||||
// String sellerDetails = "";
|
||||
// if (sellerPayoutAmount.isPositive()) {
|
||||
// sellerDetails = Res.get("disputeSummaryWindow.close.txDetails.seller",
|
||||
// formatter.formatCoinWithCode(sellerPayoutAmount),
|
||||
// sellerPayoutAddressString);
|
||||
// }
|
||||
// if (outputAmount.isPositive()) {
|
||||
// new Popup().width(900)
|
||||
// .headLine(Res.get("disputeSummaryWindow.close.txDetails.headline"))
|
||||
// .confirmation(Res.get("disputeSummaryWindow.close.txDetails",
|
||||
// formatter.formatCoinWithCode(inputAmount),
|
||||
// buyerDetails,
|
||||
// sellerDetails,
|
||||
// formatter.formatCoinWithCode(fee),
|
||||
// feePerByte,
|
||||
// kb))
|
||||
// .actionButtonText(Res.get("shared.yes"))
|
||||
// .onAction(() -> {
|
||||
// doPayout(buyerPayoutAmount,
|
||||
// sellerPayoutAmount,
|
||||
// fee,
|
||||
// buyerPayoutAddressString,
|
||||
// sellerPayoutAddressString,
|
||||
// resultHandler);
|
||||
// })
|
||||
// .closeButtonText(Res.get("shared.cancel"))
|
||||
// .show();
|
||||
// } else {
|
||||
// // No payout will be made
|
||||
// new Popup().headLine(Res.get("disputeSummaryWindow.close.noPayout.headline"))
|
||||
// .confirmation(Res.get("disputeSummaryWindow.close.noPayout.text"))
|
||||
// .actionButtonText(Res.get("shared.yes"))
|
||||
// .onAction(resultHandler::handleResult)
|
||||
// .closeButtonText(Res.get("shared.cancel"))
|
||||
// .show();
|
||||
// }
|
||||
}
|
||||
|
||||
private void doPayout(Coin buyerPayoutAmount,
|
||||
|
@ -734,82 +721,82 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
String buyerPayoutAddressString,
|
||||
String sellerPayoutAddressString,
|
||||
ResultHandler resultHandler) {
|
||||
try {
|
||||
Transaction tx = btcWalletService.createRefundPayoutTx(buyerPayoutAmount,
|
||||
sellerPayoutAmount,
|
||||
fee,
|
||||
buyerPayoutAddressString,
|
||||
sellerPayoutAddressString);
|
||||
tradeWalletService.broadcastTx(tx, new TxBroadcaster.Callback() {
|
||||
@Override
|
||||
public void onSuccess(Transaction transaction) {
|
||||
resultHandler.handleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(TxBroadcastException exception) {
|
||||
log.error("TxBroadcastException at doPayout", exception);
|
||||
new Popup().error(exception.toString()).show();
|
||||
}
|
||||
});
|
||||
} catch (InsufficientMoneyException | WalletException | TransactionVerificationException e) {
|
||||
log.error("Exception at doPayout", e);
|
||||
new Popup().error(e.toString()).show();
|
||||
}
|
||||
throw new RuntimeException("DisputeSummaryWindow.doPayout() needs updated for XMR");
|
||||
// try {
|
||||
// Transaction tx = btcWalletService.createRefundPayoutTx(buyerPayoutAmount,
|
||||
// sellerPayoutAmount,
|
||||
// fee,
|
||||
// buyerPayoutAddressString,
|
||||
// sellerPayoutAddressString);
|
||||
// tradeWalletService.broadcastTx(tx, new TxBroadcaster.Callback() {
|
||||
// @Override
|
||||
// public void onSuccess(Transaction transaction) {
|
||||
// resultHandler.handleResult();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onFailure(TxBroadcastException exception) {
|
||||
// log.error("TxBroadcastException at doPayout", exception);
|
||||
// new Popup().error(exception.toString()).show();
|
||||
// }
|
||||
// });
|
||||
// } catch (InsufficientMoneyException | WalletException | TransactionVerificationException e) {
|
||||
// log.error("Exception at doPayout", e);
|
||||
// new Popup().error(e.toString()).show();
|
||||
// }
|
||||
}
|
||||
|
||||
private void doCloseIfValid(Button closeTicketButton) {
|
||||
var disputeManager = checkNotNull(getDisputeManager(dispute));
|
||||
try {
|
||||
TradeDataValidation.validateDonationAddress(dispute.getDonationAddressOfDelayedPayoutTx(), daoFacade);
|
||||
TradeDataValidation.testIfDisputeTriesReplay(dispute, disputeManager.getDisputesAsObservableList());
|
||||
doClose(closeTicketButton);
|
||||
} catch (TradeDataValidation.AddressException exception) {
|
||||
String addressAsString = dispute.getDonationAddressOfDelayedPayoutTx();
|
||||
String tradeId = dispute.getTradeId();
|
||||
|
||||
// For mediators we do not enforce that the case cannot be closed to stay flexible,
|
||||
// but for refund agents we do.
|
||||
if (disputeManager instanceof MediationManager) {
|
||||
new Popup().width(900)
|
||||
.warning(Res.get("support.warning.disputesWithInvalidDonationAddress",
|
||||
addressAsString,
|
||||
daoFacade.getAllDonationAddresses(),
|
||||
tradeId,
|
||||
Res.get("support.warning.disputesWithInvalidDonationAddress.mediator")))
|
||||
.onAction(() -> {
|
||||
doClose(closeTicketButton);
|
||||
})
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.closeButtonText(Res.get("shared.no"))
|
||||
.show();
|
||||
} else {
|
||||
new Popup().width(900)
|
||||
.warning(Res.get("support.warning.disputesWithInvalidDonationAddress",
|
||||
addressAsString,
|
||||
daoFacade.getAllDonationAddresses(),
|
||||
tradeId,
|
||||
Res.get("support.warning.disputesWithInvalidDonationAddress.refundAgent")))
|
||||
.show();
|
||||
}
|
||||
} catch (TradeDataValidation.DisputeReplayException exception) {
|
||||
if (disputeManager instanceof MediationManager) {
|
||||
log.error("Closing of ticket failed as mediator", exception);
|
||||
new Popup().width(900)
|
||||
.warning(exception.getMessage())
|
||||
.onAction(() -> {
|
||||
doClose(closeTicketButton);
|
||||
})
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.closeButtonText(Res.get("shared.no"))
|
||||
.show();
|
||||
} else {
|
||||
log.error("Closing of ticket failed", exception);
|
||||
new Popup().width(900)
|
||||
.warning(exception.getMessage())
|
||||
.show();
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("DisputeSummaryWindow.doCloseIfValid() needs updated for XMR");
|
||||
// var disputeManager = checkNotNull(getDisputeManager(dispute));
|
||||
// try {
|
||||
// TradeDataValidation.validateDonationAddress(dispute.getDonationAddressOfDelayedPayoutTx(), daoFacade);
|
||||
// TradeDataValidation.testIfDisputeTriesReplay(dispute, disputeManager.getDisputesAsObservableList());
|
||||
// doClose(closeTicketButton);
|
||||
// } catch (TradeDataValidation.AddressException exception) {
|
||||
// String addressAsString = dispute.getDonationAddressOfDelayedPayoutTx();
|
||||
// String tradeId = dispute.getTradeId();
|
||||
//
|
||||
// // For mediators we do not enforce that the case cannot be closed to stay flexible,
|
||||
// // but for refund agents we do.
|
||||
// if (disputeManager instanceof MediationManager) {
|
||||
// new Popup().width(900)
|
||||
// .warning(Res.get("support.warning.disputesWithInvalidDonationAddress",
|
||||
// addressAsString,
|
||||
// daoFacade.getAllDonationAddresses(),
|
||||
// tradeId,
|
||||
// Res.get("support.warning.disputesWithInvalidDonationAddress.mediator")))
|
||||
// .onAction(() -> {
|
||||
// doClose(closeTicketButton);
|
||||
// })
|
||||
// .actionButtonText(Res.get("shared.yes"))
|
||||
// .closeButtonText(Res.get("shared.no"))
|
||||
// .show();
|
||||
// } else {
|
||||
// new Popup().width(900)
|
||||
// .warning(Res.get("support.warning.disputesWithInvalidDonationAddress",
|
||||
// addressAsString,
|
||||
// daoFacade.getAllDonationAddresses(),
|
||||
// tradeId,
|
||||
// Res.get("support.warning.disputesWithInvalidDonationAddress.refundAgent")))
|
||||
// .show();
|
||||
// }
|
||||
// } catch (TradeDataValidation.DisputeReplayException exception) {
|
||||
// if (disputeManager instanceof MediationManager) {
|
||||
// new Popup().width(900)
|
||||
// .warning(exception.getMessage())
|
||||
// .onAction(() -> {
|
||||
// doClose(closeTicketButton);
|
||||
// })
|
||||
// .actionButtonText(Res.get("shared.yes"))
|
||||
// .closeButtonText(Res.get("shared.no"))
|
||||
// .show();
|
||||
// } else {
|
||||
// new Popup().width(900)
|
||||
// .warning(exception.getMessage())
|
||||
// .show();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private void doClose(Button closeTicketButton) {
|
||||
|
@ -818,43 +805,58 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
return;
|
||||
}
|
||||
|
||||
boolean isRefundAgent = disputeManager instanceof RefundManager;
|
||||
// boolean isRefundAgent = disputeManager instanceof RefundManager;
|
||||
disputeResult.setLoserPublisher(isLoserPublisherCheckBox.isSelected());
|
||||
disputeResult.setCloseDate(new Date());
|
||||
dispute.setDisputeResult(disputeResult);
|
||||
dispute.setIsClosed();
|
||||
DisputeResult.Reason reason = disputeResult.getReason();
|
||||
// DisputeResult.Reason reason = disputeResult.getReason();
|
||||
|
||||
summaryNotesTextArea.textProperty().unbindBidirectional(disputeResult.summaryNotesProperty());
|
||||
String role = isRefundAgent ? Res.get("shared.refundAgent") : Res.get("shared.mediator");
|
||||
String agentNodeAddress = checkNotNull(disputeManager.getAgentNodeAddress(dispute)).getFullAddress();
|
||||
Contract contract = dispute.getContract();
|
||||
String currencyCode = contract.getOfferPayload().getCurrencyCode();
|
||||
String amount = formatter.formatCoinWithCode(contract.getTradeAmount());
|
||||
String textToSign = Res.get("disputeSummaryWindow.close.msg",
|
||||
DisplayUtils.formatDateTime(disputeResult.getCloseDate()),
|
||||
role,
|
||||
agentNodeAddress,
|
||||
dispute.getShortTradeId(),
|
||||
currencyCode,
|
||||
amount,
|
||||
formatter.formatCoinWithCode(disputeResult.getBuyerPayoutAmount()),
|
||||
formatter.formatCoinWithCode(disputeResult.getSellerPayoutAmount()),
|
||||
Res.get("disputeSummaryWindow.reason." + reason.name()),
|
||||
disputeResult.summaryNotesProperty().get()
|
||||
);
|
||||
|
||||
if (reason == DisputeResult.Reason.OPTION_TRADE &&
|
||||
dispute.getChatMessages().size() > 1 &&
|
||||
dispute.getChatMessages().get(1).isSystemMessage()) {
|
||||
textToSign += "\n" + dispute.getChatMessages().get(1).getMessage() + "\n";
|
||||
}
|
||||
// TODO (woodser): not used for xmr? calls setArbitratorSignature()
|
||||
|
||||
String summaryText = DisputeSummaryVerification.signAndApply(disputeManager, disputeResult, textToSign);
|
||||
// String role = isRefundAgent ? Res.get("shared.refundAgent") : Res.get("shared.mediator");
|
||||
// String agentNodeAddress = checkNotNull(disputeManager.getAgentNodeAddress(dispute)).getFullAddress();
|
||||
// Contract contract = dispute.getContract();
|
||||
// String currencyCode = contract.getOfferPayload().getCurrencyCode();
|
||||
// String amount = formatter.formatCoinWithCode(contract.getTradeAmount());
|
||||
//
|
||||
//
|
||||
// String textToSign = Res.get("disputeSummaryWindow.close.msg",
|
||||
// DisplayUtils.formatDateTime(disputeResult.getCloseDate()),
|
||||
// role,
|
||||
// agentNodeAddress,
|
||||
// dispute.getShortTradeId(),
|
||||
// currencyCode,
|
||||
// amount,
|
||||
// formatter.formatCoinWithCode(disputeResult.getBuyerPayoutAmount()),
|
||||
// formatter.formatCoinWithCode(disputeResult.getSellerPayoutAmount()),
|
||||
// Res.get("disputeSummaryWindow.reason." + reason.name()),
|
||||
// disputeResult.summaryNotesProperty().get()
|
||||
// );
|
||||
//
|
||||
// if (reason == DisputeResult.Reason.OPTION_TRADE &&
|
||||
// dispute.getChatMessages().size() > 1 &&
|
||||
// dispute.getChatMessages().get(1).isSystemMessage()) {
|
||||
// textToSign += "\n" + dispute.getChatMessages().get(1).getMessage() + "\n";
|
||||
// }
|
||||
//
|
||||
// String summaryText = DisputeSummaryVerification.signAndApply(disputeManager, disputeResult, textToSign);
|
||||
//
|
||||
// if (isRefundAgent) {
|
||||
// summaryText += Res.get("disputeSummaryWindow.close.nextStepsForRefundAgentArbitration");
|
||||
// } else {
|
||||
// summaryText += Res.get("disputeSummaryWindow.close.nextStepsForMediation");
|
||||
// }
|
||||
|
||||
if (isRefundAgent) {
|
||||
summaryText += Res.get("disputeSummaryWindow.close.nextStepsForRefundAgentArbitration");
|
||||
} else {
|
||||
String summaryText = Res.get("disputeSummaryWindow.close.msg",
|
||||
DisplayUtils.formatDateTime(disputeResult.getCloseDate()),
|
||||
formatter.formatCoinWithCode(disputeResult.getBuyerPayoutAmount()),
|
||||
formatter.formatCoinWithCode(disputeResult.getSellerPayoutAmount()),
|
||||
disputeResult.summaryNotesProperty().get());
|
||||
|
||||
if (dispute.isMediationDispute()) {
|
||||
summaryText += Res.get("disputeSummaryWindow.close.nextStepsForMediation");
|
||||
}
|
||||
|
||||
|
@ -877,19 +879,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
}
|
||||
|
||||
private DisputeManager<? extends DisputeList<Dispute>> getDisputeManager(Dispute dispute) {
|
||||
if (dispute.getSupportType() != null) {
|
||||
switch (dispute.getSupportType()) {
|
||||
case ARBITRATION:
|
||||
return null;
|
||||
case MEDIATION:
|
||||
return mediationManager;
|
||||
case TRADE:
|
||||
break;
|
||||
case REFUND:
|
||||
return refundManager;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return dispute.isMediationDispute() ? mediationManager : arbitrationManager;
|
||||
}
|
||||
|
||||
|
||||
|
@ -910,31 +900,25 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
Coin buyerSecurityDeposit = offer.getBuyerSecurityDeposit();
|
||||
Coin sellerSecurityDeposit = offer.getSellerSecurityDeposit();
|
||||
Coin tradeAmount = contract.getTradeAmount();
|
||||
|
||||
boolean isMediationDispute = getDisputeManager(dispute) instanceof MediationManager;
|
||||
// At mediation we require a min. payout to the losing party to keep incentive for the trader to accept the
|
||||
// mediated payout. For Refund agent cases we do not have that restriction.
|
||||
Coin minRefundAtDispute = isMediationDispute ? Restrictions.getMinRefundAtMediatedDispute() : Coin.ZERO;
|
||||
Coin maxPayoutAmount = tradeAmount
|
||||
.add(buyerSecurityDeposit)
|
||||
.add(sellerSecurityDeposit)
|
||||
.subtract(minRefundAtDispute);
|
||||
|
||||
if (selectedTradeAmountToggle == buyerGetsTradeAmountRadioButton) {
|
||||
disputeResult.setBuyerPayoutAmount(tradeAmount.add(buyerSecurityDeposit));
|
||||
disputeResult.setSellerPayoutAmount(sellerSecurityDeposit);
|
||||
disputeResult.setWinner(DisputeResult.Winner.BUYER);
|
||||
} else if (selectedTradeAmountToggle == buyerGetsAllRadioButton) {
|
||||
disputeResult.setBuyerPayoutAmount(maxPayoutAmount);
|
||||
disputeResult.setSellerPayoutAmount(minRefundAtDispute);
|
||||
disputeResult.setBuyerPayoutAmount(tradeAmount
|
||||
.add(buyerSecurityDeposit)
|
||||
.add(sellerSecurityDeposit)); // TODO (woodser): apply min payout to incentivize loser (see post v1.1.7)
|
||||
disputeResult.setSellerPayoutAmount(Coin.ZERO);
|
||||
disputeResult.setWinner(DisputeResult.Winner.BUYER);
|
||||
} else if (selectedTradeAmountToggle == sellerGetsTradeAmountRadioButton) {
|
||||
disputeResult.setBuyerPayoutAmount(buyerSecurityDeposit);
|
||||
disputeResult.setSellerPayoutAmount(tradeAmount.add(sellerSecurityDeposit));
|
||||
disputeResult.setWinner(DisputeResult.Winner.SELLER);
|
||||
} else if (selectedTradeAmountToggle == sellerGetsAllRadioButton) {
|
||||
disputeResult.setBuyerPayoutAmount(minRefundAtDispute);
|
||||
disputeResult.setSellerPayoutAmount(maxPayoutAmount);
|
||||
disputeResult.setBuyerPayoutAmount(Coin.ZERO);
|
||||
disputeResult.setSellerPayoutAmount(tradeAmount
|
||||
.add(sellerSecurityDeposit)
|
||||
.add(buyerSecurityDeposit));
|
||||
disputeResult.setWinner(DisputeResult.Winner.SELLER);
|
||||
}
|
||||
|
||||
|
@ -955,50 +939,20 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
buyerPayoutAmountInputTextField.setText(formatter.formatCoin(buyerPayoutAmount));
|
||||
sellerPayoutAmountInputTextField.setText(formatter.formatCoin(sellerPayoutAmount));
|
||||
|
||||
boolean isMediationDispute = getDisputeManager(dispute) instanceof MediationManager;
|
||||
// At mediation we require a min. payout to the losing party to keep incentive for the trader to accept the
|
||||
// mediated payout. For Refund agent cases we do not have that restriction.
|
||||
Coin minRefundAtDispute = isMediationDispute ? Restrictions.getMinRefundAtMediatedDispute() : Coin.ZERO;
|
||||
Coin maxPayoutAmount = tradeAmount
|
||||
.add(buyerSecurityDeposit)
|
||||
.add(sellerSecurityDeposit)
|
||||
.subtract(minRefundAtDispute);
|
||||
|
||||
if (buyerPayoutAmount.equals(tradeAmount.add(buyerSecurityDeposit)) &&
|
||||
sellerPayoutAmount.equals(sellerSecurityDeposit)) {
|
||||
buyerGetsTradeAmountRadioButton.setSelected(true);
|
||||
} else if (buyerPayoutAmount.equals(maxPayoutAmount) &&
|
||||
sellerPayoutAmount.equals(minRefundAtDispute)) {
|
||||
} else if (buyerPayoutAmount.equals(tradeAmount.add(buyerSecurityDeposit).add(sellerSecurityDeposit)) &&
|
||||
sellerPayoutAmount.equals(Coin.ZERO)) { // TODO (woodser): apply min payout to incentivize loser (see post v1.1.7)
|
||||
buyerGetsAllRadioButton.setSelected(true);
|
||||
} else if (sellerPayoutAmount.equals(tradeAmount.add(sellerSecurityDeposit))
|
||||
&& buyerPayoutAmount.equals(buyerSecurityDeposit)) {
|
||||
sellerGetsTradeAmountRadioButton.setSelected(true);
|
||||
} else if (sellerPayoutAmount.equals(maxPayoutAmount)
|
||||
&& buyerPayoutAmount.equals(minRefundAtDispute)) {
|
||||
} else if (sellerPayoutAmount.equals(tradeAmount.add(buyerSecurityDeposit).add(sellerSecurityDeposit))
|
||||
&& buyerPayoutAmount.equals(Coin.ZERO)) {
|
||||
sellerGetsAllRadioButton.setSelected(true);
|
||||
} else {
|
||||
customRadioButton.setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDelayedPayoutTransaction() {
|
||||
if (dispute.getDelayedPayoutTxId() == null)
|
||||
return;
|
||||
mempoolService.checkTxIsConfirmed(dispute.getDelayedPayoutTxId(), (validator -> {
|
||||
long confirms = validator.parseJsonValidateTx();
|
||||
log.info("Mempool check confirmation status of DelayedPayoutTxId returned: [{}]", confirms);
|
||||
displayPayoutStatus(confirms);
|
||||
}));
|
||||
}
|
||||
|
||||
private void displayPayoutStatus(long nConfirmStatus) {
|
||||
if (delayedPayoutTxStatus != null) {
|
||||
String status = Res.get("confidence.unknown");
|
||||
if (nConfirmStatus == 0)
|
||||
status = Res.get("confidence.seen", 1);
|
||||
else if (nConfirmStatus > 0)
|
||||
status = Res.get("confidence.confirmed", nConfirmStatus);
|
||||
delayedPayoutTxStatus.setText(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -711,24 +711,25 @@ public class ManualPayoutTxWindow extends Overlay<ManualPayoutTxWindow> {
|
|||
}
|
||||
|
||||
private void importFromMediationTicket(String tradeId) {
|
||||
clearInputFields();
|
||||
Optional<Dispute> optionalDispute = mediationManager.findDispute(tradeId);
|
||||
if (optionalDispute.isPresent()) {
|
||||
Dispute dispute = optionalDispute.get();
|
||||
depositTxHex.setText(dispute.getDepositTxId());
|
||||
if (dispute.disputeResultProperty().get() != null) {
|
||||
buyerPayoutAmount.setText(dispute.disputeResultProperty().get().getBuyerPayoutAmount().toPlainString());
|
||||
sellerPayoutAmount.setText(dispute.disputeResultProperty().get().getSellerPayoutAmount().toPlainString());
|
||||
}
|
||||
buyerAddressString.setText(dispute.getContract().getBuyerPayoutAddressString());
|
||||
sellerAddressString.setText(dispute.getContract().getSellerPayoutAddressString());
|
||||
buyerPubKeyAsHex.setText(Utils.HEX.encode(dispute.getContract().getBuyerMultiSigPubKey()));
|
||||
sellerPubKeyAsHex.setText(Utils.HEX.encode(dispute.getContract().getSellerMultiSigPubKey()));
|
||||
// switch back to the inputs pane
|
||||
hideAllPanes();
|
||||
inputsGridPane.setVisible(true);
|
||||
UserThread.execute(() -> new Popup().warning("Ticket imported. You still need to enter the multisig amount and specify if it is a legacy Tx").show());
|
||||
}
|
||||
throw new RuntimeException("ManualPayoutTxWindow.importFromMediationTicket() not adapted to XMR");
|
||||
// clearInputFields();
|
||||
// Optional<Dispute> optionalDispute = mediationManager.findDispute(tradeId);
|
||||
// if (optionalDispute.isPresent()) {
|
||||
// Dispute dispute = optionalDispute.get();
|
||||
// depositTxHex.setText(dispute.getDepositTxId());
|
||||
// if (dispute.disputeResultProperty().get() != null) {
|
||||
// buyerPayoutAmount.setText(dispute.disputeResultProperty().get().getBuyerPayoutAmount().toPlainString());
|
||||
// sellerPayoutAmount.setText(dispute.disputeResultProperty().get().getSellerPayoutAmount().toPlainString());
|
||||
// }
|
||||
// buyerAddressString.setText(dispute.getContract().getBuyerPayoutAddressString());
|
||||
// sellerAddressString.setText(dispute.getContract().getSellerPayoutAddressString());
|
||||
// buyerPubKeyAsHex.setText(Utils.HEX.encode(dispute.getContract().getBuyerMultiSigPubKey()));
|
||||
// sellerPubKeyAsHex.setText(Utils.HEX.encode(dispute.getContract().getSellerMultiSigPubKey()));
|
||||
// // switch back to the inputs pane
|
||||
// hideAllPanes();
|
||||
// inputsGridPane.setVisible(true);
|
||||
// UserThread.execute(() -> new Popup().warning("Ticket imported. You still need to enter the multisig amount and specify if it is a legacy Tx").show());
|
||||
// }
|
||||
}
|
||||
|
||||
private String generateSignature() {
|
||||
|
|
|
@ -46,9 +46,6 @@ import bisq.network.p2p.NodeAddress;
|
|||
import bisq.common.UserThread;
|
||||
import bisq.common.util.Tuple3;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.Utils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
|
@ -199,6 +196,12 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
|
|||
trade.getAssetTxProofResult() != null &&
|
||||
trade.getAssetTxProofResult() != AssetTxProofResult.UNDEFINED;
|
||||
|
||||
if (trade.getTakerFeeTxId() != null)
|
||||
rows++;
|
||||
if (trade.getMakerDepositTx() != null)
|
||||
rows++;
|
||||
if (trade.getTakerDepositTx() != null)
|
||||
rows++;
|
||||
if (trade.getPayoutTx() != null)
|
||||
rows++;
|
||||
boolean showDisputedTx = arbitrationManager.findOwnDispute(trade.getId()).isPresent() &&
|
||||
|
@ -287,25 +290,16 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
|
|||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.makerFeeTxId"), offer.getOfferFeePaymentTxId());
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.takerFeeTxId"), trade.getTakerFeeTxId());
|
||||
|
||||
String depositTxId = trade.getDepositTxId();
|
||||
Transaction depositTx = trade.getDepositTx();
|
||||
String depositTxIdFromTx = depositTx != null ? depositTx.getTxId().toString() : null;
|
||||
TxIdTextField depositTxIdTextField = addLabelTxIdTextField(gridPane, ++rowIndex,
|
||||
Res.get("shared.depositTransactionId"), depositTxId).second;
|
||||
if (depositTxId == null || !depositTxId.equals(depositTxIdFromTx)) {
|
||||
depositTxIdTextField.getTextField().setId("address-text-field-error");
|
||||
log.error("trade.getDepositTxId() and trade.getDepositTx().getTxId().toString() are not the same. " +
|
||||
"trade.getDepositTxId()={}, trade.getDepositTx().getTxId().toString()={}, depositTx={}",
|
||||
depositTxId, depositTxIdFromTx, depositTx);
|
||||
}
|
||||
|
||||
Transaction delayedPayoutTx = trade.getDelayedPayoutTx(btcWalletService);
|
||||
String delayedPayoutTxString = delayedPayoutTx != null ? delayedPayoutTx.getTxId().toString() : null;
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.delayedPayoutTxId"), delayedPayoutTxString);
|
||||
if (trade.getMakerDepositTx() != null)
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.depositTransactionId"), // TODO (woodser): separate UI labels for deposit tx ids
|
||||
trade.getMakerDepositTx().getHash());
|
||||
if (trade.getTakerDepositTx() != null)
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.depositTransactionId"), // TODO (woodser): separate UI labels for deposit tx ids
|
||||
trade.getTakerDepositTx().getHash());
|
||||
|
||||
if (trade.getPayoutTx() != null)
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.payoutTxId"),
|
||||
trade.getPayoutTx().getTxId().toString());
|
||||
trade.getPayoutTx().getHash());
|
||||
if (showDisputedTx)
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.disputedPayoutTxId"),
|
||||
arbitrationManager.findOwnDispute(trade.getId()).get().getDisputePayoutTxId());
|
||||
|
@ -347,21 +341,18 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
|
|||
textArea.setText(trade.getContractAsJson());
|
||||
String data = "Contract as json:\n";
|
||||
data += trade.getContractAsJson();
|
||||
data += "\n\nOther detail data:";
|
||||
data += "\n\nBuyerMultiSigPubKeyHex: " + Utils.HEX.encode(contract.getBuyerMultiSigPubKey());
|
||||
data += "\nSellerMultiSigPubKeyHex: " + Utils.HEX.encode(contract.getSellerMultiSigPubKey());
|
||||
if (CurrencyUtil.isFiatCurrency(offer.getCurrencyCode())) {
|
||||
data += "\n\nBuyersAccountAge: " + buyersAccountAge;
|
||||
data += "\nSellersAccountAge: " + sellersAccountAge;
|
||||
}
|
||||
|
||||
if (depositTx != null) {
|
||||
String depositTxAsHex = Utils.HEX.encode(depositTx.bitcoinSerialize(true));
|
||||
data += "\n\nRaw deposit transaction as hex:\n" + depositTxAsHex;
|
||||
}
|
||||
// TODO (woodser): include maker and taker deposit tx hex in contract?
|
||||
// if (depositTx != null) {
|
||||
// String depositTxAsHex = Utils.HEX.encode(depositTx.bitcoinSerialize(true));
|
||||
// data += "\n\nRaw deposit transaction as hex:\n" + depositTxAsHex;
|
||||
// }
|
||||
|
||||
data += "\n\nSelected mediator: " + DisputeAgentLookupMap.getKeyBaseUserName(contract.getMediatorNodeAddress().getFullAddress());
|
||||
data += "\nSelected arbitrator (refund agent): " + DisputeAgentLookupMap.getKeyBaseUserName(contract.getRefundAgentNodeAddress().getFullAddress());
|
||||
data += "\n\nSelected arbitrator: " + DisputeAgentLookupMap.getKeyBaseUserName(contract.getArbitratorNodeAddress().getFullAddress());
|
||||
|
||||
textArea.setText(data);
|
||||
textArea.setPrefHeight(50);
|
||||
|
|
|
@ -433,7 +433,10 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
|||
if (trade.getTakerFeeTxId() != null && trade.getTakerFeeTxId().contains(filterString)) {
|
||||
return true;
|
||||
}
|
||||
if (trade.getDepositTxId() != null && trade.getDepositTxId().contains(filterString)) {
|
||||
if (trade.getMakerDepositTxId() != null && trade.getMakerDepositTxId().contains(filterString)) {
|
||||
return true;
|
||||
}
|
||||
if (trade.getTakerDepositTxId() != null && trade.getTakerDepositTxId().contains(filterString)) {
|
||||
return true;
|
||||
}
|
||||
if (trade.getPayoutTxId() != null && trade.getPayoutTxId().contains(filterString)) {
|
||||
|
|
|
@ -23,7 +23,7 @@ import bisq.desktop.main.offer.MutableOfferDataModel;
|
|||
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.btc.wallet.BsqWalletService;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.offer.CreateOfferService;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.offer.OfferUtil;
|
||||
|
@ -48,7 +48,7 @@ class DuplicateOfferDataModel extends MutableOfferDataModel {
|
|||
DuplicateOfferDataModel(CreateOfferService createOfferService,
|
||||
OpenOfferManager openOfferManager,
|
||||
OfferUtil offerUtil,
|
||||
BtcWalletService btcWalletService,
|
||||
XmrWalletService btcWalletService,
|
||||
BsqWalletService bsqWalletService,
|
||||
Preferences preferences,
|
||||
User user,
|
||||
|
|
|
@ -23,8 +23,8 @@ import bisq.desktop.main.offer.MutableOfferDataModel;
|
|||
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.btc.wallet.BsqWalletService;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.Restrictions;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.CurrencyUtil;
|
||||
import bisq.core.locale.TradeCurrency;
|
||||
import bisq.core.offer.CreateOfferService;
|
||||
|
@ -65,7 +65,7 @@ class EditOfferDataModel extends MutableOfferDataModel {
|
|||
EditOfferDataModel(CreateOfferService createOfferService,
|
||||
OpenOfferManager openOfferManager,
|
||||
OfferUtil offerUtil,
|
||||
BtcWalletService btcWalletService,
|
||||
XmrWalletService xmrWalletService,
|
||||
BsqWalletService bsqWalletService,
|
||||
Preferences preferences,
|
||||
User user,
|
||||
|
@ -81,7 +81,7 @@ class EditOfferDataModel extends MutableOfferDataModel {
|
|||
super(createOfferService,
|
||||
openOfferManager,
|
||||
offerUtil,
|
||||
btcWalletService,
|
||||
xmrWalletService,
|
||||
bsqWalletService,
|
||||
preferences,
|
||||
user,
|
||||
|
|
|
@ -282,7 +282,10 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
|
|||
if (trade.getTakerFeeTxId() != null && trade.getTakerFeeTxId().contains(filterString)) {
|
||||
return true;
|
||||
}
|
||||
if (trade.getDepositTxId() != null && trade.getDepositTxId().contains(filterString)) {
|
||||
if (trade.getMakerDepositTxId() != null && trade.getMakerDepositTxId().contains(filterString)) {
|
||||
return true;
|
||||
}
|
||||
if (trade.getTakerDepositTxId() != null && trade.getTakerDepositTxId().contains(filterString)) {
|
||||
return true;
|
||||
}
|
||||
if (trade.getPayoutTxId() != null && trade.getPayoutTxId().contains(filterString)) {
|
||||
|
@ -319,8 +322,8 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
|
|||
private String checkTxs() {
|
||||
Trade trade = sortedList.get(tableView.getSelectionModel().getFocusedIndex()).getTrade();
|
||||
log.info("Initiated unfail of trade {}", trade.getId());
|
||||
if (trade.getDepositTx() == null) {
|
||||
log.info("Check unfail found no depositTx for trade {}", trade.getId());
|
||||
if (trade.getMakerDepositTx() == null || trade.getTakerDepositTx() == null) {
|
||||
log.info("Check unfail found no deposit tx(s) for trade {}", trade.getId());
|
||||
return Res.get("portfolio.failed.depositTxNull");
|
||||
}
|
||||
if (trade.getDelayedPayoutTxBytes() == null) {
|
||||
|
|
|
@ -24,14 +24,13 @@ import bisq.desktop.main.overlays.notifications.NotificationCenter;
|
|||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.main.overlays.windows.WalletPasswordWindow;
|
||||
import bisq.desktop.main.support.SupportView;
|
||||
import bisq.desktop.main.support.dispute.client.arbitration.ArbitrationClientView;
|
||||
import bisq.desktop.main.support.dispute.client.mediation.MediationClientView;
|
||||
import bisq.desktop.main.support.dispute.client.refund.RefundClientView;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.btc.setup.WalletsSetup;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.dao.DaoFacade;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.offer.OfferPayload;
|
||||
|
@ -42,21 +41,16 @@ import bisq.core.support.dispute.Dispute;
|
|||
import bisq.core.support.dispute.DisputeAlreadyOpenException;
|
||||
import bisq.core.support.dispute.DisputeList;
|
||||
import bisq.core.support.dispute.DisputeManager;
|
||||
import bisq.core.support.dispute.DisputeResult;
|
||||
import bisq.core.support.dispute.arbitration.ArbitrationManager;
|
||||
import bisq.core.support.dispute.mediation.MediationManager;
|
||||
import bisq.core.support.dispute.refund.RefundManager;
|
||||
import bisq.core.support.messages.ChatMessage;
|
||||
import bisq.core.support.traderchat.TraderChatManager;
|
||||
import bisq.core.trade.BuyerTrade;
|
||||
import bisq.core.trade.SellerTrade;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.TradeDataValidation;
|
||||
import bisq.core.trade.TradeManager;
|
||||
import bisq.core.trade.protocol.BuyerProtocol;
|
||||
import bisq.core.trade.protocol.DisputeProtocol;
|
||||
import bisq.core.trade.protocol.SellerProtocol;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
|
@ -66,8 +60,6 @@ import bisq.common.handlers.FaultHandler;
|
|||
import bisq.common.handlers.ResultHandler;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
|
@ -84,7 +76,6 @@ import javafx.collections.ObservableList;
|
|||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.Getter;
|
||||
|
@ -94,17 +85,21 @@ import javax.annotation.Nullable;
|
|||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
||||
|
||||
import monero.wallet.MoneroWallet;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
public class PendingTradesDataModel extends ActivatableDataModel {
|
||||
@Getter
|
||||
public final TradeManager tradeManager;
|
||||
public final BtcWalletService btcWalletService;
|
||||
public final XmrWalletService xmrWalletService;
|
||||
public final ArbitrationManager arbitrationManager;
|
||||
public final MediationManager mediationManager;
|
||||
public final RefundManager refundManager;
|
||||
private final P2PService p2PService;
|
||||
private final WalletsSetup walletsSetup;
|
||||
@Getter
|
||||
private final AccountAgeWitnessService accountAgeWitnessService;
|
||||
public final DaoFacade daoFacade;
|
||||
public final Navigation navigation;
|
||||
public final WalletPasswordWindow walletPasswordWindow;
|
||||
private final NotificationCenter notificationCenter;
|
||||
|
@ -115,7 +110,8 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
private boolean isMaker;
|
||||
|
||||
final ObjectProperty<PendingTradesListItem> selectedItemProperty = new SimpleObjectProperty<>();
|
||||
public final StringProperty txId = new SimpleStringProperty();
|
||||
public final StringProperty makerTxId = new SimpleStringProperty();
|
||||
public final StringProperty takerTxId = new SimpleStringProperty();
|
||||
|
||||
@Getter
|
||||
private final TraderChatManager traderChatManager;
|
||||
|
@ -132,31 +128,29 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
|
||||
@Inject
|
||||
public PendingTradesDataModel(TradeManager tradeManager,
|
||||
BtcWalletService btcWalletService,
|
||||
XmrWalletService xmrWalletService,
|
||||
PubKeyRing pubKeyRing,
|
||||
ArbitrationManager arbitrationManager,
|
||||
MediationManager mediationManager,
|
||||
RefundManager refundManager,
|
||||
TraderChatManager traderChatManager,
|
||||
Preferences preferences,
|
||||
P2PService p2PService,
|
||||
WalletsSetup walletsSetup,
|
||||
AccountAgeWitnessService accountAgeWitnessService,
|
||||
DaoFacade daoFacade,
|
||||
Navigation navigation,
|
||||
WalletPasswordWindow walletPasswordWindow,
|
||||
NotificationCenter notificationCenter,
|
||||
OfferUtil offerUtil) {
|
||||
this.tradeManager = tradeManager;
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.xmrWalletService = xmrWalletService;
|
||||
this.pubKeyRing = pubKeyRing;
|
||||
this.arbitrationManager = arbitrationManager;
|
||||
this.mediationManager = mediationManager;
|
||||
this.refundManager = refundManager;
|
||||
this.traderChatManager = traderChatManager;
|
||||
this.preferences = preferences;
|
||||
this.p2PService = p2PService;
|
||||
this.walletsSetup = walletsSetup;
|
||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||
this.daoFacade = daoFacade;
|
||||
this.navigation = navigation;
|
||||
this.walletPasswordWindow = walletPasswordWindow;
|
||||
this.notificationCenter = notificationCenter;
|
||||
|
@ -290,10 +284,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
return Coin.ZERO;
|
||||
}
|
||||
} else {
|
||||
if (trade.isCurrencyForTakerFeeBtc())
|
||||
return trade.getTakerFee();
|
||||
else
|
||||
return Coin.ZERO; // getTradeFeeAsBsq is used for BSQ
|
||||
return trade.getTakerFee();
|
||||
}
|
||||
} else {
|
||||
log.error("Trade is null at getTotalFees");
|
||||
|
@ -316,10 +307,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
return Coin.ZERO;
|
||||
}
|
||||
} else {
|
||||
if (trade.isCurrencyForTakerFeeBtc())
|
||||
return trade.getTxFee().multiply(3);
|
||||
else
|
||||
return trade.getTxFee().multiply(3).subtract(trade.getTakerFee()); // BSQ will be used as part of the miner fee
|
||||
return trade.getTxFee().multiply(3);
|
||||
}
|
||||
} else {
|
||||
log.error("Trade is null at getTotalFees");
|
||||
|
@ -343,10 +331,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
return Coin.ZERO;
|
||||
}
|
||||
} else {
|
||||
if (trade.isCurrencyForTakerFeeBtc())
|
||||
return Coin.ZERO; // getTradeFeeInBTC is used for BTC
|
||||
else
|
||||
return trade.getTakerFee();
|
||||
return Coin.ZERO; // getTradeFeeInBTC is used for BTC
|
||||
}
|
||||
} else {
|
||||
log.error("Trade is null at getTotalFees");
|
||||
|
@ -414,15 +399,18 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
return;
|
||||
}
|
||||
|
||||
Transaction depositTx = selectedTrade.getDepositTx();
|
||||
MoneroTxWallet makerDepositTx = selectedTrade.getMakerDepositTx();
|
||||
MoneroTxWallet takerDepositTx = selectedTrade.getTakerDepositTx();
|
||||
String tradeId = selectedTrade.getId();
|
||||
tradeStateChangeListener = (observable, oldValue, newValue) -> {
|
||||
if (depositTx != null) {
|
||||
txId.set(depositTx.getTxId().toString());
|
||||
if (makerDepositTx != null && takerDepositTx != null) { // TODO (woodser): this treats separate deposit ids as one unit, being both available or unavailable
|
||||
makerTxId.set(makerDepositTx.getHash());
|
||||
takerTxId.set(takerDepositTx.getHash());
|
||||
notificationCenter.setSelectedTradeId(tradeId);
|
||||
selectedTrade.stateProperty().removeListener(tradeStateChangeListener);
|
||||
} else {
|
||||
txId.set("");
|
||||
makerTxId.set("");
|
||||
takerTxId.set("");
|
||||
}
|
||||
};
|
||||
selectedTrade.stateProperty().addListener(tradeStateChangeListener);
|
||||
|
@ -434,15 +422,18 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
}
|
||||
|
||||
isMaker = tradeManager.isMyOffer(offer);
|
||||
if (depositTx != null) {
|
||||
txId.set(depositTx.getTxId().toString());
|
||||
if (makerDepositTx != null && takerDepositTx != null) {
|
||||
makerTxId.set(makerDepositTx.getHash());
|
||||
takerTxId.set(takerDepositTx.getHash());
|
||||
} else {
|
||||
txId.set("");
|
||||
makerTxId.set("");
|
||||
takerTxId.set("");
|
||||
}
|
||||
notificationCenter.setSelectedTradeId(tradeId);
|
||||
} else {
|
||||
selectedTrade = null;
|
||||
txId.set("");
|
||||
makerTxId.set("");
|
||||
takerTxId.set("");
|
||||
notificationCenter.setSelectedTradeId(null);
|
||||
}
|
||||
selectedItemProperty.set(item);
|
||||
|
@ -455,72 +446,96 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
return;
|
||||
}
|
||||
|
||||
doOpenDispute(isSupportTicket, trade.getDepositTx());
|
||||
doOpenDispute(isSupportTicket, trade);
|
||||
}
|
||||
|
||||
private void doOpenDispute(boolean isSupportTicket, Transaction depositTx) {
|
||||
// We do not support opening a dispute if the deposit tx is null. Traders have to use the support channel at keybase
|
||||
// in such cases. The mediators or arbitrators could not help anyway with a payout in such cases.
|
||||
if (depositTx == null) {
|
||||
log.error("Deposit tx must not be null");
|
||||
new Popup().instruction(Res.get("portfolio.pending.error.depositTxNull")).show();
|
||||
return;
|
||||
}
|
||||
String depositTxId = depositTx.getTxId().toString();
|
||||
private void doOpenDispute(boolean isSupportTicket, Trade trade) {
|
||||
if (trade == null) {
|
||||
log.warn("trade is null at doOpenDispute");
|
||||
return;
|
||||
}
|
||||
|
||||
Trade trade = getTrade();
|
||||
if (trade == null) {
|
||||
log.warn("trade is null at doOpenDispute");
|
||||
return;
|
||||
// We do not support opening a dispute if the deposit tx is null. Traders have to use the support channel at keybase
|
||||
// in such cases. The mediators or arbitrators could not help anyway with a payout in such cases.
|
||||
String depositTxId = null;
|
||||
if (isMaker) {
|
||||
if (trade.getMakerDepositTxId() == null) {
|
||||
log.error("Deposit tx must not be null");
|
||||
new Popup().instruction(Res.get("portfolio.pending.error.depositTxNull")).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Offer offer = trade.getOffer();
|
||||
if (offer == null) {
|
||||
log.warn("offer is null at doOpenDispute");
|
||||
return;
|
||||
depositTxId = trade.getMakerDepositTxId();
|
||||
} else {
|
||||
if (trade.getTakerDepositTxId() == null) {
|
||||
log.error("Deposit tx must not be null");
|
||||
new Popup().instruction(Res.get("portfolio.pending.error.depositTxNull")).show();
|
||||
return;
|
||||
}
|
||||
depositTxId = trade.getTakerDepositTxId();
|
||||
}
|
||||
|
||||
if (!GUIUtil.isBootstrappedOrShowPopup(p2PService)) {
|
||||
return;
|
||||
}
|
||||
Offer offer = trade.getOffer();
|
||||
if (offer == null) {
|
||||
log.warn("offer is null at doOpenDispute");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] payoutTxSerialized = null;
|
||||
String payoutTxHashAsString = null;
|
||||
Transaction payoutTx = trade.getPayoutTx();
|
||||
if (payoutTx != null) {
|
||||
payoutTxSerialized = payoutTx.bitcoinSerialize();
|
||||
payoutTxHashAsString = payoutTx.getTxId().toString();
|
||||
}
|
||||
Trade.DisputeState disputeState = trade.getDisputeState();
|
||||
DisputeManager<? extends DisputeList<Dispute>> disputeManager;
|
||||
boolean useMediation;
|
||||
boolean useRefundAgent;
|
||||
// In case we re-open a dispute we allow Trade.DisputeState.MEDIATION_REQUESTED
|
||||
useMediation = disputeState == Trade.DisputeState.NO_DISPUTE || disputeState == Trade.DisputeState.MEDIATION_REQUESTED;
|
||||
// In case we re-open a dispute we allow Trade.DisputeState.REFUND_REQUESTED
|
||||
useRefundAgent = disputeState == Trade.DisputeState.MEDIATION_CLOSED || disputeState == Trade.DisputeState.REFUND_REQUESTED;
|
||||
if (!GUIUtil.isBootstrappedOrShowPopup(p2PService)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AtomicReference<String> donationAddressString = new AtomicReference<>("");
|
||||
Transaction delayedPayoutTx = trade.getDelayedPayoutTx();
|
||||
try {
|
||||
TradeDataValidation.validateDelayedPayoutTx(trade,
|
||||
delayedPayoutTx,
|
||||
daoFacade,
|
||||
btcWalletService,
|
||||
donationAddressString::set);
|
||||
} catch (TradeDataValidation.ValidationException e) {
|
||||
// The peer sent us an invalid donation address. We do not return here as we don't want to break
|
||||
// mediation/arbitration and log only the issue. The dispute agent will run validation as well and will get
|
||||
// a popup displayed to react.
|
||||
log.error("DelayedPayoutTxValidation failed. {}", e.toString());
|
||||
byte[] payoutTxSerialized = null;
|
||||
String payoutTxHashAsString = null;
|
||||
MoneroTxWallet payoutTx = trade.getPayoutTx();
|
||||
MoneroWallet multisigWallet = xmrWalletService.getOrCreateMultisigWallet(trade.getId());
|
||||
String updatedMultisigHex = multisigWallet.getMultisigHex();
|
||||
if (payoutTx != null) {
|
||||
// payoutTxSerialized = payoutTx.bitcoinSerialize(); // TODO (woodser): no need to pass serialized txs for xmr
|
||||
// payoutTxHashAsString = payoutTx.getHashAsString();
|
||||
}
|
||||
Trade.DisputeState disputeState = trade.getDisputeState();
|
||||
DisputeManager<? extends DisputeList<Dispute>> disputeManager;
|
||||
boolean useMediation;
|
||||
boolean useArbitration;
|
||||
// If mediation is not activated we use arbitration
|
||||
if (false) { // TODO (woodser): use mediation for xmr? if (MediationManager.isMediationActivated()) {
|
||||
// In case we re-open a dispute we allow Trade.DisputeState.MEDIATION_REQUESTED or
|
||||
useMediation = disputeState == Trade.DisputeState.NO_DISPUTE || disputeState == Trade.DisputeState.MEDIATION_REQUESTED;
|
||||
// in case of arbitration disputeState == Trade.DisputeState.ARBITRATION_REQUESTED
|
||||
useArbitration = disputeState == Trade.DisputeState.MEDIATION_CLOSED || disputeState == Trade.DisputeState.DISPUTE_REQUESTED;
|
||||
} else {
|
||||
useMediation = false;
|
||||
useArbitration = true;
|
||||
}
|
||||
|
||||
if (useRefundAgent) {
|
||||
// We don't allow to continue and publish payout tx and open refund agent case.
|
||||
// In case it was caused by some bug we want to prevent a wrong payout. In case its a scam attempt we
|
||||
// want to protect the refund agent.
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if (useMediation) {
|
||||
// // If no dispute state set we start with mediation
|
||||
// disputeManager = mediationManager;
|
||||
// PubKeyRing mediatorPubKeyRing = trade.getMediatorPubKeyRing();
|
||||
// checkNotNull(mediatorPubKeyRing, "mediatorPubKeyRing must not be null");
|
||||
// byte[] depositTxSerialized = null; // depositTx.bitcoinSerialize(); // TODO (woodser): no serialized txs in xmr
|
||||
// String depositTxHashAsString = null; // depositTx.getHashAsString(); // TODO (woodser): two deposit txs for dispute
|
||||
// Dispute dispute = new Dispute(new Date().getTime(),
|
||||
// trade.getId(),
|
||||
// pubKeyRing.hashCode(), // traderId
|
||||
// true,
|
||||
// (offer.getDirection() == OfferPayload.Direction.BUY) == isMaker,
|
||||
// isMaker,
|
||||
// pubKeyRing,
|
||||
// trade.getDate().getTime(),
|
||||
// trade.getMaxTradePeriodDate().getTime(),
|
||||
// trade.getContract(),
|
||||
// trade.getContractHash(),
|
||||
// depositTxSerialized,
|
||||
// payoutTxSerialized,
|
||||
// depositTxHashAsString,
|
||||
// payoutTxHashAsString,
|
||||
// trade.getContractAsJson(),
|
||||
// trade.getMakerContractSignature(),
|
||||
// trade.getTakerContractSignature(),
|
||||
// mediatorPubKeyRing,
|
||||
// isSupportTicket,
|
||||
// SupportType.MEDIATION);
|
||||
|
||||
ResultHandler resultHandler;
|
||||
if (useMediation) {
|
||||
|
@ -529,10 +544,11 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
disputeManager = mediationManager;
|
||||
PubKeyRing mediatorPubKeyRing = trade.getMediatorPubKeyRing();
|
||||
checkNotNull(mediatorPubKeyRing, "mediatorPubKeyRing must not be null");
|
||||
byte[] depositTxSerialized = depositTx.bitcoinSerialize();
|
||||
byte[] depositTxSerialized = null; // depositTx.bitcoinSerialize(); // TODO (woodser): no serialized txs in xmr
|
||||
Dispute dispute = new Dispute(new Date().getTime(),
|
||||
trade.getId(),
|
||||
pubKeyRing.hashCode(), // traderId
|
||||
true,
|
||||
(offer.getDirection() == OfferPayload.Direction.BUY) == isMaker,
|
||||
isMaker,
|
||||
pubKeyRing,
|
||||
|
@ -553,98 +569,61 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
dispute.setExtraData("counterCurrencyTxId", trade.getCounterCurrencyTxId());
|
||||
dispute.setExtraData("counterCurrencyExtraData", trade.getCounterCurrencyExtraData());
|
||||
|
||||
dispute.setDonationAddressOfDelayedPayoutTx(donationAddressString.get());
|
||||
if (delayedPayoutTx != null) {
|
||||
dispute.setDelayedPayoutTxId(delayedPayoutTx.getTxId().toString());
|
||||
}
|
||||
|
||||
trade.setDisputeState(Trade.DisputeState.MEDIATION_REQUESTED);
|
||||
sendOpenDisputeMessage(disputeManager, resultHandler, dispute);
|
||||
sendOpenNewDisputeMessage(dispute, false, disputeManager, updatedMultisigHex);
|
||||
tradeManager.requestPersistence();
|
||||
} else if (useRefundAgent) {
|
||||
resultHandler = () -> navigation.navigateTo(MainView.class, SupportView.class, RefundClientView.class);
|
||||
} else if (useArbitration) {
|
||||
// Only if we have completed mediation we allow arbitration
|
||||
disputeManager = arbitrationManager;
|
||||
PubKeyRing arbitratorPubKeyRing = trade.getArbitratorPubKeyRing();
|
||||
checkNotNull(arbitratorPubKeyRing, "arbitratorPubKeyRing must not be null");
|
||||
byte[] depositTxSerialized = null; // depositTx.bitcoinSerialize(); TODO (woodser)
|
||||
String depositTxHashAsString = null; // depositTx.getHashAsString(); TODO (woodser)
|
||||
Dispute dispute = new Dispute(new Date().getTime(),
|
||||
trade.getId(),
|
||||
pubKeyRing.hashCode(), // traderId,
|
||||
true,
|
||||
(offer.getDirection() == OfferPayload.Direction.BUY) == isMaker,
|
||||
isMaker,
|
||||
pubKeyRing,
|
||||
trade.getDate().getTime(),
|
||||
trade.getMaxTradePeriodDate().getTime(),
|
||||
trade.getContract(),
|
||||
trade.getContractHash(),
|
||||
depositTxSerialized,
|
||||
payoutTxSerialized,
|
||||
depositTxHashAsString,
|
||||
payoutTxHashAsString,
|
||||
trade.getContractAsJson(),
|
||||
trade.getMakerContractSignature(),
|
||||
trade.getTakerContractSignature(),
|
||||
arbitratorPubKeyRing,
|
||||
isSupportTicket,
|
||||
SupportType.ARBITRATION);
|
||||
|
||||
if (delayedPayoutTx == null) {
|
||||
log.error("Delayed payout tx is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
// We only require for refund agent a confirmed deposit tx. For mediation we tolerate a unconfirmed tx as
|
||||
// no harm can be done to the mediator (refund agent who would accept a invalid deposit tx might reimburse
|
||||
// the traders but the funds never have been spent).
|
||||
TransactionConfidence confidenceForTxId = btcWalletService.getConfidenceForTxId(depositTxId);
|
||||
if (confidenceForTxId == null || confidenceForTxId.getConfidenceType() != TransactionConfidence.ConfidenceType.BUILDING) {
|
||||
log.error("Confidence for deposit tx must be BUILDING, confidenceForTxId={}", confidenceForTxId);
|
||||
new Popup().instruction(Res.get("portfolio.pending.error.depositTxNotConfirmed")).show();
|
||||
return;
|
||||
}
|
||||
|
||||
long lockTime = delayedPayoutTx.getLockTime();
|
||||
int bestChainHeight = btcWalletService.getBestChainHeight();
|
||||
long remaining = lockTime - bestChainHeight;
|
||||
if (remaining > 0) {
|
||||
new Popup().instruction(Res.get("portfolio.pending.timeLockNotOver",
|
||||
FormattingUtils.getDateFromBlockHeight(remaining), remaining))
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
|
||||
disputeManager = refundManager;
|
||||
PubKeyRing refundAgentPubKeyRing = trade.getRefundAgentPubKeyRing();
|
||||
checkNotNull(refundAgentPubKeyRing, "refundAgentPubKeyRing must not be null");
|
||||
byte[] depositTxSerialized = depositTx.bitcoinSerialize();
|
||||
String depositTxHashAsString = depositTx.getTxId().toString();
|
||||
Dispute dispute = new Dispute(new Date().getTime(),
|
||||
trade.getId(),
|
||||
pubKeyRing.hashCode(), // traderId
|
||||
(offer.getDirection() == OfferPayload.Direction.BUY) == isMaker,
|
||||
isMaker,
|
||||
pubKeyRing,
|
||||
trade.getDate().getTime(),
|
||||
trade.getMaxTradePeriodDate().getTime(),
|
||||
trade.getContract(),
|
||||
trade.getContractHash(),
|
||||
depositTxSerialized,
|
||||
payoutTxSerialized,
|
||||
depositTxHashAsString,
|
||||
payoutTxHashAsString,
|
||||
trade.getContractAsJson(),
|
||||
trade.getMakerContractSignature(),
|
||||
trade.getTakerContractSignature(),
|
||||
refundAgentPubKeyRing,
|
||||
isSupportTicket,
|
||||
SupportType.REFUND);
|
||||
dispute.setExtraData("counterCurrencyTxId", trade.getCounterCurrencyTxId());
|
||||
dispute.setExtraData("counterCurrencyExtraData", trade.getCounterCurrencyExtraData());
|
||||
|
||||
String tradeId = dispute.getTradeId();
|
||||
mediationManager.findDispute(tradeId)
|
||||
.ifPresent(mediatorsDispute -> {
|
||||
DisputeResult mediatorsDisputeResult = mediatorsDispute.getDisputeResultProperty().get();
|
||||
ChatMessage mediatorsResultMessage = mediatorsDisputeResult.getChatMessage();
|
||||
if (mediatorsResultMessage != null) {
|
||||
String mediatorAddress = Res.get("support.mediatorsAddress",
|
||||
mediatorsDispute.getContract().getRefundAgentNodeAddress().getFullAddress());
|
||||
String message = mediatorAddress + "\n\n" + mediatorsResultMessage.getMessage();
|
||||
dispute.setMediatorsDisputeResult(message);
|
||||
}
|
||||
});
|
||||
|
||||
dispute.setDonationAddressOfDelayedPayoutTx(donationAddressString.get());
|
||||
dispute.setDelayedPayoutTxId(delayedPayoutTx.getTxId().toString());
|
||||
trade.setDisputeState(Trade.DisputeState.REFUND_REQUESTED);
|
||||
|
||||
((DisputeProtocol) tradeManager.getTradeProtocol(trade)).onPublishDelayedPayoutTx(() -> {
|
||||
log.info("DelayedPayoutTx published and message sent to peer");
|
||||
sendOpenDisputeMessage(disputeManager, resultHandler, dispute);
|
||||
},
|
||||
errorMessage -> new Popup().error(errorMessage).show());
|
||||
trade.setDisputeState(Trade.DisputeState.DISPUTE_REQUESTED);
|
||||
sendOpenNewDisputeMessage(dispute, false, disputeManager, updatedMultisigHex);
|
||||
} else {
|
||||
log.warn("Invalid dispute state {}", disputeState.name());
|
||||
}
|
||||
tradeManager.requestPersistence();
|
||||
}
|
||||
|
||||
private void sendOpenNewDisputeMessage(Dispute dispute, boolean reOpen, DisputeManager<? extends DisputeList<Dispute>> disputeManager, String senderMultisigHex) {
|
||||
disputeManager.sendOpenNewDisputeMessage(dispute, reOpen, senderMultisigHex,
|
||||
() -> navigation.navigateTo(MainView.class, SupportView.class, ArbitrationClientView.class), (errorMessage, throwable) -> {
|
||||
if ((throwable instanceof DisputeAlreadyOpenException)) {
|
||||
errorMessage += "\n\n" + Res.get("portfolio.pending.openAgainDispute.msg");
|
||||
new Popup().warning(errorMessage)
|
||||
.actionButtonText(Res.get("portfolio.pending.openAgainDispute.button"))
|
||||
.onAction(() -> sendOpenNewDisputeMessage(dispute, true, disputeManager, senderMultisigHex))
|
||||
.closeButtonText(Res.get("shared.cancel")).show();
|
||||
} else {
|
||||
new Popup().warning(errorMessage).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isReadyForTxBroadcast() {
|
||||
return GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, walletsSetup);
|
||||
}
|
||||
|
@ -660,28 +639,5 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
public boolean isSignWitnessTrade() {
|
||||
return accountAgeWitnessService.isSignWitnessTrade(selectedTrade);
|
||||
}
|
||||
|
||||
private void sendOpenDisputeMessage(DisputeManager<? extends DisputeList<Dispute>> disputeManager,
|
||||
ResultHandler resultHandler,
|
||||
Dispute dispute) {
|
||||
disputeManager.sendOpenNewDisputeMessage(dispute,
|
||||
false,
|
||||
resultHandler,
|
||||
(errorMessage, throwable) -> {
|
||||
if ((throwable instanceof DisputeAlreadyOpenException)) {
|
||||
errorMessage += "\n\n" + Res.get("portfolio.pending.openAgainDispute.msg");
|
||||
new Popup().warning(errorMessage)
|
||||
.actionButtonText(Res.get("portfolio.pending.openAgainDispute.button"))
|
||||
.onAction(() -> disputeManager.sendOpenNewDisputeMessage(dispute,
|
||||
true,
|
||||
resultHandler,
|
||||
(e, t) -> log.error(e)))
|
||||
.closeButtonText(Res.get("shared.cancel"))
|
||||
.show();
|
||||
} else {
|
||||
new Popup().warning(errorMessage).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -408,14 +408,12 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
|||
Res.get("portfolio.pending.failedTrade.taker.missingTakerFeeTx");
|
||||
}
|
||||
|
||||
if (trade.getDepositTx() == null) {
|
||||
if (trade.getMakerDepositTx() == null) {
|
||||
return Res.get("portfolio.pending.failedTrade.missingDepositTx");
|
||||
}
|
||||
|
||||
if (trade.getDelayedPayoutTx() == null) {
|
||||
return isMyRoleBuyer ?
|
||||
Res.get("portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx") :
|
||||
Res.get("portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx");
|
||||
|
||||
if (trade.getTakerDepositTx() == null) {
|
||||
return Res.get("portfolio.pending.failedTrade.missingDepositTx"); // TODO (woodser): use .missingTakerDepositTx, .missingMakerDepositTx
|
||||
}
|
||||
|
||||
if (trade.hasErrorMessage()) {
|
||||
|
|
|
@ -20,13 +20,11 @@ package bisq.desktop.main.portfolio.pendingtrades;
|
|||
import bisq.desktop.Navigation;
|
||||
import bisq.desktop.common.model.ActivatableWithDataModel;
|
||||
import bisq.desktop.common.model.ViewModel;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.util.DisplayUtils;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.btc.wallet.Restrictions;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.network.MessageState;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.offer.OfferUtil;
|
||||
|
@ -45,7 +43,6 @@ import bisq.core.util.validation.BtcAddressValidator;
|
|||
import bisq.network.p2p.P2PService;
|
||||
|
||||
import bisq.common.ClockWatcher;
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.app.DevEnv;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
@ -64,7 +61,6 @@ import javafx.beans.property.SimpleIntegerProperty;
|
|||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.Getter;
|
||||
|
@ -209,27 +205,29 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
}
|
||||
|
||||
public void checkTakerFeeTx(Trade trade) {
|
||||
mempoolStatus.setValue(-1);
|
||||
mempoolService.validateOfferTakerTx(trade, (txValidator -> {
|
||||
mempoolStatus.setValue(txValidator.isFail() ? 0 : 1);
|
||||
if (txValidator.isFail()) {
|
||||
String errorMessage = "Validation of Taker Tx returned: " + txValidator.toString();
|
||||
log.warn(errorMessage);
|
||||
// prompt user to open mediation
|
||||
if (trade.getDisputeState() == Trade.DisputeState.NO_DISPUTE) {
|
||||
UserThread.runAfter(() -> {
|
||||
Popup popup = new Popup();
|
||||
popup.headLine(Res.get("portfolio.pending.openSupportTicket.headline"))
|
||||
.message(Res.get("portfolio.pending.invalidTx", errorMessage))
|
||||
.actionButtonText(Res.get("portfolio.pending.openSupportTicket.headline"))
|
||||
.onAction(dataModel::onOpenSupportTicket)
|
||||
.closeButtonText(Res.get("shared.cancel"))
|
||||
.onClose(popup::hide)
|
||||
.show();
|
||||
}, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}));
|
||||
log.warn("PendingTradesViewModel.checkTakerFeeTx() needs adapted to XMR");
|
||||
return; // TODO (woodser): PendingTradesViewModel.checkTakerFeeTx() needs adapted to XMR, use common TradeDataValidation utility
|
||||
// mempoolStatus.setValue(-1);
|
||||
// mempoolService.validateOfferTakerTx(trade, (txValidator -> {
|
||||
// mempoolStatus.setValue(txValidator.isFail() ? 0 : 1);
|
||||
// if (txValidator.isFail()) {
|
||||
// String errorMessage = "Validation of Taker Tx returned: " + txValidator.toString();
|
||||
// log.warn(errorMessage);
|
||||
// // prompt user to open mediation
|
||||
// if (trade.getDisputeState() == Trade.DisputeState.NO_DISPUTE) {
|
||||
// UserThread.runAfter(() -> {
|
||||
// Popup popup = new Popup();
|
||||
// popup.headLine(Res.get("portfolio.pending.openSupportTicket.headline"))
|
||||
// .message(Res.get("portfolio.pending.invalidTx", errorMessage))
|
||||
// .actionButtonText(Res.get("portfolio.pending.openSupportTicket.headline"))
|
||||
// .onAction(dataModel::onOpenSupportTicket)
|
||||
// .closeButtonText(Res.get("shared.cancel"))
|
||||
// .onClose(popup::hide)
|
||||
// .show();
|
||||
// }, 100, TimeUnit.MILLISECONDS);
|
||||
// }
|
||||
// }
|
||||
// }));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -329,7 +327,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
if (trade != null && dataModel.getOffer() != null && trade.getTradeAmount() != null) {
|
||||
checkNotNull(dataModel.getTrade());
|
||||
if (dataModel.isMaker() && dataModel.getOffer().isCurrencyForMakerFeeBtc() ||
|
||||
!dataModel.isMaker() && dataModel.getTrade().isCurrencyForTakerFeeBtc()) {
|
||||
!dataModel.isMaker()) {
|
||||
Coin tradeFeeInBTC = dataModel.getTradeFeeInBTC();
|
||||
|
||||
Coin minTradeFee = dataModel.isMaker() ?
|
||||
|
@ -432,20 +430,21 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
|
||||
|
||||
// #################### Phase DEPOSIT_PAID
|
||||
case SELLER_PUBLISHED_DEPOSIT_TX:
|
||||
case TAKER_PUBLISHED_DEPOSIT_TX:
|
||||
case TAKER_SAW_DEPOSIT_TX_IN_NETWORK:
|
||||
|
||||
// DEPOSIT_TX_PUBLISHED_MSG
|
||||
// seller perspective
|
||||
case SELLER_SENT_DEPOSIT_TX_PUBLISHED_MSG:
|
||||
case SELLER_SAW_ARRIVED_DEPOSIT_TX_PUBLISHED_MSG:
|
||||
case SELLER_STORED_IN_MAILBOX_DEPOSIT_TX_PUBLISHED_MSG:
|
||||
case SELLER_SEND_FAILED_DEPOSIT_TX_PUBLISHED_MSG:
|
||||
// taker perspective
|
||||
case TAKER_SENT_DEPOSIT_TX_PUBLISHED_MSG:
|
||||
case TAKER_SAW_ARRIVED_DEPOSIT_TX_PUBLISHED_MSG:
|
||||
case TAKER_STORED_IN_MAILBOX_DEPOSIT_TX_PUBLISHED_MSG:
|
||||
case TAKER_SEND_FAILED_DEPOSIT_TX_PUBLISHED_MSG:
|
||||
|
||||
// buyer perspective
|
||||
case BUYER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG:
|
||||
// maker perspective
|
||||
case MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG:
|
||||
|
||||
// Alternatively the maker could have seen the deposit tx earlier before he received the DEPOSIT_TX_PUBLISHED_MSG
|
||||
case BUYER_SAW_DEPOSIT_TX_IN_NETWORK:
|
||||
case MAKER_SAW_DEPOSIT_TX_IN_NETWORK:
|
||||
buyerState.set(BuyerState.STEP1);
|
||||
sellerState.set(SellerState.STEP1);
|
||||
break;
|
||||
|
|
|
@ -32,10 +32,11 @@ import bisq.core.support.dispute.Dispute;
|
|||
import bisq.core.support.dispute.DisputeResult;
|
||||
import bisq.core.support.dispute.mediation.MediationResultState;
|
||||
import bisq.core.trade.Contract;
|
||||
import bisq.core.trade.MakerTrade;
|
||||
import bisq.core.trade.TakerTrade;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.user.DontShowAgainLookup;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
|
||||
import bisq.network.p2p.BootstrapListener;
|
||||
|
||||
|
@ -43,9 +44,6 @@ import bisq.common.ClockWatcher;
|
|||
import bisq.common.UserThread;
|
||||
import bisq.common.util.Tuple3;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.listeners.NewBestBlockListener;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
|
||||
|
@ -97,16 +95,17 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
protected int gridRow = 0;
|
||||
private TextField timeLeftTextField;
|
||||
private ProgressBar timeLeftProgressBar;
|
||||
private TxIdTextField txIdTextField;
|
||||
private TxIdTextField makerTxIdTextField;
|
||||
private TxIdTextField takerTxIdTextField;
|
||||
private TradeStepInfo tradeStepInfo;
|
||||
private Subscription txIdSubscription;
|
||||
private Subscription makerTxIdSubscription;
|
||||
private Subscription takerTxIdSubscription;
|
||||
private ClockWatcher.Listener clockListener;
|
||||
private final ChangeListener<String> errorMessageListener;
|
||||
protected Label infoLabel;
|
||||
private Popup acceptMediationResultPopup;
|
||||
private BootstrapListener bootstrapListener;
|
||||
private TradeSubView.ChatCallback chatCallback;
|
||||
private final NewBestBlockListener newBestBlockListener;
|
||||
private ChangeListener<Boolean> pendingTradesInitializedListener;
|
||||
|
||||
|
||||
|
@ -170,21 +169,32 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
}
|
||||
};
|
||||
|
||||
newBestBlockListener = block -> {
|
||||
checkIfLockTimeIsOver();
|
||||
};
|
||||
// newBestBlockListener = block -> {
|
||||
// checkIfLockTimeIsOver();
|
||||
// };
|
||||
}
|
||||
|
||||
public void activate() {
|
||||
if (txIdTextField != null) {
|
||||
if (txIdSubscription != null)
|
||||
txIdSubscription.unsubscribe();
|
||||
if (makerTxIdTextField != null) {
|
||||
if (makerTxIdSubscription != null)
|
||||
makerTxIdSubscription.unsubscribe();
|
||||
|
||||
txIdSubscription = EasyBind.subscribe(model.dataModel.txId, id -> {
|
||||
makerTxIdSubscription = EasyBind.subscribe(model.dataModel.makerTxId, id -> {
|
||||
if (!id.isEmpty())
|
||||
txIdTextField.setup(id);
|
||||
makerTxIdTextField.setup(id);
|
||||
else
|
||||
txIdTextField.cleanup();
|
||||
makerTxIdTextField.cleanup();
|
||||
});
|
||||
}
|
||||
if (takerTxIdTextField != null) {
|
||||
if (takerTxIdSubscription != null)
|
||||
takerTxIdSubscription.unsubscribe();
|
||||
|
||||
takerTxIdSubscription = EasyBind.subscribe(model.dataModel.takerTxId, id -> {
|
||||
if (!id.isEmpty())
|
||||
takerTxIdTextField.setup(id);
|
||||
else
|
||||
takerTxIdTextField.cleanup();
|
||||
});
|
||||
}
|
||||
trade.errorMessageProperty().addListener(errorMessageListener);
|
||||
|
@ -241,8 +251,8 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
}
|
||||
|
||||
protected void onPendingTradesInitialized() {
|
||||
model.dataModel.btcWalletService.addNewBestBlockListener(newBestBlockListener);
|
||||
checkIfLockTimeIsOver();
|
||||
// model.dataModel.xmrWalletService.addNewBestBlockListener(newBestBlockListener); // TODO (woodser): different listener?
|
||||
// checkIfLockTimeIsOver();
|
||||
}
|
||||
|
||||
private void registerSubscriptions() {
|
||||
|
@ -274,38 +284,38 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
}
|
||||
|
||||
public void deactivate() {
|
||||
if (txIdSubscription != null)
|
||||
txIdSubscription.unsubscribe();
|
||||
if (makerTxIdSubscription != null)
|
||||
makerTxIdSubscription.unsubscribe();
|
||||
if (takerTxIdSubscription != null)
|
||||
takerTxIdSubscription.unsubscribe();
|
||||
|
||||
if (txIdTextField != null)
|
||||
txIdTextField.cleanup();
|
||||
if (makerTxIdTextField != null)
|
||||
makerTxIdTextField.cleanup();
|
||||
if (takerTxIdTextField != null)
|
||||
takerTxIdTextField.cleanup();
|
||||
|
||||
if (errorMessageListener != null)
|
||||
trade.errorMessageProperty().removeListener(errorMessageListener);
|
||||
if (errorMessageListener != null)
|
||||
trade.errorMessageProperty().removeListener(errorMessageListener);
|
||||
|
||||
if (disputeStateSubscription != null)
|
||||
disputeStateSubscription.unsubscribe();
|
||||
if (disputeStateSubscription != null)
|
||||
disputeStateSubscription.unsubscribe();
|
||||
|
||||
if (mediationResultStateSubscription != null)
|
||||
mediationResultStateSubscription.unsubscribe();
|
||||
if (mediationResultStateSubscription != null)
|
||||
mediationResultStateSubscription.unsubscribe();
|
||||
|
||||
if (tradePeriodStateSubscription != null)
|
||||
tradePeriodStateSubscription.unsubscribe();
|
||||
if (tradePeriodStateSubscription != null)
|
||||
tradePeriodStateSubscription.unsubscribe();
|
||||
|
||||
if (clockListener != null)
|
||||
model.clockWatcher.removeListener(clockListener);
|
||||
if (clockListener != null)
|
||||
model.clockWatcher.removeListener(clockListener);
|
||||
|
||||
if (tradeStepInfo != null)
|
||||
tradeStepInfo.setOnAction(null);
|
||||
if (tradeStepInfo != null)
|
||||
tradeStepInfo.setOnAction(null);
|
||||
|
||||
if (newBestBlockListener != null) {
|
||||
model.dataModel.btcWalletService.removeNewBestBlockListener(newBestBlockListener);
|
||||
}
|
||||
|
||||
if (acceptMediationResultPopup != null) {
|
||||
acceptMediationResultPopup.hide();
|
||||
acceptMediationResultPopup = null;
|
||||
}
|
||||
if (acceptMediationResultPopup != null) {
|
||||
acceptMediationResultPopup.hide();
|
||||
acceptMediationResultPopup = null;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -322,19 +332,35 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
Res.get("portfolio.pending.tradeInformation"));
|
||||
GridPane.setColumnSpan(tradeInfoTitledGroupBg, 2);
|
||||
|
||||
final Tuple3<Label, TxIdTextField, VBox> labelTxIdTextFieldVBoxTuple3 =
|
||||
// maker
|
||||
final Tuple3<Label, TxIdTextField, VBox> labelMakerTxIdTextFieldVBoxTuple3 =
|
||||
addTopLabelTxIdTextField(gridPane, gridRow,
|
||||
Res.get("shared.depositTransactionId"), // TODO (woodser): need separate labels for maker and taker deposit tx ids
|
||||
Layout.COMPACT_FIRST_ROW_DISTANCE);
|
||||
|
||||
GridPane.setColumnSpan(labelMakerTxIdTextFieldVBoxTuple3.third, 2);
|
||||
makerTxIdTextField = labelMakerTxIdTextFieldVBoxTuple3.second;
|
||||
|
||||
String makerId = model.dataModel.makerTxId.get();
|
||||
if (!makerId.isEmpty())
|
||||
makerTxIdTextField.setup(makerId);
|
||||
else
|
||||
makerTxIdTextField.cleanup();
|
||||
|
||||
// taker
|
||||
final Tuple3<Label, TxIdTextField, VBox> labelTakerTxIdTextFieldVBoxTuple3 =
|
||||
addTopLabelTxIdTextField(gridPane, gridRow,
|
||||
Res.get("shared.depositTransactionId"),
|
||||
Layout.COMPACT_FIRST_ROW_DISTANCE);
|
||||
|
||||
GridPane.setColumnSpan(labelTxIdTextFieldVBoxTuple3.third, 2);
|
||||
txIdTextField = labelTxIdTextFieldVBoxTuple3.second;
|
||||
GridPane.setColumnSpan(labelTakerTxIdTextFieldVBoxTuple3.third, 2);
|
||||
takerTxIdTextField = labelTakerTxIdTextFieldVBoxTuple3.second;
|
||||
|
||||
String id = model.dataModel.txId.get();
|
||||
if (!id.isEmpty())
|
||||
txIdTextField.setup(id);
|
||||
String takerId = model.dataModel.takerTxId.get();
|
||||
if (!takerId.isEmpty())
|
||||
takerTxIdTextField.setup(takerId);
|
||||
else
|
||||
txIdTextField.cleanup();
|
||||
takerTxIdTextField.cleanup();
|
||||
|
||||
if (model.dataModel.getTrade() != null) {
|
||||
checkNotNull(model.dataModel.getTrade().getOffer(), "Offer must not be null in TradeStepView");
|
||||
|
@ -439,6 +465,33 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
switch (disputeState) {
|
||||
case NO_DISPUTE:
|
||||
break;
|
||||
case DISPUTE_REQUESTED:
|
||||
if (tradeStepInfo != null) {
|
||||
tradeStepInfo.setFirstHalfOverWarnTextSupplier(this::getFirstHalfOverWarnText);
|
||||
}
|
||||
applyOnDisputeOpened();
|
||||
|
||||
ownDispute = model.dataModel.arbitrationManager.findOwnDispute(trade.getId());
|
||||
ownDispute.ifPresent(dispute -> {
|
||||
if (tradeStepInfo != null)
|
||||
tradeStepInfo.setState(TradeStepInfo.State.IN_ARBITRATION_SELF_REQUESTED);
|
||||
});
|
||||
|
||||
break;
|
||||
case DISPUTE_STARTED_BY_PEER:
|
||||
if (tradeStepInfo != null) {
|
||||
tradeStepInfo.setFirstHalfOverWarnTextSupplier(this::getFirstHalfOverWarnText);
|
||||
}
|
||||
applyOnDisputeOpened();
|
||||
|
||||
ownDispute = model.dataModel.arbitrationManager.findOwnDispute(trade.getId());
|
||||
ownDispute.ifPresent(dispute -> {
|
||||
if (tradeStepInfo != null)
|
||||
tradeStepInfo.setState(TradeStepInfo.State.IN_ARBITRATION_PEER_REQUESTED);
|
||||
});
|
||||
break;
|
||||
case DISPUTE_CLOSED:
|
||||
break;
|
||||
case MEDIATION_REQUESTED:
|
||||
if (tradeStepInfo != null) {
|
||||
tradeStepInfo.setFirstHalfOverWarnTextSupplier(this::getFirstHalfOverWarnText);
|
||||
|
@ -465,53 +518,55 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
});
|
||||
break;
|
||||
case MEDIATION_CLOSED:
|
||||
if (tradeStepInfo != null) {
|
||||
tradeStepInfo.setOnAction(e -> {
|
||||
updateMediationResultState(false);
|
||||
});
|
||||
}
|
||||
if (tradeStepInfo != null) {
|
||||
tradeStepInfo.setOnAction(e -> {
|
||||
updateMediationResultState(false);
|
||||
});
|
||||
}
|
||||
|
||||
if (tradeStepInfo != null) {
|
||||
tradeStepInfo.setState(TradeStepInfo.State.MEDIATION_RESULT);
|
||||
}
|
||||
if (tradeStepInfo != null) {
|
||||
tradeStepInfo.setState(TradeStepInfo.State.MEDIATION_RESULT);
|
||||
}
|
||||
|
||||
updateMediationResultState(true);
|
||||
break;
|
||||
updateMediationResultState(true);
|
||||
break;
|
||||
case REFUND_REQUESTED:
|
||||
if (tradeStepInfo != null) {
|
||||
tradeStepInfo.setFirstHalfOverWarnTextSupplier(this::getFirstHalfOverWarnText);
|
||||
}
|
||||
applyOnDisputeOpened();
|
||||
|
||||
ownDispute = model.dataModel.refundManager.findOwnDispute(trade.getId());
|
||||
ownDispute.ifPresent(dispute -> {
|
||||
if (tradeStepInfo != null)
|
||||
tradeStepInfo.setState(TradeStepInfo.State.IN_REFUND_REQUEST_SELF_REQUESTED);
|
||||
});
|
||||
|
||||
if (acceptMediationResultPopup != null) {
|
||||
acceptMediationResultPopup.hide();
|
||||
acceptMediationResultPopup = null;
|
||||
}
|
||||
|
||||
break;
|
||||
throw new RuntimeException("Unhandled case: " + Trade.DisputeState.REFUND_REQUESTED);
|
||||
// if (tradeStepInfo != null) {
|
||||
// tradeStepInfo.setFirstHalfOverWarnTextSupplier(this::getFirstHalfOverWarnText);
|
||||
// }
|
||||
// applyOnDisputeOpened();
|
||||
//
|
||||
// ownDispute = model.dataModel.refundManager.findOwnDispute(trade.getId());
|
||||
// ownDispute.ifPresent(dispute -> {
|
||||
// if (tradeStepInfo != null)
|
||||
// tradeStepInfo.setState(TradeStepInfo.State.IN_REFUND_REQUEST_SELF_REQUESTED);
|
||||
// });
|
||||
//
|
||||
// if (acceptMediationResultPopup != null) {
|
||||
// acceptMediationResultPopup.hide();
|
||||
// acceptMediationResultPopup = null;
|
||||
// }
|
||||
//
|
||||
// break;
|
||||
case REFUND_REQUEST_STARTED_BY_PEER:
|
||||
if (tradeStepInfo != null) {
|
||||
tradeStepInfo.setFirstHalfOverWarnTextSupplier(this::getFirstHalfOverWarnText);
|
||||
}
|
||||
applyOnDisputeOpened();
|
||||
|
||||
ownDispute = model.dataModel.refundManager.findOwnDispute(trade.getId());
|
||||
ownDispute.ifPresent(dispute -> {
|
||||
if (tradeStepInfo != null)
|
||||
tradeStepInfo.setState(TradeStepInfo.State.IN_REFUND_REQUEST_PEER_REQUESTED);
|
||||
});
|
||||
|
||||
if (acceptMediationResultPopup != null) {
|
||||
acceptMediationResultPopup.hide();
|
||||
acceptMediationResultPopup = null;
|
||||
}
|
||||
break;
|
||||
throw new RuntimeException("Unhandled case: " + Trade.DisputeState.REFUND_REQUEST_STARTED_BY_PEER);
|
||||
// if (tradeStepInfo != null) {
|
||||
// tradeStepInfo.setFirstHalfOverWarnTextSupplier(this::getFirstHalfOverWarnText);
|
||||
// }
|
||||
// applyOnDisputeOpened();
|
||||
//
|
||||
// ownDispute = model.dataModel.refundManager.findOwnDispute(trade.getId());
|
||||
// ownDispute.ifPresent(dispute -> {
|
||||
// if (tradeStepInfo != null)
|
||||
// tradeStepInfo.setState(TradeStepInfo.State.IN_REFUND_REQUEST_PEER_REQUESTED);
|
||||
// });
|
||||
//
|
||||
// if (acceptMediationResultPopup != null) {
|
||||
// acceptMediationResultPopup.hide();
|
||||
// acceptMediationResultPopup = null;
|
||||
// }
|
||||
// break;
|
||||
case REFUND_REQUEST_CLOSED:
|
||||
break;
|
||||
default:
|
||||
|
@ -587,16 +642,20 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
return;
|
||||
}
|
||||
|
||||
if (trade.getDepositTx() == null) {
|
||||
log.error("trade.getDepositTx() was null at openMediationResultPopup. " +
|
||||
"We add the trade to failed trades. TradeId={}", trade.getId());
|
||||
new Popup().warning(Res.get("portfolio.pending.mediationResult.error.depositTxNull")).show();
|
||||
return;
|
||||
} else if (trade.getDelayedPayoutTx() == null) {
|
||||
log.error("trade.getDelayedPayoutTx() was null at openMediationResultPopup. " +
|
||||
"We add the trade to failed trades. TradeId={}", trade.getId());
|
||||
new Popup().warning(Res.get("portfolio.pending.mediationResult.error.delayedPayoutTxNull")).show();
|
||||
return;
|
||||
if (trade instanceof MakerTrade && trade.getMakerDepositTx() == null) {
|
||||
log.error("trade.getMakerDepositTx() was null at openMediationResultPopup. " +
|
||||
"We add the trade to failed trades. TradeId={}", trade.getId());
|
||||
//model.dataModel.addTradeToFailedTrades(); // TODO (woodser): new way to move trade to failed trades?
|
||||
model.dataModel.onMoveInvalidTradeToFailedTrades(trade);;
|
||||
new Popup().warning(Res.get("portfolio.pending.mediationResult.error.depositTxNull")).show(); // TODO (woodser): separate error messages for maker/taker
|
||||
return;
|
||||
} else if (trade instanceof TakerTrade && trade.getTakerDepositTx() == null) {
|
||||
log.error("trade.getTakerDepositTx() was null at openMediationResultPopup. " +
|
||||
"We add the trade to failed trades. TradeId={}", trade.getId());
|
||||
//model.dataModel.addTradeToFailedTrades();
|
||||
model.dataModel.onMoveInvalidTradeToFailedTrades(trade);;
|
||||
new Popup().warning(Res.get("portfolio.pending.mediationResult.error.depositTxNull")).show();
|
||||
return;
|
||||
}
|
||||
|
||||
DisputeResult disputeResult = optionalDispute.get().getDisputeResultProperty().get();
|
||||
|
@ -607,10 +666,6 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
String myPayoutAmount = isMyRoleBuyer ? buyerPayoutAmount : sellerPayoutAmount;
|
||||
String peersPayoutAmount = isMyRoleBuyer ? sellerPayoutAmount : buyerPayoutAmount;
|
||||
|
||||
long lockTime = trade.getDelayedPayoutTx().getLockTime();
|
||||
int bestChainHeight = model.dataModel.btcWalletService.getBestChainHeight();
|
||||
long remaining = lockTime - bestChainHeight;
|
||||
|
||||
String actionButtonText = hasSelfAccepted() ?
|
||||
Res.get("portfolio.pending.mediationResult.popup.alreadyAccepted") : Res.get("shared.accept");
|
||||
|
||||
|
@ -627,15 +682,15 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
case SIG_MSG_IN_MAILBOX:
|
||||
case SIG_MSG_SEND_FAILED:
|
||||
message = Res.get("portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver",
|
||||
FormattingUtils.getDateFromBlockHeight(remaining),
|
||||
lockTime);
|
||||
"N/A", // TODO (woodser): no timelocked tx in xmr, so part of popup message is n/a
|
||||
-1);
|
||||
break;
|
||||
default:
|
||||
message = Res.get("portfolio.pending.mediationResult.popup.info",
|
||||
myPayoutAmount,
|
||||
peersPayoutAmount,
|
||||
FormattingUtils.getDateFromBlockHeight(remaining),
|
||||
lockTime);
|
||||
"N/A", // TODO (woodser): no timelocked tx in xmr, so part of popup message is n/a
|
||||
-1);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -720,19 +775,19 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkIfLockTimeIsOver() {
|
||||
if (trade.getDisputeState() == Trade.DisputeState.MEDIATION_CLOSED) {
|
||||
Transaction delayedPayoutTx = trade.getDelayedPayoutTx();
|
||||
if (delayedPayoutTx != null) {
|
||||
long lockTime = delayedPayoutTx.getLockTime();
|
||||
int bestChainHeight = model.dataModel.btcWalletService.getBestChainHeight();
|
||||
long remaining = lockTime - bestChainHeight;
|
||||
if (remaining <= 0) {
|
||||
openMediationResultPopup(Res.get("portfolio.pending.mediationResult.popup.headline", trade.getShortId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// private void checkIfLockTimeIsOver() {
|
||||
// if (trade.getDisputeState() == Trade.DisputeState.MEDIATION_CLOSED) {
|
||||
// Transaction delayedPayoutTx = trade.getDelayedPayoutTx();
|
||||
// if (delayedPayoutTx != null) {
|
||||
// long lockTime = delayedPayoutTx.getLockTime();
|
||||
// int bestChainHeight = model.dataModel.btcWalletService.getBestChainHeight();
|
||||
// long remaining = lockTime - bestChainHeight;
|
||||
// if (remaining <= 0) {
|
||||
// openMediationResultPopup(Res.get("portfolio.pending.mediationResult.popup.headline", trade.getShortId()));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
protected void checkForTimeout() {
|
||||
long unconfirmedHours = Duration.between(trade.getTakeOfferDate().toInstant(), Instant.now()).toHours();
|
||||
|
|
|
@ -17,12 +17,10 @@
|
|||
|
||||
package bisq.desktop.main.portfolio.pendingtrades.steps.buyer;
|
||||
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.main.portfolio.pendingtrades.PendingTradesViewModel;
|
||||
import bisq.desktop.main.portfolio.pendingtrades.steps.TradeStepView;
|
||||
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.trade.TradeDataValidation;
|
||||
|
||||
public class BuyerStep1View extends TradeStepView {
|
||||
|
||||
|
@ -37,8 +35,8 @@ public class BuyerStep1View extends TradeStepView {
|
|||
@Override
|
||||
protected void onPendingTradesInitialized() {
|
||||
super.onPendingTradesInitialized();
|
||||
validatePayoutTx();
|
||||
validateDepositInputs();
|
||||
//validatePayoutTx(); // TODO (woodser): no payout tx in xmr integration, do something else?
|
||||
//validateDepositInputs();
|
||||
checkForTimeout();
|
||||
}
|
||||
|
||||
|
@ -80,32 +78,32 @@ public class BuyerStep1View extends TradeStepView {
|
|||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void validatePayoutTx() {
|
||||
try {
|
||||
TradeDataValidation.validateDelayedPayoutTx(trade,
|
||||
trade.getDelayedPayoutTx(),
|
||||
model.dataModel.daoFacade,
|
||||
model.dataModel.btcWalletService);
|
||||
} catch (TradeDataValidation.MissingTxException ignore) {
|
||||
// We don't react on those errors as a failed trade might get listed initially but getting removed from the
|
||||
// trade manager after initPendingTrades which happens after activate might be called.
|
||||
} catch (TradeDataValidation.ValidationException e) {
|
||||
if (!model.dataModel.tradeManager.isAllowFaultyDelayedTxs()) {
|
||||
new Popup().warning(Res.get("portfolio.pending.invalidTx", e.getMessage())).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that deposit tx inputs are matching the trade fee txs outputs.
|
||||
private void validateDepositInputs() {
|
||||
try {
|
||||
TradeDataValidation.validateDepositInputs(trade);
|
||||
} catch (TradeDataValidation.ValidationException e) {
|
||||
if (!model.dataModel.tradeManager.isAllowFaultyDelayedTxs()) {
|
||||
new Popup().warning(Res.get("portfolio.pending.invalidTx", e.getMessage())).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
// private void validatePayoutTx() {
|
||||
// try {
|
||||
// TradeDataValidation.validateDelayedPayoutTx(trade,
|
||||
// trade.getDelayedPayoutTx(),
|
||||
// model.dataModel.daoFacade,
|
||||
// model.dataModel.btcWalletService);
|
||||
// } catch (TradeDataValidation.MissingTxException ignore) {
|
||||
// // We don't react on those errors as a failed trade might get listed initially but getting removed from the
|
||||
// // trade manager after initPendingTrades which happens after activate might be called.
|
||||
// } catch (TradeDataValidation.ValidationException e) {
|
||||
// if (!model.dataModel.tradeManager.isAllowFaultyDelayedTxs()) {
|
||||
// new Popup().warning(Res.get("portfolio.pending.invalidTx", e.getMessage())).show();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Verify that deposit tx inputs are matching the trade fee txs outputs.
|
||||
// private void validateDepositInputs() {
|
||||
// try {
|
||||
// TradeDataValidation.validateDepositInputs(trade);
|
||||
// } catch (TradeDataValidation.ValidationException e) {
|
||||
// if (!model.dataModel.tradeManager.isAllowFaultyDelayedTxs()) {
|
||||
// new Popup().warning(Res.get("portfolio.pending.invalidTx", e.getMessage())).show();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,6 @@ import bisq.core.payment.payload.PaymentMethod;
|
|||
import bisq.core.payment.payload.USPostalMoneyOrderAccountPayload;
|
||||
import bisq.core.payment.payload.WesternUnionAccountPayload;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.TradeDataValidation;
|
||||
import bisq.core.user.DontShowAgainLookup;
|
||||
|
||||
import bisq.common.Timer;
|
||||
|
@ -202,7 +201,7 @@ public class BuyerStep2View extends TradeStepView {
|
|||
@Override
|
||||
protected void onPendingTradesInitialized() {
|
||||
super.onPendingTradesInitialized();
|
||||
validatePayoutTx();
|
||||
//validatePayoutTx(); // TODO (woodser): no payout tx in xmr integration, do something else?
|
||||
model.checkTakerFeeTx(trade);
|
||||
}
|
||||
|
||||
|
@ -608,19 +607,19 @@ public class BuyerStep2View extends TradeStepView {
|
|||
}
|
||||
}
|
||||
|
||||
private void validatePayoutTx() {
|
||||
try {
|
||||
TradeDataValidation.validateDelayedPayoutTx(trade,
|
||||
trade.getDelayedPayoutTx(),
|
||||
model.dataModel.daoFacade,
|
||||
model.dataModel.btcWalletService);
|
||||
} catch (TradeDataValidation.MissingTxException ignore) {
|
||||
// We don't react on those errors as a failed trade might get listed initially but getting removed from the
|
||||
// trade manager after initPendingTrades which happens after activate might be called.
|
||||
} catch (TradeDataValidation.ValidationException e) {
|
||||
if (!model.dataModel.tradeManager.isAllowFaultyDelayedTxs()) {
|
||||
new Popup().warning(Res.get("portfolio.pending.invalidTx", e.getMessage())).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
// private void validatePayoutTx() {
|
||||
// try {
|
||||
// TradeDataValidation.validateDelayedPayoutTx(trade,
|
||||
// trade.getDelayedPayoutTx(),
|
||||
// model.dataModel.daoFacade,
|
||||
// model.dataModel.btcWalletService);
|
||||
// } catch (TradeDataValidation.MissingTxException ignore) {
|
||||
// // We don't react on those errors as a failed trade might get listed initially but getting removed from the
|
||||
// // trade manager after initPendingTrades which happens after activate might be called.
|
||||
// } catch (TradeDataValidation.ValidationException e) {
|
||||
// if (!model.dataModel.tradeManager.isAllowFaultyDelayedTxs()) {
|
||||
// new Popup().warning(Res.get("portfolio.pending.invalidTx", e.getMessage())).show();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -31,26 +31,17 @@ import bisq.desktop.main.portfolio.pendingtrades.PendingTradesViewModel;
|
|||
import bisq.desktop.main.portfolio.pendingtrades.steps.TradeStepView;
|
||||
import bisq.desktop.util.Layout;
|
||||
|
||||
import bisq.core.btc.exceptions.AddressEntryException;
|
||||
import bisq.core.btc.exceptions.InsufficientFundsException;
|
||||
import bisq.core.btc.model.AddressEntry;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.Restrictions;
|
||||
import bisq.core.btc.model.XmrAddressEntry;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.trade.txproof.AssetTxProofResult;
|
||||
import bisq.core.user.DontShowAgainLookup;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
import bisq.core.util.coin.CoinUtil;
|
||||
import bisq.core.util.validation.BtcAddressValidator;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.app.DevEnv;
|
||||
import bisq.common.handlers.FaultHandler;
|
||||
import bisq.common.handlers.ResultHandler;
|
||||
|
||||
import org.bitcoinj.core.AddressFormatException;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import com.jfoenix.controls.JFXBadge;
|
||||
|
||||
|
@ -144,7 +135,7 @@ public class BuyerStep4View extends TradeStepView {
|
|||
|
||||
HBox hBox = new HBox();
|
||||
hBox.setSpacing(10);
|
||||
useSavingsWalletButton = new AutoTooltipButton(Res.get("portfolio.pending.step5_buyer.moveToBisqWallet"));
|
||||
useSavingsWalletButton = new AutoTooltipButton(Res.get("portfolio.pending.step5_buyer.moveToHavenoWallet"));
|
||||
useSavingsWalletButton.setDefaultButton(true);
|
||||
useSavingsWalletButton.getStyleClass().add("action-button");
|
||||
Label label = new AutoTooltipLabel(Res.get("shared.OR"));
|
||||
|
@ -194,63 +185,64 @@ public class BuyerStep4View extends TradeStepView {
|
|||
}
|
||||
|
||||
private void reviewWithdrawal() {
|
||||
Coin amount = trade.getPayoutAmount();
|
||||
BtcWalletService walletService = model.dataModel.btcWalletService;
|
||||
|
||||
AddressEntry fromAddressesEntry = walletService.getOrCreateAddressEntry(trade.getId(), AddressEntry.Context.TRADE_PAYOUT);
|
||||
String fromAddresses = fromAddressesEntry.getAddressString();
|
||||
String toAddresses = withdrawAddressTextField.getText();
|
||||
if (new BtcAddressValidator().validate(toAddresses).isValid) {
|
||||
Coin balance = walletService.getBalanceForAddress(fromAddressesEntry.getAddress());
|
||||
try {
|
||||
Transaction feeEstimationTransaction = walletService.getFeeEstimationTransaction(fromAddresses, toAddresses, amount, AddressEntry.Context.TRADE_PAYOUT);
|
||||
Coin fee = feeEstimationTransaction.getFee();
|
||||
Coin receiverAmount = amount.subtract(fee);
|
||||
if (balance.isZero()) {
|
||||
new Popup().warning(Res.get("portfolio.pending.step5_buyer.alreadyWithdrawn")).show();
|
||||
model.dataModel.tradeManager.onTradeCompleted(trade);
|
||||
} else {
|
||||
if (toAddresses.isEmpty()) {
|
||||
validateWithdrawAddress();
|
||||
} else if (Restrictions.isAboveDust(receiverAmount)) {
|
||||
CoinFormatter formatter = model.btcFormatter;
|
||||
int txVsize = feeEstimationTransaction.getVsize();
|
||||
double feePerVbyte = CoinUtil.getFeePerVbyte(fee, txVsize);
|
||||
double vkb = txVsize / 1000d;
|
||||
String recAmount = formatter.formatCoinWithCode(receiverAmount);
|
||||
new Popup().headLine(Res.get("portfolio.pending.step5_buyer.confirmWithdrawal"))
|
||||
.confirmation(Res.get("shared.sendFundsDetailsWithFee",
|
||||
formatter.formatCoinWithCode(amount),
|
||||
fromAddresses,
|
||||
toAddresses,
|
||||
formatter.formatCoinWithCode(fee),
|
||||
feePerVbyte,
|
||||
vkb,
|
||||
recAmount))
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.onAction(() -> doWithdrawal(amount, fee))
|
||||
.closeButtonText(Res.get("shared.cancel"))
|
||||
.onClose(() -> {
|
||||
useSavingsWalletButton.setDisable(false);
|
||||
withdrawToExternalWalletButton.setDisable(false);
|
||||
})
|
||||
.show();
|
||||
} else {
|
||||
new Popup().warning(Res.get("portfolio.pending.step5_buyer.amountTooLow")).show();
|
||||
}
|
||||
}
|
||||
} catch (AddressFormatException e) {
|
||||
validateWithdrawAddress();
|
||||
} catch (AddressEntryException e) {
|
||||
log.error(e.getMessage());
|
||||
} catch (InsufficientFundsException e) {
|
||||
log.error(e.getMessage());
|
||||
e.printStackTrace();
|
||||
new Popup().warning(e.getMessage()).show();
|
||||
}
|
||||
} else {
|
||||
new Popup().warning(Res.get("validation.btc.invalidAddress")).show();
|
||||
}
|
||||
throw new RuntimeException("BuyerStep4View.reviewWithdrawal() not yet updated for XMR");
|
||||
// Coin amount = trade.getPayoutAmount();
|
||||
// BtcWalletService walletService = model.dataModel.btcWalletService;
|
||||
//
|
||||
// AddressEntry fromAddressesEntry = walletService.getOrCreateAddressEntry(trade.getId(), AddressEntry.Context.TRADE_PAYOUT);
|
||||
// String fromAddresses = fromAddressesEntry.getAddressString();
|
||||
// String toAddresses = withdrawAddressTextField.getText();
|
||||
// if (new BtcAddressValidator().validate(toAddresses).isValid) {
|
||||
// Coin balance = walletService.getBalanceForAddress(fromAddressesEntry.getAddress());
|
||||
// try {
|
||||
// Transaction feeEstimationTransaction = walletService.getFeeEstimationTransaction(fromAddresses, toAddresses, amount, AddressEntry.Context.TRADE_PAYOUT);
|
||||
// Coin fee = feeEstimationTransaction.getFee();
|
||||
// Coin receiverAmount = amount.subtract(fee);
|
||||
// if (balance.isZero()) {
|
||||
// new Popup().warning(Res.get("portfolio.pending.step5_buyer.alreadyWithdrawn")).show();
|
||||
// model.dataModel.tradeManager.onTradeCompleted(trade);
|
||||
// } else {
|
||||
// if (toAddresses.isEmpty()) {
|
||||
// validateWithdrawAddress();
|
||||
// } else if (Restrictions.isAboveDust(receiverAmount)) {
|
||||
// CoinFormatter formatter = model.btcFormatter;
|
||||
// int txVsize = feeEstimationTransaction.getVsize();
|
||||
// double feePerVbyte = CoinUtil.getFeePerVbyte(fee, txVsize);
|
||||
// double vkb = txVsize / 1000d;
|
||||
// String recAmount = formatter.formatCoinWithCode(receiverAmount);
|
||||
// new Popup().headLine(Res.get("portfolio.pending.step5_buyer.confirmWithdrawal"))
|
||||
// .confirmation(Res.get("shared.sendFundsDetailsWithFee",
|
||||
// formatter.formatCoinWithCode(amount),
|
||||
// fromAddresses,
|
||||
// toAddresses,
|
||||
// formatter.formatCoinWithCode(fee),
|
||||
// feePerVbyte,
|
||||
// vkb,
|
||||
// recAmount))
|
||||
// .actionButtonText(Res.get("shared.yes"))
|
||||
// .onAction(() -> doWithdrawal(amount, fee))
|
||||
// .closeButtonText(Res.get("shared.cancel"))
|
||||
// .onClose(() -> {
|
||||
// useSavingsWalletButton.setDisable(false);
|
||||
// withdrawToExternalWalletButton.setDisable(false);
|
||||
// })
|
||||
// .show();
|
||||
// } else {
|
||||
// new Popup().warning(Res.get("portfolio.pending.step5_buyer.amountTooLow")).show();
|
||||
// }
|
||||
// }
|
||||
// } catch (AddressFormatException e) {
|
||||
// validateWithdrawAddress();
|
||||
// } catch (AddressEntryException e) {
|
||||
// log.error(e.getMessage());
|
||||
// } catch (InsufficientFundsException e) {
|
||||
// log.error(e.getMessage());
|
||||
// e.printStackTrace();
|
||||
// new Popup().warning(e.getMessage()).show();
|
||||
// }
|
||||
// } else {
|
||||
// new Popup().warning(Res.get("validation.btc.invalidAddress")).show();
|
||||
// }
|
||||
}
|
||||
|
||||
private void doWithdrawal(Coin amount, Coin fee) {
|
||||
|
@ -264,12 +256,13 @@ public class BuyerStep4View extends TradeStepView {
|
|||
else
|
||||
new Popup().error(errorMessage).show();
|
||||
};
|
||||
if (model.dataModel.btcWalletService.isEncrypted()) {
|
||||
UserThread.runAfter(() -> model.dataModel.walletPasswordWindow.onAesKey(aesKey ->
|
||||
doWithdrawRequest(toAddress, amount, fee, aesKey, resultHandler, faultHandler))
|
||||
.show(), 300, TimeUnit.MILLISECONDS);
|
||||
} else
|
||||
doWithdrawRequest(toAddress, amount, fee, null, resultHandler, faultHandler);
|
||||
if (true) throw new RuntimeException("BuyerStep4View.doWithdrawal() not yet updated for XMR");
|
||||
// if (model.dataModel.btcWalletService.isEncrypted()) {
|
||||
// UserThread.runAfter(() -> model.dataModel.walletPasswordWindow.onAesKey(aesKey ->
|
||||
// doWithdrawRequest(toAddress, amount, fee, aesKey, resultHandler, faultHandler))
|
||||
// .show(), 300, TimeUnit.MILLISECONDS);
|
||||
// } else
|
||||
// doWithdrawRequest(toAddress, amount, fee, null, resultHandler, faultHandler);
|
||||
}
|
||||
|
||||
private void doWithdrawRequest(String toAddress,
|
||||
|
@ -296,7 +289,7 @@ public class BuyerStep4View extends TradeStepView {
|
|||
private void handleTradeCompleted() {
|
||||
useSavingsWalletButton.setDisable(true);
|
||||
withdrawToExternalWalletButton.setDisable(true);
|
||||
model.dataModel.btcWalletService.swapTradeEntryToAvailableEntry(trade.getId(), AddressEntry.Context.TRADE_PAYOUT);
|
||||
model.dataModel.xmrWalletService.swapTradeEntryToAvailableEntry(trade.getId(), XmrAddressEntry.Context.TRADE_PAYOUT);
|
||||
|
||||
openTradeFeedbackWindow();
|
||||
}
|
||||
|
|
|
@ -17,13 +17,14 @@
|
|||
|
||||
package bisq.desktop.main.portfolio.pendingtrades.steps.seller;
|
||||
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.main.portfolio.pendingtrades.PendingTradesViewModel;
|
||||
import bisq.desktop.main.portfolio.pendingtrades.steps.TradeStepView;
|
||||
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.trade.TradeDataValidation;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class SellerStep1View extends TradeStepView {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -37,7 +38,8 @@ public class SellerStep1View extends TradeStepView {
|
|||
@Override
|
||||
protected void onPendingTradesInitialized() {
|
||||
super.onPendingTradesInitialized();
|
||||
validateDepositInputs();
|
||||
//validateDepositInputs();
|
||||
log.warn("Need to validate fee and/or deposit txs in SellerStep1View for XMR?"); // TODO (woodser): need to validate fee and/or deposit txs in SellerStep1View?
|
||||
checkForTimeout();
|
||||
}
|
||||
|
||||
|
@ -77,16 +79,16 @@ public class SellerStep1View extends TradeStepView {
|
|||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Verify that deposit tx inputs are matching the trade fee txs outputs.
|
||||
private void validateDepositInputs() {
|
||||
try {
|
||||
TradeDataValidation.validateDepositInputs(trade);
|
||||
} catch (TradeDataValidation.ValidationException e) {
|
||||
if (!model.dataModel.tradeManager.isAllowFaultyDelayedTxs()) {
|
||||
new Popup().warning(Res.get("portfolio.pending.invalidTx", e.getMessage())).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
// // Verify that deposit tx inputs are matching the trade fee txs outputs.
|
||||
// private void validateDepositInputs() {
|
||||
// try {
|
||||
// TradeDataValidation.validateDepositInputs(trade);
|
||||
// } catch (TradeDataValidation.ValidationException e) {
|
||||
// if (!model.dataModel.tradeManager.isAllowFaultyDelayedTxs()) {
|
||||
// new Popup().warning(Res.get("portfolio.pending.invalidTx", e.getMessage())).show();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ import bisq.common.crypto.Hash;
|
|||
import bisq.common.crypto.Sig;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -49,23 +48,24 @@ public class DisputeSummaryVerification {
|
|||
public static String signAndApply(DisputeManager<? extends DisputeList<Dispute>> disputeManager,
|
||||
DisputeResult disputeResult,
|
||||
String textToSign) {
|
||||
throw new RuntimeException("DisputeSummaryVerification.signAndApply() not implemented");
|
||||
|
||||
byte[] hash = Hash.getSha256Hash(textToSign);
|
||||
KeyPair signatureKeyPair = disputeManager.getSignatureKeyPair();
|
||||
String sigAsHex;
|
||||
try {
|
||||
byte[] signature = Sig.sign(signatureKeyPair.getPrivate(), hash);
|
||||
sigAsHex = Utilities.encodeToHex(signature);
|
||||
disputeResult.setArbitratorSignature(signature);
|
||||
} catch (CryptoException e) {
|
||||
sigAsHex = "Signing failed";
|
||||
}
|
||||
|
||||
return Res.get("disputeSummaryWindow.close.msgWithSig",
|
||||
textToSign,
|
||||
SEPARATOR1,
|
||||
sigAsHex,
|
||||
SEPARATOR2);
|
||||
// byte[] hash = Hash.getSha256Hash(textToSign);
|
||||
// KeyPair signatureKeyPair = disputeManager.getSignatureKeyPair();
|
||||
// String sigAsHex;
|
||||
// try {
|
||||
// byte[] signature = Sig.sign(signatureKeyPair.getPrivate(), hash);
|
||||
// sigAsHex = Utilities.encodeToHex(signature);
|
||||
// disputeResult.setArbitratorSignature(signature);
|
||||
// } catch (CryptoException e) {
|
||||
// sigAsHex = "Signing failed";
|
||||
// }
|
||||
//
|
||||
// return Res.get("disputeSummaryWindow.close.msgWithSig",
|
||||
// textToSign,
|
||||
// SEPARATOR1,
|
||||
// sigAsHex,
|
||||
// SEPARATOR2);
|
||||
}
|
||||
|
||||
public static String verifySignature(String input,
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package bisq.desktop.main.support.dispute.agent.arbitration;
|
||||
|
||||
import bisq.desktop.common.view.FxmlView;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.main.overlays.windows.ContractWindow;
|
||||
import bisq.desktop.main.overlays.windows.DisputeSummaryWindow;
|
||||
import bisq.desktop.main.overlays.windows.TradeDetailsWindow;
|
||||
|
@ -27,7 +26,6 @@ import bisq.desktop.main.support.dispute.agent.DisputeAgentView;
|
|||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.alert.PrivateNotificationManager;
|
||||
import bisq.core.dao.DaoFacade;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.support.SupportType;
|
||||
import bisq.core.support.dispute.Dispute;
|
||||
import bisq.core.support.dispute.DisputeSession;
|
||||
|
@ -96,11 +94,11 @@ public class ArbitratorView extends DisputeAgentView {
|
|||
// Only cases with protocolVersion 1 are candidates for legacy arbitration.
|
||||
// This code path is not tested and it is not assumed that it is still be used as old arbitrators would use
|
||||
// their old Bisq version if still cases are pending.
|
||||
if (protocolVersion == 1) {
|
||||
// if (protocolVersion == 1) {
|
||||
chatPopup.closeChat();
|
||||
disputeSummaryWindow.show(dispute);
|
||||
} else {
|
||||
new Popup().warning(Res.get("support.wrongVersion", protocolVersion)).show();
|
||||
}
|
||||
// } else {
|
||||
// new Popup().warning(Res.get("support.wrongVersion", protocolVersion)).show();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,7 +113,8 @@ public class MediationClientView extends DisputeClientView {
|
|||
|
||||
@Override
|
||||
protected NodeAddress getAgentNodeAddress(Contract contract) {
|
||||
return contract.getMediatorNodeAddress();
|
||||
throw new RuntimeException("MediationClientView.getAgentNodeAddress() not implementd for XMR");
|
||||
//return contract.getMediatorNodeAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -81,7 +81,8 @@ public class RefundClientView extends DisputeClientView {
|
|||
|
||||
@Override
|
||||
protected NodeAddress getAgentNodeAddress(Contract contract) {
|
||||
return contract.getRefundAgentNodeAddress();
|
||||
throw new RuntimeException("RefundClientView.getAgentNodeAddress() not implementd for XMR");
|
||||
//return contract.getRefundAgentNodeAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue