mirror of
https://github.com/boldsuck/haveno.git
synced 2024-12-22 20:19:21 +00:00
Compare commits
13 commits
7e4e950710
...
f053a274a4
Author | SHA1 | Date | |
---|---|---|---|
|
f053a274a4 | ||
|
fdee044023 | ||
|
42ede83ca2 | ||
|
5444d96832 | ||
|
7340ca9c21 | ||
|
542441d9d2 | ||
|
c5ef60ce5c | ||
|
389c5dddac | ||
|
7240b5f222 | ||
|
34e0c4b71f | ||
|
aab4d0207e | ||
|
1a51b171a0 | ||
|
a557d90e5d |
17 changed files with 114 additions and 92 deletions
|
@ -73,18 +73,9 @@ To incentivize development and reward contributors, we adopt a simple bounty sys
|
||||||
|
|
||||||
To bring Haveno to life, we need resources. If you have the possibility, please consider [becoming a sponsor](https://haveno.exchange/sponsors/) or donating to the project:
|
To bring Haveno to life, we need resources. If you have the possibility, please consider [becoming a sponsor](https://haveno.exchange/sponsors/) or donating to the project:
|
||||||
|
|
||||||
### Monero
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_monero.png" alt="Donate Monero" width="115" height="115"><br>
|
<img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_monero.png" alt="Donate Monero" width="115" height="115"><br>
|
||||||
<code>42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F</code>
|
<code>42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F</code>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
If you are using a wallet that supports OpenAlias (like the 'official' CLI and GUI wallets), you can simply put `fund@haveno.exchange` as the "receiver" address.
|
If you are using a wallet that supports OpenAlias (like the 'official' CLI and GUI wallets), you can simply put `fund@haveno.exchange` as the "receiver" address.
|
||||||
|
|
||||||
### Bitcoin
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_bitcoin.png" alt="Donate Bitcoin" width="115" height="115"><br>
|
|
||||||
<code>1AKq3CE1yBAnxGmHXbNFfNYStcByNDc5gQ</code>
|
|
||||||
</p>
|
|
||||||
|
|
|
@ -610,7 +610,7 @@ configure(project(':desktop')) {
|
||||||
apply plugin: 'com.github.johnrengelman.shadow'
|
apply plugin: 'com.github.johnrengelman.shadow'
|
||||||
apply from: 'package/package.gradle'
|
apply from: 'package/package.gradle'
|
||||||
|
|
||||||
version = '1.0.15-SNAPSHOT'
|
version = '1.0.17-SNAPSHOT'
|
||||||
|
|
||||||
jar.manifest.attributes(
|
jar.manifest.attributes(
|
||||||
"Implementation-Title": project.name,
|
"Implementation-Title": project.name,
|
||||||
|
|
|
@ -28,7 +28,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||||
public class Version {
|
public class Version {
|
||||||
// The application versions
|
// The application versions
|
||||||
// We use semantic versioning with major, minor and patch
|
// We use semantic versioning with major, minor and patch
|
||||||
public static final String VERSION = "1.0.15";
|
public static final String VERSION = "1.0.17";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds a list of the tagged resource files for optimizing the getData requests.
|
* Holds a list of the tagged resource files for optimizing the getData requests.
|
||||||
|
|
|
@ -115,7 +115,6 @@ import lombok.Getter;
|
||||||
import monero.common.MoneroRpcConnection;
|
import monero.common.MoneroRpcConnection;
|
||||||
import monero.daemon.model.MoneroKeyImageSpentStatus;
|
import monero.daemon.model.MoneroKeyImageSpentStatus;
|
||||||
import monero.daemon.model.MoneroTx;
|
import monero.daemon.model.MoneroTx;
|
||||||
import monero.wallet.model.MoneroIncomingTransfer;
|
|
||||||
import monero.wallet.model.MoneroOutputQuery;
|
import monero.wallet.model.MoneroOutputQuery;
|
||||||
import monero.wallet.model.MoneroOutputWallet;
|
import monero.wallet.model.MoneroOutputWallet;
|
||||||
import monero.wallet.model.MoneroTransferQuery;
|
import monero.wallet.model.MoneroTransferQuery;
|
||||||
|
@ -1159,23 +1158,17 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
|
|
||||||
private void scheduleWithEarliestTxs(List<OpenOffer> openOffers, OpenOffer openOffer) {
|
private void scheduleWithEarliestTxs(List<OpenOffer> openOffers, OpenOffer openOffer) {
|
||||||
|
|
||||||
// check for sufficient balance - scheduled offers amount
|
|
||||||
BigInteger offerReserveAmount = openOffer.getOffer().getAmountNeeded();
|
|
||||||
if (xmrWalletService.getBalance().subtract(getScheduledAmount(openOffers)).compareTo(offerReserveAmount) < 0) {
|
|
||||||
throw new RuntimeException("Not enough money in Haveno wallet");
|
|
||||||
}
|
|
||||||
|
|
||||||
// get earliest available or pending txs with sufficient spendable amount
|
// get earliest available or pending txs with sufficient spendable amount
|
||||||
|
BigInteger offerReserveAmount = openOffer.getOffer().getAmountNeeded();
|
||||||
BigInteger scheduledAmount = BigInteger.ZERO;
|
BigInteger scheduledAmount = BigInteger.ZERO;
|
||||||
Set<MoneroTxWallet> scheduledTxs = new HashSet<MoneroTxWallet>();
|
Set<MoneroTxWallet> scheduledTxs = new HashSet<MoneroTxWallet>();
|
||||||
for (MoneroTxWallet tx : xmrWalletService.getTxs()) {
|
for (MoneroTxWallet tx : xmrWalletService.getTxs()) {
|
||||||
|
|
||||||
// get spendable amount
|
// get unscheduled spendable amount
|
||||||
BigInteger spendableAmount = getSpendableAmount(tx);
|
BigInteger spendableAmount = getUnscheduledSpendableAmount(tx, openOffers);
|
||||||
|
|
||||||
// skip if no spendable amount or already scheduled
|
// skip if no spendable amount
|
||||||
if (spendableAmount.equals(BigInteger.ZERO)) continue;
|
if (spendableAmount.equals(BigInteger.ZERO)) continue;
|
||||||
if (isTxScheduledByOtherOffer(openOffers, openOffer, tx.getHash())) continue;
|
|
||||||
|
|
||||||
// schedule tx
|
// schedule tx
|
||||||
scheduledAmount = scheduledAmount.add(spendableAmount);
|
scheduledAmount = scheduledAmount.add(spendableAmount);
|
||||||
|
@ -1184,7 +1177,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
// break if sufficient funds
|
// break if sufficient funds
|
||||||
if (scheduledAmount.compareTo(offerReserveAmount) >= 0) break;
|
if (scheduledAmount.compareTo(offerReserveAmount) >= 0) break;
|
||||||
}
|
}
|
||||||
if (scheduledAmount.compareTo(offerReserveAmount) < 0) throw new RuntimeException("Not enough funds to schedule offer");
|
if (scheduledAmount.compareTo(offerReserveAmount) < 0) throw new RuntimeException("Not enough funds to create offer");
|
||||||
|
|
||||||
// schedule txs
|
// schedule txs
|
||||||
openOffer.setScheduledTxHashes(scheduledTxs.stream().map(tx -> tx.getHash()).collect(Collectors.toList()));
|
openOffer.setScheduledTxHashes(scheduledTxs.stream().map(tx -> tx.getHash()).collect(Collectors.toList()));
|
||||||
|
@ -1192,6 +1185,30 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
openOffer.setState(OpenOffer.State.PENDING);
|
openOffer.setState(OpenOffer.State.PENDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BigInteger getUnscheduledSpendableAmount(MoneroTxWallet tx, List<OpenOffer> openOffers) {
|
||||||
|
if (isScheduledWithUnknownAmount(tx, openOffers)) return BigInteger.ZERO;
|
||||||
|
return getSpendableAmount(tx).subtract(getSplitAmount(tx, openOffers)).max(BigInteger.ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isScheduledWithUnknownAmount(MoneroTxWallet tx, List<OpenOffer> openOffers) {
|
||||||
|
for (OpenOffer openOffer : openOffers) {
|
||||||
|
if (openOffer.getScheduledTxHashes() == null) continue;
|
||||||
|
if (openOffer.getScheduledTxHashes().contains(tx.getHash()) && !tx.getHash().equals(openOffer.getSplitOutputTxHash())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigInteger getSplitAmount(MoneroTxWallet tx, List<OpenOffer> openOffers) {
|
||||||
|
for (OpenOffer openOffer : openOffers) {
|
||||||
|
if (openOffer.getSplitOutputTxHash() == null) continue;
|
||||||
|
if (!openOffer.getSplitOutputTxHash().equals(tx.getHash())) continue;
|
||||||
|
return openOffer.getOffer().getAmountNeeded();
|
||||||
|
}
|
||||||
|
return BigInteger.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
private BigInteger getSpendableAmount(MoneroTxWallet tx) {
|
private BigInteger getSpendableAmount(MoneroTxWallet tx) {
|
||||||
|
|
||||||
// compute spendable amount from outputs if confirmed
|
// compute spendable amount from outputs if confirmed
|
||||||
|
@ -1220,23 +1237,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
return getSpendableAmount(tx).compareTo(BigInteger.ZERO) > 0;
|
return getSpendableAmount(tx).compareTo(BigInteger.ZERO) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigInteger getScheduledAmount(List<OpenOffer> openOffers) {
|
|
||||||
BigInteger scheduledAmount = BigInteger.ZERO;
|
|
||||||
for (OpenOffer openOffer : openOffers) {
|
|
||||||
if (openOffer.getState() != OpenOffer.State.PENDING) continue;
|
|
||||||
if (openOffer.getScheduledTxHashes() == null) continue;
|
|
||||||
List<MoneroTxWallet> fundingTxs = xmrWalletService.getTxs(openOffer.getScheduledTxHashes());
|
|
||||||
for (MoneroTxWallet fundingTx : fundingTxs) {
|
|
||||||
if (fundingTx.getIncomingTransfers() != null) {
|
|
||||||
for (MoneroIncomingTransfer transfer : fundingTx.getIncomingTransfers()) {
|
|
||||||
if (transfer.getAccountIndex() == 0) scheduledAmount = scheduledAmount.add(transfer.getAmount());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return scheduledAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isTxScheduledByOtherOffer(List<OpenOffer> openOffers, OpenOffer openOffer, String txHash) {
|
private boolean isTxScheduledByOtherOffer(List<OpenOffer> openOffers, OpenOffer openOffer, String txHash) {
|
||||||
for (OpenOffer otherOffer : openOffers) {
|
for (OpenOffer otherOffer : openOffers) {
|
||||||
if (otherOffer == openOffer) continue;
|
if (otherOffer == openOffer) continue;
|
||||||
|
|
|
@ -60,6 +60,6 @@
|
||||||
</content_rating>
|
</content_rating>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
<release version="1.0.15" date="2024-12-19"/>
|
<release version="1.0.17" date="2024-12-21"/>
|
||||||
</releases>
|
</releases>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
<!-- See: https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -->
|
<!-- See: https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -->
|
||||||
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.0.15</string>
|
<string>1.0.17</string>
|
||||||
|
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.0.15</string>
|
<string>1.0.17</string>
|
||||||
|
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>Haveno</string>
|
<string>Haveno</string>
|
||||||
|
|
|
@ -200,7 +200,7 @@ public class HavenoAppMain extends HavenoExecutable {
|
||||||
|
|
||||||
// Add an icon to the dialog
|
// Add an icon to the dialog
|
||||||
Stage stage = (Stage) getDialogPane().getScene().getWindow();
|
Stage stage = (Stage) getDialogPane().getScene().getWindow();
|
||||||
stage.getIcons().add(ImageUtil.getImageByPath("lock.png"));
|
stage.getIcons().add(ImageUtil.getImageByPath("lock@2x.png"));
|
||||||
|
|
||||||
// Create the password field
|
// Create the password field
|
||||||
PasswordField passwordField = new PasswordField();
|
PasswordField passwordField = new PasswordField();
|
||||||
|
|
|
@ -38,8 +38,8 @@
|
||||||
<TableColumn fx:id="transactionColumn" minWidth="180"/>
|
<TableColumn fx:id="transactionColumn" minWidth="180"/>
|
||||||
<TableColumn fx:id="amountColumn" minWidth="110" maxWidth="110"/>
|
<TableColumn fx:id="amountColumn" minWidth="110" maxWidth="110"/>
|
||||||
<TableColumn fx:id="txFeeColumn" minWidth="110" maxWidth="110"/>
|
<TableColumn fx:id="txFeeColumn" minWidth="110" maxWidth="110"/>
|
||||||
|
<TableColumn fx:id="confidenceColumn" minWidth="60" maxWidth="130"/>
|
||||||
<TableColumn fx:id="memoColumn" minWidth="40"/>
|
<TableColumn fx:id="memoColumn" minWidth="40"/>
|
||||||
<TableColumn fx:id="confidenceColumn" minWidth="120" maxWidth="130"/>
|
|
||||||
<TableColumn fx:id="revertTxColumn" sortable="false" minWidth="110" maxWidth="110" visible="false"/>
|
<TableColumn fx:id="revertTxColumn" sortable="false" minWidth="110" maxWidth="110" visible="false"/>
|
||||||
</columns>
|
</columns>
|
||||||
</TableView>
|
</TableView>
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||||
@FXML
|
@FXML
|
||||||
TableView<TransactionsListItem> tableView;
|
TableView<TransactionsListItem> tableView;
|
||||||
@FXML
|
@FXML
|
||||||
TableColumn<TransactionsListItem, TransactionsListItem> dateColumn, detailsColumn, addressColumn, transactionColumn, amountColumn, txFeeColumn, memoColumn, confidenceColumn, revertTxColumn;
|
TableColumn<TransactionsListItem, TransactionsListItem> dateColumn, detailsColumn, addressColumn, transactionColumn, amountColumn, txFeeColumn, confidenceColumn, memoColumn, revertTxColumn;
|
||||||
@FXML
|
@FXML
|
||||||
Label numItems;
|
Label numItems;
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -133,8 +133,8 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||||
transactionColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.txId", Res.getBaseCurrencyCode())));
|
transactionColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.txId", Res.getBaseCurrencyCode())));
|
||||||
amountColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode())));
|
amountColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode())));
|
||||||
txFeeColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.txFee", Res.getBaseCurrencyCode())));
|
txFeeColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.txFee", Res.getBaseCurrencyCode())));
|
||||||
memoColumn.setGraphic(new AutoTooltipLabel(Res.get("funds.tx.memo")));
|
|
||||||
confidenceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.confirmations", Res.getBaseCurrencyCode())));
|
confidenceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.confirmations", Res.getBaseCurrencyCode())));
|
||||||
|
memoColumn.setGraphic(new AutoTooltipLabel(Res.get("funds.tx.memo")));
|
||||||
revertTxColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.revert", Res.getBaseCurrencyCode())));
|
revertTxColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.revert", Res.getBaseCurrencyCode())));
|
||||||
|
|
||||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN);
|
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN);
|
||||||
|
@ -146,8 +146,8 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||||
setTransactionColumnCellFactory();
|
setTransactionColumnCellFactory();
|
||||||
setAmountColumnCellFactory();
|
setAmountColumnCellFactory();
|
||||||
setTxFeeColumnCellFactory();
|
setTxFeeColumnCellFactory();
|
||||||
setMemoColumnCellFactory();
|
|
||||||
setConfidenceColumnCellFactory();
|
setConfidenceColumnCellFactory();
|
||||||
|
setMemoColumnCellFactory();
|
||||||
setRevertTxColumnCellFactory();
|
setRevertTxColumnCellFactory();
|
||||||
|
|
||||||
dateColumn.setComparator(Comparator.comparing(TransactionsListItem::getDate));
|
dateColumn.setComparator(Comparator.comparing(TransactionsListItem::getDate));
|
||||||
|
@ -221,8 +221,8 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||||
columns[3] = item.getTxId();
|
columns[3] = item.getTxId();
|
||||||
columns[4] = item.getAmountStr();
|
columns[4] = item.getAmountStr();
|
||||||
columns[5] = item.getTxFeeStr();
|
columns[5] = item.getTxFeeStr();
|
||||||
columns[6] = item.getMemo() == null ? "" : item.getMemo();
|
columns[6] = String.valueOf(item.getNumConfirmations());
|
||||||
columns[7] = String.valueOf(item.getNumConfirmations());
|
columns[7] = item.getMemo() == null ? "" : item.getMemo();
|
||||||
return columns;
|
return columns;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ import haveno.core.util.coin.CoinUtil;
|
||||||
import haveno.core.util.validation.AmountValidator4Decimals;
|
import haveno.core.util.validation.AmountValidator4Decimals;
|
||||||
import haveno.core.util.validation.AmountValidator8Decimals;
|
import haveno.core.util.validation.AmountValidator8Decimals;
|
||||||
import haveno.core.util.validation.InputValidator;
|
import haveno.core.util.validation.InputValidator;
|
||||||
|
import haveno.core.util.validation.InputValidator.ValidationResult;
|
||||||
import haveno.core.util.validation.MonetaryValidator;
|
import haveno.core.util.validation.MonetaryValidator;
|
||||||
import haveno.core.xmr.wallet.Restrictions;
|
import haveno.core.xmr.wallet.Restrictions;
|
||||||
import haveno.desktop.Navigation;
|
import haveno.desktop.Navigation;
|
||||||
|
@ -490,6 +491,8 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
xmrValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit()));
|
xmrValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit()));
|
||||||
if (amount.get() != null) amountValidationResult.set(isXmrInputValid(amount.get()));
|
if (amount.get() != null) amountValidationResult.set(isXmrInputValid(amount.get()));
|
||||||
updateSecurityDeposit();
|
updateSecurityDeposit();
|
||||||
|
setSecurityDepositToModel();
|
||||||
|
onFocusOutSecurityDepositTextField(true, false); // refresh security deposit field
|
||||||
applyMakerFee();
|
applyMakerFee();
|
||||||
dataModel.calculateTotalToPay();
|
dataModel.calculateTotalToPay();
|
||||||
updateButtonDisableState();
|
updateButtonDisableState();
|
||||||
|
@ -769,7 +772,8 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We want to trigger a recalculation of the volume
|
|
||||||
|
// trigger recalculation of the volume
|
||||||
UserThread.execute(() -> {
|
UserThread.execute(() -> {
|
||||||
onFocusOutVolumeTextField(true, false);
|
onFocusOutVolumeTextField(true, false);
|
||||||
onFocusOutMinAmountTextField(true, false);
|
onFocusOutMinAmountTextField(true, false);
|
||||||
|
@ -815,6 +819,11 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
}
|
}
|
||||||
|
|
||||||
maybeShowMakeOfferToUnsignedAccountWarning();
|
maybeShowMakeOfferToUnsignedAccountWarning();
|
||||||
|
|
||||||
|
// trigger recalculation of the security deposit
|
||||||
|
UserThread.execute(() -> {
|
||||||
|
onFocusOutSecurityDepositTextField(true, false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -944,11 +953,16 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
if (marketPriceMargin.get() == null && amount.get() != null && volume.get() != null) {
|
if (marketPriceMargin.get() == null && amount.get() != null && volume.get() != null) {
|
||||||
updateMarketPriceToManual();
|
updateMarketPriceToManual();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trigger recalculation of security deposit
|
||||||
|
UserThread.execute(() -> {
|
||||||
|
onFocusOutSecurityDepositTextField(true, false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onFocusOutSecurityDepositTextField(boolean oldValue, boolean newValue) {
|
void onFocusOutSecurityDepositTextField(boolean oldValue, boolean newValue) {
|
||||||
if (oldValue && !newValue) {
|
if (oldValue && !newValue && !isMinSecurityDeposit.get()) {
|
||||||
InputValidator.ValidationResult result = securityDepositValidator.validate(securityDeposit.get());
|
InputValidator.ValidationResult result = securityDepositValidator.validate(securityDeposit.get());
|
||||||
securityDepositValidationResult.set(result);
|
securityDepositValidationResult.set(result);
|
||||||
if (result.isValid) {
|
if (result.isValid) {
|
||||||
|
@ -1040,6 +1054,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
|
|
||||||
public String getSecurityDepositLabel() {
|
public String getSecurityDepositLabel() {
|
||||||
return dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer() ? Res.get("createOffer.myDeposit") :
|
return dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer() ? Res.get("createOffer.myDeposit") :
|
||||||
|
dataModel.isMinSecurityDeposit() ? Res.get("createOffer.minSecurityDepositUsed") :
|
||||||
Preferences.USE_SYMMETRIC_SECURITY_DEPOSIT ? Res.get("createOffer.setDepositForBothTraders") :
|
Preferences.USE_SYMMETRIC_SECURITY_DEPOSIT ? Res.get("createOffer.setDepositForBothTraders") :
|
||||||
dataModel.isBuyOffer() ? Res.get("createOffer.setDepositAsBuyer") : Res.get("createOffer.setDeposit");
|
dataModel.isBuyOffer() ? Res.get("createOffer.setDepositAsBuyer") : Res.get("createOffer.setDeposit");
|
||||||
}
|
}
|
||||||
|
@ -1211,7 +1226,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSecurityDepositToModel() {
|
private void setSecurityDepositToModel() {
|
||||||
if (!(dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer()) && securityDeposit.get() != null && !securityDeposit.get().isEmpty()) {
|
if (securityDeposit.get() != null && !securityDeposit.get().isEmpty() && !isMinSecurityDeposit.get()) {
|
||||||
dataModel.setSecurityDepositPct(ParsingUtils.parsePercentStringToDouble(securityDeposit.get()));
|
dataModel.setSecurityDepositPct(ParsingUtils.parsePercentStringToDouble(securityDeposit.get()));
|
||||||
} else {
|
} else {
|
||||||
dataModel.setSecurityDepositPct(Restrictions.getDefaultSecurityDepositAsPercent());
|
dataModel.setSecurityDepositPct(Restrictions.getDefaultSecurityDepositAsPercent());
|
||||||
|
@ -1282,11 +1297,11 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
|
|
||||||
private void updateSecurityDeposit() {
|
private void updateSecurityDeposit() {
|
||||||
isMinSecurityDeposit.set(dataModel.isMinSecurityDeposit());
|
isMinSecurityDeposit.set(dataModel.isMinSecurityDeposit());
|
||||||
if (dataModel.isMinSecurityDeposit()) {
|
|
||||||
securityDepositLabel.set(Res.get("createOffer.minSecurityDepositUsed"));
|
|
||||||
securityDeposit.set(HavenoUtils.formatXmr(Restrictions.getMinSecurityDeposit()));
|
|
||||||
} else {
|
|
||||||
securityDepositLabel.set(getSecurityDepositLabel());
|
securityDepositLabel.set(getSecurityDepositLabel());
|
||||||
|
if (dataModel.isMinSecurityDeposit()) {
|
||||||
|
securityDeposit.set(HavenoUtils.formatXmr(Restrictions.getMinSecurityDeposit()));
|
||||||
|
securityDepositValidationResult.set(new ValidationResult(true));
|
||||||
|
} else {
|
||||||
boolean hasBuyerAsTakerWithoutDeposit = dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer();
|
boolean hasBuyerAsTakerWithoutDeposit = dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer();
|
||||||
securityDeposit.set(FormattingUtils.formatToPercent(hasBuyerAsTakerWithoutDeposit ?
|
securityDeposit.set(FormattingUtils.formatToPercent(hasBuyerAsTakerWithoutDeposit ?
|
||||||
Restrictions.getDefaultSecurityDepositAsPercent() : // use default percent if no deposit from buyer
|
Restrictions.getDefaultSecurityDepositAsPercent() : // use default percent if no deposit from buyer
|
||||||
|
|
|
@ -185,7 +185,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
|
||||||
paymentMethodComboBox.setCellFactory(GUIUtil.getPaymentMethodCellFactory());
|
paymentMethodComboBox.setCellFactory(GUIUtil.getPaymentMethodCellFactory());
|
||||||
paymentMethodComboBox.setPrefWidth(250);
|
paymentMethodComboBox.setPrefWidth(250);
|
||||||
|
|
||||||
matchingOffersToggleButton = AwesomeDude.createIconToggleButton(AwesomeIcon.USER, null, "1.3em", null);
|
matchingOffersToggleButton = AwesomeDude.createIconToggleButton(AwesomeIcon.USER, null, "1.5em", null);
|
||||||
matchingOffersToggleButton.getStyleClass().add("toggle-button-no-slider");
|
matchingOffersToggleButton.getStyleClass().add("toggle-button-no-slider");
|
||||||
matchingOffersToggleButton.setPrefHeight(27);
|
matchingOffersToggleButton.setPrefHeight(27);
|
||||||
Tooltip matchingOffersTooltip = new Tooltip(Res.get("offerbook.matchingOffers"));
|
Tooltip matchingOffersTooltip = new Tooltip(Res.get("offerbook.matchingOffers"));
|
||||||
|
@ -1069,6 +1069,10 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
|
||||||
return new TableCell<>() {
|
return new TableCell<>() {
|
||||||
OfferFilterService.Result canTakeOfferResult = null;
|
OfferFilterService.Result canTakeOfferResult = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateItem(final OfferBookListItem item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
|
||||||
final ImageView iconView = new ImageView();
|
final ImageView iconView = new ImageView();
|
||||||
final AutoTooltipButton button = new AutoTooltipButton();
|
final AutoTooltipButton button = new AutoTooltipButton();
|
||||||
|
|
||||||
|
@ -1098,10 +1102,6 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
|
||||||
HBox.setHgrow(button2, Priority.ALWAYS);
|
HBox.setHgrow(button2, Priority.ALWAYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateItem(final OfferBookListItem item, boolean empty) {
|
|
||||||
super.updateItem(item, empty);
|
|
||||||
|
|
||||||
TableRow<OfferBookListItem> tableRow = getTableRow();
|
TableRow<OfferBookListItem> tableRow = getTableRow();
|
||||||
if (item != null && !empty) {
|
if (item != null && !empty) {
|
||||||
Offer offer = item.getOffer();
|
Offer offer = item.getOffer();
|
||||||
|
|
|
@ -260,7 +260,10 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
|
||||||
showAllTradeCurrenciesProperty.set(showAllEntry);
|
showAllTradeCurrenciesProperty.set(showAllEntry);
|
||||||
if (isEditEntry(code))
|
if (isEditEntry(code))
|
||||||
navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class);
|
navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class);
|
||||||
else if (!showAllEntry) {
|
else if (showAllEntry) {
|
||||||
|
this.selectedTradeCurrency = getDefaultTradeCurrency();
|
||||||
|
tradeCurrencyCode.set(selectedTradeCurrency.getCode());
|
||||||
|
} else {
|
||||||
this.selectedTradeCurrency = tradeCurrency;
|
this.selectedTradeCurrency = tradeCurrency;
|
||||||
tradeCurrencyCode.set(code);
|
tradeCurrencyCode.set(code);
|
||||||
}
|
}
|
||||||
|
@ -579,7 +582,10 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
|
||||||
getCurrencyAndMethodPredicate(direction, selectedTradeCurrency).and(getOffersMatchingMyAccountsPredicate()) :
|
getCurrencyAndMethodPredicate(direction, selectedTradeCurrency).and(getOffersMatchingMyAccountsPredicate()) :
|
||||||
getCurrencyAndMethodPredicate(direction, selectedTradeCurrency);
|
getCurrencyAndMethodPredicate(direction, selectedTradeCurrency);
|
||||||
|
|
||||||
|
// filter private offers
|
||||||
|
if (direction == OfferDirection.BUY) {
|
||||||
predicate = predicate.and(offerBookListItem -> offerBookListItem.getOffer().isPrivateOffer() == showPrivateOffers);
|
predicate = predicate.and(offerBookListItem -> offerBookListItem.getOffer().isPrivateOffer() == showPrivateOffers);
|
||||||
|
}
|
||||||
|
|
||||||
if (!filterText.isEmpty()) {
|
if (!filterText.isEmpty()) {
|
||||||
|
|
||||||
|
|
|
@ -561,8 +561,10 @@
|
||||||
.toggle-button-no-slider {
|
.toggle-button-no-slider {
|
||||||
-fx-focus-color: transparent;
|
-fx-focus-color: transparent;
|
||||||
-fx-faint-focus-color: transparent;
|
-fx-faint-focus-color: transparent;
|
||||||
|
-fx-background-radius: 3;
|
||||||
|
-fx-background-insets: 0, 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-button-no-slider:selected {
|
.toggle-button-no-slider:selected {
|
||||||
-fx-background-color: -bs-color-gray-ddd;
|
-fx-background-color: -bs-color-gray-bbb;
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,10 @@ The price node is separated from Haveno and is run as a standalone service. To d
|
||||||
|
|
||||||
After the price node is built and deployed, add the price node to `DEFAULT_NODES` in [ProvidersRepository.java](https://github.com/haveno-dex/haveno/blob/3cdd88b56915c7f8afd4f1a39e6c1197c2665d63/core/src/main/java/haveno/core/provider/ProvidersRepository.java#L50).
|
After the price node is built and deployed, add the price node to `DEFAULT_NODES` in [ProvidersRepository.java](https://github.com/haveno-dex/haveno/blob/3cdd88b56915c7f8afd4f1a39e6c1197c2665d63/core/src/main/java/haveno/core/provider/ProvidersRepository.java#L50).
|
||||||
|
|
||||||
|
### Update the download URL
|
||||||
|
|
||||||
|
Change every instance of `https://haveno.exchange/downloads` to your download URL. For example, `https://havenoexample.com/downloads`.
|
||||||
|
|
||||||
## Review all local changes
|
## Review all local changes
|
||||||
|
|
||||||
For comparison, placeholders to run on mainnet are marked [here on this branch](https://github.com/haveno-dex/haveno/tree/mainnet_placeholders).
|
For comparison, placeholders to run on mainnet are marked [here on this branch](https://github.com/haveno-dex/haveno/tree/mainnet_placeholders).
|
||||||
|
|
|
@ -243,6 +243,10 @@ Set `ARBITRATOR_ASSIGNS_TRADE_FEE_ADDRESS` to `true` for the arbitrator to assig
|
||||||
|
|
||||||
Otherwise set `ARBITRATOR_ASSIGNS_TRADE_FEE_ADDRESS` to `false` and set the XMR address in `getGlobalTradeFeeAddress()` to collect all trade fees to a single address (e.g. a multisig wallet shared among network administrators).
|
Otherwise set `ARBITRATOR_ASSIGNS_TRADE_FEE_ADDRESS` to `false` and set the XMR address in `getGlobalTradeFeeAddress()` to collect all trade fees to a single address (e.g. a multisig wallet shared among network administrators).
|
||||||
|
|
||||||
|
## Update the download URL
|
||||||
|
|
||||||
|
Change every instance of `https://haveno.exchange/downloads` to your download URL. For example, `https://havenoexample.com/downloads`.
|
||||||
|
|
||||||
## Start users for testing
|
## Start users for testing
|
||||||
|
|
||||||
Start user1 on Monero's mainnet using `make user1-desktop-mainnet` or Monero's stagenet using `make user1-desktop-stagenet`.
|
Start user1 on Monero's mainnet using `make user1-desktop-mainnet` or Monero's stagenet using `make user1-desktop-stagenet`.
|
||||||
|
@ -266,7 +270,7 @@ Then follow these instructions: https://github.com/haveno-dex/haveno/blob/master
|
||||||
|
|
||||||
<b>Set the mandatory minimum version for trading (optional)</b>
|
<b>Set the mandatory minimum version for trading (optional)</b>
|
||||||
|
|
||||||
If applicable, update the mandatory minimum version for trading, by entering `ctrl + f` to open the Filter window, enter a private key with developer privileges, and enter the minimum version (e.g. 1.0.16) in the field labeled "Min. version required for trading".
|
If applicable, update the mandatory minimum version for trading, by entering `ctrl + f` to open the Filter window, enter a private key with developer privileges, and enter the minimum version (e.g. 1.0.17) in the field labeled "Min. version required for trading".
|
||||||
|
|
||||||
<b>Send update alert</b>
|
<b>Send update alert</b>
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,8 @@ Follow [instructions](https://github.com/haveno-dex/haveno-ts#run-tests) to run
|
||||||
For example, the gRPC function to get offers is implemented by [`GrpcServer`](https://github.com/haveno-dex/haveno/blob/master/daemon/src/main/java/haveno/daemon/grpc/GrpcServer.java) > [`GrpcOffersService.getOffers(...)`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/daemon/src/main/java/haveno/daemon/grpc/GrpcOffersService.java#L104) > [`CoreApi.getOffers(...)`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/core/src/main/java/haveno/core/api/CoreApi.java#L128) > [`CoreOffersService.getOffers(...)`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/core/src/main/java/haveno/core/api/CoreOffersService.java#L126) > [`OfferBookService.getOffers()`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/core/src/main/java/haveno/core/offer/OfferBookService.java#L193).
|
For example, the gRPC function to get offers is implemented by [`GrpcServer`](https://github.com/haveno-dex/haveno/blob/master/daemon/src/main/java/haveno/daemon/grpc/GrpcServer.java) > [`GrpcOffersService.getOffers(...)`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/daemon/src/main/java/haveno/daemon/grpc/GrpcOffersService.java#L104) > [`CoreApi.getOffers(...)`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/core/src/main/java/haveno/core/api/CoreApi.java#L128) > [`CoreOffersService.getOffers(...)`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/core/src/main/java/haveno/core/api/CoreOffersService.java#L126) > [`OfferBookService.getOffers()`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/core/src/main/java/haveno/core/offer/OfferBookService.java#L193).
|
||||||
5. Build Haveno: `make`
|
5. Build Haveno: `make`
|
||||||
6. Update the gRPC client in haveno-ts: `npm install`
|
6. Update the gRPC client in haveno-ts: `npm install`
|
||||||
7. Add the corresponding typescript method(s) to [haveno.ts](https://github.com/haveno-dex/haveno-ts/blob/master/src/haveno.ts) with clear and concise documentation.
|
7. Add the corresponding typescript method(s) to [HavenoClient.ts](https://github.com/haveno-dex/haveno-ts/blob/master/src/HavenoClient.ts) with clear and concise documentation.
|
||||||
8. Add clean and comprehensive tests to [haveno.test.ts](https://github.com/haveno-dex/haveno-ts/blob/master/src/haveno.test.ts), following existing patterns.
|
8. Add clean and comprehensive tests to [HavenoClient.test.ts](https://github.com/haveno-dex/haveno-ts/blob/master/src/HavenoClient.test.ts), following existing patterns.
|
||||||
9. Run the tests with `npm run test -- -t 'my test'` to run tests by name and `npm test` to run all tests together. Ensure all tests pass and there are no exception stacktraces in the terminals of Alice, Bob, or the arbitrator.
|
9. Run the tests with `npm run test -- -t 'my test'` to run tests by name and `npm test` to run all tests together. Ensure all tests pass and there are no exception stacktraces in the terminals of Alice, Bob, or the arbitrator.
|
||||||
10. Open pull requests to the haveno and haveno-ts projects for the backend and frontend implementations.
|
10. Open pull requests to the haveno and haveno-ts projects for the backend and frontend implementations.
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SeedNodeMain extends ExecutableForAppWithP2p {
|
public class SeedNodeMain extends ExecutableForAppWithP2p {
|
||||||
private static final long CHECK_CONNECTION_LOSS_SEC = 30;
|
private static final long CHECK_CONNECTION_LOSS_SEC = 30;
|
||||||
private static final String VERSION = "1.0.15";
|
private static final String VERSION = "1.0.17";
|
||||||
private SeedNode seedNode;
|
private SeedNode seedNode;
|
||||||
private Timer checkConnectionLossTime;
|
private Timer checkConnectionLossTime;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue