mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-22 19:49:32 +00:00
recover from error closing dispute, show payout confirmation w/ tx fee
This commit is contained in:
parent
48baa1e602
commit
2c3dabfbf7
18 changed files with 273 additions and 358 deletions
|
@ -35,6 +35,7 @@ import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import monero.wallet.model.MoneroTxWallet;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
|
@ -170,7 +171,7 @@ public class CoreDisputesService {
|
||||||
applyPayoutAmountsToDisputeResult(payout, winningDispute, disputeResult, customWinnerAmount);
|
applyPayoutAmountsToDisputeResult(payout, winningDispute, disputeResult, customWinnerAmount);
|
||||||
|
|
||||||
// close dispute ticket
|
// close dispute ticket
|
||||||
closeDisputeTicket(arbitrationManager, winningDispute, disputeResult, () -> {
|
closeDisputeTicket(arbitrationManager, winningDispute, disputeResult, null, () -> {
|
||||||
arbitrationManager.requestPersistence();
|
arbitrationManager.requestPersistence();
|
||||||
|
|
||||||
// close peer's dispute ticket
|
// close peer's dispute ticket
|
||||||
|
@ -182,12 +183,16 @@ public class CoreDisputesService {
|
||||||
var peerDisputeResult = createDisputeResult(peerDispute, winner, reason, summaryNotes, closeDate);
|
var peerDisputeResult = createDisputeResult(peerDispute, winner, reason, summaryNotes, closeDate);
|
||||||
peerDisputeResult.setBuyerPayoutAmount(disputeResult.getBuyerPayoutAmount());
|
peerDisputeResult.setBuyerPayoutAmount(disputeResult.getBuyerPayoutAmount());
|
||||||
peerDisputeResult.setSellerPayoutAmount(disputeResult.getSellerPayoutAmount());
|
peerDisputeResult.setSellerPayoutAmount(disputeResult.getSellerPayoutAmount());
|
||||||
closeDisputeTicket(arbitrationManager, peerDispute, peerDisputeResult, () -> {
|
closeDisputeTicket(arbitrationManager, peerDispute, peerDisputeResult, null, () -> {
|
||||||
arbitrationManager.requestPersistence();
|
arbitrationManager.requestPersistence();
|
||||||
|
}, (errMessage, err) -> {
|
||||||
|
throw new IllegalStateException(errMessage, err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("could not find peer dispute");
|
throw new IllegalStateException("could not find peer dispute");
|
||||||
}
|
}
|
||||||
|
}, (errMessage, err) -> {
|
||||||
|
throw new IllegalStateException(errMessage, err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -239,10 +244,7 @@ public class CoreDisputesService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// From DisputeSummaryWindow.java
|
public void closeDisputeTicket(DisputeManager disputeManager, Dispute dispute, DisputeResult disputeResult, MoneroTxWallet payoutTx, ResultHandler resultHandler, FaultHandler faultHandler) {
|
||||||
public void closeDisputeTicket(DisputeManager disputeManager, Dispute dispute, DisputeResult disputeResult, ResultHandler resultHandler) {
|
|
||||||
dispute.setDisputeResult(disputeResult);
|
|
||||||
dispute.setIsClosed();
|
|
||||||
DisputeResult.Reason reason = disputeResult.getReason();
|
DisputeResult.Reason reason = disputeResult.getReason();
|
||||||
|
|
||||||
String role = Res.get("shared.arbitrator");
|
String role = Res.get("shared.arbitrator");
|
||||||
|
@ -272,7 +274,12 @@ public class CoreDisputesService {
|
||||||
|
|
||||||
String summaryText = DisputeSummaryVerification.signAndApply(disputeManager, disputeResult, textToSign);
|
String summaryText = DisputeSummaryVerification.signAndApply(disputeManager, disputeResult, textToSign);
|
||||||
summaryText += Res.get("disputeSummaryWindow.close.nextStepsForRefundAgentArbitration");
|
summaryText += Res.get("disputeSummaryWindow.close.nextStepsForRefundAgentArbitration");
|
||||||
disputeManager.closeDisputeTicket(disputeResult, dispute, summaryText, resultHandler);
|
|
||||||
|
disputeManager.closeDisputeTicket(disputeResult, dispute, summaryText, payoutTx, () -> {
|
||||||
|
dispute.setDisputeResult(disputeResult);
|
||||||
|
dispute.setIsClosed();
|
||||||
|
resultHandler.handleResult();
|
||||||
|
}, faultHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendDisputeChatMessage(String disputeId, String message, ArrayList<Attachment> attachments) {
|
public void sendDisputeChatMessage(String disputeId, String message, ArrayList<Attachment> attachments) {
|
||||||
|
|
|
@ -689,30 +689,118 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
}
|
}
|
||||||
|
|
||||||
// arbitrator sends result to trader when their dispute is closed
|
// arbitrator sends result to trader when their dispute is closed
|
||||||
public void closeDisputeTicket(DisputeResult disputeResult, Dispute dispute, String summaryText, ResultHandler resultHandler) {
|
public void closeDisputeTicket(DisputeResult disputeResult, Dispute dispute, String summaryText, MoneroTxWallet payoutTx, ResultHandler resultHandler, FaultHandler faultHandler) {
|
||||||
T disputeList = getDisputeList();
|
try {
|
||||||
if (disputeList == null) {
|
|
||||||
log.warn("disputes is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChatMessage chatMessage = new ChatMessage(
|
// get trade
|
||||||
|
Trade trade = tradeManager.getTrade(dispute.getTradeId());
|
||||||
|
if (trade == null) throw new RuntimeException("Dispute trade " + dispute.getTradeId() + " does not exist");
|
||||||
|
|
||||||
|
// create dispute payout tx if not given
|
||||||
|
if (payoutTx == null) payoutTx = createDisputePayoutTx(trade, dispute, disputeResult); // can be null if already published or we don't have receiver's multisig hex
|
||||||
|
|
||||||
|
// persist result in dispute's chat message
|
||||||
|
ChatMessage chatMessage = new ChatMessage(
|
||||||
getSupportType(),
|
getSupportType(),
|
||||||
dispute.getTradeId(),
|
dispute.getTradeId(),
|
||||||
dispute.getTraderPubKeyRing().hashCode(),
|
dispute.getTraderPubKeyRing().hashCode(),
|
||||||
false,
|
false,
|
||||||
summaryText,
|
summaryText,
|
||||||
p2PService.getAddress());
|
p2PService.getAddress());
|
||||||
|
disputeResult.setChatMessage(chatMessage);
|
||||||
|
dispute.addAndPersistChatMessage(chatMessage);
|
||||||
|
|
||||||
disputeResult.setChatMessage(chatMessage);
|
// create dispute closed message
|
||||||
dispute.addAndPersistChatMessage(chatMessage);
|
TradingPeer receiver = trade.getTradingPeer(dispute.getTraderPubKeyRing());
|
||||||
|
String unsignedPayoutTxHex = payoutTx == null ? null : payoutTx.getTxSet().getMultisigTxHex();
|
||||||
|
TradingPeer receiverPeer = receiver == trade.getBuyer() ? trade.getSeller() : trade.getBuyer();
|
||||||
|
boolean deferPublishPayout = unsignedPayoutTxHex != null && receiverPeer.getUpdatedMultisigHex() != null && trade.getDisputeState().ordinal() >= Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG.ordinal() ;
|
||||||
|
DisputeClosedMessage disputeClosedMessage = new DisputeClosedMessage(disputeResult,
|
||||||
|
p2PService.getAddress(),
|
||||||
|
UUID.randomUUID().toString(),
|
||||||
|
getSupportType(),
|
||||||
|
trade.getSelf().getUpdatedMultisigHex(),
|
||||||
|
trade.isPayoutPublished() ? null : unsignedPayoutTxHex, // include dispute payout tx if unpublished and arbitrator has their updated multisig info
|
||||||
|
deferPublishPayout); // instruct trader to defer publishing payout tx because peer is expected to publish imminently
|
||||||
|
|
||||||
// get trade
|
// send dispute closed message
|
||||||
Trade trade = tradeManager.getTrade(dispute.getTradeId());
|
log.info("Send {} to trader {}. tradeId={}, {}.uid={}, chatMessage.uid={}",
|
||||||
if (trade == null) {
|
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
|
||||||
log.warn("Dispute trade {} does not exist", dispute.getTradeId());
|
disputeClosedMessage.getClass().getSimpleName(), disputeClosedMessage.getTradeId(),
|
||||||
return;
|
disputeClosedMessage.getUid(), chatMessage.getUid());
|
||||||
|
mailboxMessageService.sendEncryptedMailboxMessage(receiver.getNodeAddress(),
|
||||||
|
dispute.getTraderPubKeyRing(),
|
||||||
|
disputeClosedMessage,
|
||||||
|
new SendMailboxMessageListener() {
|
||||||
|
@Override
|
||||||
|
public void onArrived() {
|
||||||
|
log.info("{} arrived at trader {}. tradeId={}, disputeClosedMessage.uid={}, " +
|
||||||
|
"chatMessage.uid={}",
|
||||||
|
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
|
||||||
|
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
|
||||||
|
chatMessage.getUid());
|
||||||
|
|
||||||
|
// We use the chatMessage wrapped inside the DisputeClosedMessage for
|
||||||
|
// the state, as that is displayed to the user and we only persist that msg
|
||||||
|
chatMessage.setArrived(true);
|
||||||
|
trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG);
|
||||||
|
trade.syncWalletNormallyForMs(30000);
|
||||||
|
requestPersistence();
|
||||||
|
resultHandler.handleResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStoredInMailbox() {
|
||||||
|
log.info("{} stored in mailbox for trader {}. tradeId={}, DisputeClosedMessage.uid={}, " +
|
||||||
|
"chatMessage.uid={}",
|
||||||
|
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
|
||||||
|
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
|
||||||
|
chatMessage.getUid());
|
||||||
|
|
||||||
|
// We use the chatMessage wrapped inside the DisputeClosedMessage for
|
||||||
|
// the state, as that is displayed to the user and we only persist that msg
|
||||||
|
chatMessage.setStoredInMailbox(true);
|
||||||
|
Trade trade = tradeManager.getTrade(dispute.getTradeId());
|
||||||
|
trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_STORED_IN_MAILBOX_DISPUTE_CLOSED_MSG);
|
||||||
|
requestPersistence();
|
||||||
|
resultHandler.handleResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFault(String errorMessage) {
|
||||||
|
log.error("{} failed: Trader {}. tradeId={}, DisputeClosedMessage.uid={}, " +
|
||||||
|
"chatMessage.uid={}, errorMessage={}",
|
||||||
|
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
|
||||||
|
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
|
||||||
|
chatMessage.getUid(), errorMessage);
|
||||||
|
|
||||||
|
// We use the chatMessage wrapped inside the DisputeClosedMessage for
|
||||||
|
// the state, as that is displayed to the user and we only persist that msg
|
||||||
|
chatMessage.setSendMessageError(errorMessage);
|
||||||
|
trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_SEND_FAILED_DISPUTE_CLOSED_MSG);
|
||||||
|
requestPersistence();
|
||||||
|
faultHandler.handleFault(errorMessage, new RuntimeException(errorMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// save state
|
||||||
|
if (payoutTx != null) {
|
||||||
|
trade.setPayoutTx(payoutTx);
|
||||||
|
trade.setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
||||||
|
}
|
||||||
|
trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_SENT_DISPUTE_CLOSED_MSG);
|
||||||
|
requestPersistence();
|
||||||
|
} catch (Exception e) {
|
||||||
|
faultHandler.handleFault(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public MoneroTxWallet createDisputePayoutTx(Trade trade, Dispute dispute, DisputeResult disputeResult) {
|
||||||
|
|
||||||
// sync and save wallet
|
// sync and save wallet
|
||||||
trade.syncWallet();
|
trade.syncWallet();
|
||||||
|
@ -739,139 +827,55 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
if (!trade.isPayoutPublished()) {
|
if (!trade.isPayoutPublished()) {
|
||||||
log.info("Arbitrator creating unsigned dispute payout tx for trade {}", trade.getId());
|
log.info("Arbitrator creating unsigned dispute payout tx for trade {}", trade.getId());
|
||||||
try {
|
try {
|
||||||
MoneroTxWallet payoutTx = createDisputePayoutTx(trade, dispute, disputeResult);
|
|
||||||
trade.setPayoutTx(payoutTx);
|
// trade wallet must be synced
|
||||||
trade.setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
if (trade.getWallet().isMultisigImportNeeded()) throw new RuntimeException("Arbitrator's wallet needs updated multisig hex to create payout tx which means a trader must have already broadcast the payout tx for trade " + dispute.getTradeId());
|
||||||
|
|
||||||
|
// collect winner and loser payout address and amounts
|
||||||
|
Contract contract = dispute.getContract();
|
||||||
|
String winnerPayoutAddress = disputeResult.getWinner() == Winner.BUYER ?
|
||||||
|
(contract.isBuyerMakerAndSellerTaker() ? contract.getMakerPayoutAddressString() : contract.getTakerPayoutAddressString()) :
|
||||||
|
(contract.isBuyerMakerAndSellerTaker() ? contract.getTakerPayoutAddressString() : contract.getMakerPayoutAddressString());
|
||||||
|
String loserPayoutAddress = winnerPayoutAddress.equals(contract.getMakerPayoutAddressString()) ? contract.getTakerPayoutAddressString() : contract.getMakerPayoutAddressString();
|
||||||
|
BigInteger winnerPayoutAmount = HavenoUtils.coinToAtomicUnits(disputeResult.getWinner() == Winner.BUYER ? disputeResult.getBuyerPayoutAmount() : disputeResult.getSellerPayoutAmount());
|
||||||
|
BigInteger loserPayoutAmount = HavenoUtils.coinToAtomicUnits(disputeResult.getWinner() == Winner.BUYER ? disputeResult.getSellerPayoutAmount() : disputeResult.getBuyerPayoutAmount());
|
||||||
|
|
||||||
|
// create transaction to get fee estimate
|
||||||
|
MoneroTxConfig txConfig = new MoneroTxConfig().setAccountIndex(0).setRelay(false);
|
||||||
|
if (winnerPayoutAmount.compareTo(BigInteger.ZERO) > 0) txConfig.addDestination(winnerPayoutAddress, winnerPayoutAmount.multiply(BigInteger.valueOf(9)).divide(BigInteger.valueOf(10))); // reduce payment amount to get fee of similar tx
|
||||||
|
if (loserPayoutAmount.compareTo(BigInteger.ZERO) > 0) txConfig.addDestination(loserPayoutAddress, loserPayoutAmount.multiply(BigInteger.valueOf(9)).divide(BigInteger.valueOf(10)));
|
||||||
|
MoneroTxWallet feeEstimateTx = trade.getWallet().createTx(txConfig);
|
||||||
|
|
||||||
|
// create payout tx by increasing estimated fee until successful
|
||||||
|
MoneroTxWallet payoutTx = null;
|
||||||
|
int numAttempts = 0;
|
||||||
|
while (payoutTx == null && numAttempts < 50) {
|
||||||
|
BigInteger feeEstimate = feeEstimateTx.getFee().add(feeEstimateTx.getFee().multiply(BigInteger.valueOf(numAttempts)).divide(BigInteger.valueOf(10))); // add 1/10th of fee until tx is successful
|
||||||
|
txConfig = new MoneroTxConfig().setAccountIndex(0).setRelay(false);
|
||||||
|
if (winnerPayoutAmount.compareTo(BigInteger.ZERO) > 0) txConfig.addDestination(winnerPayoutAddress, winnerPayoutAmount.subtract(loserPayoutAmount.equals(BigInteger.ZERO) ? feeEstimate : BigInteger.ZERO)); // winner only pays fee if loser gets 0
|
||||||
|
if (loserPayoutAmount.compareTo(BigInteger.ZERO) > 0) {
|
||||||
|
if (loserPayoutAmount.compareTo(feeEstimate) < 0) throw new RuntimeException("Loser payout is too small to cover the mining fee");
|
||||||
|
if (loserPayoutAmount.compareTo(feeEstimate) > 0) txConfig.addDestination(loserPayoutAddress, loserPayoutAmount.subtract(feeEstimate)); // loser pays fee
|
||||||
|
}
|
||||||
|
numAttempts++;
|
||||||
|
try {
|
||||||
|
payoutTx = trade.getWallet().createTx(txConfig);
|
||||||
|
} catch (MoneroError e) {
|
||||||
|
// exception expected // TODO: better way of estimating fee?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (payoutTx == null) throw new RuntimeException("Failed to generate dispute payout tx after " + numAttempts + " attempts");
|
||||||
|
log.info("Dispute payout transaction generated on attempt {}", numAttempts);
|
||||||
|
|
||||||
|
// save updated multisig hex
|
||||||
|
trade.getSelf().setUpdatedMultisigHex(trade.getWallet().exportMultisigHex());
|
||||||
|
return payoutTx;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (!trade.isPayoutPublished()) throw e;
|
if (!trade.isPayoutPublished()) throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null; // can be null if already published or we don't have receiver's multisig hex
|
||||||
// create dispute closed message
|
|
||||||
String unsignedPayoutTxHex = receiver.getUpdatedMultisigHex() == null ? null : trade.getPayoutTxHex();
|
|
||||||
TradingPeer receiverPeer = receiver == trade.getBuyer() ? trade.getSeller() : trade.getBuyer();
|
|
||||||
boolean deferPublishPayout = unsignedPayoutTxHex != null && receiverPeer.getUpdatedMultisigHex() != null && trade.getDisputeState().ordinal() >= Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG.ordinal() ;
|
|
||||||
DisputeClosedMessage disputeClosedMessage = new DisputeClosedMessage(disputeResult,
|
|
||||||
p2PService.getAddress(),
|
|
||||||
UUID.randomUUID().toString(),
|
|
||||||
getSupportType(),
|
|
||||||
trade.getSelf().getUpdatedMultisigHex(),
|
|
||||||
trade.isPayoutPublished() ? null : unsignedPayoutTxHex, // include dispute payout tx if unpublished and arbitrator has their updated multisig info
|
|
||||||
deferPublishPayout); // instruct trader to defer publishing payout tx because peer is expected to publish imminently
|
|
||||||
|
|
||||||
// send dispute closed message
|
|
||||||
log.info("Send {} to trader {}. tradeId={}, {}.uid={}, chatMessage.uid={}",
|
|
||||||
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
|
|
||||||
disputeClosedMessage.getClass().getSimpleName(), disputeClosedMessage.getTradeId(),
|
|
||||||
disputeClosedMessage.getUid(), chatMessage.getUid());
|
|
||||||
mailboxMessageService.sendEncryptedMailboxMessage(receiver.getNodeAddress(),
|
|
||||||
dispute.getTraderPubKeyRing(),
|
|
||||||
disputeClosedMessage,
|
|
||||||
new SendMailboxMessageListener() {
|
|
||||||
@Override
|
|
||||||
public void onArrived() {
|
|
||||||
log.info("{} arrived at trader {}. tradeId={}, disputeClosedMessage.uid={}, " +
|
|
||||||
"chatMessage.uid={}",
|
|
||||||
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
|
|
||||||
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
|
|
||||||
chatMessage.getUid());
|
|
||||||
|
|
||||||
// We use the chatMessage wrapped inside the DisputeClosedMessage for
|
|
||||||
// the state, as that is displayed to the user and we only persist that msg
|
|
||||||
chatMessage.setArrived(true);
|
|
||||||
trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG);
|
|
||||||
trade.syncWalletNormallyForMs(30000);
|
|
||||||
requestPersistence();
|
|
||||||
resultHandler.handleResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStoredInMailbox() {
|
|
||||||
log.info("{} stored in mailbox for trader {}. tradeId={}, DisputeClosedMessage.uid={}, " +
|
|
||||||
"chatMessage.uid={}",
|
|
||||||
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
|
|
||||||
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
|
|
||||||
chatMessage.getUid());
|
|
||||||
|
|
||||||
// We use the chatMessage wrapped inside the DisputeClosedMessage for
|
|
||||||
// the state, as that is displayed to the user and we only persist that msg
|
|
||||||
chatMessage.setStoredInMailbox(true);
|
|
||||||
Trade trade = tradeManager.getTrade(dispute.getTradeId());
|
|
||||||
trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_STORED_IN_MAILBOX_DISPUTE_CLOSED_MSG);
|
|
||||||
requestPersistence();
|
|
||||||
resultHandler.handleResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFault(String errorMessage) {
|
|
||||||
log.error("{} failed: Trader {}. tradeId={}, DisputeClosedMessage.uid={}, " +
|
|
||||||
"chatMessage.uid={}, errorMessage={}",
|
|
||||||
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
|
|
||||||
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
|
|
||||||
chatMessage.getUid(), errorMessage);
|
|
||||||
|
|
||||||
// We use the chatMessage wrapped inside the DisputeClosedMessage for
|
|
||||||
// the state, as that is displayed to the user and we only persist that msg
|
|
||||||
chatMessage.setSendMessageError(errorMessage);
|
|
||||||
trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_SEND_FAILED_DISPUTE_CLOSED_MSG);
|
|
||||||
requestPersistence();
|
|
||||||
resultHandler.handleResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
trade.setDisputeStateIfProgress(Trade.DisputeState.ARBITRATOR_SENT_DISPUTE_CLOSED_MSG);
|
|
||||||
requestPersistence();
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Utils
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private MoneroTxWallet createDisputePayoutTx(Trade trade, Dispute dispute, DisputeResult disputeResult) {
|
|
||||||
|
|
||||||
// trade wallet must be synced
|
|
||||||
if (trade.getWallet().isMultisigImportNeeded()) throw new RuntimeException("Arbitrator's wallet needs updated multisig hex to create payout tx which means a trader must have already broadcast the payout tx for trade " + dispute.getTradeId());
|
|
||||||
|
|
||||||
// collect winner and loser payout address and amounts
|
|
||||||
Contract contract = dispute.getContract();
|
|
||||||
String winnerPayoutAddress = disputeResult.getWinner() == Winner.BUYER ?
|
|
||||||
(contract.isBuyerMakerAndSellerTaker() ? contract.getMakerPayoutAddressString() : contract.getTakerPayoutAddressString()) :
|
|
||||||
(contract.isBuyerMakerAndSellerTaker() ? contract.getTakerPayoutAddressString() : contract.getMakerPayoutAddressString());
|
|
||||||
String loserPayoutAddress = winnerPayoutAddress.equals(contract.getMakerPayoutAddressString()) ? contract.getTakerPayoutAddressString() : contract.getMakerPayoutAddressString();
|
|
||||||
BigInteger winnerPayoutAmount = HavenoUtils.coinToAtomicUnits(disputeResult.getWinner() == Winner.BUYER ? disputeResult.getBuyerPayoutAmount() : disputeResult.getSellerPayoutAmount());
|
|
||||||
BigInteger loserPayoutAmount = HavenoUtils.coinToAtomicUnits(disputeResult.getWinner() == Winner.BUYER ? disputeResult.getSellerPayoutAmount() : disputeResult.getBuyerPayoutAmount());
|
|
||||||
|
|
||||||
// create transaction to get fee estimate
|
|
||||||
MoneroTxConfig txConfig = new MoneroTxConfig().setAccountIndex(0).setRelay(false);
|
|
||||||
if (winnerPayoutAmount.compareTo(BigInteger.ZERO) > 0) txConfig.addDestination(winnerPayoutAddress, winnerPayoutAmount.multiply(BigInteger.valueOf(9)).divide(BigInteger.valueOf(10))); // reduce payment amount to get fee of similar tx
|
|
||||||
if (loserPayoutAmount.compareTo(BigInteger.ZERO) > 0) txConfig.addDestination(loserPayoutAddress, loserPayoutAmount.multiply(BigInteger.valueOf(9)).divide(BigInteger.valueOf(10)));
|
|
||||||
MoneroTxWallet feeEstimateTx = trade.getWallet().createTx(txConfig);
|
|
||||||
|
|
||||||
// create payout tx by increasing estimated fee until successful
|
|
||||||
MoneroTxWallet payoutTx = null;
|
|
||||||
int numAttempts = 0;
|
|
||||||
while (payoutTx == null && numAttempts < 50) {
|
|
||||||
BigInteger feeEstimate = feeEstimateTx.getFee().add(feeEstimateTx.getFee().multiply(BigInteger.valueOf(numAttempts)).divide(BigInteger.valueOf(10))); // add 1/10th of fee until tx is successful
|
|
||||||
txConfig = new MoneroTxConfig().setAccountIndex(0).setRelay(false);
|
|
||||||
if (winnerPayoutAmount.compareTo(BigInteger.ZERO) > 0) txConfig.addDestination(winnerPayoutAddress, winnerPayoutAmount.subtract(loserPayoutAmount.equals(BigInteger.ZERO) ? feeEstimate : BigInteger.ZERO)); // winner only pays fee if loser gets 0
|
|
||||||
if (loserPayoutAmount.compareTo(BigInteger.ZERO) > 0) {
|
|
||||||
if (loserPayoutAmount.compareTo(feeEstimate) < 0) throw new RuntimeException("Loser payout is too small to cover the mining fee");
|
|
||||||
if (loserPayoutAmount.compareTo(feeEstimate) > 0) txConfig.addDestination(loserPayoutAddress, loserPayoutAmount.subtract(feeEstimate)); // loser pays fee
|
|
||||||
}
|
|
||||||
numAttempts++;
|
|
||||||
try {
|
|
||||||
payoutTx = trade.getWallet().createTx(txConfig);
|
|
||||||
} catch (MoneroError e) {
|
|
||||||
// exception expected // TODO: better way of estimating fee?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (payoutTx == null) throw new RuntimeException("Failed to generate dispute payout tx after " + numAttempts + " attempts");
|
|
||||||
log.info("Dispute payout transaction generated on attempt {}", numAttempts);
|
|
||||||
|
|
||||||
// save updated multisig hex
|
|
||||||
trade.getSelf().setUpdatedMultisigHex(trade.getWallet().exportMultisigHex());
|
|
||||||
return payoutTx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tuple2<NodeAddress, PubKeyRing> getNodeAddressPubKeyRingTuple(Dispute dispute) {
|
private Tuple2<NodeAddress, PubKeyRing> getNodeAddressPubKeyRingTuple(Dispute dispute) {
|
||||||
|
|
|
@ -1849,8 +1849,7 @@ disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=Spending: {0}\n\
|
disputeSummaryWindow.close.txDetails=Spending: {0}\n\
|
||||||
{1}{2}\
|
{1}{2}\
|
||||||
Transaction fee: {3} ({4} satoshis/vbyte)\n\
|
Transaction fee: {3}\n\n\
|
||||||
Transaction vsize: {5} vKb\n\n\
|
|
||||||
Are you sure you want to publish this transaction?
|
Are you sure you want to publish this transaction?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ disputeSummaryWindow.close.txDetails.headline=Zveřejněte transakci vrácení p
|
||||||
disputeSummaryWindow.close.txDetails.buyer=Kupující obdrží {0} na adresu: {1}\n
|
disputeSummaryWindow.close.txDetails.buyer=Kupující obdrží {0} na adresu: {1}\n
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=Prodejce obdrží {0} na adresu: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=Prodejce obdrží {0} na adresu: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=Výdaje: {0}\n{1} {2} Transakční poplatek: {3} ({4} satoshi/vbyte)\nTransakční vsize: {5} vKb\n\nOpravdu chcete tuto transakci zveřejnit?
|
disputeSummaryWindow.close.txDetails=Výdaje: {0}\n{1} {2} Transakční poplatek: {3}\n\nOpravdu chcete tuto transakci zveřejnit?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=Uzavřít bez jakékoli výplaty
|
disputeSummaryWindow.close.noPayout.headline=Uzavřít bez jakékoli výplaty
|
||||||
disputeSummaryWindow.close.noPayout.text=Chcete zavřít bez výplaty?
|
disputeSummaryWindow.close.noPayout.text=Chcete zavřít bez výplaty?
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ disputeSummaryWindow.close.txDetails.headline=Rückerstattungstransaktion veröf
|
||||||
disputeSummaryWindow.close.txDetails.buyer=Käufer erhält {0} an Adresse: {1}\n
|
disputeSummaryWindow.close.txDetails.buyer=Käufer erhält {0} an Adresse: {1}\n
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=Verkäufer erhält {0} an Adresse: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=Verkäufer erhält {0} an Adresse: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=Ausgaben: {0}\n{1}{2}Transaktionsgebühr: {3} ({4} Satoshis/Byte)\nTransaktionsgröße: {5} Kb\n\nSind Sie sicher, dass Sie diese Transaktion veröffentlichen möchten?
|
disputeSummaryWindow.close.txDetails=Ausgaben: {0}\n{1}{2}Transaktionsgebühr: {3}\n\nSind Sie sicher, dass Sie diese Transaktion veröffentlichen möchten?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=Ohne Auszahlung schließen
|
disputeSummaryWindow.close.noPayout.headline=Ohne Auszahlung schließen
|
||||||
disputeSummaryWindow.close.noPayout.text=Wollen Sie schließen ohne eine Auszahlung zu tätigen?
|
disputeSummaryWindow.close.noPayout.text=Wollen Sie schließen ohne eine Auszahlung zu tätigen?
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ disputeSummaryWindow.close.txDetails.headline=Publicar transacción de devoluci
|
||||||
disputeSummaryWindow.close.txDetails.buyer=El comprador recibe {0} en la dirección: {1}\n
|
disputeSummaryWindow.close.txDetails.buyer=El comprador recibe {0} en la dirección: {1}\n
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=El vendedor recibe {0} en la dirección: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=El vendedor recibe {0} en la dirección: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=Gastando: {0}\n{1}{2}Tasa de transacción: {3} ({4} satoshis/vbyte)\nTamaño virtual de transacción: {5} vKb\n\n¿Está seguro de que quiere publicar esta transacción?\n
|
disputeSummaryWindow.close.txDetails=Gastando: {0}\n{1}{2}Tasa de transacción: {3}\n\n¿Está seguro de que quiere publicar esta transacción?\n
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=Cerrar sin realizar algún pago
|
disputeSummaryWindow.close.noPayout.headline=Cerrar sin realizar algún pago
|
||||||
disputeSummaryWindow.close.noPayout.text=¿Quiere cerrar sin realizar algún pago?
|
disputeSummaryWindow.close.noPayout.text=¿Quiere cerrar sin realizar algún pago?
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ disputeSummaryWindow.close.txDetails.headline=Publish refund transaction
|
||||||
disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n
|
disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nAre you sure you want to publish this transaction?
|
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3}\n\nAre you sure you want to publish this transaction?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
||||||
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
||||||
|
|
|
@ -1396,7 +1396,7 @@ disputeSummaryWindow.close.txDetails.headline=Publier la transaction de rembours
|
||||||
disputeSummaryWindow.close.txDetails.buyer=L''acheteur reçoit {0} à l''adresse: {1}\n
|
disputeSummaryWindow.close.txDetails.buyer=L''acheteur reçoit {0} à l''adresse: {1}\n
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=Le vendeur reçoit {0} à l''adresse: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=Le vendeur reçoit {0} à l''adresse: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=Dépenser: {0}\n{1}{2}Frais de transaction: {3} ({4} satoshis/vbyte)\nTaille virtuelle de la transaction: {5} vKb\n\nÊtes-vous sûr de vouloir publier cette transaction ?
|
disputeSummaryWindow.close.txDetails=Dépenser: {0}\n{1}{2}Frais de transaction: {3}\n\nÊtes-vous sûr de vouloir publier cette transaction ?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=Fermé sans paiement
|
disputeSummaryWindow.close.noPayout.headline=Fermé sans paiement
|
||||||
disputeSummaryWindow.close.noPayout.text=Voulez-vous fermer sans paiement ?
|
disputeSummaryWindow.close.noPayout.text=Voulez-vous fermer sans paiement ?
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ disputeSummaryWindow.close.txDetails.headline=Pubblica transazione di rimborso
|
||||||
disputeSummaryWindow.close.txDetails.buyer=L'acquirente riceve {0} all'indirizzo: {1}\n
|
disputeSummaryWindow.close.txDetails.buyer=L'acquirente riceve {0} all'indirizzo: {1}\n
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=Il venditore riceve {0} all'indirizzo: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=Il venditore riceve {0} all'indirizzo: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nAre you sure you want to publish this transaction?
|
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3}\n\nAre you sure you want to publish this transaction?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
||||||
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ disputeSummaryWindow.close.txDetails.headline=払い戻しトランザクショ
|
||||||
disputeSummaryWindow.close.txDetails.buyer=買い手が {0} を受けます、入金先アドレス: {1}\n
|
disputeSummaryWindow.close.txDetails.buyer=買い手が {0} を受けます、入金先アドレス: {1}\n
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=売り手が {0} を受けます、入金先アドレス: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=売り手が {0} を受けます、入金先アドレス: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=支払う金額: {0}\n{1}{2}トランザクション手数料: {3}({4}サトシ/vバイト)\nトランザクションvサイズ: {5} vKb\n\nこのトランザクションを発行してもよろしいですか?
|
disputeSummaryWindow.close.txDetails=支払う金額: {0}\n{1}{2}トランザクション手数料: {3}\n\nこのトランザクションを発行してもよろしいですか?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=支払いなしで閉じる
|
disputeSummaryWindow.close.noPayout.headline=支払いなしで閉じる
|
||||||
disputeSummaryWindow.close.noPayout.text=支払いなしで閉じてもよろしいですか?
|
disputeSummaryWindow.close.noPayout.text=支払いなしで閉じてもよろしいですか?
|
||||||
|
|
|
@ -1399,7 +1399,7 @@ disputeSummaryWindow.close.txDetails.headline=Publicar transação de reembolso
|
||||||
disputeSummaryWindow.close.txDetails.buyer=Comprador recebe {0} no endereço: {1}\n
|
disputeSummaryWindow.close.txDetails.buyer=Comprador recebe {0} no endereço: {1}\n
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=Vendedor recebe {0} no endereço: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=Vendedor recebe {0} no endereço: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nAre you sure you want to publish this transaction?
|
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3}\n\nAre you sure you want to publish this transaction?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
||||||
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ disputeSummaryWindow.close.txDetails.headline=Publicar transação de reembolso
|
||||||
disputeSummaryWindow.close.txDetails.buyer=O comprador recebe {0} no endereço: {1}\n
|
disputeSummaryWindow.close.txDetails.buyer=O comprador recebe {0} no endereço: {1}\n
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=O vendedor recebe {0} no endereço: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=O vendedor recebe {0} no endereço: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nAre you sure you want to publish this transaction?
|
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3}\n\nAre you sure you want to publish this transaction?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
||||||
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ disputeSummaryWindow.close.txDetails.headline=Publish refund transaction
|
||||||
disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n
|
disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nAre you sure you want to publish this transaction?
|
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3}\n\nAre you sure you want to publish this transaction?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
||||||
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ disputeSummaryWindow.close.txDetails.headline=Publish refund transaction
|
||||||
disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n
|
disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nAre you sure you want to publish this transaction?
|
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3}\n\nAre you sure you want to publish this transaction?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
||||||
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
||||||
|
|
|
@ -1397,7 +1397,7 @@ disputeSummaryWindow.close.txDetails.headline=Publish refund transaction
|
||||||
disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n
|
disputeSummaryWindow.close.txDetails.buyer=Buyer receives {0} on address: {1}\n
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n
|
disputeSummaryWindow.close.txDetails.seller=Seller receives {0} on address: {1}\n
|
||||||
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nAre you sure you want to publish this transaction?
|
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3}\n\nAre you sure you want to publish this transaction?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
disputeSummaryWindow.close.noPayout.headline=Close without any payout
|
||||||
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ disputeSummaryWindow.close.txDetails.headline=发布交易退款
|
||||||
disputeSummaryWindow.close.txDetails.buyer=买方收到{0}在地址:{1}
|
disputeSummaryWindow.close.txDetails.buyer=买方收到{0}在地址:{1}
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=卖方收到{0}在地址:{1}
|
disputeSummaryWindow.close.txDetails.seller=卖方收到{0}在地址:{1}
|
||||||
disputeSummaryWindow.close.txDetails=费用:{0}\n{1}{2}交易费:{3}({4}satoshis/byte)\n事务大小:{5} Kb\n\n您确定要发布此交易吗?
|
disputeSummaryWindow.close.txDetails=费用:{0}\n{1}{2}交易费:{3}\n\n您确定要发布此交易吗?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=未支付关闭
|
disputeSummaryWindow.close.noPayout.headline=未支付关闭
|
||||||
disputeSummaryWindow.close.noPayout.text=你想要在未作支付的情况下关闭吗?
|
disputeSummaryWindow.close.noPayout.text=你想要在未作支付的情况下关闭吗?
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ disputeSummaryWindow.close.txDetails.headline=發佈交易退款
|
||||||
disputeSummaryWindow.close.txDetails.buyer=買方收到{0}在地址:{1}
|
disputeSummaryWindow.close.txDetails.buyer=買方收到{0}在地址:{1}
|
||||||
# suppress inspection "TrailingSpacesInProperty"
|
# suppress inspection "TrailingSpacesInProperty"
|
||||||
disputeSummaryWindow.close.txDetails.seller=賣方收到{0}在地址:{1}
|
disputeSummaryWindow.close.txDetails.seller=賣方收到{0}在地址:{1}
|
||||||
disputeSummaryWindow.close.txDetails=費用:{0}\n{1}{2}交易費:{3}({4}satoshis/byte)\n事務大小:{5} Kb\n\n您確定要發佈此交易嗎?
|
disputeSummaryWindow.close.txDetails=費用:{0}\n{1}{2}交易費:{3}\n\n您確定要發佈此交易嗎?
|
||||||
|
|
||||||
disputeSummaryWindow.close.noPayout.headline=未支付關閉
|
disputeSummaryWindow.close.noPayout.headline=未支付關閉
|
||||||
disputeSummaryWindow.close.noPayout.text=你想要在未作支付的情況下關閉嗎?
|
disputeSummaryWindow.close.noPayout.text=你想要在未作支付的情況下關閉嗎?
|
||||||
|
|
|
@ -30,6 +30,7 @@ import bisq.desktop.util.Layout;
|
||||||
import bisq.core.btc.wallet.TradeWalletService;
|
import bisq.core.btc.wallet.TradeWalletService;
|
||||||
import bisq.core.btc.wallet.XmrWalletService;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.locale.Res;
|
import bisq.core.locale.Res;
|
||||||
|
import bisq.core.support.SupportType;
|
||||||
import bisq.core.support.dispute.Dispute;
|
import bisq.core.support.dispute.Dispute;
|
||||||
import bisq.core.support.dispute.DisputeList;
|
import bisq.core.support.dispute.DisputeList;
|
||||||
import bisq.core.support.dispute.DisputeManager;
|
import bisq.core.support.dispute.DisputeManager;
|
||||||
|
@ -38,6 +39,7 @@ import bisq.core.support.dispute.arbitration.ArbitrationManager;
|
||||||
import bisq.core.support.dispute.mediation.MediationManager;
|
import bisq.core.support.dispute.mediation.MediationManager;
|
||||||
import bisq.core.support.dispute.refund.RefundManager;
|
import bisq.core.support.dispute.refund.RefundManager;
|
||||||
import bisq.core.trade.Contract;
|
import bisq.core.trade.Contract;
|
||||||
|
import bisq.core.trade.HavenoUtils;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.TradeManager;
|
import bisq.core.trade.TradeManager;
|
||||||
import bisq.core.util.FormattingUtils;
|
import bisq.core.util.FormattingUtils;
|
||||||
|
@ -78,6 +80,7 @@ import java.util.Date;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import monero.wallet.model.MoneroTxWallet;
|
||||||
|
|
||||||
import static bisq.desktop.util.FormBuilder.add2ButtonsWithBox;
|
import static bisq.desktop.util.FormBuilder.add2ButtonsWithBox;
|
||||||
import static bisq.desktop.util.FormBuilder.addConfirmationLabelLabel;
|
import static bisq.desktop.util.FormBuilder.addConfirmationLabelLabel;
|
||||||
|
@ -113,7 +116,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
private ChangeListener<Boolean> customRadioButtonSelectedListener;
|
private ChangeListener<Boolean> customRadioButtonSelectedListener;
|
||||||
private ChangeListener<Toggle> reasonToggleSelectionListener;
|
private ChangeListener<Toggle> reasonToggleSelectionListener;
|
||||||
private InputTextField buyerPayoutAmountInputTextField, sellerPayoutAmountInputTextField;
|
private InputTextField buyerPayoutAmountInputTextField, sellerPayoutAmountInputTextField;
|
||||||
private ChangeListener<String> buyerPayoutAmountListener, sellerPayoutAmountListener;
|
private ChangeListener<Boolean> buyerPayoutAmountListener, sellerPayoutAmountListener;
|
||||||
private ChangeListener<Toggle> tradeAmountToggleGroupListener;
|
private ChangeListener<Toggle> tradeAmountToggleGroupListener;
|
||||||
|
|
||||||
|
|
||||||
|
@ -319,16 +322,16 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
tradeAmountToggleGroupListener = (observable, oldValue, newValue) -> applyPayoutAmounts(newValue);
|
tradeAmountToggleGroupListener = (observable, oldValue, newValue) -> applyPayoutAmounts(newValue);
|
||||||
tradeAmountToggleGroup.selectedToggleProperty().addListener(tradeAmountToggleGroupListener);
|
tradeAmountToggleGroup.selectedToggleProperty().addListener(tradeAmountToggleGroupListener);
|
||||||
|
|
||||||
buyerPayoutAmountListener = (observable1, oldValue1, newValue1) -> applyCustomAmounts(buyerPayoutAmountInputTextField);
|
buyerPayoutAmountListener = (observable, oldValue, newValue) -> applyCustomAmounts(buyerPayoutAmountInputTextField, oldValue, newValue);
|
||||||
sellerPayoutAmountListener = (observable1, oldValue1, newValue1) -> applyCustomAmounts(sellerPayoutAmountInputTextField);
|
sellerPayoutAmountListener = (observable, oldValue, newValue) -> applyCustomAmounts(sellerPayoutAmountInputTextField, oldValue, newValue);
|
||||||
|
|
||||||
customRadioButtonSelectedListener = (observable, oldValue, newValue) -> {
|
customRadioButtonSelectedListener = (observable, oldValue, newValue) -> {
|
||||||
buyerPayoutAmountInputTextField.setEditable(newValue);
|
buyerPayoutAmountInputTextField.setEditable(newValue);
|
||||||
sellerPayoutAmountInputTextField.setEditable(newValue);
|
sellerPayoutAmountInputTextField.setEditable(newValue);
|
||||||
|
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
buyerPayoutAmountInputTextField.textProperty().addListener(buyerPayoutAmountListener);
|
buyerPayoutAmountInputTextField.focusedProperty().addListener(buyerPayoutAmountListener);
|
||||||
sellerPayoutAmountInputTextField.textProperty().addListener(sellerPayoutAmountListener);
|
sellerPayoutAmountInputTextField.focusedProperty().addListener(sellerPayoutAmountListener);
|
||||||
} else {
|
} else {
|
||||||
removePayoutAmountListeners();
|
removePayoutAmountListeners();
|
||||||
}
|
}
|
||||||
|
@ -338,11 +341,10 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
|
|
||||||
private void removePayoutAmountListeners() {
|
private void removePayoutAmountListeners() {
|
||||||
if (buyerPayoutAmountInputTextField != null && buyerPayoutAmountListener != null)
|
if (buyerPayoutAmountInputTextField != null && buyerPayoutAmountListener != null)
|
||||||
buyerPayoutAmountInputTextField.textProperty().removeListener(buyerPayoutAmountListener);
|
buyerPayoutAmountInputTextField.focusedProperty().removeListener(buyerPayoutAmountListener);
|
||||||
|
|
||||||
if (sellerPayoutAmountInputTextField != null && sellerPayoutAmountListener != null)
|
if (sellerPayoutAmountInputTextField != null && sellerPayoutAmountListener != null)
|
||||||
sellerPayoutAmountInputTextField.textProperty().removeListener(sellerPayoutAmountListener);
|
sellerPayoutAmountInputTextField.focusedProperty().removeListener(sellerPayoutAmountListener);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPayoutAmountValid() {
|
private boolean isPayoutAmountValid() {
|
||||||
|
@ -368,12 +370,12 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyCustomAmounts(InputTextField inputTextField) {
|
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
|
// We only apply adjustments at focus out, otherwise we cannot enter certain values if we update at each
|
||||||
// // keystroke.
|
// keystroke.
|
||||||
// if (!oldFocusValue || newFocusValue) {
|
if (!oldFocusValue || newFocusValue) {
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
|
|
||||||
Contract contract = dispute.getContract();
|
Contract contract = dispute.getContract();
|
||||||
Coin available = contract.getTradeAmount()
|
Coin available = contract.getTradeAmount()
|
||||||
|
@ -562,188 +564,89 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addButtons(Contract contract) {
|
private void addButtons(Contract contract) {
|
||||||
Tuple3<Button, Button, HBox> tuple = add2ButtonsWithBox(gridPane, ++rowIndex,
|
Tuple3<Button, Button, HBox> tuple = add2ButtonsWithBox(gridPane, ++rowIndex,
|
||||||
Res.get("disputeSummaryWindow.close.button"),
|
Res.get("disputeSummaryWindow.close.button"),
|
||||||
Res.get("shared.cancel"), 15, true);
|
Res.get("shared.cancel"), 15, true);
|
||||||
Button closeTicketButton = tuple.first;
|
Button closeTicketButton = tuple.first;
|
||||||
closeTicketButton.disableProperty().bind(Bindings.createBooleanBinding(
|
closeTicketButton.disableProperty().bind(Bindings.createBooleanBinding(
|
||||||
() -> tradeAmountToggleGroup.getSelectedToggle() == null
|
() -> tradeAmountToggleGroup.getSelectedToggle() == null
|
||||||
|| summaryNotesTextArea.getText() == null
|
|| summaryNotesTextArea.getText() == null
|
||||||
|| summaryNotesTextArea.getText().length() == 0
|
|| summaryNotesTextArea.getText().length() == 0
|
||||||
|| !isPayoutAmountValid(),
|
|| !isPayoutAmountValid(),
|
||||||
tradeAmountToggleGroup.selectedToggleProperty(),
|
tradeAmountToggleGroup.selectedToggleProperty(),
|
||||||
summaryNotesTextArea.textProperty(),
|
summaryNotesTextArea.textProperty(),
|
||||||
buyerPayoutAmountInputTextField.textProperty(),
|
buyerPayoutAmountInputTextField.textProperty(),
|
||||||
sellerPayoutAmountInputTextField.textProperty()));
|
sellerPayoutAmountInputTextField.textProperty()));
|
||||||
|
|
||||||
Button cancelButton = tuple.second;
|
Button cancelButton = tuple.second;
|
||||||
|
|
||||||
closeTicketButton.setOnAction(e -> {
|
closeTicketButton.setOnAction(e -> {
|
||||||
doClose(closeTicketButton);
|
|
||||||
|
|
||||||
// if (dispute.getDepositTxSerialized() == null) {
|
// create payout tx
|
||||||
// log.warn("dispute.getDepositTxSerialized is null");
|
MoneroTxWallet payoutTx = arbitrationManager.createDisputePayoutTx(trade, dispute, disputeResult);
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (dispute.getSupportType() == SupportType.REFUND &&
|
|
||||||
// peersDisputeOptional.isPresent() &&
|
|
||||||
// !peersDisputeOptional.get().isClosed()) {
|
|
||||||
// showPayoutTxConfirmation(contract,
|
|
||||||
// disputeResult,
|
|
||||||
// () -> doCloseIfValid(closeTicketButton));
|
|
||||||
// } else {
|
|
||||||
// doCloseIfValid(closeTicketButton);
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
|
|
||||||
cancelButton.setOnAction(e -> {
|
// show confirmation
|
||||||
dispute.setDisputeResult(disputeResult);
|
if (dispute.getSupportType() == SupportType.ARBITRATION &&
|
||||||
checkNotNull(getDisputeManager(dispute)).requestPersistence();
|
peersDisputeOptional.isPresent() &&
|
||||||
hide();
|
!peersDisputeOptional.get().isClosed()) {
|
||||||
});
|
showPayoutTxConfirmation(contract,
|
||||||
|
disputeResult,
|
||||||
|
payoutTx,
|
||||||
|
() -> doClose(closeTicketButton, payoutTx));
|
||||||
|
} else {
|
||||||
|
doClose(closeTicketButton, payoutTx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cancelButton.setOnAction(e -> {
|
||||||
|
dispute.setDisputeResult(disputeResult);
|
||||||
|
checkNotNull(getDisputeManager(dispute)).requestPersistence();
|
||||||
|
hide();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showPayoutTxConfirmation(Contract contract, DisputeResult disputeResult, ResultHandler resultHandler) {
|
private void showPayoutTxConfirmation(Contract contract, DisputeResult disputeResult, MoneroTxWallet payoutTx, ResultHandler resultHandler) {
|
||||||
throw new RuntimeException("DisputeSummaryWindow.showPayoutTxConfimration() needs updated for XMR");
|
Coin buyerPayoutAmount = disputeResult.getBuyerPayoutAmount();
|
||||||
// Coin buyerPayoutAmount = disputeResult.getBuyerPayoutAmount();
|
String buyerPayoutAddressString = contract.getBuyerPayoutAddressString();
|
||||||
// String buyerPayoutAddressString = contract.getBuyerPayoutAddressString();
|
Coin sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
|
||||||
// Coin sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
|
String sellerPayoutAddressString = contract.getSellerPayoutAddressString();
|
||||||
// String sellerPayoutAddressString = contract.getSellerPayoutAddressString();
|
Coin outputAmount = buyerPayoutAmount.add(sellerPayoutAmount);
|
||||||
// Coin outputAmount = buyerPayoutAmount.add(sellerPayoutAmount);
|
String buyerDetails = "";
|
||||||
// Tuple2<Coin, Integer> feeTuple = txFeeEstimationService.getEstimatedFeeAndTxSize(outputAmount, feeService, btcWalletService);
|
if (buyerPayoutAmount.isPositive()) {
|
||||||
// Coin fee = feeTuple.first;
|
buyerDetails = Res.get("disputeSummaryWindow.close.txDetails.buyer",
|
||||||
// Integer txSize = feeTuple.second;
|
formatter.formatCoinWithCode(buyerPayoutAmount),
|
||||||
// double feePerByte = CoinUtil.getFeePerByte(fee, txSize);
|
buyerPayoutAddressString);
|
||||||
// double kb = txSize / 1000d;
|
}
|
||||||
// Coin inputAmount = outputAmount.add(fee);
|
String sellerDetails = "";
|
||||||
// String buyerDetails = "";
|
if (sellerPayoutAmount.isPositive()) {
|
||||||
// if (buyerPayoutAmount.isPositive()) {
|
sellerDetails = Res.get("disputeSummaryWindow.close.txDetails.seller",
|
||||||
// buyerDetails = Res.get("disputeSummaryWindow.close.txDetails.buyer",
|
formatter.formatCoinWithCode(sellerPayoutAmount),
|
||||||
// formatter.formatCoinWithCode(buyerPayoutAmount),
|
sellerPayoutAddressString);
|
||||||
// buyerPayoutAddressString);
|
}
|
||||||
// }
|
if (outputAmount.isPositive()) {
|
||||||
// String sellerDetails = "";
|
new Popup().width(900)
|
||||||
// if (sellerPayoutAmount.isPositive()) {
|
.headLine(Res.get("disputeSummaryWindow.close.txDetails.headline"))
|
||||||
// sellerDetails = Res.get("disputeSummaryWindow.close.txDetails.seller",
|
.confirmation(Res.get("disputeSummaryWindow.close.txDetails",
|
||||||
// formatter.formatCoinWithCode(sellerPayoutAmount),
|
formatter.formatCoinWithCode(outputAmount),
|
||||||
// sellerPayoutAddressString);
|
buyerDetails,
|
||||||
// }
|
sellerDetails,
|
||||||
// if (outputAmount.isPositive()) {
|
formatter.formatCoinWithCode(HavenoUtils.atomicUnitsToCoin(payoutTx.getFee()))))
|
||||||
// new Popup().width(900)
|
.actionButtonText(Res.get("shared.yes"))
|
||||||
// .headLine(Res.get("disputeSummaryWindow.close.txDetails.headline"))
|
.onAction(() -> resultHandler.handleResult())
|
||||||
// .confirmation(Res.get("disputeSummaryWindow.close.txDetails",
|
.closeButtonText(Res.get("shared.cancel"))
|
||||||
// formatter.formatCoinWithCode(inputAmount),
|
.show();
|
||||||
// buyerDetails,
|
} else {
|
||||||
// sellerDetails,
|
// No payout will be made
|
||||||
// formatter.formatCoinWithCode(fee),
|
new Popup().headLine(Res.get("disputeSummaryWindow.close.noPayout.headline"))
|
||||||
// feePerByte,
|
.confirmation(Res.get("disputeSummaryWindow.close.noPayout.text"))
|
||||||
// kb))
|
.actionButtonText(Res.get("shared.yes"))
|
||||||
// .actionButtonText(Res.get("shared.yes"))
|
.onAction(resultHandler::handleResult)
|
||||||
// .onAction(() -> {
|
.closeButtonText(Res.get("shared.cancel"))
|
||||||
// doPayout(buyerPayoutAmount,
|
.show();
|
||||||
// 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,
|
private void doClose(Button closeTicketButton, MoneroTxWallet payoutTx) {
|
||||||
Coin sellerPayoutAmount,
|
|
||||||
Coin fee,
|
|
||||||
String buyerPayoutAddressString,
|
|
||||||
String sellerPayoutAddressString,
|
|
||||||
ResultHandler resultHandler) {
|
|
||||||
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) {
|
|
||||||
throw new RuntimeException("DisputeSummaryWindow.doCloseIfValid() needs updated for XMR");
|
|
||||||
// var disputeManager = checkNotNull(getDisputeManager(dispute));
|
|
||||||
// try {
|
|
||||||
// TradeDataValidation.validateDonationAddress(dispute.getDonationAddressOfDelayedPayoutTx());
|
|
||||||
// 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,
|
|
||||||
// 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,
|
|
||||||
// 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) {
|
|
||||||
DisputeManager<? extends DisputeList<Dispute>> disputeManager = getDisputeManager(dispute);
|
DisputeManager<? extends DisputeList<Dispute>> disputeManager = getDisputeManager(dispute);
|
||||||
if (disputeManager == null) {
|
if (disputeManager == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -752,15 +655,17 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
summaryNotesTextArea.textProperty().unbindBidirectional(disputeResult.summaryNotesProperty());
|
summaryNotesTextArea.textProperty().unbindBidirectional(disputeResult.summaryNotesProperty());
|
||||||
|
|
||||||
disputeResult.setCloseDate(new Date());
|
disputeResult.setCloseDate(new Date());
|
||||||
disputesService.closeDisputeTicket(disputeManager, dispute, disputeResult, () -> {
|
disputesService.closeDisputeTicket(disputeManager, dispute, disputeResult, payoutTx, () -> {
|
||||||
if (peersDisputeOptional.isPresent() && !peersDisputeOptional.get().isClosed() && !DevEnv.isDevMode()) {
|
if (peersDisputeOptional.isPresent() && !peersDisputeOptional.get().isClosed() && !DevEnv.isDevMode()) {
|
||||||
new Popup().attention(Res.get("disputeSummaryWindow.close.closePeer")).show();
|
new Popup().attention(Res.get("disputeSummaryWindow.close.closePeer")).show();
|
||||||
}
|
}
|
||||||
disputeManager.requestPersistence();
|
disputeManager.requestPersistence();
|
||||||
|
closeTicketButton.disableProperty().unbind();
|
||||||
|
hide();
|
||||||
|
}, (errMessage, err) -> {
|
||||||
|
log.error(errMessage);
|
||||||
|
new Popup().error(err.toString()).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
closeTicketButton.disableProperty().unbind();
|
|
||||||
hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DisputeManager<? extends DisputeList<Dispute>> getDisputeManager(Dispute dispute) {
|
private DisputeManager<? extends DisputeList<Dispute>> getDisputeManager(Dispute dispute) {
|
||||||
|
@ -818,7 +723,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
sellerPayoutAmount.equals(sellerSecurityDeposit)) {
|
sellerPayoutAmount.equals(sellerSecurityDeposit)) {
|
||||||
buyerGetsTradeAmountRadioButton.setSelected(true);
|
buyerGetsTradeAmountRadioButton.setSelected(true);
|
||||||
} else if (buyerPayoutAmount.equals(tradeAmount.add(buyerSecurityDeposit).add(sellerSecurityDeposit)) &&
|
} 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)
|
sellerPayoutAmount.equals(Coin.ZERO)) {
|
||||||
buyerGetsAllRadioButton.setSelected(true);
|
buyerGetsAllRadioButton.setSelected(true);
|
||||||
} else if (sellerPayoutAmount.equals(tradeAmount.add(sellerSecurityDeposit))
|
} else if (sellerPayoutAmount.equals(tradeAmount.add(sellerSecurityDeposit))
|
||||||
&& buyerPayoutAmount.equals(buyerSecurityDeposit)) {
|
&& buyerPayoutAmount.equals(buyerSecurityDeposit)) {
|
||||||
|
|
Loading…
Reference in a new issue