mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-22 19:49:32 +00:00
set buyer and seller payout tx fee and amount, fix csv export #720
This commit is contained in:
parent
846a8634e5
commit
8800d9ea46
18 changed files with 374 additions and 247 deletions
|
@ -163,9 +163,6 @@ public class CoreDisputesService {
|
||||||
}
|
}
|
||||||
applyPayoutAmountsToDisputeResult(payout, winningDispute, winnerDisputeResult, customWinnerAmount);
|
applyPayoutAmountsToDisputeResult(payout, winningDispute, winnerDisputeResult, customWinnerAmount);
|
||||||
|
|
||||||
// create dispute payout tx
|
|
||||||
trade.getProcessModel().setUnsignedPayoutTx(arbitrationManager.createDisputePayoutTx(trade, winningDispute.getContract(), winnerDisputeResult, false));
|
|
||||||
|
|
||||||
// close winning dispute ticket
|
// close winning dispute ticket
|
||||||
closeDisputeTicket(arbitrationManager, winningDispute, winnerDisputeResult, () -> {
|
closeDisputeTicket(arbitrationManager, winningDispute, winnerDisputeResult, () -> {
|
||||||
arbitrationManager.requestPersistence();
|
arbitrationManager.requestPersistence();
|
||||||
|
@ -180,8 +177,8 @@ public class CoreDisputesService {
|
||||||
if (!loserDisputeOptional.isPresent()) throw new IllegalStateException("could not find peer dispute");
|
if (!loserDisputeOptional.isPresent()) throw new IllegalStateException("could not find peer dispute");
|
||||||
var loserDispute = loserDisputeOptional.get();
|
var loserDispute = loserDisputeOptional.get();
|
||||||
var loserDisputeResult = createDisputeResult(loserDispute, winner, reason, summaryNotes, closeDate);
|
var loserDisputeResult = createDisputeResult(loserDispute, winner, reason, summaryNotes, closeDate);
|
||||||
loserDisputeResult.setBuyerPayoutAmount(winnerDisputeResult.getBuyerPayoutAmount());
|
loserDisputeResult.setBuyerPayoutAmountBeforeCost(winnerDisputeResult.getBuyerPayoutAmountBeforeCost());
|
||||||
loserDisputeResult.setSellerPayoutAmount(winnerDisputeResult.getSellerPayoutAmount());
|
loserDisputeResult.setSellerPayoutAmountBeforeCost(winnerDisputeResult.getSellerPayoutAmountBeforeCost());
|
||||||
loserDisputeResult.setSubtractFeeFrom(winnerDisputeResult.getSubtractFeeFrom());
|
loserDisputeResult.setSubtractFeeFrom(winnerDisputeResult.getSubtractFeeFrom());
|
||||||
closeDisputeTicket(arbitrationManager, loserDispute, loserDisputeResult, () -> {
|
closeDisputeTicket(arbitrationManager, loserDispute, loserDisputeResult, () -> {
|
||||||
arbitrationManager.requestPersistence();
|
arbitrationManager.requestPersistence();
|
||||||
|
@ -217,31 +214,23 @@ public class CoreDisputesService {
|
||||||
BigInteger tradeAmount = contract.getTradeAmount();
|
BigInteger tradeAmount = contract.getTradeAmount();
|
||||||
disputeResult.setSubtractFeeFrom(DisputeResult.SubtractFeeFrom.BUYER_AND_SELLER);
|
disputeResult.setSubtractFeeFrom(DisputeResult.SubtractFeeFrom.BUYER_AND_SELLER);
|
||||||
if (payout == DisputePayout.BUYER_GETS_TRADE_AMOUNT) {
|
if (payout == DisputePayout.BUYER_GETS_TRADE_AMOUNT) {
|
||||||
disputeResult.setBuyerPayoutAmount(tradeAmount.add(buyerSecurityDeposit));
|
disputeResult.setBuyerPayoutAmountBeforeCost(tradeAmount.add(buyerSecurityDeposit));
|
||||||
disputeResult.setSellerPayoutAmount(sellerSecurityDeposit);
|
disputeResult.setSellerPayoutAmountBeforeCost(sellerSecurityDeposit);
|
||||||
} else if (payout == DisputePayout.BUYER_GETS_ALL) {
|
} else if (payout == DisputePayout.BUYER_GETS_ALL) {
|
||||||
disputeResult.setBuyerPayoutAmount(tradeAmount
|
disputeResult.setBuyerPayoutAmountBeforeCost(tradeAmount.add(buyerSecurityDeposit).add(sellerSecurityDeposit)); // TODO (woodser): apply min payout to incentivize loser? (see post v1.1.7)
|
||||||
.add(buyerSecurityDeposit)
|
disputeResult.setSellerPayoutAmountBeforeCost(BigInteger.valueOf(0));
|
||||||
.add(sellerSecurityDeposit)); // TODO (woodser): apply min payout to incentivize loser? (see post v1.1.7)
|
|
||||||
disputeResult.setSellerPayoutAmount(BigInteger.valueOf(0));
|
|
||||||
} else if (payout == DisputePayout.SELLER_GETS_TRADE_AMOUNT) {
|
} else if (payout == DisputePayout.SELLER_GETS_TRADE_AMOUNT) {
|
||||||
disputeResult.setBuyerPayoutAmount(buyerSecurityDeposit);
|
disputeResult.setBuyerPayoutAmountBeforeCost(buyerSecurityDeposit);
|
||||||
disputeResult.setSellerPayoutAmount(tradeAmount.add(sellerSecurityDeposit));
|
disputeResult.setSellerPayoutAmountBeforeCost(tradeAmount.add(sellerSecurityDeposit));
|
||||||
} else if (payout == DisputePayout.SELLER_GETS_ALL) {
|
} else if (payout == DisputePayout.SELLER_GETS_ALL) {
|
||||||
disputeResult.setBuyerPayoutAmount(BigInteger.valueOf(0));
|
disputeResult.setBuyerPayoutAmountBeforeCost(BigInteger.valueOf(0));
|
||||||
disputeResult.setSellerPayoutAmount(tradeAmount
|
disputeResult.setSellerPayoutAmountBeforeCost(tradeAmount.add(sellerSecurityDeposit).add(buyerSecurityDeposit));
|
||||||
.add(sellerSecurityDeposit)
|
|
||||||
.add(buyerSecurityDeposit));
|
|
||||||
} else if (payout == DisputePayout.CUSTOM) {
|
} else if (payout == DisputePayout.CUSTOM) {
|
||||||
if (customWinnerAmount > trade.getWallet().getBalance().longValueExact()) {
|
if (customWinnerAmount > trade.getWallet().getBalance().longValueExact()) throw new RuntimeException("Winner payout is more than the trade wallet's balance");
|
||||||
throw new RuntimeException("Winner payout is more than the trade wallet's balance");
|
|
||||||
}
|
|
||||||
long loserAmount = tradeAmount.add(buyerSecurityDeposit).add(sellerSecurityDeposit).subtract(BigInteger.valueOf(customWinnerAmount)).longValueExact();
|
long loserAmount = tradeAmount.add(buyerSecurityDeposit).add(sellerSecurityDeposit).subtract(BigInteger.valueOf(customWinnerAmount)).longValueExact();
|
||||||
if (loserAmount < 0) {
|
if (loserAmount < 0) throw new RuntimeException("Loser payout cannot be negative");
|
||||||
throw new RuntimeException("Loser payout cannot be negative");
|
disputeResult.setBuyerPayoutAmountBeforeCost(BigInteger.valueOf(disputeResult.getWinner() == DisputeResult.Winner.BUYER ? customWinnerAmount : loserAmount));
|
||||||
}
|
disputeResult.setSellerPayoutAmountBeforeCost(BigInteger.valueOf(disputeResult.getWinner() == DisputeResult.Winner.BUYER ? loserAmount : customWinnerAmount));
|
||||||
disputeResult.setBuyerPayoutAmount(BigInteger.valueOf(disputeResult.getWinner() == DisputeResult.Winner.BUYER ? customWinnerAmount : loserAmount));
|
|
||||||
disputeResult.setSellerPayoutAmount(BigInteger.valueOf(disputeResult.getWinner() == DisputeResult.Winner.BUYER ? loserAmount : customWinnerAmount));
|
|
||||||
disputeResult.setSubtractFeeFrom(disputeResult.getWinner() == DisputeResult.Winner.BUYER ? SubtractFeeFrom.SELLER_ONLY : SubtractFeeFrom.BUYER_ONLY); // winner gets exact amount, loser pays mining fee
|
disputeResult.setSubtractFeeFrom(disputeResult.getWinner() == DisputeResult.Winner.BUYER ? SubtractFeeFrom.SELLER_ONLY : SubtractFeeFrom.BUYER_ONLY); // winner gets exact amount, loser pays mining fee
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,8 +252,8 @@ public class CoreDisputesService {
|
||||||
currencyCode,
|
currencyCode,
|
||||||
Res.get("disputeSummaryWindow.reason." + reason.name()),
|
Res.get("disputeSummaryWindow.reason." + reason.name()),
|
||||||
amount,
|
amount,
|
||||||
HavenoUtils.formatXmr(disputeResult.getBuyerPayoutAmount(), true),
|
HavenoUtils.formatXmr(disputeResult.getBuyerPayoutAmountBeforeCost(), true),
|
||||||
HavenoUtils.formatXmr(disputeResult.getSellerPayoutAmount(), true),
|
HavenoUtils.formatXmr(disputeResult.getSellerPayoutAmountBeforeCost(), true),
|
||||||
disputeResult.summaryNotesProperty().get()
|
disputeResult.summaryNotesProperty().get()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,12 @@ public class TradeInfo implements Payload {
|
||||||
private final long amount;
|
private final long amount;
|
||||||
private final long buyerSecurityDeposit;
|
private final long buyerSecurityDeposit;
|
||||||
private final long sellerSecurityDeposit;
|
private final long sellerSecurityDeposit;
|
||||||
|
private final long buyerDepositTxFee;
|
||||||
|
private final long sellerDepositTxFee;
|
||||||
|
private final long buyerPayoutTxFee;
|
||||||
|
private final long sellerPayoutTxFee;
|
||||||
|
private final long buyerPayoutAmount;
|
||||||
|
private final long sellerPayoutAmount;
|
||||||
private final String price;
|
private final String price;
|
||||||
private final String volume;
|
private final String volume;
|
||||||
private final String arbitratorNodeAddress;
|
private final String arbitratorNodeAddress;
|
||||||
|
@ -105,6 +111,12 @@ public class TradeInfo implements Payload {
|
||||||
this.amount = builder.getAmount();
|
this.amount = builder.getAmount();
|
||||||
this.buyerSecurityDeposit = builder.getBuyerSecurityDeposit();
|
this.buyerSecurityDeposit = builder.getBuyerSecurityDeposit();
|
||||||
this.sellerSecurityDeposit = builder.getSellerSecurityDeposit();
|
this.sellerSecurityDeposit = builder.getSellerSecurityDeposit();
|
||||||
|
this.buyerDepositTxFee = builder.getBuyerDepositTxFee();
|
||||||
|
this.sellerDepositTxFee = builder.getSellerDepositTxFee();
|
||||||
|
this.buyerPayoutTxFee = builder.getBuyerPayoutTxFee();
|
||||||
|
this.sellerPayoutTxFee = builder.getSellerPayoutTxFee();
|
||||||
|
this.buyerPayoutAmount = builder.getBuyerPayoutAmount();
|
||||||
|
this.sellerPayoutAmount = builder.getSellerPayoutAmount();
|
||||||
this.price = builder.getPrice();
|
this.price = builder.getPrice();
|
||||||
this.volume = builder.getVolume();
|
this.volume = builder.getVolume();
|
||||||
this.arbitratorNodeAddress = builder.getArbitratorNodeAddress();
|
this.arbitratorNodeAddress = builder.getArbitratorNodeAddress();
|
||||||
|
@ -161,6 +173,13 @@ public class TradeInfo implements Payload {
|
||||||
.withAmount(trade.getAmount().longValueExact())
|
.withAmount(trade.getAmount().longValueExact())
|
||||||
.withBuyerSecurityDeposit(trade.getBuyer().getSecurityDeposit() == null ? -1 : trade.getBuyer().getSecurityDeposit().longValueExact())
|
.withBuyerSecurityDeposit(trade.getBuyer().getSecurityDeposit() == null ? -1 : trade.getBuyer().getSecurityDeposit().longValueExact())
|
||||||
.withSellerSecurityDeposit(trade.getSeller().getSecurityDeposit() == null ? -1 : trade.getSeller().getSecurityDeposit().longValueExact())
|
.withSellerSecurityDeposit(trade.getSeller().getSecurityDeposit() == null ? -1 : trade.getSeller().getSecurityDeposit().longValueExact())
|
||||||
|
.withBuyerDepositTxFee(trade.getBuyer().getDepositTxFee() == null ? -1 : trade.getBuyer().getDepositTxFee().longValueExact())
|
||||||
|
.withSellerDepositTxFee(trade.getSeller().getDepositTxFee() == null ? -1 : trade.getSeller().getDepositTxFee().longValueExact())
|
||||||
|
.withBuyerPayoutTxFee(trade.getBuyer().getPayoutTxFee() == null ? -1 : trade.getBuyer().getPayoutTxFee().longValueExact())
|
||||||
|
.withSellerPayoutTxFee(trade.getSeller().getPayoutTxFee() == null ? -1 : trade.getSeller().getPayoutTxFee().longValueExact())
|
||||||
|
.withBuyerPayoutAmount(trade.getBuyer().getPayoutAmount() == null ? -1 : trade.getBuyer().getPayoutAmount().longValueExact())
|
||||||
|
.withSellerPayoutAmount(trade.getSeller().getPayoutAmount() == null ? -1 : trade.getSeller().getPayoutAmount().longValueExact())
|
||||||
|
.withTotalTxFee(trade.getTotalTxFee().longValueExact())
|
||||||
.withPrice(toPreciseTradePrice.apply(trade))
|
.withPrice(toPreciseTradePrice.apply(trade))
|
||||||
.withVolume(toRoundedVolume.apply(trade))
|
.withVolume(toRoundedVolume.apply(trade))
|
||||||
.withArbitratorNodeAddress(toArbitratorNodeAddress.apply(trade))
|
.withArbitratorNodeAddress(toArbitratorNodeAddress.apply(trade))
|
||||||
|
@ -204,6 +223,12 @@ public class TradeInfo implements Payload {
|
||||||
.setAmount(amount)
|
.setAmount(amount)
|
||||||
.setBuyerSecurityDeposit(buyerSecurityDeposit)
|
.setBuyerSecurityDeposit(buyerSecurityDeposit)
|
||||||
.setSellerSecurityDeposit(sellerSecurityDeposit)
|
.setSellerSecurityDeposit(sellerSecurityDeposit)
|
||||||
|
.setBuyerDepositTxFee(buyerDepositTxFee)
|
||||||
|
.setSellerDepositTxFee(sellerDepositTxFee)
|
||||||
|
.setBuyerPayoutTxFee(buyerPayoutTxFee)
|
||||||
|
.setSellerPayoutTxFee(sellerPayoutTxFee)
|
||||||
|
.setBuyerPayoutAmount(buyerPayoutAmount)
|
||||||
|
.setSellerPayoutAmount(sellerPayoutAmount)
|
||||||
.setPrice(price)
|
.setPrice(price)
|
||||||
.setTradeVolume(volume)
|
.setTradeVolume(volume)
|
||||||
.setArbitratorNodeAddress(arbitratorNodeAddress)
|
.setArbitratorNodeAddress(arbitratorNodeAddress)
|
||||||
|
@ -241,6 +266,12 @@ public class TradeInfo implements Payload {
|
||||||
.withAmount(proto.getAmount())
|
.withAmount(proto.getAmount())
|
||||||
.withBuyerSecurityDeposit(proto.getBuyerSecurityDeposit())
|
.withBuyerSecurityDeposit(proto.getBuyerSecurityDeposit())
|
||||||
.withSellerSecurityDeposit(proto.getSellerSecurityDeposit())
|
.withSellerSecurityDeposit(proto.getSellerSecurityDeposit())
|
||||||
|
.withBuyerDepositTxFee(proto.getBuyerDepositTxFee())
|
||||||
|
.withSellerDepositTxFee(proto.getSellerDepositTxFee())
|
||||||
|
.withBuyerPayoutTxFee(proto.getBuyerPayoutTxFee())
|
||||||
|
.withSellerPayoutTxFee(proto.getSellerPayoutTxFee())
|
||||||
|
.withBuyerPayoutAmount(proto.getBuyerPayoutAmount())
|
||||||
|
.withSellerPayoutAmount(proto.getSellerPayoutAmount())
|
||||||
.withPrice(proto.getPrice())
|
.withPrice(proto.getPrice())
|
||||||
.withVolume(proto.getTradeVolume())
|
.withVolume(proto.getTradeVolume())
|
||||||
.withPeriodState(proto.getPeriodState())
|
.withPeriodState(proto.getPeriodState())
|
||||||
|
@ -278,6 +309,12 @@ public class TradeInfo implements Payload {
|
||||||
", amount='" + amount + '\'' + "\n" +
|
", amount='" + amount + '\'' + "\n" +
|
||||||
", buyerSecurityDeposit='" + buyerSecurityDeposit + '\'' + "\n" +
|
", buyerSecurityDeposit='" + buyerSecurityDeposit + '\'' + "\n" +
|
||||||
", sellerSecurityDeposit='" + sellerSecurityDeposit + '\'' + "\n" +
|
", sellerSecurityDeposit='" + sellerSecurityDeposit + '\'' + "\n" +
|
||||||
|
", buyerDepositTxFee='" + buyerDepositTxFee + '\'' + "\n" +
|
||||||
|
", sellerDepositTxFee='" + sellerDepositTxFee + '\'' + "\n" +
|
||||||
|
", buyerPayoutTxFee='" + buyerPayoutTxFee + '\'' + "\n" +
|
||||||
|
", sellerPayoutTxFee='" + sellerPayoutTxFee + '\'' + "\n" +
|
||||||
|
", buyerPayoutAmount='" + buyerPayoutAmount + '\'' + "\n" +
|
||||||
|
", sellerPayoutAmount='" + sellerPayoutAmount + '\'' + "\n" +
|
||||||
", price='" + price + '\'' + "\n" +
|
", price='" + price + '\'' + "\n" +
|
||||||
", arbitratorNodeAddress='" + arbitratorNodeAddress + '\'' + "\n" +
|
", arbitratorNodeAddress='" + arbitratorNodeAddress + '\'' + "\n" +
|
||||||
", tradePeerNodeAddress='" + tradePeerNodeAddress + '\'' + "\n" +
|
", tradePeerNodeAddress='" + tradePeerNodeAddress + '\'' + "\n" +
|
||||||
|
|
|
@ -41,6 +41,12 @@ public final class TradeInfoV1Builder {
|
||||||
private long takerFee;
|
private long takerFee;
|
||||||
private long buyerSecurityDeposit;
|
private long buyerSecurityDeposit;
|
||||||
private long sellerSecurityDeposit;
|
private long sellerSecurityDeposit;
|
||||||
|
private long buyerDepositTxFee;
|
||||||
|
private long sellerDepositTxFee;
|
||||||
|
private long buyerPayoutTxFee;
|
||||||
|
private long sellerPayoutTxFee;
|
||||||
|
private long buyerPayoutAmount;
|
||||||
|
private long sellerPayoutAmount;
|
||||||
private String makerDepositTxId;
|
private String makerDepositTxId;
|
||||||
private String takerDepositTxId;
|
private String takerDepositTxId;
|
||||||
private String payoutTxId;
|
private String payoutTxId;
|
||||||
|
@ -117,6 +123,36 @@ public final class TradeInfoV1Builder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TradeInfoV1Builder withBuyerDepositTxFee(long buyerDepositTxFee) {
|
||||||
|
this.buyerDepositTxFee = buyerDepositTxFee;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TradeInfoV1Builder withSellerDepositTxFee(long sellerDepositTxFee) {
|
||||||
|
this.sellerDepositTxFee = sellerDepositTxFee;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TradeInfoV1Builder withBuyerPayoutTxFee(long buyerPayoutTxFee) {
|
||||||
|
this.buyerPayoutTxFee = buyerPayoutTxFee;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TradeInfoV1Builder withSellerPayoutTxFee(long sellerPayoutTxFee) {
|
||||||
|
this.sellerPayoutTxFee = sellerPayoutTxFee;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TradeInfoV1Builder withBuyerPayoutAmount(long buyerPayoutAmount) {
|
||||||
|
this.buyerPayoutAmount = buyerPayoutAmount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TradeInfoV1Builder withSellerPayoutAmount(long sellerPayoutAmount) {
|
||||||
|
this.sellerPayoutAmount = sellerPayoutAmount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public TradeInfoV1Builder withMakerDepositTxId(String makerDepositTxId) {
|
public TradeInfoV1Builder withMakerDepositTxId(String makerDepositTxId) {
|
||||||
this.makerDepositTxId = makerDepositTxId;
|
this.makerDepositTxId = makerDepositTxId;
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -69,7 +69,6 @@ import java.math.BigInteger;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -729,23 +728,21 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
dispute.addAndPersistChatMessage(chatMessage);
|
dispute.addAndPersistChatMessage(chatMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create dispute payout tx if not published
|
// create dispute payout tx once per trader if we have their updated multisig hex
|
||||||
TradePeer receiver = trade.getTradePeer(dispute.getTraderPubKeyRing());
|
TradePeer receiver = trade.getTradePeer(dispute.getTraderPubKeyRing());
|
||||||
if (!trade.isPayoutPublished() && receiver.getUpdatedMultisigHex() != null) {
|
if (!trade.isPayoutPublished() && receiver.getUpdatedMultisigHex() != null && receiver.getUnsignedPayoutTxHex() == null) {
|
||||||
trade.getProcessModel().setUnsignedPayoutTx(createDisputePayoutTx(trade, dispute.getContract(), disputeResult, false)); // can be null if we don't have receiver's multisig hex
|
createDisputePayoutTx(trade, dispute.getContract(), disputeResult, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create dispute closed message
|
// create dispute closed message
|
||||||
MoneroTxWallet unsignedPayoutTx = receiver.getUpdatedMultisigHex() == null ? null : trade.getProcessModel().getUnsignedPayoutTx();
|
|
||||||
String unsignedPayoutTxHex = unsignedPayoutTx == null ? null : unsignedPayoutTx.getTxSet().getMultisigTxHex();
|
|
||||||
TradePeer receiverPeer = receiver == trade.getBuyer() ? trade.getSeller() : trade.getBuyer();
|
TradePeer receiverPeer = receiver == trade.getBuyer() ? trade.getSeller() : trade.getBuyer();
|
||||||
boolean deferPublishPayout = !exists && unsignedPayoutTxHex != null && receiverPeer.getUpdatedMultisigHex() != null && trade.getDisputeState().ordinal() >= Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG.ordinal();
|
boolean deferPublishPayout = !exists && receiver.getUnsignedPayoutTxHex() != null && receiverPeer.getUpdatedMultisigHex() != null && trade.getDisputeState().ordinal() >= Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG.ordinal();
|
||||||
DisputeClosedMessage disputeClosedMessage = new DisputeClosedMessage(disputeResult,
|
DisputeClosedMessage disputeClosedMessage = new DisputeClosedMessage(disputeResult,
|
||||||
p2PService.getAddress(),
|
p2PService.getAddress(),
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
getSupportType(),
|
getSupportType(),
|
||||||
trade.getSelf().getUpdatedMultisigHex(),
|
trade.getSelf().getUpdatedMultisigHex(),
|
||||||
unsignedPayoutTxHex, // include dispute payout tx if arbitrator has their updated multisig info
|
receiver.getUnsignedPayoutTxHex(), // include dispute payout tx if arbitrator has their updated multisig info
|
||||||
deferPublishPayout); // instruct trader to defer publishing payout tx because peer is expected to publish imminently
|
deferPublishPayout); // instruct trader to defer publishing payout tx because peer is expected to publish imminently
|
||||||
|
|
||||||
// send dispute closed message
|
// send dispute closed message
|
||||||
|
@ -812,12 +809,6 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// save state
|
|
||||||
if (unsignedPayoutTx != null) {
|
|
||||||
trade.setPayoutTx(unsignedPayoutTx);
|
|
||||||
trade.setPayoutTxHex(unsignedPayoutTx.getTxSet().getMultisigTxHex());
|
|
||||||
}
|
|
||||||
trade.advanceDisputeState(Trade.DisputeState.ARBITRATOR_SENT_DISPUTE_CLOSED_MSG);
|
trade.advanceDisputeState(Trade.DisputeState.ARBITRATOR_SENT_DISPUTE_CLOSED_MSG);
|
||||||
requestPersistence();
|
requestPersistence();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -829,7 +820,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
// Utils
|
// Utils
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public MoneroTxWallet createDisputePayoutTx(Trade trade, Contract contract, DisputeResult disputeResult, boolean skipMultisigImport) {
|
public MoneroTxWallet createDisputePayoutTx(Trade trade, Contract contract, DisputeResult disputeResult, boolean updateState) {
|
||||||
|
|
||||||
// import multisig hex
|
// import multisig hex
|
||||||
trade.importMultisigHex();
|
trade.importMultisigHex();
|
||||||
|
@ -848,42 +839,34 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
// trade wallet must be synced
|
// 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 " + trade.getId());
|
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 " + trade.getId());
|
||||||
|
|
||||||
// collect winner and loser payout address and amounts
|
// check amounts
|
||||||
String winnerPayoutAddress = disputeResult.getWinner() == Winner.BUYER ?
|
if (disputeResult.getBuyerPayoutAmountBeforeCost().compareTo(BigInteger.ZERO) < 0) throw new RuntimeException("Buyer payout cannot be negative");
|
||||||
(contract.isBuyerMakerAndSellerTaker() ? contract.getMakerPayoutAddressString() : contract.getTakerPayoutAddressString()) :
|
if (disputeResult.getSellerPayoutAmountBeforeCost().compareTo(BigInteger.ZERO) < 0) throw new RuntimeException("Seller payout cannot be negative");
|
||||||
(contract.isBuyerMakerAndSellerTaker() ? contract.getTakerPayoutAddressString() : contract.getMakerPayoutAddressString());
|
if (disputeResult.getBuyerPayoutAmountBeforeCost().add(disputeResult.getSellerPayoutAmountBeforeCost()).compareTo(trade.getWallet().getUnlockedBalance()) > 0) {
|
||||||
String loserPayoutAddress = winnerPayoutAddress.equals(contract.getMakerPayoutAddressString()) ? contract.getTakerPayoutAddressString() : contract.getMakerPayoutAddressString();
|
throw new RuntimeException("The payout amounts are more than the wallet's unlocked balance, unlocked balance=" + trade.getWallet().getUnlockedBalance() + " vs " + disputeResult.getBuyerPayoutAmountBeforeCost() + " + " + disputeResult.getSellerPayoutAmountBeforeCost() + " = " + (disputeResult.getBuyerPayoutAmountBeforeCost().add(disputeResult.getSellerPayoutAmountBeforeCost())));
|
||||||
BigInteger winnerPayoutAmount = disputeResult.getWinner() == Winner.BUYER ? disputeResult.getBuyerPayoutAmount() : disputeResult.getSellerPayoutAmount();
|
|
||||||
BigInteger loserPayoutAmount = disputeResult.getWinner() == Winner.BUYER ? disputeResult.getSellerPayoutAmount() : disputeResult.getBuyerPayoutAmount();
|
|
||||||
|
|
||||||
// check sufficient balance
|
|
||||||
if (winnerPayoutAmount.compareTo(BigInteger.ZERO) < 0) throw new RuntimeException("Winner payout cannot be negative");
|
|
||||||
if (loserPayoutAmount.compareTo(BigInteger.ZERO) < 0) throw new RuntimeException("Loser payout cannot be negative");
|
|
||||||
if (winnerPayoutAmount.add(loserPayoutAmount).compareTo(trade.getWallet().getUnlockedBalance()) > 0) {
|
|
||||||
throw new RuntimeException("The payout amounts are more than the wallet's unlocked balance, unlocked balance=" + trade.getWallet().getUnlockedBalance() + " vs " + winnerPayoutAmount + " + " + loserPayoutAmount + " = " + (winnerPayoutAmount.add(loserPayoutAmount)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add any loss of precision to winner payout
|
|
||||||
winnerPayoutAmount = winnerPayoutAmount.add(trade.getWallet().getUnlockedBalance().subtract(winnerPayoutAmount.add(loserPayoutAmount)));
|
|
||||||
|
|
||||||
// create dispute payout tx config
|
// create dispute payout tx config
|
||||||
MoneroTxConfig txConfig = new MoneroTxConfig().setAccountIndex(0);
|
MoneroTxConfig txConfig = new MoneroTxConfig().setAccountIndex(0);
|
||||||
|
String buyerPayoutAddress = contract.isBuyerMakerAndSellerTaker() ? contract.getMakerPayoutAddressString() : contract.getTakerPayoutAddressString();
|
||||||
|
String sellerPayoutAddress = contract.isBuyerMakerAndSellerTaker() ? contract.getTakerPayoutAddressString() : contract.getMakerPayoutAddressString();
|
||||||
txConfig.setPriority(XmrWalletService.PROTOCOL_FEE_PRIORITY);
|
txConfig.setPriority(XmrWalletService.PROTOCOL_FEE_PRIORITY);
|
||||||
if (winnerPayoutAmount.compareTo(BigInteger.ZERO) > 0) txConfig.addDestination(winnerPayoutAddress, winnerPayoutAmount);
|
if (disputeResult.getBuyerPayoutAmountBeforeCost().compareTo(BigInteger.ZERO) > 0) txConfig.addDestination(buyerPayoutAddress, disputeResult.getBuyerPayoutAmountBeforeCost());
|
||||||
if (loserPayoutAmount.compareTo(BigInteger.ZERO) > 0) txConfig.addDestination(loserPayoutAddress, loserPayoutAmount);
|
if (disputeResult.getSellerPayoutAmountBeforeCost().compareTo(BigInteger.ZERO) > 0) txConfig.addDestination(sellerPayoutAddress, disputeResult.getSellerPayoutAmountBeforeCost());
|
||||||
|
|
||||||
// configure who pays mining fee
|
// configure who pays mining fee
|
||||||
|
BigInteger loserPayoutAmount = disputeResult.getWinner() == Winner.BUYER ? disputeResult.getSellerPayoutAmountBeforeCost() : disputeResult.getBuyerPayoutAmountBeforeCost();
|
||||||
if (loserPayoutAmount.equals(BigInteger.ZERO)) txConfig.setSubtractFeeFrom(0); // winner pays fee if loser gets 0
|
if (loserPayoutAmount.equals(BigInteger.ZERO)) txConfig.setSubtractFeeFrom(0); // winner pays fee if loser gets 0
|
||||||
else {
|
else {
|
||||||
switch (disputeResult.getSubtractFeeFrom()) {
|
switch (disputeResult.getSubtractFeeFrom()) {
|
||||||
case BUYER_AND_SELLER:
|
case BUYER_AND_SELLER:
|
||||||
txConfig.setSubtractFeeFrom(Arrays.asList(0, 1));
|
txConfig.setSubtractFeeFrom(0, 1);
|
||||||
break;
|
break;
|
||||||
case BUYER_ONLY:
|
case BUYER_ONLY:
|
||||||
txConfig.setSubtractFeeFrom(disputeResult.getWinner() == Winner.BUYER ? 0 : 1);
|
txConfig.setSubtractFeeFrom(0);
|
||||||
break;
|
break;
|
||||||
case SELLER_ONLY:
|
case SELLER_ONLY:
|
||||||
txConfig.setSubtractFeeFrom(disputeResult.getWinner() == Winner.SELLER ? 0 : 1);
|
txConfig.setSubtractFeeFrom(1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -897,8 +880,14 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
throw new RuntimeException("Loser payout is too small to cover the mining fee");
|
throw new RuntimeException("Loser payout is too small to cover the mining fee");
|
||||||
}
|
}
|
||||||
|
|
||||||
// save updated multisig hex
|
// update trade state
|
||||||
trade.getSelf().setUpdatedMultisigHex(trade.getWallet().exportMultisigHex());
|
if (updateState) {
|
||||||
|
trade.getProcessModel().setUnsignedPayoutTx(payoutTx);
|
||||||
|
trade.setPayoutTx(payoutTx);
|
||||||
|
trade.setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
||||||
|
if (trade.getBuyer().getUpdatedMultisigHex() != null && trade.getBuyer().getUnsignedPayoutTxHex() == null) trade.getBuyer().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
||||||
|
if (trade.getSeller().getUpdatedMultisigHex() != null && trade.getSeller().getUnsignedPayoutTxHex() == null) trade.getSeller().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
||||||
|
}
|
||||||
return payoutTx;
|
return payoutTx;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
trade.syncAndPollWallet();
|
trade.syncAndPollWallet();
|
||||||
|
|
|
@ -86,8 +86,8 @@ public final class DisputeResult implements NetworkPayload {
|
||||||
@Setter
|
@Setter
|
||||||
@Nullable
|
@Nullable
|
||||||
private byte[] arbitratorSignature;
|
private byte[] arbitratorSignature;
|
||||||
private long buyerPayoutAmount;
|
private long buyerPayoutAmountBeforeCost;
|
||||||
private long sellerPayoutAmount;
|
private long sellerPayoutAmountBeforeCost;
|
||||||
@Setter
|
@Setter
|
||||||
@Nullable
|
@Nullable
|
||||||
private byte[] arbitratorPubKey;
|
private byte[] arbitratorPubKey;
|
||||||
|
@ -109,8 +109,8 @@ public final class DisputeResult implements NetworkPayload {
|
||||||
String summaryNotes,
|
String summaryNotes,
|
||||||
@Nullable ChatMessage chatMessage,
|
@Nullable ChatMessage chatMessage,
|
||||||
@Nullable byte[] arbitratorSignature,
|
@Nullable byte[] arbitratorSignature,
|
||||||
long buyerPayoutAmount,
|
long buyerPayoutAmountBeforeCost,
|
||||||
long sellerPayoutAmount,
|
long sellerPayoutAmountBeforeCost,
|
||||||
@Nullable byte[] arbitratorPubKey,
|
@Nullable byte[] arbitratorPubKey,
|
||||||
long closeDate) {
|
long closeDate) {
|
||||||
this.tradeId = tradeId;
|
this.tradeId = tradeId;
|
||||||
|
@ -124,8 +124,8 @@ public final class DisputeResult implements NetworkPayload {
|
||||||
this.summaryNotesProperty.set(summaryNotes);
|
this.summaryNotesProperty.set(summaryNotes);
|
||||||
this.chatMessage = chatMessage;
|
this.chatMessage = chatMessage;
|
||||||
this.arbitratorSignature = arbitratorSignature;
|
this.arbitratorSignature = arbitratorSignature;
|
||||||
this.buyerPayoutAmount = buyerPayoutAmount;
|
this.buyerPayoutAmountBeforeCost = buyerPayoutAmountBeforeCost;
|
||||||
this.sellerPayoutAmount = sellerPayoutAmount;
|
this.sellerPayoutAmountBeforeCost = sellerPayoutAmountBeforeCost;
|
||||||
this.arbitratorPubKey = arbitratorPubKey;
|
this.arbitratorPubKey = arbitratorPubKey;
|
||||||
this.closeDate = closeDate;
|
this.closeDate = closeDate;
|
||||||
}
|
}
|
||||||
|
@ -147,8 +147,8 @@ public final class DisputeResult implements NetworkPayload {
|
||||||
proto.getSummaryNotes(),
|
proto.getSummaryNotes(),
|
||||||
proto.getChatMessage() == null ? null : ChatMessage.fromPayloadProto(proto.getChatMessage()),
|
proto.getChatMessage() == null ? null : ChatMessage.fromPayloadProto(proto.getChatMessage()),
|
||||||
proto.getArbitratorSignature().toByteArray(),
|
proto.getArbitratorSignature().toByteArray(),
|
||||||
proto.getBuyerPayoutAmount(),
|
proto.getBuyerPayoutAmountBeforeCost(),
|
||||||
proto.getSellerPayoutAmount(),
|
proto.getSellerPayoutAmountBeforeCost(),
|
||||||
proto.getArbitratorPubKey().toByteArray(),
|
proto.getArbitratorPubKey().toByteArray(),
|
||||||
proto.getCloseDate());
|
proto.getCloseDate());
|
||||||
}
|
}
|
||||||
|
@ -163,8 +163,8 @@ public final class DisputeResult implements NetworkPayload {
|
||||||
.setIdVerification(idVerificationProperty.get())
|
.setIdVerification(idVerificationProperty.get())
|
||||||
.setScreenCast(screenCastProperty.get())
|
.setScreenCast(screenCastProperty.get())
|
||||||
.setSummaryNotes(summaryNotesProperty.get())
|
.setSummaryNotes(summaryNotesProperty.get())
|
||||||
.setBuyerPayoutAmount(buyerPayoutAmount)
|
.setBuyerPayoutAmountBeforeCost(buyerPayoutAmountBeforeCost)
|
||||||
.setSellerPayoutAmount(sellerPayoutAmount)
|
.setSellerPayoutAmountBeforeCost(sellerPayoutAmountBeforeCost)
|
||||||
.setCloseDate(closeDate);
|
.setCloseDate(closeDate);
|
||||||
|
|
||||||
Optional.ofNullable(arbitratorSignature).ifPresent(arbitratorSignature -> builder.setArbitratorSignature(ByteString.copyFrom(arbitratorSignature)));
|
Optional.ofNullable(arbitratorSignature).ifPresent(arbitratorSignature -> builder.setArbitratorSignature(ByteString.copyFrom(arbitratorSignature)));
|
||||||
|
@ -213,22 +213,22 @@ public final class DisputeResult implements NetworkPayload {
|
||||||
return summaryNotesProperty;
|
return summaryNotesProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBuyerPayoutAmount(BigInteger buyerPayoutAmount) {
|
public void setBuyerPayoutAmountBeforeCost(BigInteger buyerPayoutAmountBeforeCost) {
|
||||||
if (buyerPayoutAmount.compareTo(BigInteger.ZERO) < 0) throw new IllegalArgumentException("buyerPayoutAmount cannot be negative");
|
if (buyerPayoutAmountBeforeCost.compareTo(BigInteger.ZERO) < 0) throw new IllegalArgumentException("buyerPayoutAmountBeforeCost cannot be negative");
|
||||||
this.buyerPayoutAmount = buyerPayoutAmount.longValueExact();
|
this.buyerPayoutAmountBeforeCost = buyerPayoutAmountBeforeCost.longValueExact();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigInteger getBuyerPayoutAmount() {
|
public BigInteger getBuyerPayoutAmountBeforeCost() {
|
||||||
return BigInteger.valueOf(buyerPayoutAmount);
|
return BigInteger.valueOf(buyerPayoutAmountBeforeCost);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSellerPayoutAmount(BigInteger sellerPayoutAmount) {
|
public void setSellerPayoutAmountBeforeCost(BigInteger sellerPayoutAmountBeforeCost) {
|
||||||
if (sellerPayoutAmount.compareTo(BigInteger.ZERO) < 0) throw new IllegalArgumentException("sellerPayoutAmount cannot be negative");
|
if (sellerPayoutAmountBeforeCost.compareTo(BigInteger.ZERO) < 0) throw new IllegalArgumentException("sellerPayoutAmountBeforeCost cannot be negative");
|
||||||
this.sellerPayoutAmount = sellerPayoutAmount.longValueExact();
|
this.sellerPayoutAmountBeforeCost = sellerPayoutAmountBeforeCost.longValueExact();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigInteger getSellerPayoutAmount() {
|
public BigInteger getSellerPayoutAmountBeforeCost() {
|
||||||
return BigInteger.valueOf(sellerPayoutAmount);
|
return BigInteger.valueOf(sellerPayoutAmountBeforeCost);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCloseDate(Date closeDate) {
|
public void setCloseDate(Date closeDate) {
|
||||||
|
@ -253,8 +253,8 @@ public final class DisputeResult implements NetworkPayload {
|
||||||
",\n summaryNotesProperty=" + summaryNotesProperty +
|
",\n summaryNotesProperty=" + summaryNotesProperty +
|
||||||
",\n chatMessage=" + chatMessage +
|
",\n chatMessage=" + chatMessage +
|
||||||
",\n arbitratorSignature=" + Utilities.bytesAsHexString(arbitratorSignature) +
|
",\n arbitratorSignature=" + Utilities.bytesAsHexString(arbitratorSignature) +
|
||||||
",\n buyerPayoutAmount=" + buyerPayoutAmount +
|
",\n buyerPayoutAmountBeforeCost=" + buyerPayoutAmountBeforeCost +
|
||||||
",\n sellerPayoutAmount=" + sellerPayoutAmount +
|
",\n sellerPayoutAmountBeforeCost=" + sellerPayoutAmountBeforeCost +
|
||||||
",\n arbitratorPubKey=" + Utilities.bytesAsHexString(arbitratorPubKey) +
|
",\n arbitratorPubKey=" + Utilities.bytesAsHexString(arbitratorPubKey) +
|
||||||
",\n closeDate=" + closeDate +
|
",\n closeDate=" + closeDate +
|
||||||
"\n}";
|
"\n}";
|
||||||
|
|
|
@ -380,42 +380,22 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
||||||
if (!arbitratorSignedPayoutTx.getOutputSum().equals(destinationSum.add(arbitratorSignedPayoutTx.getChangeAmount()))) throw new RuntimeException("Sum of outputs != destination amounts + change amount");
|
if (!arbitratorSignedPayoutTx.getOutputSum().equals(destinationSum.add(arbitratorSignedPayoutTx.getChangeAmount()))) throw new RuntimeException("Sum of outputs != destination amounts + change amount");
|
||||||
|
|
||||||
// get actual payout amounts
|
// get actual payout amounts
|
||||||
BigInteger actualWinnerAmount = disputeResult.getWinner() == Winner.BUYER ? buyerPayoutDestination.getAmount() : sellerPayoutDestination.getAmount();
|
BigInteger actualBuyerAmount = buyerPayoutDestination == null ? BigInteger.ZERO : buyerPayoutDestination.getAmount();
|
||||||
BigInteger actualLoserAmount = numDestinations == 1 ? BigInteger.ZERO : disputeResult.getWinner() == Winner.BUYER ? sellerPayoutDestination.getAmount() : buyerPayoutDestination.getAmount();
|
BigInteger actualSellerAmount = sellerPayoutDestination == null ? BigInteger.ZERO : sellerPayoutDestination.getAmount();
|
||||||
|
|
||||||
// verify payouts sum to unlocked balance within loss of precision due to conversion to centineros
|
// verify payouts sum to unlocked balance within loss of precision due to conversion to centineros
|
||||||
BigInteger txCost = arbitratorSignedPayoutTx.getFee().add(arbitratorSignedPayoutTx.getChangeAmount()); // fee + lost dust change
|
BigInteger txCost = arbitratorSignedPayoutTx.getFee().add(arbitratorSignedPayoutTx.getChangeAmount()); // cost = fee + lost dust change
|
||||||
if (trade.getWallet().getUnlockedBalance().subtract(actualWinnerAmount.add(actualLoserAmount).add(txCost)).compareTo(BigInteger.valueOf(0)) > 0) {
|
if (!arbitratorSignedPayoutTx.getChangeAmount().equals(BigInteger.ZERO)) log.warn("Dust left in multisig wallet for {} {}: {}", getClass().getSimpleName(), trade.getId(), arbitratorSignedPayoutTx.getChangeAmount());
|
||||||
throw new RuntimeException("The dispute payout amounts do not sum to the wallet's unlocked balance while verifying the dispute payout tx, unlocked balance=" + trade.getWallet().getUnlockedBalance() + " vs sum payout amount=" + actualWinnerAmount.add(actualLoserAmount) + ", winner payout=" + actualWinnerAmount + ", loser payout=" + actualLoserAmount);
|
if (trade.getWallet().getUnlockedBalance().subtract(actualBuyerAmount.add(actualSellerAmount).add(txCost)).compareTo(BigInteger.valueOf(0)) > 0) {
|
||||||
|
throw new RuntimeException("The dispute payout amounts do not sum to the wallet's unlocked balance while verifying the dispute payout tx, unlocked balance=" + trade.getWallet().getUnlockedBalance() + " vs sum payout amount=" + actualBuyerAmount.add(actualSellerAmount) + ", buyer payout=" + actualBuyerAmount + ", seller payout=" + actualSellerAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get expected payout amounts
|
// verify payout amounts
|
||||||
BigInteger expectedWinnerAmount = disputeResult.getWinner() == Winner.BUYER ? disputeResult.getBuyerPayoutAmount() : disputeResult.getSellerPayoutAmount();
|
BigInteger[] buyerSellerPayoutTxCost = getBuyerSellerPayoutTxCost(disputeResult, txCost);
|
||||||
BigInteger expectedLoserAmount = disputeResult.getWinner() == Winner.BUYER ? disputeResult.getSellerPayoutAmount() : disputeResult.getBuyerPayoutAmount();
|
BigInteger expectedBuyerAmount = disputeResult.getBuyerPayoutAmountBeforeCost().subtract(buyerSellerPayoutTxCost[0]);
|
||||||
|
BigInteger expectedSellerAmount = disputeResult.getSellerPayoutAmountBeforeCost().subtract(buyerSellerPayoutTxCost[1]);
|
||||||
// subtract mining fee from expected payouts
|
if (!expectedBuyerAmount.equals(actualBuyerAmount)) throw new RuntimeException("Unexpected buyer payout: " + expectedBuyerAmount + " vs " + actualBuyerAmount);
|
||||||
if (expectedLoserAmount.equals(BigInteger.ZERO)) expectedWinnerAmount = expectedWinnerAmount.subtract(txCost); // winner pays fee if loser gets 0
|
if (!expectedSellerAmount.equals(actualSellerAmount)) throw new RuntimeException("Unexpected seller payout: " + expectedSellerAmount + " vs " + actualSellerAmount);
|
||||||
else {
|
|
||||||
switch (disputeResult.getSubtractFeeFrom()) {
|
|
||||||
case BUYER_AND_SELLER:
|
|
||||||
BigInteger txCostSplit = txCost.divide(BigInteger.valueOf(2));
|
|
||||||
expectedWinnerAmount = expectedWinnerAmount.subtract(txCostSplit);
|
|
||||||
expectedLoserAmount = expectedLoserAmount.subtract(txCostSplit);
|
|
||||||
break;
|
|
||||||
case BUYER_ONLY:
|
|
||||||
expectedWinnerAmount = expectedWinnerAmount.subtract(disputeResult.getWinner() == Winner.BUYER ? txCost : BigInteger.ZERO);
|
|
||||||
expectedLoserAmount = expectedLoserAmount.subtract(disputeResult.getWinner() == Winner.BUYER ? BigInteger.ZERO : txCost);
|
|
||||||
break;
|
|
||||||
case SELLER_ONLY:
|
|
||||||
expectedWinnerAmount = expectedWinnerAmount.subtract(disputeResult.getWinner() == Winner.BUYER ? BigInteger.ZERO : txCost);
|
|
||||||
expectedLoserAmount = expectedLoserAmount.subtract(disputeResult.getWinner() == Winner.BUYER ? txCost : BigInteger.ZERO);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify winner and loser payout amounts
|
|
||||||
if (!expectedWinnerAmount.equals(actualWinnerAmount)) throw new RuntimeException("Unexpected winner payout: " + expectedWinnerAmount + " vs " + actualWinnerAmount);
|
|
||||||
if (!expectedLoserAmount.equals(actualLoserAmount)) throw new RuntimeException("Unexpected loser payout: " + expectedLoserAmount + " vs " + actualLoserAmount);
|
|
||||||
|
|
||||||
// check wallet's daemon connection
|
// check wallet's daemon connection
|
||||||
trade.checkDaemonConnection();
|
trade.checkDaemonConnection();
|
||||||
|
@ -431,7 +411,8 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
||||||
boolean signed = trade.getPayoutTxHex() != null && !nonSignedDisputePayoutTxHexes.contains(trade.getPayoutTxHex());
|
boolean signed = trade.getPayoutTxHex() != null && !nonSignedDisputePayoutTxHexes.contains(trade.getPayoutTxHex());
|
||||||
|
|
||||||
// sign arbitrator-signed payout tx
|
// sign arbitrator-signed payout tx
|
||||||
if (!signed) {
|
if (signed) disputeTxSet.setMultisigTxHex(trade.getPayoutTxHex());
|
||||||
|
else {
|
||||||
MoneroMultisigSignResult result = multisigWallet.signMultisigTxHex(unsignedPayoutTxHex);
|
MoneroMultisigSignResult result = multisigWallet.signMultisigTxHex(unsignedPayoutTxHex);
|
||||||
if (result.getSignedMultisigTxHex() == null) throw new RuntimeException("Error signing arbitrator-signed payout tx");
|
if (result.getSignedMultisigTxHex() == null) throw new RuntimeException("Error signing arbitrator-signed payout tx");
|
||||||
String signedMultisigTxHex = result.getSignedMultisigTxHex();
|
String signedMultisigTxHex = result.getSignedMultisigTxHex();
|
||||||
|
@ -443,8 +424,9 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
||||||
// TODO (monero-project): creating tx will require exchanging updated multisig hex if message needs reprocessed. provide weight with describe_transfer so fee can be estimated?
|
// TODO (monero-project): creating tx will require exchanging updated multisig hex if message needs reprocessed. provide weight with describe_transfer so fee can be estimated?
|
||||||
MoneroTxWallet feeEstimateTx = null;
|
MoneroTxWallet feeEstimateTx = null;
|
||||||
try {
|
try {
|
||||||
feeEstimateTx = createDisputePayoutTx(trade, dispute.getContract(), disputeResult, true);
|
feeEstimateTx = createDisputePayoutTx(trade, dispute.getContract(), disputeResult, false);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
log.warn("Could not recreate dispute payout tx to verify fee: " + e.getMessage());
|
log.warn("Could not recreate dispute payout tx to verify fee: " + e.getMessage());
|
||||||
}
|
}
|
||||||
if (feeEstimateTx != null) {
|
if (feeEstimateTx != null) {
|
||||||
|
@ -453,8 +435,6 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
||||||
if (feeDiff > XmrWalletService.MINER_FEE_TOLERANCE) throw new IllegalArgumentException("Miner fee is not within " + (XmrWalletService.MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + arbitratorSignedPayoutTx.getFee());
|
if (feeDiff > XmrWalletService.MINER_FEE_TOLERANCE) throw new IllegalArgumentException("Miner fee is not within " + (XmrWalletService.MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + arbitratorSignedPayoutTx.getFee());
|
||||||
log.info("Payout tx fee {} is within tolerance, diff %={}", arbitratorSignedPayoutTx.getFee(), feeDiff);
|
log.info("Payout tx fee {} is within tolerance, diff %={}", arbitratorSignedPayoutTx.getFee(), feeDiff);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
disputeTxSet.setMultisigTxHex(trade.getPayoutTxHex());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// submit fully signed payout tx to the network
|
// submit fully signed payout tx to the network
|
||||||
|
@ -468,4 +448,26 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
||||||
dispute.setDisputePayoutTxId(disputeTxSet.getTxs().get(0).getHash());
|
dispute.setDisputePayoutTxId(disputeTxSet.getTxs().get(0).getHash());
|
||||||
return disputeTxSet;
|
return disputeTxSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BigInteger[] getBuyerSellerPayoutTxCost(DisputeResult disputeResult, BigInteger payoutTxCost) {
|
||||||
|
boolean isBuyerWinner = disputeResult.getWinner() == Winner.BUYER;
|
||||||
|
BigInteger loserAmount = isBuyerWinner ? disputeResult.getSellerPayoutAmountBeforeCost() : disputeResult.getBuyerPayoutAmountBeforeCost();
|
||||||
|
if (loserAmount.equals(BigInteger.valueOf(0))) {
|
||||||
|
BigInteger buyerPayoutTxFee = isBuyerWinner ? payoutTxCost : BigInteger.ZERO;
|
||||||
|
BigInteger sellerPayoutTxFee = isBuyerWinner ? BigInteger.ZERO : payoutTxCost;
|
||||||
|
return new BigInteger[] { buyerPayoutTxFee, sellerPayoutTxFee };
|
||||||
|
} else {
|
||||||
|
switch (disputeResult.getSubtractFeeFrom()) {
|
||||||
|
case BUYER_AND_SELLER:
|
||||||
|
BigInteger payoutTxFeeSplit = payoutTxCost.divide(BigInteger.valueOf(2));
|
||||||
|
return new BigInteger[] { payoutTxFeeSplit, payoutTxFeeSplit };
|
||||||
|
case BUYER_ONLY:
|
||||||
|
return new BigInteger[] { payoutTxCost, BigInteger.ZERO };
|
||||||
|
case SELLER_ONLY:
|
||||||
|
return new BigInteger[] { BigInteger.ZERO, payoutTxCost };
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unsupported subtract fee from: " + disputeResult.getSubtractFeeFrom());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,8 +188,8 @@ public final class MediationManager extends DisputeManager<MediationDisputeList>
|
||||||
Trade trade = tradeOptional.get();
|
Trade trade = tradeOptional.get();
|
||||||
if (trade.getDisputeState() == Trade.DisputeState.MEDIATION_REQUESTED ||
|
if (trade.getDisputeState() == Trade.DisputeState.MEDIATION_REQUESTED ||
|
||||||
trade.getDisputeState() == Trade.DisputeState.MEDIATION_STARTED_BY_PEER) {
|
trade.getDisputeState() == Trade.DisputeState.MEDIATION_STARTED_BY_PEER) {
|
||||||
trade.getProcessModel().setBuyerPayoutAmountFromMediation(disputeResult.getBuyerPayoutAmount().longValueExact());
|
trade.getProcessModel().setBuyerPayoutAmountFromMediation(disputeResult.getBuyerPayoutAmountBeforeCost().longValueExact());
|
||||||
trade.getProcessModel().setSellerPayoutAmountFromMediation(disputeResult.getSellerPayoutAmount().longValueExact());
|
trade.getProcessModel().setSellerPayoutAmountFromMediation(disputeResult.getSellerPayoutAmountBeforeCost().longValueExact());
|
||||||
|
|
||||||
trade.setDisputeState(Trade.DisputeState.MEDIATION_CLOSED);
|
trade.setDisputeState(Trade.DisputeState.MEDIATION_CLOSED);
|
||||||
|
|
||||||
|
@ -222,8 +222,8 @@ public final class MediationManager extends DisputeManager<MediationDisputeList>
|
||||||
Optional<Dispute> optionalDispute = findDispute(tradeId);
|
Optional<Dispute> optionalDispute = findDispute(tradeId);
|
||||||
checkArgument(optionalDispute.isPresent(), "dispute must be present");
|
checkArgument(optionalDispute.isPresent(), "dispute must be present");
|
||||||
DisputeResult disputeResult = optionalDispute.get().getDisputeResultProperty().get();
|
DisputeResult disputeResult = optionalDispute.get().getDisputeResultProperty().get();
|
||||||
BigInteger buyerPayoutAmount = disputeResult.getBuyerPayoutAmount();
|
BigInteger buyerPayoutAmount = disputeResult.getBuyerPayoutAmountBeforeCost();
|
||||||
BigInteger sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
|
BigInteger sellerPayoutAmount = disputeResult.getSellerPayoutAmountBeforeCost();
|
||||||
ProcessModel processModel = trade.getProcessModel();
|
ProcessModel processModel = trade.getProcessModel();
|
||||||
processModel.setBuyerPayoutAmountFromMediation(buyerPayoutAmount.longValueExact());
|
processModel.setBuyerPayoutAmountFromMediation(buyerPayoutAmount.longValueExact());
|
||||||
processModel.setSellerPayoutAmountFromMediation(sellerPayoutAmount.longValueExact());
|
processModel.setSellerPayoutAmountFromMediation(sellerPayoutAmount.longValueExact());
|
||||||
|
|
|
@ -58,6 +58,9 @@ import java.util.concurrent.TimeUnit;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import monero.common.MoneroRpcConnection;
|
import monero.common.MoneroRpcConnection;
|
||||||
|
import monero.wallet.model.MoneroDestination;
|
||||||
|
import monero.wallet.model.MoneroTxWallet;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -543,4 +546,12 @@ public class HavenoUtils {
|
||||||
if (c1 == null) return false;
|
if (c1 == null) return false;
|
||||||
return c1.equals(c2); // equality considers uri, username, and password
|
return c1.equals(c2); // equality considers uri, username, and password
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: move to monero-java MoneroTxWallet
|
||||||
|
public static MoneroDestination getDestination(String address, MoneroTxWallet tx) {
|
||||||
|
for (MoneroDestination destination : tx.getOutgoingTransfer().getDestinations()) {
|
||||||
|
if (address.equals(destination.getAddress())) return destination;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,8 @@ import haveno.core.payment.payload.PaymentAccountPayload;
|
||||||
import haveno.core.proto.CoreProtoResolver;
|
import haveno.core.proto.CoreProtoResolver;
|
||||||
import haveno.core.proto.network.CoreNetworkProtoResolver;
|
import haveno.core.proto.network.CoreNetworkProtoResolver;
|
||||||
import haveno.core.support.dispute.Dispute;
|
import haveno.core.support.dispute.Dispute;
|
||||||
|
import haveno.core.support.dispute.DisputeResult;
|
||||||
|
import haveno.core.support.dispute.arbitration.ArbitrationManager;
|
||||||
import haveno.core.support.dispute.mediation.MediationResultState;
|
import haveno.core.support.dispute.mediation.MediationResultState;
|
||||||
import haveno.core.support.dispute.refund.RefundResultState;
|
import haveno.core.support.dispute.refund.RefundResultState;
|
||||||
import haveno.core.support.messages.ChatMessage;
|
import haveno.core.support.messages.ChatMessage;
|
||||||
|
@ -323,7 +325,6 @@ public abstract class Trade implements Tradable, Model {
|
||||||
@Getter
|
@Getter
|
||||||
private final Offer offer;
|
private final Offer offer;
|
||||||
private final long takerFee;
|
private final long takerFee;
|
||||||
private final long totalTxFee;
|
|
||||||
|
|
||||||
// Added in 1.5.1
|
// Added in 1.5.1
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -451,6 +452,7 @@ public abstract class Trade implements Tradable, Model {
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private String payoutTxKey;
|
private String payoutTxKey;
|
||||||
|
private long payoutTxFee;
|
||||||
private Long payoutHeight;
|
private Long payoutHeight;
|
||||||
private IdlePayoutSyncer idlePayoutSyncer;
|
private IdlePayoutSyncer idlePayoutSyncer;
|
||||||
|
|
||||||
|
@ -472,7 +474,6 @@ public abstract class Trade implements Tradable, Model {
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
this.amount = tradeAmount.longValueExact();
|
this.amount = tradeAmount.longValueExact();
|
||||||
this.takerFee = takerFee.longValueExact();
|
this.takerFee = takerFee.longValueExact();
|
||||||
this.totalTxFee = 0l; // TODO: sum tx fees
|
|
||||||
this.price = tradePrice;
|
this.price = tradePrice;
|
||||||
this.xmrWalletService = xmrWalletService;
|
this.xmrWalletService = xmrWalletService;
|
||||||
this.processModel = processModel;
|
this.processModel = processModel;
|
||||||
|
@ -941,20 +942,25 @@ public abstract class Trade implements Tradable, Model {
|
||||||
.setRelay(false)
|
.setRelay(false)
|
||||||
.setPriority(XmrWalletService.PROTOCOL_FEE_PRIORITY));
|
.setPriority(XmrWalletService.PROTOCOL_FEE_PRIORITY));
|
||||||
|
|
||||||
// save updated multisig hex
|
// update state
|
||||||
|
BigInteger payoutTxFeeSplit = payoutTx.getFee().divide(BigInteger.valueOf(2));
|
||||||
|
getBuyer().setPayoutTxFee(payoutTxFeeSplit);
|
||||||
|
getBuyer().setPayoutAmount(HavenoUtils.getDestination(buyerPayoutAddress, payoutTx).getAmount());
|
||||||
|
getSeller().setPayoutTxFee(payoutTxFeeSplit);
|
||||||
|
getSeller().setPayoutAmount(HavenoUtils.getDestination(sellerPayoutAddress, payoutTx).getAmount());
|
||||||
getSelf().setUpdatedMultisigHex(multisigWallet.exportMultisigHex());
|
getSelf().setUpdatedMultisigHex(multisigWallet.exportMultisigHex());
|
||||||
return payoutTx;
|
return payoutTx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify a payout tx.
|
* Process a payout tx.
|
||||||
*
|
*
|
||||||
* @param payoutTxHex is the payout tx hex to verify
|
* @param payoutTxHex is the payout tx hex to verify
|
||||||
* @param sign signs the payout tx if true
|
* @param sign signs the payout tx if true
|
||||||
* @param publish publishes the signed payout tx if true
|
* @param publish publishes the signed payout tx if true
|
||||||
*/
|
*/
|
||||||
public void verifyPayoutTx(String payoutTxHex, boolean sign, boolean publish) {
|
public void processPayoutTx(String payoutTxHex, boolean sign, boolean publish) {
|
||||||
log.info("Verifying payout tx");
|
log.info("Processing payout tx for {} {}", getClass().getSimpleName(), getId());
|
||||||
|
|
||||||
// gather relevant info
|
// gather relevant info
|
||||||
MoneroWallet wallet = getWallet();
|
MoneroWallet wallet = getWallet();
|
||||||
|
@ -981,6 +987,7 @@ public abstract class Trade implements Tradable, Model {
|
||||||
if (!sellerPayoutDestination.getAddress().equals(contract.getSellerPayoutAddressString())) throw new IllegalArgumentException("Seller payout address does not match contract");
|
if (!sellerPayoutDestination.getAddress().equals(contract.getSellerPayoutAddressString())) throw new IllegalArgumentException("Seller payout address does not match contract");
|
||||||
|
|
||||||
// verify change address is multisig's primary address
|
// verify change address is multisig's primary address
|
||||||
|
if (!payoutTx.getChangeAmount().equals(BigInteger.ZERO)) log.warn("Dust left in multisig wallet for {} {}: {}", getClass().getSimpleName(), getId(), payoutTx.getChangeAmount());
|
||||||
if (!payoutTx.getChangeAmount().equals(BigInteger.ZERO) && !payoutTx.getChangeAddress().equals(wallet.getPrimaryAddress())) throw new IllegalArgumentException("Change address is not multisig wallet's primary address");
|
if (!payoutTx.getChangeAmount().equals(BigInteger.ZERO) && !payoutTx.getChangeAddress().equals(wallet.getPrimaryAddress())) throw new IllegalArgumentException("Change address is not multisig wallet's primary address");
|
||||||
|
|
||||||
// verify sum of outputs = destination amounts + change amount
|
// verify sum of outputs = destination amounts + change amount
|
||||||
|
@ -988,11 +995,12 @@ public abstract class Trade implements Tradable, Model {
|
||||||
|
|
||||||
// verify buyer destination amount is deposit amount + this amount - 1/2 tx costs
|
// verify buyer destination amount is deposit amount + this amount - 1/2 tx costs
|
||||||
BigInteger txCost = payoutTx.getFee().add(payoutTx.getChangeAmount());
|
BigInteger txCost = payoutTx.getFee().add(payoutTx.getChangeAmount());
|
||||||
BigInteger expectedBuyerPayout = buyerDepositAmount.add(tradeAmount).subtract(txCost.divide(BigInteger.valueOf(2)));
|
BigInteger txCostSplit = txCost.divide(BigInteger.valueOf(2));
|
||||||
|
BigInteger expectedBuyerPayout = buyerDepositAmount.add(tradeAmount).subtract(txCostSplit);
|
||||||
if (!buyerPayoutDestination.getAmount().equals(expectedBuyerPayout)) throw new IllegalArgumentException("Buyer destination amount is not deposit amount + trade amount - 1/2 tx costs, " + buyerPayoutDestination.getAmount() + " vs " + expectedBuyerPayout);
|
if (!buyerPayoutDestination.getAmount().equals(expectedBuyerPayout)) throw new IllegalArgumentException("Buyer destination amount is not deposit amount + trade amount - 1/2 tx costs, " + buyerPayoutDestination.getAmount() + " vs " + expectedBuyerPayout);
|
||||||
|
|
||||||
// verify seller destination amount is deposit amount - this amount - 1/2 tx costs
|
// verify seller destination amount is deposit amount - this amount - 1/2 tx costs
|
||||||
BigInteger expectedSellerPayout = sellerDepositAmount.subtract(tradeAmount).subtract(txCost.divide(BigInteger.valueOf(2)));
|
BigInteger expectedSellerPayout = sellerDepositAmount.subtract(tradeAmount).subtract(txCostSplit);
|
||||||
if (!sellerPayoutDestination.getAmount().equals(expectedSellerPayout)) throw new IllegalArgumentException("Seller destination amount is not deposit amount - trade amount - 1/2 tx costs, " + sellerPayoutDestination.getAmount() + " vs " + expectedSellerPayout);
|
if (!sellerPayoutDestination.getAmount().equals(expectedSellerPayout)) throw new IllegalArgumentException("Seller destination amount is not deposit amount - trade amount - 1/2 tx costs, " + sellerPayoutDestination.getAmount() + " vs " + expectedSellerPayout);
|
||||||
|
|
||||||
// check wallet connection
|
// check wallet connection
|
||||||
|
@ -1025,7 +1033,6 @@ public abstract class Trade implements Tradable, Model {
|
||||||
|
|
||||||
// submit payout tx
|
// submit payout tx
|
||||||
if (publish) {
|
if (publish) {
|
||||||
//if (true) throw new RuntimeException("Let's pretend there's an error last second submitting tx to daemon, so we need to resubmit payout hex");
|
|
||||||
wallet.submitMultisigTxHex(payoutTxHex);
|
wallet.submitMultisigTxHex(payoutTxHex);
|
||||||
pollWallet();
|
pollWallet();
|
||||||
}
|
}
|
||||||
|
@ -1296,9 +1303,47 @@ public abstract class Trade implements Tradable, Model {
|
||||||
public void setPayoutTx(MoneroTxWallet payoutTx) {
|
public void setPayoutTx(MoneroTxWallet payoutTx) {
|
||||||
this.payoutTx = payoutTx;
|
this.payoutTx = payoutTx;
|
||||||
payoutTxId = payoutTx.getHash();
|
payoutTxId = payoutTx.getHash();
|
||||||
if ("".equals(payoutTxId)) payoutTxId = null; // tx hash is empty until signed
|
if ("".equals(payoutTxId)) payoutTxId = null; // tx id is empty until signed
|
||||||
payoutTxKey = payoutTx.getKey();
|
payoutTxKey = payoutTx.getKey();
|
||||||
|
payoutTxFee = payoutTx.getFee().longValueExact();
|
||||||
for (Dispute dispute : getDisputes()) dispute.setDisputePayoutTxId(payoutTxId);
|
for (Dispute dispute : getDisputes()) dispute.setDisputePayoutTxId(payoutTxId);
|
||||||
|
|
||||||
|
// set final payout amounts
|
||||||
|
if (getDisputeState().isNotDisputed()) {
|
||||||
|
BigInteger splitTxFee = payoutTx.getFee().divide(BigInteger.valueOf(2));
|
||||||
|
getBuyer().setPayoutTxFee(splitTxFee);
|
||||||
|
getSeller().setPayoutTxFee(splitTxFee);
|
||||||
|
getBuyer().setPayoutAmount(getBuyer().getSecurityDeposit().subtract(getBuyer().getPayoutTxFee()).add(getAmount()));
|
||||||
|
getSeller().setPayoutAmount(getSeller().getSecurityDeposit().subtract(getSeller().getPayoutTxFee()));
|
||||||
|
} else if (getDisputeState().isClosed()) {
|
||||||
|
DisputeResult disputeResult = getDisputeResult();
|
||||||
|
BigInteger[] buyerSellerPayoutTxFees = ArbitrationManager.getBuyerSellerPayoutTxCost(disputeResult, payoutTx.getFee());
|
||||||
|
getBuyer().setPayoutTxFee(buyerSellerPayoutTxFees[0]);
|
||||||
|
getSeller().setPayoutTxFee(buyerSellerPayoutTxFees[1]);
|
||||||
|
getBuyer().setPayoutAmount(disputeResult.getBuyerPayoutAmountBeforeCost().subtract(getBuyer().getPayoutTxFee()));
|
||||||
|
getSeller().setPayoutAmount(disputeResult.getSellerPayoutAmountBeforeCost().subtract(getSeller().getPayoutTxFee()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisputeResult getDisputeResult() {
|
||||||
|
if (getDisputes().isEmpty()) return null;
|
||||||
|
return getDisputes().get(getDisputes().size() - 1).getDisputeResultProperty().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public MoneroTx getPayoutTx() {
|
||||||
|
if (payoutTx == null) {
|
||||||
|
payoutTx = payoutTxId == null ? null : (this instanceof ArbitratorTrade) ? xmrWalletService.getTxWithCache(payoutTxId) : xmrWalletService.getWallet().getTx(payoutTxId);
|
||||||
|
}
|
||||||
|
return payoutTx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayoutTxFee(BigInteger payoutTxFee) {
|
||||||
|
this.payoutTxFee = payoutTxFee.longValueExact();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigInteger getPayoutTxFee() {
|
||||||
|
return BigInteger.valueOf(payoutTxFee);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setErrorMessage(String errorMessage) {
|
public void setErrorMessage(String errorMessage) {
|
||||||
|
@ -1653,15 +1698,7 @@ public abstract class Trade implements Tradable, Model {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BigInteger getTotalTxFee() {
|
public BigInteger getTotalTxFee() {
|
||||||
return BigInteger.valueOf(totalTxFee);
|
return getSelf().getDepositTxFee().add(getSelf().getPayoutTxFee()); // sum my tx fees
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public MoneroTx getPayoutTx() {
|
|
||||||
if (payoutTx == null) {
|
|
||||||
payoutTx = payoutTxId == null ? null : (this instanceof ArbitratorTrade) ? xmrWalletService.getTxWithCache(payoutTxId) : xmrWalletService.getWallet().getTx(payoutTxId);
|
|
||||||
}
|
|
||||||
return payoutTx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasErrorMessage() {
|
public boolean hasErrorMessage() {
|
||||||
|
@ -2019,7 +2056,6 @@ public abstract class Trade implements Tradable, Model {
|
||||||
protobuf.Trade.Builder builder = protobuf.Trade.newBuilder()
|
protobuf.Trade.Builder builder = protobuf.Trade.newBuilder()
|
||||||
.setOffer(offer.toProtoMessage())
|
.setOffer(offer.toProtoMessage())
|
||||||
.setTakerFee(takerFee)
|
.setTakerFee(takerFee)
|
||||||
.setTotalTxFee(totalTxFee)
|
|
||||||
.setTakeOfferDate(takeOfferDate)
|
.setTakeOfferDate(takeOfferDate)
|
||||||
.setProcessModel(processModel.toProtoMessage())
|
.setProcessModel(processModel.toProtoMessage())
|
||||||
.setAmount(amount)
|
.setAmount(amount)
|
||||||
|
@ -2081,7 +2117,7 @@ public abstract class Trade implements Tradable, Model {
|
||||||
return "Trade{" +
|
return "Trade{" +
|
||||||
"\n offer=" + offer +
|
"\n offer=" + offer +
|
||||||
",\n takerFee=" + takerFee +
|
",\n takerFee=" + takerFee +
|
||||||
",\n totalTxFee=" + totalTxFee +
|
",\n totalTxFee=" + getTotalTxFee() +
|
||||||
",\n takeOfferDate=" + takeOfferDate +
|
",\n takeOfferDate=" + takeOfferDate +
|
||||||
",\n processModel=" + processModel +
|
",\n processModel=" + processModel +
|
||||||
",\n payoutTxId='" + payoutTxId + '\'' +
|
",\n payoutTxId='" + payoutTxId + '\'' +
|
||||||
|
@ -2098,7 +2134,6 @@ public abstract class Trade implements Tradable, Model {
|
||||||
",\n counterCurrencyTxId='" + counterCurrencyTxId + '\'' +
|
",\n counterCurrencyTxId='" + counterCurrencyTxId + '\'' +
|
||||||
",\n counterCurrencyExtraData='" + counterCurrencyExtraData + '\'' +
|
",\n counterCurrencyExtraData='" + counterCurrencyExtraData + '\'' +
|
||||||
",\n chatMessages=" + chatMessages +
|
",\n chatMessages=" + chatMessages +
|
||||||
",\n totalTxFee=" + totalTxFee +
|
|
||||||
",\n takerFee=" + takerFee +
|
",\n takerFee=" + takerFee +
|
||||||
",\n xmrWalletService=" + xmrWalletService +
|
",\n xmrWalletService=" + xmrWalletService +
|
||||||
",\n stateProperty=" + stateProperty +
|
",\n stateProperty=" + stateProperty +
|
||||||
|
|
|
@ -136,6 +136,11 @@ public final class TradePeer implements PersistablePayload {
|
||||||
private long depositTxFee;
|
private long depositTxFee;
|
||||||
private long securityDeposit;
|
private long securityDeposit;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@Setter
|
||||||
|
private String unsignedPayoutTxHex;
|
||||||
|
private long payoutTxFee;
|
||||||
|
private long payoutAmount;
|
||||||
|
@Nullable
|
||||||
private String updatedMultisigHex;
|
private String updatedMultisigHex;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
|
@ -161,6 +166,22 @@ public final class TradePeer implements PersistablePayload {
|
||||||
this.securityDeposit = securityDeposit.longValueExact();
|
this.securityDeposit = securityDeposit.longValueExact();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BigInteger getPayoutTxFee() {
|
||||||
|
return BigInteger.valueOf(payoutTxFee);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayoutTxFee(BigInteger payoutTxFee) {
|
||||||
|
this.payoutTxFee = payoutTxFee.longValueExact();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigInteger getPayoutAmount() {
|
||||||
|
return BigInteger.valueOf(payoutAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayoutAmount(BigInteger payoutAmount) {
|
||||||
|
this.payoutAmount = payoutAmount.longValueExact();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Message toProtoMessage() {
|
public Message toProtoMessage() {
|
||||||
final protobuf.TradePeer.Builder builder = protobuf.TradePeer.newBuilder();
|
final protobuf.TradePeer.Builder builder = protobuf.TradePeer.newBuilder();
|
||||||
|
@ -191,12 +212,15 @@ public final class TradePeer implements PersistablePayload {
|
||||||
Optional.ofNullable(preparedMultisigHex).ifPresent(e -> builder.setPreparedMultisigHex(preparedMultisigHex));
|
Optional.ofNullable(preparedMultisigHex).ifPresent(e -> builder.setPreparedMultisigHex(preparedMultisigHex));
|
||||||
Optional.ofNullable(madeMultisigHex).ifPresent(e -> builder.setMadeMultisigHex(madeMultisigHex));
|
Optional.ofNullable(madeMultisigHex).ifPresent(e -> builder.setMadeMultisigHex(madeMultisigHex));
|
||||||
Optional.ofNullable(exchangedMultisigHex).ifPresent(e -> builder.setExchangedMultisigHex(exchangedMultisigHex));
|
Optional.ofNullable(exchangedMultisigHex).ifPresent(e -> builder.setExchangedMultisigHex(exchangedMultisigHex));
|
||||||
|
Optional.ofNullable(updatedMultisigHex).ifPresent(e -> builder.setUpdatedMultisigHex(updatedMultisigHex));
|
||||||
Optional.ofNullable(depositTxHash).ifPresent(e -> builder.setDepositTxHash(depositTxHash));
|
Optional.ofNullable(depositTxHash).ifPresent(e -> builder.setDepositTxHash(depositTxHash));
|
||||||
Optional.ofNullable(depositTxHex).ifPresent(e -> builder.setDepositTxHex(depositTxHex));
|
Optional.ofNullable(depositTxHex).ifPresent(e -> builder.setDepositTxHex(depositTxHex));
|
||||||
Optional.ofNullable(depositTxKey).ifPresent(e -> builder.setDepositTxKey(depositTxKey));
|
Optional.ofNullable(depositTxKey).ifPresent(e -> builder.setDepositTxKey(depositTxKey));
|
||||||
Optional.ofNullable(depositTxFee).ifPresent(e -> builder.setDepositTxFee(depositTxFee));
|
Optional.ofNullable(depositTxFee).ifPresent(e -> builder.setDepositTxFee(depositTxFee));
|
||||||
Optional.ofNullable(securityDeposit).ifPresent(e -> builder.setSecurityDeposit(securityDeposit));
|
Optional.ofNullable(securityDeposit).ifPresent(e -> builder.setSecurityDeposit(securityDeposit));
|
||||||
Optional.ofNullable(updatedMultisigHex).ifPresent(e -> builder.setUpdatedMultisigHex(updatedMultisigHex));
|
Optional.ofNullable(unsignedPayoutTxHex).ifPresent(e -> builder.setUnsignedPayoutTxHex(unsignedPayoutTxHex));
|
||||||
|
Optional.ofNullable(payoutTxFee).ifPresent(e -> builder.setPayoutTxFee(payoutTxFee));
|
||||||
|
Optional.ofNullable(payoutAmount).ifPresent(e -> builder.setPayoutAmount(payoutAmount));
|
||||||
builder.setDepositsConfirmedMessageAcked(depositsConfirmedMessageAcked);
|
builder.setDepositsConfirmedMessageAcked(depositsConfirmedMessageAcked);
|
||||||
|
|
||||||
builder.setCurrentDate(currentDate);
|
builder.setCurrentDate(currentDate);
|
||||||
|
@ -237,13 +261,16 @@ public final class TradePeer implements PersistablePayload {
|
||||||
tradePeer.setPreparedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getPreparedMultisigHex()));
|
tradePeer.setPreparedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getPreparedMultisigHex()));
|
||||||
tradePeer.setMadeMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getMadeMultisigHex()));
|
tradePeer.setMadeMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getMadeMultisigHex()));
|
||||||
tradePeer.setExchangedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getExchangedMultisigHex()));
|
tradePeer.setExchangedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getExchangedMultisigHex()));
|
||||||
|
tradePeer.setUpdatedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex()));
|
||||||
|
tradePeer.setDepositsConfirmedMessageAcked(proto.getDepositsConfirmedMessageAcked());
|
||||||
tradePeer.setDepositTxHash(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHash()));
|
tradePeer.setDepositTxHash(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHash()));
|
||||||
tradePeer.setDepositTxHex(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHex()));
|
tradePeer.setDepositTxHex(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHex()));
|
||||||
tradePeer.setDepositTxKey(ProtoUtil.stringOrNullFromProto(proto.getDepositTxKey()));
|
tradePeer.setDepositTxKey(ProtoUtil.stringOrNullFromProto(proto.getDepositTxKey()));
|
||||||
tradePeer.setDepositTxFee(BigInteger.valueOf(proto.getDepositTxFee()));
|
tradePeer.setDepositTxFee(BigInteger.valueOf(proto.getDepositTxFee()));
|
||||||
tradePeer.setSecurityDeposit(BigInteger.valueOf(proto.getSecurityDeposit()));
|
tradePeer.setSecurityDeposit(BigInteger.valueOf(proto.getSecurityDeposit()));
|
||||||
tradePeer.setUpdatedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex()));
|
tradePeer.setUnsignedPayoutTxHex(ProtoUtil.stringOrNullFromProto(proto.getUnsignedPayoutTxHex()));
|
||||||
tradePeer.setDepositsConfirmedMessageAcked(proto.getDepositsConfirmedMessageAcked());
|
tradePeer.setPayoutTxFee(BigInteger.valueOf(proto.getPayoutTxFee()));
|
||||||
|
tradePeer.setPayoutAmount(BigInteger.valueOf(proto.getPayoutAmount()));
|
||||||
return tradePeer;
|
return tradePeer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,17 +136,17 @@ public class ProcessPaymentReceivedMessage extends TradeTask {
|
||||||
if (!trade.isPayoutPublished()) {
|
if (!trade.isPayoutPublished()) {
|
||||||
if (isSigned) {
|
if (isSigned) {
|
||||||
log.info("{} {} publishing signed payout tx from seller", trade.getClass().getSimpleName(), trade.getId());
|
log.info("{} {} publishing signed payout tx from seller", trade.getClass().getSimpleName(), trade.getId());
|
||||||
trade.verifyPayoutTx(message.getSignedPayoutTxHex(), false, true);
|
trade.processPayoutTx(message.getSignedPayoutTxHex(), false, true);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
PaymentSentMessage paymentSentMessage = (trade.isArbitrator() ? trade.getBuyer() : trade.getArbitrator()).getPaymentSentMessage();
|
PaymentSentMessage paymentSentMessage = (trade.isArbitrator() ? trade.getBuyer() : trade.getArbitrator()).getPaymentSentMessage();
|
||||||
if (paymentSentMessage == null) throw new RuntimeException("Process model does not have payment sent message for " + trade.getClass().getSimpleName() + " " + trade.getId());
|
if (paymentSentMessage == null) throw new RuntimeException("Process model does not have payment sent message for " + trade.getClass().getSimpleName() + " " + trade.getId());
|
||||||
if (StringUtils.equals(trade.getPayoutTxHex(), paymentSentMessage.getPayoutTxHex())) { // unsigned
|
if (StringUtils.equals(trade.getPayoutTxHex(), paymentSentMessage.getPayoutTxHex())) { // unsigned
|
||||||
log.info("{} {} verifying, signing, and publishing payout tx", trade.getClass().getSimpleName(), trade.getId());
|
log.info("{} {} verifying, signing, and publishing payout tx", trade.getClass().getSimpleName(), trade.getId());
|
||||||
trade.verifyPayoutTx(message.getUnsignedPayoutTxHex(), true, true);
|
trade.processPayoutTx(message.getUnsignedPayoutTxHex(), true, true);
|
||||||
} else {
|
} else {
|
||||||
log.info("{} {} re-verifying and publishing payout tx", trade.getClass().getSimpleName(), trade.getId());
|
log.info("{} {} re-verifying and publishing payout tx", trade.getClass().getSimpleName(), trade.getId());
|
||||||
trade.verifyPayoutTx(trade.getPayoutTxHex(), false, true);
|
trade.processPayoutTx(trade.getPayoutTxHex(), false, true);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
trade.syncAndPollWallet();
|
trade.syncAndPollWallet();
|
||||||
|
@ -157,7 +157,7 @@ public class ProcessPaymentReceivedMessage extends TradeTask {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.info("Payout tx already published for {} {}", trade.getClass().getSimpleName(), trade.getId());
|
log.info("Payout tx already published for {} {}", trade.getClass().getSimpleName(), trade.getId());
|
||||||
if (message.getSignedPayoutTxHex() != null && !trade.isPayoutConfirmed()) trade.verifyPayoutTx(message.getSignedPayoutTxHex(), false, true);
|
if (message.getSignedPayoutTxHex() != null && !trade.isPayoutConfirmed()) trade.processPayoutTx(message.getSignedPayoutTxHex(), false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class SellerPreparePaymentReceivedMessage extends TradeTask {
|
||||||
if (trade.getPayoutTxHex() != null) {
|
if (trade.getPayoutTxHex() != null) {
|
||||||
try {
|
try {
|
||||||
log.info("Seller verifying, signing, and publishing payout tx for trade {}", trade.getId());
|
log.info("Seller verifying, signing, and publishing payout tx for trade {}", trade.getId());
|
||||||
trade.verifyPayoutTx(trade.getPayoutTxHex(), true, true);
|
trade.processPayoutTx(trade.getPayoutTxHex(), true, true);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Error verifying, signing, and publishing payout tx for trade {}: {}. Creating unsigned payout tx", trade.getId(), e.getMessage());
|
log.warn("Error verifying, signing, and publishing payout tx for trade {}: {}. Creating unsigned payout tx", trade.getId(), e.getMessage());
|
||||||
createUnsignedPayoutTx();
|
createUnsignedPayoutTx();
|
||||||
|
@ -60,7 +60,7 @@ public class SellerPreparePaymentReceivedMessage extends TradeTask {
|
||||||
|
|
||||||
// republish payout tx from previous message
|
// republish payout tx from previous message
|
||||||
log.info("Seller re-verifying and publishing payout tx for trade {}", trade.getId());
|
log.info("Seller re-verifying and publishing payout tx for trade {}", trade.getId());
|
||||||
trade.verifyPayoutTx(trade.getArbitrator().getPaymentReceivedMessage().getSignedPayoutTxHex(), false, true);
|
trade.processPayoutTx(trade.getArbitrator().getPaymentReceivedMessage().getSignedPayoutTxHex(), false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
processModel.getTradeManager().requestPersistence();
|
processModel.getTradeManager().requestPersistence();
|
||||||
|
|
|
@ -211,8 +211,8 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
if (applyPeersDisputeResult) {
|
if (applyPeersDisputeResult) {
|
||||||
// If the other peers dispute has been closed we apply the result to ourselves
|
// If the other peers dispute has been closed we apply the result to ourselves
|
||||||
DisputeResult peersDisputeResult = peersDisputeOptional.get().getDisputeResultProperty().get();
|
DisputeResult peersDisputeResult = peersDisputeOptional.get().getDisputeResultProperty().get();
|
||||||
disputeResult.setBuyerPayoutAmount(peersDisputeResult.getBuyerPayoutAmount());
|
disputeResult.setBuyerPayoutAmountBeforeCost(peersDisputeResult.getBuyerPayoutAmountBeforeCost());
|
||||||
disputeResult.setSellerPayoutAmount(peersDisputeResult.getSellerPayoutAmount());
|
disputeResult.setSellerPayoutAmountBeforeCost(peersDisputeResult.getSellerPayoutAmountBeforeCost());
|
||||||
disputeResult.setWinner(peersDisputeResult.getWinner());
|
disputeResult.setWinner(peersDisputeResult.getWinner());
|
||||||
disputeResult.setReason(peersDisputeResult.getReason());
|
disputeResult.setReason(peersDisputeResult.getReason());
|
||||||
disputeResult.setSummaryNotes(peersDisputeResult.summaryNotesProperty().get());
|
disputeResult.setSummaryNotes(peersDisputeResult.summaryNotesProperty().get());
|
||||||
|
@ -402,8 +402,8 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
buyerPayoutAmountInputTextField.setText(formattedCounterPartAmount);
|
buyerPayoutAmountInputTextField.setText(formattedCounterPartAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
disputeResult.setBuyerPayoutAmount(buyerAmount);
|
disputeResult.setBuyerPayoutAmountBeforeCost(buyerAmount);
|
||||||
disputeResult.setSellerPayoutAmount(sellerAmount);
|
disputeResult.setSellerPayoutAmountBeforeCost(sellerAmount);
|
||||||
disputeResult.setWinner(buyerAmount.compareTo(sellerAmount) > 0 ? DisputeResult.Winner.BUYER : DisputeResult.Winner.SELLER); // TODO: UI should allow selection of receiver of exact custom amount, otherwise defaulting to bigger receiver. could extend API to specify who pays payout tx fee: buyer, seller, or both
|
disputeResult.setWinner(buyerAmount.compareTo(sellerAmount) > 0 ? DisputeResult.Winner.BUYER : DisputeResult.Winner.SELLER); // TODO: UI should allow selection of receiver of exact custom amount, otherwise defaulting to bigger receiver. could extend API to specify who pays payout tx fee: buyer, seller, or both
|
||||||
disputeResult.setSubtractFeeFrom(buyerAmount.compareTo(sellerAmount) > 0 ? DisputeResult.SubtractFeeFrom.SELLER_ONLY : DisputeResult.SubtractFeeFrom.BUYER_ONLY);
|
disputeResult.setSubtractFeeFrom(buyerAmount.compareTo(sellerAmount) > 0 ? DisputeResult.SubtractFeeFrom.SELLER_ONLY : DisputeResult.SubtractFeeFrom.BUYER_ONLY);
|
||||||
}
|
}
|
||||||
|
@ -587,8 +587,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
!trade.isPayoutPublished()) {
|
!trade.isPayoutPublished()) {
|
||||||
|
|
||||||
// create payout tx
|
// create payout tx
|
||||||
MoneroTxWallet payoutTx = arbitrationManager.createDisputePayoutTx(trade, dispute.getContract(), disputeResult, false);
|
MoneroTxWallet payoutTx = arbitrationManager.createDisputePayoutTx(trade, dispute.getContract(), disputeResult, true);
|
||||||
trade.getProcessModel().setUnsignedPayoutTx((MoneroTxWallet) payoutTx);
|
|
||||||
|
|
||||||
// show confirmation
|
// show confirmation
|
||||||
showPayoutTxConfirmation(contract,
|
showPayoutTxConfirmation(contract,
|
||||||
|
@ -709,8 +708,8 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
throw new IllegalStateException("Unknown radio button");
|
throw new IllegalStateException("Unknown radio button");
|
||||||
}
|
}
|
||||||
disputesService.applyPayoutAmountsToDisputeResult(payout, dispute, disputeResult, -1);
|
disputesService.applyPayoutAmountsToDisputeResult(payout, dispute, disputeResult, -1);
|
||||||
buyerPayoutAmountInputTextField.setText(HavenoUtils.formatXmr(disputeResult.getBuyerPayoutAmount()));
|
buyerPayoutAmountInputTextField.setText(HavenoUtils.formatXmr(disputeResult.getBuyerPayoutAmountBeforeCost()));
|
||||||
sellerPayoutAmountInputTextField.setText(HavenoUtils.formatXmr(disputeResult.getSellerPayoutAmount()));
|
sellerPayoutAmountInputTextField.setText(HavenoUtils.formatXmr(disputeResult.getSellerPayoutAmountBeforeCost()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyTradeAmountRadioButtonStates() {
|
private void applyTradeAmountRadioButtonStates() {
|
||||||
|
@ -719,8 +718,8 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
BigInteger sellerSecurityDeposit = trade.getSeller().getSecurityDeposit();
|
BigInteger sellerSecurityDeposit = trade.getSeller().getSecurityDeposit();
|
||||||
BigInteger tradeAmount = contract.getTradeAmount();
|
BigInteger tradeAmount = contract.getTradeAmount();
|
||||||
|
|
||||||
BigInteger buyerPayoutAmount = disputeResult.getBuyerPayoutAmount();
|
BigInteger buyerPayoutAmount = disputeResult.getBuyerPayoutAmountBeforeCost();
|
||||||
BigInteger sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
|
BigInteger sellerPayoutAmount = disputeResult.getSellerPayoutAmountBeforeCost();
|
||||||
|
|
||||||
buyerPayoutAmountInputTextField.setText(HavenoUtils.formatXmr(buyerPayoutAmount));
|
buyerPayoutAmountInputTextField.setText(HavenoUtils.formatXmr(buyerPayoutAmount));
|
||||||
sellerPayoutAmountInputTextField.setText(HavenoUtils.formatXmr(sellerPayoutAmount));
|
sellerPayoutAmountInputTextField.setText(HavenoUtils.formatXmr(sellerPayoutAmount));
|
||||||
|
|
|
@ -91,8 +91,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
VOLUME(Res.get("shared.amount")),
|
VOLUME(Res.get("shared.amount")),
|
||||||
VOLUME_CURRENCY(Res.get("shared.currency")),
|
VOLUME_CURRENCY(Res.get("shared.currency")),
|
||||||
TX_FEE(Res.get("shared.txFee")),
|
TX_FEE(Res.get("shared.txFee")),
|
||||||
TRADE_FEE_BTC(Res.get("shared.tradeFee") + " BTC"),
|
TRADE_FEE(Res.get("shared.tradeFee")),
|
||||||
TRADE_FEE_BSQ(Res.get("shared.tradeFee") + " BSQ"),
|
|
||||||
BUYER_SEC(Res.get("shared.buyerSecurityDeposit")),
|
BUYER_SEC(Res.get("shared.buyerSecurityDeposit")),
|
||||||
SELLER_SEC(Res.get("shared.sellerSecurityDeposit")),
|
SELLER_SEC(Res.get("shared.sellerSecurityDeposit")),
|
||||||
OFFER_TYPE(Res.get("shared.offerType")),
|
OFFER_TYPE(Res.get("shared.offerType")),
|
||||||
|
@ -158,7 +157,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
widthListener = (observable, oldValue, newValue) -> onWidthChange((double) newValue);
|
widthListener = (observable, oldValue, newValue) -> onWidthChange((double) newValue);
|
||||||
tradeFeeColumn.setGraphic(new AutoTooltipLabel(ColumnNames.TRADE_FEE_BTC.toString().replace(" BTC", "")));
|
tradeFeeColumn.setGraphic(new AutoTooltipLabel(ColumnNames.TRADE_FEE.toString().replace(" BTC", "")));
|
||||||
buyerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(ColumnNames.BUYER_SEC.toString()));
|
buyerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(ColumnNames.BUYER_SEC.toString()));
|
||||||
sellerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(ColumnNames.SELLER_SEC.toString()));
|
sellerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(ColumnNames.SELLER_SEC.toString()));
|
||||||
priceColumn.setGraphic(new AutoTooltipLabel(ColumnNames.PRICE.toString()));
|
priceColumn.setGraphic(new AutoTooltipLabel(ColumnNames.PRICE.toString()));
|
||||||
|
@ -275,11 +274,9 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
columns[ColumnNames.VOLUME_CURRENCY.ordinal()] = item.getVolumeCurrencyAsString();
|
columns[ColumnNames.VOLUME_CURRENCY.ordinal()] = item.getVolumeCurrencyAsString();
|
||||||
columns[ColumnNames.TX_FEE.ordinal()] = item.getTxFeeAsString();
|
columns[ColumnNames.TX_FEE.ordinal()] = item.getTxFeeAsString();
|
||||||
if (model.dataModel.isCurrencyForTradeFeeBtc(item.getTradable())) {
|
if (model.dataModel.isCurrencyForTradeFeeBtc(item.getTradable())) {
|
||||||
columns[ColumnNames.TRADE_FEE_BTC.ordinal()] = item.getTradeFeeAsString(false);
|
columns[ColumnNames.TRADE_FEE.ordinal()] = item.getTradeFeeAsString(false);
|
||||||
columns[ColumnNames.TRADE_FEE_BSQ.ordinal()] = "";
|
|
||||||
} else {
|
} else {
|
||||||
columns[ColumnNames.TRADE_FEE_BTC.ordinal()] = "";
|
columns[ColumnNames.TRADE_FEE.ordinal()] = "";
|
||||||
columns[ColumnNames.TRADE_FEE_BSQ.ordinal()] = item.getTradeFeeAsString(false);
|
|
||||||
}
|
}
|
||||||
columns[ColumnNames.BUYER_SEC.ordinal()] = item.getBuyerSecurityDepositAsString();
|
columns[ColumnNames.BUYER_SEC.ordinal()] = item.getBuyerSecurityDepositAsString();
|
||||||
columns[ColumnNames.SELLER_SEC.ordinal()] = item.getSellerSecurityDepositAsString();
|
columns[ColumnNames.SELLER_SEC.ordinal()] = item.getSellerSecurityDepositAsString();
|
||||||
|
|
|
@ -679,8 +679,8 @@ public abstract class TradeStepView extends AnchorPane {
|
||||||
DisputeResult disputeResult = optionalDispute.get().getDisputeResultProperty().get();
|
DisputeResult disputeResult = optionalDispute.get().getDisputeResultProperty().get();
|
||||||
Contract contract = checkNotNull(trade.getContract(), "contract must not be null");
|
Contract contract = checkNotNull(trade.getContract(), "contract must not be null");
|
||||||
boolean isMyRoleBuyer = contract.isMyRoleBuyer(model.dataModel.getPubKeyRingProvider().get());
|
boolean isMyRoleBuyer = contract.isMyRoleBuyer(model.dataModel.getPubKeyRingProvider().get());
|
||||||
String buyerPayoutAmount = HavenoUtils.formatXmr(disputeResult.getBuyerPayoutAmount(), true);
|
String buyerPayoutAmount = HavenoUtils.formatXmr(disputeResult.getBuyerPayoutAmountBeforeCost(), true);
|
||||||
String sellerPayoutAmount = HavenoUtils.formatXmr(disputeResult.getSellerPayoutAmount(), true);
|
String sellerPayoutAmount = HavenoUtils.formatXmr(disputeResult.getSellerPayoutAmountBeforeCost(), true);
|
||||||
String myPayoutAmount = isMyRoleBuyer ? buyerPayoutAmount : sellerPayoutAmount;
|
String myPayoutAmount = isMyRoleBuyer ? buyerPayoutAmount : sellerPayoutAmount;
|
||||||
String peersPayoutAmount = isMyRoleBuyer ? sellerPayoutAmount : buyerPayoutAmount;
|
String peersPayoutAmount = isMyRoleBuyer ? sellerPayoutAmount : buyerPayoutAmount;
|
||||||
|
|
||||||
|
|
|
@ -639,10 +639,9 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
|
||||||
String sellersRole = contract.isBuyerMakerAndSellerTaker() ? "Seller as taker" : "Seller as maker";
|
String sellersRole = contract.isBuyerMakerAndSellerTaker() ? "Seller as taker" : "Seller as maker";
|
||||||
String opener = firstDispute.isDisputeOpenerIsBuyer() ? buyersRole : sellersRole;
|
String opener = firstDispute.isDisputeOpenerIsBuyer() ? buyersRole : sellersRole;
|
||||||
DisputeResult disputeResult = firstDispute.getDisputeResultProperty().get();
|
DisputeResult disputeResult = firstDispute.getDisputeResultProperty().get();
|
||||||
String winner = disputeResult != null &&
|
String winner = disputeResult != null && disputeResult.getWinner() == DisputeResult.Winner.BUYER ? "Buyer" : "Seller";
|
||||||
disputeResult.getWinner() == DisputeResult.Winner.BUYER ? "Buyer" : "Seller";
|
String buyerPayoutAmount = disputeResult != null ? HavenoUtils.formatXmr(disputeResult.getBuyerPayoutAmountBeforeCost(), true) : "";
|
||||||
String buyerPayoutAmount = disputeResult != null ? HavenoUtils.formatXmr(disputeResult.getBuyerPayoutAmount(), true) : "";
|
String sellerPayoutAmount = disputeResult != null ? HavenoUtils.formatXmr(disputeResult.getSellerPayoutAmountBeforeCost(), true) : "";
|
||||||
String sellerPayoutAmount = disputeResult != null ? HavenoUtils.formatXmr(disputeResult.getSellerPayoutAmount(), true) : "";
|
|
||||||
|
|
||||||
int index = disputeIndex.incrementAndGet();
|
int index = disputeIndex.incrementAndGet();
|
||||||
String tradeDateString = dateFormatter.format(firstDispute.getTradeDate());
|
String tradeDateString = dateFormatter.format(firstDispute.getTradeDate());
|
||||||
|
|
|
@ -831,33 +831,38 @@ message TradeInfo {
|
||||||
uint64 date = 4;
|
uint64 date = 4;
|
||||||
string role = 5;
|
string role = 5;
|
||||||
uint64 taker_fee = 6 [jstype = JS_STRING];
|
uint64 taker_fee = 6 [jstype = JS_STRING];
|
||||||
string taker_fee_tx_id = 7;
|
uint64 amount = 7 [jstype = JS_STRING];
|
||||||
string payout_tx_id = 8;
|
uint64 buyer_security_deposit = 8 [jstype = JS_STRING];
|
||||||
uint64 amount = 9 [jstype = JS_STRING];
|
uint64 seller_security_deposit = 9 [jstype = JS_STRING];
|
||||||
uint64 buyer_security_deposit = 10 [jstype = JS_STRING];
|
uint64 buyer_deposit_tx_fee = 10 [jstype = JS_STRING];
|
||||||
uint64 seller_security_deposit = 11 [jstype = JS_STRING];
|
uint64 seller_deposit_tx_fee = 11 [jstype = JS_STRING];
|
||||||
string price = 12;
|
uint64 buyer_payout_tx_fee = 12 [jstype = JS_STRING];
|
||||||
string arbitrator_node_address = 13;
|
uint64 seller_payout_tx_fee = 13 [jstype = JS_STRING];
|
||||||
string trade_peer_node_address = 14;
|
uint64 buyer_payout_amount = 14 [jstype = JS_STRING];
|
||||||
string state = 15;
|
uint64 seller_payout_amount = 15 [jstype = JS_STRING];
|
||||||
string phase = 16;
|
string price = 16;
|
||||||
string period_state = 17;
|
string arbitrator_node_address = 17;
|
||||||
string payout_state = 18;
|
string trade_peer_node_address = 18;
|
||||||
string dispute_state = 19;
|
string state = 19;
|
||||||
bool is_deposits_published = 20;
|
string phase = 20;
|
||||||
bool is_deposits_confirmed = 21;
|
string period_state = 21;
|
||||||
bool is_deposits_unlocked = 22;
|
string payout_state = 22;
|
||||||
bool is_payment_sent = 23;
|
string dispute_state = 23;
|
||||||
bool is_payment_received = 24;
|
bool is_deposits_published = 24;
|
||||||
bool is_payout_published = 25;
|
bool is_deposits_confirmed = 25;
|
||||||
bool is_payout_confirmed = 26;
|
bool is_deposits_unlocked = 26;
|
||||||
bool is_payout_unlocked = 27;
|
bool is_payment_sent = 27;
|
||||||
bool is_completed = 28;
|
bool is_payment_received = 28;
|
||||||
string contract_as_json = 29;
|
bool is_payout_published = 29;
|
||||||
ContractInfo contract = 30;
|
bool is_payout_confirmed = 30;
|
||||||
string trade_volume = 31;
|
bool is_payout_unlocked = 31;
|
||||||
string maker_deposit_tx_id = 32;
|
bool is_completed = 32;
|
||||||
string taker_deposit_tx_id = 33;
|
string contract_as_json = 33;
|
||||||
|
ContractInfo contract = 34;
|
||||||
|
string trade_volume = 35;
|
||||||
|
string maker_deposit_tx_id = 36;
|
||||||
|
string taker_deposit_tx_id = 37;
|
||||||
|
string payout_tx_id = 38;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ContractInfo {
|
message ContractInfo {
|
||||||
|
|
|
@ -758,8 +758,8 @@ message DisputeResult {
|
||||||
string summary_notes = 8;
|
string summary_notes = 8;
|
||||||
ChatMessage chat_message = 9;
|
ChatMessage chat_message = 9;
|
||||||
bytes arbitrator_signature = 10;
|
bytes arbitrator_signature = 10;
|
||||||
int64 buyer_payout_amount = 11;
|
int64 buyer_payout_amount_before_cost = 11;
|
||||||
int64 seller_payout_amount = 12;
|
int64 seller_payout_amount_before_cost = 12;
|
||||||
SubtractFeeFrom subtract_fee_from = 13;
|
SubtractFeeFrom subtract_fee_from = 13;
|
||||||
bytes arbitrator_pub_key = 14;
|
bytes arbitrator_pub_key = 14;
|
||||||
int64 close_date = 15;
|
int64 close_date = 15;
|
||||||
|
@ -1488,28 +1488,27 @@ message Trade {
|
||||||
string payout_tx_key = 5;
|
string payout_tx_key = 5;
|
||||||
int64 amount = 6;
|
int64 amount = 6;
|
||||||
int64 taker_fee = 8;
|
int64 taker_fee = 8;
|
||||||
int64 total_tx_fee = 9;
|
int64 take_offer_date = 9;
|
||||||
int64 take_offer_date = 10;
|
int64 price = 10;
|
||||||
int64 price = 11;
|
State state = 11;
|
||||||
State state = 12;
|
PayoutState payout_state = 12;
|
||||||
PayoutState payout_state = 13;
|
DisputeState dispute_state = 13;
|
||||||
DisputeState dispute_state = 14;
|
TradePeriodState period_state = 14;
|
||||||
TradePeriodState period_state = 15;
|
Contract contract = 15;
|
||||||
Contract contract = 16;
|
string contract_as_json = 16;
|
||||||
string contract_as_json = 17;
|
bytes contract_hash = 17;
|
||||||
bytes contract_hash = 18;
|
NodeAddress arbitrator_node_address = 18;
|
||||||
NodeAddress arbitrator_node_address = 19;
|
NodeAddress mediator_node_address = 19;
|
||||||
NodeAddress mediator_node_address = 20;
|
string error_message = 20;
|
||||||
string error_message = 21;
|
string counter_currency_tx_id = 21;
|
||||||
string counter_currency_tx_id = 22;
|
repeated ChatMessage chat_message = 22;
|
||||||
repeated ChatMessage chat_message = 23;
|
MediationResultState mediation_result_state = 23;
|
||||||
MediationResultState mediation_result_state = 24;
|
int64 lock_time = 24;
|
||||||
int64 lock_time = 25;
|
int64 start_time = 25;
|
||||||
int64 start_time = 26;
|
NodeAddress refund_agent_node_address = 26;
|
||||||
NodeAddress refund_agent_node_address = 27;
|
RefundResultState refund_result_state = 27;
|
||||||
RefundResultState refund_result_state = 28;
|
string counter_currency_extra_data = 28;
|
||||||
string counter_currency_extra_data = 29;
|
string uid = 29;
|
||||||
string uid = 30;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message BuyerAsMakerTrade {
|
message BuyerAsMakerTrade {
|
||||||
|
@ -1572,21 +1571,23 @@ message TradePeer {
|
||||||
PaymentSentMessage payment_sent_message = 23;
|
PaymentSentMessage payment_sent_message = 23;
|
||||||
PaymentReceivedMessage payment_received_message = 24;
|
PaymentReceivedMessage payment_received_message = 24;
|
||||||
DisputeClosedMessage dispute_closed_message = 25;
|
DisputeClosedMessage dispute_closed_message = 25;
|
||||||
|
string reserve_tx_hash = 26;
|
||||||
string reserve_tx_hash = 1001;
|
string reserve_tx_hex = 27;
|
||||||
string reserve_tx_hex = 1002;
|
string reserve_tx_key = 28;
|
||||||
string reserve_tx_key = 1003;
|
repeated string reserve_tx_key_images = 29;
|
||||||
repeated string reserve_tx_key_images = 1004;
|
string prepared_multisig_hex = 30;
|
||||||
string prepared_multisig_hex = 1005;
|
string made_multisig_hex = 31;
|
||||||
string made_multisig_hex = 1006;
|
string exchanged_multisig_hex = 32;
|
||||||
string exchanged_multisig_hex = 1007;
|
string updated_multisig_hex = 33;
|
||||||
string deposit_tx_hash = 1008;
|
bool deposits_confirmed_message_acked = 34;
|
||||||
string deposit_tx_hex = 1009;
|
string deposit_tx_hash = 35;
|
||||||
string deposit_tx_key = 1010;
|
string deposit_tx_hex = 36;
|
||||||
int64 deposit_tx_fee = 1011;
|
string deposit_tx_key = 37;
|
||||||
int64 security_deposit = 1012;
|
int64 deposit_tx_fee = 38;
|
||||||
string updated_multisig_hex = 1013;
|
int64 security_deposit = 39;
|
||||||
bool deposits_confirmed_message_acked = 1014;
|
string unsigned_payout_tx_hex = 40;
|
||||||
|
int64 payout_tx_fee = 41;
|
||||||
|
int64 payout_amount = 42;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Loading…
Reference in a new issue