mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-05-20 07:20:41 -04:00
Bisq
This commit is contained in:
commit
8a38081c04
2800 changed files with 344130 additions and 0 deletions
817
cli/src/main/java/bisq/cli/CliMain.java
Normal file
817
cli/src/main/java/bisq/cli/CliMain.java
Normal file
|
@ -0,0 +1,817 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.formatMarketPrice;
|
||||
import static bisq.cli.CurrencyFormat.formatTxFeeRateInfo;
|
||||
import static bisq.cli.CurrencyFormat.toSatoshis;
|
||||
import static bisq.cli.CurrencyFormat.toSecurityDepositAsPct;
|
||||
import static bisq.cli.Method.*;
|
||||
import static bisq.cli.TableFormat.*;
|
||||
import static bisq.cli.opts.OptLabel.*;
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.err;
|
||||
import static java.lang.System.exit;
|
||||
import static java.lang.System.out;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.opts.ArgumentList;
|
||||
import bisq.cli.opts.CancelOfferOptionParser;
|
||||
import bisq.cli.opts.CreateCryptoCurrencyPaymentAcctOptionParser;
|
||||
import bisq.cli.opts.CreateOfferOptionParser;
|
||||
import bisq.cli.opts.CreatePaymentAcctOptionParser;
|
||||
import bisq.cli.opts.GetAddressBalanceOptionParser;
|
||||
import bisq.cli.opts.GetBTCMarketPriceOptionParser;
|
||||
import bisq.cli.opts.GetBalanceOptionParser;
|
||||
import bisq.cli.opts.GetOfferOptionParser;
|
||||
import bisq.cli.opts.GetOffersOptionParser;
|
||||
import bisq.cli.opts.GetPaymentAcctFormOptionParser;
|
||||
import bisq.cli.opts.GetTradeOptionParser;
|
||||
import bisq.cli.opts.GetTransactionOptionParser;
|
||||
import bisq.cli.opts.RegisterDisputeAgentOptionParser;
|
||||
import bisq.cli.opts.RemoveWalletPasswordOptionParser;
|
||||
import bisq.cli.opts.SendBsqOptionParser;
|
||||
import bisq.cli.opts.SendBtcOptionParser;
|
||||
import bisq.cli.opts.SetTxFeeRateOptionParser;
|
||||
import bisq.cli.opts.SetWalletPasswordOptionParser;
|
||||
import bisq.cli.opts.SimpleMethodOptionParser;
|
||||
import bisq.cli.opts.TakeOfferOptionParser;
|
||||
import bisq.cli.opts.UnlockWalletOptionParser;
|
||||
import bisq.cli.opts.VerifyBsqSentToAddressOptionParser;
|
||||
import bisq.cli.opts.WithdrawFundsOptionParser;
|
||||
|
||||
/**
|
||||
* A command-line client for the Bisq gRPC API.
|
||||
*/
|
||||
@Slf4j
|
||||
public class CliMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
run(args);
|
||||
} catch (Throwable t) {
|
||||
err.println("Error: " + t.getMessage());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static void run(String[] args) {
|
||||
var parser = new OptionParser();
|
||||
|
||||
var helpOpt = parser.accepts(OPT_HELP, "Print this help text")
|
||||
.forHelp();
|
||||
|
||||
var hostOpt = parser.accepts(OPT_HOST, "rpc server hostname or ip")
|
||||
.withRequiredArg()
|
||||
.defaultsTo("localhost");
|
||||
|
||||
var portOpt = parser.accepts(OPT_PORT, "rpc server port")
|
||||
.withRequiredArg()
|
||||
.ofType(Integer.class)
|
||||
.defaultsTo(9998);
|
||||
|
||||
var passwordOpt = parser.accepts(OPT_PASSWORD, "rpc server password")
|
||||
.withRequiredArg();
|
||||
|
||||
// Parse the CLI opts host, port, password, method name, and help. The help opt
|
||||
// may indicate the user is asking for method level help, and will be excluded
|
||||
// from the parsed options if a method opt is present in String[] args.
|
||||
OptionSet options = parser.parse(new ArgumentList(args).getCLIArguments());
|
||||
@SuppressWarnings("unchecked")
|
||||
var nonOptionArgs = (List<String>) options.nonOptionArguments();
|
||||
|
||||
// If neither the help opt nor a method name is present, print CLI level help
|
||||
// to stderr and throw an exception.
|
||||
if (!options.has(helpOpt) && nonOptionArgs.isEmpty()) {
|
||||
printHelp(parser, err);
|
||||
throw new IllegalArgumentException("no method specified");
|
||||
}
|
||||
|
||||
// If the help opt is present, but not a method name, print CLI level help
|
||||
// to stdout.
|
||||
if (options.has(helpOpt) && nonOptionArgs.isEmpty()) {
|
||||
printHelp(parser, out);
|
||||
return;
|
||||
}
|
||||
|
||||
var host = options.valueOf(hostOpt);
|
||||
var port = options.valueOf(portOpt);
|
||||
var password = options.valueOf(passwordOpt);
|
||||
if (password == null)
|
||||
throw new IllegalArgumentException("missing required 'password' option");
|
||||
|
||||
var methodName = nonOptionArgs.get(0);
|
||||
Method method;
|
||||
try {
|
||||
method = getMethodFromCmd(methodName);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new IllegalArgumentException(format("'%s' is not a supported method", methodName));
|
||||
}
|
||||
|
||||
GrpcClient client = new GrpcClient(host, port, password);
|
||||
try {
|
||||
switch (method) {
|
||||
case getversion: {
|
||||
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var version = client.getVersion();
|
||||
out.println(version);
|
||||
return;
|
||||
}
|
||||
case getbalance: {
|
||||
var opts = new GetBalanceOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var currencyCode = opts.getCurrencyCode();
|
||||
var balances = client.getBalances(currencyCode);
|
||||
switch (currencyCode.toUpperCase()) {
|
||||
case "BSQ":
|
||||
out.println(formatBsqBalanceInfoTbl(balances.getBsq()));
|
||||
break;
|
||||
case "BTC":
|
||||
out.println(formatBtcBalanceInfoTbl(balances.getBtc()));
|
||||
break;
|
||||
case "":
|
||||
default:
|
||||
out.println(formatBalancesTbls(balances));
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case getaddressbalance: {
|
||||
var opts = new GetAddressBalanceOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var address = opts.getAddress();
|
||||
var addressBalance = client.getAddressBalance(address);
|
||||
out.println(formatAddressBalanceTbl(singletonList(addressBalance)));
|
||||
return;
|
||||
}
|
||||
case getbtcprice: {
|
||||
var opts = new GetBTCMarketPriceOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var currencyCode = opts.getCurrencyCode();
|
||||
var price = client.getBtcPrice(currencyCode);
|
||||
out.println(formatMarketPrice(price));
|
||||
return;
|
||||
}
|
||||
case getfundingaddresses: {
|
||||
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var fundingAddresses = client.getFundingAddresses();
|
||||
out.println(formatAddressBalanceTbl(fundingAddresses));
|
||||
return;
|
||||
}
|
||||
case getunusedbsqaddress: {
|
||||
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var address = client.getUnusedBsqAddress();
|
||||
out.println(address);
|
||||
return;
|
||||
}
|
||||
case sendbsq: {
|
||||
var opts = new SendBsqOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var address = opts.getAddress();
|
||||
var amount = opts.getAmount();
|
||||
verifyStringIsValidDecimal(OPT_AMOUNT, amount);
|
||||
|
||||
var txFeeRate = opts.getFeeRate();
|
||||
if (!txFeeRate.isEmpty())
|
||||
verifyStringIsValidLong(OPT_TX_FEE_RATE, txFeeRate);
|
||||
|
||||
var txInfo = client.sendBsq(address, amount, txFeeRate);
|
||||
out.printf("%s bsq sent to %s in tx %s%n",
|
||||
amount,
|
||||
address,
|
||||
txInfo.getTxId());
|
||||
return;
|
||||
}
|
||||
case sendbtc: {
|
||||
var opts = new SendBtcOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var address = opts.getAddress();
|
||||
var amount = opts.getAmount();
|
||||
verifyStringIsValidDecimal(OPT_AMOUNT, amount);
|
||||
|
||||
var txFeeRate = opts.getFeeRate();
|
||||
if (!txFeeRate.isEmpty())
|
||||
verifyStringIsValidLong(OPT_TX_FEE_RATE, txFeeRate);
|
||||
|
||||
var memo = opts.getMemo();
|
||||
|
||||
var txInfo = client.sendBtc(address, amount, txFeeRate, memo);
|
||||
out.printf("%s btc sent to %s in tx %s%n",
|
||||
amount,
|
||||
address,
|
||||
txInfo.getTxId());
|
||||
return;
|
||||
}
|
||||
case verifybsqsenttoaddress: {
|
||||
var opts = new VerifyBsqSentToAddressOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var address = opts.getAddress();
|
||||
var amount = opts.getAmount();
|
||||
verifyStringIsValidDecimal(OPT_AMOUNT, amount);
|
||||
|
||||
var bsqWasSent = client.verifyBsqSentToAddress(address, amount);
|
||||
out.printf("%s bsq %s sent to address %s%n",
|
||||
amount,
|
||||
bsqWasSent ? "has been" : "has not been",
|
||||
address);
|
||||
return;
|
||||
}
|
||||
case gettxfeerate: {
|
||||
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var txFeeRate = client.getTxFeeRate();
|
||||
out.println(formatTxFeeRateInfo(txFeeRate));
|
||||
return;
|
||||
}
|
||||
case settxfeerate: {
|
||||
var opts = new SetTxFeeRateOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var txFeeRate = client.setTxFeeRate(toLong(opts.getFeeRate()));
|
||||
out.println(formatTxFeeRateInfo(txFeeRate));
|
||||
return;
|
||||
}
|
||||
case unsettxfeerate: {
|
||||
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var txFeeRate = client.unsetTxFeeRate();
|
||||
out.println(formatTxFeeRateInfo(txFeeRate));
|
||||
return;
|
||||
}
|
||||
case gettransaction: {
|
||||
var opts = new GetTransactionOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var txId = opts.getTxId();
|
||||
var tx = client.getTransaction(txId);
|
||||
out.println(TransactionFormat.format(tx));
|
||||
return;
|
||||
}
|
||||
case createoffer: {
|
||||
var opts = new CreateOfferOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var paymentAcctId = opts.getPaymentAccountId();
|
||||
var direction = opts.getDirection();
|
||||
var currencyCode = opts.getCurrencyCode();
|
||||
var amount = toSatoshis(opts.getAmount());
|
||||
var minAmount = toSatoshis(opts.getMinAmount());
|
||||
var useMarketBasedPrice = opts.isUsingMktPriceMargin();
|
||||
var fixedPrice = opts.getFixedPrice();
|
||||
var marketPriceMargin = opts.getMktPriceMarginAsBigDecimal();
|
||||
var securityDeposit = toSecurityDepositAsPct(opts.getSecurityDeposit());
|
||||
var makerFeeCurrencyCode = opts.getMakerFeeCurrencyCode();
|
||||
var offer = client.createOffer(direction,
|
||||
currencyCode,
|
||||
amount,
|
||||
minAmount,
|
||||
useMarketBasedPrice,
|
||||
fixedPrice,
|
||||
marketPriceMargin.doubleValue(),
|
||||
securityDeposit,
|
||||
paymentAcctId,
|
||||
makerFeeCurrencyCode);
|
||||
out.println(formatOfferTable(singletonList(offer), currencyCode));
|
||||
return;
|
||||
}
|
||||
case canceloffer: {
|
||||
var opts = new CancelOfferOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var offerId = opts.getOfferId();
|
||||
client.cancelOffer(offerId);
|
||||
out.println("offer canceled and removed from offer book");
|
||||
return;
|
||||
}
|
||||
case getoffer: {
|
||||
var opts = new GetOfferOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var offerId = opts.getOfferId();
|
||||
var offer = client.getOffer(offerId);
|
||||
out.println(formatOfferTable(singletonList(offer), offer.getCounterCurrencyCode()));
|
||||
return;
|
||||
}
|
||||
case getmyoffer: {
|
||||
var opts = new GetOfferOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var offerId = opts.getOfferId();
|
||||
var offer = client.getMyOffer(offerId);
|
||||
out.println(formatOfferTable(singletonList(offer), offer.getCounterCurrencyCode()));
|
||||
return;
|
||||
}
|
||||
case getoffers: {
|
||||
var opts = new GetOffersOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var direction = opts.getDirection();
|
||||
var currencyCode = opts.getCurrencyCode();
|
||||
List<OfferInfo> offers = client.getOffers(direction, currencyCode);
|
||||
if (offers.isEmpty())
|
||||
out.printf("no %s %s offers found%n", direction, currencyCode);
|
||||
else
|
||||
out.println(formatOfferTable(offers, currencyCode));
|
||||
|
||||
return;
|
||||
}
|
||||
case getmyoffers: {
|
||||
var opts = new GetOffersOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var direction = opts.getDirection();
|
||||
var currencyCode = opts.getCurrencyCode();
|
||||
List<OfferInfo> offers = client.getMyOffers(direction, currencyCode);
|
||||
if (offers.isEmpty())
|
||||
out.printf("no %s %s offers found%n", direction, currencyCode);
|
||||
else
|
||||
out.println(formatOfferTable(offers, currencyCode));
|
||||
|
||||
return;
|
||||
}
|
||||
case takeoffer: {
|
||||
var opts = new TakeOfferOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var offerId = opts.getOfferId();
|
||||
var paymentAccountId = opts.getPaymentAccountId();
|
||||
var takerFeeCurrencyCode = opts.getTakerFeeCurrencyCode();
|
||||
var trade = client.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode);
|
||||
out.printf("trade %s successfully taken%n", trade.getTradeId());
|
||||
return;
|
||||
}
|
||||
case gettrade: {
|
||||
// TODO make short-id a valid argument?
|
||||
var opts = new GetTradeOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var tradeId = opts.getTradeId();
|
||||
var showContract = opts.getShowContract();
|
||||
var trade = client.getTrade(tradeId);
|
||||
if (showContract)
|
||||
out.println(trade.getContractAsJson());
|
||||
else
|
||||
out.println(TradeFormat.format(trade));
|
||||
|
||||
return;
|
||||
}
|
||||
case confirmpaymentstarted: {
|
||||
var opts = new GetTradeOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var tradeId = opts.getTradeId();
|
||||
client.confirmPaymentStarted(tradeId);
|
||||
out.printf("trade %s payment started message sent%n", tradeId);
|
||||
return;
|
||||
}
|
||||
case confirmpaymentreceived: {
|
||||
var opts = new GetTradeOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var tradeId = opts.getTradeId();
|
||||
client.confirmPaymentReceived(tradeId);
|
||||
out.printf("trade %s payment received message sent%n", tradeId);
|
||||
return;
|
||||
}
|
||||
case keepfunds: {
|
||||
var opts = new GetTradeOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var tradeId = opts.getTradeId();
|
||||
client.keepFunds(tradeId);
|
||||
out.printf("funds from trade %s saved in bisq wallet%n", tradeId);
|
||||
return;
|
||||
}
|
||||
case withdrawfunds: {
|
||||
var opts = new WithdrawFundsOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var tradeId = opts.getTradeId();
|
||||
var address = opts.getAddress();
|
||||
// Multi-word memos must be double quoted.
|
||||
var memo = opts.getMemo();
|
||||
client.withdrawFunds(tradeId, address, memo);
|
||||
out.printf("trade %s funds sent to btc address %s%n", tradeId, address);
|
||||
return;
|
||||
}
|
||||
case getpaymentmethods: {
|
||||
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var paymentMethods = client.getPaymentMethods();
|
||||
paymentMethods.forEach(p -> out.println(p.getId()));
|
||||
return;
|
||||
}
|
||||
case getpaymentacctform: {
|
||||
var opts = new GetPaymentAcctFormOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var paymentMethodId = opts.getPaymentMethodId();
|
||||
String jsonString = client.getPaymentAcctFormAsJson(paymentMethodId);
|
||||
File jsonFile = saveFileToDisk(paymentMethodId.toLowerCase(),
|
||||
".json",
|
||||
jsonString);
|
||||
out.printf("payment account form %s%nsaved to %s%n",
|
||||
jsonString, jsonFile.getAbsolutePath());
|
||||
out.println("Edit the file, and use as the argument to a 'createpaymentacct' command.");
|
||||
return;
|
||||
}
|
||||
case createpaymentacct: {
|
||||
var opts = new CreatePaymentAcctOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var paymentAccountForm = opts.getPaymentAcctForm();
|
||||
String jsonString;
|
||||
try {
|
||||
jsonString = new String(Files.readAllBytes(paymentAccountForm));
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(
|
||||
format("could not read %s", paymentAccountForm));
|
||||
}
|
||||
var paymentAccount = client.createPaymentAccount(jsonString);
|
||||
out.println("payment account saved");
|
||||
out.println(formatPaymentAcctTbl(singletonList(paymentAccount)));
|
||||
return;
|
||||
}
|
||||
case createcryptopaymentacct: {
|
||||
var opts = new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var accountName = opts.getAccountName();
|
||||
var currencyCode = opts.getCurrencyCode();
|
||||
var address = opts.getAddress();
|
||||
var isTradeInstant = opts.getIsTradeInstant();
|
||||
var paymentAccount = client.createCryptoCurrencyPaymentAccount(accountName,
|
||||
currencyCode,
|
||||
address,
|
||||
isTradeInstant);
|
||||
out.println("payment account saved");
|
||||
out.println(formatPaymentAcctTbl(singletonList(paymentAccount)));
|
||||
return;
|
||||
}
|
||||
case getpaymentaccts: {
|
||||
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var paymentAccounts = client.getPaymentAccounts();
|
||||
if (paymentAccounts.size() > 0)
|
||||
out.println(formatPaymentAcctTbl(paymentAccounts));
|
||||
else
|
||||
out.println("no payment accounts are saved");
|
||||
|
||||
return;
|
||||
}
|
||||
case lockwallet: {
|
||||
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
client.lockWallet();
|
||||
out.println("wallet locked");
|
||||
return;
|
||||
}
|
||||
case unlockwallet: {
|
||||
var opts = new UnlockWalletOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var walletPassword = opts.getPassword();
|
||||
var timeout = opts.getUnlockTimeout();
|
||||
client.unlockWallet(walletPassword, timeout);
|
||||
out.println("wallet unlocked");
|
||||
return;
|
||||
}
|
||||
case removewalletpassword: {
|
||||
var opts = new RemoveWalletPasswordOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var walletPassword = opts.getPassword();
|
||||
client.removeWalletPassword(walletPassword);
|
||||
out.println("wallet decrypted");
|
||||
return;
|
||||
}
|
||||
case setwalletpassword: {
|
||||
var opts = new SetWalletPasswordOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var walletPassword = opts.getPassword();
|
||||
var newWalletPassword = opts.getNewPassword();
|
||||
client.setWalletPassword(walletPassword, newWalletPassword);
|
||||
out.println("wallet encrypted" + (!newWalletPassword.isEmpty() ? " with new password" : ""));
|
||||
return;
|
||||
}
|
||||
case registerdisputeagent: {
|
||||
var opts = new RegisterDisputeAgentOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var disputeAgentType = opts.getDisputeAgentType();
|
||||
var registrationKey = opts.getRegistrationKey();
|
||||
client.registerDisputeAgent(disputeAgentType, registrationKey);
|
||||
out.println(disputeAgentType + " registered");
|
||||
return;
|
||||
}
|
||||
case stop: {
|
||||
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
client.stopServer();
|
||||
out.println("server shutdown signal received");
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
throw new RuntimeException(format("unhandled method '%s'", method));
|
||||
}
|
||||
}
|
||||
} catch (StatusRuntimeException ex) {
|
||||
// Remove the leading gRPC status code (e.g. "UNKNOWN: ") from the message
|
||||
String message = ex.getMessage().replaceFirst("^[A-Z_]+: ", "");
|
||||
if (message.equals("io exception"))
|
||||
throw new RuntimeException(message + ", server may not be running", ex);
|
||||
else
|
||||
throw new RuntimeException(message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Method getMethodFromCmd(String methodName) {
|
||||
// TODO if we use const type for enum we need add some mapping. Even if we don't
|
||||
// change now it is handy to have flexibility in case we change internal code
|
||||
// and don't want to break user commands.
|
||||
return Method.valueOf(methodName.toLowerCase());
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static void verifyStringIsValidDecimal(String optionLabel, String optionValue) {
|
||||
try {
|
||||
Double.parseDouble(optionValue);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException(format("--%s=%s, '%s' is not a number",
|
||||
optionLabel,
|
||||
optionValue,
|
||||
optionValue));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static void verifyStringIsValidLong(String optionLabel, String optionValue) {
|
||||
try {
|
||||
Long.parseLong(optionValue);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException(format("--%s=%s, '%s' is not a number",
|
||||
optionLabel,
|
||||
optionValue,
|
||||
optionValue));
|
||||
}
|
||||
}
|
||||
|
||||
private static long toLong(String param) {
|
||||
try {
|
||||
return Long.parseLong(param);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException(format("'%s' is not a number", param));
|
||||
}
|
||||
}
|
||||
|
||||
private static File saveFileToDisk(String prefix,
|
||||
@SuppressWarnings("SameParameterValue") String suffix,
|
||||
String text) {
|
||||
String timestamp = Long.toUnsignedString(new Date().getTime());
|
||||
String relativeFileName = prefix + "_" + timestamp + suffix;
|
||||
try {
|
||||
Path path = Paths.get(relativeFileName);
|
||||
if (!Files.exists(path)) {
|
||||
try (PrintWriter out = new PrintWriter(path.toString())) {
|
||||
out.println(text);
|
||||
}
|
||||
return path.toAbsolutePath().toFile();
|
||||
} else {
|
||||
throw new IllegalStateException(format("could not overwrite existing file '%s'", relativeFileName));
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new IllegalStateException(format("could not create file '%s'", relativeFileName));
|
||||
}
|
||||
}
|
||||
|
||||
private static void printHelp(OptionParser parser, @SuppressWarnings("SameParameterValue") PrintStream stream) {
|
||||
try {
|
||||
stream.println("Bisq RPC Client");
|
||||
stream.println();
|
||||
stream.println("Usage: bisq-cli [options] <method> [params]");
|
||||
stream.println();
|
||||
parser.printHelpOn(stream);
|
||||
stream.println();
|
||||
String rowFormat = "%-25s%-52s%s%n";
|
||||
stream.format(rowFormat, "Method", "Params", "Description");
|
||||
stream.format(rowFormat, "------", "------", "------------");
|
||||
stream.format(rowFormat, getversion.name(), "", "Get server version");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getbalance.name(), "[--currency-code=<bsq|btc>]", "Get server wallet balances");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getaddressbalance.name(), "--address=<btc-address>", "Get server wallet address balance");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getbtcprice.name(), "--currency-code=<currency-code>", "Get current market btc price");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getfundingaddresses.name(), "", "Get BTC funding addresses");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getunusedbsqaddress.name(), "", "Get unused BSQ address");
|
||||
stream.println();
|
||||
stream.format(rowFormat, sendbsq.name(), "--address=<bsq-address> --amount=<bsq-amount> \\", "Send BSQ");
|
||||
stream.format(rowFormat, "", "[--tx-fee-rate=<sats/byte>]", "");
|
||||
stream.println();
|
||||
stream.format(rowFormat, sendbtc.name(), "--address=<btc-address> --amount=<btc-amount> \\", "Send BTC");
|
||||
stream.format(rowFormat, "", "[--tx-fee-rate=<sats/byte>]", "");
|
||||
stream.format(rowFormat, "", "[--memo=<\"memo\">]", "");
|
||||
stream.println();
|
||||
stream.format(rowFormat, verifybsqsenttoaddress.name(), "--address=<bsq-address> --amount=<bsq-amount>",
|
||||
"Verify amount was sent to BSQ wallet address");
|
||||
stream.println();
|
||||
stream.format(rowFormat, gettxfeerate.name(), "", "Get current tx fee rate in sats/byte");
|
||||
stream.println();
|
||||
stream.format(rowFormat, settxfeerate.name(), "--tx-fee-rate=<sats/byte>", "Set custom tx fee rate in sats/byte");
|
||||
stream.println();
|
||||
stream.format(rowFormat, unsettxfeerate.name(), "", "Unset custom tx fee rate");
|
||||
stream.println();
|
||||
stream.format(rowFormat, gettransaction.name(), "--transaction-id=<transaction-id>", "Get transaction with id");
|
||||
stream.println();
|
||||
stream.format(rowFormat, createoffer.name(), "--payment-account=<payment-account-id> \\", "Create and place an offer");
|
||||
stream.format(rowFormat, "", "--direction=<buy|sell> \\", "");
|
||||
stream.format(rowFormat, "", "--currency-code=<currency-code> \\", "");
|
||||
stream.format(rowFormat, "", "--amount=<btc-amount> \\", "");
|
||||
stream.format(rowFormat, "", "[--min-amount=<min-btc-amount>] \\", "");
|
||||
stream.format(rowFormat, "", "--fixed-price=<price> | --market-price=margin=<percent> \\", "");
|
||||
stream.format(rowFormat, "", "--security-deposit=<percent> \\", "");
|
||||
stream.format(rowFormat, "", "[--fee-currency=<bsq|btc>]", "");
|
||||
stream.println();
|
||||
stream.format(rowFormat, canceloffer.name(), "--offer-id=<offer-id>", "Cancel offer with id");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getoffer.name(), "--offer-id=<offer-id>", "Get current offer with id");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getmyoffer.name(), "--offer-id=<offer-id>", "Get my current offer with id");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getoffers.name(), "--direction=<buy|sell> \\", "Get current offers");
|
||||
stream.format(rowFormat, "", "--currency-code=<currency-code>", "");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getmyoffers.name(), "--direction=<buy|sell> \\", "Get my current offers");
|
||||
stream.format(rowFormat, "", "--currency-code=<currency-code>", "");
|
||||
stream.println();
|
||||
stream.format(rowFormat, takeoffer.name(), "--offer-id=<offer-id> \\", "Take offer with id");
|
||||
stream.format(rowFormat, "", "--payment-account=<payment-account-id>", "");
|
||||
stream.format(rowFormat, "", "[--fee-currency=<btc|bsq>]", "");
|
||||
stream.println();
|
||||
stream.format(rowFormat, gettrade.name(), "--trade-id=<trade-id> \\", "Get trade summary or full contract");
|
||||
stream.format(rowFormat, "", "[--show-contract=<true|false>]", "");
|
||||
stream.println();
|
||||
stream.format(rowFormat, confirmpaymentstarted.name(), "--trade-id=<trade-id>", "Confirm payment started");
|
||||
stream.println();
|
||||
stream.format(rowFormat, confirmpaymentreceived.name(), "--trade-id=<trade-id>", "Confirm payment received");
|
||||
stream.println();
|
||||
stream.format(rowFormat, keepfunds.name(), "--trade-id=<trade-id>", "Keep received funds in Bisq wallet");
|
||||
stream.println();
|
||||
stream.format(rowFormat, withdrawfunds.name(), "--trade-id=<trade-id> --address=<btc-address> \\",
|
||||
"Withdraw received funds to external wallet address");
|
||||
stream.format(rowFormat, "", "[--memo=<\"memo\">]", "");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getpaymentmethods.name(), "", "Get list of supported payment account method ids");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getpaymentacctform.name(), "--payment-method-id=<payment-method-id>", "Get a new payment account form");
|
||||
stream.println();
|
||||
stream.format(rowFormat, createpaymentacct.name(), "--payment-account-form=<path>", "Create a new payment account");
|
||||
stream.println();
|
||||
stream.format(rowFormat, createcryptopaymentacct.name(), "--account-name=<name> \\", "Create a new cryptocurrency payment account");
|
||||
stream.format(rowFormat, "", "--currency-code=<bsq> \\", "");
|
||||
stream.format(rowFormat, "", "--address=<bsq-address>", "");
|
||||
stream.format(rowFormat, "", "--trade-instant=<true|false>", "");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getpaymentaccts.name(), "", "Get user payment accounts");
|
||||
stream.println();
|
||||
stream.format(rowFormat, lockwallet.name(), "", "Remove wallet password from memory, locking the wallet");
|
||||
stream.println();
|
||||
stream.format(rowFormat, unlockwallet.name(), "--wallet-password=<password> --timeout=<seconds>",
|
||||
"Store wallet password in memory for timeout seconds");
|
||||
stream.println();
|
||||
stream.format(rowFormat, setwalletpassword.name(), "--wallet-password=<password> \\",
|
||||
"Encrypt wallet with password, or set new password on encrypted wallet");
|
||||
stream.format(rowFormat, "", "[--new-wallet-password=<new-password>]", "");
|
||||
stream.println();
|
||||
stream.format(rowFormat, stop.name(), "", "Shut down the server");
|
||||
stream.println();
|
||||
stream.println("Method Help Usage: bisq-cli [options] <method> --help");
|
||||
stream.println();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace(stream);
|
||||
}
|
||||
}
|
||||
}
|
79
cli/src/main/java/bisq/cli/ColumnHeaderConstants.java
Normal file
79
cli/src/main/java/bisq/cli/ColumnHeaderConstants.java
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
|
||||
class ColumnHeaderConstants {
|
||||
|
||||
// For inserting 2 spaces between column headers.
|
||||
static final String COL_HEADER_DELIMITER = " ";
|
||||
|
||||
// Table column header format specs, right padded with two spaces. In some cases
|
||||
// such as COL_HEADER_CREATION_DATE, COL_HEADER_VOLUME and COL_HEADER_UUID, the
|
||||
// expected max data string length is accounted for. In others, column header
|
||||
// lengths are expected to be greater than any column value length.
|
||||
static final String COL_HEADER_ADDRESS = padEnd("%-3s Address", 52, ' ');
|
||||
static final String COL_HEADER_AMOUNT = "BTC(min - max)";
|
||||
static final String COL_HEADER_AVAILABLE_BALANCE = "Available Balance";
|
||||
static final String COL_HEADER_AVAILABLE_CONFIRMED_BALANCE = "Available Confirmed Balance";
|
||||
static final String COL_HEADER_UNCONFIRMED_CHANGE_BALANCE = "Unconfirmed Change Balance";
|
||||
static final String COL_HEADER_RESERVED_BALANCE = "Reserved Balance";
|
||||
static final String COL_HEADER_TOTAL_AVAILABLE_BALANCE = "Total Available Balance";
|
||||
static final String COL_HEADER_LOCKED_BALANCE = "Locked Balance";
|
||||
static final String COL_HEADER_LOCKED_FOR_VOTING_BALANCE = "Locked For Voting Balance";
|
||||
static final String COL_HEADER_LOCKUP_BONDS_BALANCE = "Lockup Bonds Balance";
|
||||
static final String COL_HEADER_UNLOCKING_BONDS_BALANCE = "Unlocking Bonds Balance";
|
||||
static final String COL_HEADER_UNVERIFIED_BALANCE = "Unverified Balance";
|
||||
static final String COL_HEADER_CONFIRMATIONS = "Confirmations";
|
||||
static final String COL_HEADER_IS_USED_ADDRESS = "Is Used";
|
||||
static final String COL_HEADER_CREATION_DATE = padEnd("Creation Date (UTC)", 20, ' ');
|
||||
static final String COL_HEADER_CURRENCY = "Currency";
|
||||
static final String COL_HEADER_DIRECTION = "Buy/Sell";
|
||||
static final String COL_HEADER_NAME = "Name";
|
||||
static final String COL_HEADER_PAYMENT_METHOD = "Payment Method";
|
||||
static final String COL_HEADER_PRICE = "Price in %-3s for 1 BTC";
|
||||
static final String COL_HEADER_PRICE_OF_ALTCOIN = "Price in BTC for 1 %-3s";
|
||||
static final String COL_HEADER_TRADE_AMOUNT = padStart("Amount(%-3s)", 12, ' ');
|
||||
static final String COL_HEADER_TRADE_BSQ_BUYER_ADDRESS = "BSQ Buyer Address";
|
||||
static final String COL_HEADER_TRADE_BUYER_COST = padEnd("Buyer Cost(%-3s)", 15, ' ');
|
||||
static final String COL_HEADER_TRADE_DEPOSIT_CONFIRMED = "Deposit Confirmed";
|
||||
static final String COL_HEADER_TRADE_DEPOSIT_PUBLISHED = "Deposit Published";
|
||||
static final String COL_HEADER_TRADE_PAYMENT_SENT = padEnd("%-3s Sent", 8, ' ');
|
||||
static final String COL_HEADER_TRADE_PAYMENT_RECEIVED = padEnd("%-3s Received", 12, ' ');
|
||||
static final String COL_HEADER_TRADE_PAYOUT_PUBLISHED = "Payout Published";
|
||||
static final String COL_HEADER_TRADE_WITHDRAWN = "Withdrawn";
|
||||
static final String COL_HEADER_TRADE_ROLE = "My Role";
|
||||
static final String COL_HEADER_TRADE_SHORT_ID = "ID";
|
||||
static final String COL_HEADER_TRADE_TX_FEE = padEnd("Tx Fee(BTC)", 12, ' ');
|
||||
static final String COL_HEADER_TRADE_MAKER_FEE = padEnd("Maker Fee(%-3s)", 12, ' '); // "Maker Fee(%-3s)";
|
||||
static final String COL_HEADER_TRADE_TAKER_FEE = padEnd("Taker Fee(%-3s)", 12, ' '); // "Taker Fee(%-3s)";
|
||||
|
||||
static final String COL_HEADER_TX_ID = "Tx ID";
|
||||
static final String COL_HEADER_TX_INPUT_SUM = "Tx Inputs (BTC)";
|
||||
static final String COL_HEADER_TX_OUTPUT_SUM = "Tx Outputs (BTC)";
|
||||
static final String COL_HEADER_TX_FEE = "Tx Fee (BTC)";
|
||||
static final String COL_HEADER_TX_SIZE = "Tx Size (Bytes)";
|
||||
static final String COL_HEADER_TX_IS_CONFIRMED = "Is Confirmed";
|
||||
static final String COL_HEADER_TX_MEMO = "Memo";
|
||||
|
||||
static final String COL_HEADER_VOLUME = padEnd("%-3s(min - max)", 15, ' ');
|
||||
|
||||
static final String COL_HEADER_UUID = padEnd("ID", 52, ' ');
|
||||
}
|
35
cli/src/main/java/bisq/cli/CryptoCurrencyUtil.java
Normal file
35
cli/src/main/java/bisq/cli/CryptoCurrencyUtil.java
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class CryptoCurrencyUtil {
|
||||
|
||||
public static boolean isSupportedCryptoCurrency(String currencyCode) {
|
||||
return getSupportedCryptoCurrencies().contains(currencyCode.toUpperCase());
|
||||
}
|
||||
|
||||
public static List<String> getSupportedCryptoCurrencies() {
|
||||
final List<String> result = new ArrayList<>();
|
||||
result.add("BSQ");
|
||||
result.sort(String::compareTo);
|
||||
return result;
|
||||
}
|
||||
}
|
155
cli/src/main/java/bisq/cli/CurrencyFormat.java
Normal file
155
cli/src/main/java/bisq/cli/CurrencyFormat.java
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import bisq.proto.grpc.TxFeeRateInfo;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.math.RoundingMode.HALF_UP;
|
||||
import static java.math.RoundingMode.UNNECESSARY;
|
||||
|
||||
@VisibleForTesting
|
||||
public class CurrencyFormat {
|
||||
|
||||
private static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
|
||||
static final BigDecimal SATOSHI_DIVISOR = new BigDecimal(100_000_000);
|
||||
static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.00000000");
|
||||
static final DecimalFormat BTC_TX_FEE_FORMAT = new DecimalFormat("###,###,##0");
|
||||
|
||||
static final BigDecimal BSQ_SATOSHI_DIVISOR = new BigDecimal(100);
|
||||
static final DecimalFormat BSQ_FORMAT = new DecimalFormat("###,###,###,##0.00");
|
||||
static final DecimalFormat SEND_BSQ_FORMAT = new DecimalFormat("###########0.00");
|
||||
|
||||
static final BigDecimal SECURITY_DEPOSIT_MULTIPLICAND = new BigDecimal("0.01");
|
||||
|
||||
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
|
||||
public static String formatSatoshis(long sats) {
|
||||
return BTC_FORMAT.format(BigDecimal.valueOf(sats).divide(SATOSHI_DIVISOR));
|
||||
}
|
||||
|
||||
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
|
||||
public static String formatBsq(long sats) {
|
||||
return BSQ_FORMAT.format(BigDecimal.valueOf(sats).divide(BSQ_SATOSHI_DIVISOR));
|
||||
}
|
||||
|
||||
public static String formatBsqAmount(long bsqSats) {
|
||||
// BSQ sats = trade.getOffer().getVolume()
|
||||
NUMBER_FORMAT.setMinimumFractionDigits(2);
|
||||
NUMBER_FORMAT.setMaximumFractionDigits(2);
|
||||
NUMBER_FORMAT.setRoundingMode(HALF_UP);
|
||||
return SEND_BSQ_FORMAT.format((double) bsqSats / SATOSHI_DIVISOR.doubleValue());
|
||||
}
|
||||
|
||||
public static String formatTxFeeRateInfo(TxFeeRateInfo txFeeRateInfo) {
|
||||
if (txFeeRateInfo.getUseCustomTxFeeRate())
|
||||
return format("custom tx fee rate: %s sats/byte, network rate: %s sats/byte, min network rate: %s sats/byte",
|
||||
formatFeeSatoshis(txFeeRateInfo.getCustomTxFeeRate()),
|
||||
formatFeeSatoshis(txFeeRateInfo.getFeeServiceRate()),
|
||||
formatFeeSatoshis(txFeeRateInfo.getMinFeeServiceRate()));
|
||||
else
|
||||
return format("tx fee rate: %s sats/byte, min tx fee rate: %s sats/byte",
|
||||
formatFeeSatoshis(txFeeRateInfo.getFeeServiceRate()),
|
||||
formatFeeSatoshis(txFeeRateInfo.getMinFeeServiceRate()));
|
||||
}
|
||||
|
||||
public static String formatAmountRange(long minAmount, long amount) {
|
||||
return minAmount != amount
|
||||
? formatSatoshis(minAmount) + " - " + formatSatoshis(amount)
|
||||
: formatSatoshis(amount);
|
||||
}
|
||||
|
||||
public static String formatVolumeRange(long minVolume, long volume) {
|
||||
return minVolume != volume
|
||||
? formatOfferVolume(minVolume) + " - " + formatOfferVolume(volume)
|
||||
: formatOfferVolume(volume);
|
||||
}
|
||||
|
||||
public static String formatCryptoCurrencyVolumeRange(long minVolume, long volume) {
|
||||
return minVolume != volume
|
||||
? formatCryptoCurrencyOfferVolume(minVolume) + " - " + formatCryptoCurrencyOfferVolume(volume)
|
||||
: formatCryptoCurrencyOfferVolume(volume);
|
||||
}
|
||||
|
||||
public static String formatMarketPrice(double price) {
|
||||
NUMBER_FORMAT.setMinimumFractionDigits(4);
|
||||
NUMBER_FORMAT.setMaximumFractionDigits(4);
|
||||
return NUMBER_FORMAT.format(price);
|
||||
}
|
||||
|
||||
public static String formatPrice(long price) {
|
||||
NUMBER_FORMAT.setMinimumFractionDigits(4);
|
||||
NUMBER_FORMAT.setMaximumFractionDigits(4);
|
||||
NUMBER_FORMAT.setRoundingMode(UNNECESSARY);
|
||||
return NUMBER_FORMAT.format((double) price / 10_000);
|
||||
}
|
||||
|
||||
public static String formatCryptoCurrencyPrice(long price) {
|
||||
NUMBER_FORMAT.setMinimumFractionDigits(8);
|
||||
NUMBER_FORMAT.setMaximumFractionDigits(8);
|
||||
NUMBER_FORMAT.setRoundingMode(UNNECESSARY);
|
||||
return NUMBER_FORMAT.format((double) price / SATOSHI_DIVISOR.doubleValue());
|
||||
}
|
||||
|
||||
public static String formatOfferVolume(long volume) {
|
||||
NUMBER_FORMAT.setMinimumFractionDigits(0);
|
||||
NUMBER_FORMAT.setMaximumFractionDigits(0);
|
||||
NUMBER_FORMAT.setRoundingMode(HALF_UP);
|
||||
return NUMBER_FORMAT.format((double) volume / 10_000);
|
||||
}
|
||||
|
||||
public static String formatCryptoCurrencyOfferVolume(long volume) {
|
||||
NUMBER_FORMAT.setMinimumFractionDigits(2);
|
||||
NUMBER_FORMAT.setMaximumFractionDigits(2);
|
||||
NUMBER_FORMAT.setRoundingMode(HALF_UP);
|
||||
return NUMBER_FORMAT.format((double) volume / SATOSHI_DIVISOR.doubleValue());
|
||||
}
|
||||
|
||||
public static long toSatoshis(String btc) {
|
||||
if (btc.startsWith("-"))
|
||||
throw new IllegalArgumentException(format("'%s' is not a positive number", btc));
|
||||
|
||||
try {
|
||||
return new BigDecimal(btc).multiply(SATOSHI_DIVISOR).longValue();
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException(format("'%s' is not a number", btc));
|
||||
}
|
||||
}
|
||||
|
||||
public static double toSecurityDepositAsPct(String securityDepositInput) {
|
||||
try {
|
||||
return new BigDecimal(securityDepositInput)
|
||||
.multiply(SECURITY_DEPOSIT_MULTIPLICAND).doubleValue();
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException(format("'%s' is not a number", securityDepositInput));
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatFeeSatoshis(long sats) {
|
||||
return BTC_TX_FEE_FORMAT.format(BigDecimal.valueOf(sats));
|
||||
}
|
||||
}
|
60
cli/src/main/java/bisq/cli/DirectionFormat.java
Normal file
60
cli/src/main/java/bisq/cli/DirectionFormat.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static bisq.cli.ColumnHeaderConstants.COL_HEADER_DIRECTION;
|
||||
import static java.lang.String.format;
|
||||
import static protobuf.OfferPayload.Direction.BUY;
|
||||
import static protobuf.OfferPayload.Direction.SELL;
|
||||
|
||||
class DirectionFormat {
|
||||
|
||||
static int getLongestDirectionColWidth(List<OfferInfo> offers) {
|
||||
if (offers.isEmpty() || offers.get(0).getBaseCurrencyCode().equals("BTC"))
|
||||
return COL_HEADER_DIRECTION.length();
|
||||
else
|
||||
return 18; // .e.g., "Sell BSQ (Buy BTC)".length()
|
||||
}
|
||||
|
||||
static final Function<OfferInfo, String> directionFormat = (offer) -> {
|
||||
String baseCurrencyCode = offer.getBaseCurrencyCode();
|
||||
boolean isCryptoCurrencyOffer = !baseCurrencyCode.equals("BTC");
|
||||
if (!isCryptoCurrencyOffer) {
|
||||
return baseCurrencyCode;
|
||||
} else {
|
||||
// Return "Sell BSQ (Buy BTC)", or "Buy BSQ (Sell BTC)".
|
||||
String direction = offer.getDirection();
|
||||
String mirroredDirection = getMirroredDirection(direction);
|
||||
Function<String, String> mixedCase = (word) -> word.charAt(0) + word.substring(1).toLowerCase();
|
||||
return format("%s %s (%s %s)",
|
||||
mixedCase.apply(mirroredDirection),
|
||||
baseCurrencyCode,
|
||||
mixedCase.apply(direction),
|
||||
offer.getCounterCurrencyCode());
|
||||
}
|
||||
};
|
||||
|
||||
static String getMirroredDirection(String directionAsString) {
|
||||
return directionAsString.equalsIgnoreCase(BUY.name()) ? SELL.name() : BUY.name();
|
||||
}
|
||||
}
|
516
cli/src/main/java/bisq/cli/GrpcClient.java
Normal file
516
cli/src/main/java/bisq/cli/GrpcClient.java
Normal file
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import bisq.proto.grpc.AddressBalanceInfo;
|
||||
import bisq.proto.grpc.BalancesInfo;
|
||||
import bisq.proto.grpc.BsqBalanceInfo;
|
||||
import bisq.proto.grpc.BtcBalanceInfo;
|
||||
import bisq.proto.grpc.CancelOfferRequest;
|
||||
import bisq.proto.grpc.ConfirmPaymentReceivedRequest;
|
||||
import bisq.proto.grpc.ConfirmPaymentStartedRequest;
|
||||
import bisq.proto.grpc.CreateCryptoCurrencyPaymentAccountRequest;
|
||||
import bisq.proto.grpc.CreateOfferRequest;
|
||||
import bisq.proto.grpc.CreatePaymentAccountRequest;
|
||||
import bisq.proto.grpc.GetAddressBalanceRequest;
|
||||
import bisq.proto.grpc.GetBalancesRequest;
|
||||
import bisq.proto.grpc.GetCryptoCurrencyPaymentMethodsRequest;
|
||||
import bisq.proto.grpc.GetFundingAddressesRequest;
|
||||
import bisq.proto.grpc.GetMethodHelpRequest;
|
||||
import bisq.proto.grpc.GetMyOfferRequest;
|
||||
import bisq.proto.grpc.GetMyOffersRequest;
|
||||
import bisq.proto.grpc.GetOfferRequest;
|
||||
import bisq.proto.grpc.GetOffersRequest;
|
||||
import bisq.proto.grpc.GetPaymentAccountFormRequest;
|
||||
import bisq.proto.grpc.GetPaymentAccountsRequest;
|
||||
import bisq.proto.grpc.GetPaymentMethodsRequest;
|
||||
import bisq.proto.grpc.GetTradeRequest;
|
||||
import bisq.proto.grpc.GetTransactionRequest;
|
||||
import bisq.proto.grpc.GetTxFeeRateRequest;
|
||||
import bisq.proto.grpc.GetUnusedBsqAddressRequest;
|
||||
import bisq.proto.grpc.GetVersionRequest;
|
||||
import bisq.proto.grpc.KeepFundsRequest;
|
||||
import bisq.proto.grpc.LockWalletRequest;
|
||||
import bisq.proto.grpc.MarketPriceRequest;
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
import bisq.proto.grpc.RegisterDisputeAgentRequest;
|
||||
import bisq.proto.grpc.RemoveWalletPasswordRequest;
|
||||
import bisq.proto.grpc.SendBsqRequest;
|
||||
import bisq.proto.grpc.SendBtcRequest;
|
||||
import bisq.proto.grpc.SetTxFeeRatePreferenceRequest;
|
||||
import bisq.proto.grpc.SetWalletPasswordRequest;
|
||||
import bisq.proto.grpc.StopRequest;
|
||||
import bisq.proto.grpc.TakeOfferReply;
|
||||
import bisq.proto.grpc.TakeOfferRequest;
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
import bisq.proto.grpc.TxFeeRateInfo;
|
||||
import bisq.proto.grpc.TxInfo;
|
||||
import bisq.proto.grpc.UnlockWalletRequest;
|
||||
import bisq.proto.grpc.UnsetTxFeeRatePreferenceRequest;
|
||||
import bisq.proto.grpc.VerifyBsqSentToAddressRequest;
|
||||
import bisq.proto.grpc.WithdrawFundsRequest;
|
||||
|
||||
import protobuf.PaymentAccount;
|
||||
import protobuf.PaymentMethod;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.cli.CryptoCurrencyUtil.isSupportedCryptoCurrency;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static protobuf.OfferPayload.Direction.BUY;
|
||||
import static protobuf.OfferPayload.Direction.SELL;
|
||||
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
@Slf4j
|
||||
public final class GrpcClient {
|
||||
|
||||
private final GrpcStubs grpcStubs;
|
||||
|
||||
public GrpcClient(String apiHost, int apiPort, String apiPassword) {
|
||||
this.grpcStubs = new GrpcStubs(apiHost, apiPort, apiPassword);
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
var request = GetVersionRequest.newBuilder().build();
|
||||
return grpcStubs.versionService.getVersion(request).getVersion();
|
||||
}
|
||||
|
||||
public BalancesInfo getBalances() {
|
||||
return getBalances("");
|
||||
}
|
||||
|
||||
public BsqBalanceInfo getBsqBalances() {
|
||||
return getBalances("BSQ").getBsq();
|
||||
}
|
||||
|
||||
public BtcBalanceInfo getBtcBalances() {
|
||||
return getBalances("BTC").getBtc();
|
||||
}
|
||||
|
||||
public BalancesInfo getBalances(String currencyCode) {
|
||||
var request = GetBalancesRequest.newBuilder()
|
||||
.setCurrencyCode(currencyCode)
|
||||
.build();
|
||||
return grpcStubs.walletsService.getBalances(request).getBalances();
|
||||
}
|
||||
|
||||
public AddressBalanceInfo getAddressBalance(String address) {
|
||||
var request = GetAddressBalanceRequest.newBuilder()
|
||||
.setAddress(address).build();
|
||||
return grpcStubs.walletsService.getAddressBalance(request).getAddressBalanceInfo();
|
||||
}
|
||||
|
||||
public double getBtcPrice(String currencyCode) {
|
||||
var request = MarketPriceRequest.newBuilder()
|
||||
.setCurrencyCode(currencyCode)
|
||||
.build();
|
||||
return grpcStubs.priceService.getMarketPrice(request).getPrice();
|
||||
}
|
||||
|
||||
public List<AddressBalanceInfo> getFundingAddresses() {
|
||||
var request = GetFundingAddressesRequest.newBuilder().build();
|
||||
return grpcStubs.walletsService.getFundingAddresses(request).getAddressBalanceInfoList();
|
||||
}
|
||||
|
||||
public String getUnusedBsqAddress() {
|
||||
var request = GetUnusedBsqAddressRequest.newBuilder().build();
|
||||
return grpcStubs.walletsService.getUnusedBsqAddress(request).getAddress();
|
||||
}
|
||||
|
||||
public String getUnusedBtcAddress() {
|
||||
var request = GetFundingAddressesRequest.newBuilder().build();
|
||||
var addressBalances = grpcStubs.walletsService.getFundingAddresses(request)
|
||||
.getAddressBalanceInfoList();
|
||||
//noinspection OptionalGetWithoutIsPresent
|
||||
return addressBalances.stream()
|
||||
.filter(AddressBalanceInfo::getIsAddressUnused)
|
||||
.findFirst()
|
||||
.get()
|
||||
.getAddress();
|
||||
}
|
||||
|
||||
public TxInfo sendBsq(String address, String amount, String txFeeRate) {
|
||||
var request = SendBsqRequest.newBuilder()
|
||||
.setAddress(address)
|
||||
.setAmount(amount)
|
||||
.setTxFeeRate(txFeeRate)
|
||||
.build();
|
||||
return grpcStubs.walletsService.sendBsq(request).getTxInfo();
|
||||
}
|
||||
|
||||
public TxInfo sendBtc(String address, String amount, String txFeeRate, String memo) {
|
||||
var request = SendBtcRequest.newBuilder()
|
||||
.setAddress(address)
|
||||
.setAmount(amount)
|
||||
.setTxFeeRate(txFeeRate)
|
||||
.setMemo(memo)
|
||||
.build();
|
||||
return grpcStubs.walletsService.sendBtc(request).getTxInfo();
|
||||
}
|
||||
|
||||
public boolean verifyBsqSentToAddress(String address, String amount) {
|
||||
var request = VerifyBsqSentToAddressRequest.newBuilder()
|
||||
.setAddress(address)
|
||||
.setAmount(amount)
|
||||
.build();
|
||||
return grpcStubs.walletsService.verifyBsqSentToAddress(request).getIsAmountReceived();
|
||||
}
|
||||
|
||||
public TxFeeRateInfo getTxFeeRate() {
|
||||
var request = GetTxFeeRateRequest.newBuilder().build();
|
||||
return grpcStubs.walletsService.getTxFeeRate(request).getTxFeeRateInfo();
|
||||
}
|
||||
|
||||
public TxFeeRateInfo setTxFeeRate(long txFeeRate) {
|
||||
var request = SetTxFeeRatePreferenceRequest.newBuilder()
|
||||
.setTxFeeRatePreference(txFeeRate)
|
||||
.build();
|
||||
return grpcStubs.walletsService.setTxFeeRatePreference(request).getTxFeeRateInfo();
|
||||
}
|
||||
|
||||
public TxFeeRateInfo unsetTxFeeRate() {
|
||||
var request = UnsetTxFeeRatePreferenceRequest.newBuilder().build();
|
||||
return grpcStubs.walletsService.unsetTxFeeRatePreference(request).getTxFeeRateInfo();
|
||||
}
|
||||
|
||||
public TxInfo getTransaction(String txId) {
|
||||
var request = GetTransactionRequest.newBuilder()
|
||||
.setTxId(txId)
|
||||
.build();
|
||||
return grpcStubs.walletsService.getTransaction(request).getTxInfo();
|
||||
}
|
||||
|
||||
public OfferInfo createFixedPricedOffer(String direction,
|
||||
String currencyCode,
|
||||
long amount,
|
||||
long minAmount,
|
||||
String fixedPrice,
|
||||
double securityDeposit,
|
||||
String paymentAcctId,
|
||||
String makerFeeCurrencyCode) {
|
||||
return createOffer(direction,
|
||||
currencyCode,
|
||||
amount,
|
||||
minAmount,
|
||||
false,
|
||||
fixedPrice,
|
||||
0.00,
|
||||
securityDeposit,
|
||||
paymentAcctId,
|
||||
makerFeeCurrencyCode);
|
||||
}
|
||||
|
||||
public OfferInfo createMarketBasedPricedOffer(String direction,
|
||||
String currencyCode,
|
||||
long amount,
|
||||
long minAmount,
|
||||
double marketPriceMargin,
|
||||
double securityDeposit,
|
||||
String paymentAcctId,
|
||||
String makerFeeCurrencyCode) {
|
||||
return createOffer(direction,
|
||||
currencyCode,
|
||||
amount,
|
||||
minAmount,
|
||||
true,
|
||||
"0",
|
||||
marketPriceMargin,
|
||||
securityDeposit,
|
||||
paymentAcctId,
|
||||
makerFeeCurrencyCode);
|
||||
}
|
||||
|
||||
public OfferInfo createOffer(String direction,
|
||||
String currencyCode,
|
||||
long amount,
|
||||
long minAmount,
|
||||
boolean useMarketBasedPrice,
|
||||
String fixedPrice,
|
||||
double marketPriceMargin,
|
||||
double securityDeposit,
|
||||
String paymentAcctId,
|
||||
String makerFeeCurrencyCode) {
|
||||
var request = CreateOfferRequest.newBuilder()
|
||||
.setDirection(direction)
|
||||
.setCurrencyCode(currencyCode)
|
||||
.setAmount(amount)
|
||||
.setMinAmount(minAmount)
|
||||
.setUseMarketBasedPrice(useMarketBasedPrice)
|
||||
.setPrice(fixedPrice)
|
||||
.setMarketPriceMargin(marketPriceMargin)
|
||||
.setBuyerSecurityDeposit(securityDeposit)
|
||||
.setPaymentAccountId(paymentAcctId)
|
||||
.setMakerFeeCurrencyCode(makerFeeCurrencyCode)
|
||||
.build();
|
||||
return grpcStubs.offersService.createOffer(request).getOffer();
|
||||
}
|
||||
|
||||
public void cancelOffer(String offerId) {
|
||||
var request = CancelOfferRequest.newBuilder()
|
||||
.setId(offerId)
|
||||
.build();
|
||||
grpcStubs.offersService.cancelOffer(request);
|
||||
}
|
||||
|
||||
public OfferInfo getOffer(String offerId) {
|
||||
var request = GetOfferRequest.newBuilder()
|
||||
.setId(offerId)
|
||||
.build();
|
||||
return grpcStubs.offersService.getOffer(request).getOffer();
|
||||
}
|
||||
|
||||
public OfferInfo getMyOffer(String offerId) {
|
||||
var request = GetMyOfferRequest.newBuilder()
|
||||
.setId(offerId)
|
||||
.build();
|
||||
return grpcStubs.offersService.getMyOffer(request).getOffer();
|
||||
}
|
||||
|
||||
public List<OfferInfo> getOffers(String direction, String currencyCode) {
|
||||
if (isSupportedCryptoCurrency(currencyCode)) {
|
||||
return getCryptoCurrencyOffers(direction, currencyCode);
|
||||
} else {
|
||||
var request = GetOffersRequest.newBuilder()
|
||||
.setDirection(direction)
|
||||
.setCurrencyCode(currencyCode)
|
||||
.build();
|
||||
return grpcStubs.offersService.getOffers(request).getOffersList();
|
||||
}
|
||||
}
|
||||
|
||||
public List<OfferInfo> getCryptoCurrencyOffers(String direction, String currencyCode) {
|
||||
return getOffers(direction, "BTC").stream()
|
||||
.filter(o -> o.getBaseCurrencyCode().equalsIgnoreCase(currencyCode))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
public List<OfferInfo> getOffersSortedByDate(String currencyCode) {
|
||||
ArrayList<OfferInfo> offers = new ArrayList<>();
|
||||
offers.addAll(getOffers(BUY.name(), currencyCode));
|
||||
offers.addAll(getOffers(SELL.name(), currencyCode));
|
||||
return sortOffersByDate(offers);
|
||||
}
|
||||
|
||||
public List<OfferInfo> getOffersSortedByDate(String direction, String currencyCode) {
|
||||
var offers = getOffers(direction, currencyCode);
|
||||
return offers.isEmpty() ? offers : sortOffersByDate(offers);
|
||||
}
|
||||
|
||||
public List<OfferInfo> getBsqOffersSortedByDate() {
|
||||
ArrayList<OfferInfo> offers = new ArrayList<>();
|
||||
offers.addAll(getCryptoCurrencyOffers(BUY.name(), "BSQ"));
|
||||
offers.addAll(getCryptoCurrencyOffers(SELL.name(), "BSQ"));
|
||||
return sortOffersByDate(offers);
|
||||
}
|
||||
|
||||
public List<OfferInfo> getMyOffers(String direction, String currencyCode) {
|
||||
if (isSupportedCryptoCurrency(currencyCode)) {
|
||||
return getMyCryptoCurrencyOffers(direction, currencyCode);
|
||||
} else {
|
||||
var request = GetMyOffersRequest.newBuilder()
|
||||
.setDirection(direction)
|
||||
.setCurrencyCode(currencyCode)
|
||||
.build();
|
||||
return grpcStubs.offersService.getMyOffers(request).getOffersList();
|
||||
}
|
||||
}
|
||||
|
||||
public List<OfferInfo> getMyCryptoCurrencyOffers(String direction, String currencyCode) {
|
||||
return getMyOffers(direction, "BTC").stream()
|
||||
.filter(o -> o.getBaseCurrencyCode().equalsIgnoreCase(currencyCode))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
public List<OfferInfo> getMyOffersSortedByDate(String direction, String currencyCode) {
|
||||
var offers = getMyOffers(direction, currencyCode);
|
||||
return offers.isEmpty() ? offers : sortOffersByDate(offers);
|
||||
}
|
||||
|
||||
public List<OfferInfo> getMyOffersSortedByDate(String currencyCode) {
|
||||
ArrayList<OfferInfo> offers = new ArrayList<>();
|
||||
offers.addAll(getMyOffers(BUY.name(), currencyCode));
|
||||
offers.addAll(getMyOffers(SELL.name(), currencyCode));
|
||||
return sortOffersByDate(offers);
|
||||
}
|
||||
|
||||
public List<OfferInfo> getMyBsqOffersSortedByDate() {
|
||||
ArrayList<OfferInfo> offers = new ArrayList<>();
|
||||
offers.addAll(getMyCryptoCurrencyOffers(BUY.name(), "BSQ"));
|
||||
offers.addAll(getMyCryptoCurrencyOffers(SELL.name(), "BSQ"));
|
||||
return sortOffersByDate(offers);
|
||||
}
|
||||
|
||||
public OfferInfo getMostRecentOffer(String direction, String currencyCode) {
|
||||
List<OfferInfo> offers = getOffersSortedByDate(direction, currencyCode);
|
||||
return offers.isEmpty() ? null : offers.get(offers.size() - 1);
|
||||
}
|
||||
|
||||
public List<OfferInfo> sortOffersByDate(List<OfferInfo> offerInfoList) {
|
||||
return offerInfoList.stream()
|
||||
.sorted(comparing(OfferInfo::getDate))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId, String takerFeeCurrencyCode) {
|
||||
var request = TakeOfferRequest.newBuilder()
|
||||
.setOfferId(offerId)
|
||||
.setPaymentAccountId(paymentAccountId)
|
||||
.setTakerFeeCurrencyCode(takerFeeCurrencyCode)
|
||||
.build();
|
||||
return grpcStubs.tradesService.takeOffer(request);
|
||||
}
|
||||
|
||||
public TradeInfo takeOffer(String offerId, String paymentAccountId, String takerFeeCurrencyCode) {
|
||||
var reply = getTakeOfferReply(offerId, paymentAccountId, takerFeeCurrencyCode);
|
||||
if (reply.hasTrade())
|
||||
return reply.getTrade();
|
||||
else
|
||||
throw new IllegalStateException(reply.getFailureReason().getDescription());
|
||||
}
|
||||
|
||||
public TradeInfo getTrade(String tradeId) {
|
||||
var request = GetTradeRequest.newBuilder()
|
||||
.setTradeId(tradeId)
|
||||
.build();
|
||||
return grpcStubs.tradesService.getTrade(request).getTrade();
|
||||
}
|
||||
|
||||
public void confirmPaymentStarted(String tradeId) {
|
||||
var request = ConfirmPaymentStartedRequest.newBuilder()
|
||||
.setTradeId(tradeId)
|
||||
.build();
|
||||
grpcStubs.tradesService.confirmPaymentStarted(request);
|
||||
}
|
||||
|
||||
public void confirmPaymentReceived(String tradeId) {
|
||||
var request = ConfirmPaymentReceivedRequest.newBuilder()
|
||||
.setTradeId(tradeId)
|
||||
.build();
|
||||
grpcStubs.tradesService.confirmPaymentReceived(request);
|
||||
}
|
||||
|
||||
public void keepFunds(String tradeId) {
|
||||
var request = KeepFundsRequest.newBuilder()
|
||||
.setTradeId(tradeId)
|
||||
.build();
|
||||
grpcStubs.tradesService.keepFunds(request);
|
||||
}
|
||||
|
||||
public void withdrawFunds(String tradeId, String address, String memo) {
|
||||
var request = WithdrawFundsRequest.newBuilder()
|
||||
.setTradeId(tradeId)
|
||||
.setAddress(address)
|
||||
.setMemo(memo)
|
||||
.build();
|
||||
grpcStubs.tradesService.withdrawFunds(request);
|
||||
}
|
||||
|
||||
public List<PaymentMethod> getPaymentMethods() {
|
||||
var request = GetPaymentMethodsRequest.newBuilder().build();
|
||||
return grpcStubs.paymentAccountsService.getPaymentMethods(request).getPaymentMethodsList();
|
||||
}
|
||||
|
||||
public String getPaymentAcctFormAsJson(String paymentMethodId) {
|
||||
var request = GetPaymentAccountFormRequest.newBuilder()
|
||||
.setPaymentMethodId(paymentMethodId)
|
||||
.build();
|
||||
return grpcStubs.paymentAccountsService.getPaymentAccountForm(request).getPaymentAccountFormJson();
|
||||
}
|
||||
|
||||
public PaymentAccount createPaymentAccount(String json) {
|
||||
var request = CreatePaymentAccountRequest.newBuilder()
|
||||
.setPaymentAccountForm(json)
|
||||
.build();
|
||||
return grpcStubs.paymentAccountsService.createPaymentAccount(request).getPaymentAccount();
|
||||
}
|
||||
|
||||
public List<PaymentAccount> getPaymentAccounts() {
|
||||
var request = GetPaymentAccountsRequest.newBuilder().build();
|
||||
return grpcStubs.paymentAccountsService.getPaymentAccounts(request).getPaymentAccountsList();
|
||||
}
|
||||
|
||||
public PaymentAccount createCryptoCurrencyPaymentAccount(String accountName,
|
||||
String currencyCode,
|
||||
String address,
|
||||
boolean tradeInstant) {
|
||||
var request = CreateCryptoCurrencyPaymentAccountRequest.newBuilder()
|
||||
.setAccountName(accountName)
|
||||
.setCurrencyCode(currencyCode)
|
||||
.setAddress(address)
|
||||
.setTradeInstant(tradeInstant)
|
||||
.build();
|
||||
return grpcStubs.paymentAccountsService.createCryptoCurrencyPaymentAccount(request).getPaymentAccount();
|
||||
}
|
||||
|
||||
public List<PaymentMethod> getCryptoPaymentMethods() {
|
||||
var request = GetCryptoCurrencyPaymentMethodsRequest.newBuilder().build();
|
||||
return grpcStubs.paymentAccountsService.getCryptoCurrencyPaymentMethods(request).getPaymentMethodsList();
|
||||
}
|
||||
|
||||
public void lockWallet() {
|
||||
var request = LockWalletRequest.newBuilder().build();
|
||||
grpcStubs.walletsService.lockWallet(request);
|
||||
}
|
||||
|
||||
public void unlockWallet(String walletPassword, long timeout) {
|
||||
var request = UnlockWalletRequest.newBuilder()
|
||||
.setPassword(walletPassword)
|
||||
.setTimeout(timeout).build();
|
||||
grpcStubs.walletsService.unlockWallet(request);
|
||||
}
|
||||
|
||||
public void removeWalletPassword(String walletPassword) {
|
||||
var request = RemoveWalletPasswordRequest.newBuilder()
|
||||
.setPassword(walletPassword).build();
|
||||
grpcStubs.walletsService.removeWalletPassword(request);
|
||||
}
|
||||
|
||||
public void setWalletPassword(String walletPassword) {
|
||||
var request = SetWalletPasswordRequest.newBuilder()
|
||||
.setPassword(walletPassword).build();
|
||||
grpcStubs.walletsService.setWalletPassword(request);
|
||||
}
|
||||
|
||||
public void setWalletPassword(String oldWalletPassword, String newWalletPassword) {
|
||||
var request = SetWalletPasswordRequest.newBuilder()
|
||||
.setPassword(oldWalletPassword)
|
||||
.setNewPassword(newWalletPassword).build();
|
||||
grpcStubs.walletsService.setWalletPassword(request);
|
||||
}
|
||||
|
||||
public void registerDisputeAgent(String disputeAgentType, String registrationKey) {
|
||||
var request = RegisterDisputeAgentRequest.newBuilder()
|
||||
.setDisputeAgentType(disputeAgentType).setRegistrationKey(registrationKey).build();
|
||||
grpcStubs.disputeAgentsService.registerDisputeAgent(request);
|
||||
}
|
||||
|
||||
public void stopServer() {
|
||||
var request = StopRequest.newBuilder().build();
|
||||
grpcStubs.shutdownService.stop(request);
|
||||
}
|
||||
|
||||
public String getMethodHelp(Method method) {
|
||||
var request = GetMethodHelpRequest.newBuilder().setMethodName(method.name()).build();
|
||||
return grpcStubs.helpService.getMethodHelp(request).getMethodHelp();
|
||||
}
|
||||
}
|
69
cli/src/main/java/bisq/cli/GrpcStubs.java
Normal file
69
cli/src/main/java/bisq/cli/GrpcStubs.java
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import bisq.proto.grpc.DisputeAgentsGrpc;
|
||||
import bisq.proto.grpc.GetVersionGrpc;
|
||||
import bisq.proto.grpc.HelpGrpc;
|
||||
import bisq.proto.grpc.OffersGrpc;
|
||||
import bisq.proto.grpc.PaymentAccountsGrpc;
|
||||
import bisq.proto.grpc.PriceGrpc;
|
||||
import bisq.proto.grpc.ShutdownServerGrpc;
|
||||
import bisq.proto.grpc.TradesGrpc;
|
||||
import bisq.proto.grpc.WalletsGrpc;
|
||||
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
public final class GrpcStubs {
|
||||
|
||||
public final DisputeAgentsGrpc.DisputeAgentsBlockingStub disputeAgentsService;
|
||||
public final HelpGrpc.HelpBlockingStub helpService;
|
||||
public final GetVersionGrpc.GetVersionBlockingStub versionService;
|
||||
public final OffersGrpc.OffersBlockingStub offersService;
|
||||
public final PaymentAccountsGrpc.PaymentAccountsBlockingStub paymentAccountsService;
|
||||
public final PriceGrpc.PriceBlockingStub priceService;
|
||||
public final ShutdownServerGrpc.ShutdownServerBlockingStub shutdownService;
|
||||
public final TradesGrpc.TradesBlockingStub tradesService;
|
||||
public final WalletsGrpc.WalletsBlockingStub walletsService;
|
||||
|
||||
public GrpcStubs(String apiHost, int apiPort, String apiPassword) {
|
||||
CallCredentials credentials = new PasswordCallCredentials(apiPassword);
|
||||
|
||||
var channel = ManagedChannelBuilder.forAddress(apiHost, apiPort).usePlaintext().build();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
channel.shutdown().awaitTermination(1, SECONDS);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}));
|
||||
|
||||
this.disputeAgentsService = DisputeAgentsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.helpService = HelpGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.versionService = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.offersService = OffersGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.paymentAccountsService = PaymentAccountsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.priceService = PriceGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.shutdownService = ShutdownServerGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.tradesService = TradesGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.walletsService = WalletsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
}
|
||||
}
|
60
cli/src/main/java/bisq/cli/Method.java
Normal file
60
cli/src/main/java/bisq/cli/Method.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
/**
|
||||
* Currently supported api methods.
|
||||
*/
|
||||
public enum Method {
|
||||
canceloffer,
|
||||
confirmpaymentreceived,
|
||||
confirmpaymentstarted,
|
||||
createoffer,
|
||||
createpaymentacct,
|
||||
createcryptopaymentacct,
|
||||
getaddressbalance,
|
||||
getbalance,
|
||||
getbtcprice,
|
||||
getfundingaddresses,
|
||||
getmyoffer,
|
||||
getmyoffers,
|
||||
getoffer,
|
||||
getoffers,
|
||||
getpaymentacctform,
|
||||
getpaymentaccts,
|
||||
getpaymentmethods,
|
||||
gettrade,
|
||||
gettransaction,
|
||||
gettxfeerate,
|
||||
getunusedbsqaddress,
|
||||
getversion,
|
||||
keepfunds,
|
||||
lockwallet,
|
||||
registerdisputeagent,
|
||||
removewalletpassword,
|
||||
sendbsq,
|
||||
sendbtc,
|
||||
verifybsqsenttoaddress,
|
||||
settxfeerate,
|
||||
setwalletpassword,
|
||||
takeoffer,
|
||||
unlockwallet,
|
||||
unsettxfeerate,
|
||||
withdrawfunds,
|
||||
stop
|
||||
}
|
62
cli/src/main/java/bisq/cli/PasswordCallCredentials.java
Normal file
62
cli/src/main/java/bisq/cli/PasswordCallCredentials.java
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.Metadata.Key;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
|
||||
import static io.grpc.Status.UNAUTHENTICATED;
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Sets the {@value PASSWORD_KEY} rpc call header to a given value.
|
||||
*/
|
||||
class PasswordCallCredentials extends CallCredentials {
|
||||
|
||||
public static final String PASSWORD_KEY = "password";
|
||||
|
||||
private final String passwordValue;
|
||||
|
||||
public PasswordCallCredentials(String passwordValue) {
|
||||
if (passwordValue == null)
|
||||
throw new IllegalArgumentException(format("'%s' value must not be null", PASSWORD_KEY));
|
||||
this.passwordValue = passwordValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyRequestMetadata(RequestInfo requestInfo, Executor appExecutor, MetadataApplier metadataApplier) {
|
||||
appExecutor.execute(() -> {
|
||||
try {
|
||||
var headers = new Metadata();
|
||||
var passwordKey = Key.of(PASSWORD_KEY, ASCII_STRING_MARSHALLER);
|
||||
headers.put(passwordKey, passwordValue);
|
||||
metadataApplier.apply(headers);
|
||||
} catch (Throwable ex) {
|
||||
metadataApplier.fail(UNAUTHENTICATED.withCause(ex));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void thisUsesUnstableApi() {
|
||||
}
|
||||
}
|
276
cli/src/main/java/bisq/cli/TableFormat.java
Normal file
276
cli/src/main/java/bisq/cli/TableFormat.java
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import bisq.proto.grpc.AddressBalanceInfo;
|
||||
import bisq.proto.grpc.BalancesInfo;
|
||||
import bisq.proto.grpc.BsqBalanceInfo;
|
||||
import bisq.proto.grpc.BtcBalanceInfo;
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import protobuf.PaymentAccount;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.cli.ColumnHeaderConstants.*;
|
||||
import static bisq.cli.CurrencyFormat.*;
|
||||
import static bisq.cli.DirectionFormat.directionFormat;
|
||||
import static bisq.cli.DirectionFormat.getLongestDirectionColWidth;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Collections.max;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.TimeZone.getTimeZone;
|
||||
|
||||
@VisibleForTesting
|
||||
public class TableFormat {
|
||||
|
||||
static final TimeZone TZ_UTC = getTimeZone("UTC");
|
||||
static final SimpleDateFormat DATE_FORMAT_ISO_8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
|
||||
public static String formatAddressBalanceTbl(List<AddressBalanceInfo> addressBalanceInfo) {
|
||||
String headerFormatString = COL_HEADER_ADDRESS + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_AVAILABLE_BALANCE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_CONFIRMATIONS + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_IS_USED_ADDRESS + COL_HEADER_DELIMITER + "\n";
|
||||
String headerLine = format(headerFormatString, "BTC");
|
||||
|
||||
String colDataFormat = "%-" + COL_HEADER_ADDRESS.length() + "s" // lt justify
|
||||
+ " %" + (COL_HEADER_AVAILABLE_BALANCE.length() - 1) + "s" // rt justify
|
||||
+ " %" + COL_HEADER_CONFIRMATIONS.length() + "d" // rt justify
|
||||
+ " %-" + COL_HEADER_IS_USED_ADDRESS.length() + "s"; // lt justify
|
||||
return headerLine
|
||||
+ addressBalanceInfo.stream()
|
||||
.map(info -> format(colDataFormat,
|
||||
info.getAddress(),
|
||||
formatSatoshis(info.getBalance()),
|
||||
info.getNumConfirmations(),
|
||||
info.getIsAddressUnused() ? "NO" : "YES"))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
public static String formatBalancesTbls(BalancesInfo balancesInfo) {
|
||||
return "BTC" + "\n"
|
||||
+ formatBtcBalanceInfoTbl(balancesInfo.getBtc()) + "\n"
|
||||
+ "BSQ" + "\n"
|
||||
+ formatBsqBalanceInfoTbl(balancesInfo.getBsq());
|
||||
}
|
||||
|
||||
public static String formatBsqBalanceInfoTbl(BsqBalanceInfo bsqBalanceInfo) {
|
||||
String headerLine = COL_HEADER_AVAILABLE_CONFIRMED_BALANCE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_UNVERIFIED_BALANCE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_UNCONFIRMED_CHANGE_BALANCE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_LOCKED_FOR_VOTING_BALANCE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_LOCKUP_BONDS_BALANCE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_UNLOCKING_BONDS_BALANCE + COL_HEADER_DELIMITER + "\n";
|
||||
String colDataFormat = "%" + COL_HEADER_AVAILABLE_CONFIRMED_BALANCE.length() + "s" // rt justify
|
||||
+ " %" + (COL_HEADER_UNVERIFIED_BALANCE.length() + 1) + "s" // rt justify
|
||||
+ " %" + (COL_HEADER_UNCONFIRMED_CHANGE_BALANCE.length() + 1) + "s" // rt justify
|
||||
+ " %" + (COL_HEADER_LOCKED_FOR_VOTING_BALANCE.length() + 1) + "s" // rt justify
|
||||
+ " %" + (COL_HEADER_LOCKUP_BONDS_BALANCE.length() + 1) + "s" // rt justify
|
||||
+ " %" + (COL_HEADER_UNLOCKING_BONDS_BALANCE.length() + 1) + "s"; // rt justify
|
||||
return headerLine + format(colDataFormat,
|
||||
formatBsq(bsqBalanceInfo.getAvailableConfirmedBalance()),
|
||||
formatBsq(bsqBalanceInfo.getUnverifiedBalance()),
|
||||
formatBsq(bsqBalanceInfo.getUnconfirmedChangeBalance()),
|
||||
formatBsq(bsqBalanceInfo.getLockedForVotingBalance()),
|
||||
formatBsq(bsqBalanceInfo.getLockupBondsBalance()),
|
||||
formatBsq(bsqBalanceInfo.getUnlockingBondsBalance()));
|
||||
}
|
||||
|
||||
public static String formatBtcBalanceInfoTbl(BtcBalanceInfo btcBalanceInfo) {
|
||||
String headerLine = COL_HEADER_AVAILABLE_BALANCE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_RESERVED_BALANCE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_TOTAL_AVAILABLE_BALANCE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_LOCKED_BALANCE + COL_HEADER_DELIMITER + "\n";
|
||||
String colDataFormat = "%" + COL_HEADER_AVAILABLE_BALANCE.length() + "s" // rt justify
|
||||
+ " %" + (COL_HEADER_RESERVED_BALANCE.length() + 1) + "s" // rt justify
|
||||
+ " %" + (COL_HEADER_TOTAL_AVAILABLE_BALANCE.length() + 1) + "s" // rt justify
|
||||
+ " %" + (COL_HEADER_LOCKED_BALANCE.length() + 1) + "s"; // rt justify
|
||||
return headerLine + format(colDataFormat,
|
||||
formatSatoshis(btcBalanceInfo.getAvailableBalance()),
|
||||
formatSatoshis(btcBalanceInfo.getReservedBalance()),
|
||||
formatSatoshis(btcBalanceInfo.getTotalAvailableBalance()),
|
||||
formatSatoshis(btcBalanceInfo.getLockedBalance()));
|
||||
}
|
||||
|
||||
public static String formatPaymentAcctTbl(List<PaymentAccount> paymentAccounts) {
|
||||
// Some column values might be longer than header, so we need to calculate them.
|
||||
int nameColWidth = getLongestColumnSize(
|
||||
COL_HEADER_NAME.length(),
|
||||
paymentAccounts.stream().map(PaymentAccount::getAccountName)
|
||||
.collect(Collectors.toList()));
|
||||
int paymentMethodColWidth = getLongestColumnSize(
|
||||
COL_HEADER_PAYMENT_METHOD.length(),
|
||||
paymentAccounts.stream().map(a -> a.getPaymentMethod().getId())
|
||||
.collect(Collectors.toList()));
|
||||
String headerLine = padEnd(COL_HEADER_NAME, nameColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_CURRENCY + COL_HEADER_DELIMITER
|
||||
+ padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_UUID + COL_HEADER_DELIMITER + "\n";
|
||||
String colDataFormat = "%-" + nameColWidth + "s" // left justify
|
||||
+ " %-" + COL_HEADER_CURRENCY.length() + "s" // left justify
|
||||
+ " %-" + paymentMethodColWidth + "s" // left justify
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s"; // left justify
|
||||
return headerLine
|
||||
+ paymentAccounts.stream()
|
||||
.map(a -> format(colDataFormat,
|
||||
a.getAccountName(),
|
||||
a.getSelectedTradeCurrency().getCode(),
|
||||
a.getPaymentMethod().getId(),
|
||||
a.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
public static String formatOfferTable(List<OfferInfo> offers, String currencyCode) {
|
||||
if (offers == null || offers.isEmpty())
|
||||
throw new IllegalArgumentException(format("%s offers argument is empty", currencyCode.toLowerCase()));
|
||||
|
||||
String baseCurrencyCode = offers.get(0).getBaseCurrencyCode();
|
||||
return baseCurrencyCode.equalsIgnoreCase("BTC")
|
||||
? formatFiatOfferTable(offers, currencyCode)
|
||||
: formatCryptoCurrencyOfferTable(offers, baseCurrencyCode);
|
||||
}
|
||||
|
||||
private static String formatFiatOfferTable(List<OfferInfo> offers, String fiatCurrencyCode) {
|
||||
// Some column values might be longer than header, so we need to calculate them.
|
||||
int amountColWith = getLongestAmountColWidth(offers);
|
||||
int volumeColWidth = getLongestVolumeColWidth(offers);
|
||||
int paymentMethodColWidth = getLongestPaymentMethodColWidth(offers);
|
||||
String headersFormat = COL_HEADER_DIRECTION + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_PRICE + COL_HEADER_DELIMITER // includes %s -> fiatCurrencyCode
|
||||
+ padStart(COL_HEADER_AMOUNT, amountColWith, ' ') + COL_HEADER_DELIMITER
|
||||
// COL_HEADER_VOLUME includes %s -> fiatCurrencyCode
|
||||
+ padStart(COL_HEADER_VOLUME, volumeColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_CREATION_DATE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_UUID.trim() + "%n";
|
||||
String headerLine = format(headersFormat,
|
||||
fiatCurrencyCode.toUpperCase(),
|
||||
fiatCurrencyCode.toUpperCase());
|
||||
String colDataFormat = "%-" + (COL_HEADER_DIRECTION.length() + COL_HEADER_DELIMITER.length()) + "s"
|
||||
+ "%" + (COL_HEADER_PRICE.length() - 1) + "s"
|
||||
+ " %" + amountColWith + "s"
|
||||
+ " %" + (volumeColWidth - 1) + "s"
|
||||
+ " %-" + paymentMethodColWidth + "s"
|
||||
+ " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s";
|
||||
return headerLine
|
||||
+ offers.stream()
|
||||
.map(o -> format(colDataFormat,
|
||||
o.getDirection(),
|
||||
formatPrice(o.getPrice()),
|
||||
formatAmountRange(o.getMinAmount(), o.getAmount()),
|
||||
formatVolumeRange(o.getMinVolume(), o.getVolume()),
|
||||
o.getPaymentMethodShortName(),
|
||||
formatTimestamp(o.getDate()),
|
||||
o.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
private static String formatCryptoCurrencyOfferTable(List<OfferInfo> offers, String cryptoCurrencyCode) {
|
||||
// Some column values might be longer than header, so we need to calculate them.
|
||||
int directionColWidth = getLongestDirectionColWidth(offers);
|
||||
int amountColWith = getLongestAmountColWidth(offers);
|
||||
int volumeColWidth = getLongestCryptoCurrencyVolumeColWidth(offers);
|
||||
int paymentMethodColWidth = getLongestPaymentMethodColWidth(offers);
|
||||
// TODO use memoize function to avoid duplicate the formatting done above?
|
||||
String headersFormat = padEnd(COL_HEADER_DIRECTION, directionColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_PRICE_OF_ALTCOIN + COL_HEADER_DELIMITER // includes %s -> cryptoCurrencyCode
|
||||
+ padStart(COL_HEADER_AMOUNT, amountColWith, ' ') + COL_HEADER_DELIMITER
|
||||
// COL_HEADER_VOLUME includes %s -> cryptoCurrencyCode
|
||||
+ padStart(COL_HEADER_VOLUME, volumeColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_CREATION_DATE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_UUID.trim() + "%n";
|
||||
String headerLine = format(headersFormat,
|
||||
cryptoCurrencyCode.toUpperCase(),
|
||||
cryptoCurrencyCode.toUpperCase());
|
||||
String colDataFormat = "%-" + directionColWidth + "s"
|
||||
+ "%" + (COL_HEADER_PRICE_OF_ALTCOIN.length() + 1) + "s"
|
||||
+ " %" + amountColWith + "s"
|
||||
+ " %" + (volumeColWidth - 1) + "s"
|
||||
+ " %-" + paymentMethodColWidth + "s"
|
||||
+ " %-" + (COL_HEADER_CREATION_DATE.length()) + "s"
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s";
|
||||
return headerLine
|
||||
+ offers.stream()
|
||||
.map(o -> format(colDataFormat,
|
||||
directionFormat.apply(o),
|
||||
formatCryptoCurrencyPrice(o.getPrice()),
|
||||
formatAmountRange(o.getMinAmount(), o.getAmount()),
|
||||
formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()),
|
||||
o.getPaymentMethodShortName(),
|
||||
formatTimestamp(o.getDate()),
|
||||
o.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
private static int getLongestPaymentMethodColWidth(List<OfferInfo> offers) {
|
||||
return getLongestColumnSize(
|
||||
COL_HEADER_PAYMENT_METHOD.length(),
|
||||
offers.stream()
|
||||
.map(OfferInfo::getPaymentMethodShortName)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static int getLongestAmountColWidth(List<OfferInfo> offers) {
|
||||
return getLongestColumnSize(
|
||||
COL_HEADER_AMOUNT.length(),
|
||||
offers.stream()
|
||||
.map(o -> formatAmountRange(o.getMinAmount(), o.getAmount()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static int getLongestVolumeColWidth(List<OfferInfo> offers) {
|
||||
// Pad this col width by 1 space.
|
||||
return 1 + getLongestColumnSize(
|
||||
COL_HEADER_VOLUME.length(),
|
||||
offers.stream()
|
||||
.map(o -> formatVolumeRange(o.getMinVolume(), o.getVolume()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static int getLongestCryptoCurrencyVolumeColWidth(List<OfferInfo> offers) {
|
||||
// Pad this col width by 1 space.
|
||||
return 1 + getLongestColumnSize(
|
||||
COL_HEADER_VOLUME.length(),
|
||||
offers.stream()
|
||||
.map(o -> formatCryptoCurrencyVolumeRange(o.getMinVolume(), o.getVolume()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
// Return size of the longest string value, or the header.len, whichever is greater.
|
||||
private static int getLongestColumnSize(int headerLength, List<String> strings) {
|
||||
int longest = max(strings, comparing(String::length)).length();
|
||||
return Math.max(longest, headerLength);
|
||||
}
|
||||
|
||||
private static String formatTimestamp(long timestamp) {
|
||||
DATE_FORMAT_ISO_8601.setTimeZone(TZ_UTC);
|
||||
return DATE_FORMAT_ISO_8601.format(new Date(timestamp));
|
||||
}
|
||||
}
|
221
cli/src/main/java/bisq/cli/TradeFormat.java
Normal file
221
cli/src/main/java/bisq/cli/TradeFormat.java
Normal file
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import bisq.proto.grpc.ContractInfo;
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static bisq.cli.ColumnHeaderConstants.*;
|
||||
import static bisq.cli.CurrencyFormat.*;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
|
||||
@VisibleForTesting
|
||||
public class TradeFormat {
|
||||
|
||||
private static final String YES = "YES";
|
||||
private static final String NO = "NO";
|
||||
|
||||
// TODO add String format(List<TradeInfo> trades)
|
||||
|
||||
@VisibleForTesting
|
||||
public static String format(TradeInfo tradeInfo) {
|
||||
// Some column values might be longer than header, so we need to calculate them.
|
||||
int shortIdColWidth = Math.max(COL_HEADER_TRADE_SHORT_ID.length(), tradeInfo.getShortId().length());
|
||||
int roleColWidth = Math.max(COL_HEADER_TRADE_ROLE.length(), tradeInfo.getRole().length());
|
||||
|
||||
// We only show taker fee under its header when user is the taker.
|
||||
boolean isTaker = tradeInfo.getRole().toLowerCase().contains("taker");
|
||||
Supplier<String> makerFeeHeader = () -> !isTaker ?
|
||||
COL_HEADER_TRADE_MAKER_FEE + COL_HEADER_DELIMITER
|
||||
: "";
|
||||
Supplier<String> makerFeeHeaderSpec = () -> !isTaker ?
|
||||
"%" + (COL_HEADER_TRADE_MAKER_FEE.length() + 2) + "s"
|
||||
: "";
|
||||
Supplier<String> takerFeeHeader = () -> isTaker ?
|
||||
COL_HEADER_TRADE_TAKER_FEE + COL_HEADER_DELIMITER
|
||||
: "";
|
||||
Supplier<String> takerFeeHeaderSpec = () -> isTaker ?
|
||||
"%" + (COL_HEADER_TRADE_TAKER_FEE.length() + 2) + "s"
|
||||
: "";
|
||||
|
||||
boolean showBsqBuyerAddress = shouldShowBsqBuyerAddress(tradeInfo, isTaker);
|
||||
Supplier<String> bsqBuyerAddressHeader = () -> showBsqBuyerAddress ? COL_HEADER_TRADE_BSQ_BUYER_ADDRESS : "";
|
||||
Supplier<String> bsqBuyerAddressHeaderSpec = () -> showBsqBuyerAddress ? "%s" : "";
|
||||
|
||||
String headersFormat = padEnd(COL_HEADER_TRADE_SHORT_ID, shortIdColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ padEnd(COL_HEADER_TRADE_ROLE, roleColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ priceHeader.apply(tradeInfo) + COL_HEADER_DELIMITER // includes %s -> currencyCode
|
||||
+ padEnd(COL_HEADER_TRADE_AMOUNT, 12, ' ') + COL_HEADER_DELIMITER
|
||||
+ padEnd(COL_HEADER_TRADE_TX_FEE, 12, ' ') + COL_HEADER_DELIMITER
|
||||
+ makerFeeHeader.get()
|
||||
// maker or taker fee header, not both
|
||||
+ takerFeeHeader.get()
|
||||
+ COL_HEADER_TRADE_DEPOSIT_PUBLISHED + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_TRADE_DEPOSIT_CONFIRMED + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_TRADE_BUYER_COST + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_TRADE_PAYMENT_SENT + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_TRADE_PAYMENT_RECEIVED + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_TRADE_PAYOUT_PUBLISHED + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_TRADE_WITHDRAWN + COL_HEADER_DELIMITER
|
||||
+ bsqBuyerAddressHeader.get()
|
||||
+ "%n";
|
||||
|
||||
String counterCurrencyCode = tradeInfo.getOffer().getCounterCurrencyCode();
|
||||
String baseCurrencyCode = tradeInfo.getOffer().getBaseCurrencyCode();
|
||||
|
||||
String headerLine = String.format(headersFormat,
|
||||
/* COL_HEADER_PRICE */ priceHeaderCurrencyCode.apply(tradeInfo),
|
||||
/* COL_HEADER_TRADE_AMOUNT */ baseCurrencyCode,
|
||||
/* COL_HEADER_TRADE_(M||T)AKER_FEE */ makerTakerFeeHeaderCurrencyCode.apply(tradeInfo, isTaker),
|
||||
/* COL_HEADER_TRADE_BUYER_COST */ counterCurrencyCode,
|
||||
/* COL_HEADER_TRADE_PAYMENT_SENT */ paymentStatusHeaderCurrencyCode.apply(tradeInfo),
|
||||
/* COL_HEADER_TRADE_PAYMENT_RECEIVED */ paymentStatusHeaderCurrencyCode.apply(tradeInfo));
|
||||
|
||||
String colDataFormat = "%-" + shortIdColWidth + "s" // lt justify
|
||||
+ " %-" + (roleColWidth + COL_HEADER_DELIMITER.length()) + "s" // left
|
||||
+ "%" + (COL_HEADER_PRICE.length() - 1) + "s" // rt justify
|
||||
+ "%" + (COL_HEADER_TRADE_AMOUNT.length() + 1) + "s" // rt justify
|
||||
+ "%" + (COL_HEADER_TRADE_TX_FEE.length() + 1) + "s" // rt justify
|
||||
+ makerFeeHeaderSpec.get() // rt justify
|
||||
// OR (one of them is an empty string)
|
||||
+ takerFeeHeaderSpec.get() // rt justify
|
||||
+ " %-" + COL_HEADER_TRADE_DEPOSIT_PUBLISHED.length() + "s" // lt justify
|
||||
+ " %-" + COL_HEADER_TRADE_DEPOSIT_CONFIRMED.length() + "s" // lt justify
|
||||
+ "%" + (COL_HEADER_TRADE_BUYER_COST.length() + 1) + "s" // rt justify
|
||||
+ " %-" + (COL_HEADER_TRADE_PAYMENT_SENT.length() - 1) + "s" // left
|
||||
+ " %-" + (COL_HEADER_TRADE_PAYMENT_RECEIVED.length() - 1) + "s" // left
|
||||
+ " %-" + COL_HEADER_TRADE_PAYOUT_PUBLISHED.length() + "s" // lt justify
|
||||
+ " %-" + (COL_HEADER_TRADE_WITHDRAWN.length() + 2) + "s"
|
||||
+ bsqBuyerAddressHeaderSpec.get();
|
||||
|
||||
return headerLine + formatTradeData(colDataFormat, tradeInfo, isTaker, showBsqBuyerAddress);
|
||||
}
|
||||
|
||||
private static String formatTradeData(String format,
|
||||
TradeInfo tradeInfo,
|
||||
boolean isTaker,
|
||||
boolean showBsqBuyerAddress) {
|
||||
return String.format(format,
|
||||
tradeInfo.getShortId(),
|
||||
tradeInfo.getRole(),
|
||||
priceFormat.apply(tradeInfo),
|
||||
amountFormat.apply(tradeInfo),
|
||||
makerTakerMinerTxFeeFormat.apply(tradeInfo, isTaker),
|
||||
makerTakerFeeFormat.apply(tradeInfo, isTaker),
|
||||
tradeInfo.getIsDepositPublished() ? YES : NO,
|
||||
tradeInfo.getIsDepositConfirmed() ? YES : NO,
|
||||
tradeCostFormat.apply(tradeInfo),
|
||||
tradeInfo.getIsFiatSent() ? YES : NO,
|
||||
tradeInfo.getIsFiatReceived() ? YES : NO,
|
||||
tradeInfo.getIsPayoutPublished() ? YES : NO,
|
||||
tradeInfo.getIsWithdrawn() ? YES : NO,
|
||||
bsqReceiveAddress.apply(tradeInfo, showBsqBuyerAddress));
|
||||
}
|
||||
|
||||
private static final Function<TradeInfo, String> priceHeader = (t) ->
|
||||
t.getOffer().getBaseCurrencyCode().equals("BTC")
|
||||
? COL_HEADER_PRICE
|
||||
: COL_HEADER_PRICE_OF_ALTCOIN;
|
||||
|
||||
private static final Function<TradeInfo, String> priceHeaderCurrencyCode = (t) ->
|
||||
t.getOffer().getBaseCurrencyCode().equals("BTC")
|
||||
? t.getOffer().getCounterCurrencyCode()
|
||||
: t.getOffer().getBaseCurrencyCode();
|
||||
|
||||
private static final BiFunction<TradeInfo, Boolean, String> makerTakerFeeHeaderCurrencyCode = (t, isTaker) -> {
|
||||
if (isTaker) {
|
||||
return t.getIsCurrencyForTakerFeeBtc() ? "BTC" : "BSQ";
|
||||
} else {
|
||||
return t.getOffer().getIsCurrencyForMakerFeeBtc() ? "BTC" : "BSQ";
|
||||
}
|
||||
};
|
||||
|
||||
private static final Function<TradeInfo, String> paymentStatusHeaderCurrencyCode = (t) ->
|
||||
t.getOffer().getBaseCurrencyCode().equals("BTC")
|
||||
? t.getOffer().getCounterCurrencyCode()
|
||||
: t.getOffer().getBaseCurrencyCode();
|
||||
|
||||
private static final Function<TradeInfo, String> priceFormat = (t) ->
|
||||
t.getOffer().getBaseCurrencyCode().equals("BTC")
|
||||
? formatPrice(t.getTradePrice())
|
||||
: formatCryptoCurrencyPrice(t.getOffer().getPrice());
|
||||
|
||||
private static final Function<TradeInfo, String> amountFormat = (t) ->
|
||||
t.getOffer().getBaseCurrencyCode().equals("BTC")
|
||||
? formatSatoshis(t.getTradeAmountAsLong())
|
||||
: formatCryptoCurrencyOfferVolume(t.getOffer().getVolume());
|
||||
|
||||
private static final BiFunction<TradeInfo, Boolean, String> makerTakerMinerTxFeeFormat = (t, isTaker) -> {
|
||||
if (isTaker) {
|
||||
return formatSatoshis(t.getTxFeeAsLong());
|
||||
} else {
|
||||
return formatSatoshis(t.getOffer().getTxFee());
|
||||
}
|
||||
};
|
||||
|
||||
private static final BiFunction<TradeInfo, Boolean, String> makerTakerFeeFormat = (t, isTaker) -> {
|
||||
if (isTaker) {
|
||||
return t.getIsCurrencyForTakerFeeBtc()
|
||||
? formatSatoshis(t.getTakerFeeAsLong())
|
||||
: formatBsq(t.getTakerFeeAsLong());
|
||||
} else {
|
||||
return t.getOffer().getIsCurrencyForMakerFeeBtc()
|
||||
? formatSatoshis(t.getOffer().getMakerFee())
|
||||
: formatBsq(t.getOffer().getMakerFee());
|
||||
}
|
||||
};
|
||||
|
||||
private static final Function<TradeInfo, String> tradeCostFormat = (t) ->
|
||||
t.getOffer().getBaseCurrencyCode().equals("BTC")
|
||||
? formatOfferVolume(t.getOffer().getVolume())
|
||||
: formatSatoshis(t.getTradeAmountAsLong());
|
||||
|
||||
private static final BiFunction<TradeInfo, Boolean, String> bsqReceiveAddress = (t, showBsqBuyerAddress) -> {
|
||||
if (showBsqBuyerAddress) {
|
||||
ContractInfo contract = t.getContract();
|
||||
boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker();
|
||||
return isBuyerMakerAndSellerTaker // (is BTC buyer / maker)
|
||||
? contract.getTakerPaymentAccountPayload().getAddress()
|
||||
: contract.getMakerPaymentAccountPayload().getAddress();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
private static boolean shouldShowBsqBuyerAddress(TradeInfo tradeInfo, boolean isTaker) {
|
||||
if (tradeInfo.getOffer().getBaseCurrencyCode().equals("BTC")) {
|
||||
return false;
|
||||
} else {
|
||||
ContractInfo contract = tradeInfo.getContract();
|
||||
// Do not forget buyer and seller refer to BTC buyer and seller, not BSQ
|
||||
// buyer and seller. If you are buying BSQ, you are the (BTC) seller.
|
||||
boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker();
|
||||
if (isTaker) {
|
||||
return !isBuyerMakerAndSellerTaker;
|
||||
} else {
|
||||
return isBuyerMakerAndSellerTaker;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
59
cli/src/main/java/bisq/cli/TransactionFormat.java
Normal file
59
cli/src/main/java/bisq/cli/TransactionFormat.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import bisq.proto.grpc.TxInfo;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import static bisq.cli.ColumnHeaderConstants.*;
|
||||
import static bisq.cli.CurrencyFormat.formatSatoshis;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
|
||||
@VisibleForTesting
|
||||
public class TransactionFormat {
|
||||
|
||||
public static String format(TxInfo txInfo) {
|
||||
String headerLine = padEnd(COL_HEADER_TX_ID, txInfo.getTxId().length(), ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_TX_IS_CONFIRMED + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_TX_INPUT_SUM + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_TX_OUTPUT_SUM + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_TX_FEE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_TX_SIZE + COL_HEADER_DELIMITER
|
||||
+ (txInfo.getMemo().isEmpty() ? "" : COL_HEADER_TX_MEMO + COL_HEADER_DELIMITER)
|
||||
+ "\n";
|
||||
|
||||
String colDataFormat = "%-" + txInfo.getTxId().length() + "s"
|
||||
+ " %" + COL_HEADER_TX_IS_CONFIRMED.length() + "s"
|
||||
+ " %" + COL_HEADER_TX_INPUT_SUM.length() + "s"
|
||||
+ " %" + COL_HEADER_TX_OUTPUT_SUM.length() + "s"
|
||||
+ " %" + COL_HEADER_TX_FEE.length() + "s"
|
||||
+ " %" + COL_HEADER_TX_SIZE.length() + "s"
|
||||
+ " %s";
|
||||
|
||||
return headerLine
|
||||
+ String.format(colDataFormat,
|
||||
txInfo.getTxId(),
|
||||
txInfo.getIsPending() ? "NO" : "YES", // pending=true means not confirmed
|
||||
formatSatoshis(txInfo.getInputSum()),
|
||||
formatSatoshis(txInfo.getOutputSum()),
|
||||
formatSatoshis(txInfo.getFee()),
|
||||
txInfo.getSize(),
|
||||
txInfo.getMemo().isEmpty() ? "" : txInfo.getMemo());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
import joptsimple.OptionException;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_HELP;
|
||||
|
||||
abstract class AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
// The full command line args passed to CliMain.main(String[] args).
|
||||
// CLI and Method level arguments are derived from args by an ArgumentList(args).
|
||||
protected final String[] args;
|
||||
|
||||
protected final OptionParser parser = new OptionParser();
|
||||
|
||||
// The help option for a specific api method, e.g., takeoffer -help.
|
||||
protected final OptionSpec<Void> helpOpt = parser.accepts(OPT_HELP, "Print method help").forHelp();
|
||||
|
||||
@Getter
|
||||
protected OptionSet options;
|
||||
@Getter
|
||||
protected List<String> nonOptionArguments;
|
||||
|
||||
protected AbstractMethodOptionParser(String[] args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public AbstractMethodOptionParser parse() {
|
||||
try {
|
||||
options = parser.parse(new ArgumentList(args).getMethodArguments());
|
||||
//noinspection unchecked
|
||||
nonOptionArguments = (List<String>) options.nonOptionArguments();
|
||||
return this;
|
||||
} catch (OptionException ex) {
|
||||
throw new IllegalArgumentException(cliExceptionMessageStyle.apply(ex), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isForHelp() {
|
||||
return options.has(helpOpt);
|
||||
}
|
||||
|
||||
private final Function<OptionException, String> cliExceptionMessageStyle = (ex) -> {
|
||||
if (ex.getMessage() == null)
|
||||
return null;
|
||||
|
||||
var optionToken = "option ";
|
||||
var cliMessage = ex.getMessage().toLowerCase();
|
||||
if (cliMessage.startsWith(optionToken) && cliMessage.length() > optionToken.length()) {
|
||||
cliMessage = cliMessage.substring(cliMessage.indexOf(" ") + 1);
|
||||
}
|
||||
return cliMessage;
|
||||
};
|
||||
}
|
124
cli/src/main/java/bisq/cli/opts/ArgumentList.java
Normal file
124
cli/src/main/java/bisq/cli/opts/ArgumentList.java
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Wrapper for an array of command line arguments.
|
||||
*
|
||||
* Used to extract CLI connection and authentication arguments, or method arguments
|
||||
* before parsing CLI or method opts
|
||||
*
|
||||
*/
|
||||
public class ArgumentList {
|
||||
|
||||
private final Predicate<String> isCliOpt = (o) ->
|
||||
o.startsWith("--password") || o.startsWith("-password")
|
||||
|| o.startsWith("--port") || o.startsWith("-port")
|
||||
|| o.startsWith("--host") || o.startsWith("-host");
|
||||
|
||||
|
||||
// The method name is the only positional opt in a command (easy to identify).
|
||||
// If the positional argument does not match a Method, or there are more than one
|
||||
// positional arguments, the joptsimple parser or CLI will fail as expected.
|
||||
private final Predicate<String> isMethodNameOpt = (o) -> !o.startsWith("-");
|
||||
|
||||
private final Predicate<String> isHelpOpt = (o) -> o.startsWith("--help") || o.startsWith("-help");
|
||||
|
||||
private final String[] arguments;
|
||||
private int currentIndex;
|
||||
|
||||
public ArgumentList(String... arguments) {
|
||||
this.arguments = arguments.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only the CLI connection & authentication, and method name args
|
||||
* (--password, --host, --port, --help, method name) contained in the original
|
||||
* String[] args; excludes the method specific arguments.
|
||||
*
|
||||
* If String[] args contains both a method name (the only positional opt) and a help
|
||||
* argument (--help, -help), it is assumed the user wants method help, not CLI help,
|
||||
* and the help argument is not included in the returned String[].
|
||||
*/
|
||||
public String[] getCLIArguments() {
|
||||
currentIndex = 0;
|
||||
Optional<String> methodNameArgument = Optional.empty();
|
||||
Optional<String> helpArgument = Optional.empty();
|
||||
List<String> prunedArguments = new ArrayList<>();
|
||||
|
||||
while (hasMore()) {
|
||||
String arg = peek();
|
||||
if (isMethodNameOpt.test(arg)) {
|
||||
methodNameArgument = Optional.of(arg);
|
||||
prunedArguments.add(arg);
|
||||
}
|
||||
|
||||
if (isCliOpt.test(arg))
|
||||
prunedArguments.add(arg);
|
||||
|
||||
if (isHelpOpt.test(arg))
|
||||
helpArgument = Optional.of(arg);
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
// Include the saved CLI help argument if the positional method name argument
|
||||
// was not found.
|
||||
if (!methodNameArgument.isPresent() && helpArgument.isPresent())
|
||||
prunedArguments.add(helpArgument.get());
|
||||
|
||||
return prunedArguments.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only the method args contained in the original String[] args; excludes the
|
||||
* CLI connection & authentication opts (--password, --host, --port), plus the
|
||||
* positional method name arg.
|
||||
*/
|
||||
public String[] getMethodArguments() {
|
||||
List<String> prunedArguments = new ArrayList<>();
|
||||
currentIndex = 0;
|
||||
while (hasMore()) {
|
||||
String arg = peek();
|
||||
if (!isCliOpt.test(arg) && !isMethodNameOpt.test(arg)) {
|
||||
prunedArguments.add(arg);
|
||||
}
|
||||
next();
|
||||
}
|
||||
return prunedArguments.toArray(new String[0]);
|
||||
}
|
||||
|
||||
|
||||
boolean hasMore() {
|
||||
return currentIndex < arguments.length;
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
String next() {
|
||||
return arguments[currentIndex++];
|
||||
}
|
||||
|
||||
String peek() {
|
||||
return arguments[currentIndex];
|
||||
}
|
||||
}
|
50
cli/src/main/java/bisq/cli/opts/CancelOfferOptionParser.java
Normal file
50
cli/src/main/java/bisq/cli/opts/CancelOfferOptionParser.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_OFFER_ID;
|
||||
|
||||
public class CancelOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to cancel")
|
||||
.withRequiredArg();
|
||||
|
||||
public CancelOfferOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public CancelOfferOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(offerIdOpt) || options.valueOf(offerIdOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no offer id specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getOfferId() {
|
||||
return options.valueOf(offerIdOpt);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_ACCOUNT_NAME;
|
||||
import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
|
||||
import static bisq.cli.opts.OptLabel.OPT_CURRENCY_CODE;
|
||||
import static bisq.cli.opts.OptLabel.OPT_TRADE_INSTANT;
|
||||
|
||||
public class CreateCryptoCurrencyPaymentAcctOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> accountNameOpt = parser.accepts(OPT_ACCOUNT_NAME, "crypto currency account name")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "crypto currency code (bsq only)")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> addressOpt = parser.accepts(OPT_ADDRESS, "bsq address")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<Boolean> tradeInstantOpt = parser.accepts(OPT_TRADE_INSTANT, "create trade instant account")
|
||||
.withOptionalArg()
|
||||
.ofType(boolean.class)
|
||||
.defaultsTo(Boolean.FALSE);
|
||||
|
||||
public CreateCryptoCurrencyPaymentAcctOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public CreateCryptoCurrencyPaymentAcctOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(accountNameOpt) || options.valueOf(accountNameOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no payment account name specified");
|
||||
|
||||
if (!options.has(currencyCodeOpt) || options.valueOf(currencyCodeOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no currency code specified");
|
||||
|
||||
if (!options.valueOf(currencyCodeOpt).equalsIgnoreCase("bsq"))
|
||||
throw new IllegalArgumentException("api only supports bsq crypto currency payment accounts");
|
||||
|
||||
if (!options.has(addressOpt) || options.valueOf(addressOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no bsq address specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAccountName() {
|
||||
return options.valueOf(accountNameOpt);
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return options.valueOf(currencyCodeOpt);
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return options.valueOf(addressOpt);
|
||||
}
|
||||
|
||||
public boolean getIsTradeInstant() {
|
||||
return options.valueOf(tradeInstantOpt);
|
||||
}
|
||||
}
|
144
cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java
Normal file
144
cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.*;
|
||||
import static joptsimple.internal.Strings.EMPTY;
|
||||
|
||||
public class CreateOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT,
|
||||
"id of payment account used for offer")
|
||||
.withRequiredArg()
|
||||
.defaultsTo(EMPTY);
|
||||
|
||||
final OptionSpec<String> directionOpt = parser.accepts(OPT_DIRECTION, "offer direction (buy|sell)")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency code (eur|usd|...)")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> amountOpt = parser.accepts(OPT_AMOUNT, "amount of btc to buy or sell")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> minAmountOpt = parser.accepts(OPT_MIN_AMOUNT, "minimum amount of btc to buy or sell")
|
||||
.withOptionalArg();
|
||||
|
||||
final OptionSpec<String> mktPriceMarginOpt = parser.accepts(OPT_MKT_PRICE_MARGIN, "market btc price margin (%)")
|
||||
.withOptionalArg()
|
||||
.defaultsTo("0.00");
|
||||
|
||||
final OptionSpec<String> fixedPriceOpt = parser.accepts(OPT_FIXED_PRICE, "fixed btc price")
|
||||
.withOptionalArg()
|
||||
.defaultsTo("0");
|
||||
|
||||
final OptionSpec<String> securityDepositOpt = parser.accepts(OPT_SECURITY_DEPOSIT, "maker security deposit (%)")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> makerFeeCurrencyCodeOpt = parser.accepts(OPT_FEE_CURRENCY, "maker fee currency code (bsq|btc)")
|
||||
.withOptionalArg()
|
||||
.defaultsTo("btc");
|
||||
|
||||
public CreateOfferOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public CreateOfferOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no payment account id specified");
|
||||
|
||||
if (!options.has(directionOpt) || options.valueOf(directionOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no direction (buy|sell) specified");
|
||||
|
||||
if (!options.has(currencyCodeOpt) || options.valueOf(currencyCodeOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no currency code specified");
|
||||
|
||||
if (!options.has(amountOpt) || options.valueOf(amountOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no btc amount specified");
|
||||
|
||||
if (!options.has(mktPriceMarginOpt) && !options.has(fixedPriceOpt))
|
||||
throw new IllegalArgumentException("no market price margin or fixed price specified");
|
||||
|
||||
if (options.has(mktPriceMarginOpt) && options.valueOf(mktPriceMarginOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no market price margin specified");
|
||||
|
||||
if (options.has(fixedPriceOpt) && options.valueOf(fixedPriceOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no fixed price specified");
|
||||
|
||||
if (!options.has(securityDepositOpt) || options.valueOf(securityDepositOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no security deposit specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPaymentAccountId() {
|
||||
return options.valueOf(paymentAccountIdOpt);
|
||||
}
|
||||
|
||||
public String getDirection() {
|
||||
return options.valueOf(directionOpt);
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return options.valueOf(currencyCodeOpt);
|
||||
}
|
||||
|
||||
public String getAmount() {
|
||||
return options.valueOf(amountOpt);
|
||||
}
|
||||
|
||||
public String getMinAmount() {
|
||||
return options.has(minAmountOpt) ? options.valueOf(minAmountOpt) : getAmount();
|
||||
}
|
||||
|
||||
public boolean isUsingMktPriceMargin() {
|
||||
return options.has(mktPriceMarginOpt);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String getMktPriceMargin() {
|
||||
return isUsingMktPriceMargin() ? options.valueOf(mktPriceMarginOpt) : "0.00";
|
||||
}
|
||||
|
||||
public BigDecimal getMktPriceMarginAsBigDecimal() {
|
||||
return isUsingMktPriceMargin() ? new BigDecimal(options.valueOf(mktPriceMarginOpt)) : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
public String getFixedPrice() {
|
||||
return options.has(fixedPriceOpt) ? options.valueOf(fixedPriceOpt) : "0.00";
|
||||
}
|
||||
|
||||
public String getSecurityDeposit() {
|
||||
return options.valueOf(securityDepositOpt);
|
||||
}
|
||||
|
||||
public String getMakerFeeCurrencyCode() {
|
||||
return options.has(makerFeeCurrencyCodeOpt) ? options.valueOf(makerFeeCurrencyCodeOpt) : "btc";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_FORM;
|
||||
import static java.lang.String.format;
|
||||
|
||||
public class CreatePaymentAcctOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> paymentAcctFormPathOpt = parser.accepts(OPT_PAYMENT_ACCOUNT_FORM,
|
||||
"path to json payment account form")
|
||||
.withRequiredArg();
|
||||
|
||||
public CreatePaymentAcctOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public CreatePaymentAcctOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(paymentAcctFormPathOpt) || options.valueOf(paymentAcctFormPathOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no path to json payment account form specified");
|
||||
|
||||
Path path = Paths.get(options.valueOf(paymentAcctFormPathOpt));
|
||||
if (!path.toFile().exists())
|
||||
throw new IllegalStateException(
|
||||
format("json payment account form '%s' could not be found",
|
||||
path));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Path getPaymentAcctForm() {
|
||||
return Paths.get(options.valueOf(paymentAcctFormPathOpt));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
|
||||
|
||||
public class GetAddressBalanceOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> addressOpt = parser.accepts(OPT_ADDRESS, "wallet btc address")
|
||||
.withRequiredArg();
|
||||
|
||||
public GetAddressBalanceOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public GetAddressBalanceOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(addressOpt) || options.valueOf(addressOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no address specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return options.valueOf(addressOpt);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_CURRENCY_CODE;
|
||||
|
||||
public class GetBTCMarketPriceOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency-code")
|
||||
.withRequiredArg();
|
||||
|
||||
public GetBTCMarketPriceOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public GetBTCMarketPriceOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(currencyCodeOpt) || options.valueOf(currencyCodeOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no currency code specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return options.valueOf(currencyCodeOpt);
|
||||
}
|
||||
}
|
43
cli/src/main/java/bisq/cli/opts/GetBalanceOptionParser.java
Normal file
43
cli/src/main/java/bisq/cli/opts/GetBalanceOptionParser.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_CURRENCY_CODE;
|
||||
import static joptsimple.internal.Strings.EMPTY;
|
||||
|
||||
public class GetBalanceOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "wallet currency code (bsq|btc)")
|
||||
.withOptionalArg()
|
||||
.defaultsTo(EMPTY);
|
||||
|
||||
public GetBalanceOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public GetBalanceOptionParser parse() {
|
||||
return (GetBalanceOptionParser) super.parse();
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return options.has(currencyCodeOpt) ? options.valueOf(currencyCodeOpt) : "";
|
||||
}
|
||||
}
|
50
cli/src/main/java/bisq/cli/opts/GetOfferOptionParser.java
Normal file
50
cli/src/main/java/bisq/cli/opts/GetOfferOptionParser.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_OFFER_ID;
|
||||
|
||||
public class GetOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to get")
|
||||
.withRequiredArg();
|
||||
|
||||
public GetOfferOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public GetOfferOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(offerIdOpt) || options.valueOf(offerIdOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no offer id specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getOfferId() {
|
||||
return options.valueOf(offerIdOpt);
|
||||
}
|
||||
}
|
61
cli/src/main/java/bisq/cli/opts/GetOffersOptionParser.java
Normal file
61
cli/src/main/java/bisq/cli/opts/GetOffersOptionParser.java
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_CURRENCY_CODE;
|
||||
import static bisq.cli.opts.OptLabel.OPT_DIRECTION;
|
||||
|
||||
public class GetOffersOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> directionOpt = parser.accepts(OPT_DIRECTION, "offer direction (buy|sell)")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency code (eur|usd|...)")
|
||||
.withRequiredArg();
|
||||
|
||||
public GetOffersOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public GetOffersOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(directionOpt) || options.valueOf(directionOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no direction (buy|sell) specified");
|
||||
|
||||
if (!options.has(currencyCodeOpt) || options.valueOf(currencyCodeOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no currency code specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDirection() {
|
||||
return options.valueOf(directionOpt);
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return options.valueOf(currencyCodeOpt);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_PAYMENT_METHOD_ID;
|
||||
|
||||
public class GetPaymentAcctFormOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> paymentMethodIdOpt = parser.accepts(OPT_PAYMENT_METHOD_ID,
|
||||
"id of payment method type used by a payment account")
|
||||
.withRequiredArg();
|
||||
|
||||
public GetPaymentAcctFormOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public GetPaymentAcctFormOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(paymentMethodIdOpt) || options.valueOf(paymentMethodIdOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no payment method id specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPaymentMethodId() {
|
||||
return options.valueOf(paymentMethodIdOpt);
|
||||
}
|
||||
}
|
60
cli/src/main/java/bisq/cli/opts/GetTradeOptionParser.java
Normal file
60
cli/src/main/java/bisq/cli/opts/GetTradeOptionParser.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_SHOW_CONTRACT;
|
||||
import static bisq.cli.opts.OptLabel.OPT_TRADE_ID;
|
||||
|
||||
public class GetTradeOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> tradeIdOpt = parser.accepts(OPT_TRADE_ID, "id of trade")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<Boolean> showContractOpt = parser.accepts(OPT_SHOW_CONTRACT, "show trade's json contract")
|
||||
.withOptionalArg()
|
||||
.ofType(boolean.class)
|
||||
.defaultsTo(Boolean.FALSE);
|
||||
|
||||
public GetTradeOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public GetTradeOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(tradeIdOpt) || options.valueOf(tradeIdOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no trade id specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getTradeId() {
|
||||
return options.valueOf(tradeIdOpt);
|
||||
}
|
||||
|
||||
public boolean getShowContract() {
|
||||
return options.has(showContractOpt) ? options.valueOf(showContractOpt) : false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_TRANSACTION_ID;
|
||||
|
||||
public class GetTransactionOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> txIdOpt = parser.accepts(OPT_TRANSACTION_ID, "id of transaction")
|
||||
.withRequiredArg();
|
||||
|
||||
public GetTransactionOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public GetTransactionOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(txIdOpt) || options.valueOf(txIdOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no tx id specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getTxId() {
|
||||
return options.valueOf(txIdOpt);
|
||||
}
|
||||
}
|
25
cli/src/main/java/bisq/cli/opts/MethodOpts.java
Normal file
25
cli/src/main/java/bisq/cli/opts/MethodOpts.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
public interface MethodOpts {
|
||||
|
||||
MethodOpts parse();
|
||||
|
||||
boolean isForHelp();
|
||||
}
|
53
cli/src/main/java/bisq/cli/opts/OptLabel.java
Normal file
53
cli/src/main/java/bisq/cli/opts/OptLabel.java
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
/**
|
||||
* CLI opt label definitions.
|
||||
*/
|
||||
public class OptLabel {
|
||||
public final static String OPT_ACCOUNT_NAME = "account-name";
|
||||
public final static String OPT_ADDRESS = "address";
|
||||
public final static String OPT_AMOUNT = "amount";
|
||||
public final static String OPT_CURRENCY_CODE = "currency-code";
|
||||
public final static String OPT_DIRECTION = "direction";
|
||||
public final static String OPT_DISPUTE_AGENT_TYPE = "dispute-agent-type";
|
||||
public final static String OPT_FEE_CURRENCY = "fee-currency";
|
||||
public final static String OPT_FIXED_PRICE = "fixed-price";
|
||||
public final static String OPT_HELP = "help";
|
||||
public final static String OPT_HOST = "host";
|
||||
public final static String OPT_MEMO = "memo";
|
||||
public final static String OPT_MKT_PRICE_MARGIN = "market-price-margin";
|
||||
public final static String OPT_MIN_AMOUNT = "min-amount";
|
||||
public final static String OPT_OFFER_ID = "offer-id";
|
||||
public final static String OPT_PASSWORD = "password";
|
||||
public final static String OPT_PAYMENT_ACCOUNT = "payment-account";
|
||||
public final static String OPT_PAYMENT_ACCOUNT_FORM = "payment-account-form";
|
||||
public final static String OPT_PAYMENT_METHOD_ID = "payment-method-id";
|
||||
public final static String OPT_PORT = "port";
|
||||
public final static String OPT_REGISTRATION_KEY = "registration-key";
|
||||
public final static String OPT_SECURITY_DEPOSIT = "security-deposit";
|
||||
public final static String OPT_SHOW_CONTRACT = "show-contract";
|
||||
public final static String OPT_TRADE_ID = "trade-id";
|
||||
public final static String OPT_TRADE_INSTANT = "trade-instant";
|
||||
public final static String OPT_TIMEOUT = "timeout";
|
||||
public final static String OPT_TRANSACTION_ID = "transaction-id";
|
||||
public final static String OPT_TX_FEE_RATE = "tx-fee-rate";
|
||||
public final static String OPT_WALLET_PASSWORD = "wallet-password";
|
||||
public final static String OPT_NEW_WALLET_PASSWORD = "new-wallet-password";
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_DISPUTE_AGENT_TYPE;
|
||||
import static bisq.cli.opts.OptLabel.OPT_REGISTRATION_KEY;
|
||||
|
||||
public class RegisterDisputeAgentOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> disputeAgentTypeOpt = parser.accepts(OPT_DISPUTE_AGENT_TYPE, "dispute agent type")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> registrationKeyOpt = parser.accepts(OPT_REGISTRATION_KEY, "registration key")
|
||||
.withRequiredArg();
|
||||
|
||||
public RegisterDisputeAgentOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public RegisterDisputeAgentOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(disputeAgentTypeOpt) || options.valueOf(disputeAgentTypeOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no dispute agent type specified");
|
||||
|
||||
if (!options.has(registrationKeyOpt) || options.valueOf(registrationKeyOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no registration key specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDisputeAgentType() {
|
||||
return options.valueOf(disputeAgentTypeOpt);
|
||||
}
|
||||
|
||||
public String getRegistrationKey() {
|
||||
return options.valueOf(registrationKeyOpt);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_WALLET_PASSWORD;
|
||||
|
||||
public class RemoveWalletPasswordOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "bisq wallet password")
|
||||
.withRequiredArg();
|
||||
|
||||
public RemoveWalletPasswordOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public RemoveWalletPasswordOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(passwordOpt) || options.valueOf(passwordOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no password specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return options.valueOf(passwordOpt);
|
||||
}
|
||||
}
|
71
cli/src/main/java/bisq/cli/opts/SendBsqOptionParser.java
Normal file
71
cli/src/main/java/bisq/cli/opts/SendBsqOptionParser.java
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
|
||||
import static bisq.cli.opts.OptLabel.OPT_AMOUNT;
|
||||
import static bisq.cli.opts.OptLabel.OPT_TX_FEE_RATE;
|
||||
import static joptsimple.internal.Strings.EMPTY;
|
||||
|
||||
public class SendBsqOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> addressOpt = parser.accepts(OPT_ADDRESS, "destination bsq address")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> amountOpt = parser.accepts(OPT_AMOUNT, "amount of bsq to send")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> feeRateOpt = parser.accepts(OPT_TX_FEE_RATE, "optional tx fee rate (sats/byte)")
|
||||
.withOptionalArg()
|
||||
.defaultsTo(EMPTY);
|
||||
|
||||
public SendBsqOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public SendBsqOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(addressOpt) || options.valueOf(addressOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no bsq address specified");
|
||||
|
||||
if (!options.has(amountOpt) || options.valueOf(amountOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no bsq amount specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return options.valueOf(addressOpt);
|
||||
}
|
||||
|
||||
public String getAmount() {
|
||||
return options.valueOf(amountOpt);
|
||||
}
|
||||
|
||||
public String getFeeRate() {
|
||||
return options.has(feeRateOpt) ? options.valueOf(feeRateOpt) : "";
|
||||
}
|
||||
}
|
80
cli/src/main/java/bisq/cli/opts/SendBtcOptionParser.java
Normal file
80
cli/src/main/java/bisq/cli/opts/SendBtcOptionParser.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
|
||||
import static bisq.cli.opts.OptLabel.OPT_AMOUNT;
|
||||
import static bisq.cli.opts.OptLabel.OPT_MEMO;
|
||||
import static bisq.cli.opts.OptLabel.OPT_TX_FEE_RATE;
|
||||
import static joptsimple.internal.Strings.EMPTY;
|
||||
|
||||
public class SendBtcOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> addressOpt = parser.accepts(OPT_ADDRESS, "destination btc address")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> amountOpt = parser.accepts(OPT_AMOUNT, "amount of btc to send")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> feeRateOpt = parser.accepts(OPT_TX_FEE_RATE, "optional tx fee rate (sats/byte)")
|
||||
.withOptionalArg()
|
||||
.defaultsTo(EMPTY);
|
||||
|
||||
final OptionSpec<String> memoOpt = parser.accepts(OPT_MEMO, "optional tx memo")
|
||||
.withOptionalArg()
|
||||
.defaultsTo(EMPTY);
|
||||
|
||||
public SendBtcOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public SendBtcOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(addressOpt) || options.valueOf(addressOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no btc address specified");
|
||||
|
||||
if (!options.has(amountOpt) || options.valueOf(amountOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no btc amount specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return options.valueOf(addressOpt);
|
||||
}
|
||||
|
||||
public String getAmount() {
|
||||
return options.valueOf(amountOpt);
|
||||
}
|
||||
|
||||
public String getFeeRate() {
|
||||
return options.has(feeRateOpt) ? options.valueOf(feeRateOpt) : "";
|
||||
}
|
||||
|
||||
public String getMemo() {
|
||||
return options.has(memoOpt) ? options.valueOf(memoOpt) : "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_TX_FEE_RATE;
|
||||
|
||||
public class SetTxFeeRateOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> feeRateOpt = parser.accepts(OPT_TX_FEE_RATE,
|
||||
"tx fee rate preference (sats/byte)")
|
||||
.withRequiredArg();
|
||||
|
||||
public SetTxFeeRateOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public SetTxFeeRateOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(feeRateOpt) || options.valueOf(feeRateOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no tx fee rate specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getFeeRate() {
|
||||
return options.valueOf(feeRateOpt);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_NEW_WALLET_PASSWORD;
|
||||
import static bisq.cli.opts.OptLabel.OPT_WALLET_PASSWORD;
|
||||
import static joptsimple.internal.Strings.EMPTY;
|
||||
|
||||
public class SetWalletPasswordOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "bisq wallet password")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> newPasswordOpt = parser.accepts(OPT_NEW_WALLET_PASSWORD, "new bisq wallet password")
|
||||
.withOptionalArg()
|
||||
.defaultsTo(EMPTY);
|
||||
|
||||
public SetWalletPasswordOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public SetWalletPasswordOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(passwordOpt) || options.valueOf(passwordOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no password specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return options.valueOf(passwordOpt);
|
||||
}
|
||||
|
||||
public String getNewPassword() {
|
||||
return options.has(newPasswordOpt) ? options.valueOf(newPasswordOpt) : "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
public class SimpleMethodOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
public SimpleMethodOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public SimpleMethodOptionParser parse() {
|
||||
return (SimpleMethodOptionParser) super.parse();
|
||||
}
|
||||
}
|
70
cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java
Normal file
70
cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_FEE_CURRENCY;
|
||||
import static bisq.cli.opts.OptLabel.OPT_OFFER_ID;
|
||||
import static bisq.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT;
|
||||
|
||||
public class TakeOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to take")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT, "id of payment account used for trade")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> takerFeeCurrencyCodeOpt = parser.accepts(OPT_FEE_CURRENCY, "taker fee currency code (bsq|btc)")
|
||||
.withOptionalArg()
|
||||
.defaultsTo("btc");
|
||||
|
||||
public TakeOfferOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public TakeOfferOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(offerIdOpt) || options.valueOf(offerIdOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no offer id specified");
|
||||
|
||||
if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no payment account id specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getOfferId() {
|
||||
return options.valueOf(offerIdOpt);
|
||||
}
|
||||
|
||||
public String getPaymentAccountId() {
|
||||
return options.valueOf(paymentAccountIdOpt);
|
||||
}
|
||||
|
||||
public String getTakerFeeCurrencyCode() {
|
||||
return options.has(takerFeeCurrencyCodeOpt) ? options.valueOf(takerFeeCurrencyCodeOpt) : "btc";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_TIMEOUT;
|
||||
import static bisq.cli.opts.OptLabel.OPT_WALLET_PASSWORD;
|
||||
|
||||
public class UnlockWalletOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "bisq wallet password")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<Long> unlockTimeoutOpt = parser.accepts(OPT_TIMEOUT, "wallet unlock timeout (s)")
|
||||
.withRequiredArg()
|
||||
.ofType(long.class)
|
||||
.defaultsTo(0L);
|
||||
|
||||
public UnlockWalletOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public UnlockWalletOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(passwordOpt) || options.valueOf(passwordOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no password specified");
|
||||
|
||||
if (!options.has(unlockTimeoutOpt) || options.valueOf(unlockTimeoutOpt) <= 0)
|
||||
throw new IllegalArgumentException("no unlock timeout specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return options.valueOf(passwordOpt);
|
||||
}
|
||||
|
||||
public long getUnlockTimeout() {
|
||||
return options.valueOf(unlockTimeoutOpt);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
|
||||
import static bisq.cli.opts.OptLabel.OPT_AMOUNT;
|
||||
|
||||
public class VerifyBsqSentToAddressOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> addressOpt = parser.accepts(OPT_ADDRESS, "receiving bsq address")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> amountOpt = parser.accepts(OPT_AMOUNT, "amount of bsq received")
|
||||
.withRequiredArg();
|
||||
|
||||
public VerifyBsqSentToAddressOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public VerifyBsqSentToAddressOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(addressOpt) || options.valueOf(addressOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no bsq address specified");
|
||||
|
||||
if (!options.has(amountOpt) || options.valueOf(amountOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no bsq amount specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return options.valueOf(addressOpt);
|
||||
}
|
||||
|
||||
public String getAmount() {
|
||||
return options.valueOf(amountOpt);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
|
||||
import static bisq.cli.opts.OptLabel.OPT_MEMO;
|
||||
import static bisq.cli.opts.OptLabel.OPT_TRADE_ID;
|
||||
import static joptsimple.internal.Strings.EMPTY;
|
||||
|
||||
public class WithdrawFundsOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> tradeIdOpt = parser.accepts(OPT_TRADE_ID, "id of trade")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> addressOpt = parser.accepts(OPT_ADDRESS, "destination btc address")
|
||||
.withRequiredArg();
|
||||
|
||||
final OptionSpec<String> memoOpt = parser.accepts(OPT_MEMO, "optional tx memo")
|
||||
.withOptionalArg()
|
||||
.defaultsTo(EMPTY);
|
||||
|
||||
public WithdrawFundsOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public WithdrawFundsOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(tradeIdOpt) || options.valueOf(tradeIdOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no trade id specified");
|
||||
|
||||
if (!options.has(addressOpt) || options.valueOf(addressOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no destination address specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getTradeId() {
|
||||
return options.valueOf(tradeIdOpt);
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return options.valueOf(addressOpt);
|
||||
}
|
||||
|
||||
public String getMemo() {
|
||||
return options.has(memoOpt) ? options.valueOf(memoOpt) : "";
|
||||
}
|
||||
}
|
15
cli/src/main/resources/logback.xml
Normal file
15
cli/src/main/resources/logback.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30}: %msg %xEx%n)</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="TRACE">
|
||||
<appender-ref ref="CONSOLE_APPENDER"/>
|
||||
</root>
|
||||
|
||||
<logger name="io.grpc.netty" level="WARN"/>
|
||||
|
||||
</configuration>
|
39
cli/src/test/java/bisq/cli/GetOffersSmokeTest.java
Normal file
39
cli/src/test/java/bisq/cli/GetOffersSmokeTest.java
Normal file
|
@ -0,0 +1,39 @@
|
|||
package bisq.cli;
|
||||
|
||||
import static java.lang.System.out;
|
||||
|
||||
/**
|
||||
Smoke tests for getoffers method. Useful for examining the format of the console output.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Run `./bisq-daemon --apiPassword=xyz --appDataDir=$TESTDIR`
|
||||
|
||||
This can be run on mainnet.
|
||||
*/
|
||||
public class GetOffersSmokeTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
out.println(">>> getoffers buy usd");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "--direction=buy", "--currency-code=usd"});
|
||||
out.println(">>> getoffers sell usd");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "--direction=sell", "--currency-code=usd"});
|
||||
|
||||
out.println(">>> getoffers buy eur");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "--direction=buy", "--currency-code=eur"});
|
||||
out.println(">>> getoffers sell eur");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "--direction=sell", "--currency-code=eur"});
|
||||
|
||||
out.println(">>> getoffers buy gbp");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "--direction=buy", "--currency-code=gbp"});
|
||||
out.println(">>> getoffers sell gbp");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "--direction=sell", "--currency-code=gbp"});
|
||||
|
||||
out.println(">>> getoffers buy brl");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "--direction=buy", "--currency-code=brl"});
|
||||
out.println(">>> getoffers sell brl");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "--direction=sell", "--currency-code=brl"});
|
||||
}
|
||||
|
||||
}
|
267
cli/src/test/java/bisq/cli/opt/OptionParsersTest.java
Normal file
267
cli/src/test/java/bisq/cli/opt/OptionParsersTest.java
Normal file
|
@ -0,0 +1,267 @@
|
|||
package bisq.cli.opt;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static bisq.cli.Method.canceloffer;
|
||||
import static bisq.cli.Method.createcryptopaymentacct;
|
||||
import static bisq.cli.Method.createoffer;
|
||||
import static bisq.cli.Method.createpaymentacct;
|
||||
import static bisq.cli.opts.OptLabel.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.opts.CancelOfferOptionParser;
|
||||
import bisq.cli.opts.CreateCryptoCurrencyPaymentAcctOptionParser;
|
||||
import bisq.cli.opts.CreateOfferOptionParser;
|
||||
import bisq.cli.opts.CreatePaymentAcctOptionParser;
|
||||
|
||||
|
||||
public class OptionParsersTest {
|
||||
|
||||
private static final String PASSWORD_OPT = "--" + OPT_PASSWORD + "=" + "xyz";
|
||||
|
||||
// canceloffer opt parser tests
|
||||
|
||||
@Test
|
||||
public void testCancelOfferWithMissingOfferIdOptShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
canceloffer.name()
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CancelOfferOptionParser(args).parse());
|
||||
assertEquals("no offer id specified", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelOfferWithEmptyOfferIdOptShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
canceloffer.name(),
|
||||
"--" + OPT_OFFER_ID + "=" // missing opt value
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CancelOfferOptionParser(args).parse());
|
||||
assertEquals("no offer id specified", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelOfferWithMissingOfferIdValueShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
canceloffer.name(),
|
||||
"--" + OPT_OFFER_ID // missing equals sign & opt value
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CancelOfferOptionParser(args).parse());
|
||||
assertEquals("offer-id requires an argument", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidCancelOfferOpts() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
canceloffer.name(),
|
||||
"--" + OPT_OFFER_ID + "=" + "ABC-OFFER-ID"
|
||||
};
|
||||
new CancelOfferOptionParser(args).parse();
|
||||
}
|
||||
|
||||
// createoffer opt parser tests
|
||||
|
||||
@Test
|
||||
public void testCreateOfferOptParserWithMissingPaymentAccountIdOptShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createoffer.name()
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CreateOfferOptionParser(args).parse());
|
||||
assertEquals("no payment account id specified", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateOfferOptParserWithEmptyPaymentAccountIdOptShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createoffer.name(),
|
||||
"--" + OPT_PAYMENT_ACCOUNT
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CreateOfferOptionParser(args).parse());
|
||||
assertEquals("payment-account requires an argument", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateOfferOptParserWithMissingDirectionOptShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createoffer.name(),
|
||||
"--" + OPT_PAYMENT_ACCOUNT + "=" + "abc-payment-acct-id-123"
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CreateOfferOptionParser(args).parse());
|
||||
assertEquals("no direction (buy|sell) specified", exception.getMessage());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateOfferOptParserWithMissingDirectionOptValueShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createoffer.name(),
|
||||
"--" + OPT_PAYMENT_ACCOUNT + "=" + "abc-payment-acct-id-123",
|
||||
"--" + OPT_DIRECTION + "=" + ""
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CreateOfferOptionParser(args).parse());
|
||||
assertEquals("no direction (buy|sell) specified", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidCreateOfferOpts() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createoffer.name(),
|
||||
"--" + OPT_PAYMENT_ACCOUNT + "=" + "abc-payment-acct-id-123",
|
||||
"--" + OPT_DIRECTION + "=" + "BUY",
|
||||
"--" + OPT_CURRENCY_CODE + "=" + "EUR",
|
||||
"--" + OPT_AMOUNT + "=" + "0.125",
|
||||
"--" + OPT_MKT_PRICE_MARGIN + "=" + "0.0",
|
||||
"--" + OPT_SECURITY_DEPOSIT + "=" + "25.0"
|
||||
};
|
||||
CreateOfferOptionParser parser = new CreateOfferOptionParser(args).parse();
|
||||
assertEquals("abc-payment-acct-id-123", parser.getPaymentAccountId());
|
||||
assertEquals("BUY", parser.getDirection());
|
||||
assertEquals("EUR", parser.getCurrencyCode());
|
||||
assertEquals("0.125", parser.getAmount());
|
||||
assertEquals("0.0", parser.getMktPriceMargin());
|
||||
assertEquals("25.0", parser.getSecurityDeposit());
|
||||
}
|
||||
|
||||
// createpaymentacct opt parser tests
|
||||
|
||||
@Test
|
||||
public void testCreatePaymentAcctOptParserWithMissingPaymentFormOptShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createpaymentacct.name()
|
||||
// OPT_PAYMENT_ACCOUNT_FORM
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CreatePaymentAcctOptionParser(args).parse());
|
||||
assertEquals("no path to json payment account form specified", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePaymentAcctOptParserWithMissingPaymentFormOptValueShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createpaymentacct.name(),
|
||||
"--" + OPT_PAYMENT_ACCOUNT_FORM + "="
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CreatePaymentAcctOptionParser(args).parse());
|
||||
assertEquals("no path to json payment account form specified", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePaymentAcctOptParserWithInvalidPaymentFormOptValueShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createpaymentacct.name(),
|
||||
"--" + OPT_PAYMENT_ACCOUNT_FORM + "=" + "/tmp/milkyway/solarsystem/mars"
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CreatePaymentAcctOptionParser(args).parse());
|
||||
if (System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)
|
||||
assertEquals("json payment account form '\\tmp\\milkyway\\solarsystem\\mars' could not be found",
|
||||
exception.getMessage());
|
||||
else
|
||||
assertEquals("json payment account form '/tmp/milkyway/solarsystem/mars' could not be found",
|
||||
exception.getMessage());
|
||||
}
|
||||
|
||||
// createcryptopaymentacct parser tests
|
||||
|
||||
@Test
|
||||
public void testCreateCryptoCurrencyPaymentAcctOptionParserWithMissingAcctNameOptShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createcryptopaymentacct.name()
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse());
|
||||
assertEquals("no payment account name specified", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateCryptoCurrencyPaymentAcctOptionParserWithEmptyAcctNameOptShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createcryptopaymentacct.name(),
|
||||
"--" + OPT_ACCOUNT_NAME
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse());
|
||||
assertEquals("account-name requires an argument", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateCryptoCurrencyPaymentAcctOptionParserWithMissingCurrencyCodeOptShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createcryptopaymentacct.name(),
|
||||
"--" + OPT_ACCOUNT_NAME + "=" + "bsq payment account"
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse());
|
||||
assertEquals("no currency code specified", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateCryptoCurrencyPaymentAcctOptionParserWithInvalidCurrencyCodeOptShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createcryptopaymentacct.name(),
|
||||
"--" + OPT_ACCOUNT_NAME + "=" + "bsq payment account",
|
||||
"--" + OPT_CURRENCY_CODE + "=" + "xmr"
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse());
|
||||
assertEquals("api only supports bsq crypto currency payment accounts", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateCryptoCurrencyPaymentAcctOptionParserWithMissingAddressOptShouldThrowException() {
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createcryptopaymentacct.name(),
|
||||
"--" + OPT_ACCOUNT_NAME + "=" + "bsq payment account",
|
||||
"--" + OPT_CURRENCY_CODE + "=" + "bsq"
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse());
|
||||
assertEquals("no bsq address specified", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateCryptoCurrencyPaymentAcctOptionParser() {
|
||||
var acctName = "bsq payment account";
|
||||
var currencyCode = "bsq";
|
||||
var address = "B1nXyZ"; // address is validated on server
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
createcryptopaymentacct.name(),
|
||||
"--" + OPT_ACCOUNT_NAME + "=" + acctName,
|
||||
"--" + OPT_CURRENCY_CODE + "=" + currencyCode,
|
||||
"--" + OPT_ADDRESS + "=" + address
|
||||
};
|
||||
var parser = new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse();
|
||||
assertEquals(acctName, parser.getAccountName());
|
||||
assertEquals(currencyCode, parser.getCurrencyCode());
|
||||
assertEquals(address, parser.getAddress());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue