mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-23 23:06:39 +01:00
Merge remote-tracking branch 'origin/master' into add-xmr-tx-key-service-2
This commit is contained in:
commit
c6f1e5274f
114 changed files with 2819 additions and 448 deletions
29
build.gradle
29
build.gradle
|
@ -57,7 +57,7 @@ configure(subprojects) {
|
||||||
junitVersion = '4.12'
|
junitVersion = '4.12'
|
||||||
jupiterVersion = '5.3.2'
|
jupiterVersion = '5.3.2'
|
||||||
kotlinVersion = '1.3.41'
|
kotlinVersion = '1.3.41'
|
||||||
knowmXchangeVersion = '4.3.3'
|
knowmXchangeVersion = '4.4.2'
|
||||||
langVersion = '3.8'
|
langVersion = '3.8'
|
||||||
logbackVersion = '1.1.11'
|
logbackVersion = '1.1.11'
|
||||||
loggingVersion = '1.2'
|
loggingVersion = '1.2'
|
||||||
|
@ -378,7 +378,7 @@ configure(project(':desktop')) {
|
||||||
apply plugin: 'witness'
|
apply plugin: 'witness'
|
||||||
apply from: '../gradle/witness/gradle-witness.gradle'
|
apply from: '../gradle/witness/gradle-witness.gradle'
|
||||||
|
|
||||||
version = '1.3.6-SNAPSHOT'
|
version = '1.3.7'
|
||||||
|
|
||||||
mainClassName = 'bisq.desktop.app.BisqAppMain'
|
mainClassName = 'bisq.desktop.app.BisqAppMain'
|
||||||
|
|
||||||
|
@ -458,20 +458,43 @@ configure(project(':pricenode')) {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(":core")
|
compile project(":core")
|
||||||
|
|
||||||
|
compileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||||
|
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||||
|
|
||||||
implementation "com.google.code.gson:gson:$gsonVersion"
|
implementation "com.google.code.gson:gson:$gsonVersion"
|
||||||
implementation "commons-codec:commons-codec:$codecVersion"
|
implementation "commons-codec:commons-codec:$codecVersion"
|
||||||
implementation "org.apache.httpcomponents:httpcore:$httpcoreVersion"
|
implementation "org.apache.httpcomponents:httpcore:$httpcoreVersion"
|
||||||
implementation("org.apache.httpcomponents:httpclient:$httpclientVersion") {
|
implementation("org.apache.httpcomponents:httpclient:$httpclientVersion") {
|
||||||
exclude(module: 'commons-codec')
|
exclude(module: 'commons-codec')
|
||||||
}
|
}
|
||||||
compile("org.knowm.xchange:xchange-bitcoinaverage:$knowmXchangeVersion")
|
compile("org.knowm.xchange:xchange-bitbay:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-btcmarkets:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-binance:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-bitfinex:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-bitflyer:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-bitstamp:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-cexio:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-coinmate:$knowmXchangeVersion")
|
||||||
compile("org.knowm.xchange:xchange-coinmarketcap:$knowmXchangeVersion")
|
compile("org.knowm.xchange:xchange-coinmarketcap:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-coinone:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-exmo:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-hitbtc:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-huobi:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-independentreserve:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-kraken:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-luno:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-mercadobitcoin:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-paribu:$knowmXchangeVersion")
|
||||||
compile("org.knowm.xchange:xchange-poloniex:$knowmXchangeVersion")
|
compile("org.knowm.xchange:xchange-poloniex:$knowmXchangeVersion")
|
||||||
|
compile("org.knowm.xchange:xchange-quoine:$knowmXchangeVersion")
|
||||||
compile("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
|
compile("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
|
||||||
compile("org.springframework.boot:spring-boot-starter-actuator")
|
compile("org.springframework.boot:spring-boot-starter-actuator")
|
||||||
testCompile "org.junit.jupiter:junit-jupiter-api:$jupiterVersion"
|
testCompile "org.junit.jupiter:junit-jupiter-api:$jupiterVersion"
|
||||||
testCompile "org.junit.jupiter:junit-jupiter-params:$jupiterVersion"
|
testCompile "org.junit.jupiter:junit-jupiter-params:$jupiterVersion"
|
||||||
testRuntime("org.junit.jupiter:junit-jupiter-engine:$jupiterVersion")
|
testRuntime("org.junit.jupiter:junit-jupiter-engine:$jupiterVersion")
|
||||||
|
testCompileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||||
|
testAnnotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|
|
@ -27,7 +27,7 @@ public class Version {
|
||||||
// VERSION = 0.5.0 introduces proto buffer for the P2P network and local DB and is a not backward compatible update
|
// VERSION = 0.5.0 introduces proto buffer for the P2P network and local DB and is a not backward compatible update
|
||||||
// Therefore all sub versions start again with 1
|
// Therefore all sub versions start again with 1
|
||||||
// We use semantic versioning with major, minor and patch
|
// We use semantic versioning with major, minor and patch
|
||||||
public static final String VERSION = "1.3.6";
|
public static final String VERSION = "1.3.7";
|
||||||
|
|
||||||
public static int getMajorVersion(String version) {
|
public static int getMajorVersion(String version) {
|
||||||
return getSubVersion(version, 0);
|
return getSubVersion(version, 0);
|
||||||
|
|
|
@ -52,6 +52,13 @@ import javax.sound.sampled.DataLine;
|
||||||
import javax.sound.sampled.LineUnavailableException;
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
import javax.sound.sampled.SourceDataLine;
|
import javax.sound.sampled.SourceDataLine;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents that Bisq gets hibernated from the OS. On OSX there is a tool called caffeinate but it seems it does not
|
||||||
|
* provide the behaviour we need, thus we use the trick to play a almost silent sound file in a loop. This keeps the
|
||||||
|
* application active even if the OS has moved to hibernate. Hibernating Bisq would cause network degradations and other
|
||||||
|
* resource limitations which would lead to offers not published or if a taker takes an offer that the trade process is
|
||||||
|
* at risk to fail due too slow response time.
|
||||||
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Singleton
|
@Singleton
|
||||||
public class AvoidStandbyModeService {
|
public class AvoidStandbyModeService {
|
||||||
|
@ -88,7 +95,7 @@ public class AvoidStandbyModeService {
|
||||||
|
|
||||||
private void start() {
|
private void start() {
|
||||||
isStopped = false;
|
isStopped = false;
|
||||||
if (Utilities.isLinux() || Utilities.isOSX()) {
|
if (Utilities.isLinux()) {
|
||||||
startInhibitor();
|
startInhibitor();
|
||||||
} else {
|
} else {
|
||||||
new Thread(this::playSilentAudioFile, "AvoidStandbyModeService-thread").start();
|
new Thread(this::playSilentAudioFile, "AvoidStandbyModeService-thread").start();
|
||||||
|
@ -125,7 +132,6 @@ public class AvoidStandbyModeService {
|
||||||
|
|
||||||
private void stopInhibitor() {
|
private void stopInhibitor() {
|
||||||
try {
|
try {
|
||||||
// Cannot toggle off osx caffeinate, but it will shutdown with bisq.
|
|
||||||
if (Utilities.isLinux()) {
|
if (Utilities.isLinux()) {
|
||||||
if (!isStopped) {
|
if (!isStopped) {
|
||||||
Objects.requireNonNull(stopLinuxInhibitorCountdownLatch).await();
|
Objects.requireNonNull(stopLinuxInhibitorCountdownLatch).await();
|
||||||
|
@ -167,7 +173,9 @@ public class AvoidStandbyModeService {
|
||||||
|
|
||||||
private File getSoundFile() throws IOException, ResourceNotFoundException {
|
private File getSoundFile() throws IOException, ResourceNotFoundException {
|
||||||
File soundFile = new File(config.appDataDir, "prevent-app-nap-silent-sound.aiff");
|
File soundFile = new File(config.appDataDir, "prevent-app-nap-silent-sound.aiff");
|
||||||
if (!soundFile.exists()) {
|
// We replaced the old file which was 42 MB with a smaller file of 0.8 MB. To enforce replacement we check for
|
||||||
|
// the size...
|
||||||
|
if (!soundFile.exists() || soundFile.length() > 42000000) {
|
||||||
FileUtil.resourceToFile("prevent-app-nap-silent-sound.aiff", soundFile);
|
FileUtil.resourceToFile("prevent-app-nap-silent-sound.aiff", soundFile);
|
||||||
}
|
}
|
||||||
return soundFile;
|
return soundFile;
|
||||||
|
@ -196,7 +204,7 @@ public class AvoidStandbyModeService {
|
||||||
? new String[]{cmd, "--app-id", "Bisq", "--inhibit", "suspend", "--reason", "Avoid Standby", "--inhibit-only"}
|
? new String[]{cmd, "--app-id", "Bisq", "--inhibit", "suspend", "--reason", "Avoid Standby", "--inhibit-only"}
|
||||||
: new String[]{cmd, "--who", "Bisq", "--what", "sleep", "--why", "Avoid Standby", "--mode", "block", "tail", "-f", "/dev/null"};
|
: new String[]{cmd, "--who", "Bisq", "--what", "sleep", "--why", "Avoid Standby", "--mode", "block", "tail", "-f", "/dev/null"};
|
||||||
} else {
|
} else {
|
||||||
params = Utilities.isOSX() ? new String[]{cmd, "-w", "" + ProcessHandle.current().pid()} : null;
|
params = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
params = null; // fall back to silent audio file player
|
params = null; // fall back to silent audio file player
|
||||||
|
@ -235,7 +243,6 @@ public class AvoidStandbyModeService {
|
||||||
new ArrayList<>() {{
|
new ArrayList<>() {{
|
||||||
add(gnomeSessionInhibitPathSpec.get()); // On linux, preferred inhibitor is gnome-session-inhibit,
|
add(gnomeSessionInhibitPathSpec.get()); // On linux, preferred inhibitor is gnome-session-inhibit,
|
||||||
add(systemdInhibitPathSpec.get()); // then fall back to systemd-inhibit if it is installed.
|
add(systemdInhibitPathSpec.get()); // then fall back to systemd-inhibit if it is installed.
|
||||||
add(caffeinatePathSec.get()); // On OSX, caffeinate should be installed.
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
private final Supplier<Optional<String>> gnomeSessionInhibitPathSpec = () ->
|
private final Supplier<Optional<String>> gnomeSessionInhibitPathSpec = () ->
|
||||||
|
@ -243,7 +250,4 @@ public class AvoidStandbyModeService {
|
||||||
|
|
||||||
private final Supplier<Optional<String>> systemdInhibitPathSpec = () ->
|
private final Supplier<Optional<String>> systemdInhibitPathSpec = () ->
|
||||||
cmdPath.apply(new String[]{"/usr/bin/systemd-inhibit", "/bin/systemd-inhibit"});
|
cmdPath.apply(new String[]{"/usr/bin/systemd-inhibit", "/bin/systemd-inhibit"});
|
||||||
|
|
||||||
private final Supplier<Optional<String>> caffeinatePathSec = () ->
|
|
||||||
cmdPath.apply(new String[]{"/usr/bin/caffeinate"});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ public class LocalBitcoinNode {
|
||||||
*/
|
*/
|
||||||
private static boolean detect(int port) {
|
private static boolean detect(int port) {
|
||||||
try (Socket socket = new Socket()) {
|
try (Socket socket = new Socket()) {
|
||||||
var address = new InetSocketAddress(InetAddress.getLocalHost(), port);
|
var address = new InetSocketAddress(InetAddress.getLoopbackAddress(), port);
|
||||||
socket.connect(address, CONNECTION_TIMEOUT);
|
socket.connect(address, CONNECTION_TIMEOUT);
|
||||||
log.info("Local Bitcoin node detected on port {}", port);
|
log.info("Local Bitcoin node detected on port {}", port);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -140,6 +140,10 @@ public class OfferUtil {
|
||||||
if (makerFee == null)
|
if (makerFee == null)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
Coin surplusFunds = availableBalance.subtract(makerFee);
|
||||||
|
if (Restrictions.isDust(surplusFunds)) {
|
||||||
|
return false; // we can't be left with dust
|
||||||
|
}
|
||||||
return !availableBalance.subtract(makerFee).isNegative();
|
return !availableBalance.subtract(makerFee).isNegative();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +175,10 @@ public class OfferUtil {
|
||||||
if (takerFee == null)
|
if (takerFee == null)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
Coin surplusFunds = availableBalance.subtract(takerFee);
|
||||||
|
if (Restrictions.isDust(surplusFunds)) {
|
||||||
|
return false; // we can't be left with dust
|
||||||
|
}
|
||||||
return !availableBalance.subtract(takerFee).isNegative();
|
return !availableBalance.subtract(takerFee).isNegative();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,11 @@ import javax.annotation.Nullable;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ProvidersRepository {
|
public class ProvidersRepository {
|
||||||
private static final List<String> DEFAULT_NODES = Arrays.asList(
|
private static final List<String> DEFAULT_NODES = Arrays.asList(
|
||||||
"http://xc3nh4juf2hshy7e.onion/", // @emzy
|
"http://wizpriceje6q5tdrxkyiazsgu7irquiqjy2dptezqhrtu7l2qelqktid.onion/", // @wiz
|
||||||
"http://ceaanhbvluug4we6.onion/", // @mrosseel
|
"http://emzypricpidesmyqg2hc6dkwitqzaxrqnpkdg3ae2wef5znncu2ambqd.onion/", // @emzy
|
||||||
"http://44mgyoe2b6oqiytt.onion/", // @devinbileck
|
"http://aprcndeiwdrkbf4fq7iozxbd27dl72oeo76n7zmjwdi4z34agdrnheyd.onion/", // @mrosseel
|
||||||
"http://62nvujg5iou3vu3i.onion/", // @alexej996
|
"http://devinpndvdwll4wiqcyq5e7itezmarg7rzicrvf6brzkwxdm374kmmyd.onion/", // @devinbileck
|
||||||
"http://gztmprecgqjq64zh.onion/" // @wiz
|
"http://ro7nv73awqs3ga2qtqeqawrjpbxwarsazznszvr6whv7tes5ehffopid.onion/" // @alexej996
|
||||||
);
|
);
|
||||||
|
|
||||||
private final Config config;
|
private final Config config;
|
||||||
|
@ -110,7 +110,7 @@ public class ProvidersRepository {
|
||||||
// If we run in localhost mode we don't have the tor node running, so we need a clearnet host
|
// If we run in localhost mode we don't have the tor node running, so we need a clearnet host
|
||||||
// Use localhost for using a locally running provider
|
// Use localhost for using a locally running provider
|
||||||
// providerAsString = Collections.singletonList("http://localhost:8080/");
|
// providerAsString = Collections.singletonList("http://localhost:8080/");
|
||||||
providers = Collections.singletonList("http://174.138.104.137:8080/"); // @miker
|
providers = Collections.singletonList("https://price.bisq.wiz.biz/"); // @wiz
|
||||||
} else {
|
} else {
|
||||||
providers = DEFAULT_NODES;
|
providers = DEFAULT_NODES;
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ public class PriceFeedService {
|
||||||
private String currencyCode;
|
private String currencyCode;
|
||||||
private final StringProperty currencyCodeProperty = new SimpleStringProperty();
|
private final StringProperty currencyCodeProperty = new SimpleStringProperty();
|
||||||
private final IntegerProperty updateCounter = new SimpleIntegerProperty(0);
|
private final IntegerProperty updateCounter = new SimpleIntegerProperty(0);
|
||||||
private long epochInSecondAtLastRequest;
|
private long epochInMillisAtLastRequest;
|
||||||
private Map<String, Long> timeStampMap = new HashMap<>();
|
private Map<String, Long> timeStampMap = new HashMap<>();
|
||||||
private long retryDelay = 1;
|
private long retryDelay = 1;
|
||||||
private long requestTs;
|
private long requestTs;
|
||||||
|
@ -280,24 +280,8 @@ public class PriceFeedService {
|
||||||
return updateCounter;
|
return updateCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getLastRequestTimeStampBtcAverage() {
|
public Date getLastRequestTimeStamp() {
|
||||||
return new Date(epochInSecondAtLastRequest);
|
return new Date(epochInMillisAtLastRequest);
|
||||||
}
|
|
||||||
|
|
||||||
public Date getLastRequestTimeStampPoloniex() {
|
|
||||||
Long ts = timeStampMap.get("btcAverageTs");
|
|
||||||
if (ts != null) {
|
|
||||||
return new Date(ts);
|
|
||||||
} else
|
|
||||||
return new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getLastRequestTimeStampCoinmarketcap() {
|
|
||||||
Long ts = timeStampMap.get("coinmarketcapTs");
|
|
||||||
if (ts != null) {
|
|
||||||
return new Date(ts);
|
|
||||||
} else
|
|
||||||
return new Date();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyLatestBisqMarketPrice(Set<TradeStatistics2> tradeStatisticsSet) {
|
public void applyLatestBisqMarketPrice(Set<TradeStatistics2> tradeStatisticsSet) {
|
||||||
|
@ -400,7 +384,12 @@ public class PriceFeedService {
|
||||||
UserThread.execute(() -> {
|
UserThread.execute(() -> {
|
||||||
checkNotNull(result, "Result must not be null at requestAllPrices");
|
checkNotNull(result, "Result must not be null at requestAllPrices");
|
||||||
timeStampMap = result.first;
|
timeStampMap = result.first;
|
||||||
epochInSecondAtLastRequest = timeStampMap.get("btcAverageTs");
|
|
||||||
|
// Each currency rate has a different timestamp, depending on when
|
||||||
|
// the pricenode aggregate rate was calculated
|
||||||
|
// However, the request timestamp is when the pricenode was queried
|
||||||
|
epochInMillisAtLastRequest = System.currentTimeMillis();
|
||||||
|
|
||||||
final Map<String, MarketPrice> priceMap = result.second;
|
final Map<String, MarketPrice> priceMap = result.second;
|
||||||
|
|
||||||
cache.putAll(priceMap);
|
cache.putAll(priceMap);
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.core.NetworkParameters;
|
import org.bitcoinj.core.NetworkParameters;
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Transaction;
|
||||||
import org.bitcoinj.core.TransactionInput;
|
import org.bitcoinj.core.TransactionInput;
|
||||||
|
import org.bitcoinj.core.TransactionOutPoint;
|
||||||
import org.bitcoinj.core.TransactionOutput;
|
import org.bitcoinj.core.TransactionOutput;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -70,6 +71,12 @@ public class DelayedPayoutTxValidation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class InvalidInputException extends Exception {
|
||||||
|
InvalidInputException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void validatePayoutTx(Trade trade,
|
public static void validatePayoutTx(Trade trade,
|
||||||
Transaction delayedPayoutTx,
|
Transaction delayedPayoutTx,
|
||||||
DaoFacade daoFacade,
|
DaoFacade daoFacade,
|
||||||
|
@ -184,4 +191,19 @@ public class DelayedPayoutTxValidation {
|
||||||
throw new DonationAddressException(errorMsg);
|
throw new DonationAddressException(errorMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void validatePayoutTxInput(Transaction depositTx,
|
||||||
|
Transaction delayedPayoutTx)
|
||||||
|
throws InvalidInputException {
|
||||||
|
TransactionInput input = delayedPayoutTx.getInput(0);
|
||||||
|
checkNotNull(input, "delayedPayoutTx.getInput(0) must not be null");
|
||||||
|
// input.getConnectedOutput() is null as the tx is not committed at that point
|
||||||
|
|
||||||
|
TransactionOutPoint outpoint = input.getOutpoint();
|
||||||
|
if (!outpoint.getHash().toString().equals(depositTx.getHashAsString()) || outpoint.getIndex() != 0) {
|
||||||
|
throw new InvalidInputException("Input of delayed payout transaction does not point to output of deposit tx.\n" +
|
||||||
|
"Delayed payout tx=" + delayedPayoutTx + "\n" +
|
||||||
|
"Deposit tx=" + depositTx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ import org.bitcoinj.core.Transaction;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class BuyerVerifiesFinalDelayedPayoutTx extends TradeTask {
|
public class BuyerVerifiesFinalDelayedPayoutTx extends TradeTask {
|
||||||
@SuppressWarnings({"unused"})
|
@SuppressWarnings({"unused"})
|
||||||
|
@ -40,18 +42,25 @@ public class BuyerVerifiesFinalDelayedPayoutTx extends TradeTask {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
Transaction delayedPayoutTx = trade.getDelayedPayoutTx();
|
Transaction delayedPayoutTx = trade.getDelayedPayoutTx();
|
||||||
|
checkNotNull(delayedPayoutTx, "trade.getDelayedPayoutTx() must not be null");
|
||||||
// Check again tx
|
// Check again tx
|
||||||
DelayedPayoutTxValidation.validatePayoutTx(trade,
|
DelayedPayoutTxValidation.validatePayoutTx(trade,
|
||||||
delayedPayoutTx,
|
delayedPayoutTx,
|
||||||
processModel.getDaoFacade(),
|
processModel.getDaoFacade(),
|
||||||
processModel.getBtcWalletService());
|
processModel.getBtcWalletService());
|
||||||
|
|
||||||
|
// Now as we know the deposit tx we can also verify the input
|
||||||
|
Transaction depositTx = trade.getDepositTx();
|
||||||
|
checkNotNull(depositTx, "trade.getDepositTx() must not be null");
|
||||||
|
DelayedPayoutTxValidation.validatePayoutTxInput(depositTx, delayedPayoutTx);
|
||||||
|
|
||||||
complete();
|
complete();
|
||||||
} catch (DelayedPayoutTxValidation.DonationAddressException |
|
} catch (DelayedPayoutTxValidation.DonationAddressException |
|
||||||
DelayedPayoutTxValidation.MissingDelayedPayoutTxException |
|
DelayedPayoutTxValidation.MissingDelayedPayoutTxException |
|
||||||
DelayedPayoutTxValidation.InvalidTxException |
|
DelayedPayoutTxValidation.InvalidTxException |
|
||||||
DelayedPayoutTxValidation.InvalidLockTimeException |
|
DelayedPayoutTxValidation.InvalidLockTimeException |
|
||||||
DelayedPayoutTxValidation.AmountMismatchException e) {
|
DelayedPayoutTxValidation.AmountMismatchException |
|
||||||
|
DelayedPayoutTxValidation.InvalidInputException e) {
|
||||||
failed(e.getMessage());
|
failed(e.getMessage());
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(t);
|
failed(t);
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
# nodeaddress.onion:port [(@owner,@backup)]
|
# nodeaddress.onion:port [(@owner,@backup)]
|
||||||
5quyxpxheyvzmb2d.onion:8000 (@miker)
|
5quyxpxheyvzmb2d.onion:8000 (@miker)
|
||||||
s67qglwhkgkyvr74.onion:8000 (@emzy)
|
s67qglwhkgkyvr74.onion:8000 (@emzy)
|
||||||
3f3cu2yw7u457ztq.onion:8000 (@devinbileck,@ripcurlx)
|
|
||||||
723ljisnynbtdohi.onion:8000 (@emzy)
|
723ljisnynbtdohi.onion:8000 (@emzy)
|
||||||
rm7b56wbrcczpjvl.onion:8000 (@miker)
|
rm7b56wbrcczpjvl.onion:8000 (@miker)
|
||||||
fl3mmribyxgrv63c.onion:8000 (@devinbileck,@ripcurlx)
|
|
||||||
wizseedscybbttk4bmb2lzvbuk2jtect37lcpva4l3twktmkzemwbead.onion:8000 (@wiz)
|
wizseedscybbttk4bmb2lzvbuk2jtect37lcpva4l3twktmkzemwbead.onion:8000 (@wiz)
|
||||||
wizseed3d376esppbmbjxk2fhk2jg5fpucddrzj2kxtbxbx4vrnwclad.onion:8000 (@wiz)
|
wizseed3d376esppbmbjxk2fhk2jg5fpucddrzj2kxtbxbx4vrnwclad.onion:8000 (@wiz)
|
||||||
wizseed7ab2gi3x267xahrp2pkndyrovczezzb46jk6quvguciuyqrid.onion:8000 (@wiz)
|
wizseed7ab2gi3x267xahrp2pkndyrovczezzb46jk6quvguciuyqrid.onion:8000 (@wiz)
|
||||||
devinv3rhon24gqf5v6ondoqgyrbzyqihzyouzv7ptltsewhfmox2zqd.onion:8000 (@devinbileck)
|
devinv3rhon24gqf5v6ondoqgyrbzyqihzyouzv7ptltsewhfmox2zqd.onion:8000 (@devinbileck)
|
||||||
|
devinsn2teu33efff62bnvwbxmfgbfjlgqsu3ad4b4fudx3a725eqnyd.onion:8000 (@devinbileck)
|
||||||
|
devinsn3xuzxhj6pmammrxpydhwwmwp75qkksedo5dn2tlmu7jggo7id.onion:8000 (@devinbileck)
|
||||||
sn3emzy56u3mxzsr4geysc52feoq5qt7ja56km6gygwnszkshunn2sid.onion:8000 (@emzy)
|
sn3emzy56u3mxzsr4geysc52feoq5qt7ja56km6gygwnszkshunn2sid.onion:8000 (@emzy)
|
||||||
sn2bisqad7ncazupgbd3dcedqh5ptirgwofw63djwpdtftwhddo75oid.onion:8000 (@miker)
|
sn2bisqad7ncazupgbd3dcedqh5ptirgwofw63djwpdtftwhddo75oid.onion:8000 (@miker)
|
||||||
|
|
|
@ -243,7 +243,6 @@ mainView.marketPrice.bisqInternalPrice=Price of latest Bisq trade
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=There is no market price from external price feed providers available.\n\
|
mainView.marketPrice.tooltip.bisqInternalPrice=There is no market price from external price feed providers available.\n\
|
||||||
The displayed price is the latest Bisq trade price for that currency.
|
The displayed price is the latest Bisq trade price for that currency.
|
||||||
mainView.marketPrice.tooltip=Market price is provided by {0}{1}\nLast update: {2}\nProvider node URL: {3}
|
mainView.marketPrice.tooltip=Market price is provided by {0}{1}\nLast update: {2}\nProvider node URL: {3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=If the altcoin is not available at Poloniex we use https://coinmarketcap.com
|
|
||||||
mainView.balance.available=Available balance
|
mainView.balance.available=Available balance
|
||||||
mainView.balance.reserved=Reserved in offers
|
mainView.balance.reserved=Reserved in offers
|
||||||
mainView.balance.locked=Locked in trades
|
mainView.balance.locked=Locked in trades
|
||||||
|
@ -1179,10 +1178,9 @@ setting.about.support=Support Bisq
|
||||||
setting.about.def=Bisq is not a company—it is a project open to the community. If you want to participate or support Bisq please follow the links below.
|
setting.about.def=Bisq is not a company—it is a project open to the community. If you want to participate or support Bisq please follow the links below.
|
||||||
setting.about.contribute=Contribute
|
setting.about.contribute=Contribute
|
||||||
setting.about.providers=Data providers
|
setting.about.providers=Data providers
|
||||||
setting.about.apisWithFee=Bisq uses 3rd party APIs for Fiat and Altcoin market prices as well as for mining fee estimation.
|
+setting.about.apisWithFee=Bisq uses Bisq Price Indices for Fiat and Altcoin market prices, and Bisq Mempool Nodes for mining fee estimation.
|
||||||
setting.about.apis=Bisq uses 3rd party APIs for Fiat and Altcoin market prices.
|
+setting.about.apis=Bisq uses Bisq Price Indices for Fiat and Altcoin market prices.
|
||||||
setting.about.pricesProvided=Market prices provided by
|
setting.about.pricesProvided=Market prices provided by
|
||||||
setting.about.pricesProviders={0}, {1} and {2}
|
|
||||||
setting.about.feeEstimation.label=Mining fee estimation provided by
|
setting.about.feeEstimation.label=Mining fee estimation provided by
|
||||||
setting.about.versionDetails=Version details
|
setting.about.versionDetails=Version details
|
||||||
setting.about.version=Application version
|
setting.about.version=Application version
|
||||||
|
@ -1539,6 +1537,10 @@ You should write down the seed words on a sheet of paper. Do not save them on yo
|
||||||
Please note that the seed words are NOT a replacement for a backup.\n\
|
Please note that the seed words are NOT a replacement for a backup.\n\
|
||||||
You need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\n\
|
You need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\n\
|
||||||
Importing seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!
|
Importing seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\n\
|
||||||
|
You need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\n\
|
||||||
|
Importing seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\n\
|
||||||
|
See the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=You have not setup a wallet password which would protect the display of the seed words.\n\n\
|
account.seed.warn.noPw.msg=You have not setup a wallet password which would protect the display of the seed words.\n\n\
|
||||||
Do you want to display the seed words?
|
Do you want to display the seed words?
|
||||||
account.seed.warn.noPw.yes=Yes, and don't ask me again
|
account.seed.warn.noPw.yes=Yes, and don't ask me again
|
||||||
|
@ -3142,12 +3144,15 @@ payment.cashDeposit.info=Please confirm your bank allows you to send cash deposi
|
||||||
payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut \
|
payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut \
|
||||||
otherwise the BTC buyer cannot send you the funds.
|
otherwise the BTC buyer cannot send you the funds.
|
||||||
|
|
||||||
payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\n\
|
payment.usPostalMoneyOrder.info=Trading using US Postal Money Orders (USPMO) on Bisq requires that you understand the following:\n\
|
||||||
However, please be aware of potentially increased risks associated with their use. Bisq will not bear any \
|
\n\
|
||||||
responsibility in case a sent money order is stolen, and the mediator or arbitrator will in such cases award the BTC \
|
- BTC buyers must write the BTC Seller’s name in both the Payer and the Payee’s fields & take a high-resolution photo of the USPMO and envelope with proof of tracking before sending.\n\
|
||||||
to the sender of the money order, provided they can produce tracking information and receipts. \
|
- BTC buyers must send the USPMO to the BTC seller with Delivery Confirmation.\n\
|
||||||
It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the \
|
\n\
|
||||||
risk that the money order is cashed by someone else.
|
In the event mediation is necessary, or if there is a trade dispute, you will be required to send the photos to the Bisq mediator or refund agent, together with the USPMO Serial Number, Post Office Number, and dollar amount, so they can verify the details on the US Post Office website.\n\n\
|
||||||
|
Failure to provide the required information to the Mediator or Arbitrator will result in losing the dispute case.\n\n\
|
||||||
|
In all dispute cases, the USPMO sender bears 100% of the burden of responsibility in providing evidence/proof to the Mediator or Arbitrator.\n\n\
|
||||||
|
If you do not understand these requirements, do not trade using USPMO on Bisq.
|
||||||
|
|
||||||
payment.f2f.contact=Contact info
|
payment.f2f.contact=Contact info
|
||||||
payment.f2f.contact.prompt=How you want to get contacted by the trading peer? (email address, phone number,...)
|
payment.f2f.contact.prompt=How you want to get contacted by the trading peer? (email address, phone number,...)
|
||||||
|
|
|
@ -242,7 +242,6 @@ mainView.marketPriceWithProvider.label=Marktpreis von {0}
|
||||||
mainView.marketPrice.bisqInternalPrice=Preis des letzten Bisq-Handels
|
mainView.marketPrice.bisqInternalPrice=Preis des letzten Bisq-Handels
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=Es ist kein Marktpreis von externen Marktpreis-Anbietern verfügbar.\nDer angezeigte Preis, ist der letzte Bisq-Handelspreis für diese Währung.
|
mainView.marketPrice.tooltip.bisqInternalPrice=Es ist kein Marktpreis von externen Marktpreis-Anbietern verfügbar.\nDer angezeigte Preis, ist der letzte Bisq-Handelspreis für diese Währung.
|
||||||
mainView.marketPrice.tooltip=Marktpreis bereitgestellt von {0}{1}\nLetzte Aktualisierung: {2}\nURL des Knoten-Anbieters: {3}
|
mainView.marketPrice.tooltip=Marktpreis bereitgestellt von {0}{1}\nLetzte Aktualisierung: {2}\nURL des Knoten-Anbieters: {3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=Falls der Altcoin nicht auf Poloniex verfügbar ist nutzen wir https://coinmarketcap.com
|
|
||||||
mainView.balance.available=Verfügbarer Betrag
|
mainView.balance.available=Verfügbarer Betrag
|
||||||
mainView.balance.reserved=In Angeboten reserviert
|
mainView.balance.reserved=In Angeboten reserviert
|
||||||
mainView.balance.locked=In Trades gesperrt
|
mainView.balance.locked=In Trades gesperrt
|
||||||
|
@ -355,8 +354,8 @@ shared.notSigned.noNeed=Dieser Kontotyp verwendet keine Unterzeichnung
|
||||||
|
|
||||||
offerbook.nrOffers=Anzahl der Angebote: {0}
|
offerbook.nrOffers=Anzahl der Angebote: {0}
|
||||||
offerbook.volume={0} (min - max)
|
offerbook.volume={0} (min - max)
|
||||||
offerbook.deposit=Deposit BTC (%)
|
offerbook.deposit=Kaution BTC (%)
|
||||||
offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed.
|
offerbook.deposit.help=Kaution, die von jedem Trader gezahlt wird, um den Trade zu garantieren. Wird zurückgezahlt, wenn der Trade abgeschlossen ist.
|
||||||
|
|
||||||
offerbook.createOfferToBuy=Neues Angebot erstellen, um {0} zu kaufen
|
offerbook.createOfferToBuy=Neues Angebot erstellen, um {0} zu kaufen
|
||||||
offerbook.createOfferToSell=Neues Angebot erstellen, um {0} zu verkaufen
|
offerbook.createOfferToSell=Neues Angebot erstellen, um {0} zu verkaufen
|
||||||
|
@ -464,7 +463,7 @@ createOffer.tac=Mit der Erstellung dieses Angebots stimme ich zu, mit jedem Hän
|
||||||
createOffer.currencyForFee=Handelsgebühr
|
createOffer.currencyForFee=Handelsgebühr
|
||||||
createOffer.setDeposit=Kaution des Käufers festlegen (%)
|
createOffer.setDeposit=Kaution des Käufers festlegen (%)
|
||||||
createOffer.setDepositAsBuyer=Meine Kaution als Käufer festlegen (%)
|
createOffer.setDepositAsBuyer=Meine Kaution als Käufer festlegen (%)
|
||||||
createOffer.setDepositForBothTraders=Set both traders' security deposit (%)
|
createOffer.setDepositForBothTraders=Legen Sie die Kaution beider Trader fest (%)
|
||||||
createOffer.securityDepositInfo=Die Kaution ihres Käufers wird {0}
|
createOffer.securityDepositInfo=Die Kaution ihres Käufers wird {0}
|
||||||
createOffer.securityDepositInfoAsBuyer=Ihre Kaution als Käufer wird {0}
|
createOffer.securityDepositInfoAsBuyer=Ihre Kaution als Käufer wird {0}
|
||||||
createOffer.minSecurityDepositUsed=Min. Kaution des Käufers wird verwendet
|
createOffer.minSecurityDepositUsed=Min. Kaution des Käufers wird verwendet
|
||||||
|
@ -683,7 +682,7 @@ portfolio.pending.step5_buyer.refunded=Rückerstattete Kaution
|
||||||
portfolio.pending.step5_buyer.withdrawBTC=Ihre Bitcoins abheben
|
portfolio.pending.step5_buyer.withdrawBTC=Ihre Bitcoins abheben
|
||||||
portfolio.pending.step5_buyer.amount=Abzuhebender Betrag
|
portfolio.pending.step5_buyer.amount=Abzuhebender Betrag
|
||||||
portfolio.pending.step5_buyer.withdrawToAddress=An diese Adresse abheben
|
portfolio.pending.step5_buyer.withdrawToAddress=An diese Adresse abheben
|
||||||
portfolio.pending.step5_buyer.moveToBisqWallet=Keep funds in Bisq wallet
|
portfolio.pending.step5_buyer.moveToBisqWallet=Gelder in der Bisq Wallet aufbewahren
|
||||||
portfolio.pending.step5_buyer.withdrawExternal=An externe Wallet abheben
|
portfolio.pending.step5_buyer.withdrawExternal=An externe Wallet abheben
|
||||||
portfolio.pending.step5_buyer.alreadyWithdrawn=Ihre Gelder wurden bereits abgehoben.\nBitte überprüfen Sie den Transaktionsverlauf.
|
portfolio.pending.step5_buyer.alreadyWithdrawn=Ihre Gelder wurden bereits abgehoben.\nBitte überprüfen Sie den Transaktionsverlauf.
|
||||||
portfolio.pending.step5_buyer.confirmWithdrawal=Anfrage zum Abheben bestätigen
|
portfolio.pending.step5_buyer.confirmWithdrawal=Anfrage zum Abheben bestätigen
|
||||||
|
@ -1015,7 +1014,6 @@ setting.about.providers=Datenanbieter
|
||||||
setting.about.apisWithFee=Bisq nutzt für Fiatgeld- und Altcoin-Marktpreise sowie geschätzte Mining-Gebühren die APIs 3tr.
|
setting.about.apisWithFee=Bisq nutzt für Fiatgeld- und Altcoin-Marktpreise sowie geschätzte Mining-Gebühren die APIs 3tr.
|
||||||
setting.about.apis=Bisq nutzt für Fiatgeld- und Altcoin-Marktpreise die APIs 3tr.
|
setting.about.apis=Bisq nutzt für Fiatgeld- und Altcoin-Marktpreise die APIs 3tr.
|
||||||
setting.about.pricesProvided=Marktpreise zur Verfügung gestellt von
|
setting.about.pricesProvided=Marktpreise zur Verfügung gestellt von
|
||||||
setting.about.pricesProviders={0}, {1} und {2}
|
|
||||||
setting.about.feeEstimation.label=Geschätzte Mining-Gebühr bereitgestellt von
|
setting.about.feeEstimation.label=Geschätzte Mining-Gebühr bereitgestellt von
|
||||||
setting.about.versionDetails=Versionsdetails
|
setting.about.versionDetails=Versionsdetails
|
||||||
setting.about.version=Anwendungsversion
|
setting.about.version=Anwendungsversion
|
||||||
|
@ -1153,6 +1151,7 @@ account.password.info=Mit Passwortschutz müssen Sie Ihr Passwort eingeben, sowo
|
||||||
|
|
||||||
account.seed.backup.title=Backup der Seed-Wörter Ihrer Wallet erstellen
|
account.seed.backup.title=Backup der Seed-Wörter Ihrer Wallet erstellen
|
||||||
account.seed.info=Bitte schreiben Sie die sowohl Seed-Wörter als auch das Datum auf! Mit diesen Seed-Wörtern und dem Datum können Sie Ihre Wallet jederzeit wiederherstellen.\nDie Seed-Wörter werden für die BTC- und BSQ-Wallet genutzt.\n\nSchreiben Sie die Seed-Wörter auf ein Blatt Papier schreiben und speichern Sie sie nicht auf Ihrem Computer.\n\nBitte beachten Sie, dass die Seed-Wörter KEIN Ersatz für ein Backup sind.\nSie müssen ein Backup des gesamten Anwendungsverzeichnisses unter \"Konto/Backup\" erstellen, um den ursprünglichen Zustand der Anwendung wiederherstellen zu können.\nDas Importieren der Seed-Wörter wird nur für Notfälle empfohlen. Die Anwendung wird ohne richtiges Backup der Datenbankdateien und Schlüssel nicht funktionieren!
|
account.seed.info=Bitte schreiben Sie die sowohl Seed-Wörter als auch das Datum auf! Mit diesen Seed-Wörtern und dem Datum können Sie Ihre Wallet jederzeit wiederherstellen.\nDie Seed-Wörter werden für die BTC- und BSQ-Wallet genutzt.\n\nSchreiben Sie die Seed-Wörter auf ein Blatt Papier schreiben und speichern Sie sie nicht auf Ihrem Computer.\n\nBitte beachten Sie, dass die Seed-Wörter KEIN Ersatz für ein Backup sind.\nSie müssen ein Backup des gesamten Anwendungsverzeichnisses unter \"Konto/Backup\" erstellen, um den ursprünglichen Zustand der Anwendung wiederherstellen zu können.\nDas Importieren der Seed-Wörter wird nur für Notfälle empfohlen. Die Anwendung wird ohne richtiges Backup der Datenbankdateien und Schlüssel nicht funktionieren!
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=Sie haben kein Wallet-Passwort festgelegt, was das Anzeigen der Seed-Wörter schützen würde.\n\nMöchten Sie die Seed-Wörter jetzt anzeigen?
|
account.seed.warn.noPw.msg=Sie haben kein Wallet-Passwort festgelegt, was das Anzeigen der Seed-Wörter schützen würde.\n\nMöchten Sie die Seed-Wörter jetzt anzeigen?
|
||||||
account.seed.warn.noPw.yes=Ja, und nicht erneut fragen
|
account.seed.warn.noPw.yes=Ja, und nicht erneut fragen
|
||||||
account.seed.enterPw=Geben Sie Ihr Passwort ein um die Seed-Wörter zu sehen
|
account.seed.enterPw=Geben Sie Ihr Passwort ein um die Seed-Wörter zu sehen
|
||||||
|
@ -1999,7 +1998,7 @@ filterWindow.disableDaoBelowVersion=Min. für DAO erforderliche Version
|
||||||
filterWindow.disableTradeBelowVersion=Min. zum Handeln erforderliche Version
|
filterWindow.disableTradeBelowVersion=Min. zum Handeln erforderliche Version
|
||||||
filterWindow.add=Filter hinzufügen
|
filterWindow.add=Filter hinzufügen
|
||||||
filterWindow.remove=Filter entfernen
|
filterWindow.remove=Filter entfernen
|
||||||
filterWindow.btcFeeReceiverAddresses=BTC fee receiver addresses
|
filterWindow.btcFeeReceiverAddresses=BTC Gebühr Empfänger-Adressen
|
||||||
|
|
||||||
offerDetailsWindow.minBtcAmount=Min. BTC-Betrag
|
offerDetailsWindow.minBtcAmount=Min. BTC-Betrag
|
||||||
offerDetailsWindow.min=(min. {0})
|
offerDetailsWindow.min=(min. {0})
|
||||||
|
@ -2217,22 +2216,22 @@ popup.accountSigning.signedByPeer=Eines Ihrer Zahlungskonten wurde von einem Tra
|
||||||
popup.accountSigning.peerLimitLifted=Das anfängliche Limit für eines Ihrer Konten wurde aufgehoben.\n\n{0}
|
popup.accountSigning.peerLimitLifted=Das anfängliche Limit für eines Ihrer Konten wurde aufgehoben.\n\n{0}
|
||||||
popup.accountSigning.peerSigner=Eines Ihrer Konten ist reif genug, um andere Zahlungskonten zu unterzeichnen, und das anfängliche Limit für eines Ihrer Konten wurde aufgehoben.\n\n{0}
|
popup.accountSigning.peerSigner=Eines Ihrer Konten ist reif genug, um andere Zahlungskonten zu unterzeichnen, und das anfängliche Limit für eines Ihrer Konten wurde aufgehoben.\n\n{0}
|
||||||
|
|
||||||
popup.accountSigning.singleAccountSelect.headline=Select account age witness
|
popup.accountSigning.singleAccountSelect.headline=Zeuge für Konto-Alter auswählen
|
||||||
popup.accountSigning.singleAccountSelect.description=Search for account age witness.
|
popup.accountSigning.singleAccountSelect.description=Nach Zeuge für Konto-Alter suchen
|
||||||
popup.accountSigning.singleAccountSelect.datePicker=Select point of time for signing
|
popup.accountSigning.singleAccountSelect.datePicker=Zeitpunkt für Unterzeichnung auswählen
|
||||||
popup.accountSigning.confirmSingleAccount.headline=Confirm selected account age witness
|
popup.accountSigning.confirmSingleAccount.headline=Ausgewählten Zeugen für Konto-Alter bestätigen
|
||||||
popup.accountSigning.confirmSingleAccount.selectedHash=Selected witness hash
|
popup.accountSigning.confirmSingleAccount.selectedHash=Ausgewählter Zeugen-Hash
|
||||||
popup.accountSigning.confirmSingleAccount.button=Sign account age witness
|
popup.accountSigning.confirmSingleAccount.button=Zeuge für Konto-Alter unterzeichnen
|
||||||
popup.accountSigning.successSingleAccount.description=Witness {0} was signed
|
popup.accountSigning.successSingleAccount.description=Zeuge {0} wurde unterzeichnet
|
||||||
popup.accountSigning.successSingleAccount.success.headline=Success
|
popup.accountSigning.successSingleAccount.success.headline=Erfolg
|
||||||
popup.accountSigning.successSingleAccount.signError=Failed to sign witness, {0}
|
popup.accountSigning.successSingleAccount.signError=Zeuge nicht unterzeichnet, {0}
|
||||||
|
|
||||||
popup.accountSigning.unsignedPubKeys.headline=Unsigned Pubkeys
|
popup.accountSigning.unsignedPubKeys.headline=Nicht unterzeichnete Pubkeys
|
||||||
popup.accountSigning.unsignedPubKeys.sign=Sign Pubkeys
|
popup.accountSigning.unsignedPubKeys.sign=Pubkeys unterzeichnen
|
||||||
popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed
|
popup.accountSigning.unsignedPubKeys.signed=Pubkeys wurden unterzeichnet
|
||||||
popup.accountSigning.unsignedPubKeys.result.headline=Signing completed
|
popup.accountSigning.unsignedPubKeys.result.headline=Unterzeichnung abgeschlossen
|
||||||
popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys
|
popup.accountSigning.unsignedPubKeys.result.signed=Unterzeichnete Pubkeys
|
||||||
popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign
|
popup.accountSigning.unsignedPubKeys.result.failed=Fehler bei Unterzeichnung
|
||||||
|
|
||||||
####################################################################
|
####################################################################
|
||||||
# Notifications
|
# Notifications
|
||||||
|
@ -2509,13 +2508,13 @@ payment.accountType=Kontotyp
|
||||||
payment.checking=Überprüfe
|
payment.checking=Überprüfe
|
||||||
payment.savings=Ersparnisse
|
payment.savings=Ersparnisse
|
||||||
payment.personalId=Personalausweis
|
payment.personalId=Personalausweis
|
||||||
payment.clearXchange.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle:\nhttps://www.zellepay.com/get-started\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Bisq account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle''s somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Bisq.
|
payment.clearXchange.info=Zelle ist ein Geldtransferdienst, der am besten *durch* eine andere Bank funktioniert.\n\n1. Sehen Sie auf dieser Seite nach, ob (und wie) Ihre Bank mit Zelle zusammenarbeitet:\nhttps://www.zellepay.com/get-started\n\n2. Achten Sie besonders auf Ihre Überweisungslimits - die Sendelimits variieren je nach Bank, und die Banken geben oft separate Tages-, Wochen- und Monatslimits an.\n\n3. Wenn Ihre Bank nicht mit Zelle zusammenarbeitet, können Sie sie trotzdem über die Zelle Mobile App benutzen, aber Ihre Überweisungslimits werden viel niedriger sein.\n\n4. Der auf Ihrem Bisq-Konto angegebene Name MUSS mit dem Namen auf Ihrem Zelle/Bankkonto übereinstimmen. \n\nWenn Sie eine Zelle Transaktion nicht wie in Ihrem Handelsvertrag angegeben durchführen können, verlieren Sie möglicherweise einen Teil (oder die gesamte) Kaution.\n\nWegen des etwas höheren Chargeback-Risikos von Zelle wird Verkäufern empfohlen, nicht unterzeichnete Käufer per E-Mail oder SMS zu kontaktieren, um zu überprüfen, ob der Käufer wirklich das in Bisq angegebene Zelle-Konto besitzt.
|
||||||
payment.fasterPayments.newRequirements.info=Einige Banken haben damit begonnen, den vollständigen Namen des Empfängers für Faster Payments Überweisungen zu überprüfen. Ihr aktuelles Faster Payments-Konto gibt keinen vollständigen Namen an.\n\nBitte erwägen Sie, Ihr Faster Payments-Konto in Bisq neu einzurichten, um zukünftigen {0} Käufern einen vollständigen Namen zu geben.\n\nWenn Sie das Konto neu erstellen, stellen Sie sicher, dass Sie die genaue Bankleitzahl, Kontonummer und die "Salt"-Werte für die Altersverifikation von Ihrem alten Konto auf Ihr neues Konto kopieren. Dadurch wird sichergestellt, dass das Alter und der Unterschriftsstatus Ihres bestehenden Kontos erhalten bleiben.
|
payment.fasterPayments.newRequirements.info=Einige Banken haben damit begonnen, den vollständigen Namen des Empfängers für Faster Payments Überweisungen zu überprüfen. Ihr aktuelles Faster Payments-Konto gibt keinen vollständigen Namen an.\n\nBitte erwägen Sie, Ihr Faster Payments-Konto in Bisq neu einzurichten, um zukünftigen {0} Käufern einen vollständigen Namen zu geben.\n\nWenn Sie das Konto neu erstellen, stellen Sie sicher, dass Sie die genaue Bankleitzahl, Kontonummer und die "Salt"-Werte für die Altersverifikation von Ihrem alten Konto auf Ihr neues Konto kopieren. Dadurch wird sichergestellt, dass das Alter und der Unterschriftsstatus Ihres bestehenden Kontos erhalten bleiben.
|
||||||
payment.moneyGram.info=Wenn MoneyGram verwendet wird, muss der BTC Käufer die MoneyGram und ein Foto der Quittung per E-Mail an den BTC-Verkäufer senden. Die Quittung muss den vollständigen Namen, das Land, das Bundesland des Verkäufers und den Betrag deutlich zeigen. Der Käufer bekommt die E-Mail-Adresse des Verkäufers im Handelsprozess angezeigt.
|
payment.moneyGram.info=Wenn MoneyGram verwendet wird, muss der BTC Käufer die MoneyGram und ein Foto der Quittung per E-Mail an den BTC-Verkäufer senden. Die Quittung muss den vollständigen Namen, das Land, das Bundesland des Verkäufers und den Betrag deutlich zeigen. Der Käufer bekommt die E-Mail-Adresse des Verkäufers im Handelsprozess angezeigt.
|
||||||
payment.westernUnion.info=Wenn Western Union verwendet wird, muss der BTC Käufer die MTCN (Tracking-Nummer) und ein Foto der Quittung per E-Mail an den BTC-Verkäufer senden. Die Quittung muss den vollständigen Namen, die Stadt, das Land des Verkäufers und den Betrag deutlich zeigen. Der Käufer bekommt die E-Mail-Adresse des Verkäufers im Handelsprozess angezeigt.
|
payment.westernUnion.info=Wenn Western Union verwendet wird, muss der BTC Käufer die MTCN (Tracking-Nummer) und ein Foto der Quittung per E-Mail an den BTC-Verkäufer senden. Die Quittung muss den vollständigen Namen, die Stadt, das Land des Verkäufers und den Betrag deutlich zeigen. Der Käufer bekommt die E-Mail-Adresse des Verkäufers im Handelsprozess angezeigt.
|
||||||
payment.halCash.info=Bei Verwendung von HalCash muss der BTC-Käufer dem BTC-Verkäufer den HalCash-Code per SMS vom Mobiltelefon senden.\n\nBitte achten Sie darauf, dass Sie den maximalen Betrag, den Sie bei Ihrer Bank mit HalCash versenden dürfen, nicht überschreiten. Der Mindestbetrag pro Auszahlung beträgt 10 EUR und der Höchstbetrag 600 EUR. Bei wiederholten Abhebungen sind es 3000 EUR pro Empfänger pro Tag und 6000 EUR pro Empfänger pro Monat. Bitte überprüfen Sie diese Limits bei Ihrer Bank, um sicherzustellen, dass sie die gleichen Limits wie hier angegeben verwenden.\n\nDer Auszahlungsbetrag muss ein Vielfaches von 10 EUR betragen, da Sie keine anderen Beträge an einem Geldautomaten abheben können. Die Benutzeroberfläche beim Erstellen und Annehmen eines Angebots passt den BTC-Betrag so an, dass der EUR-Betrag korrekt ist. Sie können keinen marktbasierten Preis verwenden, da sich der EUR-Betrag bei sich ändernden Preisen ändern würde.\n\nIm Streitfall muss der BTC-Käufer den Nachweis erbringen, dass er die EUR geschickt hat.
|
payment.halCash.info=Bei Verwendung von HalCash muss der BTC-Käufer dem BTC-Verkäufer den HalCash-Code per SMS vom Mobiltelefon senden.\n\nBitte achten Sie darauf, dass Sie den maximalen Betrag, den Sie bei Ihrer Bank mit HalCash versenden dürfen, nicht überschreiten. Der Mindestbetrag pro Auszahlung beträgt 10 EUR und der Höchstbetrag 600 EUR. Bei wiederholten Abhebungen sind es 3000 EUR pro Empfänger pro Tag und 6000 EUR pro Empfänger pro Monat. Bitte überprüfen Sie diese Limits bei Ihrer Bank, um sicherzustellen, dass sie die gleichen Limits wie hier angegeben verwenden.\n\nDer Auszahlungsbetrag muss ein Vielfaches von 10 EUR betragen, da Sie keine anderen Beträge an einem Geldautomaten abheben können. Die Benutzeroberfläche beim Erstellen und Annehmen eines Angebots passt den BTC-Betrag so an, dass der EUR-Betrag korrekt ist. Sie können keinen marktbasierten Preis verwenden, da sich der EUR-Betrag bei sich ändernden Preisen ändern würde.\n\nIm Streitfall muss der BTC-Käufer den Nachweis erbringen, dass er die EUR geschickt hat.
|
||||||
payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk.\n\nTo mitigate this risk, Bisq sets per-trade limits based on two factors:\n\n1. The estimated level of chargeback risk for the payment method used\n2. The age of your account for that payment method\n\nThe account you are creating now is new and its age is zero. As your account ages, your per-trade limits will grow:\n\n● During the 1st month, your per-trade limit will be {0}\n● During the 2nd month, your per-trade limit will be {1}\n● After the 2nd month, your per-trade limit will be {2}\n\nPlease note: limits only apply to trade size. You can place as many trades as you like.
|
payment.limits.info=Bitte beachten Sie, dass alle Banküberweisungen mit einem gewissen Rückbuchungsrisiko verbunden sind.\n\nUm dieses Risiko zu mindern, setzt Bisq Limits pro Trade fest, die auf zwei Faktoren basieren:\n\n1. Die geschätzte Höhe des Rückbuchungsrisikos für die verwendete Zahlungsmethode\n2. Das Alter Ihres Kontos für diese Zahlungsmethode\n\nDas Konto, das Sie jetzt anlegen, ist neu und sein Alter ist Null. Wenn Ihr Konto altert, werden die Limits pro Trade erhöht:\n\n● Während des 1. Monats wird Ihr Limit pro Trade {0} betragen\n● Während des 2. Monats wird Ihr Limit pro Trade {1} betragen\n● Nach dem 2. Monat wird Ihr Limit pro Trade {2} betragen\n\nBitte beachten Sie: Die Beschränkungen gelten nur für die Trade-Größe. Sie können so viele Trades platzieren, wie Sie möchten.
|
||||||
payment.limits.info.withSigning=To limit chargeback risk, Bisq sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing, and increase with account age.\n\nSee more:\nhttps://bisq.wiki/Account_limits\n\nPlease note: limits only apply to trade size. You can place as many trades as you like.
|
payment.limits.info.withSigning=Um das Rückbuchungsrisiko zu begrenzen, setzt Bisq für diesen Zahlungskontotyp Limits pro Trade auf der Grundlage der folgenden 2 Faktoren fest:\n\n1. Allgemeines Rückbuchungsrisiko für die Zahlungsmethode\n2. Status der Kontounterzeichnung\n\nDieses Zahlungskonto ist noch nicht unterzeichnet, so dass es auf den Kauf von {0} pro Trade beschränkt ist. Nach der Unterzeichnung werden die Kauflimits wie folgt erhöht:\n\n● Vor der Unterzeichnung und für 30 Tage nach der Unterzeichnung beträgt Ihr Kauflimit pro Trade {0}\n● 30 Tage nach der Unterzeichnung beträgt Ihr Kauflimit pro Trade {1}\n● 60 Tage nach der Unterzeichnung beträgt Ihr Kauflimit pro Trade {2}\n\nVerkaufslimits sind von der Kontounterzeichnung nicht betroffen und erhöhen sich mit dem Alter des Kontos.\n\nSiehe mehr:\nhttps://bisq.wiki/Account_limits\n\n\nBitte beachten Sie: Die Beschränkungen gelten nur für die Trade-Größe. Sie können so viele Trades platzieren, wie Sie möchten.
|
||||||
|
|
||||||
payment.cashDeposit.info=Bitte bestätigen Sie, dass Ihre Bank Bareinzahlungen in Konten von anderen Personen erlaubt. Zum Beispiel werden diese Einzahlungen bei der Bank of America und Wells Fargo nicht mehr erlaubt.
|
payment.cashDeposit.info=Bitte bestätigen Sie, dass Ihre Bank Bareinzahlungen in Konten von anderen Personen erlaubt. Zum Beispiel werden diese Einzahlungen bei der Bank of America und Wells Fargo nicht mehr erlaubt.
|
||||||
|
|
||||||
|
|
|
@ -242,7 +242,6 @@ mainView.marketPriceWithProvider.label=Precio de mercado por {0}
|
||||||
mainView.marketPrice.bisqInternalPrice=Precio del último intercambio en Bisq
|
mainView.marketPrice.bisqInternalPrice=Precio del último intercambio en Bisq
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=No existe un precio de mercado disponible proveniente de fuentes externas.\nEl precio mostrado es el último precio de intercambio en Bisq para esa moneda.
|
mainView.marketPrice.tooltip.bisqInternalPrice=No existe un precio de mercado disponible proveniente de fuentes externas.\nEl precio mostrado es el último precio de intercambio en Bisq para esa moneda.
|
||||||
mainView.marketPrice.tooltip=Precio de mercado ofrecido por {0}{1}\nÚltima actualización: {2}\nURL del nodo proveedor: {3}
|
mainView.marketPrice.tooltip=Precio de mercado ofrecido por {0}{1}\nÚltima actualización: {2}\nURL del nodo proveedor: {3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=Si la altcoin no está disponible en Poloniex usamos https://coinmarketcap.com
|
|
||||||
mainView.balance.available=Saldo disponible
|
mainView.balance.available=Saldo disponible
|
||||||
mainView.balance.reserved=Reservado en ofertas
|
mainView.balance.reserved=Reservado en ofertas
|
||||||
mainView.balance.locked=Bloqueado en intercambios
|
mainView.balance.locked=Bloqueado en intercambios
|
||||||
|
@ -1015,7 +1014,6 @@ setting.about.providers=Proveedores de datos
|
||||||
setting.about.apisWithFee=Bisq usa APIs de terceros para los precios de los mercados Fiat y Altcoin así como para la estimación de tasas de minado.
|
setting.about.apisWithFee=Bisq usa APIs de terceros para los precios de los mercados Fiat y Altcoin así como para la estimación de tasas de minado.
|
||||||
setting.about.apis=Bisq utiliza APIs de terceros para los precios de mercado de Fiat y Altcoin.
|
setting.about.apis=Bisq utiliza APIs de terceros para los precios de mercado de Fiat y Altcoin.
|
||||||
setting.about.pricesProvided=Precios de mercado proporcionados por:
|
setting.about.pricesProvided=Precios de mercado proporcionados por:
|
||||||
setting.about.pricesProviders={0}, {1} y {2}
|
|
||||||
setting.about.feeEstimation.label=Estimación de comisión de minería proporcionada por:
|
setting.about.feeEstimation.label=Estimación de comisión de minería proporcionada por:
|
||||||
setting.about.versionDetails=Detalles de la versión
|
setting.about.versionDetails=Detalles de la versión
|
||||||
setting.about.version=Versión de la aplicación:
|
setting.about.version=Versión de la aplicación:
|
||||||
|
@ -1153,6 +1151,7 @@ account.password.info=Con protección por contraseña necesitará introducir su
|
||||||
|
|
||||||
account.seed.backup.title=Copia de seguridad de palabras semilla del monedero
|
account.seed.backup.title=Copia de seguridad de palabras semilla del monedero
|
||||||
account.seed.info=Por favor apunte en un papel tanto las palabras semilla del monedero como la fecha! Puede recuperar su monedero en cualquier momento con las palabras semilla y la fecha.\nLas mismas palabras semilla se usan para el monedero BTC como BSQ\n\nDebe apuntar las palabras semillas en una hoja de papel. No la guarde en su computadora.\n\nPor favor, tenga en cuenta que las palabras semilla no son un sustituto de la copia de seguridad.\nNecesita hacer la copia de seguridad de todo el directorio de aplicación en la pantalla \"Cuenta/Copia de Seguridad\" para recuperar un estado de aplicación válido y los datos.\nImportar las palabras semilla solo se recomienda para casos de emergencia. La aplicación no será funcional sin una buena copia de seguridad de los archivos de la base de datos y las claves!
|
account.seed.info=Por favor apunte en un papel tanto las palabras semilla del monedero como la fecha! Puede recuperar su monedero en cualquier momento con las palabras semilla y la fecha.\nLas mismas palabras semilla se usan para el monedero BTC como BSQ\n\nDebe apuntar las palabras semillas en una hoja de papel. No la guarde en su computadora.\n\nPor favor, tenga en cuenta que las palabras semilla no son un sustituto de la copia de seguridad.\nNecesita hacer la copia de seguridad de todo el directorio de aplicación en la pantalla \"Cuenta/Copia de Seguridad\" para recuperar un estado de aplicación válido y los datos.\nImportar las palabras semilla solo se recomienda para casos de emergencia. La aplicación no será funcional sin una buena copia de seguridad de los archivos de la base de datos y las claves!
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=No ha establecido una contraseña de cartera que proteja la visualización de las palabras semilla.\n\n¿Quiere que se muestren las palabras semilla?
|
account.seed.warn.noPw.msg=No ha establecido una contraseña de cartera que proteja la visualización de las palabras semilla.\n\n¿Quiere que se muestren las palabras semilla?
|
||||||
account.seed.warn.noPw.yes=Sí, y no preguntar de nuevo
|
account.seed.warn.noPw.yes=Sí, y no preguntar de nuevo
|
||||||
account.seed.enterPw=Introducir contraseña para ver las palabras semilla
|
account.seed.enterPw=Introducir contraseña para ver las palabras semilla
|
||||||
|
|
|
@ -242,7 +242,6 @@ mainView.marketPriceWithProvider.label=قیمت بازار بر اساس {0}
|
||||||
mainView.marketPrice.bisqInternalPrice=قیمت آخرین معاملهی Bisq
|
mainView.marketPrice.bisqInternalPrice=قیمت آخرین معاملهی Bisq
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=قیمت بازارهای خارجی موجود نیست.\nقیمت نمایش داده شده، از آخرین معاملهی Bisq برای ارز موردنظر اتخاذ شده است.
|
mainView.marketPrice.tooltip.bisqInternalPrice=قیمت بازارهای خارجی موجود نیست.\nقیمت نمایش داده شده، از آخرین معاملهی Bisq برای ارز موردنظر اتخاذ شده است.
|
||||||
mainView.marketPrice.tooltip=قیمت بازار توسط {0}{1} ارائه شده است\nآخرین به روز رسانی: {2}\nURL لینک Node ارائه دهنده: {3}
|
mainView.marketPrice.tooltip=قیمت بازار توسط {0}{1} ارائه شده است\nآخرین به روز رسانی: {2}\nURL لینک Node ارائه دهنده: {3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=در صورتی که آلتکوین در Poloniex موجود نباشد، از نرخ https://coinmarketcap.com استفاده می کنیم.
|
|
||||||
mainView.balance.available=موجودی در دسترس
|
mainView.balance.available=موجودی در دسترس
|
||||||
mainView.balance.reserved=رزرو شده در پیشنهادها
|
mainView.balance.reserved=رزرو شده در پیشنهادها
|
||||||
mainView.balance.locked=قفل شده در معاملات
|
mainView.balance.locked=قفل شده در معاملات
|
||||||
|
@ -1015,7 +1014,6 @@ setting.about.providers=ارائه دهندگان داده
|
||||||
setting.about.apisWithFee=Bisq از APIهای شخص ثالث 3rd party برای قیمت های روز فیات و آلت کوین و همچنین برای برآورد هزینه تراکنش شبکه استفاده می کند.
|
setting.about.apisWithFee=Bisq از APIهای شخص ثالث 3rd party برای قیمت های روز فیات و آلت کوین و همچنین برای برآورد هزینه تراکنش شبکه استفاده می کند.
|
||||||
setting.about.apis=Bisq از APIهای شخص ثالث 3rd party برای قیمت های روز فیات و آلت کوین استفاده می کند.
|
setting.about.apis=Bisq از APIهای شخص ثالث 3rd party برای قیمت های روز فیات و آلت کوین استفاده می کند.
|
||||||
setting.about.pricesProvided=قیمتهای بازار ارائه شده توسط
|
setting.about.pricesProvided=قیمتهای بازار ارائه شده توسط
|
||||||
setting.about.pricesProviders={0}, {1} و {2}
|
|
||||||
setting.about.feeEstimation.label=برآورد کارمزد استخراج ارائه شده توسط
|
setting.about.feeEstimation.label=برآورد کارمزد استخراج ارائه شده توسط
|
||||||
setting.about.versionDetails=جزئیات نسخه
|
setting.about.versionDetails=جزئیات نسخه
|
||||||
setting.about.version=نسخه برنامه
|
setting.about.version=نسخه برنامه
|
||||||
|
@ -1153,6 +1151,7 @@ account.password.info=با محافظت رمزعبوری شما باید با ه
|
||||||
|
|
||||||
account.seed.backup.title=پشتیبان گیری از کلمات رمز خصوصی کیف های پول شما
|
account.seed.backup.title=پشتیبان گیری از کلمات رمز خصوصی کیف های پول شما
|
||||||
account.seed.info=لطفا هم کلمات seed و هم تاریخ را یادداشت کنید! شما هر زمانی که بخواهید میتوانید کیفپولتان را با استفاده از کلمات seed و تاریخ بازیابی کنید.\nهمین کلمات seed برای کیفپولهای BTC و BSQ هم استفاده میشود.\n\nشما باید کلمات seed را روی یک برگ کاغذ یادداشت کنید. آنها را روی کامپیوتر خودتان ذخیره نکنید.\n\nلطفا توجه کنید که کلمات seed جایگزینی برای یک پشتیبان نیستند.\nبرای بازیابی وضعیت و دادههای برنامه باید از طریق صفحه \"Account/Backup\" از کل پوشه برنامه پشتیبان بسازید.\nوارد کردن کلمات seed فقط در موارد اورژانسی توصیه میشود. برنامه بدون پشتیبان از پایگاه داده و کلیدهای مناسب درست عمل نخواهد کرد!
|
account.seed.info=لطفا هم کلمات seed و هم تاریخ را یادداشت کنید! شما هر زمانی که بخواهید میتوانید کیفپولتان را با استفاده از کلمات seed و تاریخ بازیابی کنید.\nهمین کلمات seed برای کیفپولهای BTC و BSQ هم استفاده میشود.\n\nشما باید کلمات seed را روی یک برگ کاغذ یادداشت کنید. آنها را روی کامپیوتر خودتان ذخیره نکنید.\n\nلطفا توجه کنید که کلمات seed جایگزینی برای یک پشتیبان نیستند.\nبرای بازیابی وضعیت و دادههای برنامه باید از طریق صفحه \"Account/Backup\" از کل پوشه برنامه پشتیبان بسازید.\nوارد کردن کلمات seed فقط در موارد اورژانسی توصیه میشود. برنامه بدون پشتیبان از پایگاه داده و کلیدهای مناسب درست عمل نخواهد کرد!
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=شما یک رمز عبور کیف پول تنظیم نکرده اید که از نمایش کلمات رمز خصوصی محافظت کند.\n\nآیا می خواهید کلمات رمز خصوصی نشان داده شود؟
|
account.seed.warn.noPw.msg=شما یک رمز عبور کیف پول تنظیم نکرده اید که از نمایش کلمات رمز خصوصی محافظت کند.\n\nآیا می خواهید کلمات رمز خصوصی نشان داده شود؟
|
||||||
account.seed.warn.noPw.yes=بلی، و دوباره از من نپرس
|
account.seed.warn.noPw.yes=بلی، و دوباره از من نپرس
|
||||||
account.seed.enterPw=وارد کردن رمز عبور به منظور مشاهده ی کلمات رمز خصوصی
|
account.seed.enterPw=وارد کردن رمز عبور به منظور مشاهده ی کلمات رمز خصوصی
|
||||||
|
|
|
@ -242,7 +242,6 @@ mainView.marketPriceWithProvider.label=Prix du marché par {0}
|
||||||
mainView.marketPrice.bisqInternalPrice=Cours de la dernière transaction Bisq
|
mainView.marketPrice.bisqInternalPrice=Cours de la dernière transaction Bisq
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=Il n'y a pas de cours de marché disponible depuis une source externe.\nLe cours affiché est celui de la dernière transaction Bisq pour cette devise.
|
mainView.marketPrice.tooltip.bisqInternalPrice=Il n'y a pas de cours de marché disponible depuis une source externe.\nLe cours affiché est celui de la dernière transaction Bisq pour cette devise.
|
||||||
mainView.marketPrice.tooltip=Le prix de marché est fourni par {0}{1}\nDernière mise à jour: {2}\nURL du noeud: {3}
|
mainView.marketPrice.tooltip=Le prix de marché est fourni par {0}{1}\nDernière mise à jour: {2}\nURL du noeud: {3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=Si l'altcoin n'est pas disponible sur Poloniex nous utilisons https://coinmarketcap.com
|
|
||||||
mainView.balance.available=Solde disponible
|
mainView.balance.available=Solde disponible
|
||||||
mainView.balance.reserved=Réservé en ordres
|
mainView.balance.reserved=Réservé en ordres
|
||||||
mainView.balance.locked=Bloqué en transactions
|
mainView.balance.locked=Bloqué en transactions
|
||||||
|
@ -1015,7 +1014,6 @@ setting.about.providers=Fournisseurs de données
|
||||||
setting.about.apisWithFee=Bisq utilise des APIs tierces ou 3rd party pour le taux de change des devises nationales et des cryptomonnaies, aussi bien que pour obtenir une estimation des frais de minage.
|
setting.about.apisWithFee=Bisq utilise des APIs tierces ou 3rd party pour le taux de change des devises nationales et des cryptomonnaies, aussi bien que pour obtenir une estimation des frais de minage.
|
||||||
setting.about.apis=Bisq utilise des APIs tierces ou 3rd party pour le taux de change des devises nationales et des cryptomonnaies.
|
setting.about.apis=Bisq utilise des APIs tierces ou 3rd party pour le taux de change des devises nationales et des cryptomonnaies.
|
||||||
setting.about.pricesProvided=Prix de marché fourni par
|
setting.about.pricesProvided=Prix de marché fourni par
|
||||||
setting.about.pricesProviders={0}, {1} et {2}
|
|
||||||
setting.about.feeEstimation.label=Estimation des frais de minage fournie par
|
setting.about.feeEstimation.label=Estimation des frais de minage fournie par
|
||||||
setting.about.versionDetails=Détails sur la version
|
setting.about.versionDetails=Détails sur la version
|
||||||
setting.about.version=Version de l'application
|
setting.about.version=Version de l'application
|
||||||
|
@ -1153,6 +1151,7 @@ account.password.info=Avec la protection par mot de passe, vous devrez entrer vo
|
||||||
|
|
||||||
account.seed.backup.title=Sauvegarder les mots composant la seed de votre portefeuille
|
account.seed.backup.title=Sauvegarder les mots composant la seed de votre portefeuille
|
||||||
account.seed.info=Veuillez noter les mots de la seed du portefeuille ainsi que la date! Vous pouvez récupérer votre portefeuille à tout moment avec les mots de la seed et la date.\nLes mêmes mots-clés de la seed sont utilisés pour les portefeuilles BTC et BSQ.\n\nVous devriez écrire les mots de la seed sur une feuille de papier. Ne les enregistrez pas sur votre ordinateur.\n\nVeuillez noter que les mots de la seed ne remplacent PAS une sauvegarde.\nVous devez créer une sauvegarde de l'intégralité du répertoire de l'application à partir de l'écran \"Compte/Sauvergarde\" pour restaurer correctement les données de l'application.\nL'importation de mots de la seed n'est recommandée qu'en cas d'urgence. L'application ne sera pas fonctionnelle sans une sauvegarde adéquate des fichiers et des clés de la base de données !
|
account.seed.info=Veuillez noter les mots de la seed du portefeuille ainsi que la date! Vous pouvez récupérer votre portefeuille à tout moment avec les mots de la seed et la date.\nLes mêmes mots-clés de la seed sont utilisés pour les portefeuilles BTC et BSQ.\n\nVous devriez écrire les mots de la seed sur une feuille de papier. Ne les enregistrez pas sur votre ordinateur.\n\nVeuillez noter que les mots de la seed ne remplacent PAS une sauvegarde.\nVous devez créer une sauvegarde de l'intégralité du répertoire de l'application à partir de l'écran \"Compte/Sauvergarde\" pour restaurer correctement les données de l'application.\nL'importation de mots de la seed n'est recommandée qu'en cas d'urgence. L'application ne sera pas fonctionnelle sans une sauvegarde adéquate des fichiers et des clés de la base de données !
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=Vous n'avez pas configuré un mot de passe de portefeuille qui protégerait l'affichage des mots composant la seed.\n\nVoulez-vous afficher les mots composant la seed?
|
account.seed.warn.noPw.msg=Vous n'avez pas configuré un mot de passe de portefeuille qui protégerait l'affichage des mots composant la seed.\n\nVoulez-vous afficher les mots composant la seed?
|
||||||
account.seed.warn.noPw.yes=Oui, et ne me le demander plus à l'avenir
|
account.seed.warn.noPw.yes=Oui, et ne me le demander plus à l'avenir
|
||||||
account.seed.enterPw=Entrer le mot de passe afficher les mots composant la seed
|
account.seed.enterPw=Entrer le mot de passe afficher les mots composant la seed
|
||||||
|
|
|
@ -242,7 +242,6 @@ mainView.marketPriceWithProvider.label={0} による市場価格
|
||||||
mainView.marketPrice.bisqInternalPrice=Bisqにおける最新の取引価格
|
mainView.marketPrice.bisqInternalPrice=Bisqにおける最新の取引価格
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=利用可能な外部価格フィードプロバイダーからの市場価格がありません。\n表示されている価格は、その通貨の最新のBisq取引価格です。
|
mainView.marketPrice.tooltip.bisqInternalPrice=利用可能な外部価格フィードプロバイダーからの市場価格がありません。\n表示されている価格は、その通貨の最新のBisq取引価格です。
|
||||||
mainView.marketPrice.tooltip=市場価格は{0}{1}に提供されています\n最終更新: {2}\n提供者のノードのURL: {3}
|
mainView.marketPrice.tooltip=市場価格は{0}{1}に提供されています\n最終更新: {2}\n提供者のノードのURL: {3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=アルトコインがPoloniexで利用可能でない場合、https://coinmarketcap.com を利用します
|
|
||||||
mainView.balance.available=利用可能残高
|
mainView.balance.available=利用可能残高
|
||||||
mainView.balance.reserved=オファーのために予約済み
|
mainView.balance.reserved=オファーのために予約済み
|
||||||
mainView.balance.locked=トレードにロック中
|
mainView.balance.locked=トレードにロック中
|
||||||
|
@ -1015,7 +1014,6 @@ setting.about.providers=データプロバイダー
|
||||||
setting.about.apisWithFee=Bisqは、法定通貨とアルトコインの市場価格や、マイニング料金の推定にサードパーティAPIを使用します。
|
setting.about.apisWithFee=Bisqは、法定通貨とアルトコインの市場価格や、マイニング料金の推定にサードパーティAPIを使用します。
|
||||||
setting.about.apis=Bisqは法定通貨とアルトコインの市場価格の為にサードパーティAPIを使用します。
|
setting.about.apis=Bisqは法定通貨とアルトコインの市場価格の為にサードパーティAPIを使用します。
|
||||||
setting.about.pricesProvided=市場価格を提供している:
|
setting.about.pricesProvided=市場価格を提供している:
|
||||||
setting.about.pricesProviders={0}, {1} と {2}
|
|
||||||
setting.about.feeEstimation.label=推定マイニング手数料の提供:
|
setting.about.feeEstimation.label=推定マイニング手数料の提供:
|
||||||
setting.about.versionDetails=バージョン詳細
|
setting.about.versionDetails=バージョン詳細
|
||||||
setting.about.version=アプリのバージョン
|
setting.about.version=アプリのバージョン
|
||||||
|
@ -1153,6 +1151,7 @@ account.password.info=パスワード保護を使用すると、アプリケー
|
||||||
|
|
||||||
account.seed.backup.title=あなたのウォレットのシードワードをバックアップ
|
account.seed.backup.title=あなたのウォレットのシードワードをバックアップ
|
||||||
account.seed.info=ウォレットのシードワードと日付の両方を書き留めてください!あなたはシードワードと日付でいつでもウォレットを復元することができます。\nBTCおよびBSQウォレットには同じシードワードが使用されています。\n\nあなたは一枚の紙にシードワードを書き留めるべきです。コンピュータに保存しないでください。\n\nシードワードはバックアップの代わりにはならないことに気をつけて下さい。\nアプリケーションの状態とデータを復元するには「アカウント/バックアップ」画面からアプリケーションディレクトリ全体のバックアップを作成する必要があります。\nシードワードのインポートは緊急の場合にのみ推奨されます。データベースファイルとキーの適切なバックアップがなければ、アプリケーションは機能しません!
|
account.seed.info=ウォレットのシードワードと日付の両方を書き留めてください!あなたはシードワードと日付でいつでもウォレットを復元することができます。\nBTCおよびBSQウォレットには同じシードワードが使用されています。\n\nあなたは一枚の紙にシードワードを書き留めるべきです。コンピュータに保存しないでください。\n\nシードワードはバックアップの代わりにはならないことに気をつけて下さい。\nアプリケーションの状態とデータを復元するには「アカウント/バックアップ」画面からアプリケーションディレクトリ全体のバックアップを作成する必要があります。\nシードワードのインポートは緊急の場合にのみ推奨されます。データベースファイルとキーの適切なバックアップがなければ、アプリケーションは機能しません!
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=シードワードの表示を保護するためのウォレットパスワードを設定していません。 \n\nシードワードを表示しますか?
|
account.seed.warn.noPw.msg=シードワードの表示を保護するためのウォレットパスワードを設定していません。 \n\nシードワードを表示しますか?
|
||||||
account.seed.warn.noPw.yes=はい、そして次回から確認しないで下さい
|
account.seed.warn.noPw.yes=はい、そして次回から確認しないで下さい
|
||||||
account.seed.enterPw=シードワードを見るためにパスワードを入力
|
account.seed.enterPw=シードワードを見るためにパスワードを入力
|
||||||
|
|
|
@ -242,7 +242,6 @@ mainView.marketPriceWithProvider.label=Preço de mercado por {0}
|
||||||
mainView.marketPrice.bisqInternalPrice=Preço da última negociação Bisq
|
mainView.marketPrice.bisqInternalPrice=Preço da última negociação Bisq
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=Não foi encontrado preço de mercado nos provedores externos.\nO preço exibido corresponde ao último preço de negociação no Bisq para essa moeda.
|
mainView.marketPrice.tooltip.bisqInternalPrice=Não foi encontrado preço de mercado nos provedores externos.\nO preço exibido corresponde ao último preço de negociação no Bisq para essa moeda.
|
||||||
mainView.marketPrice.tooltip=Preço de Mercado fornecido por {0}{1}\nÚltima atualização: {2}\nURL do provedor: {3}
|
mainView.marketPrice.tooltip=Preço de Mercado fornecido por {0}{1}\nÚltima atualização: {2}\nURL do provedor: {3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=Se a altcoin não estiver disponível na Poloniex, usaremos https://coinmarketcap.com
|
|
||||||
mainView.balance.available=Saldo disponível
|
mainView.balance.available=Saldo disponível
|
||||||
mainView.balance.reserved=Reservado em ofertas
|
mainView.balance.reserved=Reservado em ofertas
|
||||||
mainView.balance.locked=Travado em negociações
|
mainView.balance.locked=Travado em negociações
|
||||||
|
@ -1015,7 +1014,6 @@ setting.about.providers=Provedores de dados
|
||||||
setting.about.apisWithFee=O Bisq utiliza APIs de terceiros para obter os preços de moedas fiduciárias e de altcoins, assim como para estimar a taxa de mineração.
|
setting.about.apisWithFee=O Bisq utiliza APIs de terceiros para obter os preços de moedas fiduciárias e de altcoins, assim como para estimar a taxa de mineração.
|
||||||
setting.about.apis=Bisq utiliza APIs de terceiros para os preços de moedas fiduciárias e altcoins.
|
setting.about.apis=Bisq utiliza APIs de terceiros para os preços de moedas fiduciárias e altcoins.
|
||||||
setting.about.pricesProvided=Preços de mercado fornecidos por
|
setting.about.pricesProvided=Preços de mercado fornecidos por
|
||||||
setting.about.pricesProviders={0}, {1} e {2}
|
|
||||||
setting.about.feeEstimation.label=Estimativa da taxa de mineração fornecida por
|
setting.about.feeEstimation.label=Estimativa da taxa de mineração fornecida por
|
||||||
setting.about.versionDetails=Detalhes da versão
|
setting.about.versionDetails=Detalhes da versão
|
||||||
setting.about.version=Versão do programa
|
setting.about.version=Versão do programa
|
||||||
|
@ -1153,6 +1151,7 @@ account.password.info=Ao proteger a carteira com uma senha, você precisará dig
|
||||||
|
|
||||||
account.seed.backup.title=Fazer backup das palavras-semente da carteira
|
account.seed.backup.title=Fazer backup das palavras-semente da carteira
|
||||||
account.seed.info=Por favor, anote em um papel a data e as palavras-semente da carteira! Com essas informações, você poderá recuperar sua carteira à qualquer momento.\nA semente exibida é usada tanto para a carteira BTC quanto para a carteira BSQ.\n\nVocê deve anotá-las em uma folha de papel. Jamais anote as palavras em um arquivo no seu computador ou em seu e-mail.\n\nNote que a semente da carteira NÃO substitui um backup.\nPara fazer isso, você precisa fazer backup da pasta do Bisq na seção \"Conta/Backup\".\nA importação da semente da carteira só é recomendada em casos de emergência. O programa não funcionará corretamente se você não recuperá-lo através de um backup!
|
account.seed.info=Por favor, anote em um papel a data e as palavras-semente da carteira! Com essas informações, você poderá recuperar sua carteira à qualquer momento.\nA semente exibida é usada tanto para a carteira BTC quanto para a carteira BSQ.\n\nVocê deve anotá-las em uma folha de papel. Jamais anote as palavras em um arquivo no seu computador ou em seu e-mail.\n\nNote que a semente da carteira NÃO substitui um backup.\nPara fazer isso, você precisa fazer backup da pasta do Bisq na seção \"Conta/Backup\".\nA importação da semente da carteira só é recomendada em casos de emergência. O programa não funcionará corretamente se você não recuperá-lo através de um backup!
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=Você não definiu uma senha para carteira, que protegeria a exibição das palavras-semente.\n\nGostaria de exibir as palavras-semente?
|
account.seed.warn.noPw.msg=Você não definiu uma senha para carteira, que protegeria a exibição das palavras-semente.\n\nGostaria de exibir as palavras-semente?
|
||||||
account.seed.warn.noPw.yes=Sim, e não me pergunte novamente
|
account.seed.warn.noPw.yes=Sim, e não me pergunte novamente
|
||||||
account.seed.enterPw=Digite a senha para ver a semente da carteira
|
account.seed.enterPw=Digite a senha para ver a semente da carteira
|
||||||
|
|
|
@ -242,7 +242,6 @@ mainView.marketPriceWithProvider.label=Preço de mercado por {0}
|
||||||
mainView.marketPrice.bisqInternalPrice=Preço do último negócio do Bisq
|
mainView.marketPrice.bisqInternalPrice=Preço do último negócio do Bisq
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=Não há preço de mercado de fornecedores de feed de preço externos disponíveis.\nO preço exibido é o mais recente preço de negócio do Bisq para essa moeda.
|
mainView.marketPrice.tooltip.bisqInternalPrice=Não há preço de mercado de fornecedores de feed de preço externos disponíveis.\nO preço exibido é o mais recente preço de negócio do Bisq para essa moeda.
|
||||||
mainView.marketPrice.tooltip=O preço de mercado é fornecido por {0} {1}\nÚltima atualização: {2}\nURL do nó do provedor: {3}
|
mainView.marketPrice.tooltip=O preço de mercado é fornecido por {0} {1}\nÚltima atualização: {2}\nURL do nó do provedor: {3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=Se a altcoin não estiver disponível na Poloniex, usaremos https://coinmarketcap.com
|
|
||||||
mainView.balance.available=Saldo disponível
|
mainView.balance.available=Saldo disponível
|
||||||
mainView.balance.reserved=Reservado em ofertas
|
mainView.balance.reserved=Reservado em ofertas
|
||||||
mainView.balance.locked=Bloqueado em negócios
|
mainView.balance.locked=Bloqueado em negócios
|
||||||
|
@ -1015,7 +1014,6 @@ setting.about.providers=Provedores de dados
|
||||||
setting.about.apisWithFee=A Bisq usa APIs de terceiros para os preços de mercado de moedas fiduciárias e Altcoin, bem como para estimativas de taxas de mineração.
|
setting.about.apisWithFee=A Bisq usa APIs de terceiros para os preços de mercado de moedas fiduciárias e Altcoin, bem como para estimativas de taxas de mineração.
|
||||||
setting.about.apis=Bisq utiliza APIs de terceiros para os preços de moedas fiduciárias e altcoins.
|
setting.about.apis=Bisq utiliza APIs de terceiros para os preços de moedas fiduciárias e altcoins.
|
||||||
setting.about.pricesProvided=Preços de mercado fornecidos por
|
setting.about.pricesProvided=Preços de mercado fornecidos por
|
||||||
setting.about.pricesProviders={0}, {1} e {2}
|
|
||||||
setting.about.feeEstimation.label=Taxa de mineração fornecida por
|
setting.about.feeEstimation.label=Taxa de mineração fornecida por
|
||||||
setting.about.versionDetails=Detalhes da versão
|
setting.about.versionDetails=Detalhes da versão
|
||||||
setting.about.version=Versão do programa
|
setting.about.version=Versão do programa
|
||||||
|
@ -1153,6 +1151,7 @@ account.password.info=Com a proteção por senha, você precisará inserir a sua
|
||||||
|
|
||||||
account.seed.backup.title=Fazer backup das palavras semente da sua carteira
|
account.seed.backup.title=Fazer backup das palavras semente da sua carteira
|
||||||
account.seed.info=Por favor, anote as palavras-semente da carteira e a data! Você pode recuperar sua carteira a qualquer momento com palavras-semente e a data.\nAs mesmas palavras-semente são usadas para a carteira BTC e BSQ.\n\nVocê deve anotar as palavras-semente numa folha de papel. Não as guarde no seu computador.\n\nPor favor, note que as palavras-semente não são um substituto para um backup.\nVocê precisa criar um backup de todo o diretório do programa a partir do ecrã \"Conta/Backup\" para recuperar o estado e os dados do programa.\nA importação de palavras-semente é recomendada apenas para casos de emergência. O programa não será funcional sem um backup adequado dos arquivos da base de dados e das chaves!
|
account.seed.info=Por favor, anote as palavras-semente da carteira e a data! Você pode recuperar sua carteira a qualquer momento com palavras-semente e a data.\nAs mesmas palavras-semente são usadas para a carteira BTC e BSQ.\n\nVocê deve anotar as palavras-semente numa folha de papel. Não as guarde no seu computador.\n\nPor favor, note que as palavras-semente não são um substituto para um backup.\nVocê precisa criar um backup de todo o diretório do programa a partir do ecrã \"Conta/Backup\" para recuperar o estado e os dados do programa.\nA importação de palavras-semente é recomendada apenas para casos de emergência. O programa não será funcional sem um backup adequado dos arquivos da base de dados e das chaves!
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=Você não definiu uma senha da carteira que protegeria a exibição das palavras-semente.\n\nVocê quer exibir as palavras-semente?
|
account.seed.warn.noPw.msg=Você não definiu uma senha da carteira que protegeria a exibição das palavras-semente.\n\nVocê quer exibir as palavras-semente?
|
||||||
account.seed.warn.noPw.yes=Sim, e não me pergunte novamente
|
account.seed.warn.noPw.yes=Sim, e não me pergunte novamente
|
||||||
account.seed.enterPw=Digite a senha para ver palavras-semente
|
account.seed.enterPw=Digite a senha para ver palavras-semente
|
||||||
|
|
|
@ -242,7 +242,6 @@ mainView.marketPriceWithProvider.label=Рыночный курс {0}
|
||||||
mainView.marketPrice.bisqInternalPrice=Курс последней сделки в Bisq
|
mainView.marketPrice.bisqInternalPrice=Курс последней сделки в Bisq
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=Нет данных от источника рыночного курса.\nПредоставлен курс последней сделки в Bisq для этой валютной пары.
|
mainView.marketPrice.tooltip.bisqInternalPrice=Нет данных от источника рыночного курса.\nПредоставлен курс последней сделки в Bisq для этой валютной пары.
|
||||||
mainView.marketPrice.tooltip=Рыночный курс предоставлен {0}{1}\nОбновление: {2}\nURL источника данных: {3}
|
mainView.marketPrice.tooltip=Рыночный курс предоставлен {0}{1}\nОбновление: {2}\nURL источника данных: {3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=Если альткойн недоступен на Poloniex, используется https://coinmarketcap.com
|
|
||||||
mainView.balance.available=Доступный баланс
|
mainView.balance.available=Доступный баланс
|
||||||
mainView.balance.reserved=Выделено на предложения
|
mainView.balance.reserved=Выделено на предложения
|
||||||
mainView.balance.locked=Используется в сделках
|
mainView.balance.locked=Используется в сделках
|
||||||
|
@ -1015,7 +1014,6 @@ setting.about.providers=Источники данных
|
||||||
setting.about.apisWithFee=Bisq использует сторонние API для определения рыночного курса валют и альткойнов, а также расчёта комиссии майнера.
|
setting.about.apisWithFee=Bisq использует сторонние API для определения рыночного курса валют и альткойнов, а также расчёта комиссии майнера.
|
||||||
setting.about.apis=Bisq использует сторонние API для определения рыночного курса валют и альткойнов.
|
setting.about.apis=Bisq использует сторонние API для определения рыночного курса валют и альткойнов.
|
||||||
setting.about.pricesProvided=Рыночный курс предоставлен
|
setting.about.pricesProvided=Рыночный курс предоставлен
|
||||||
setting.about.pricesProviders={0}, {1} и {2}
|
|
||||||
setting.about.feeEstimation.label=Расчёт комиссии майнера предоставлен
|
setting.about.feeEstimation.label=Расчёт комиссии майнера предоставлен
|
||||||
setting.about.versionDetails=Подробности версии
|
setting.about.versionDetails=Подробности версии
|
||||||
setting.about.version=Версия приложения
|
setting.about.version=Версия приложения
|
||||||
|
@ -1153,6 +1151,7 @@ account.password.info=При использовании пароля его не
|
||||||
|
|
||||||
account.seed.backup.title=Сохраните мнемоническую фразу для вашего кошелька
|
account.seed.backup.title=Сохраните мнемоническую фразу для вашего кошелька
|
||||||
account.seed.info=Запишите мнемоническую фразу для кошелька и дату создания его! Используя эти данные, вы сможете восстановить ваш кошелёк когда угодно.\nДля обоих кошельков, BTC и BSQ, используется одна и та же мнемоническая фраза.\n\nВам следует записать её на бумаге и не хранить на компьютере.\n\nМнемоническая фраза НЕ заменяет резервную копию.\nВам следует сделать резервную копию всего каталога приложения в разделе \«Счёт/Резервное копирование\» для восстановления состояния приложения и данных.\nИмпорт мнемонической фразы рекомендуется только в экстренных случаях. Приложение не будет функционировать должным образом без наличия резервной копии файлов базы данных и ключей!
|
account.seed.info=Запишите мнемоническую фразу для кошелька и дату создания его! Используя эти данные, вы сможете восстановить ваш кошелёк когда угодно.\nДля обоих кошельков, BTC и BSQ, используется одна и та же мнемоническая фраза.\n\nВам следует записать её на бумаге и не хранить на компьютере.\n\nМнемоническая фраза НЕ заменяет резервную копию.\nВам следует сделать резервную копию всего каталога приложения в разделе \«Счёт/Резервное копирование\» для восстановления состояния приложения и данных.\nИмпорт мнемонической фразы рекомендуется только в экстренных случаях. Приложение не будет функционировать должным образом без наличия резервной копии файлов базы данных и ключей!
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=Вы не установили пароль от кошелька для защиты мнемонической фразы.\n\nОтобразить мнемоническую фразу на экране?
|
account.seed.warn.noPw.msg=Вы не установили пароль от кошелька для защиты мнемонической фразы.\n\nОтобразить мнемоническую фразу на экране?
|
||||||
account.seed.warn.noPw.yes=Да и не спрашивать снова
|
account.seed.warn.noPw.yes=Да и не спрашивать снова
|
||||||
account.seed.enterPw=Введите пароль, чтобы увидеть мнемоническую фразу
|
account.seed.enterPw=Введите пароль, чтобы увидеть мнемоническую фразу
|
||||||
|
|
|
@ -242,7 +242,6 @@ mainView.marketPriceWithProvider.label=ราคาตลาดโดย {0}
|
||||||
mainView.marketPrice.bisqInternalPrice=ราคาของการซื้อขาย Bisq ล่าสุด
|
mainView.marketPrice.bisqInternalPrice=ราคาของการซื้อขาย Bisq ล่าสุด
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=ไม่มีราคาตลาดจากผู้ให้บริการด้านราคาภายนอก\nราคาที่แสดงเป็นราคาล่าสุดของ Bisq สำหรับสกุลเงินนั้น
|
mainView.marketPrice.tooltip.bisqInternalPrice=ไม่มีราคาตลาดจากผู้ให้บริการด้านราคาภายนอก\nราคาที่แสดงเป็นราคาล่าสุดของ Bisq สำหรับสกุลเงินนั้น
|
||||||
mainView.marketPrice.tooltip=ราคาตลาดจัดทำโดย {0} {1} \nอัปเดตล่าสุด: {2} \nnode URL ของผู้ให้บริการ: {3}
|
mainView.marketPrice.tooltip=ราคาตลาดจัดทำโดย {0} {1} \nอัปเดตล่าสุด: {2} \nnode URL ของผู้ให้บริการ: {3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=หาก altcoin ไม่สามารถใช้งานได้ที่ Poloniex ทางเราใช้ https://coinmarketcap.com
|
|
||||||
mainView.balance.available=ยอดคงเหลือที่พร้อมใช้งาน
|
mainView.balance.available=ยอดคงเหลือที่พร้อมใช้งาน
|
||||||
mainView.balance.reserved=ข้อเสนอได้รับการจองแล้ว
|
mainView.balance.reserved=ข้อเสนอได้รับการจองแล้ว
|
||||||
mainView.balance.locked=ล็อคในการซื้อขาย
|
mainView.balance.locked=ล็อคในการซื้อขาย
|
||||||
|
@ -1015,7 +1014,6 @@ setting.about.providers=ผู้ให้บริการข้อมูล
|
||||||
setting.about.apisWithFee=Bisq ใช้ APIs ของบุคคลที่ 3 สำหรับราคาตลาดของ Fiat และ Altcoin ตลอดจนการประมาณค่าการขุด
|
setting.about.apisWithFee=Bisq ใช้ APIs ของบุคคลที่ 3 สำหรับราคาตลาดของ Fiat และ Altcoin ตลอดจนการประมาณค่าการขุด
|
||||||
setting.about.apis=Bisq ใช้ APIs ของบุคคลที่ 3 สำหรับ Fiat และ Altcoin ในราคาตลาด
|
setting.about.apis=Bisq ใช้ APIs ของบุคคลที่ 3 สำหรับ Fiat และ Altcoin ในราคาตลาด
|
||||||
setting.about.pricesProvided=ราคาตลาดจัดโดย
|
setting.about.pricesProvided=ราคาตลาดจัดโดย
|
||||||
setting.about.pricesProviders={0}, {1} และ {2}
|
|
||||||
setting.about.feeEstimation.label=การประมาณค่าธรรมเนียมการขุดโดย
|
setting.about.feeEstimation.label=การประมาณค่าธรรมเนียมการขุดโดย
|
||||||
setting.about.versionDetails=รายละเอียดของเวอร์ชั่น
|
setting.about.versionDetails=รายละเอียดของเวอร์ชั่น
|
||||||
setting.about.version=เวอร์ชั่นของแอปพลิเคชั่น
|
setting.about.version=เวอร์ชั่นของแอปพลิเคชั่น
|
||||||
|
@ -1153,6 +1151,7 @@ account.password.info=ด้วยระบบป้องกันรหัส
|
||||||
|
|
||||||
account.seed.backup.title=สำรองข้อมูล wallet โค้ดของคุณ
|
account.seed.backup.title=สำรองข้อมูล wallet โค้ดของคุณ
|
||||||
account.seed.info=โปรดเขียนรหัสสำรองข้อมูล wallet และวันที่! คุณสามารถกู้ข้อมูล wallet ของคุณได้ทุกเมื่อด้วย รหัสสำรองข้อมูล wallet และวันที่\nรหัสสำรองข้อมูล ใช้ทั้ง BTC และ BSQ wallet\n\nคุณควรเขียนรหัสสำรองข้อมูล wallet ลงบนแผ่นกระดาษและไม่บันทึกไว้ในคอมพิวเตอร์ของคุณ\n\nโปรดทราบว่า รหัสสำรองข้อมูล wallet ไม่ได้แทนการสำรองข้อมูล\nคุณจำเป็นต้องสำรองข้อมูลสารบบแอ็พพลิเคชั่นทั้งหมดที่หน้าจอ \"บัญชี / การสำรองข้อมูล \" เพื่อกู้คืนสถานะแอ็พพลิเคชั่นและข้อมูลที่ถูกต้อง\nการนำเข้ารหัสสำรองข้อมูล wallet เป็นคำแนะนำเฉพาะสำหรับกรณีฉุกเฉินเท่านั้น แอพพลิเคชั่นจะไม่สามารถใช้งานได้หากไม่มีไฟล์สำรองฐานข้อมูลและคีย์ที่ถูกต้อง!
|
account.seed.info=โปรดเขียนรหัสสำรองข้อมูล wallet และวันที่! คุณสามารถกู้ข้อมูล wallet ของคุณได้ทุกเมื่อด้วย รหัสสำรองข้อมูล wallet และวันที่\nรหัสสำรองข้อมูล ใช้ทั้ง BTC และ BSQ wallet\n\nคุณควรเขียนรหัสสำรองข้อมูล wallet ลงบนแผ่นกระดาษและไม่บันทึกไว้ในคอมพิวเตอร์ของคุณ\n\nโปรดทราบว่า รหัสสำรองข้อมูล wallet ไม่ได้แทนการสำรองข้อมูล\nคุณจำเป็นต้องสำรองข้อมูลสารบบแอ็พพลิเคชั่นทั้งหมดที่หน้าจอ \"บัญชี / การสำรองข้อมูล \" เพื่อกู้คืนสถานะแอ็พพลิเคชั่นและข้อมูลที่ถูกต้อง\nการนำเข้ารหัสสำรองข้อมูล wallet เป็นคำแนะนำเฉพาะสำหรับกรณีฉุกเฉินเท่านั้น แอพพลิเคชั่นจะไม่สามารถใช้งานได้หากไม่มีไฟล์สำรองฐานข้อมูลและคีย์ที่ถูกต้อง!
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=คุณยังไม่ได้ตั้งรหัสผ่าน wallet ซึ่งจะช่วยป้องกันการแสดงผลของรหัสสำรองข้อมูล wallet \n\nคุณต้องการแสดงรหัสสำรองข้อมูล wallet หรือไม่
|
account.seed.warn.noPw.msg=คุณยังไม่ได้ตั้งรหัสผ่าน wallet ซึ่งจะช่วยป้องกันการแสดงผลของรหัสสำรองข้อมูล wallet \n\nคุณต้องการแสดงรหัสสำรองข้อมูล wallet หรือไม่
|
||||||
account.seed.warn.noPw.yes=ใช่ และไม่ต้องถามฉันอีก
|
account.seed.warn.noPw.yes=ใช่ และไม่ต้องถามฉันอีก
|
||||||
account.seed.enterPw=ป้อนรหัสผ่านเพื่อดูรหัสสำรองข้อมูล wallet
|
account.seed.enterPw=ป้อนรหัสผ่านเพื่อดูรหัสสำรองข้อมูล wallet
|
||||||
|
|
|
@ -242,7 +242,6 @@ mainView.marketPriceWithProvider.label=Giá thị trường theo {0}
|
||||||
mainView.marketPrice.bisqInternalPrice=Giá giao dịch Bisq gần nhất
|
mainView.marketPrice.bisqInternalPrice=Giá giao dịch Bisq gần nhất
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=Không có giá thị trường từ nhà cung cấp bên ngoài.\nGiá hiển thị là giá giao dịch Bisq gần nhất với đồng tiền này.
|
mainView.marketPrice.tooltip.bisqInternalPrice=Không có giá thị trường từ nhà cung cấp bên ngoài.\nGiá hiển thị là giá giao dịch Bisq gần nhất với đồng tiền này.
|
||||||
mainView.marketPrice.tooltip=Giá thị trường được cung cấp bởi {0}{1}\nCập nhật mới nhất: {2}\nURL nút nhà cung cấp: {3}
|
mainView.marketPrice.tooltip=Giá thị trường được cung cấp bởi {0}{1}\nCập nhật mới nhất: {2}\nURL nút nhà cung cấp: {3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=Nếu altcoin không có trên Poloniex, sử dụng https://coinmarketcap.com
|
|
||||||
mainView.balance.available=Số dư hiện có
|
mainView.balance.available=Số dư hiện có
|
||||||
mainView.balance.reserved=Phần được bảo lưu trong báo giá
|
mainView.balance.reserved=Phần được bảo lưu trong báo giá
|
||||||
mainView.balance.locked=Khóa trong giao dịch
|
mainView.balance.locked=Khóa trong giao dịch
|
||||||
|
@ -1015,7 +1014,6 @@ setting.about.providers=Nhà cung cấp dữ liệu
|
||||||
setting.about.apisWithFee=Bisq sử dụng API bên thứ 3 để ước tính giá thị trường Fiat và Altcoin cũng như phí đào.
|
setting.about.apisWithFee=Bisq sử dụng API bên thứ 3 để ước tính giá thị trường Fiat và Altcoin cũng như phí đào.
|
||||||
setting.about.apis=Bisq sử dụng API bên thứ 3 để ước tính giá thị trường Fiat và Altcoin.
|
setting.about.apis=Bisq sử dụng API bên thứ 3 để ước tính giá thị trường Fiat và Altcoin.
|
||||||
setting.about.pricesProvided=Giá thị trường cung cấp bởi
|
setting.about.pricesProvided=Giá thị trường cung cấp bởi
|
||||||
setting.about.pricesProviders={0}, {1} và {2}
|
|
||||||
setting.about.feeEstimation.label=Ước tính phí đào cung cấp bởi
|
setting.about.feeEstimation.label=Ước tính phí đào cung cấp bởi
|
||||||
setting.about.versionDetails=Thông tin về phiên bản
|
setting.about.versionDetails=Thông tin về phiên bản
|
||||||
setting.about.version=Phiên bản ứng dụng
|
setting.about.version=Phiên bản ứng dụng
|
||||||
|
@ -1153,6 +1151,7 @@ account.password.info=Bằng cách bảo vệ với mật khẩu, bạn cần nh
|
||||||
|
|
||||||
account.seed.backup.title=Sao lưu dự phòng từ khởi tạo ví của bạn
|
account.seed.backup.title=Sao lưu dự phòng từ khởi tạo ví của bạn
|
||||||
account.seed.info=Hãy viết ra từ khởi tạo ví của bạn và ngày! Bạn có thể khôi phục ví của bạn bất cứ lúc nào với các từ khởi tạo và ngày này.\nTừ khởi tạo được sử dụng chung cho cả ví BTC và BSQ.\n\nBạn nên viết các từ khởi tạo ra tờ giấy. Không được lưu trên máy tính.\n\nLưu ý rằng từ khởi tạo KHÔNG PHẢI là phương án thay thế cho sao lưu dự phòng.\nBạn cần sao lưu dự phòng toàn bộ thư mục của ứng dụng tại màn hình \"Tài khoản/Sao lưu dự phòng\" để khôi phục trạng thái và dữ liệu ứng dụng.\nNhập từ khởi tạo chỉ được thực hiện trong tình huống khẩn cấp. Ứng dụng sẽ không hoạt động mà không có dự phòng các file dữ liệu và khóa phù hợp!
|
account.seed.info=Hãy viết ra từ khởi tạo ví của bạn và ngày! Bạn có thể khôi phục ví của bạn bất cứ lúc nào với các từ khởi tạo và ngày này.\nTừ khởi tạo được sử dụng chung cho cả ví BTC và BSQ.\n\nBạn nên viết các từ khởi tạo ra tờ giấy. Không được lưu trên máy tính.\n\nLưu ý rằng từ khởi tạo KHÔNG PHẢI là phương án thay thế cho sao lưu dự phòng.\nBạn cần sao lưu dự phòng toàn bộ thư mục của ứng dụng tại màn hình \"Tài khoản/Sao lưu dự phòng\" để khôi phục trạng thái và dữ liệu ứng dụng.\nNhập từ khởi tạo chỉ được thực hiện trong tình huống khẩn cấp. Ứng dụng sẽ không hoạt động mà không có dự phòng các file dữ liệu và khóa phù hợp!
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=Bạn đã tạo mật khẩu ví để bảo vệ tránh hiển thị Seed words.\n\nBạn có muốn hiển thị Seed words?
|
account.seed.warn.noPw.msg=Bạn đã tạo mật khẩu ví để bảo vệ tránh hiển thị Seed words.\n\nBạn có muốn hiển thị Seed words?
|
||||||
account.seed.warn.noPw.yes=Có và không hỏi lại
|
account.seed.warn.noPw.yes=Có và không hỏi lại
|
||||||
account.seed.enterPw=Nhập mật khẩu để xem seed words
|
account.seed.enterPw=Nhập mật khẩu để xem seed words
|
||||||
|
|
|
@ -242,7 +242,6 @@ mainView.marketPriceWithProvider.label=交易所价格提供商:{0}
|
||||||
mainView.marketPrice.bisqInternalPrice=最新 Bisq 交易的价格
|
mainView.marketPrice.bisqInternalPrice=最新 Bisq 交易的价格
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=外部交易所供应商没有可用的市场价格。\n显示的价格是该货币的最新 Bisq 交易价格。
|
mainView.marketPrice.tooltip.bisqInternalPrice=外部交易所供应商没有可用的市场价格。\n显示的价格是该货币的最新 Bisq 交易价格。
|
||||||
mainView.marketPrice.tooltip=交易所价格提供者 {0}{1}\n最后更新:{2}\n提供者节点 URL:{3}
|
mainView.marketPrice.tooltip=交易所价格提供者 {0}{1}\n最后更新:{2}\n提供者节点 URL:{3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=如果数字货币在 Poloniex 不可用,我们使用 https://coinmarketcap.com
|
|
||||||
mainView.balance.available=可用余额
|
mainView.balance.available=可用余额
|
||||||
mainView.balance.reserved=保证金
|
mainView.balance.reserved=保证金
|
||||||
mainView.balance.locked=冻结余额
|
mainView.balance.locked=冻结余额
|
||||||
|
@ -1015,7 +1014,6 @@ setting.about.providers=数据提供商
|
||||||
setting.about.apisWithFee=Bisq 使用第三方 API 获取法定货币与虚拟币的市场价以及矿工手续费的估价。
|
setting.about.apisWithFee=Bisq 使用第三方 API 获取法定货币与虚拟币的市场价以及矿工手续费的估价。
|
||||||
setting.about.apis=Bisq 使用第三方 API 获取法定货币与虚拟币的市场价。
|
setting.about.apis=Bisq 使用第三方 API 获取法定货币与虚拟币的市场价。
|
||||||
setting.about.pricesProvided=交易所价格提供商
|
setting.about.pricesProvided=交易所价格提供商
|
||||||
setting.about.pricesProviders={0}、{1} 和 {2}
|
|
||||||
setting.about.feeEstimation.label=矿工手续费估算提供商
|
setting.about.feeEstimation.label=矿工手续费估算提供商
|
||||||
setting.about.versionDetails=版本详情
|
setting.about.versionDetails=版本详情
|
||||||
setting.about.version=应用程序版本
|
setting.about.version=应用程序版本
|
||||||
|
@ -1153,6 +1151,7 @@ account.password.info=使用密码保护,您需要在将比特币从钱包中
|
||||||
|
|
||||||
account.seed.backup.title=备份您的钱包还原密钥
|
account.seed.backup.title=备份您的钱包还原密钥
|
||||||
account.seed.info=请写下钱包还原密钥和时间!\n您可以通过还原密钥和时间在任何时候恢复您的钱包。\n还原密钥用于 BTC 和 BSQ 钱包。\n\n您应该在一张纸上写下还原密钥并且不要保存它们在您的电脑上。\n请注意还原密钥并不能代替备份。\n您需要备份完整的应用程序目录在”账户/备份“界面去恢复有效的应用程序状态和数据。
|
account.seed.info=请写下钱包还原密钥和时间!\n您可以通过还原密钥和时间在任何时候恢复您的钱包。\n还原密钥用于 BTC 和 BSQ 钱包。\n\n您应该在一张纸上写下还原密钥并且不要保存它们在您的电脑上。\n请注意还原密钥并不能代替备份。\n您需要备份完整的应用程序目录在”账户/备份“界面去恢复有效的应用程序状态和数据。
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=您还没有设置一个可以保护还原密钥显示的钱包密码。\n\n要显示还原密钥吗?
|
account.seed.warn.noPw.msg=您还没有设置一个可以保护还原密钥显示的钱包密码。\n\n要显示还原密钥吗?
|
||||||
account.seed.warn.noPw.yes=是的,不要再问我
|
account.seed.warn.noPw.yes=是的,不要再问我
|
||||||
account.seed.enterPw=输入密码查看还原密钥
|
account.seed.enterPw=输入密码查看还原密钥
|
||||||
|
|
|
@ -242,7 +242,6 @@ mainView.marketPriceWithProvider.label=交易所價格提供商:{0}
|
||||||
mainView.marketPrice.bisqInternalPrice=最新 Bisq 交易的價格
|
mainView.marketPrice.bisqInternalPrice=最新 Bisq 交易的價格
|
||||||
mainView.marketPrice.tooltip.bisqInternalPrice=外部交易所供應商沒有可用的市場價格。\n顯示的價格是該貨幣的最新 Bisq 交易價格。
|
mainView.marketPrice.tooltip.bisqInternalPrice=外部交易所供應商沒有可用的市場價格。\n顯示的價格是該貨幣的最新 Bisq 交易價格。
|
||||||
mainView.marketPrice.tooltip=交易所價格提供者 {0}{1}\n最後更新:{2}\n提供者節點 URL:{3}
|
mainView.marketPrice.tooltip=交易所價格提供者 {0}{1}\n最後更新:{2}\n提供者節點 URL:{3}
|
||||||
mainView.marketPrice.tooltip.altcoinExtra=如果數字貨幣在 Poloniex 不可用,我們使用 https://coinmarketcap.com
|
|
||||||
mainView.balance.available=可用餘額
|
mainView.balance.available=可用餘額
|
||||||
mainView.balance.reserved=保證金
|
mainView.balance.reserved=保證金
|
||||||
mainView.balance.locked=凍結餘額
|
mainView.balance.locked=凍結餘額
|
||||||
|
@ -1015,7 +1014,6 @@ setting.about.providers=資料提供商
|
||||||
setting.about.apisWithFee=Bisq 使用第三方 API 獲取法定貨幣與虛擬幣的市場價以及礦工手續費的估價。
|
setting.about.apisWithFee=Bisq 使用第三方 API 獲取法定貨幣與虛擬幣的市場價以及礦工手續費的估價。
|
||||||
setting.about.apis=Bisq 使用第三方 API 獲取法定貨幣與虛擬幣的市場價。
|
setting.about.apis=Bisq 使用第三方 API 獲取法定貨幣與虛擬幣的市場價。
|
||||||
setting.about.pricesProvided=交易所價格提供商
|
setting.about.pricesProvided=交易所價格提供商
|
||||||
setting.about.pricesProviders={0}、{1} 和 {2}
|
|
||||||
setting.about.feeEstimation.label=礦工手續費估算提供商
|
setting.about.feeEstimation.label=礦工手續費估算提供商
|
||||||
setting.about.versionDetails=版本詳情
|
setting.about.versionDetails=版本詳情
|
||||||
setting.about.version=應用程式版本
|
setting.about.version=應用程式版本
|
||||||
|
@ -1153,6 +1151,7 @@ account.password.info=使用密碼保護,您需要在將比特幣從錢包中
|
||||||
|
|
||||||
account.seed.backup.title=備份您的錢包還原金鑰
|
account.seed.backup.title=備份您的錢包還原金鑰
|
||||||
account.seed.info=請寫下錢包還原金鑰和時間!\n您可以通過還原金鑰和時間在任何時候恢復您的錢包。\n還原金鑰用於 BTC 和 BSQ 錢包。\n\n您應該在一張紙上寫下還原金鑰並且不要儲存它們在您的電腦上。\n請注意還原金鑰並不能代替備份。\n您需要備份完整的應用程式目錄在”賬戶/備份“介面去恢復有效的應用程式狀態和資料。
|
account.seed.info=請寫下錢包還原金鑰和時間!\n您可以通過還原金鑰和時間在任何時候恢復您的錢包。\n還原金鑰用於 BTC 和 BSQ 錢包。\n\n您應該在一張紙上寫下還原金鑰並且不要儲存它們在您的電腦上。\n請注意還原金鑰並不能代替備份。\n您需要備份完整的應用程式目錄在”賬戶/備份“介面去恢復有效的應用程式狀態和資料。
|
||||||
|
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page https://bisq.wiki/Backing_up_application_data for extended info.
|
||||||
account.seed.warn.noPw.msg=您還沒有設定一個可以保護還原金鑰顯示的錢包密碼。\n\n要顯示還原金鑰嗎?
|
account.seed.warn.noPw.msg=您還沒有設定一個可以保護還原金鑰顯示的錢包密碼。\n\n要顯示還原金鑰嗎?
|
||||||
account.seed.warn.noPw.yes=是的,不要再問我
|
account.seed.warn.noPw.yes=是的,不要再問我
|
||||||
account.seed.enterPw=輸入密碼檢視還原金鑰
|
account.seed.enterPw=輸入密碼檢視還原金鑰
|
||||||
|
|
Binary file not shown.
|
@ -1,6 +1,6 @@
|
||||||
TXT CHECKPOINTS 1
|
TXT CHECKPOINTS 1
|
||||||
0
|
0
|
||||||
315
|
317
|
||||||
AAAAAAAAB+EH4QfhAAAH4AEAAABjl7tqvU/FIcDT9gcbVlA4nwtFUbxAtOawZzBpAAAAAKzkcK7NqciBjI/ldojNKncrWleVSgDfBCCn3VRrbSxXaw5/Sf//AB0z8Bkv
|
AAAAAAAAB+EH4QfhAAAH4AEAAABjl7tqvU/FIcDT9gcbVlA4nwtFUbxAtOawZzBpAAAAAKzkcK7NqciBjI/ldojNKncrWleVSgDfBCCn3VRrbSxXaw5/Sf//AB0z8Bkv
|
||||||
AAAAAAAAD8EPwQ/BAAAPwAEAAADfP83Sx8MZ9RsrnZCvqzAwqB2Ma+ZesNAJrTfwAAAAACwESaNKhvRgz6WuE7UFdFk1xwzfRY/OIdIOPzX5yaAdjnWUSf//AB0GrNq5
|
AAAAAAAAD8EPwQ/BAAAPwAEAAADfP83Sx8MZ9RsrnZCvqzAwqB2Ma+ZesNAJrTfwAAAAACwESaNKhvRgz6WuE7UFdFk1xwzfRY/OIdIOPzX5yaAdjnWUSf//AB0GrNq5
|
||||||
AAAAAAAAF6EXoRehAAAXoAEAAADonWzAaUAKd30XT3NnHKobZMnLOuHdzm/xtehsAAAAAD8cUJA6NBIHHcqPHLc4IrfHw+6mjCGu3e+wRO81EvpnMVqrSf//AB1ffy8G
|
AAAAAAAAF6EXoRehAAAXoAEAAADonWzAaUAKd30XT3NnHKobZMnLOuHdzm/xtehsAAAAAD8cUJA6NBIHHcqPHLc4IrfHw+6mjCGu3e+wRO81EvpnMVqrSf//AB1ffy8G
|
||||||
|
@ -316,3 +316,5 @@ DyhWrFlPqEc/c9+uAAmZAAAgACDcrvMMuRSy9GQxh8BFNt6wIDWYhvU4EQAAAAAAAAAAABVOP5QKoWx9
|
||||||
D5uvExILinkBRJMsAAmg4ADg/yfO2pclcPlRE5XVKCPZeF63mn30v+yHDgAAAAAAAAAAAPaDA4g+h9tjjVWtJ587WMHb07Mcfa1BoDJCANQd+z94wJDEXvaXEhf5l8cN
|
D5uvExILinkBRJMsAAmg4ADg/yfO2pclcPlRE5XVKCPZeF63mn30v+yHDgAAAAAAAAAAAPaDA4g+h9tjjVWtJ587WMHb07Mcfa1BoDJCANQd+z94wJDEXvaXEhf5l8cN
|
||||||
EAgacoNqeblmwryDAAmowAAAACBrw4jKPLZgZpVWF50edwCXFDSCydrEBwAAAAAAAAAAAJzD7FqR8uJdE2JDv+AVjncVcclu70I3QzG/aH2H/bHffOnYXjV/FBdyiZsc
|
EAgacoNqeblmwryDAAmowAAAACBrw4jKPLZgZpVWF50edwCXFDSCydrEBwAAAAAAAAAAAJzD7FqR8uJdE2JDv+AVjncVcclu70I3QzG/aH2H/bHffOnYXjV/FBdyiZsc
|
||||||
EGp3iH6JaGGCuz8LAAmwoAAAQCAgBboCfncZ/s4WkKcsLjW+V4vlPQoDAwAAAAAAAAAAALs0nxXJbP7VZ+Fc3dhRpL6RN6QfYphMw8iCNdqEKGL6e/joXvLUERcyQVAj
|
EGp3iH6JaGGCuz8LAAmwoAAAQCAgBboCfncZ/s4WkKcsLjW+V4vlPQoDAwAAAAAAAAAAALs0nxXJbP7VZ+Fc3dhRpL6RN6QfYphMw8iCNdqEKGL6e/joXvLUERcyQVAj
|
||||||
|
ENuF9IkxgIodMy/3AAm4gAAAACCTOuNmt+7x2vxFMkHRbDxRElZiABP9DAAAAAAAAAAAAAERAwV0KNK6qPCLxwDpQCkrbHi2jFc/Kldv0N5pJMkE+XP7XhnVERet5rYy
|
||||||
|
EUyU1REypM3OngUzAAnAYAAAgCB898coFK4fvpBHu54gmeu1eEuspbAwCAAAAAAAAAAAAHQ93L5j0nbFj5FI6biHU0sUuxN+qzGCaK4mc2P3nQYZpEoMXxU6EBeWCMCJ
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
# pull base image
|
# pull base image
|
||||||
FROM openjdk:8-jdk
|
FROM openjdk:8-jdk
|
||||||
ENV version 1.3.6-SNAPSHOT
|
ENV version 1.3.7
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends openjfx && rm -rf /var/lib/apt/lists/* &&
|
RUN apt-get update && apt-get install -y --no-install-recommends openjfx && rm -rf /var/lib/apt/lists/* &&
|
||||||
apt-get install -y vim fakeroot
|
apt-get install -y vim fakeroot
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# - Update version below
|
# - Update version below
|
||||||
# - Ensure JAVA_HOME below is pointing to OracleJDK 10 directory
|
# - Ensure JAVA_HOME below is pointing to OracleJDK 10 directory
|
||||||
|
|
||||||
version=1.3.6-SNAPSHOT
|
version=1.3.7
|
||||||
version_base=$(echo $version | awk -F'[_-]' '{print $1}')
|
version_base=$(echo $version | awk -F'[_-]' '{print $1}')
|
||||||
if [ ! -f "$JAVA_HOME/bin/javapackager" ]; then
|
if [ ! -f "$JAVA_HOME/bin/javapackager" ]; then
|
||||||
if [ -d "/usr/lib/jvm/jdk-10.0.2" ]; then
|
if [ -d "/usr/lib/jvm/jdk-10.0.2" ]; then
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
# Prior to running this script:
|
# Prior to running this script:
|
||||||
# - Update version below
|
# - Update version below
|
||||||
|
|
||||||
version=1.3.6-SNAPSHOT
|
version=1.3.7
|
||||||
base_dir=$( cd "$(dirname "$0")" ; pwd -P )/../../..
|
base_dir=$( cd "$(dirname "$0")" ; pwd -P )/../../..
|
||||||
package_dir=$base_dir/desktop/package
|
package_dir=$base_dir/desktop/package
|
||||||
release_dir=$base_dir/desktop/release/$version
|
release_dir=$base_dir/desktop/release/$version
|
||||||
|
|
|
@ -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.3.6</string>
|
<string>1.3.7</string>
|
||||||
|
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.3.6</string>
|
<string>1.3.7</string>
|
||||||
|
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>Bisq</string>
|
<string>Bisq</string>
|
||||||
|
|
|
@ -6,7 +6,7 @@ mkdir -p deploy
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
version="1.3.6-SNAPSHOT"
|
version="1.3.7"
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
./gradlew :desktop:build -x test shadowJar
|
./gradlew :desktop:build -x test shadowJar
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
cd ../../
|
cd ../../
|
||||||
|
|
||||||
version="1.3.6-SNAPSHOT"
|
version="1.3.7"
|
||||||
|
|
||||||
target_dir="releases/$version"
|
target_dir="releases/$version"
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
cd $(dirname $0)/../../../
|
cd $(dirname $0)/../../../
|
||||||
|
|
||||||
oldVersion=1.3.5
|
oldVersion=1.3.6
|
||||||
newVersion=1.3.6
|
newVersion=1.3.7
|
||||||
|
|
||||||
find . -type f \( -name "finalize.sh" \
|
find . -type f \( -name "finalize.sh" \
|
||||||
-o -name "create_app.sh" \
|
-o -name "create_app.sh" \
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
set version=1.3.6-SNAPSHOT
|
set version=1.3.7
|
||||||
if not exist "%JAVA_HOME%\bin\javapackager.exe" (
|
if not exist "%JAVA_HOME%\bin\javapackager.exe" (
|
||||||
if not exist "%ProgramFiles%\Java\jdk-10.0.2" (
|
if not exist "%ProgramFiles%\Java\jdk-10.0.2" (
|
||||||
echo Javapackager not found. Update JAVA_HOME variable to point to OracleJDK.
|
echo Javapackager not found. Update JAVA_HOME variable to point to OracleJDK.
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
set version=1.3.6-SNAPSHOT
|
set version=1.3.7
|
||||||
set release_dir=%~dp0..\..\..\releases\%version%
|
set release_dir=%~dp0..\..\..\releases\%version%
|
||||||
set package_dir=%~dp0..
|
set package_dir=%~dp0..
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ import bisq.common.BisqException;
|
||||||
import bisq.core.locale.GlobalSettings;
|
import bisq.core.locale.GlobalSettings;
|
||||||
import bisq.core.locale.LanguageUtil;
|
import bisq.core.locale.LanguageUtil;
|
||||||
import bisq.core.locale.Res;
|
import bisq.core.locale.Res;
|
||||||
|
import bisq.core.provider.price.MarketPrice;
|
||||||
|
|
||||||
import bisq.common.Timer;
|
import bisq.common.Timer;
|
||||||
import bisq.common.UserThread;
|
import bisq.common.UserThread;
|
||||||
|
@ -95,6 +96,7 @@ import javafx.beans.value.ChangeListener;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
@ -325,8 +327,9 @@ public class MainView extends InitializableView<StackPane, MainViewModel>
|
||||||
primaryNav.getStyleClass().add("nav-primary");
|
primaryNav.getStyleClass().add("nav-primary");
|
||||||
HBox.setHgrow(primaryNav, Priority.SOMETIMES);
|
HBox.setHgrow(primaryNav, Priority.SOMETIMES);
|
||||||
|
|
||||||
HBox secondaryNav = new HBox(supportButtonWithBadge, getNavigationSpacer(), settingsButtonWithBadge,
|
HBox secondaryNav = new HBox(supportButtonWithBadge, getNavigationSeparator(),
|
||||||
getNavigationSpacer(), accountButtonWithBadge, getNavigationSpacer(), daoButtonWithBadge);
|
settingsButtonWithBadge, getNavigationSeparator(), accountButtonWithBadge,
|
||||||
|
getNavigationSeparator(), daoButtonWithBadge);
|
||||||
secondaryNav.getStyleClass().add("nav-secondary");
|
secondaryNav.getStyleClass().add("nav-secondary");
|
||||||
HBox.setHgrow(secondaryNav, Priority.SOMETIMES);
|
HBox.setHgrow(secondaryNav, Priority.SOMETIMES);
|
||||||
|
|
||||||
|
@ -515,7 +518,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel>
|
||||||
private void updateMarketPriceLabel(Label label) {
|
private void updateMarketPriceLabel(Label label) {
|
||||||
if (model.getIsPriceAvailable().get()) {
|
if (model.getIsPriceAvailable().get()) {
|
||||||
if (model.getIsExternallyProvidedPrice().get()) {
|
if (model.getIsExternallyProvidedPrice().get()) {
|
||||||
label.setText(Res.get("mainView.marketPriceWithProvider.label", getPriceProvider()));
|
label.setText(Res.get("mainView.marketPriceWithProvider.label", "Bisq Price Index"));
|
||||||
label.setTooltip(new Tooltip(getPriceProviderTooltipString()));
|
label.setTooltip(new Tooltip(getPriceProviderTooltipString()));
|
||||||
} else {
|
} else {
|
||||||
label.setText(Res.get("mainView.marketPrice.bisqInternalPrice"));
|
label.setText(Res.get("mainView.marketPrice.bisqInternalPrice"));
|
||||||
|
@ -531,22 +534,14 @@ public class MainView extends InitializableView<StackPane, MainViewModel>
|
||||||
@NotNull
|
@NotNull
|
||||||
private String getPriceProviderTooltipString() {
|
private String getPriceProviderTooltipString() {
|
||||||
|
|
||||||
String res;
|
String selectedCurrencyCode = model.getPriceFeedService().getCurrencyCode();
|
||||||
if (model.getIsFiatCurrencyPriceFeedSelected().get()) {
|
MarketPrice selectedMarketPrice = model.getPriceFeedService().getMarketPrice(selectedCurrencyCode);
|
||||||
res = Res.get("mainView.marketPrice.tooltip",
|
|
||||||
"https://bitcoinaverage.com",
|
return Res.get("mainView.marketPrice.tooltip",
|
||||||
|
"Bisq Price Index for " + selectedCurrencyCode,
|
||||||
"",
|
"",
|
||||||
DisplayUtils.formatTime(model.getPriceFeedService().getLastRequestTimeStampBtcAverage()),
|
DisplayUtils.formatTime(new Date(selectedMarketPrice.getTimestampSec())),
|
||||||
model.getPriceFeedService().getProviderNodeAddress());
|
model.getPriceFeedService().getProviderNodeAddress());
|
||||||
} else {
|
|
||||||
String altcoinExtra = "\n" + Res.get("mainView.marketPrice.tooltip.altcoinExtra");
|
|
||||||
res = Res.get("mainView.marketPrice.tooltip",
|
|
||||||
"https://poloniex.com",
|
|
||||||
altcoinExtra,
|
|
||||||
DisplayUtils.formatTime(model.getPriceFeedService().getLastRequestTimeStampPoloniex()),
|
|
||||||
model.getPriceFeedService().getProviderNodeAddress());
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private VBox createSplashScreen() {
|
private VBox createSplashScreen() {
|
||||||
|
|
|
@ -163,7 +163,23 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
||||||
seedWordsTextArea.getStyleClass().remove("validation-error");
|
seedWordsTextArea.getStyleClass().remove("validation-error");
|
||||||
restoreDatePicker.getStyleClass().remove("validation-error");
|
restoreDatePicker.getStyleClass().remove("validation-error");
|
||||||
|
|
||||||
|
String key = "showBackupWarningAtSeedPhrase";
|
||||||
|
if (DontShowAgainLookup.showAgain(key)) {
|
||||||
|
new Popup().warning(Res.get("account.seed.backup.warning"))
|
||||||
|
.onAction(() -> {
|
||||||
|
showSeedPhrase();
|
||||||
|
})
|
||||||
|
.actionButtonText(Res.get("shared.iUnderstand"))
|
||||||
|
.useIUnderstandButton()
|
||||||
|
.dontShowAgainId(key)
|
||||||
|
.hideCloseButton()
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
showSeedPhrase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showSeedPhrase() {
|
||||||
DeterministicSeed keyChainSeed = btcWalletService.getKeyChainSeed();
|
DeterministicSeed keyChainSeed = btcWalletService.getKeyChainSeed();
|
||||||
// wallet creation date is not encrypted
|
// wallet creation date is not encrypted
|
||||||
walletCreationDate = Instant.ofEpochSecond(walletsManager.getChainSeedCreationTimeSeconds()).atZone(ZoneId.systemDefault()).toLocalDate();
|
walletCreationDate = Instant.ofEpochSecond(walletsManager.getChainSeedCreationTimeSeconds()).atZone(ZoneId.systemDefault()).toLocalDate();
|
||||||
|
|
|
@ -718,8 +718,14 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||||
return totalToPayAsCoin;
|
return totalToPayAsCoin;
|
||||||
}
|
}
|
||||||
|
|
||||||
Coin getBsqBalance() {
|
Coin getUsableBsqBalance() {
|
||||||
return bsqWalletService.getAvailableConfirmedBalance();
|
// we have to keep a minimum amount of BSQ == bitcoin dust limit
|
||||||
|
// otherwise there would be dust violations for change UTXOs
|
||||||
|
// essentially means the minimum usable balance of BSQ is 5.46
|
||||||
|
Coin usableBsqBalance = bsqWalletService.getAvailableConfirmedBalance().subtract(Restrictions.getMinNonDustOutput());
|
||||||
|
if (usableBsqBalance.isNegative())
|
||||||
|
usableBsqBalance = Coin.ZERO;
|
||||||
|
return usableBsqBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMarketPriceAvailable(boolean marketPriceAvailable) {
|
public void setMarketPriceAvailable(boolean marketPriceAvailable) {
|
||||||
|
|
|
@ -373,9 +373,9 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
|
||||||
String message = null;
|
String message = null;
|
||||||
if (makerFee != null) {
|
if (makerFee != null) {
|
||||||
message = Res.get("popup.warning.insufficientBsqFundsForBtcFeePayment",
|
message = Res.get("popup.warning.insufficientBsqFundsForBtcFeePayment",
|
||||||
bsqFormatter.formatCoinWithCode(makerFee.subtract(model.getDataModel().getBsqBalance())));
|
bsqFormatter.formatCoinWithCode(makerFee.subtract(model.getDataModel().getUsableBsqBalance())));
|
||||||
|
|
||||||
} else if (model.getDataModel().getBsqBalance().isZero())
|
} else if (model.getDataModel().getUsableBsqBalance().isZero())
|
||||||
message = Res.get("popup.warning.noBsqFundsForBtcFeePayment");
|
message = Res.get("popup.warning.noBsqFundsForBtcFeePayment");
|
||||||
|
|
||||||
if (message != null)
|
if (message != null)
|
||||||
|
@ -1109,9 +1109,9 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
|
||||||
String missingBsq = null;
|
String missingBsq = null;
|
||||||
if (makerFee != null) {
|
if (makerFee != null) {
|
||||||
missingBsq = Res.get("popup.warning.insufficientBsqFundsForBtcFeePayment",
|
missingBsq = Res.get("popup.warning.insufficientBsqFundsForBtcFeePayment",
|
||||||
bsqFormatter.formatCoinWithCode(makerFee.subtract(model.getDataModel().getBsqBalance())));
|
bsqFormatter.formatCoinWithCode(makerFee.subtract(model.getDataModel().getUsableBsqBalance())));
|
||||||
|
|
||||||
} else if (model.getDataModel().getBsqBalance().isZero()) {
|
} else if (model.getDataModel().getUsableBsqBalance().isZero()) {
|
||||||
missingBsq = Res.get("popup.warning.noBsqFundsForBtcFeePayment");
|
missingBsq = Res.get("popup.warning.noBsqFundsForBtcFeePayment");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1153,6 +1153,10 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
String info;
|
String info;
|
||||||
String timeSinceSigning;
|
String timeSinceSigning;
|
||||||
|
|
||||||
|
boolean needsSigning = PaymentMethod.hasChargebackRisk(
|
||||||
|
item.getOffer().getPaymentMethod(), item.getOffer().getCurrencyCode());
|
||||||
|
|
||||||
|
if (needsSigning) {
|
||||||
if (accountAgeWitnessService.hasSignedWitness(item.getOffer())) {
|
if (accountAgeWitnessService.hasSignedWitness(item.getOffer())) {
|
||||||
AccountAgeWitnessService.SignState signState = accountAgeWitnessService.getSignState(item.getOffer());
|
AccountAgeWitnessService.SignState signState = accountAgeWitnessService.getSignState(item.getOffer());
|
||||||
icon = GUIUtil.getIconForSignState(signState);
|
icon = GUIUtil.getIconForSignState(signState);
|
||||||
|
@ -1163,9 +1167,6 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
timeSinceSigning = Res.get("offerbook.timeSinceSigning.daysSinceSigning",
|
timeSinceSigning = Res.get("offerbook.timeSinceSigning.daysSinceSigning",
|
||||||
daysSinceSigning);
|
daysSinceSigning);
|
||||||
} else {
|
} else {
|
||||||
boolean needsSigning = PaymentMethod.hasChargebackRisk(
|
|
||||||
item.getOffer().getPaymentMethod(), item.getOffer().getCurrencyCode());
|
|
||||||
if (needsSigning) {
|
|
||||||
AccountAgeWitnessService.SignState signState = accountAgeWitnessService.getSignState(item.getOffer());
|
AccountAgeWitnessService.SignState signState = accountAgeWitnessService.getSignState(item.getOffer());
|
||||||
|
|
||||||
icon = GUIUtil.getIconForSignState(signState);
|
icon = GUIUtil.getIconForSignState(signState);
|
||||||
|
@ -1180,12 +1181,13 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
info = Res.get("shared.notSigned");
|
info = Res.get("shared.notSigned");
|
||||||
timeSinceSigning = Res.get("offerbook.timeSinceSigning.notSigned");
|
timeSinceSigning = Res.get("offerbook.timeSinceSigning.notSigned");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
icon = MaterialDesignIcon.INFORMATION_OUTLINE;
|
icon = MaterialDesignIcon.INFORMATION_OUTLINE;
|
||||||
info = Res.get("shared.notSigned.noNeed");
|
info = Res.get("shared.notSigned.noNeed");
|
||||||
timeSinceSigning = Res.get("offerbook.timeSinceSigning.notSigned.noNeed");
|
timeSinceSigning = Res.get("offerbook.timeSinceSigning.notSigned.noNeed");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
InfoAutoTooltipLabel label = new InfoAutoTooltipLabel(timeSinceSigning, icon, ContentDisplay.RIGHT, info);
|
InfoAutoTooltipLabel label = new InfoAutoTooltipLabel(timeSinceSigning, icon, ContentDisplay.RIGHT, info);
|
||||||
setGraphic(label);
|
setGraphic(label);
|
||||||
|
|
|
@ -639,8 +639,14 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
return offer.getSellerSecurityDeposit();
|
return offer.getSellerSecurityDeposit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Coin getBsqBalance() {
|
public Coin getUsableBsqBalance() {
|
||||||
return bsqWalletService.getAvailableConfirmedBalance();
|
// we have to keep a minimum amount of BSQ == bitcoin dust limit
|
||||||
|
// otherwise there would be dust violations for change UTXOs
|
||||||
|
// essentially means the minimum usable balance of BSQ is 5.46
|
||||||
|
Coin usableBsqBalance = bsqWalletService.getAvailableConfirmedBalance().subtract(Restrictions.getMinNonDustOutput());
|
||||||
|
if (usableBsqBalance.isNegative())
|
||||||
|
usableBsqBalance = Coin.ZERO;
|
||||||
|
return usableBsqBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isHalCashAccount() {
|
public boolean isHalCashAccount() {
|
||||||
|
|
|
@ -927,9 +927,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
String missingBsq = null;
|
String missingBsq = null;
|
||||||
if (takerFee != null) {
|
if (takerFee != null) {
|
||||||
missingBsq = Res.get("popup.warning.insufficientBsqFundsForBtcFeePayment",
|
missingBsq = Res.get("popup.warning.insufficientBsqFundsForBtcFeePayment",
|
||||||
bsqFormatter.formatCoinWithCode(takerFee.subtract(model.dataModel.getBsqBalance())));
|
bsqFormatter.formatCoinWithCode(takerFee.subtract(model.dataModel.getUsableBsqBalance())));
|
||||||
|
|
||||||
} else if (model.dataModel.getBsqBalance().isZero()) {
|
} else if (model.dataModel.getUsableBsqBalance().isZero()) {
|
||||||
missingBsq = Res.get("popup.warning.noBsqFundsForBtcFeePayment");
|
missingBsq = Res.get("popup.warning.noBsqFundsForBtcFeePayment");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1225,9 +1225,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||||
String message = null;
|
String message = null;
|
||||||
if (takerFee != null)
|
if (takerFee != null)
|
||||||
message = Res.get("popup.warning.insufficientBsqFundsForBtcFeePayment",
|
message = Res.get("popup.warning.insufficientBsqFundsForBtcFeePayment",
|
||||||
bsqFormatter.formatCoinWithCode(takerFee.subtract(model.dataModel.getBsqBalance())));
|
bsqFormatter.formatCoinWithCode(takerFee.subtract(model.dataModel.getUsableBsqBalance())));
|
||||||
|
|
||||||
else if (model.dataModel.getBsqBalance().isZero())
|
else if (model.dataModel.getUsableBsqBalance().isZero())
|
||||||
message = Res.get("popup.warning.noBsqFundsForBtcFeePayment");
|
message = Res.get("popup.warning.noBsqFundsForBtcFeePayment");
|
||||||
|
|
||||||
if (message != null)
|
if (message != null)
|
||||||
|
|
|
@ -285,29 +285,6 @@ public class ProposalResultsWindow extends TabbedOverlay<ProposalResultsWindow>
|
||||||
});
|
});
|
||||||
votesTableView.getColumns().add(column);
|
votesTableView.getColumns().add(column);
|
||||||
|
|
||||||
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.stakeAndMerit"));
|
|
||||||
column.setSortable(false);
|
|
||||||
column.setMinWidth(100);
|
|
||||||
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
|
|
||||||
column.setCellFactory(
|
|
||||||
new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public TableCell<VoteListItem, VoteListItem> call(
|
|
||||||
TableColumn<VoteListItem, VoteListItem> column) {
|
|
||||||
return new TableCell<>() {
|
|
||||||
@Override
|
|
||||||
public void updateItem(final VoteListItem item, boolean empty) {
|
|
||||||
super.updateItem(item, empty);
|
|
||||||
if (item != null)
|
|
||||||
setText(item.getMeritAndStake());
|
|
||||||
else
|
|
||||||
setText("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
votesTableView.getColumns().add(column);
|
|
||||||
|
|
||||||
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.merit"));
|
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.merit"));
|
||||||
column.setSortable(false);
|
column.setSortable(false);
|
||||||
column.setMinWidth(100);
|
column.setMinWidth(100);
|
||||||
|
@ -354,6 +331,29 @@ public class ProposalResultsWindow extends TabbedOverlay<ProposalResultsWindow>
|
||||||
});
|
});
|
||||||
votesTableView.getColumns().add(column);
|
votesTableView.getColumns().add(column);
|
||||||
|
|
||||||
|
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.stakeAndMerit"));
|
||||||
|
column.setSortable(false);
|
||||||
|
column.setMinWidth(100);
|
||||||
|
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
|
||||||
|
column.setCellFactory(
|
||||||
|
new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public TableCell<VoteListItem, VoteListItem> call(
|
||||||
|
TableColumn<VoteListItem, VoteListItem> column) {
|
||||||
|
return new TableCell<>() {
|
||||||
|
@Override
|
||||||
|
public void updateItem(final VoteListItem item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
if (item != null)
|
||||||
|
setText(item.getMeritAndStake());
|
||||||
|
else
|
||||||
|
setText("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
votesTableView.getColumns().add(column);
|
||||||
|
|
||||||
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.vote"));
|
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.vote"));
|
||||||
column.setSortable(false);
|
column.setSortable(false);
|
||||||
column.setMinWidth(50);
|
column.setMinWidth(50);
|
||||||
|
|
|
@ -78,10 +78,8 @@ public class AboutView extends ActivatableView<GridPane, Void> {
|
||||||
label = addLabel(root, gridRow, Res.get(isBtc ? "setting.about.apisWithFee" : "setting.about.apis"), Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE);
|
label = addLabel(root, gridRow, Res.get(isBtc ? "setting.about.apisWithFee" : "setting.about.apis"), Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE);
|
||||||
label.setWrapText(true);
|
label.setWrapText(true);
|
||||||
GridPane.setHalignment(label, HPos.LEFT);
|
GridPane.setHalignment(label, HPos.LEFT);
|
||||||
addCompactTopLabelTextField(root, ++gridRow, Res.get("setting.about.pricesProvided"), Res.get("setting.about.pricesProviders",
|
addCompactTopLabelTextField(root, ++gridRow, Res.get("setting.about.pricesProvided"),
|
||||||
"BitcoinAverage (https://bitcoinaverage.com)",
|
"Bisq Price Index (https://bisq.wiki/Bisq_Price_Index)");
|
||||||
"Poloniex (https://poloniex.com)",
|
|
||||||
"Coinmarketcap (https://coinmarketcap.com)"));
|
|
||||||
if (isBtc)
|
if (isBtc)
|
||||||
addCompactTopLabelTextField(root, ++gridRow, Res.get("setting.about.feeEstimation.label"), "mempool.space (https://mempool.space)");
|
addCompactTopLabelTextField(root, ++gridRow, Res.get("setting.about.feeEstimation.label"), "mempool.space (https://mempool.space)");
|
||||||
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 7.3 KiB |
BIN
p2p/src/main/resources/AccountAgeWitnessStore_BTC_MAINNET
(Stored with Git LFS)
BIN
p2p/src/main/resources/AccountAgeWitnessStore_BTC_MAINNET
(Stored with Git LFS)
Binary file not shown.
BIN
p2p/src/main/resources/DaoStateStore_BTC_MAINNET
(Stored with Git LFS)
BIN
p2p/src/main/resources/DaoStateStore_BTC_MAINNET
(Stored with Git LFS)
Binary file not shown.
BIN
p2p/src/main/resources/SignedWitnessStore_BTC_MAINNET
(Stored with Git LFS)
BIN
p2p/src/main/resources/SignedWitnessStore_BTC_MAINNET
(Stored with Git LFS)
Binary file not shown.
BIN
p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET
(Stored with Git LFS)
BIN
p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET
(Stored with Git LFS)
Binary file not shown.
|
@ -5,7 +5,6 @@ Run the following commands:
|
||||||
|
|
||||||
heroku create
|
heroku create
|
||||||
heroku buildpacks:add heroku/gradle
|
heroku buildpacks:add heroku/gradle
|
||||||
heroku config:set BITCOIN_AVG_PUBKEY=[your pubkey] BITCOIN_AVG_PRIVKEY=[your privkey]
|
|
||||||
git push heroku master
|
git push heroku master
|
||||||
curl https://your-app-123456.herokuapp.com/getAllMarketPrices
|
curl https://your-app-123456.herokuapp.com/getAllMarketPrices
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ Operating a production pricenode is a valuable service to the Bisq network, and
|
||||||
|
|
||||||
To run a pricenode, you will need:
|
To run a pricenode, you will need:
|
||||||
|
|
||||||
- [BitcoinAverage API keys](https://bitcoinaverage.com/en/plans). Free plans are fine for local development or personal nodes; paid plans should be used for well-known production nodes.
|
|
||||||
- JDK 8 if you want to build and run a node locally.
|
- JDK 8 if you want to build and run a node locally.
|
||||||
- The `tor` binary (e.g. `brew install tor`) if you want to run a hidden service locally.
|
- The `tor` binary (e.g. `brew install tor`) if you want to run a hidden service locally.
|
||||||
|
|
||||||
|
@ -40,21 +39,6 @@ curl -s https://raw.githubusercontent.com/bisq-network/bisq/master/pricenode/ins
|
||||||
|
|
||||||
At the end of the installer script, it should print your Tor onion hostname.
|
At the end of the installer script, it should print your Tor onion hostname.
|
||||||
|
|
||||||
### Setting your BitcoinAverage API keys
|
|
||||||
|
|
||||||
Open `/etc/default/bisq-pricenode.env` in a text editor and look for these lines:
|
|
||||||
```bash
|
|
||||||
BITCOIN_AVG_PUBKEY=foo
|
|
||||||
BITCOIN_AVG_PRIVKEY=bar
|
|
||||||
```
|
|
||||||
|
|
||||||
Add your pubkey and privkey and then reload/restart bisq-pricenode service:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl restart bisq-pricenode
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test
|
### Test
|
||||||
|
|
||||||
To manually test endpoints, run each of the following:
|
To manually test endpoints, run each of the following:
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
BITCOIN_AVG_PUBKEY=foo
|
|
||||||
BITCOIN_AVG_PRIVKEY=bar
|
|
||||||
JAVA_OPTS=""
|
JAVA_OPTS=""
|
||||||
|
|
|
@ -5,7 +5,7 @@ After=network.target
|
||||||
[Service]
|
[Service]
|
||||||
SyslogIdentifier=bisq-pricenode
|
SyslogIdentifier=bisq-pricenode
|
||||||
EnvironmentFile=/etc/default/bisq-pricenode.env
|
EnvironmentFile=/etc/default/bisq-pricenode.env
|
||||||
ExecStart=/bisq/bisq/bisq-pricenode 2 2
|
ExecStart=/bisq/bisq/bisq-pricenode 2
|
||||||
ExecStop=/bin/kill -TERM ${MAINPID}
|
ExecStop=/bin/kill -TERM ${MAINPID}
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
while true
|
while true
|
||||||
do
|
do
|
||||||
echo `date` "(Re)-starting node"
|
echo `date` "(Re)-starting node"
|
||||||
BITCOIN_AVG_PUBKEY=$BTCAVERAGE_PUBKEY BITCOIN_AVG_PRIVKEY=$BTCAVERAGE_PRIVKEY java -jar ./build/libs/bisq-pricenode.jar 2 2
|
java -jar ./build/libs/bisq-pricenode.jar 2 2
|
||||||
echo `date` "node terminated unexpectedly!!"
|
echo `date` "node terminated unexpectedly!!"
|
||||||
sleep 3
|
sleep 3
|
||||||
done
|
done
|
||||||
|
|
|
@ -36,7 +36,7 @@ sudo -H -i -u "${ROOT_USER}" DEBIAN_FRONTEND=noninteractive apt-get upgrade -qq
|
||||||
|
|
||||||
echo "[*] Installing Git LFS"
|
echo "[*] Installing Git LFS"
|
||||||
sudo -H -i -u "${ROOT_USER}" curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash
|
sudo -H -i -u "${ROOT_USER}" curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash
|
||||||
sudo -H -i -u "${ROOT_USER}" apt-get install git-lfs
|
sudo -H -i -u "${ROOT_USER}" apt-get -y install git-lfs
|
||||||
sudo -H -i -u "${ROOT_USER}" git lfs install
|
sudo -H -i -u "${ROOT_USER}" git lfs install
|
||||||
|
|
||||||
echo "[*] Installing Tor"
|
echo "[*] Installing Tor"
|
||||||
|
@ -46,7 +46,7 @@ echo "[*] Adding Tor configuration"
|
||||||
if ! grep "${BISQ_TORHS}" /etc/tor/torrc >/dev/null 2>&1;then
|
if ! grep "${BISQ_TORHS}" /etc/tor/torrc >/dev/null 2>&1;then
|
||||||
sudo -H -i -u "${ROOT_USER}" sh -c "echo HiddenServiceDir ${TOR_RESOURCES}/${BISQ_TORHS}/ >> ${TOR_CONF}"
|
sudo -H -i -u "${ROOT_USER}" sh -c "echo HiddenServiceDir ${TOR_RESOURCES}/${BISQ_TORHS}/ >> ${TOR_CONF}"
|
||||||
sudo -H -i -u "${ROOT_USER}" sh -c "echo HiddenServicePort 80 127.0.0.1:8080 >> ${TOR_CONF}"
|
sudo -H -i -u "${ROOT_USER}" sh -c "echo HiddenServicePort 80 127.0.0.1:8080 >> ${TOR_CONF}"
|
||||||
sudo -H -i -u "${ROOT_USER}" sh -c "echo HiddenServiceVersion 2 >> ${TOR_CONF}"
|
sudo -H -i -u "${ROOT_USER}" sh -c "echo HiddenServiceVersion 3 >> ${TOR_CONF}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[*] Creating Bisq user with Tor access"
|
echo "[*] Creating Bisq user with Tor access"
|
||||||
|
@ -60,8 +60,8 @@ echo "[*] Cloning Bisq repo"
|
||||||
sudo -H -i -u "${BISQ_USER}" git config --global advice.detachedHead false
|
sudo -H -i -u "${BISQ_USER}" git config --global advice.detachedHead false
|
||||||
sudo -H -i -u "${BISQ_USER}" git clone --branch "${BISQ_REPO_TAG}" "${BISQ_REPO_URL}" "${BISQ_HOME}/${BISQ_REPO_NAME}"
|
sudo -H -i -u "${BISQ_USER}" git clone --branch "${BISQ_REPO_TAG}" "${BISQ_REPO_URL}" "${BISQ_HOME}/${BISQ_REPO_NAME}"
|
||||||
|
|
||||||
echo "[*] Installing OpenJDK 10.0.2 from Bisq repo"
|
echo "[*] Installing OpenJDK 11"
|
||||||
sudo -H -i -u "${ROOT_USER}" "${BISQ_HOME}/${BISQ_REPO_NAME}/scripts/install_java.sh"
|
sudo -H -i -u "${ROOT_USER}" apt-get install -qq -y openjdk-11-jdk
|
||||||
|
|
||||||
echo "[*] Checking out Bisq ${BISQ_LATEST_RELEASE}"
|
echo "[*] Checking out Bisq ${BISQ_LATEST_RELEASE}"
|
||||||
sudo -H -i -u "${BISQ_USER}" sh -c "cd ${BISQ_HOME}/${BISQ_REPO_NAME} && git checkout ${BISQ_LATEST_RELEASE}"
|
sudo -H -i -u "${BISQ_USER}" sh -c "cd ${BISQ_HOME}/${BISQ_REPO_NAME} && git checkout ${BISQ_LATEST_RELEASE}"
|
||||||
|
|
|
@ -19,24 +19,54 @@ package bisq.price.spot;
|
||||||
|
|
||||||
import bisq.price.PriceProvider;
|
import bisq.price.PriceProvider;
|
||||||
|
|
||||||
|
import bisq.core.locale.CurrencyUtil;
|
||||||
|
import bisq.core.locale.TradeCurrency;
|
||||||
|
|
||||||
|
import org.knowm.xchange.Exchange;
|
||||||
|
import org.knowm.xchange.ExchangeFactory;
|
||||||
|
import org.knowm.xchange.currency.Currency;
|
||||||
|
import org.knowm.xchange.currency.CurrencyPair;
|
||||||
|
import org.knowm.xchange.dto.marketdata.Ticker;
|
||||||
|
import org.knowm.xchange.exceptions.ExchangeException;
|
||||||
|
import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException;
|
||||||
|
import org.knowm.xchange.service.marketdata.MarketDataService;
|
||||||
|
import org.knowm.xchange.service.marketdata.params.CurrencyPairsParam;
|
||||||
|
import org.knowm.xchange.service.marketdata.params.Params;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for providers of bitcoin {@link ExchangeRate} data. Implementations
|
* Abstract base class for providers of bitcoin {@link ExchangeRate} data. Implementations
|
||||||
* are marked with the {@link org.springframework.stereotype.Component} annotation in
|
* are marked with the {@link org.springframework.stereotype.Component} annotation in
|
||||||
* order to be discovered via classpath scanning. Implementations are also marked with the
|
* order to be discovered via classpath scanning. If multiple
|
||||||
* {@link org.springframework.core.annotation.Order} annotation to determine their
|
* {@link ExchangeRateProvider}s retrieve rates for the same currency, then the
|
||||||
* precedence over each other in the case of two or more services returning exchange rate
|
* {@link ExchangeRateService} will average them out and expose an aggregate rate.
|
||||||
* data for the same currency pair. In such cases, results from the provider with the
|
|
||||||
* higher order value will take precedence over the provider with a lower value,
|
|
||||||
* presuming that such providers are being iterated over in an ordered list.
|
|
||||||
*
|
*
|
||||||
* @see ExchangeRateService#ExchangeRateService(java.util.List)
|
* @see ExchangeRateService#getAllMarketPrices()
|
||||||
*/
|
*/
|
||||||
public abstract class ExchangeRateProvider extends PriceProvider<Set<ExchangeRate>> {
|
public abstract class ExchangeRateProvider extends PriceProvider<Set<ExchangeRate>> {
|
||||||
|
|
||||||
|
public static final Set<String> SUPPORTED_CRYPTO_CURRENCIES = CurrencyUtil.getAllSortedCryptoCurrencies().stream()
|
||||||
|
.map(TradeCurrency::getCode)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
public static final Set<String> SUPPORTED_FIAT_CURRENCIES = CurrencyUtil.getAllSortedFiatCurrencies().stream()
|
||||||
|
.map(TradeCurrency::getCode)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final String prefix;
|
private final String prefix;
|
||||||
|
|
||||||
|
@ -60,4 +90,196 @@ public abstract class ExchangeRateProvider extends PriceProvider<Set<ExchangeRat
|
||||||
.filter(e -> "USD".equals(e.getCurrency()) || "LTC".equals(e.getCurrency()))
|
.filter(e -> "USD".equals(e.getCurrency()) || "LTC".equals(e.getCurrency()))
|
||||||
.forEach(e -> log.info("BTC/{}: {}", e.getCurrency(), e.getPrice()));
|
.forEach(e -> log.info("BTC/{}: {}", e.getCurrency(), e.getPrice()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param exchangeClass Class of the {@link Exchange} for which the rates should be
|
||||||
|
* polled
|
||||||
|
* @return Exchange rates for Bisq-supported fiat currencies and altcoins in the
|
||||||
|
* specified {@link Exchange}
|
||||||
|
*
|
||||||
|
* @see CurrencyUtil#getAllSortedFiatCurrencies()
|
||||||
|
* @see CurrencyUtil#getAllSortedCryptoCurrencies()
|
||||||
|
*/
|
||||||
|
protected Set<ExchangeRate> doGet(Class<? extends Exchange> exchangeClass) {
|
||||||
|
Set<ExchangeRate> result = new HashSet<ExchangeRate>();
|
||||||
|
|
||||||
|
// Initialize XChange objects
|
||||||
|
Exchange exchange = ExchangeFactory.INSTANCE.createExchange(exchangeClass.getName());
|
||||||
|
MarketDataService marketDataService = exchange.getMarketDataService();
|
||||||
|
|
||||||
|
// Retrieve all currency pairs supported by the exchange
|
||||||
|
List<CurrencyPair> allCurrencyPairsOnExchange = exchange.getExchangeSymbols();
|
||||||
|
|
||||||
|
// Find out which currency pairs we are interested in polling ("desired pairs")
|
||||||
|
// This will be the intersection of:
|
||||||
|
// 1) the pairs available on the exchange, and
|
||||||
|
// 2) the pairs Bisq considers relevant / valid
|
||||||
|
// This will result in two lists of desired pairs (fiat and alts)
|
||||||
|
|
||||||
|
// Find the desired fiat pairs (pair format is BTC-FIAT)
|
||||||
|
List<CurrencyPair> desiredFiatPairs = allCurrencyPairsOnExchange.stream()
|
||||||
|
.filter(cp -> cp.base.equals(Currency.BTC))
|
||||||
|
.filter(cp -> SUPPORTED_FIAT_CURRENCIES.contains(cp.counter.getCurrencyCode()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// Find the desired altcoin pairs (pair format is ALT-BTC)
|
||||||
|
List<CurrencyPair> desiredCryptoPairs = allCurrencyPairsOnExchange.stream()
|
||||||
|
.filter(cp -> cp.counter.equals(Currency.BTC))
|
||||||
|
.filter(cp -> SUPPORTED_CRYPTO_CURRENCIES.contains(cp.base.getCurrencyCode()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// Retrieve in bulk all tickers offered by the exchange
|
||||||
|
// The benefits of this approach (vs polling each ticker) are twofold:
|
||||||
|
// 1) the polling of the exchange is faster (one HTTP call vs several)
|
||||||
|
// 2) it's easier to stay below any API rate limits the exchange might have
|
||||||
|
List<Ticker> tickersRetrievedFromExchange = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
tickersRetrievedFromExchange = marketDataService.getTickers(new CurrencyPairsParam() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link MarketDataService#getTickers(Params)} interface requires a
|
||||||
|
* {@link CurrencyPairsParam} argument when polling for tickers in bulk.
|
||||||
|
* This parameter is meant to indicate a list of currency pairs for which
|
||||||
|
* the tickers should be polled. However, the actual implementations for
|
||||||
|
* the different exchanges differ, for example:
|
||||||
|
* - some will ignore it (and retrieve all available tickers)
|
||||||
|
* - some will require it (and will fail if a null or empty list is given)
|
||||||
|
* - some will properly handle it
|
||||||
|
*
|
||||||
|
* We take a simplistic approach, namely:
|
||||||
|
* - for providers that require such a filter, specify one
|
||||||
|
* - for all others, do not specify one
|
||||||
|
*
|
||||||
|
* We make this distinction using
|
||||||
|
* {@link ExchangeRateProvider#requiresFilterDuringBulkTickerRetrieval}
|
||||||
|
*
|
||||||
|
* @return Filter (list of desired currency pairs) to be used during bulk
|
||||||
|
* ticker retrieval
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<CurrencyPair> getCurrencyPairs() {
|
||||||
|
// If required by the exchange implementation, specify a filter
|
||||||
|
// (list of pairs which should be retrieved)
|
||||||
|
if (requiresFilterDuringBulkTickerRetrieval()) {
|
||||||
|
return Stream.of(desiredFiatPairs, desiredCryptoPairs)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, specify an empty list, indicating that the API should
|
||||||
|
// simply return all available tickers
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tickersRetrievedFromExchange.isEmpty()) {
|
||||||
|
// If the bulk ticker retrieval went through, but no tickers were
|
||||||
|
// retrieved, this is a strong indication that this specific exchange
|
||||||
|
// needs a specific list of pairs given as argument, for bulk retrieval to
|
||||||
|
// work. See requiresFilterDuringBulkTickerRetrieval()
|
||||||
|
throw new IllegalArgumentException("No tickers retrieved, " +
|
||||||
|
"exchange requires explicit filter argument during bulk retrieval?");
|
||||||
|
}
|
||||||
|
} catch (NotYetImplementedForExchangeException e) {
|
||||||
|
// Thrown when a provider has no marketDataService.getTickers() implementation
|
||||||
|
// either because the exchange API does not provide it, or because it has not
|
||||||
|
// been implemented yet in the knowm xchange library
|
||||||
|
|
||||||
|
// In this case (retrieval of bulk tickers is not possible) retrieve the
|
||||||
|
// tickers one by one
|
||||||
|
List<Ticker> finalTickersRetrievedFromExchange = tickersRetrievedFromExchange;
|
||||||
|
Stream.of(desiredFiatPairs, desiredCryptoPairs)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
.forEach(cp -> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// This is done in a loop, and can therefore result in a burst
|
||||||
|
// of API calls. Some exchanges do not allow bursts
|
||||||
|
// A simplistic solution is to delay every call by 1 second
|
||||||
|
// TODO Switch to using a more elegant solution (per exchange)
|
||||||
|
// like ResilienceSpecification (needs knowm xchange libs v5)
|
||||||
|
if (getMarketDataCallDelay() > 0) {
|
||||||
|
Thread.sleep(getMarketDataCallDelay());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ticker ticker = marketDataService.getTicker(cp);
|
||||||
|
finalTickersRetrievedFromExchange.add(ticker);
|
||||||
|
|
||||||
|
} catch (IOException | InterruptedException ioException) {
|
||||||
|
ioException.printStackTrace();
|
||||||
|
log.error("Could not query tickers for " + getName(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (ExchangeException | // Errors reported by the exchange (rate limit, etc)
|
||||||
|
IOException | // Errors while trying to connect to the API (timeouts, etc)
|
||||||
|
// Potential error when integrating new exchange (hints that exchange
|
||||||
|
// provider implementation needs to overwrite
|
||||||
|
// requiresFilterDuringBulkTickerRetrieval() and have it return true )
|
||||||
|
IllegalArgumentException e) {
|
||||||
|
// Catch and handle all other possible exceptions
|
||||||
|
// If there was a problem with polling this exchange, return right away,
|
||||||
|
// since there are no results to parse and process
|
||||||
|
log.error("Could not query tickers for provider " + getName(), e);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an ExchangeRate for each desired currency pair ticker that was retrieved
|
||||||
|
Predicate<Ticker> isDesiredFiatPair = t -> desiredFiatPairs.contains(t.getCurrencyPair());
|
||||||
|
Predicate<Ticker> isDesiredCryptoPair = t -> desiredCryptoPairs.contains(t.getCurrencyPair());
|
||||||
|
tickersRetrievedFromExchange.stream()
|
||||||
|
.filter(isDesiredFiatPair.or(isDesiredCryptoPair)) // Only consider desired pairs
|
||||||
|
.forEach(t -> {
|
||||||
|
// All tickers here match all requirements
|
||||||
|
|
||||||
|
// We have two kinds of currency pairs, BTC-FIAT and ALT-BTC
|
||||||
|
// In the first one, BTC is the first currency of the pair
|
||||||
|
// In the second type, BTC is listed as the second currency
|
||||||
|
// Distinguish between the two and create ExchangeRates accordingly
|
||||||
|
|
||||||
|
// In every Bisq ExchangeRate, BTC is one currency in the pair
|
||||||
|
// Extract the other currency from the ticker, to create ExchangeRates
|
||||||
|
String otherExchangeRateCurrency;
|
||||||
|
if (t.getCurrencyPair().base.equals(Currency.BTC)) {
|
||||||
|
otherExchangeRateCurrency = t.getCurrencyPair().counter.getCurrencyCode();
|
||||||
|
} else {
|
||||||
|
otherExchangeRateCurrency = t.getCurrencyPair().base.getCurrencyCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(new ExchangeRate(
|
||||||
|
otherExchangeRateCurrency,
|
||||||
|
t.getLast(),
|
||||||
|
// Some exchanges do not provide timestamps
|
||||||
|
t.getTimestamp() == null ? new Date() : t.getTimestamp(),
|
||||||
|
this.getName()
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies optional delay between certain kind of API calls that can result in
|
||||||
|
* bursts. We want to avoid bursts, because this can cause certain exchanges to
|
||||||
|
* temporarily restrict access to the pricenode IP.
|
||||||
|
*
|
||||||
|
* @return Amount of milliseconds of delay between marketDataService.getTicker calls.
|
||||||
|
* By default 0, but can be overwritten by each provider.
|
||||||
|
*/
|
||||||
|
protected long getMarketDataCallDelay() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether or not the bulk retrieval of tickers from the exchange requires an
|
||||||
|
* explicit filter (list of desired pairs) or not. If true, the
|
||||||
|
* {@link MarketDataService#getTickers(Params)} call will be constructed and given as
|
||||||
|
* argument, which acts as a filter indicating for which pairs the ticker should be
|
||||||
|
* retrieved. If false, {@link MarketDataService#getTickers(Params)} will be called
|
||||||
|
* with an empty argument, indicating that the API should simply return all available
|
||||||
|
* tickers on the exchange
|
||||||
|
*/
|
||||||
|
protected boolean requiresFilterDuringBulkTickerRetrieval() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,20 +17,25 @@
|
||||||
|
|
||||||
package bisq.price.spot;
|
package bisq.price.spot;
|
||||||
|
|
||||||
import bisq.price.spot.providers.BitcoinAverage;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.OptionalDouble;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* High-level {@link ExchangeRate} data operations.
|
* High-level {@link ExchangeRate} data operations.
|
||||||
*/
|
*/
|
||||||
|
@ -53,32 +58,105 @@ class ExchangeRateService {
|
||||||
|
|
||||||
public Map<String, Object> getAllMarketPrices() {
|
public Map<String, Object> getAllMarketPrices() {
|
||||||
Map<String, Object> metadata = new LinkedHashMap<>();
|
Map<String, Object> metadata = new LinkedHashMap<>();
|
||||||
Map<String, ExchangeRate> allExchangeRates = new LinkedHashMap<>();
|
Map<String, ExchangeRate> aggregateExchangeRates = getAggregateExchangeRates();
|
||||||
|
|
||||||
providers.forEach(p -> {
|
providers.forEach(p -> {
|
||||||
Set<ExchangeRate> exchangeRates = p.get();
|
Set<ExchangeRate> exchangeRates = p.get();
|
||||||
|
|
||||||
|
// Specific metadata fields for specific providers are expected by the client,
|
||||||
|
// mostly for historical reasons
|
||||||
|
// Therefore, add metadata fields for all known providers
|
||||||
|
// Rates are encapsulated in the "data" map below
|
||||||
metadata.putAll(getMetadata(p, exchangeRates));
|
metadata.putAll(getMetadata(p, exchangeRates));
|
||||||
exchangeRates.forEach(e ->
|
|
||||||
allExchangeRates.put(e.getCurrency(), e)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return new LinkedHashMap<String, Object>() {{
|
LinkedHashMap<String, Object> result = new LinkedHashMap<>();
|
||||||
putAll(metadata);
|
result.putAll(metadata);
|
||||||
// Use a sorted list by currency code to make comparision of json data between different
|
// Use a sorted list by currency code to make comparision of json data between
|
||||||
// price nodes easier
|
// different price nodes easier
|
||||||
List<ExchangeRate> values = new ArrayList<>(allExchangeRates.values());
|
List<ExchangeRate> values = new ArrayList<>(aggregateExchangeRates.values());
|
||||||
values.sort(Comparator.comparing(ExchangeRate::getCurrency));
|
values.sort(Comparator.comparing(ExchangeRate::getCurrency));
|
||||||
put("data", values);
|
result.put("data", values);
|
||||||
}};
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each currency, create an aggregate {@link ExchangeRate} based on the currency's
|
||||||
|
* rates from all providers. If multiple providers have rates for the currency, then
|
||||||
|
* aggregate price = average of retrieved prices. If a single provider has rates for
|
||||||
|
* the currency, then aggregate price = the rate from that provider.
|
||||||
|
*
|
||||||
|
* @return Aggregate {@link ExchangeRate}s based on info from all providers, indexed
|
||||||
|
* by currency code
|
||||||
|
*/
|
||||||
|
private Map<String, ExchangeRate> getAggregateExchangeRates() {
|
||||||
|
Map<String, ExchangeRate> aggregateExchangeRates = new HashMap<>();
|
||||||
|
|
||||||
|
// Query all providers and collect all exchange rates, grouped by currency code
|
||||||
|
// key = currency code
|
||||||
|
// value = list of exchange rates
|
||||||
|
Map<String, List<ExchangeRate>> currencyCodeToExchangeRates = getCurrencyCodeToExchangeRates();
|
||||||
|
|
||||||
|
// For each currency code, calculate aggregate rate
|
||||||
|
currencyCodeToExchangeRates.forEach((currencyCode, exchangeRateList) -> {
|
||||||
|
if (exchangeRateList.isEmpty()) {
|
||||||
|
// If the map was built incorrectly and this currency points to an empty
|
||||||
|
// list of rates, skip it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExchangeRate aggregateExchangeRate;
|
||||||
|
if (exchangeRateList.size() == 1) {
|
||||||
|
// If a single provider has rates for this currency, then aggregate = rate
|
||||||
|
// from that provider
|
||||||
|
aggregateExchangeRate = exchangeRateList.get(0);
|
||||||
|
} else {
|
||||||
|
// If multiple providers have rates for this currency, then
|
||||||
|
// aggregate = average of the rates
|
||||||
|
OptionalDouble opt = exchangeRateList.stream().mapToDouble(ExchangeRate::getPrice).average();
|
||||||
|
// List size > 1, so opt is always set
|
||||||
|
double priceAvg = opt.orElseThrow(IllegalStateException::new);
|
||||||
|
|
||||||
|
aggregateExchangeRate = new ExchangeRate(
|
||||||
|
currencyCode,
|
||||||
|
BigDecimal.valueOf(priceAvg),
|
||||||
|
new Date(), // timestamp = time when avg is calculated
|
||||||
|
"Bisq-Aggregate");
|
||||||
|
}
|
||||||
|
aggregateExchangeRates.put(aggregateExchangeRate.getCurrency(), aggregateExchangeRate);
|
||||||
|
});
|
||||||
|
|
||||||
|
return aggregateExchangeRates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return All {@link ExchangeRate}s from all providers, grouped by currency code
|
||||||
|
*/
|
||||||
|
private Map<String, List<ExchangeRate>> getCurrencyCodeToExchangeRates() {
|
||||||
|
Map<String, List<ExchangeRate>> currencyCodeToExchangeRates = new HashMap<>();
|
||||||
|
for (ExchangeRateProvider p : providers) {
|
||||||
|
for (ExchangeRate exchangeRate : p.get()) {
|
||||||
|
String currencyCode = exchangeRate.getCurrency();
|
||||||
|
if (currencyCodeToExchangeRates.containsKey(currencyCode)) {
|
||||||
|
List<ExchangeRate> l = new ArrayList<>(currencyCodeToExchangeRates.get(currencyCode));
|
||||||
|
l.add(exchangeRate);
|
||||||
|
currencyCodeToExchangeRates.put(currencyCode, l);
|
||||||
|
} else {
|
||||||
|
currencyCodeToExchangeRates.put(currencyCode, asList(exchangeRate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currencyCodeToExchangeRates;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> getMetadata(ExchangeRateProvider provider, Set<ExchangeRate> exchangeRates) {
|
private Map<String, Object> getMetadata(ExchangeRateProvider provider, Set<ExchangeRate> exchangeRates) {
|
||||||
Map<String, Object> metadata = new LinkedHashMap<>();
|
Map<String, Object> metadata = new LinkedHashMap<>();
|
||||||
|
|
||||||
// In case a provider is not available we still want to deliver the data of the other providers, so we catch
|
// In case a provider is not available we still want to deliver the data of the
|
||||||
// a possible exception and leave timestamp at 0. The Bisq app will check if the timestamp is in a tolerance
|
// other providers, so we catch a possible exception and leave timestamp at 0. The
|
||||||
// window and if it is too old it will show that the price is not available.
|
// Bisq app will check if the timestamp is in a tolerance window and if it is too
|
||||||
|
// old it will show that the price is not available.
|
||||||
long timestamp = 0;
|
long timestamp = 0;
|
||||||
try {
|
try {
|
||||||
timestamp = getTimestamp(provider, exchangeRates);
|
timestamp = getTimestamp(provider, exchangeRates);
|
||||||
|
@ -88,10 +166,6 @@ class ExchangeRateService {
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (provider instanceof BitcoinAverage.Local) {
|
|
||||||
metadata.put("btcAverageTs", timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
String prefix = provider.getPrefix();
|
String prefix = provider.getPrefix();
|
||||||
metadata.put(prefix + "Ts", timestamp);
|
metadata.put(prefix + "Ts", timestamp);
|
||||||
metadata.put(prefix + "Count", exchangeRates.size());
|
metadata.put(prefix + "Count", exchangeRates.size());
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.btcmarkets.BTCMarketsExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class BTCMarkets extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public BTCMarkets() {
|
||||||
|
super("BTCMARKETS", "btcmarkets", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: AUD
|
||||||
|
// Supported alts: ETH, LTC
|
||||||
|
return doGet(BTCMarketsExchange.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.binance.BinanceExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Binance extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Binance() {
|
||||||
|
super("BINANCE", "binance", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: EUR, GBP, NGN, RUB, TRY, UAH, ZAR
|
||||||
|
// Supported alts: BEAM, DAI, DASH, DCR, DOGE, ETC, ETH, LTC, NAV, PIVX, XMR, XZC,
|
||||||
|
// ZEC, ZEN
|
||||||
|
return doGet(BinanceExchange.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.bitbay.BitbayExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Bitbay extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Bitbay() {
|
||||||
|
super("BITBAY", "bitbay", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: EUR, GBP, PLN, USD
|
||||||
|
// Supported alts: DASH, ETH, LTC
|
||||||
|
return doGet(BitbayExchange.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,141 +19,38 @@ package bisq.price.spot.providers;
|
||||||
|
|
||||||
import bisq.price.spot.ExchangeRate;
|
import bisq.price.spot.ExchangeRate;
|
||||||
import bisq.price.spot.ExchangeRateProvider;
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
import bisq.common.util.Hex;
|
|
||||||
|
|
||||||
import org.knowm.xchange.bitcoinaverage.dto.marketdata.BitcoinAverageTicker;
|
|
||||||
import org.knowm.xchange.bitcoinaverage.dto.marketdata.BitcoinAverageTickers;
|
|
||||||
|
|
||||||
import org.springframework.core.annotation.Order;
|
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
import org.springframework.http.RequestEntity;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the BitcoinAverage API documentation at https://apiv2.bitcoinaverage.com/#ticker-data-all
|
* Stub implementation (similar to #CoinMarketCap) for backward compatibility with legacy
|
||||||
|
* Bisq clients
|
||||||
*/
|
*/
|
||||||
public abstract class BitcoinAverage extends ExchangeRateProvider {
|
@Component
|
||||||
|
class BitcoinAverage extends ExchangeRateProvider {
|
||||||
|
|
||||||
/**
|
public BitcoinAverage() {
|
||||||
* Max number of requests allowed per month on the BitcoinAverage developer plan.
|
// Simulate a deactivated BitcoinAverage provider
|
||||||
* Note the actual max value is 45,000; we use the more conservative value below to
|
// We still need the class to exist and be registered as a provider though,
|
||||||
* ensure we do not exceed it. See https://bitcoinaverage.com/en/plans.
|
// because the returned data structure must contain the "btcAverageTs" key
|
||||||
*/
|
// for backward compatibility with Bisq clients which hardcode that key
|
||||||
private static final double MAX_REQUESTS_PER_MONTH = 42_514;
|
super("BA", "btcAverage", Duration.ofMinutes(100));
|
||||||
|
|
||||||
private final RestTemplate restTemplate = new RestTemplate();
|
|
||||||
private final String symbolSet;
|
|
||||||
|
|
||||||
private String pubKey;
|
|
||||||
private Mac mac;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param symbolSet "global" or "local"; see https://apiv2.bitcoinaverage.com/#supported-currencies
|
|
||||||
*/
|
|
||||||
public BitcoinAverage(String name, String prefix, double pctMaxRequests, String symbolSet, Environment env) {
|
|
||||||
super(name, prefix, refreshIntervalFor(pctMaxRequests));
|
|
||||||
this.symbolSet = symbolSet;
|
|
||||||
this.pubKey = env.getRequiredProperty("BITCOIN_AVG_PUBKEY");
|
|
||||||
this.mac = initMac(env.getRequiredProperty("BITCOIN_AVG_PRIVKEY"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see CoinMarketCap#doGet()
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Set<ExchangeRate> doGet() {
|
public Set<ExchangeRate> doGet() {
|
||||||
|
|
||||||
return getTickersKeyedByCurrency().entrySet().stream()
|
HashSet<ExchangeRate> exchangeRates = new HashSet<>();
|
||||||
.filter(e -> supportedCurrency(e.getKey()))
|
exchangeRates.add(new ExchangeRate("NON_EXISTING_SYMBOL_BA", 0, 0L, getName()));
|
||||||
.map(e ->
|
return exchangeRates;
|
||||||
new ExchangeRate(
|
|
||||||
e.getKey(),
|
|
||||||
e.getValue().getLast(),
|
|
||||||
e.getValue().getTimestamp(),
|
|
||||||
this.getName()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean supportedCurrency(String currencyCode) {
|
|
||||||
// ignore Venezuelan bolivars as the "official" exchange rate is just wishful thinking
|
|
||||||
// we should use this API with a custom provider instead: http://api.bitcoinvenezuela.com/1
|
|
||||||
return !"VEF".equals(currencyCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, BitcoinAverageTicker> getTickersKeyedByCurrency() {
|
|
||||||
// go from a map with keys like "BTCUSD", "BTCVEF"
|
|
||||||
return getTickersKeyedByCurrencyPair().entrySet().stream()
|
|
||||||
// to a map with keys like "USD", "VEF"
|
|
||||||
.collect(Collectors.toMap(e -> e.getKey().substring(3), Map.Entry::getValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, BitcoinAverageTicker> getTickersKeyedByCurrencyPair() {
|
|
||||||
return restTemplate.exchange(
|
|
||||||
RequestEntity
|
|
||||||
.get(UriComponentsBuilder
|
|
||||||
.fromUriString("https://apiv2.bitcoinaverage.com/indices/{symbol-set}/ticker/all?crypto=BTC")
|
|
||||||
.buildAndExpand(symbolSet)
|
|
||||||
.toUri())
|
|
||||||
.header("X-signature", getAuthSignature())
|
|
||||||
.build(),
|
|
||||||
BitcoinAverageTickers.class
|
|
||||||
).getBody().getTickers();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getAuthSignature() {
|
|
||||||
String payload = String.format("%s.%s", Instant.now().getEpochSecond(), pubKey);
|
|
||||||
return String.format("%s.%s", payload, Hex.encode(mac.doFinal(payload.getBytes(Charsets.UTF_8))));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Mac initMac(String privKey) {
|
|
||||||
String algorithm = "HmacSHA256";
|
|
||||||
SecretKey secretKey = new SecretKeySpec(privKey.getBytes(Charsets.UTF_8), algorithm);
|
|
||||||
try {
|
|
||||||
Mac mac = Mac.getInstance(algorithm);
|
|
||||||
mac.init(secretKey);
|
|
||||||
return mac;
|
|
||||||
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Duration refreshIntervalFor(double pctMaxRequests) {
|
|
||||||
long requestsPerMonth = (long) (MAX_REQUESTS_PER_MONTH * pctMaxRequests);
|
|
||||||
return Duration.ofDays(31).dividedBy(requestsPerMonth);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@Order(1)
|
|
||||||
public static class Global extends BitcoinAverage {
|
|
||||||
public Global(Environment env) {
|
|
||||||
super("BTCA_G", "btcAverageG", 0.3, "global", env);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@Order(2)
|
|
||||||
public static class Local extends BitcoinAverage {
|
|
||||||
public Local(Environment env) {
|
|
||||||
super("BTCA_L", "btcAverageL", 0.7, "local", env);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.bitfinex.BitfinexExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Bitfinex extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Bitfinex() {
|
||||||
|
super("BITFINEX", "bitfinex", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: EUR, GBP, JPY, USD
|
||||||
|
// Supported alts: DAI, ETC, ETH, LTC, XMR, ZEC
|
||||||
|
return doGet(BitfinexExchange.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.bitflyer.BitflyerExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Bitflyer extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Bitflyer() {
|
||||||
|
super("BITFLYER", "bitflyer", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: JPY
|
||||||
|
// Supported alts: ETH
|
||||||
|
return doGet(BitflyerExchange.class);
|
||||||
|
}
|
||||||
|
}
|
102
pricenode/src/main/java/bisq/price/spot/providers/Bitpay.java
Normal file
102
pricenode/src/main/java/bisq/price/spot/providers/Bitpay.java
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
import bisq.price.util.bitpay.BitpayMarketData;
|
||||||
|
import bisq.price.util.bitpay.BitpayTicker;
|
||||||
|
|
||||||
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
|
import org.springframework.http.RequestEntity;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Bitpay extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
private final RestTemplate restTemplate = new RestTemplate();
|
||||||
|
|
||||||
|
public Bitpay() {
|
||||||
|
super("BITPAY", "bitpay", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
|
||||||
|
Set<ExchangeRate> result = new HashSet<ExchangeRate>();
|
||||||
|
|
||||||
|
Predicate<BitpayTicker> isDesiredFiatPair = t -> SUPPORTED_FIAT_CURRENCIES.contains(t.getCode());
|
||||||
|
Predicate<BitpayTicker> isDesiredCryptoPair = t -> SUPPORTED_CRYPTO_CURRENCIES.contains(t.getCode());
|
||||||
|
|
||||||
|
getTickers()
|
||||||
|
.filter(isDesiredFiatPair.or(isDesiredCryptoPair))
|
||||||
|
.forEach(ticker -> {
|
||||||
|
boolean useInverseRate = false;
|
||||||
|
if (SUPPORTED_CRYPTO_CURRENCIES.contains(ticker.getCode())) {
|
||||||
|
// Use inverse rate for alts, because the API returns the
|
||||||
|
// conversion rate in the opposite direction than what we need
|
||||||
|
// API returns the BTC/Alt rate, we need the Alt/BTC rate
|
||||||
|
useInverseRate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal rate = ticker.getRate();
|
||||||
|
// Find the inverse rate, while using enough decimals to reflect very
|
||||||
|
// small exchange rates
|
||||||
|
BigDecimal inverseRate = (rate.compareTo(BigDecimal.ZERO) > 0) ?
|
||||||
|
BigDecimal.ONE.divide(rate, 8, RoundingMode.HALF_UP) :
|
||||||
|
BigDecimal.ZERO;
|
||||||
|
|
||||||
|
result.add(new ExchangeRate(
|
||||||
|
ticker.getCode(),
|
||||||
|
(useInverseRate ? inverseRate : rate),
|
||||||
|
new Date(),
|
||||||
|
this.getName()
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<BitpayTicker> getTickers() {
|
||||||
|
BitpayMarketData marketData = restTemplate.exchange(
|
||||||
|
RequestEntity
|
||||||
|
.get(UriComponentsBuilder
|
||||||
|
.fromUriString("https://bitpay.com/rates").build()
|
||||||
|
.toUri())
|
||||||
|
.build(),
|
||||||
|
new ParameterizedTypeReference<BitpayMarketData>() {
|
||||||
|
}
|
||||||
|
).getBody();
|
||||||
|
|
||||||
|
return Arrays.stream(marketData.getData());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.bitstamp.BitstampExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Bitstamp extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Bitstamp() {
|
||||||
|
super("BITSTAMP", "bitstamp", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: EUR, GBP, USD
|
||||||
|
// Supported alts: ETH, LTC
|
||||||
|
return doGet(BitstampExchange.class);
|
||||||
|
}
|
||||||
|
}
|
49
pricenode/src/main/java/bisq/price/spot/providers/CexIO.java
Normal file
49
pricenode/src/main/java/bisq/price/spot/providers/CexIO.java
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.cexio.CexIOExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class CexIO extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public CexIO() {
|
||||||
|
super("CexIO", "cexio", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: EUR, GBP, RUB, USD
|
||||||
|
// Supported alts: DASH, ETH, LTC
|
||||||
|
return doGet(CexIOExchange.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean requiresFilterDuringBulkTickerRetrieval() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
104
pricenode/src/main/java/bisq/price/spot/providers/CoinGecko.java
Normal file
104
pricenode/src/main/java/bisq/price/spot/providers/CoinGecko.java
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
import bisq.price.util.coingecko.CoinGeckoMarketData;
|
||||||
|
|
||||||
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
|
import org.springframework.http.RequestEntity;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class CoinGecko extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
private final RestTemplate restTemplate = new RestTemplate();
|
||||||
|
|
||||||
|
public CoinGecko() {
|
||||||
|
super("COINGECKO", "coingecko", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
|
||||||
|
// Rate limit for the CoinGecko API is 10 calls each second per IP address
|
||||||
|
// We retrieve all rates in bulk, so we only make 1 call per provider poll
|
||||||
|
|
||||||
|
Set<ExchangeRate> result = new HashSet<ExchangeRate>();
|
||||||
|
|
||||||
|
Predicate<Map.Entry> isDesiredFiatPair = t -> SUPPORTED_FIAT_CURRENCIES.contains(t.getKey());
|
||||||
|
Predicate<Map.Entry> isDesiredCryptoPair = t -> SUPPORTED_CRYPTO_CURRENCIES.contains(t.getKey());
|
||||||
|
|
||||||
|
getMarketData().getRates().entrySet().stream()
|
||||||
|
.filter(isDesiredFiatPair.or(isDesiredCryptoPair))
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
|
||||||
|
.forEach((key, ticker) -> {
|
||||||
|
|
||||||
|
boolean useInverseRate = false;
|
||||||
|
if (SUPPORTED_CRYPTO_CURRENCIES.contains(key)) {
|
||||||
|
// Use inverse rate for alts, because the API returns the
|
||||||
|
// conversion rate in the opposite direction than what we need
|
||||||
|
// API returns the BTC/Alt rate, we need the Alt/BTC rate
|
||||||
|
useInverseRate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal rate = ticker.getValue();
|
||||||
|
// Find the inverse rate, while using enough decimals to reflect very
|
||||||
|
// small exchange rates
|
||||||
|
BigDecimal inverseRate = (rate.compareTo(BigDecimal.ZERO) > 0) ?
|
||||||
|
BigDecimal.ONE.divide(rate, 8, RoundingMode.HALF_UP) :
|
||||||
|
BigDecimal.ZERO;
|
||||||
|
|
||||||
|
result.add(new ExchangeRate(
|
||||||
|
key,
|
||||||
|
(useInverseRate ? inverseRate : rate),
|
||||||
|
new Date(),
|
||||||
|
this.getName()
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CoinGeckoMarketData getMarketData() {
|
||||||
|
return restTemplate.exchange(
|
||||||
|
RequestEntity
|
||||||
|
.get(UriComponentsBuilder
|
||||||
|
.fromUriString("https://api.coingecko.com/api/v3/exchange_rates").build()
|
||||||
|
.toUri())
|
||||||
|
.build(),
|
||||||
|
new ParameterizedTypeReference<CoinGeckoMarketData>() {
|
||||||
|
}
|
||||||
|
).getBody();
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,6 @@ package bisq.price.spot.providers;
|
||||||
import bisq.price.spot.ExchangeRate;
|
import bisq.price.spot.ExchangeRate;
|
||||||
import bisq.price.spot.ExchangeRateProvider;
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
import org.springframework.core.annotation.Order;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
@ -32,7 +31,6 @@ import java.util.Set;
|
||||||
* Stub implementation of CoinMarketCap price provider to prevent NullPointerExceptions within legacy clients
|
* Stub implementation of CoinMarketCap price provider to prevent NullPointerExceptions within legacy clients
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@Order(3)
|
|
||||||
class CoinMarketCap extends ExchangeRateProvider {
|
class CoinMarketCap extends ExchangeRateProvider {
|
||||||
|
|
||||||
public CoinMarketCap() {
|
public CoinMarketCap() {
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.coinmate.CoinmateExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Coinmate extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Coinmate() {
|
||||||
|
super("Coinmate", "coinmate", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: CZK, EUR
|
||||||
|
// Supported alts: DASH, ETH, LTC
|
||||||
|
return doGet(CoinmateExchange.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.coinone.CoinoneExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Coinone extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Coinone() {
|
||||||
|
super("COINONE", "coinone", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: KRW
|
||||||
|
// Supported alts: -
|
||||||
|
return doGet(CoinoneExchange.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
import bisq.price.util.coinpaprika.CoinpaprikaMarketData;
|
||||||
|
|
||||||
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
|
import org.springframework.http.RequestEntity;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Coinpaprika extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
private final RestTemplate restTemplate = new RestTemplate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to determine the currencies in which the BTC price can be quoted. There seems
|
||||||
|
* to be no programatic way to retrieve it, so we get the value from the API
|
||||||
|
* documentation (see "quotes" param decsribed at
|
||||||
|
* https://api.coinpaprika.com/#operation/getTickersById ). The hardcoded value below
|
||||||
|
* is the list of allowed values as per the API documentation, but without BTC and ETH
|
||||||
|
* as it makes no sense to quote the BTC price in them.
|
||||||
|
*/
|
||||||
|
private final static String SUPPORTED_CURRENCIES =
|
||||||
|
("USD, EUR, PLN, KRW, GBP, CAD, JPY, RUB, TRY, NZD, AUD, CHF, UAH, HKD, " +
|
||||||
|
"SGD, NGN, PHP, MXN, BRL, THB, CLP, CNY, CZK, DKK, HUF, IDR, ILS," +
|
||||||
|
"INR, MYR, NOK, PKR, SEK, TWD, ZAR, VND, BOB, COP, PEN, ARS, ISK")
|
||||||
|
.replace(" ", ""); // Strip any spaces
|
||||||
|
|
||||||
|
public Coinpaprika() {
|
||||||
|
super("COINPAPRIKA", "coinpaprika", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
|
||||||
|
// Single IP address can send less than 10 requests per second
|
||||||
|
// We make only 1 API call per provider poll, so we're not at risk of reaching it
|
||||||
|
|
||||||
|
Set<ExchangeRate> result = new HashSet<ExchangeRate>();
|
||||||
|
|
||||||
|
Predicate<Map.Entry> isDesiredFiatPair = t -> SUPPORTED_FIAT_CURRENCIES.contains(t.getKey());
|
||||||
|
|
||||||
|
getMarketData().getQuotes().entrySet().stream()
|
||||||
|
.filter(isDesiredFiatPair)
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
|
||||||
|
.forEach((key, ticker) -> {
|
||||||
|
|
||||||
|
result.add(new ExchangeRate(
|
||||||
|
key,
|
||||||
|
ticker.getPrice(),
|
||||||
|
new Date(),
|
||||||
|
this.getName()
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CoinpaprikaMarketData getMarketData() {
|
||||||
|
return restTemplate.exchange(
|
||||||
|
RequestEntity
|
||||||
|
.get(UriComponentsBuilder
|
||||||
|
.fromUriString(
|
||||||
|
"https://api.coinpaprika.com/v1/tickers/btc-bitcoin?quotes=" +
|
||||||
|
SUPPORTED_CURRENCIES).build()
|
||||||
|
.toUri())
|
||||||
|
.build(),
|
||||||
|
new ParameterizedTypeReference<CoinpaprikaMarketData>() {
|
||||||
|
}
|
||||||
|
).getBody();
|
||||||
|
}
|
||||||
|
}
|
46
pricenode/src/main/java/bisq/price/spot/providers/Exmo.java
Normal file
46
pricenode/src/main/java/bisq/price/spot/providers/Exmo.java
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.exmo.ExmoExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Exmo extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Exmo() {
|
||||||
|
// API rate limit = 10 calls / second from the same IP ( see https://exmo.com/en/api )
|
||||||
|
super("EXMO", "exmo", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: EUR, PLN, RUB, UAH (Ukrainian hryvnia), USD
|
||||||
|
// Supported alts: DASH, DOGE, ETC, ETH, LTC, XMR, ZEC
|
||||||
|
return doGet(ExmoExchange.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.hitbtc.v2.HitbtcExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Hitbtc extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Hitbtc() {
|
||||||
|
super("HITBTC", "hitbtc", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: USD
|
||||||
|
// Supported alts: AEON, BTM, DASH, DCR, DOGE, EMC, ETC, ETH, GRIN, LTC, NAV,
|
||||||
|
// PART, XMR, XRC, XZC, ZEC, ZEN
|
||||||
|
return doGet(HitbtcExchange.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
pricenode/src/main/java/bisq/price/spot/providers/Huobi.java
Normal file
45
pricenode/src/main/java/bisq/price/spot/providers/Huobi.java
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.huobi.HuobiExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Huobi extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Huobi() {
|
||||||
|
super("HUOBI", "huobi", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: -
|
||||||
|
// Supported alts: BTM, DASH, DCR, DOGE, ETC, ETH, FAIR, LTC, XMR, XZC, ZEC, ZEN
|
||||||
|
return doGet(HuobiExchange.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.independentreserve.IndependentReserveExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class IndependentReserve extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public IndependentReserve() {
|
||||||
|
super("IndependentReserve", "independentreserve", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: AUD, NZD (New Zealand Dollar), USD
|
||||||
|
// Supported alts: -
|
||||||
|
return doGet(IndependentReserveExchange.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.kraken.KrakenExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Kraken extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Kraken() {
|
||||||
|
super("KRAKEN", "kraken", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: AUD, CAD, CHF, EUR, GBP, JPY, USD
|
||||||
|
// Supported alts: DASH, DOGE, ETC, ETH, LTC, XMR, ZEC
|
||||||
|
return doGet(KrakenExchange.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean requiresFilterDuringBulkTickerRetrieval() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
52
pricenode/src/main/java/bisq/price/spot/providers/Luno.java
Normal file
52
pricenode/src/main/java/bisq/price/spot/providers/Luno.java
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.luno.LunoExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Luno extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Luno() {
|
||||||
|
super("LUNO", "luno", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: IDR (Indonesian rupiah), MYR (Malaysian ringgit),
|
||||||
|
// NGN (Nigerian Naira), ZAR (South African rand)
|
||||||
|
// Supported alts: -
|
||||||
|
return doGet(LunoExchange.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long getMarketDataCallDelay() {
|
||||||
|
// Luno allows only 1 MarketData call per second
|
||||||
|
// (see https://www.luno.com/en/developers/api )
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.mercadobitcoin.MercadoBitcoinExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MercadoBitcoin extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public MercadoBitcoin() {
|
||||||
|
super("MercadoBitcoin", "mercadobitcoin", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: BRL (Brazilian Real)
|
||||||
|
// Supported alts: -
|
||||||
|
return doGet(MercadoBitcoinExchange.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.paribu.ParibuExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Paribu extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Paribu() {
|
||||||
|
super("PARIBU", "paribu", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: TRY (Turkish Lira)
|
||||||
|
// Supported alts: -
|
||||||
|
return doGet(ParibuExchange.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,76 +19,26 @@ package bisq.price.spot.providers;
|
||||||
|
|
||||||
import bisq.price.spot.ExchangeRate;
|
import bisq.price.spot.ExchangeRate;
|
||||||
import bisq.price.spot.ExchangeRateProvider;
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
import bisq.price.util.Altcoins;
|
|
||||||
|
|
||||||
import org.knowm.xchange.currency.Currency;
|
import org.knowm.xchange.poloniex.PoloniexExchange;
|
||||||
import org.knowm.xchange.currency.CurrencyPair;
|
|
||||||
import org.knowm.xchange.poloniex.dto.marketdata.PoloniexMarketData;
|
|
||||||
import org.knowm.xchange.poloniex.dto.marketdata.PoloniexTicker;
|
|
||||||
|
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
|
||||||
import org.springframework.core.annotation.Order;
|
|
||||||
import org.springframework.http.RequestEntity;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Order(4)
|
|
||||||
class Poloniex extends ExchangeRateProvider {
|
class Poloniex extends ExchangeRateProvider {
|
||||||
|
|
||||||
private final RestTemplate restTemplate = new RestTemplate();
|
|
||||||
|
|
||||||
public Poloniex() {
|
public Poloniex() {
|
||||||
super("POLO", "poloniex", Duration.ofMinutes(1));
|
super("POLO", "poloniex", Duration.ofMinutes(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<ExchangeRate> doGet() {
|
public Set<ExchangeRate> doGet() {
|
||||||
Date timestamp = new Date(); // Poloniex tickers don't include their own timestamp
|
// Supported fiat: -
|
||||||
|
// Supported alts: DASH, DCR, DOGE, ETC, ETH, LTC, XMR, ZEC
|
||||||
return getTickers()
|
return doGet(PoloniexExchange.class);
|
||||||
.filter(t -> t.getCurrencyPair().base.equals(Currency.BTC))
|
|
||||||
.filter(t -> Altcoins.ALL_SUPPORTED.contains(t.getCurrencyPair().counter.getCurrencyCode()))
|
|
||||||
.map(t ->
|
|
||||||
new ExchangeRate(
|
|
||||||
t.getCurrencyPair().counter.getCurrencyCode(),
|
|
||||||
t.getPoloniexMarketData().getLast(),
|
|
||||||
timestamp,
|
|
||||||
this.getName()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Stream<PoloniexTicker> getTickers() {
|
|
||||||
|
|
||||||
return getTickersKeyedByCurrencyPair().entrySet().stream()
|
|
||||||
.map(e -> {
|
|
||||||
String pair = e.getKey();
|
|
||||||
PoloniexMarketData data = e.getValue();
|
|
||||||
String[] symbols = pair.split("_"); // e.g. BTC_USD => [BTC, USD]
|
|
||||||
return new PoloniexTicker(data, new CurrencyPair(symbols[0], symbols[1]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, PoloniexMarketData> getTickersKeyedByCurrencyPair() {
|
|
||||||
return restTemplate.exchange(
|
|
||||||
RequestEntity
|
|
||||||
.get(UriComponentsBuilder
|
|
||||||
.fromUriString("https://poloniex.com/public?command=returnTicker").build()
|
|
||||||
.toUri())
|
|
||||||
.build(),
|
|
||||||
new ParameterizedTypeReference<Map<String, PoloniexMarketData>>() {
|
|
||||||
}
|
|
||||||
).getBody();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import org.knowm.xchange.quoine.QuoineExchange;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Quoine extends ExchangeRateProvider {
|
||||||
|
|
||||||
|
public Quoine() {
|
||||||
|
super("QUOINE", "quoine", Duration.ofMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ExchangeRate> doGet() {
|
||||||
|
// Supported fiat: AUD, CNY, EUR, HKD, IDR, INR, JPY, PHP, SGD, USD
|
||||||
|
// Supported alts: ETH
|
||||||
|
return doGet(QuoineExchange.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean requiresFilterDuringBulkTickerRetrieval() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package bisq.price.util.bitpay;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class BitpayMarketData {
|
||||||
|
|
||||||
|
private BitpayTicker[] data;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package bisq.price.util.bitpay;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class BitpayTicker {
|
||||||
|
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private BigDecimal rate;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package bisq.price.util.coingecko;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class CoinGeckoMarketData {
|
||||||
|
|
||||||
|
private Map<String, CoinGeckoTicker> rates;
|
||||||
|
|
||||||
|
public void setRates(Map<String, CoinGeckoTicker> rates) {
|
||||||
|
// Convert keys to uppercase ("usd" -> "USD") when deserializing API response
|
||||||
|
this.rates = rates.entrySet().stream()
|
||||||
|
.collect(Collectors.toMap(entry -> entry.getKey().toUpperCase(), entry -> entry.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package bisq.price.util.coingecko;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class CoinGeckoTicker {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String unit;
|
||||||
|
|
||||||
|
private BigDecimal value;
|
||||||
|
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package bisq.price.util.coinpaprika;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class CoinpaprikaMarketData {
|
||||||
|
|
||||||
|
// All other json fields can be ignored, we don't need them
|
||||||
|
|
||||||
|
private Map<String, CoinpaprikaTicker> quotes;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package bisq.price.util.coinpaprika;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class CoinpaprikaTicker {
|
||||||
|
|
||||||
|
// All other json fields can be ignored, we don't need them
|
||||||
|
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,6 @@ spring.jackson.serialization.indent_output=true
|
||||||
# and set it to hostname exposing the fee estimation API
|
# and set it to hostname exposing the fee estimation API
|
||||||
bisq.price.mining.providers.mempoolHostname.1=mempool.space
|
bisq.price.mining.providers.mempoolHostname.1=mempool.space
|
||||||
bisq.price.mining.providers.mempoolHostname.2=mempool.emzy.de
|
bisq.price.mining.providers.mempoolHostname.2=mempool.emzy.de
|
||||||
# bisq.price.mining.providers.mempoolHostname.3=someHostOrIP
|
bisq.price.mining.providers.mempoolHostname.3=mempool.ninja
|
||||||
# bisq.price.mining.providers.mempoolHostname.4=someHostOrIP
|
# bisq.price.mining.providers.mempoolHostname.4=someHostOrIP
|
||||||
# bisq.price.mining.providers.mempoolHostname.5=someHostOrIP
|
# bisq.price.mining.providers.mempoolHostname.5=someHostOrIP
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package bisq.price;
|
||||||
|
|
||||||
|
import bisq.price.spot.ExchangeRate;
|
||||||
|
import bisq.price.spot.ExchangeRateProvider;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public abstract class AbstractExchangeRateProviderTest {
|
||||||
|
|
||||||
|
protected void doGet_successfulCall(ExchangeRateProvider exchangeProvider) {
|
||||||
|
|
||||||
|
// Use the XChange library to call the provider API, in order to retrieve the
|
||||||
|
// exchange rates. If the API call fails, or the response body cannot be parsed,
|
||||||
|
// the test will fail with an exception
|
||||||
|
Set<ExchangeRate> retrievedExchangeRates = exchangeProvider.doGet();
|
||||||
|
|
||||||
|
// Log the valid exchange rates which were retrieved
|
||||||
|
// Useful when running the tests, to easily identify which exchanges provide
|
||||||
|
// useful pairs
|
||||||
|
retrievedExchangeRates.forEach(e -> log.info("Found exchange rate " + e.toString()));
|
||||||
|
|
||||||
|
// Sanity checks
|
||||||
|
assertTrue(retrievedExchangeRates.size() > 0);
|
||||||
|
checkProviderCurrencyPairs(retrievedExchangeRates);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that every retrieved currency pair is between BTC and either
|
||||||
|
* A) a fiat currency on the list of Bisq-supported fiat currencies, or
|
||||||
|
* B) an altcoin on the list of Bisq-supported altcoins
|
||||||
|
*
|
||||||
|
* @param retrievedExchangeRates Exchange rates retrieved from the provider
|
||||||
|
*/
|
||||||
|
private void checkProviderCurrencyPairs(Set<ExchangeRate> retrievedExchangeRates) {
|
||||||
|
Set<String> retrievedRatesCurrencies = retrievedExchangeRates.stream()
|
||||||
|
.map(ExchangeRate::getCurrency)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Set<String> supportedFiatCurrenciesRetrieved = ExchangeRateProvider.SUPPORTED_FIAT_CURRENCIES.stream()
|
||||||
|
.filter(f -> retrievedRatesCurrencies.contains(f))
|
||||||
|
.collect(Collectors.toCollection(TreeSet::new));
|
||||||
|
log.info("Retrieved rates for supported fiat currencies: " + supportedFiatCurrenciesRetrieved);
|
||||||
|
|
||||||
|
Set<String> supportedCryptoCurrenciesRetrieved = ExchangeRateProvider.SUPPORTED_CRYPTO_CURRENCIES.stream()
|
||||||
|
.filter(c -> retrievedRatesCurrencies.contains(c))
|
||||||
|
.collect(Collectors.toCollection(TreeSet::new));
|
||||||
|
log.info("Retrieved rates for supported altcoins: " + supportedCryptoCurrenciesRetrieved);
|
||||||
|
|
||||||
|
Set<String> supportedCurrencies = Sets.union(
|
||||||
|
ExchangeRateProvider.SUPPORTED_CRYPTO_CURRENCIES,
|
||||||
|
ExchangeRateProvider.SUPPORTED_FIAT_CURRENCIES);
|
||||||
|
|
||||||
|
Set unsupportedCurrencies = Sets.difference(retrievedRatesCurrencies, supportedCurrencies);
|
||||||
|
assertTrue("Retrieved exchange rates contain unsupported currencies: " + unsupportedCurrencies,
|
||||||
|
unsupportedCurrencies.isEmpty());
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,15 +17,22 @@
|
||||||
|
|
||||||
package bisq.price.spot;
|
package bisq.price.spot;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.apache.commons.lang3.RandomUtils;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.OptionalDouble;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -74,8 +81,7 @@ public class ExchangeRateServiceTest {
|
||||||
|
|
||||||
Map<String, Object> retrievedData = service.getAllMarketPrices();
|
Map<String, Object> retrievedData = service.getAllMarketPrices();
|
||||||
|
|
||||||
doSanityChecksForRetrievedDataSingleProvider(
|
doSanityChecksForRetrievedDataSingleProvider(retrievedData, dummyProvider, numberOfCurrencyPairsOnExchange);
|
||||||
retrievedData, dummyProvider.getPrefix(), numberOfCurrencyPairsOnExchange);
|
|
||||||
|
|
||||||
// No exchange rates provided by this exchange, two things should happen
|
// No exchange rates provided by this exchange, two things should happen
|
||||||
// A) the timestamp should be set to 0
|
// A) the timestamp should be set to 0
|
||||||
|
@ -101,15 +107,14 @@ public class ExchangeRateServiceTest {
|
||||||
|
|
||||||
Map<String, Object> retrievedData = service.getAllMarketPrices();
|
Map<String, Object> retrievedData = service.getAllMarketPrices();
|
||||||
|
|
||||||
doSanityChecksForRetrievedDataSingleProvider(
|
doSanityChecksForRetrievedDataSingleProvider(retrievedData, dummyProvider, numberOfCurrencyPairsOnExchange);
|
||||||
retrievedData, dummyProvider.getPrefix(), numberOfCurrencyPairsOnExchange);
|
|
||||||
|
|
||||||
// One rate was provided by this provider, so the timestamp should not be 0
|
// One rate was provided by this provider, so the timestamp should not be 0
|
||||||
assertNotEquals(0L, retrievedData.get(dummyProvider.getPrefix() + "Ts"));
|
assertNotEquals(0L, retrievedData.get(dummyProvider.getPrefix() + "Ts"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAllMarketPrices_withMultipleProviders() {
|
public void getAllMarketPrices_withMultipleProviders_differentCurrencyCodes() {
|
||||||
int numberOfCurrencyPairsOnExchange = 1;
|
int numberOfCurrencyPairsOnExchange = 1;
|
||||||
ExchangeRateProvider dummyProvider1 = buildDummyExchangeRateProvider(numberOfCurrencyPairsOnExchange);
|
ExchangeRateProvider dummyProvider1 = buildDummyExchangeRateProvider(numberOfCurrencyPairsOnExchange);
|
||||||
ExchangeRateProvider dummyProvider2 = buildDummyExchangeRateProvider(numberOfCurrencyPairsOnExchange);
|
ExchangeRateProvider dummyProvider2 = buildDummyExchangeRateProvider(numberOfCurrencyPairsOnExchange);
|
||||||
|
@ -117,8 +122,7 @@ public class ExchangeRateServiceTest {
|
||||||
|
|
||||||
Map<String, Object> retrievedData = service.getAllMarketPrices();
|
Map<String, Object> retrievedData = service.getAllMarketPrices();
|
||||||
|
|
||||||
doSanityChecksForRetrievedDataMultipleProviders(retrievedData,
|
doSanityChecksForRetrievedDataMultipleProviders(retrievedData, asList(dummyProvider1, dummyProvider2));
|
||||||
asList(dummyProvider1.getPrefix(), dummyProvider2.getPrefix()));
|
|
||||||
|
|
||||||
// One rate was provided by each provider in this service, so the timestamp
|
// One rate was provided by each provider in this service, so the timestamp
|
||||||
// (for both providers) should not be 0
|
// (for both providers) should not be 0
|
||||||
|
@ -126,21 +130,49 @@ public class ExchangeRateServiceTest {
|
||||||
assertNotEquals(0L, retrievedData.get(dummyProvider2.getPrefix() + "Ts"));
|
assertNotEquals(0L, retrievedData.get(dummyProvider2.getPrefix() + "Ts"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the scenario when multiple providers have rates for the same currencies
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void getAllMarketPrices_withMultipleProviders_overlappingCurrencyCodes() {
|
||||||
|
|
||||||
|
// List of currencies for which multiple providers will have exchange rates
|
||||||
|
Set<String> rateCurrencyCodes = Sets.newHashSet("CURRENCY-1", "CURRENCY-2", "CURRENCY-3");
|
||||||
|
|
||||||
|
// Create several dummy providers, each providing their own rates for the same set of currencies
|
||||||
|
ExchangeRateProvider dummyProvider1 = buildDummyExchangeRateProvider(rateCurrencyCodes);
|
||||||
|
ExchangeRateProvider dummyProvider2 = buildDummyExchangeRateProvider(rateCurrencyCodes);
|
||||||
|
|
||||||
|
ExchangeRateService service = new ExchangeRateService(asList(dummyProvider1, dummyProvider2));
|
||||||
|
|
||||||
|
Map<String, Object> retrievedData = service.getAllMarketPrices();
|
||||||
|
|
||||||
|
doSanityChecksForRetrievedDataMultipleProviders(retrievedData, asList(dummyProvider1, dummyProvider2));
|
||||||
|
|
||||||
|
// At least one rate was provided by each provider in this service, so the
|
||||||
|
// timestamp (for both providers) should not be 0
|
||||||
|
assertNotEquals(0L, retrievedData.get(dummyProvider1.getPrefix() + "Ts"));
|
||||||
|
assertNotEquals(0L, retrievedData.get(dummyProvider2.getPrefix() + "Ts"));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs generic sanity checks on the response format and contents.
|
* Performs generic sanity checks on the response format and contents.
|
||||||
*
|
*
|
||||||
* @param retrievedData Response data retrieved from the {@link ExchangeRateService}
|
* @param retrievedData Response data retrieved from the {@link ExchangeRateService}
|
||||||
* @param providerPrefix {@link ExchangeRateProvider#getPrefix()}
|
* @param provider {@link ExchangeRateProvider} available to the
|
||||||
* @param numberOfCurrencyPairsOnExchange Number of currency pairs this exchange was initiated with
|
* {@link ExchangeRateService}
|
||||||
|
* @param numberOfCurrencyPairsOnExchange Number of currency pairs this exchange was
|
||||||
|
* initiated with
|
||||||
*/
|
*/
|
||||||
private void doSanityChecksForRetrievedDataSingleProvider(Map<String, Object> retrievedData,
|
private void doSanityChecksForRetrievedDataSingleProvider(Map<String, Object> retrievedData,
|
||||||
String providerPrefix,
|
ExchangeRateProvider provider,
|
||||||
int numberOfCurrencyPairsOnExchange) {
|
int numberOfCurrencyPairsOnExchange) {
|
||||||
// Check response structure
|
// Check response structure
|
||||||
doSanityChecksForRetrievedDataMultipleProviders(retrievedData, asList(providerPrefix));
|
doSanityChecksForRetrievedDataMultipleProviders(retrievedData, asList(provider));
|
||||||
|
|
||||||
// Check that the amount of provided exchange rates matches expected value
|
// Check that the amount of provided exchange rates matches expected value
|
||||||
// For one provider, the amount of rates of that provider should be the total amount of rates in the response
|
// For one provider, the amount of rates of that provider should be the total
|
||||||
|
// amount of rates in the response
|
||||||
List<String> retrievedMarketPricesData = (List<String>) retrievedData.get("data");
|
List<String> retrievedMarketPricesData = (List<String>) retrievedData.get("data");
|
||||||
assertEquals(numberOfCurrencyPairsOnExchange, retrievedMarketPricesData.size());
|
assertEquals(numberOfCurrencyPairsOnExchange, retrievedMarketPricesData.size());
|
||||||
}
|
}
|
||||||
|
@ -149,26 +181,81 @@ public class ExchangeRateServiceTest {
|
||||||
* Performs generic sanity checks on the response format and contents.
|
* Performs generic sanity checks on the response format and contents.
|
||||||
*
|
*
|
||||||
* @param retrievedData Response data retrieved from the {@link ExchangeRateService}
|
* @param retrievedData Response data retrieved from the {@link ExchangeRateService}
|
||||||
* @param providerPrefixes List of all {@link ExchangeRateProvider#getPrefix()} the
|
* @param providers List of all {@link ExchangeRateProvider#getPrefix()} the
|
||||||
* {@link ExchangeRateService} uses
|
* {@link ExchangeRateService} uses
|
||||||
*/
|
*/
|
||||||
private void doSanityChecksForRetrievedDataMultipleProviders(Map<String, Object> retrievedData,
|
private void doSanityChecksForRetrievedDataMultipleProviders(Map<String, Object> retrievedData,
|
||||||
List<String> providerPrefixes) {
|
List<ExchangeRateProvider> providers) {
|
||||||
// Check the correct amount of entries were present in the service response:
|
// Check the correct amount of entries were present in the service response:
|
||||||
// The timestamp and the count fields are per provider, so N providers means N
|
// The timestamp and the count fields are per provider, so N providers means N
|
||||||
// times those fields timestamp (x N) + count (x N) + price data (stored as a list
|
// times those fields timestamp (x N) + count (x N) + price data (stored as a list
|
||||||
// under the key "data"). So expected size is Nx2 + 1.
|
// under the key "data"). So expected size is Nx2 + 1.
|
||||||
int n = providerPrefixes.size();
|
int n = providers.size();
|
||||||
assertEquals(n * 2 + 1, retrievedData.size());
|
assertEquals(n * 2 + 1, retrievedData.size());
|
||||||
for (String providerPrefix : providerPrefixes) {
|
for (ExchangeRateProvider provider : providers) {
|
||||||
|
String providerPrefix = provider.getPrefix();
|
||||||
assertNotNull(retrievedData.get(providerPrefix + "Ts"));
|
assertNotNull(retrievedData.get(providerPrefix + "Ts"));
|
||||||
assertNotNull(retrievedData.get(providerPrefix + "Count"));
|
assertNotNull(retrievedData.get(providerPrefix + "Count"));
|
||||||
}
|
}
|
||||||
assertNotNull(retrievedData.get("data"));
|
|
||||||
|
|
||||||
// TODO Add checks for the case when rates for the same currency pair is retrieved from multiple providers
|
// Check validity of the data field
|
||||||
|
List<ExchangeRate> retrievedRates = (List<ExchangeRate>) retrievedData.get("data");
|
||||||
|
assertNotNull(retrievedRates);
|
||||||
|
|
||||||
|
// It should contain no duplicate ExchangeRate objects
|
||||||
|
int uniqueRates = Sets.newHashSet(retrievedRates).size();
|
||||||
|
int totalRates = retrievedRates.size();
|
||||||
|
assertEquals(uniqueRates, totalRates, "Found duplicate rates in data field");
|
||||||
|
|
||||||
|
// There should be only one ExchangeRate per currency
|
||||||
|
// In other words, even if multiple providers return rates for the same currency,
|
||||||
|
// the ExchangeRateService should expose only one (aggregate) ExchangeRate for
|
||||||
|
// that currency
|
||||||
|
Map<String, ExchangeRate> currencyCodeToExchangeRateFromService = retrievedRates.stream()
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
ExchangeRate::getCurrency, exchangeRate -> exchangeRate
|
||||||
|
));
|
||||||
|
int uniqueCurrencyCodes = currencyCodeToExchangeRateFromService.keySet().size();
|
||||||
|
assertEquals(uniqueCurrencyCodes, uniqueRates, "Found currency code with multiple exchange rates");
|
||||||
|
|
||||||
|
// Collect all ExchangeRates from all providers and group them by currency code
|
||||||
|
Map<String, List<ExchangeRate>> currencyCodeToExchangeRatesFromProviders = new HashMap<>();
|
||||||
|
for (ExchangeRateProvider p : providers) {
|
||||||
|
for (ExchangeRate exchangeRate : p.get()) {
|
||||||
|
String currencyCode = exchangeRate.getCurrency();
|
||||||
|
if (currencyCodeToExchangeRatesFromProviders.containsKey(currencyCode)) {
|
||||||
|
List<ExchangeRate> l = new ArrayList<>(currencyCodeToExchangeRatesFromProviders.get(currencyCode));
|
||||||
|
l.add(exchangeRate);
|
||||||
|
currencyCodeToExchangeRatesFromProviders.put(currencyCode, l);
|
||||||
|
} else {
|
||||||
|
currencyCodeToExchangeRatesFromProviders.put(currencyCode, asList(exchangeRate));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For each ExchangeRate which is covered by multiple providers, ensure the rate
|
||||||
|
// value is an average
|
||||||
|
currencyCodeToExchangeRatesFromProviders.forEach((currencyCode, exchangeRateList) -> {
|
||||||
|
ExchangeRate rateFromService = currencyCodeToExchangeRateFromService.get(currencyCode);
|
||||||
|
double priceFromService = rateFromService.getPrice();
|
||||||
|
|
||||||
|
OptionalDouble opt = exchangeRateList.stream().mapToDouble(ExchangeRate::getPrice).average();
|
||||||
|
double priceAvgFromProviders = opt.getAsDouble();
|
||||||
|
|
||||||
|
// Ensure that the ExchangeRateService correctly aggregates exchange rates
|
||||||
|
// from multiple providers. If multiple providers contain rates for a
|
||||||
|
// currency, the service should return a single aggregate rate
|
||||||
|
// Expected value for aggregate rate = avg(provider rates)
|
||||||
|
// This formula works for any number of providers for a specific currency
|
||||||
|
assertEquals(priceFromService, priceAvgFromProviders, "Service returned incorrect aggregate rate");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param numberOfRatesAvailable Number of exchange rates this provider returns
|
||||||
|
* @return Dummy {@link ExchangeRateProvider} providing rates for
|
||||||
|
* "numberOfRatesAvailable" random currency codes
|
||||||
|
*/
|
||||||
private ExchangeRateProvider buildDummyExchangeRateProvider(int numberOfRatesAvailable) {
|
private ExchangeRateProvider buildDummyExchangeRateProvider(int numberOfRatesAvailable) {
|
||||||
ExchangeRateProvider dummyProvider = new ExchangeRateProvider(
|
ExchangeRateProvider dummyProvider = new ExchangeRateProvider(
|
||||||
"ExchangeName-" + getRandomAlphaNumericString(5),
|
"ExchangeName-" + getRandomAlphaNumericString(5),
|
||||||
|
@ -187,8 +274,44 @@ public class ExchangeRateServiceTest {
|
||||||
// Simulate the required amount of rates
|
// Simulate the required amount of rates
|
||||||
for (int i = 0; i < numberOfRatesAvailable; i++) {
|
for (int i = 0; i < numberOfRatesAvailable; i++) {
|
||||||
exchangeRates.add(new ExchangeRate(
|
exchangeRates.add(new ExchangeRate(
|
||||||
"DUM-" + getRandomAlphaNumericString(3), // random symbol, avoid duplicates
|
// random symbol, avoid duplicates
|
||||||
0,
|
"DUM-" + getRandomAlphaNumericString(3),
|
||||||
|
RandomUtils.nextDouble(1, 1000), // random price
|
||||||
|
System.currentTimeMillis(),
|
||||||
|
getName())); // ExchangeRateProvider name
|
||||||
|
}
|
||||||
|
|
||||||
|
return exchangeRates;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize provider
|
||||||
|
dummyProvider.start();
|
||||||
|
dummyProvider.stop();
|
||||||
|
|
||||||
|
return dummyProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExchangeRateProvider buildDummyExchangeRateProvider(Set<String> rateCurrencyCodes) {
|
||||||
|
ExchangeRateProvider dummyProvider = new ExchangeRateProvider(
|
||||||
|
"ExchangeName-" + getRandomAlphaNumericString(5),
|
||||||
|
"EXCH-" + getRandomAlphaNumericString(3),
|
||||||
|
Duration.ofDays(1)) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRunning() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Set<ExchangeRate> doGet() {
|
||||||
|
HashSet<ExchangeRate> exchangeRates = new HashSet<>();
|
||||||
|
|
||||||
|
// Simulate the required amount of rates
|
||||||
|
for (String rateCurrencyCode : rateCurrencyCodes) {
|
||||||
|
exchangeRates.add(new ExchangeRate(
|
||||||
|
rateCurrencyCode,
|
||||||
|
RandomUtils.nextDouble(1, 1000), // random price
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
getName())); // ExchangeRateProvider name
|
getName())); // ExchangeRateProvider name
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.AbstractExchangeRateProviderTest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class BTCMarketsTest extends AbstractExchangeRateProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doGet_successfulCall() {
|
||||||
|
doGet_successfulCall(new BTCMarkets());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.AbstractExchangeRateProviderTest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class BinanceTest extends AbstractExchangeRateProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doGet_successfulCall() {
|
||||||
|
doGet_successfulCall(new Binance());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.AbstractExchangeRateProviderTest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class BitbayTest extends AbstractExchangeRateProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doGet_successfulCall() {
|
||||||
|
doGet_successfulCall(new Bitbay());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.AbstractExchangeRateProviderTest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class BitfinexTest extends AbstractExchangeRateProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doGet_successfulCall() {
|
||||||
|
doGet_successfulCall(new Bitfinex());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.AbstractExchangeRateProviderTest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class BitflyerTest extends AbstractExchangeRateProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doGet_successfulCall() {
|
||||||
|
doGet_successfulCall(new Bitflyer());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.AbstractExchangeRateProviderTest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class BitpayTest extends AbstractExchangeRateProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doGet_successfulCall() {
|
||||||
|
doGet_successfulCall(new Bitpay());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.AbstractExchangeRateProviderTest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class BitstampTest extends AbstractExchangeRateProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doGet_successfulCall() {
|
||||||
|
doGet_successfulCall(new Bitstamp());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.AbstractExchangeRateProviderTest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class CexIOTest extends AbstractExchangeRateProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doGet_successfulCall() {
|
||||||
|
doGet_successfulCall(new CexIO());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.AbstractExchangeRateProviderTest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class CoinGeckoTest extends AbstractExchangeRateProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doGet_successfulCall() {
|
||||||
|
doGet_successfulCall(new CoinGecko());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.AbstractExchangeRateProviderTest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class CoinmateTest extends AbstractExchangeRateProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doGet_successfulCall() {
|
||||||
|
doGet_successfulCall(new Coinmate());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.price.spot.providers;
|
||||||
|
|
||||||
|
import bisq.price.AbstractExchangeRateProviderTest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class CoinoneTest extends AbstractExchangeRateProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doGet_successfulCall() {
|
||||||
|
doGet_successfulCall(new Coinone());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue