mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 23:18:17 +01:00
Savings wallet (WIP)
This commit is contained in:
parent
f07aa9ba6a
commit
091eae4965
19 changed files with 288 additions and 169 deletions
|
@ -36,12 +36,12 @@ public class FeePolicy {
|
||||||
// disputed payout tx: 408 bytes
|
// disputed payout tx: 408 bytes
|
||||||
|
|
||||||
// We set a fixed fee to make the needed amounts in the trade predictable.
|
// We set a fixed fee to make the needed amounts in the trade predictable.
|
||||||
// We use 0.0003 BTC (0.12 EUR @ 400 EUR/BTC) which is for our tx sizes about 75-150 satoshi/byte
|
// We use 0.0002 BTC (0.08 EUR @ 400 EUR/BTC) which is for our tx sizes about 50-90 satoshi/byte
|
||||||
// We cannot make that user defined as it need to be the same for both users, so we can only change that in
|
// We cannot make that user defined as it need to be the same for both users, so we can only change that in
|
||||||
// software updates
|
// software updates
|
||||||
// TODO before Beta we should get a good future proof guess as a change causes incompatible versions
|
// TODO before Beta we should get a good future proof guess as a change causes incompatible versions
|
||||||
public static Coin getFixedTxFeeForTrades() {
|
public static Coin getFixedTxFeeForTrades() {
|
||||||
return Coin.valueOf(30_000);
|
return Coin.valueOf(20_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For non trade transactions (withdrawal) we use the default fee calculation
|
// For non trade transactions (withdrawal) we use the default fee calculation
|
||||||
|
@ -50,7 +50,7 @@ public class FeePolicy {
|
||||||
// The BitcoinJ fee calculation use kb so a tx size < 1kb will still pay the fee for a kb tx.
|
// The BitcoinJ fee calculation use kb so a tx size < 1kb will still pay the fee for a kb tx.
|
||||||
// Our payout tx has about 370 bytes so we get a fee/kb value of about 90 satoshi/byte making it high priority
|
// Our payout tx has about 370 bytes so we get a fee/kb value of about 90 satoshi/byte making it high priority
|
||||||
// Other payout transactions (E.g. arbitrators many collected transactions) will go with 30 satoshi/byte if > 1kb
|
// Other payout transactions (E.g. arbitrators many collected transactions) will go with 30 satoshi/byte if > 1kb
|
||||||
private static Coin FEE_PER_KB = Coin.valueOf(30_000); // 0.0003 BTC about 0.12 EUR @ 400 EUR/BTC
|
private static Coin FEE_PER_KB = Coin.valueOf(20_000); // 0.0002 BTC about 0.08 EUR @ 400 EUR/BTC
|
||||||
|
|
||||||
public static void setFeePerKb(Coin feePerKb) {
|
public static void setFeePerKb(Coin feePerKb) {
|
||||||
FEE_PER_KB = feePerKb;
|
FEE_PER_KB = feePerKb;
|
||||||
|
@ -84,8 +84,8 @@ public class FeePolicy {
|
||||||
|
|
||||||
|
|
||||||
// TODO will be increased once we get higher limits
|
// TODO will be increased once we get higher limits
|
||||||
// 0.01 BTC; about 0.4 EUR @ 400 EUR/BTC
|
// 0.02 BTC; about 8 EUR @ 400 EUR/BTC
|
||||||
public static Coin getSecurityDeposit() {
|
public static Coin getSecurityDeposit() {
|
||||||
return Coin.valueOf(1_000_000);
|
return Coin.valueOf(2_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -333,6 +333,12 @@ public class WalletService {
|
||||||
.findAny();
|
.findAny();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<AddressEntry> getTradeAddressEntryList() {
|
||||||
|
return getAddressEntryList().stream()
|
||||||
|
.filter(e -> e.getContext().equals(AddressEntry.Context.TRADE))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// SavingsAddressEntry
|
// SavingsAddressEntry
|
||||||
|
@ -493,6 +499,10 @@ public class WalletService {
|
||||||
return wallet != null ? getBalance(wallet.calculateAllSpendCandidates(), address) : Coin.ZERO;
|
return wallet != null ? getBalance(wallet.calculateAllSpendCandidates(), address) : Coin.ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Coin getBalanceForAddressEntryWithTradeId(String tradeId) {
|
||||||
|
return getBalanceForAddress(getTradeAddressEntry(tradeId).getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
private Coin getBalance(List<TransactionOutput> transactionOutputs, Address address) {
|
private Coin getBalance(List<TransactionOutput> transactionOutputs, Address address) {
|
||||||
Coin balance = Coin.ZERO;
|
Coin balance = Coin.ZERO;
|
||||||
for (TransactionOutput transactionOutput : transactionOutputs) {
|
for (TransactionOutput transactionOutput : transactionOutputs) {
|
||||||
|
@ -539,7 +549,8 @@ public class WalletService {
|
||||||
Coin fee;
|
Coin fee;
|
||||||
try {
|
try {
|
||||||
wallet.completeTx(getSendRequest(fromAddress, toAddress, amount, aesKey));
|
wallet.completeTx(getSendRequest(fromAddress, toAddress, amount, aesKey));
|
||||||
fee = Coin.ZERO;
|
// We use the min fee for now as the mix of savingswallet/trade wallet has some nasty edge cases...
|
||||||
|
fee = FeePolicy.getFixedTxFeeForTrades();
|
||||||
} catch (InsufficientMoneyException e) {
|
} catch (InsufficientMoneyException e) {
|
||||||
log.info("The amount to be transferred is not enough to pay the transaction fees of {}. " +
|
log.info("The amount to be transferred is not enough to pay the transaction fees of {}. " +
|
||||||
"We subtract that fee from the receivers amount to make the transaction possible.");
|
"We subtract that fee from the receivers amount to make the transaction possible.");
|
||||||
|
@ -556,7 +567,8 @@ public class WalletService {
|
||||||
Coin fee;
|
Coin fee;
|
||||||
try {
|
try {
|
||||||
wallet.completeTx(getSendRequestForMultipleAddresses(fromAddresses, toAddress, amount, null, aesKey));
|
wallet.completeTx(getSendRequestForMultipleAddresses(fromAddresses, toAddress, amount, null, aesKey));
|
||||||
fee = Coin.ZERO;
|
// We use the min fee for now as the mix of savingswallet/trade wallet has some nasty edge cases...
|
||||||
|
fee = FeePolicy.getFixedTxFeeForTrades();
|
||||||
} catch (InsufficientMoneyException e) {
|
} catch (InsufficientMoneyException e) {
|
||||||
log.info("The amount to be transferred is not enough to pay the transaction fees of {}. " +
|
log.info("The amount to be transferred is not enough to pay the transaction fees of {}. " +
|
||||||
"We subtract that fee from the receivers amount to make the transaction possible.");
|
"We subtract that fee from the receivers amount to make the transaction possible.");
|
||||||
|
|
|
@ -67,18 +67,18 @@ public final class PaymentMethod implements Persistable, Comparable {
|
||||||
public static PaymentMethod BLOCK_CHAINS;
|
public static PaymentMethod BLOCK_CHAINS;
|
||||||
|
|
||||||
public static final List<PaymentMethod> ALL_VALUES = new ArrayList<>(Arrays.asList(
|
public static final List<PaymentMethod> ALL_VALUES = new ArrayList<>(Arrays.asList(
|
||||||
OK_PAY = new PaymentMethod(OK_PAY_ID, 0, DAY, Coin.parseCoin("0.4")), // tx instant so min. wait time
|
OK_PAY = new PaymentMethod(OK_PAY_ID, 0, DAY, Coin.parseCoin("1")), // tx instant so min. wait time
|
||||||
PERFECT_MONEY = new PaymentMethod(PERFECT_MONEY_ID, 0, DAY, Coin.parseCoin("0.4")),
|
SEPA = new PaymentMethod(SEPA_ID, 0, 8 * DAY, Coin.parseCoin("0.5")), // sepa takes 1-3 business days. We use 8 days to include safety for holidays
|
||||||
SEPA = new PaymentMethod(SEPA_ID, 0, 8 * DAY, Coin.parseCoin("0.3")), // sepa takes 1-3 business days. We use 8 days to include safety for holidays
|
NATIONAL_BANK = new PaymentMethod(NATIONAL_BANK_ID, 0, 4 * DAY, Coin.parseCoin("0.5")),
|
||||||
NATIONAL_BANK = new PaymentMethod(NATIONAL_BANK_ID, 0, 4 * DAY, Coin.parseCoin("0.3")),
|
SAME_BANK = new PaymentMethod(SAME_BANK_ID, 0, 2 * DAY, Coin.parseCoin("0.5")),
|
||||||
SAME_BANK = new PaymentMethod(SAME_BANK_ID, 0, 2 * DAY, Coin.parseCoin("0.3")),
|
SPECIFIC_BANKS = new PaymentMethod(SPECIFIC_BANKS_ID, 0, 4 * DAY, Coin.parseCoin("0.5")),
|
||||||
SPECIFIC_BANKS = new PaymentMethod(SPECIFIC_BANKS_ID, 0, 4 * DAY, Coin.parseCoin("0.3")),
|
PERFECT_MONEY = new PaymentMethod(PERFECT_MONEY_ID, 0, DAY, Coin.parseCoin("0.75")),
|
||||||
SWISH = new PaymentMethod(SWISH_ID, 0, DAY, Coin.parseCoin("0.4")),
|
SWISH = new PaymentMethod(SWISH_ID, 0, DAY, Coin.parseCoin("0.75")),
|
||||||
ALI_PAY = new PaymentMethod(ALI_PAY_ID, 0, DAY, Coin.parseCoin("0.4")),
|
ALI_PAY = new PaymentMethod(ALI_PAY_ID, 0, DAY, Coin.parseCoin("0.75")),
|
||||||
/* FED_WIRE = new PaymentMethod(FED_WIRE_ID, 0, DAY, Coin.parseCoin("0.1")),*/
|
/* FED_WIRE = new PaymentMethod(FED_WIRE_ID, 0, DAY, Coin.parseCoin("0.1")),*/
|
||||||
/* TRANSFER_WISE = new PaymentMethod(TRANSFER_WISE_ID, 0, DAY, Coin.parseCoin("0.1")),*/
|
/* TRANSFER_WISE = new PaymentMethod(TRANSFER_WISE_ID, 0, DAY, Coin.parseCoin("0.1")),*/
|
||||||
/* US_POSTAL_MONEY_ORDER = new PaymentMethod(US_POSTAL_MONEY_ORDER_ID, 0, DAY, Coin.parseCoin("0.1")),*/
|
/* US_POSTAL_MONEY_ORDER = new PaymentMethod(US_POSTAL_MONEY_ORDER_ID, 0, DAY, Coin.parseCoin("0.1")),*/
|
||||||
BLOCK_CHAINS = new PaymentMethod(BLOCK_CHAINS_ID, 0, DAY, Coin.parseCoin("0.5"))
|
BLOCK_CHAINS = new PaymentMethod(BLOCK_CHAINS_ID, 0, DAY, Coin.parseCoin("1"))
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
package io.bitsquare.trade;
|
|
||||||
|
|
||||||
import io.bitsquare.btc.AddressEntry;
|
|
||||||
import io.bitsquare.btc.WalletService;
|
|
||||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class TradableCollections {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TradableCollections.class);
|
|
||||||
|
|
||||||
public static List<AddressEntry> getAddressEntriesForAvailableBalance(OpenOfferManager openOfferManager, TradeManager tradeManager, WalletService walletService) {
|
|
||||||
Set<String> reservedTrades = getNotCompletedTradableItems(openOfferManager, tradeManager).stream()
|
|
||||||
.map(tradable -> tradable.getOffer().getId())
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
|
|
||||||
List<AddressEntry> list = new ArrayList<>();
|
|
||||||
list.addAll(walletService.getAddressEntryList().stream()
|
|
||||||
.filter(e -> walletService.getBalanceForAddress(e.getAddress()).isPositive())
|
|
||||||
.filter(e -> !reservedTrades.contains(e.getOfferId()))
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Set<Tradable> getNotCompletedTradableItems(OpenOfferManager openOfferManager, TradeManager tradeManager) {
|
|
||||||
return Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream())
|
|
||||||
.filter(tradable -> !(tradable instanceof Trade) || ((Trade) tradable).getState().getPhase() != Trade.Phase.PAYOUT_PAID)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
}
|
|
110
core/src/main/java/io/bitsquare/trade/TradableHelper.java
Normal file
110
core/src/main/java/io/bitsquare/trade/TradableHelper.java
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
|
import io.bitsquare.btc.AddressEntry;
|
||||||
|
import io.bitsquare.btc.FeePolicy;
|
||||||
|
import io.bitsquare.btc.WalletService;
|
||||||
|
import io.bitsquare.trade.closed.ClosedTradableManager;
|
||||||
|
import io.bitsquare.trade.failed.FailedTradesManager;
|
||||||
|
import io.bitsquare.trade.offer.Offer;
|
||||||
|
import io.bitsquare.trade.offer.OpenOffer;
|
||||||
|
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||||
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class TradableHelper {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(TradableHelper.class);
|
||||||
|
|
||||||
|
public static List<AddressEntry> getAddressEntriesForAvailableBalance(OpenOfferManager openOfferManager, TradeManager tradeManager, WalletService walletService) {
|
||||||
|
Set<String> reservedTradeIds = getNotCompletedTradableItems(openOfferManager, tradeManager).stream()
|
||||||
|
.map(tradable -> tradable.getOffer().getId())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
return walletService.getAddressEntryList().stream()
|
||||||
|
.filter(e -> walletService.getBalanceForAddress(e.getAddress()).isPositive())
|
||||||
|
.filter(e -> !reservedTradeIds.contains(e.getOfferId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Tradable> getNotCompletedTradableItems(OpenOfferManager openOfferManager, TradeManager tradeManager) {
|
||||||
|
return Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream())
|
||||||
|
.filter(tradable -> !(tradable instanceof Trade) || ((Trade) tradable).getState().getPhase() != Trade.Phase.PAYOUT_PAID)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Coin getBalanceInOpenOffer(OpenOffer openOffer) {
|
||||||
|
Offer offer = openOffer.getOffer();
|
||||||
|
Coin balance = FeePolicy.getSecurityDeposit().add(FeePolicy.getFeePerKb());
|
||||||
|
// For the seller we add the trade amount
|
||||||
|
if (offer.getDirection() == Offer.Direction.SELL)
|
||||||
|
balance = balance.add(offer.getAmount());
|
||||||
|
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Coin getBalanceInTrade(Trade trade, WalletService walletService) {
|
||||||
|
AddressEntry addressEntry = walletService.getTradeAddressEntry(trade.getId());
|
||||||
|
Coin balance = FeePolicy.getSecurityDeposit().add(FeePolicy.getFeePerKb());
|
||||||
|
// For the seller we add the trade amount
|
||||||
|
if (trade.getContract() != null &&
|
||||||
|
trade.getTradeAmount() != null &&
|
||||||
|
trade.getContract().getSellerPayoutAddressString().equals(addressEntry.getAddressString()))
|
||||||
|
balance = balance.add(trade.getTradeAmount());
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Coin getAvailableBalance(AddressEntry addressEntry, WalletService walletService,
|
||||||
|
OpenOfferManager openOfferManager, TradeManager tradeManager,
|
||||||
|
ClosedTradableManager closedTradableManager,
|
||||||
|
FailedTradesManager failedTradesManager) {
|
||||||
|
Coin balance;
|
||||||
|
Coin totalBalance = walletService.getBalanceForAddress(addressEntry.getAddress());
|
||||||
|
String id = addressEntry.getOfferId();
|
||||||
|
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(id);
|
||||||
|
Optional<Trade> tradeOptional = tradeManager.getTradeById(id);
|
||||||
|
Optional<Tradable> closedTradableOptional = closedTradableManager.getTradableById(id);
|
||||||
|
Optional<Trade> failedTradesOptional = failedTradesManager.getTradeById(id);
|
||||||
|
|
||||||
|
if (openOfferOptional.isPresent()) {
|
||||||
|
balance = totalBalance.subtract(TradableHelper.getBalanceInOpenOffer(openOfferOptional.get()));
|
||||||
|
} else if (tradeOptional.isPresent()) {
|
||||||
|
Trade trade = tradeOptional.get();
|
||||||
|
if (trade.getState().getPhase() != Trade.Phase.PAYOUT_PAID)
|
||||||
|
balance = totalBalance.subtract(TradableHelper.getBalanceInTrade(trade, walletService));
|
||||||
|
else
|
||||||
|
balance = totalBalance;
|
||||||
|
} else if (closedTradableOptional.isPresent()) {
|
||||||
|
Tradable tradable = closedTradableOptional.get();
|
||||||
|
Coin balanceInTrade = Coin.ZERO;
|
||||||
|
if (tradable instanceof OpenOffer)
|
||||||
|
balanceInTrade = TradableHelper.getBalanceInOpenOffer((OpenOffer) tradable);
|
||||||
|
else if (tradable instanceof Trade)
|
||||||
|
balanceInTrade = TradableHelper.getBalanceInTrade((Trade) tradable, walletService);
|
||||||
|
balance = totalBalance.subtract(balanceInTrade);
|
||||||
|
} else if (failedTradesOptional.isPresent()) {
|
||||||
|
balance = totalBalance.subtract(TradableHelper.getBalanceInTrade(failedTradesOptional.get(), walletService));
|
||||||
|
} else {
|
||||||
|
balance = totalBalance;
|
||||||
|
}
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Coin getReservedBalance(Tradable tradable, WalletService walletService) {
|
||||||
|
AddressEntry addressEntry = walletService.getTradeAddressEntry(tradable.getId());
|
||||||
|
Coin balance = walletService.getBalanceForAddress(addressEntry.getAddress());
|
||||||
|
if (tradable instanceof Trade) {
|
||||||
|
Trade trade = (Trade) tradable;
|
||||||
|
if (trade.getState().getPhase().ordinal() < Trade.Phase.PAYOUT_PAID.ordinal())
|
||||||
|
balance = TradableHelper.getBalanceInTrade(trade, walletService);
|
||||||
|
} else if (tradable instanceof OpenOffer) {
|
||||||
|
balance = TradableHelper.getBalanceInOpenOffer((OpenOffer) tradable);
|
||||||
|
}
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
}
|
|
@ -147,7 +147,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
public void closeAllOpenOffers(@Nullable Runnable completeHandler) {
|
public void closeAllOpenOffers(@Nullable Runnable completeHandler) {
|
||||||
openOffers.forEach(openOffer -> offerBookService.removeOfferAtShutDown(openOffer.getOffer()));
|
openOffers.forEach(openOffer -> offerBookService.removeOfferAtShutDown(openOffer.getOffer()));
|
||||||
if (completeHandler != null)
|
if (completeHandler != null)
|
||||||
UserThread.runAfter(completeHandler::run, openOffers.size() * 200 + 300, TimeUnit.MILLISECONDS);
|
UserThread.runAfter(completeHandler::run, openOffers.size() * 100 + 200, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -55,9 +55,11 @@ import io.bitsquare.p2p.network.Connection;
|
||||||
import io.bitsquare.p2p.network.ConnectionListener;
|
import io.bitsquare.p2p.network.ConnectionListener;
|
||||||
import io.bitsquare.p2p.peers.keepalive.messages.Ping;
|
import io.bitsquare.p2p.peers.keepalive.messages.Ping;
|
||||||
import io.bitsquare.payment.OKPayAccount;
|
import io.bitsquare.payment.OKPayAccount;
|
||||||
import io.bitsquare.trade.TradableCollections;
|
import io.bitsquare.trade.TradableHelper;
|
||||||
import io.bitsquare.trade.Trade;
|
import io.bitsquare.trade.Trade;
|
||||||
import io.bitsquare.trade.TradeManager;
|
import io.bitsquare.trade.TradeManager;
|
||||||
|
import io.bitsquare.trade.closed.ClosedTradableManager;
|
||||||
|
import io.bitsquare.trade.failed.FailedTradesManager;
|
||||||
import io.bitsquare.trade.offer.OpenOffer;
|
import io.bitsquare.trade.offer.OpenOffer;
|
||||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||||
import io.bitsquare.user.Preferences;
|
import io.bitsquare.user.Preferences;
|
||||||
|
@ -80,6 +82,7 @@ import java.util.Optional;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class MainViewModel implements ViewModel {
|
public class MainViewModel implements ViewModel {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MainViewModel.class);
|
private static final Logger log = LoggerFactory.getLogger(MainViewModel.class);
|
||||||
|
@ -137,6 +140,8 @@ public class MainViewModel implements ViewModel {
|
||||||
|
|
||||||
private MonadicBinding<Boolean> allServicesDone, tradesAndUIReady;
|
private MonadicBinding<Boolean> allServicesDone, tradesAndUIReady;
|
||||||
private final PriceFeed priceFeed;
|
private final PriceFeed priceFeed;
|
||||||
|
private final ClosedTradableManager closedTradableManager;
|
||||||
|
private final FailedTradesManager failedTradesManager;
|
||||||
private final User user;
|
private final User user;
|
||||||
private int numBtcPeers = 0;
|
private int numBtcPeers = 0;
|
||||||
private Timer checkNumberOfBtcPeersTimer;
|
private Timer checkNumberOfBtcPeersTimer;
|
||||||
|
@ -153,11 +158,14 @@ public class MainViewModel implements ViewModel {
|
||||||
public MainViewModel(WalletService walletService, TradeWalletService tradeWalletService,
|
public MainViewModel(WalletService walletService, TradeWalletService tradeWalletService,
|
||||||
PriceFeed priceFeed,
|
PriceFeed priceFeed,
|
||||||
ArbitratorManager arbitratorManager, P2PService p2PService, TradeManager tradeManager,
|
ArbitratorManager arbitratorManager, P2PService p2PService, TradeManager tradeManager,
|
||||||
OpenOfferManager openOfferManager, DisputeManager disputeManager, Preferences preferences,
|
OpenOfferManager openOfferManager, ClosedTradableManager closedTradableManager,
|
||||||
|
FailedTradesManager failedTradesManager, DisputeManager disputeManager, Preferences preferences,
|
||||||
User user, AlertManager alertManager, WalletPasswordWindow walletPasswordWindow,
|
User user, AlertManager alertManager, WalletPasswordWindow walletPasswordWindow,
|
||||||
NotificationCenter notificationCenter, TacWindow tacWindow, Clock clock,
|
NotificationCenter notificationCenter, TacWindow tacWindow, Clock clock,
|
||||||
KeyRing keyRing, Navigation navigation, BSFormatter formatter) {
|
KeyRing keyRing, Navigation navigation, BSFormatter formatter) {
|
||||||
this.priceFeed = priceFeed;
|
this.priceFeed = priceFeed;
|
||||||
|
this.closedTradableManager = closedTradableManager;
|
||||||
|
this.failedTradesManager = failedTradesManager;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.walletService = walletService;
|
this.walletService = walletService;
|
||||||
this.tradeWalletService = tradeWalletService;
|
this.tradeWalletService = tradeWalletService;
|
||||||
|
@ -670,7 +678,7 @@ public class MainViewModel implements ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void swapPendingTradeAddressEntriesToSavingsWallet() {
|
private void swapPendingTradeAddressEntriesToSavingsWallet() {
|
||||||
TradableCollections.getAddressEntriesForAvailableBalance(openOfferManager, tradeManager, walletService).stream()
|
TradableHelper.getAddressEntriesForAvailableBalance(openOfferManager, tradeManager, walletService).stream()
|
||||||
.filter(addressEntry -> addressEntry.getOfferId() != null)
|
.filter(addressEntry -> addressEntry.getOfferId() != null)
|
||||||
.forEach(addressEntry -> walletService.swapTradeToSavings(addressEntry.getOfferId()));
|
.forEach(addressEntry -> walletService.swapTradeToSavings(addressEntry.getOfferId()));
|
||||||
}
|
}
|
||||||
|
@ -691,12 +699,36 @@ public class MainViewModel implements ViewModel {
|
||||||
updateLockedBalance();
|
updateLockedBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateAvailableBalance() {
|
||||||
|
Optional<Coin> totalAvailableOptional = Stream.concat(walletService.getSavingsAddressEntryList().stream(), walletService.getTradeAddressEntryList().stream())
|
||||||
|
.filter(addressEntry -> walletService.getBalanceForAddress(addressEntry.getAddress()).isPositive())
|
||||||
|
.map(addressEntry -> TradableHelper.getAvailableBalance(addressEntry,
|
||||||
|
walletService,
|
||||||
|
openOfferManager,
|
||||||
|
tradeManager,
|
||||||
|
closedTradableManager,
|
||||||
|
failedTradesManager))
|
||||||
|
.filter(balance -> balance.isPositive())
|
||||||
|
.reduce(Coin::add);
|
||||||
|
|
||||||
|
|
||||||
|
/*Optional<Coin> totalAvailableOptional = TradableHelper.getAddressEntriesForAvailableBalance(openOfferManager, tradeManager, walletService)
|
||||||
|
.stream()
|
||||||
|
.map(e -> walletService.getBalanceForAddress(e.getAddress()))
|
||||||
|
.reduce(Coin::add);*/
|
||||||
|
if (totalAvailableOptional.isPresent())
|
||||||
|
availableBalance.set(formatter.formatCoinWithCode(totalAvailableOptional.get()));
|
||||||
|
else
|
||||||
|
availableBalance.set(formatter.formatCoinWithCode(Coin.ZERO));
|
||||||
|
}
|
||||||
|
|
||||||
private void updateReservedBalance() {
|
private void updateReservedBalance() {
|
||||||
Coin sum = Coin.valueOf(TradableCollections.getNotCompletedTradableItems(openOfferManager, tradeManager).stream()
|
Coin sum = Coin.valueOf(TradableHelper.getNotCompletedTradableItems(openOfferManager, tradeManager).stream()
|
||||||
.map(tradable -> walletService.getTradeAddressEntry(tradable.getId()))
|
.filter(tradable -> tradable instanceof OpenOffer)
|
||||||
.map(addressEntry -> walletService.getBalanceForAddress(addressEntry.getAddress()))
|
.map(tradable -> TradableHelper.getReservedBalance(tradable, walletService))
|
||||||
.mapToLong(Coin::getValue)
|
.mapToLong(Coin::getValue)
|
||||||
.sum());
|
.sum());
|
||||||
|
|
||||||
reservedBalance.set(formatter.formatCoinWithCode(sum));
|
reservedBalance.set(formatter.formatCoinWithCode(sum));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -725,17 +757,6 @@ public class MainViewModel implements ViewModel {
|
||||||
lockedBalance.set(formatter.formatCoinWithCode(sum));
|
lockedBalance.set(formatter.formatCoinWithCode(sum));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateAvailableBalance() {
|
|
||||||
Optional<Coin> totalAvailableOptional = TradableCollections.getAddressEntriesForAvailableBalance(openOfferManager, tradeManager, walletService)
|
|
||||||
.stream()
|
|
||||||
.map(e -> walletService.getBalanceForAddress(e.getAddress())).reduce(Coin::add);
|
|
||||||
if (totalAvailableOptional.isPresent())
|
|
||||||
availableBalance.set(formatter.formatCoinWithCode(totalAvailableOptional.get()));
|
|
||||||
else
|
|
||||||
availableBalance.set(formatter.formatCoinWithCode(Coin.ZERO));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void onDisputesChangeListener(List<? extends Dispute> addedList, @Nullable List<? extends Dispute> removedList) {
|
private void onDisputesChangeListener(List<? extends Dispute> addedList, @Nullable List<? extends Dispute> removedList) {
|
||||||
if (removedList != null) {
|
if (removedList != null) {
|
||||||
removedList.stream().forEach(dispute -> {
|
removedList.stream().forEach(dispute -> {
|
||||||
|
|
|
@ -83,19 +83,6 @@ public class FundsView extends ActivatableViewAndModel<TabPane, Activatable> {
|
||||||
navigation.navigateTo(MainView.class, FundsView.class, ReservedView.class);
|
navigation.navigateTo(MainView.class, FundsView.class, ReservedView.class);
|
||||||
else if (root.getSelectionModel().getSelectedItem() == transactionsTab)
|
else if (root.getSelectionModel().getSelectedItem() == transactionsTab)
|
||||||
navigation.navigateTo(MainView.class, FundsView.class, TransactionsView.class);
|
navigation.navigateTo(MainView.class, FundsView.class, TransactionsView.class);
|
||||||
|
|
||||||
String key = "tradeWalletInfoAtFunds";
|
|
||||||
/* if (!BitsquareApp.DEV_MODE)
|
|
||||||
new Popup().backgroundInfo("Bitsquare does not use a single application wallet, but dedicated wallets for every trade.\n\n" +
|
|
||||||
"Funding of the wallet will be done when needed, for instance when you create or take an offer.\n" +
|
|
||||||
"Withdrawing funds can be done after a trade is completed.\n\n" +
|
|
||||||
"Dedicated wallets help protect user privacy and prevent leaking information of previous trades to other " +
|
|
||||||
"traders.")
|
|
||||||
.actionButtonText("Visit FAQ web page")
|
|
||||||
.onAction(() -> Utilities.openWebPage("https://bitsquare.io/faq"))
|
|
||||||
.closeButtonText("I understand")
|
|
||||||
.dontShowAgainId(key, preferences)
|
|
||||||
.show();*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -132,7 +132,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
.compareTo(o2.getProgressIndicator().getProgress()));
|
.compareTo(o2.getProgressIndicator().getProgress()));
|
||||||
usageColumn.setComparator((a, b) -> (a.getNumTxOutputs() < b.getNumTxOutputs()) ? -1 : ((a.getNumTxOutputs() == b.getNumTxOutputs()) ? 0 : 1));
|
usageColumn.setComparator((a, b) -> (a.getNumTxOutputs() < b.getNumTxOutputs()) ? -1 : ((a.getNumTxOutputs() == b.getNumTxOutputs()) ? 0 : 1));
|
||||||
tableView.getSortOrder().add(usageColumn);
|
tableView.getSortOrder().add(usageColumn);
|
||||||
|
tableView.setItems(sortedList);
|
||||||
|
|
||||||
titledGroupBg = addTitledGroupBg(gridPane, gridRow, 3, "Fund your wallet");
|
titledGroupBg = addTitledGroupBg(gridPane, gridRow, 3, "Fund your wallet");
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
protected void activate() {
|
protected void activate() {
|
||||||
tableView.getSelectionModel().selectedItemProperty().addListener(tableViewSelectionListener);
|
tableView.getSelectionModel().selectedItemProperty().addListener(tableViewSelectionListener);
|
||||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||||
tableView.setItems(sortedList);
|
|
||||||
updateList();
|
updateList();
|
||||||
|
|
||||||
walletService.addBalanceListener(balanceListener);
|
walletService.addBalanceListener(balanceListener);
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
package io.bitsquare.gui.main.funds.reserved;
|
package io.bitsquare.gui.main.funds.reserved;
|
||||||
|
|
||||||
import io.bitsquare.btc.AddressEntry;
|
import io.bitsquare.btc.AddressEntry;
|
||||||
import io.bitsquare.btc.FeePolicy;
|
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.btc.listeners.BalanceListener;
|
import io.bitsquare.btc.listeners.BalanceListener;
|
||||||
import io.bitsquare.gui.util.BSFormatter;
|
import io.bitsquare.gui.util.BSFormatter;
|
||||||
import io.bitsquare.trade.Tradable;
|
import io.bitsquare.trade.Tradable;
|
||||||
|
import io.bitsquare.trade.TradableHelper;
|
||||||
import io.bitsquare.trade.Trade;
|
import io.bitsquare.trade.Trade;
|
||||||
import io.bitsquare.trade.offer.OpenOffer;
|
import io.bitsquare.trade.offer.OpenOffer;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
@ -62,60 +62,50 @@ public class ReservedListItem {
|
||||||
balanceListener = new BalanceListener(getAddress()) {
|
balanceListener = new BalanceListener(getAddress()) {
|
||||||
@Override
|
@Override
|
||||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||||
updateBalance(balance);
|
updateBalance();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
walletService.addBalanceListener(balanceListener);
|
walletService.addBalanceListener(balanceListener);
|
||||||
updateBalance(walletService.getBalanceForAddress(getAddress()));
|
updateBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
walletService.removeBalanceListener(balanceListener);
|
walletService.removeBalanceListener(balanceListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateBalance(Coin balance) {
|
private void updateBalance() {
|
||||||
this.balance = balance;
|
balance = TradableHelper.getReservedBalance(tradable, walletService);
|
||||||
if (balance != null) {
|
if (balance != null)
|
||||||
balanceLabel.setText(formatter.formatCoin(balance));
|
balanceLabel.setText(formatter.formatCoin(this.balance));
|
||||||
|
|
||||||
if (tradable instanceof Trade) {
|
if (tradable instanceof Trade) {
|
||||||
Trade trade = (Trade) tradable;
|
Trade trade = (Trade) tradable;
|
||||||
Trade.Phase phase = trade.getState().getPhase();
|
Trade.Phase phase = trade.getState().getPhase();
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
case PREPARATION:
|
case PREPARATION:
|
||||||
case TAKER_FEE_PAID:
|
case TAKER_FEE_PAID:
|
||||||
fundsInfo = "Reserved in local wallet";
|
fundsInfo = "Reserved in local wallet";
|
||||||
break;
|
break;
|
||||||
case DEPOSIT_REQUESTED:
|
case DEPOSIT_REQUESTED:
|
||||||
case DEPOSIT_PAID:
|
case DEPOSIT_PAID:
|
||||||
case FIAT_SENT:
|
case FIAT_SENT:
|
||||||
case FIAT_RECEIVED:
|
case FIAT_RECEIVED:
|
||||||
fundsInfo = "Locked in MultiSig";
|
fundsInfo = "Locked in MultiSig";
|
||||||
// We ignore the tx fee as it will be paid by both (once deposit, once payout)
|
break;
|
||||||
Coin balanceInDeposit = FeePolicy.getSecurityDeposit().add(FeePolicy.getFeePerKb());
|
case PAYOUT_PAID:
|
||||||
// For the seller we add the trade amount
|
fundsInfo = "Received in local wallet";
|
||||||
if (trade.getContract() != null &&
|
break;
|
||||||
trade.getTradeAmount() != null &&
|
case WITHDRAWN:
|
||||||
trade.getContract().getSellerPayoutAddressString().equals(addressString))
|
log.error("Invalid state at updateBalance (WITHDRAWN)");
|
||||||
balanceInDeposit = balanceInDeposit.add(trade.getTradeAmount());
|
break;
|
||||||
|
case DISPUTE:
|
||||||
balanceLabel.setText(formatter.formatCoin(balanceInDeposit));
|
log.error("Invalid state at updateBalance (DISPUTE)");
|
||||||
break;
|
break;
|
||||||
case PAYOUT_PAID:
|
default:
|
||||||
fundsInfo = "Received in local wallet";
|
log.warn("Not supported tradePhase: " + phase);
|
||||||
break;
|
|
||||||
case WITHDRAWN:
|
|
||||||
log.error("Invalid state at updateBalance (WITHDRAWN)");
|
|
||||||
break;
|
|
||||||
case DISPUTE:
|
|
||||||
log.error("Invalid state at updateBalance (DISPUTE)");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
log.warn("Not supported tradePhase: " + phase);
|
|
||||||
}
|
|
||||||
} else if (tradable instanceof OpenOffer) {
|
|
||||||
fundsInfo = "Reserved in local wallet";
|
|
||||||
}
|
}
|
||||||
|
} else if (tradable instanceof OpenOffer) {
|
||||||
|
fundsInfo = "Reserved in local wallet";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ import io.bitsquare.gui.main.overlays.windows.OfferDetailsWindow;
|
||||||
import io.bitsquare.gui.main.overlays.windows.TradeDetailsWindow;
|
import io.bitsquare.gui.main.overlays.windows.TradeDetailsWindow;
|
||||||
import io.bitsquare.gui.util.BSFormatter;
|
import io.bitsquare.gui.util.BSFormatter;
|
||||||
import io.bitsquare.trade.Tradable;
|
import io.bitsquare.trade.Tradable;
|
||||||
import io.bitsquare.trade.TradableCollections;
|
import io.bitsquare.trade.TradableHelper;
|
||||||
import io.bitsquare.trade.Trade;
|
import io.bitsquare.trade.Trade;
|
||||||
import io.bitsquare.trade.TradeManager;
|
import io.bitsquare.trade.TradeManager;
|
||||||
import io.bitsquare.trade.offer.OpenOffer;
|
import io.bitsquare.trade.offer.OpenOffer;
|
||||||
|
@ -38,6 +38,7 @@ import io.bitsquare.trade.offer.OpenOfferManager;
|
||||||
import io.bitsquare.user.Preferences;
|
import io.bitsquare.user.Preferences;
|
||||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.collections.transformation.SortedList;
|
import javafx.collections.transformation.SortedList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
@ -68,6 +69,8 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||||
private final ObservableList<ReservedListItem> observableList = FXCollections.observableArrayList();
|
private final ObservableList<ReservedListItem> observableList = FXCollections.observableArrayList();
|
||||||
private final SortedList<ReservedListItem> sortedList = new SortedList<>(observableList);
|
private final SortedList<ReservedListItem> sortedList = new SortedList<>(observableList);
|
||||||
private BalanceListener balanceListener;
|
private BalanceListener balanceListener;
|
||||||
|
private ListChangeListener<OpenOffer> openOfferListChangeListener;
|
||||||
|
private ListChangeListener<Trade> tradeListChangeListener;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -86,7 +89,6 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||||
this.tradeDetailsWindow = tradeDetailsWindow;
|
this.tradeDetailsWindow = tradeDetailsWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
|
@ -100,7 +102,12 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||||
addressColumn.setComparator((o1, o2) -> o1.getAddressString().compareTo(o2.getAddressString()));
|
addressColumn.setComparator((o1, o2) -> o1.getAddressString().compareTo(o2.getAddressString()));
|
||||||
detailsColumn.setComparator((o1, o2) -> o1.getTradable().getId().compareTo(o2.getTradable().getId()));
|
detailsColumn.setComparator((o1, o2) -> o1.getTradable().getId().compareTo(o2.getTradable().getId()));
|
||||||
balanceColumn.setComparator((o1, o2) -> o1.getBalance().compareTo(o2.getBalance()));
|
balanceColumn.setComparator((o1, o2) -> o1.getBalance().compareTo(o2.getBalance()));
|
||||||
dateColumn.setComparator((o1, o2) -> getTradable(o2).get().getDate().compareTo(getTradable(o1).get().getDate()));
|
dateColumn.setComparator((o1, o2) -> {
|
||||||
|
if (getTradable(o1).isPresent() && getTradable(o2).isPresent())
|
||||||
|
return getTradable(o2).get().getDate().compareTo(getTradable(o1).get().getDate());
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
tableView.getSortOrder().add(dateColumn);
|
tableView.getSortOrder().add(dateColumn);
|
||||||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||||
|
|
||||||
|
@ -110,10 +117,14 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||||
updateList();
|
updateList();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
openOfferListChangeListener = c -> updateList();
|
||||||
|
tradeListChangeListener = c -> updateList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void activate() {
|
protected void activate() {
|
||||||
|
openOfferManager.getOpenOffers().addListener(openOfferListChangeListener);
|
||||||
|
tradeManager.getTrades().addListener(tradeListChangeListener);
|
||||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||||
tableView.setItems(sortedList);
|
tableView.setItems(sortedList);
|
||||||
updateList();
|
updateList();
|
||||||
|
@ -123,6 +134,8 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void deactivate() {
|
protected void deactivate() {
|
||||||
|
openOfferManager.getOpenOffers().removeListener(openOfferListChangeListener);
|
||||||
|
tradeManager.getTrades().removeListener(tradeListChangeListener);
|
||||||
sortedList.comparatorProperty().unbind();
|
sortedList.comparatorProperty().unbind();
|
||||||
observableList.forEach(ReservedListItem::cleanup);
|
observableList.forEach(ReservedListItem::cleanup);
|
||||||
walletService.removeBalanceListener(balanceListener);
|
walletService.removeBalanceListener(balanceListener);
|
||||||
|
@ -135,9 +148,7 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||||
|
|
||||||
private void updateList() {
|
private void updateList() {
|
||||||
observableList.forEach(ReservedListItem::cleanup);
|
observableList.forEach(ReservedListItem::cleanup);
|
||||||
|
observableList.setAll(TradableHelper.getNotCompletedTradableItems(openOfferManager, tradeManager).stream()
|
||||||
|
|
||||||
observableList.setAll(TradableCollections.getNotCompletedTradableItems(openOfferManager, tradeManager).stream()
|
|
||||||
.map(tradable -> new ReservedListItem(tradable, walletService.getTradeAddressEntry(tradable.getOffer().getId()), walletService, formatter))
|
.map(tradable -> new ReservedListItem(tradable, walletService.getTradeAddressEntry(tradable.getOffer().getId()), walletService, formatter))
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,11 @@ import io.bitsquare.btc.AddressEntry;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.btc.listeners.BalanceListener;
|
import io.bitsquare.btc.listeners.BalanceListener;
|
||||||
import io.bitsquare.gui.util.BSFormatter;
|
import io.bitsquare.gui.util.BSFormatter;
|
||||||
|
import io.bitsquare.trade.TradableHelper;
|
||||||
|
import io.bitsquare.trade.TradeManager;
|
||||||
|
import io.bitsquare.trade.closed.ClosedTradableManager;
|
||||||
|
import io.bitsquare.trade.failed.FailedTradesManager;
|
||||||
|
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import org.bitcoinj.core.Address;
|
import org.bitcoinj.core.Address;
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
@ -31,13 +36,25 @@ public class WithdrawalListItem {
|
||||||
private final Label balanceLabel;
|
private final Label balanceLabel;
|
||||||
private final AddressEntry addressEntry;
|
private final AddressEntry addressEntry;
|
||||||
private final WalletService walletService;
|
private final WalletService walletService;
|
||||||
|
private final OpenOfferManager openOfferManager;
|
||||||
|
private final TradeManager tradeManager;
|
||||||
|
private final ClosedTradableManager closedTradableManager;
|
||||||
|
private final FailedTradesManager failedTradesManager;
|
||||||
private final BSFormatter formatter;
|
private final BSFormatter formatter;
|
||||||
private Coin balance;
|
private Coin balance;
|
||||||
private final String addressString;
|
private final String addressString;
|
||||||
|
|
||||||
public WithdrawalListItem(AddressEntry addressEntry, WalletService walletService, BSFormatter formatter) {
|
public WithdrawalListItem(AddressEntry addressEntry, WalletService walletService,
|
||||||
|
OpenOfferManager openOfferManager, TradeManager tradeManager,
|
||||||
|
ClosedTradableManager closedTradableManager,
|
||||||
|
FailedTradesManager failedTradesManager,
|
||||||
|
BSFormatter formatter) {
|
||||||
this.addressEntry = addressEntry;
|
this.addressEntry = addressEntry;
|
||||||
this.walletService = walletService;
|
this.walletService = walletService;
|
||||||
|
this.openOfferManager = openOfferManager;
|
||||||
|
this.tradeManager = tradeManager;
|
||||||
|
this.closedTradableManager = closedTradableManager;
|
||||||
|
this.failedTradesManager = failedTradesManager;
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
addressString = addressEntry.getAddressString();
|
addressString = addressEntry.getAddressString();
|
||||||
|
|
||||||
|
@ -46,23 +63,28 @@ public class WithdrawalListItem {
|
||||||
balanceListener = new BalanceListener(getAddress()) {
|
balanceListener = new BalanceListener(getAddress()) {
|
||||||
@Override
|
@Override
|
||||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||||
updateBalance(balance);
|
updateBalance();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
walletService.addBalanceListener(balanceListener);
|
walletService.addBalanceListener(balanceListener);
|
||||||
|
|
||||||
updateBalance(walletService.getBalanceForAddress(getAddress()));
|
updateBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
walletService.removeBalanceListener(balanceListener);
|
walletService.removeBalanceListener(balanceListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateBalance(Coin balance) {
|
private void updateBalance() {
|
||||||
this.balance = balance;
|
balance = TradableHelper.getAvailableBalance(addressEntry,
|
||||||
if (balance != null) {
|
walletService,
|
||||||
balanceLabel.setText(formatter.formatCoin(balance));
|
openOfferManager,
|
||||||
}
|
tradeManager,
|
||||||
|
closedTradableManager,
|
||||||
|
failedTradesManager);
|
||||||
|
|
||||||
|
if (balance != null)
|
||||||
|
balanceLabel.setText(formatter.formatCoin(this.balance));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final String getLabel() {
|
public final String getLabel() {
|
||||||
|
|
|
@ -34,7 +34,6 @@ import io.bitsquare.gui.main.overlays.windows.WalletPasswordWindow;
|
||||||
import io.bitsquare.gui.util.BSFormatter;
|
import io.bitsquare.gui.util.BSFormatter;
|
||||||
import io.bitsquare.gui.util.validation.BtcAddressValidator;
|
import io.bitsquare.gui.util.validation.BtcAddressValidator;
|
||||||
import io.bitsquare.trade.Tradable;
|
import io.bitsquare.trade.Tradable;
|
||||||
import io.bitsquare.trade.TradableCollections;
|
|
||||||
import io.bitsquare.trade.Trade;
|
import io.bitsquare.trade.Trade;
|
||||||
import io.bitsquare.trade.TradeManager;
|
import io.bitsquare.trade.TradeManager;
|
||||||
import io.bitsquare.trade.closed.ClosedTradableManager;
|
import io.bitsquare.trade.closed.ClosedTradableManager;
|
||||||
|
@ -61,6 +60,7 @@ import org.spongycastle.crypto.params.KeyParameter;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@FxmlView
|
@FxmlView
|
||||||
public class WithdrawalView extends ActivatableView<VBox, Void> {
|
public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||||
|
@ -197,7 +197,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||||
withdrawToTextField.getText(), senderAmount, null);
|
withdrawToTextField.getText(), senderAmount, null);
|
||||||
Coin receiverAmount = senderAmount.subtract(requiredFee);
|
Coin receiverAmount = senderAmount.subtract(requiredFee);
|
||||||
if (BitsquareApp.DEV_MODE) {
|
if (BitsquareApp.DEV_MODE) {
|
||||||
doWithdraw(receiverAmount, callback);
|
doWithdraw(senderAmount, callback);
|
||||||
} else {
|
} else {
|
||||||
new Popup().headLine("Confirm withdrawal request")
|
new Popup().headLine("Confirm withdrawal request")
|
||||||
.confirmation("Sending: " + formatter.formatCoinWithCode(senderAmount) + "\n" +
|
.confirmation("Sending: " + formatter.formatCoinWithCode(senderAmount) + "\n" +
|
||||||
|
@ -283,8 +283,11 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||||
|
|
||||||
private void updateList() {
|
private void updateList() {
|
||||||
observableList.forEach(WithdrawalListItem::cleanup);
|
observableList.forEach(WithdrawalListItem::cleanup);
|
||||||
observableList.setAll(TradableCollections.getAddressEntriesForAvailableBalance(openOfferManager, tradeManager, walletService).stream()
|
observableList.setAll(Stream.concat(walletService.getSavingsAddressEntryList().stream(), walletService.getTradeAddressEntryList().stream())
|
||||||
.map(addressEntry -> new WithdrawalListItem(addressEntry, walletService, formatter))
|
.filter(addressEntry -> walletService.getBalanceForAddress(addressEntry.getAddress()).isPositive())
|
||||||
|
.map(addressEntry -> new WithdrawalListItem(addressEntry, walletService, openOfferManager, tradeManager,
|
||||||
|
closedTradableManager, failedTradesManager, formatter))
|
||||||
|
.filter(item -> item.getBalance().isPositive())
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -318,9 +318,11 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void useSavingsWalletForFunding() {
|
void fundFromSavingsWallet() {
|
||||||
useSavingsWallet = true;
|
this.useSavingsWallet = true;
|
||||||
updateBalance();
|
updateBalance();
|
||||||
|
if (!isWalletFunded.get())
|
||||||
|
this.useSavingsWallet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ package io.bitsquare.gui.main.offer.createoffer;
|
||||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||||
import io.bitsquare.app.BitsquareApp;
|
import io.bitsquare.app.BitsquareApp;
|
||||||
import io.bitsquare.btc.FeePolicy;
|
|
||||||
import io.bitsquare.common.UserThread;
|
import io.bitsquare.common.UserThread;
|
||||||
import io.bitsquare.common.util.Tuple2;
|
import io.bitsquare.common.util.Tuple2;
|
||||||
import io.bitsquare.common.util.Tuple3;
|
import io.bitsquare.common.util.Tuple3;
|
||||||
|
@ -296,13 +295,11 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
model.totalToPay.get() + " to your local Bitsquare trading wallet.\n" +
|
model.totalToPay.get() + " to your local Bitsquare trading wallet.\n" +
|
||||||
"The amount is the sum of " + tradeAmountText + "the security deposit, the trading fee and " +
|
"The amount is the sum of " + tradeAmountText + "the security deposit, the trading fee and " +
|
||||||
"the bitcoin mining fee.\n\n" +
|
"the bitcoin mining fee.\n\n" +
|
||||||
"Please send from your external Bitcoin wallet the exact amount to the address: " +
|
"You can choose between 2 options:\n" +
|
||||||
|
"Either you transfer from your Bitsquare wallet the funds or if you prefer better privacy by " +
|
||||||
|
"separating all trade transactions you can send from your external Bitcoin wallet the exact amount to the address: " +
|
||||||
model.getAddressAsString() + "\n" +
|
model.getAddressAsString() + "\n" +
|
||||||
"(you can copy the address in the screen below after closing that popup)\n\n" +
|
"(you can copy the address in the screen below after closing that popup)\n\n" +
|
||||||
"Make sure you use a sufficiently high mining fee of at least " +
|
|
||||||
model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) +
|
|
||||||
" to avoid problems that your transaction does not get confirmed in the blockchain.\n" +
|
|
||||||
"Transactions with a lower fee will not be accepted.\n\n" +
|
|
||||||
"You can see the status of your incoming payment and all the details in the screen below.")
|
"You can see the status of your incoming payment and all the details in the screen below.")
|
||||||
.dontShowAgainId(key, preferences)
|
.dontShowAgainId(key, preferences)
|
||||||
.show();
|
.show();
|
||||||
|
@ -804,7 +801,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
fundFromSavingsWalletButton = new Button("Transfer funds from Bitsquare wallet");
|
fundFromSavingsWalletButton = new Button("Transfer funds from Bitsquare wallet");
|
||||||
fundFromSavingsWalletButton.setDefaultButton(true);
|
fundFromSavingsWalletButton.setDefaultButton(true);
|
||||||
fundFromSavingsWalletButton.setDefaultButton(false);
|
fundFromSavingsWalletButton.setDefaultButton(false);
|
||||||
fundFromSavingsWalletButton.setOnAction(e -> model.useSavingsWalletForFunding());
|
fundFromSavingsWalletButton.setOnAction(e -> model.fundFromSavingsWallet());
|
||||||
Label label = new Label("OR");
|
Label label = new Label("OR");
|
||||||
label.setPadding(new Insets(5, 0, 0, 0));
|
label.setPadding(new Insets(5, 0, 0, 0));
|
||||||
fundFromExternalWalletButton = new Button("Pay in funds from external wallet");
|
fundFromExternalWalletButton = new Button("Pay in funds from external wallet");
|
||||||
|
|
|
@ -375,8 +375,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||||
updateSpinnerInfo();
|
updateSpinnerInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean useSavingsWalletForFunding() {
|
boolean fundFromSavingsWallet() {
|
||||||
dataModel.useSavingsWalletForFunding();
|
dataModel.fundFromSavingsWallet();
|
||||||
if (dataModel.isWalletFunded.get()) {
|
if (dataModel.isWalletFunded.get()) {
|
||||||
updateButtonDisableState();
|
updateButtonDisableState();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -252,9 +252,11 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
||||||
this.paymentAccount = paymentAccount;
|
this.paymentAccount = paymentAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void useSavingsWalletForFunding() {
|
void fundFromSavingsWallet() {
|
||||||
useSavingsWallet = true;
|
useSavingsWallet = true;
|
||||||
updateBalance();
|
updateBalance();
|
||||||
|
if (!isWalletFunded.get())
|
||||||
|
this.useSavingsWallet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ package io.bitsquare.gui.main.offer.takeoffer;
|
||||||
import de.jensd.fx.fontawesome.AwesomeDude;
|
import de.jensd.fx.fontawesome.AwesomeDude;
|
||||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||||
import io.bitsquare.app.BitsquareApp;
|
import io.bitsquare.app.BitsquareApp;
|
||||||
import io.bitsquare.btc.FeePolicy;
|
|
||||||
import io.bitsquare.common.UserThread;
|
import io.bitsquare.common.UserThread;
|
||||||
import io.bitsquare.common.util.Tuple2;
|
import io.bitsquare.common.util.Tuple2;
|
||||||
import io.bitsquare.common.util.Tuple3;
|
import io.bitsquare.common.util.Tuple3;
|
||||||
|
@ -309,12 +308,11 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
model.totalToPay.get() + " to your local Bitsquare trading wallet.\n" +
|
model.totalToPay.get() + " to your local Bitsquare trading wallet.\n" +
|
||||||
"The amount is the sum of " + tradeAmountText + "the security deposit, the trading fee and " +
|
"The amount is the sum of " + tradeAmountText + "the security deposit, the trading fee and " +
|
||||||
"the bitcoin mining fee.\n\n" +
|
"the bitcoin mining fee.\n\n" +
|
||||||
"Please send from your external Bitcoin wallet the exact amount to the address: " +
|
"You can choose between 2 options:\n" +
|
||||||
model.dataModel.getAddressEntry().getAddressString() + "\n(you can copy the address in the screen below after closing that popup)\n\n" +
|
"Either you transfer from your Bitsquare wallet the funds or if you prefer better privacy by " +
|
||||||
"Make sure you use a sufficiently high mining fee of at least " +
|
"separating all trade transactions you can send from your external Bitcoin wallet the exact amount to the address: " +
|
||||||
model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) +
|
model.dataModel.getAddressEntry().getAddressString() + "\n" +
|
||||||
" to avoid problems that your transaction does not get confirmed in the blockchain.\n" +
|
"(you can copy the address in the screen below after closing that popup)\n\n" +
|
||||||
"Transactions with a lower fee will not be accepted.\n\n" +
|
|
||||||
"You can see the status of your incoming payment and all the details in the screen below.")
|
"You can see the status of your incoming payment and all the details in the screen below.")
|
||||||
.dontShowAgainId(key, preferences)
|
.dontShowAgainId(key, preferences)
|
||||||
.show();
|
.show();
|
||||||
|
@ -726,7 +724,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
fundFromSavingsWalletButton = new Button("Transfer funds from Bitsquare wallet");
|
fundFromSavingsWalletButton = new Button("Transfer funds from Bitsquare wallet");
|
||||||
fundFromSavingsWalletButton.setDefaultButton(true);
|
fundFromSavingsWalletButton.setDefaultButton(true);
|
||||||
fundFromSavingsWalletButton.setDefaultButton(false);
|
fundFromSavingsWalletButton.setDefaultButton(false);
|
||||||
fundFromSavingsWalletButton.setOnAction(e -> model.useSavingsWalletForFunding());
|
fundFromSavingsWalletButton.setOnAction(e -> model.fundFromSavingsWallet());
|
||||||
Label label = new Label("OR");
|
Label label = new Label("OR");
|
||||||
label.setPadding(new Insets(5, 0, 0, 0));
|
label.setPadding(new Insets(5, 0, 0, 0));
|
||||||
fundFromExternalWalletButton = new Button("Pay in funds from external wallet");
|
fundFromExternalWalletButton = new Button("Pay in funds from external wallet");
|
||||||
|
|
|
@ -207,8 +207,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||||
updateSpinnerInfo();
|
updateSpinnerInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean useSavingsWalletForFunding() {
|
boolean fundFromSavingsWallet() {
|
||||||
dataModel.useSavingsWalletForFunding();
|
dataModel.fundFromSavingsWallet();
|
||||||
if (dataModel.isWalletFunded.get()) {
|
if (dataModel.isWalletFunded.get()) {
|
||||||
updateButtonDisableState();
|
updateButtonDisableState();
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Add table
Reference in a new issue