From f3c4d987dc04e5a2ff1b4fe8677bd68e181a9c4d Mon Sep 17 00:00:00 2001 From: KewbitXMR Date: Mon, 9 Jun 2025 15:43:21 +0700 Subject: [PATCH 1/2] WIP: old changes I want to merge forward --- bot/src/main/java/haveno/cli/CliMain.java | 783 ++++++++++++++++++ .../haveno/cli/ColumnHeaderConstants.java | 81 ++ .../java/haveno/cli/CryptoCurrencyUtil.java | 35 + .../main/java/haveno/cli/CurrencyFormat.java | 115 +++ .../main/java/haveno/cli/DirectionFormat.java | 60 ++ bot/src/main/java/haveno/cli/GrpcClient.java | 290 +++++++ bot/src/main/java/haveno/cli/GrpcStubs.java | 68 ++ bot/src/main/java/haveno/cli/Method.java | 63 ++ .../haveno/cli/PasswordCallCredentials.java | 62 ++ .../java/haveno/cli/TransactionFormat.java | 26 + .../cli/opts/AbstractMethodOptionParser.java | 90 ++ .../java/haveno/cli/opts/ArgumentList.java | 124 +++ .../cli/opts/CancelOfferOptionParser.java | 38 + ...CryptoCurrencyPaymentAcctOptionParser.java | 90 ++ .../cli/opts/CreateOfferOptionParser.java | 144 ++++ .../opts/CreatePaymentAcctOptionParser.java | 61 ++ .../opts/GetAddressBalanceOptionParser.java | 50 ++ .../opts/GetBTCMarketPriceOptionParser.java | 50 ++ .../cli/opts/GetBalanceOptionParser.java | 43 + .../haveno/cli/opts/GetOfferOptionParser.java | 50 ++ .../cli/opts/GetOffersOptionParser.java | 61 ++ .../opts/GetPaymentAcctFormOptionParser.java | 51 ++ .../haveno/cli/opts/GetTradeOptionParser.java | 60 ++ .../cli/opts/GetTradesOptionParser.java | 83 ++ .../cli/opts/GetTransactionOptionParser.java | 50 ++ .../main/java/haveno/cli/opts/MethodOpts.java | 25 + .../haveno/cli/opts/OfferIdOptionParser.java | 60 ++ .../main/java/haveno/cli/opts/OptLabel.java | 56 ++ .../RegisterDisputeAgentOptionParser.java | 61 ++ .../RemoveWalletPasswordOptionParser.java | 50 ++ .../haveno/cli/opts/SendBtcOptionParser.java | 80 ++ .../cli/opts/SetTxFeeRateOptionParser.java | 51 ++ .../opts/SetWalletPasswordOptionParser.java | 60 ++ .../cli/opts/SimpleMethodOptionParser.java | 30 + .../cli/opts/TakeOfferOptionParser.java | 48 ++ .../cli/opts/UnlockWalletOptionParser.java | 63 ++ .../cli/opts/WithdrawFundsOptionParser.java | 71 ++ .../cli/request/OffersServiceRequest.java | 163 ++++ .../PaymentAccountsServiceRequest.java | 99 +++ .../cli/request/TradesServiceRequest.java | 107 +++ .../cli/request/WalletsServiceRequest.java | 123 +++ bot/src/main/java/haveno/cli/table/Table.java | 152 ++++ .../table/builder/AbstractTableBuilder.java | 44 + .../builder/AbstractTradeListBuilder.java | 241 ++++++ .../builder/AddressBalanceTableBuilder.java | 78 ++ .../table/builder/BtcBalanceTableBuilder.java | 71 ++ .../builder/ClosedTradeTableBuilder.java | 69 ++ .../builder/FailedTradeTableBuilder.java | 64 ++ .../cli/table/builder/OfferTableBuilder.java | 272 ++++++ .../table/builder/OpenTradeTableBuilder.java | 62 ++ .../builder/PaymentAccountTableBuilder.java | 71 ++ .../cli/table/builder/TableBuilder.java | 67 ++ .../table/builder/TableBuilderConstants.java | 82 ++ .../haveno/cli/table/builder/TableType.java | 34 + .../builder/TradeDetailTableBuilder.java | 98 +++ .../builder/TradeTableColumnSupplier.java | 290 +++++++ .../builder/TransactionTableBuilder.java | 100 +++ .../cli/table/column/AbstractColumn.java | 86 ++ .../cli/table/column/BooleanColumn.java | 131 +++ .../haveno/cli/table/column/BtcColumn.java | 48 ++ .../java/haveno/cli/table/column/Column.java | 122 +++ .../cli/table/column/CryptoVolumeColumn.java | 88 ++ .../haveno/cli/table/column/DoubleColumn.java | 93 +++ .../cli/table/column/IntegerColumn.java | 93 +++ .../table/column/Iso8601DateTimeColumn.java | 63 ++ .../haveno/cli/table/column/LongColumn.java | 93 +++ .../cli/table/column/MixedTradeFeeColumn.java | 59 ++ .../haveno/cli/table/column/NumberColumn.java | 32 + .../cli/table/column/SatoshiColumn.java | 72 ++ .../haveno/cli/table/column/StringColumn.java | 102 +++ .../cli/table/column/ZippedStringColumns.java | 127 +++ bot/src/main/resources/logback.xml | 15 + .../test/java/haveno/cli/AbstractCliTest.java | 262 ++++++ .../java/haveno/cli/CreateOfferSmokeTest.java | 66 ++ .../haveno/cli/EditXmrOffersSmokeTest.java | 100 +++ .../java/haveno/cli/GetOffersSmokeTest.java | 72 ++ .../java/haveno/cli/GetTradesSmokeTest.java | 49 ++ .../haveno/cli/opts/OptionParsersTest.java | 248 ++++++ .../cli/table/AddressCliOutputDiffTest.java | 61 ++ .../table/GetBalanceCliOutputDiffTest.java | 30 + .../cli/table/GetOffersCliOutputDiffTest.java | 123 +++ .../cli/table/GetTradeCliOutputDiffTest.java | 50 ++ .../GetTransactionCliOutputDiffTest.java | 23 + .../PaymentAccountsCliOutputDiffTest.java | 37 + 84 files changed, 7915 insertions(+) create mode 100644 bot/src/main/java/haveno/cli/CliMain.java create mode 100644 bot/src/main/java/haveno/cli/ColumnHeaderConstants.java create mode 100644 bot/src/main/java/haveno/cli/CryptoCurrencyUtil.java create mode 100644 bot/src/main/java/haveno/cli/CurrencyFormat.java create mode 100644 bot/src/main/java/haveno/cli/DirectionFormat.java create mode 100644 bot/src/main/java/haveno/cli/GrpcClient.java create mode 100644 bot/src/main/java/haveno/cli/GrpcStubs.java create mode 100644 bot/src/main/java/haveno/cli/Method.java create mode 100644 bot/src/main/java/haveno/cli/PasswordCallCredentials.java create mode 100644 bot/src/main/java/haveno/cli/TransactionFormat.java create mode 100644 bot/src/main/java/haveno/cli/opts/AbstractMethodOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/ArgumentList.java create mode 100644 bot/src/main/java/haveno/cli/opts/CancelOfferOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/CreateOfferOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/CreatePaymentAcctOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/GetAddressBalanceOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/GetBTCMarketPriceOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/GetBalanceOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/GetOfferOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/GetOffersOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/GetPaymentAcctFormOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/GetTradeOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/GetTradesOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/GetTransactionOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/MethodOpts.java create mode 100644 bot/src/main/java/haveno/cli/opts/OfferIdOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/OptLabel.java create mode 100644 bot/src/main/java/haveno/cli/opts/RegisterDisputeAgentOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/RemoveWalletPasswordOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/SendBtcOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/SetTxFeeRateOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/SetWalletPasswordOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/SimpleMethodOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/TakeOfferOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/UnlockWalletOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/opts/WithdrawFundsOptionParser.java create mode 100644 bot/src/main/java/haveno/cli/request/OffersServiceRequest.java create mode 100644 bot/src/main/java/haveno/cli/request/PaymentAccountsServiceRequest.java create mode 100644 bot/src/main/java/haveno/cli/request/TradesServiceRequest.java create mode 100644 bot/src/main/java/haveno/cli/request/WalletsServiceRequest.java create mode 100644 bot/src/main/java/haveno/cli/table/Table.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/AbstractTableBuilder.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/AbstractTradeListBuilder.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/AddressBalanceTableBuilder.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/BtcBalanceTableBuilder.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/ClosedTradeTableBuilder.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/FailedTradeTableBuilder.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/OfferTableBuilder.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/OpenTradeTableBuilder.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/PaymentAccountTableBuilder.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/TableBuilder.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/TableBuilderConstants.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/TableType.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/TradeDetailTableBuilder.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/TradeTableColumnSupplier.java create mode 100644 bot/src/main/java/haveno/cli/table/builder/TransactionTableBuilder.java create mode 100644 bot/src/main/java/haveno/cli/table/column/AbstractColumn.java create mode 100644 bot/src/main/java/haveno/cli/table/column/BooleanColumn.java create mode 100644 bot/src/main/java/haveno/cli/table/column/BtcColumn.java create mode 100644 bot/src/main/java/haveno/cli/table/column/Column.java create mode 100644 bot/src/main/java/haveno/cli/table/column/CryptoVolumeColumn.java create mode 100644 bot/src/main/java/haveno/cli/table/column/DoubleColumn.java create mode 100644 bot/src/main/java/haveno/cli/table/column/IntegerColumn.java create mode 100644 bot/src/main/java/haveno/cli/table/column/Iso8601DateTimeColumn.java create mode 100644 bot/src/main/java/haveno/cli/table/column/LongColumn.java create mode 100644 bot/src/main/java/haveno/cli/table/column/MixedTradeFeeColumn.java create mode 100644 bot/src/main/java/haveno/cli/table/column/NumberColumn.java create mode 100644 bot/src/main/java/haveno/cli/table/column/SatoshiColumn.java create mode 100644 bot/src/main/java/haveno/cli/table/column/StringColumn.java create mode 100644 bot/src/main/java/haveno/cli/table/column/ZippedStringColumns.java create mode 100644 bot/src/main/resources/logback.xml create mode 100644 bot/src/test/java/haveno/cli/AbstractCliTest.java create mode 100644 bot/src/test/java/haveno/cli/CreateOfferSmokeTest.java create mode 100644 bot/src/test/java/haveno/cli/EditXmrOffersSmokeTest.java create mode 100644 bot/src/test/java/haveno/cli/GetOffersSmokeTest.java create mode 100644 bot/src/test/java/haveno/cli/GetTradesSmokeTest.java create mode 100644 bot/src/test/java/haveno/cli/opts/OptionParsersTest.java create mode 100644 bot/src/test/java/haveno/cli/table/AddressCliOutputDiffTest.java create mode 100644 bot/src/test/java/haveno/cli/table/GetBalanceCliOutputDiffTest.java create mode 100644 bot/src/test/java/haveno/cli/table/GetOffersCliOutputDiffTest.java create mode 100644 bot/src/test/java/haveno/cli/table/GetTradeCliOutputDiffTest.java create mode 100644 bot/src/test/java/haveno/cli/table/GetTransactionCliOutputDiffTest.java create mode 100644 bot/src/test/java/haveno/cli/table/PaymentAccountsCliOutputDiffTest.java diff --git a/bot/src/main/java/haveno/cli/CliMain.java b/bot/src/main/java/haveno/cli/CliMain.java new file mode 100644 index 00000000..58dcce46 --- /dev/null +++ b/bot/src/main/java/haveno/cli/CliMain.java @@ -0,0 +1,783 @@ +/* + * 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 . + */ + +package haveno.cli; + +import haveno.cli.opts.ArgumentList; +import haveno.cli.opts.CancelOfferOptionParser; +import haveno.cli.opts.CreateCryptoCurrencyPaymentAcctOptionParser; +import haveno.cli.opts.CreateOfferOptionParser; +import haveno.cli.opts.CreatePaymentAcctOptionParser; +import haveno.cli.opts.GetAddressBalanceOptionParser; +import haveno.cli.opts.GetBTCMarketPriceOptionParser; +import haveno.cli.opts.GetBalanceOptionParser; +import haveno.cli.opts.GetOffersOptionParser; +import haveno.cli.opts.GetPaymentAcctFormOptionParser; +import haveno.cli.opts.GetTradeOptionParser; +import haveno.cli.opts.GetTradesOptionParser; +import haveno.cli.opts.OfferIdOptionParser; +import haveno.cli.opts.RegisterDisputeAgentOptionParser; +import haveno.cli.opts.RemoveWalletPasswordOptionParser; +import haveno.cli.opts.SendBtcOptionParser; +import haveno.cli.opts.SetWalletPasswordOptionParser; +import haveno.cli.opts.SimpleMethodOptionParser; +import haveno.cli.opts.TakeOfferOptionParser; +import haveno.cli.opts.UnlockWalletOptionParser; +import haveno.cli.opts.WithdrawFundsOptionParser; +import haveno.cli.table.builder.TableBuilder; +import haveno.proto.grpc.OfferInfo; +import io.grpc.StatusRuntimeException; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Date; +import java.util.List; + +import static haveno.cli.CurrencyFormat.formatInternalFiatPrice; +import static haveno.cli.CurrencyFormat.toSatoshis; +import static haveno.cli.Method.canceloffer; +import static haveno.cli.Method.closetrade; +import static haveno.cli.Method.confirmpaymentreceived; +import static haveno.cli.Method.confirmpaymentsent; +import static haveno.cli.Method.createcryptopaymentacct; +import static haveno.cli.Method.createoffer; +import static haveno.cli.Method.createpaymentacct; +import static haveno.cli.Method.editoffer; +import static haveno.cli.Method.failtrade; +import static haveno.cli.Method.getaddressbalance; +import static haveno.cli.Method.getbalance; +import static haveno.cli.Method.getbtcprice; +import static haveno.cli.Method.getfundingaddresses; +import static haveno.cli.Method.getmyoffer; +import static haveno.cli.Method.getmyoffers; +import static haveno.cli.Method.getoffer; +import static haveno.cli.Method.getoffers; +import static haveno.cli.Method.getpaymentacctform; +import static haveno.cli.Method.getpaymentaccts; +import static haveno.cli.Method.getpaymentmethods; +import static haveno.cli.Method.gettrade; +import static haveno.cli.Method.gettrades; +import static haveno.cli.Method.gettransaction; +import static haveno.cli.Method.gettxfeerate; +import static haveno.cli.Method.getunusedbsqaddress; +import static haveno.cli.Method.getversion; +import static haveno.cli.Method.lockwallet; +import static haveno.cli.Method.sendbtc; +import static haveno.cli.Method.settxfeerate; +import static haveno.cli.Method.setwalletpassword; +import static haveno.cli.Method.stop; +import static haveno.cli.Method.takeoffer; +import static haveno.cli.Method.unfailtrade; +import static haveno.cli.Method.unlockwallet; +import static haveno.cli.Method.unsettxfeerate; +import static haveno.cli.Method.withdrawfunds; +import static haveno.cli.opts.OptLabel.OPT_AMOUNT; +import static haveno.cli.opts.OptLabel.OPT_HELP; +import static haveno.cli.opts.OptLabel.OPT_HOST; +import static haveno.cli.opts.OptLabel.OPT_PASSWORD; +import static haveno.cli.opts.OptLabel.OPT_PORT; +import static haveno.cli.opts.OptLabel.OPT_TX_FEE_RATE; +import static haveno.cli.table.builder.TableType.ADDRESS_BALANCE_TBL; +import static haveno.cli.table.builder.TableType.BTC_BALANCE_TBL; +import static haveno.cli.table.builder.TableType.CLOSED_TRADES_TBL; +import static haveno.cli.table.builder.TableType.FAILED_TRADES_TBL; +import static haveno.cli.table.builder.TableType.OFFER_TBL; +import static haveno.cli.table.builder.TableType.OPEN_TRADES_TBL; +import static haveno.cli.table.builder.TableType.PAYMENT_ACCOUNT_TBL; +import static haveno.cli.table.builder.TableType.TRADE_DETAIL_TBL; +import static haveno.proto.grpc.GetTradesRequest.Category.CLOSED; +import static haveno.proto.grpc.GetTradesRequest.Category.OPEN; +import static java.lang.String.format; +import static java.lang.System.err; +import static java.lang.System.exit; +import static java.lang.System.out; + +/** + * A command-line client for the Haveno 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) 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 "BTC": + new TableBuilder(BTC_BALANCE_TBL, balances.getBtc()).build().print(out); + break; + case "": + default: { + out.println("BTC"); + new TableBuilder(BTC_BALANCE_TBL, balances.getBtc()).build().print(out); + 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); + new TableBuilder(ADDRESS_BALANCE_TBL, addressBalance).build().print(out); + 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(formatInternalFiatPrice(price)); + return; + } + case getfundingaddresses: { + if (new SimpleMethodOptionParser(args).parse().isForHelp()) { + out.println(client.getMethodHelp(method)); + return; + } + var fundingAddresses = client.getFundingAddresses(); + new TableBuilder(ADDRESS_BALANCE_TBL, fundingAddresses).build().print(out); + 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(); + + throw new RuntimeException("Send BTC not implemented"); + } + 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 marketPriceMarginPct = opts.getMktPriceMarginPct(); + var securityDepositPct = opts.getSecurityDepositPct(); + var triggerPrice = "0"; // Cannot be defined until the new offer is added to book. + OfferInfo offer; + offer = client.createOffer(direction, + currencyCode, + amount, + minAmount, + useMarketBasedPrice, + fixedPrice, + marketPriceMarginPct, + securityDepositPct, + paymentAcctId, + triggerPrice); + new TableBuilder(OFFER_TBL, offer).build().print(out); + 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 OfferIdOptionParser(args).parse(); + if (opts.isForHelp()) { + out.println(client.getMethodHelp(method)); + return; + } + var offerId = opts.getOfferId(); + var offer = client.getOffer(offerId); + new TableBuilder(OFFER_TBL, offer).build().print(out); + return; + } + case getmyoffer: { + var opts = new OfferIdOptionParser(args).parse(); + if (opts.isForHelp()) { + out.println(client.getMethodHelp(method)); + return; + } + var offerId = opts.getOfferId(); + var offer = client.getMyOffer(offerId); + new TableBuilder(OFFER_TBL, offer).build().print(out); + 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 offers = client.getOffers(direction, currencyCode); + if (offers.isEmpty()) + out.printf("no %s %s offers found%n", direction, currencyCode); + else + new TableBuilder(OFFER_TBL, offers).build().print(out); + + 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 offers = client.getMyOffers(direction, currencyCode); + if (offers.isEmpty()) + out.printf("no %s %s offers found%n", direction, currencyCode); + else + new TableBuilder(OFFER_TBL, offers).build().print(out); + + 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 trade = client.takeOffer(offerId, paymentAccountId); + 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 + new TableBuilder(TRADE_DETAIL_TBL, trade).build().print(out); + + return; + } + case gettrades: { + var opts = new GetTradesOptionParser(args).parse(); + if (opts.isForHelp()) { + out.println(client.getMethodHelp(method)); + return; + } + var category = opts.getCategory(); + var trades = category.equals(OPEN) + ? client.getOpenTrades() + : client.getTradeHistory(category); + if (trades.isEmpty()) { + out.printf("no %s trades found%n", category.name().toLowerCase()); + } else { + var tableType = category.equals(OPEN) + ? OPEN_TRADES_TBL + : category.equals(CLOSED) ? CLOSED_TRADES_TBL : FAILED_TRADES_TBL; + new TableBuilder(tableType, trades).build().print(out); + } + return; + } + case confirmpaymentsent: { + var opts = new GetTradeOptionParser(args).parse(); + if (opts.isForHelp()) { + out.println(client.getMethodHelp(method)); + return; + } + var tradeId = opts.getTradeId(); + client.confirmPaymentSent(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 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"); + new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccount).build().print(out); + 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"); + new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccount).build().print(out); + return; + } + case getpaymentaccts: { + if (new SimpleMethodOptionParser(args).parse().isForHelp()) { + out.println(client.getMethodHelp(method)); + return; + } + var paymentAccounts = client.getPaymentAccounts(); + if (paymentAccounts.size() > 0) + new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccounts).build().print(out); + 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., INVALID_ARGUMENT, + // NOT_FOUND, ..., UNKNOWN from the exception 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("Haveno RPC Client"); + stream.println(); + stream.println("Usage: haveno-cli [options] [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=]", "Get server wallet balances"); + stream.println(); + stream.format(rowFormat, getaddressbalance.name(), "--address=", "Get server wallet address balance"); + stream.println(); + stream.format(rowFormat, getbtcprice.name(), "--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, "", "[--tx-fee-rate=]", ""); + stream.println(); + stream.format(rowFormat, sendbtc.name(), "--address= --amount= \\", "Send BTC"); + stream.format(rowFormat, "", "[--tx-fee-rate=]", ""); + stream.format(rowFormat, "", "[--memo=<\"memo\">]", ""); + 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=", "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=", "Get transaction with id"); + stream.println(); + stream.format(rowFormat, createoffer.name(), "--payment-account= \\", "Create and place an offer"); + stream.format(rowFormat, "", "--direction= \\", ""); + stream.format(rowFormat, "", "--currency-code= \\", ""); + stream.format(rowFormat, "", "--amount= \\", ""); + stream.format(rowFormat, "", "[--min-amount=] \\", ""); + stream.format(rowFormat, "", "--fixed-price= | --market-price-margin= \\", ""); + stream.format(rowFormat, "", "--security-deposit= \\", ""); + stream.format(rowFormat, "", "[--fee-currency=]", ""); + stream.format(rowFormat, "", "[--trigger-price=]", ""); + stream.format(rowFormat, "", "[--swap=]", ""); + stream.println(); + stream.format(rowFormat, editoffer.name(), "--offer-id= \\", "Edit offer with id"); + stream.format(rowFormat, "", "[--fixed-price=] \\", ""); + stream.format(rowFormat, "", "[--market-price-margin=] \\", ""); + stream.format(rowFormat, "", "[--trigger-price=] \\", ""); + stream.format(rowFormat, "", "[--enabled=]", ""); + stream.println(); + stream.format(rowFormat, canceloffer.name(), "--offer-id=", "Cancel offer with id"); + stream.println(); + stream.format(rowFormat, getoffer.name(), "--offer-id=", "Get current offer with id"); + stream.println(); + stream.format(rowFormat, getmyoffer.name(), "--offer-id=", "Get my current offer with id"); + stream.println(); + stream.format(rowFormat, getoffers.name(), "--direction= \\", "Get current offers"); + stream.format(rowFormat, "", "--currency-code=", ""); + stream.println(); + stream.format(rowFormat, getmyoffers.name(), "--direction= \\", "Get my current offers"); + stream.format(rowFormat, "", "--currency-code=", ""); + stream.println(); + stream.format(rowFormat, takeoffer.name(), "--offer-id= \\", "Take offer with id"); + stream.format(rowFormat, "", "[--payment-account=]", ""); + stream.format(rowFormat, "", "[--fee-currency=]", ""); + stream.println(); + stream.format(rowFormat, gettrade.name(), "--trade-id= \\", "Get trade summary or full contract"); + stream.format(rowFormat, "", "[--show-contract=]", ""); + stream.println(); + stream.format(rowFormat, gettrades.name(), "[--category=]", "Get open (default), closed, or failed trades"); + stream.println(); + stream.format(rowFormat, confirmpaymentsent.name(), "--trade-id=", "Confirm payment started"); + stream.println(); + stream.format(rowFormat, confirmpaymentreceived.name(), "--trade-id=", "Confirm payment received"); + stream.println(); + stream.format(rowFormat, closetrade.name(), "--trade-id=", "Close completed trade"); + stream.println(); + stream.format(rowFormat, withdrawfunds.name(), "--trade-id= --address= \\", + "Withdraw received trade funds to external wallet address"); + stream.format(rowFormat, "", "[--memo=<\"memo\">]", ""); + stream.println(); + stream.format(rowFormat, failtrade.name(), "--trade-id=", "Change open trade to failed trade"); + stream.println(); + stream.format(rowFormat, unfailtrade.name(), "--trade-id=", "Change failed trade to open trade"); + 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=", "Get a new payment account form"); + stream.println(); + stream.format(rowFormat, createpaymentacct.name(), "--payment-account-form=", "Create a new payment account"); + stream.println(); + stream.format(rowFormat, createcryptopaymentacct.name(), "--account-name= \\", "Create a new cryptocurrency payment account"); + stream.format(rowFormat, "", "--currency-code= \\", ""); + stream.format(rowFormat, "", "--address=", ""); + stream.format(rowFormat, "", "--trade-instant=", ""); + 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= --timeout=", + "Store wallet password in memory for timeout seconds"); + stream.println(); + stream.format(rowFormat, setwalletpassword.name(), "--wallet-password= \\", + "Encrypt wallet with password, or set new password on encrypted wallet"); + stream.format(rowFormat, "", "[--new-wallet-password=]", ""); + stream.println(); + stream.format(rowFormat, stop.name(), "", "Shut down the server"); + stream.println(); + stream.println("Method Help Usage: haveno-cli [options] --help"); + stream.println(); + } catch (IOException ex) { + ex.printStackTrace(stream); + } + } +} diff --git a/bot/src/main/java/haveno/cli/ColumnHeaderConstants.java b/bot/src/main/java/haveno/cli/ColumnHeaderConstants.java new file mode 100644 index 00000000..5f1ef39e --- /dev/null +++ b/bot/src/main/java/haveno/cli/ColumnHeaderConstants.java @@ -0,0 +1,81 @@ +/* + * 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 . + */ + +package haveno.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_BALANCE = "Balance"; + 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_RESERVED_OFFER_BALANCE = "Reserved Offer Balance"; + static final String COL_HEADER_RESERVED_TRADE_BALANCE = "Reserved Trade 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_CRYPTO = "Price in BTC for 1 %-3s"; + static final String COL_HEADER_TRADE_AMOUNT = padStart("Amount(%-3s)", 12, ' '); + 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, ' '); +} diff --git a/bot/src/main/java/haveno/cli/CryptoCurrencyUtil.java b/bot/src/main/java/haveno/cli/CryptoCurrencyUtil.java new file mode 100644 index 00000000..572130db --- /dev/null +++ b/bot/src/main/java/haveno/cli/CryptoCurrencyUtil.java @@ -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 . + */ + +package haveno.cli; + +import java.util.ArrayList; +import java.util.List; + +public class CryptoCurrencyUtil { + + public static boolean apiDoesSupportCryptoCurrency(String currencyCode) { + return getSupportedCryptoCurrencies().contains(currencyCode.toUpperCase()); + } + + public static List getSupportedCryptoCurrencies() { + final List result = new ArrayList<>(); + result.add("BCH"); + result.sort(String::compareTo); + return result; + } +} diff --git a/bot/src/main/java/haveno/cli/CurrencyFormat.java b/bot/src/main/java/haveno/cli/CurrencyFormat.java new file mode 100644 index 00000000..c88b8c62 --- /dev/null +++ b/bot/src/main/java/haveno/cli/CurrencyFormat.java @@ -0,0 +1,115 @@ +/* + * 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 . + */ + +package haveno.cli; + +import com.google.common.annotations.VisibleForTesting; + +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.Locale; + +import static java.lang.String.format; +import static java.math.RoundingMode.HALF_UP; +import static java.math.RoundingMode.UNNECESSARY; + +/** + * Utility for formatting amounts, volumes and fees; there is no i18n support in the CLI. + */ +@VisibleForTesting +public class CurrencyFormat { + + // Use the US locale as a base for all DecimalFormats, but commas should be omitted from number strings. + private static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = DecimalFormatSymbols.getInstance(Locale.US); + + // Use the US locale as a base for all NumberFormats, but commas should be omitted from number strings. + private static final NumberFormat US_LOCALE_NUMBER_FORMAT = NumberFormat.getInstance(Locale.US); + + // Formats numbers for internal use, i.e., grpc request parameters. + private static final DecimalFormat INTERNAL_FIAT_DECIMAL_FORMAT = new DecimalFormat("##############0.0000"); + + static final BigDecimal SATOSHI_DIVISOR = new BigDecimal(100_000_000); + static final DecimalFormat SATOSHI_FORMAT = new DecimalFormat("###,##0.00000000", DECIMAL_FORMAT_SYMBOLS); + static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.########", DECIMAL_FORMAT_SYMBOLS); + static final DecimalFormat BTC_TX_FEE_FORMAT = new DecimalFormat("###,###,##0", DECIMAL_FORMAT_SYMBOLS); + + static final BigDecimal BSQ_SATOSHI_DIVISOR = new BigDecimal(100); + static final DecimalFormat BSQ_FORMAT = new DecimalFormat("###,###,###,##0.00", DECIMAL_FORMAT_SYMBOLS); + + public static String formatSatoshis(String sats) { + //noinspection BigDecimalMethodWithoutRoundingCalled + return SATOSHI_FORMAT.format(new BigDecimal(sats).divide(SATOSHI_DIVISOR)); + } + + @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") + public static String formatSatoshis(long sats) { + return SATOSHI_FORMAT.format(new BigDecimal(sats).divide(SATOSHI_DIVISOR)); + } + + @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") + public static String formatBtc(long sats) { + return BTC_FORMAT.format(new BigDecimal(sats).divide(SATOSHI_DIVISOR)); + } + + @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") + public static String formatBsq(long sats) { + return BSQ_FORMAT.format(new BigDecimal(sats).divide(BSQ_SATOSHI_DIVISOR)); + } + + public static String formatInternalFiatPrice(BigDecimal price) { + INTERNAL_FIAT_DECIMAL_FORMAT.setMinimumFractionDigits(4); + INTERNAL_FIAT_DECIMAL_FORMAT.setMaximumFractionDigits(4); + return INTERNAL_FIAT_DECIMAL_FORMAT.format(price); + } + + public static String formatInternalFiatPrice(double price) { + US_LOCALE_NUMBER_FORMAT.setMinimumFractionDigits(4); + US_LOCALE_NUMBER_FORMAT.setMaximumFractionDigits(4); + return US_LOCALE_NUMBER_FORMAT.format(price); + } + + public static String formatPrice(long price) { + US_LOCALE_NUMBER_FORMAT.setMinimumFractionDigits(4); + US_LOCALE_NUMBER_FORMAT.setMaximumFractionDigits(4); + US_LOCALE_NUMBER_FORMAT.setRoundingMode(UNNECESSARY); + return US_LOCALE_NUMBER_FORMAT.format((double) price / 10_000); + } + + public static String formatFiatVolume(long volume) { + US_LOCALE_NUMBER_FORMAT.setMinimumFractionDigits(0); + US_LOCALE_NUMBER_FORMAT.setMaximumFractionDigits(0); + US_LOCALE_NUMBER_FORMAT.setRoundingMode(HALF_UP); + return US_LOCALE_NUMBER_FORMAT.format((double) volume / 10_000); + } + + 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 String formatFeeSatoshis(long sats) { + return BTC_TX_FEE_FORMAT.format(BigDecimal.valueOf(sats)); + } +} diff --git a/bot/src/main/java/haveno/cli/DirectionFormat.java b/bot/src/main/java/haveno/cli/DirectionFormat.java new file mode 100644 index 00000000..28a30f17 --- /dev/null +++ b/bot/src/main/java/haveno/cli/DirectionFormat.java @@ -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 . + */ + +package haveno.cli; + +import haveno.proto.grpc.OfferInfo; + +import java.util.List; +import java.util.function.Function; + +import static haveno.cli.ColumnHeaderConstants.COL_HEADER_DIRECTION; +import static java.lang.String.format; +import static protobuf.OfferDirection.BUY; +import static protobuf.OfferDirection.SELL; + +class DirectionFormat { + + static int getLongestDirectionColWidth(List offers) { + if (offers.isEmpty() || offers.get(0).getBaseCurrencyCode().equals("XMR")) + return COL_HEADER_DIRECTION.length(); + else + return 18; // .e.g., "Sell BSQ (Buy XMR)".length() + } + + static final Function directionFormat = (offer) -> { + String baseCurrencyCode = offer.getBaseCurrencyCode(); + boolean isCryptoCurrencyOffer = !baseCurrencyCode.equals("XMR"); + if (!isCryptoCurrencyOffer) { + return baseCurrencyCode; + } else { + // Return "Sell BSQ (Buy XMR)", or "Buy BSQ (Sell XMR)". + String direction = offer.getDirection(); + String mirroredDirection = getMirroredDirection(direction); + Function 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(); + } +} diff --git a/bot/src/main/java/haveno/cli/GrpcClient.java b/bot/src/main/java/haveno/cli/GrpcClient.java new file mode 100644 index 00000000..229104cc --- /dev/null +++ b/bot/src/main/java/haveno/cli/GrpcClient.java @@ -0,0 +1,290 @@ +/* + * 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 . + */ + +package haveno.cli; + +import haveno.cli.request.OffersServiceRequest; +import haveno.cli.request.PaymentAccountsServiceRequest; +import haveno.cli.request.TradesServiceRequest; +import haveno.cli.request.WalletsServiceRequest; +import haveno.proto.grpc.AddressBalanceInfo; +import haveno.proto.grpc.BalancesInfo; +import haveno.proto.grpc.BtcBalanceInfo; +import haveno.proto.grpc.GetMethodHelpRequest; +import haveno.proto.grpc.GetTradesRequest; +import haveno.proto.grpc.GetVersionRequest; +import haveno.proto.grpc.OfferInfo; +import haveno.proto.grpc.RegisterDisputeAgentRequest; +import haveno.proto.grpc.StopRequest; +import haveno.proto.grpc.TradeInfo; +import lombok.extern.slf4j.Slf4j; +import protobuf.PaymentAccount; +import protobuf.PaymentMethod; + +import java.util.List; + + +@SuppressWarnings("ResultOfMethodCallIgnored") +@Slf4j +public final class GrpcClient { + + private final GrpcStubs grpcStubs; + private final OffersServiceRequest offersServiceRequest; + private final TradesServiceRequest tradesServiceRequest; + private final WalletsServiceRequest walletsServiceRequest; + private final PaymentAccountsServiceRequest paymentAccountsServiceRequest; + + public GrpcClient(String apiHost, + int apiPort, + String apiPassword) { + this.grpcStubs = new GrpcStubs(apiHost, apiPort, apiPassword); + this.offersServiceRequest = new OffersServiceRequest(grpcStubs); + this.tradesServiceRequest = new TradesServiceRequest(grpcStubs); + this.walletsServiceRequest = new WalletsServiceRequest(grpcStubs); + this.paymentAccountsServiceRequest = new PaymentAccountsServiceRequest(grpcStubs); + } + + public String getVersion() { + var request = GetVersionRequest.newBuilder().build(); + return grpcStubs.versionService.getVersion(request).getVersion(); + } + + public BalancesInfo getBalances() { + return walletsServiceRequest.getBalances(); + } + + public BtcBalanceInfo getBtcBalances() { + return walletsServiceRequest.getBtcBalances(); + } + + public BalancesInfo getBalances(String currencyCode) { + return walletsServiceRequest.getBalances(currencyCode); + } + + public AddressBalanceInfo getAddressBalance(String address) { + return walletsServiceRequest.getAddressBalance(address); + } + + public double getBtcPrice(String currencyCode) { + return walletsServiceRequest.getBtcPrice(currencyCode); + } + + public List getFundingAddresses() { + return walletsServiceRequest.getFundingAddresses(); + } + + public String getUnusedBtcAddress() { + return walletsServiceRequest.getUnusedBtcAddress(); + } + + public OfferInfo createFixedPricedOffer(String direction, + String currencyCode, + long amount, + long minAmount, + String fixedPrice, + double securityDepositPct, + String paymentAcctId) { + return offersServiceRequest.createOffer(direction, + currencyCode, + amount, + minAmount, + false, + fixedPrice, + 0.00, + securityDepositPct, + paymentAcctId, + "0" /* no trigger price */); + } + + public OfferInfo createMarketBasedPricedOffer(String direction, + String currencyCode, + long amount, + long minAmount, + double marketPriceMarginPct, + double securityDepositPct, + String paymentAcctId, + String triggerPrice) { + return offersServiceRequest.createOffer(direction, + currencyCode, + amount, + minAmount, + true, + "0", + marketPriceMarginPct, + securityDepositPct, + paymentAcctId, + triggerPrice); + } + + public OfferInfo createOffer(String direction, + String currencyCode, + long amount, + long minAmount, + boolean useMarketBasedPrice, + String fixedPrice, + double marketPriceMarginPct, + double securityDepositPct, + String paymentAcctId, + String triggerPrice) { + return offersServiceRequest.createOffer(direction, + currencyCode, + amount, + minAmount, + useMarketBasedPrice, + fixedPrice, + marketPriceMarginPct, + securityDepositPct, + paymentAcctId, + triggerPrice); + } + + public void cancelOffer(String offerId) { + offersServiceRequest.cancelOffer(offerId); + } + + public OfferInfo getOffer(String offerId) { + return offersServiceRequest.getOffer(offerId); + } + + @Deprecated // Since 5-Dec-2021. + // Endpoint to be removed from future version. Use getOffer service method instead. + public OfferInfo getMyOffer(String offerId) { + return offersServiceRequest.getMyOffer(offerId); + } + + public List getOffers(String direction, String currencyCode) { + return offersServiceRequest.getOffers(direction, currencyCode); + } + + public List getOffersSortedByDate(String currencyCode) { + return offersServiceRequest.getOffersSortedByDate(currencyCode); + } + + public List getOffersSortedByDate(String direction, String currencyCode) { + return offersServiceRequest.getOffersSortedByDate(direction, currencyCode); + } + + public List getMyOffers(String direction, String currencyCode) { + return offersServiceRequest.getMyOffers(direction, currencyCode); + } + + public List getMyOffersSortedByDate(String currencyCode) { + return offersServiceRequest.getMyOffersSortedByDate(currencyCode); + } + + public List getMyOffersSortedByDate(String direction, String currencyCode) { + return offersServiceRequest.getMyOffersSortedByDate(direction, currencyCode); + } + + public TradeInfo takeOffer(String offerId, String paymentAccountId) { + return tradesServiceRequest.takeOffer(offerId, paymentAccountId); + } + + public TradeInfo getTrade(String tradeId) { + return tradesServiceRequest.getTrade(tradeId); + } + + public List getOpenTrades() { + return tradesServiceRequest.getOpenTrades(); + } + + public List getTradeHistory(GetTradesRequest.Category category) { + return tradesServiceRequest.getTradeHistory(category); + } + + public void confirmPaymentSent(String tradeId) { + tradesServiceRequest.confirmPaymentSent(tradeId); + } + + public void confirmPaymentReceived(String tradeId) { + tradesServiceRequest.confirmPaymentReceived(tradeId); + } + + public void withdrawFunds(String tradeId, String address, String memo) { + tradesServiceRequest.withdrawFunds(tradeId, address, memo); + } + + public List getPaymentMethods() { + return paymentAccountsServiceRequest.getPaymentMethods(); + } + + public String getPaymentAcctFormAsJson(String paymentMethodId) { + return paymentAccountsServiceRequest.getPaymentAcctFormAsJson(paymentMethodId); + } + + public PaymentAccount createPaymentAccount(String json) { + return paymentAccountsServiceRequest.createPaymentAccount(json); + } + + public List getPaymentAccounts() { + return paymentAccountsServiceRequest.getPaymentAccounts(); + } + + public PaymentAccount getPaymentAccount(String accountName) { + return paymentAccountsServiceRequest.getPaymentAccount(accountName); + } + + public PaymentAccount createCryptoCurrencyPaymentAccount(String accountName, + String currencyCode, + String address, + boolean tradeInstant) { + return paymentAccountsServiceRequest.createCryptoCurrencyPaymentAccount(accountName, + currencyCode, + address, + tradeInstant); + } + + public List getCryptoPaymentMethods() { + return paymentAccountsServiceRequest.getCryptoPaymentMethods(); + } + + public void lockWallet() { + walletsServiceRequest.lockWallet(); + } + + public void unlockWallet(String walletPassword, long timeout) { + walletsServiceRequest.unlockWallet(walletPassword, timeout); + } + + public void removeWalletPassword(String walletPassword) { + walletsServiceRequest.removeWalletPassword(walletPassword); + } + + public void setWalletPassword(String walletPassword) { + walletsServiceRequest.setWalletPassword(walletPassword); + } + + public void setWalletPassword(String oldWalletPassword, String newWalletPassword) { + walletsServiceRequest.setWalletPassword(oldWalletPassword, newWalletPassword); + } + + 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(); + } +} diff --git a/bot/src/main/java/haveno/cli/GrpcStubs.java b/bot/src/main/java/haveno/cli/GrpcStubs.java new file mode 100644 index 00000000..41749617 --- /dev/null +++ b/bot/src/main/java/haveno/cli/GrpcStubs.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ + +package haveno.cli; + +import haveno.proto.grpc.DisputeAgentsGrpc; +import haveno.proto.grpc.GetVersionGrpc; +import haveno.proto.grpc.HelpGrpc; +import haveno.proto.grpc.OffersGrpc; +import haveno.proto.grpc.PaymentAccountsGrpc; +import haveno.proto.grpc.PriceGrpc; +import haveno.proto.grpc.ShutdownServerGrpc; +import haveno.proto.grpc.TradesGrpc; +import haveno.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); + } +} diff --git a/bot/src/main/java/haveno/cli/Method.java b/bot/src/main/java/haveno/cli/Method.java new file mode 100644 index 00000000..f83416c3 --- /dev/null +++ b/bot/src/main/java/haveno/cli/Method.java @@ -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 . + */ + +package haveno.cli; + +/** + * Currently supported api methods. + */ +public enum Method { + canceloffer, + closetrade, + confirmpaymentreceived, + confirmpaymentsent, + createoffer, + editoffer, + createpaymentacct, + createcryptopaymentacct, + getaddressbalance, + getbalance, + getbtcprice, + getfundingaddresses, + @Deprecated // Since 27-Dec-2021. + getmyoffer, // Endpoint to be removed from future version. Use getoffer instead. + getmyoffers, + getoffer, + getoffers, + getpaymentacctform, + getpaymentaccts, + getpaymentmethods, + gettrade, + gettrades, + failtrade, + unfailtrade, + gettransaction, + gettxfeerate, + getunusedbsqaddress, + getversion, + lockwallet, + registerdisputeagent, + removewalletpassword, + sendbtc, + settxfeerate, + setwalletpassword, + takeoffer, + unlockwallet, + unsettxfeerate, + withdrawfunds, + stop +} diff --git a/bot/src/main/java/haveno/cli/PasswordCallCredentials.java b/bot/src/main/java/haveno/cli/PasswordCallCredentials.java new file mode 100644 index 00000000..f7809d1a --- /dev/null +++ b/bot/src/main/java/haveno/cli/PasswordCallCredentials.java @@ -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 . + */ + +package haveno.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() { + } +} diff --git a/bot/src/main/java/haveno/cli/TransactionFormat.java b/bot/src/main/java/haveno/cli/TransactionFormat.java new file mode 100644 index 00000000..fc0b5ab9 --- /dev/null +++ b/bot/src/main/java/haveno/cli/TransactionFormat.java @@ -0,0 +1,26 @@ +/* + * 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 . + */ + +package haveno.cli; + +import com.google.common.annotations.VisibleForTesting; + + +@VisibleForTesting +public class TransactionFormat { + +} diff --git a/bot/src/main/java/haveno/cli/opts/AbstractMethodOptionParser.java b/bot/src/main/java/haveno/cli/opts/AbstractMethodOptionParser.java new file mode 100644 index 00000000..9ad9a374 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/AbstractMethodOptionParser.java @@ -0,0 +1,90 @@ +/* + * 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 . + */ + +package haveno.cli.opts; + +import joptsimple.OptionException; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import joptsimple.OptionSpec; +import lombok.Getter; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +import static haveno.cli.opts.OptLabel.OPT_HELP; +import static java.lang.String.format; + +@SuppressWarnings("unchecked") +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 helpOpt = parser.accepts(OPT_HELP, "Print method help").forHelp(); + + @Getter + protected OptionSet options; + @Getter + protected List nonOptionArguments; + + protected AbstractMethodOptionParser(String[] args) { + this.args = args; + } + + public AbstractMethodOptionParser parse() { + try { + options = parser.parse(new ArgumentList(args).getMethodArguments()); + nonOptionArguments = (List) options.nonOptionArguments(); + return this; + } catch (OptionException ex) { + throw new IllegalArgumentException(cliExceptionMessageStyle.apply(ex), ex); + } + } + + public boolean isForHelp() { + return options.has(helpOpt); + } + + protected void verifyStringIsValidDouble(String string) { + try { + Double.valueOf(string); + } catch (NumberFormatException ex) { + throw new IllegalArgumentException(format("%s is not a number", string)); + } + } + + protected final Predicate> valueNotSpecified = (opt) -> + !options.hasArgument(opt) || options.valueOf(opt).isEmpty(); + + private final Function 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; + }; +} diff --git a/bot/src/main/java/haveno/cli/opts/ArgumentList.java b/bot/src/main/java/haveno/cli/opts/ArgumentList.java new file mode 100644 index 00000000..cf8ff977 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/ArgumentList.java @@ -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 . + */ + +package haveno.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 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 isMethodNameOpt = (o) -> !o.startsWith("-"); + + private final Predicate 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 methodNameArgument = Optional.empty(); + Optional helpArgument = Optional.empty(); + List 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 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]; + } +} diff --git a/bot/src/main/java/haveno/cli/opts/CancelOfferOptionParser.java b/bot/src/main/java/haveno/cli/opts/CancelOfferOptionParser.java new file mode 100644 index 00000000..b0439b5f --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/CancelOfferOptionParser.java @@ -0,0 +1,38 @@ +/* + * 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 . + */ + +package haveno.cli.opts; + + +public class CancelOfferOptionParser extends OfferIdOptionParser implements MethodOpts { + + public CancelOfferOptionParser(String[] args) { + super(args); + } + + public CancelOfferOptionParser parse() { + super.parse(); + + // Super class will short-circuit parsing if help option is present. + + return this; + } + + public String getOfferId() { + return options.valueOf(offerIdOpt); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java b/bot/src/main/java/haveno/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java new file mode 100644 index 00000000..9a65e7ca --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java @@ -0,0 +1,90 @@ +/* + * 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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.CryptoCurrencyUtil.apiDoesSupportCryptoCurrency; +import static haveno.cli.opts.OptLabel.OPT_ACCOUNT_NAME; +import static haveno.cli.opts.OptLabel.OPT_ADDRESS; +import static haveno.cli.opts.OptLabel.OPT_CURRENCY_CODE; +import static haveno.cli.opts.OptLabel.OPT_TRADE_INSTANT; +import static java.lang.String.format; + +public class CreateCryptoCurrencyPaymentAcctOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec accountNameOpt = parser.accepts(OPT_ACCOUNT_NAME, "crypto currency account name") + .withRequiredArg(); + + final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "crypto currency code (xmr)") + .withRequiredArg(); + + final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "crypto address") + .withRequiredArg(); + + final OptionSpec 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"); + + String cryptoCurrencyCode = options.valueOf(currencyCodeOpt); + if (!apiDoesSupportCryptoCurrency(cryptoCurrencyCode)) + throw new IllegalArgumentException(format("api does not support %s payment accounts", + cryptoCurrencyCode.toLowerCase())); + + if (!options.has(addressOpt) || options.valueOf(addressOpt).isEmpty()) + throw new IllegalArgumentException(format("no %s address specified", + cryptoCurrencyCode.toLowerCase())); + + 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); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/CreateOfferOptionParser.java b/bot/src/main/java/haveno/cli/opts/CreateOfferOptionParser.java new file mode 100644 index 00000000..610667cd --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/CreateOfferOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_AMOUNT; +import static haveno.cli.opts.OptLabel.OPT_CURRENCY_CODE; +import static haveno.cli.opts.OptLabel.OPT_DIRECTION; +import static haveno.cli.opts.OptLabel.OPT_FIXED_PRICE; +import static haveno.cli.opts.OptLabel.OPT_MIN_AMOUNT; +import static haveno.cli.opts.OptLabel.OPT_MKT_PRICE_MARGIN; +import static haveno.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_ID; +import static haveno.cli.opts.OptLabel.OPT_SECURITY_DEPOSIT; +import static joptsimple.internal.Strings.EMPTY; + +public class CreateOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT_ID, + "id of payment account used for offer") + .withRequiredArg() + .defaultsTo(EMPTY); + + final OptionSpec directionOpt = parser.accepts(OPT_DIRECTION, "offer direction (buy|sell)") + .withRequiredArg(); + + final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency code (xmr|eur|usd|...)") + .withRequiredArg(); + + final OptionSpec amountOpt = parser.accepts(OPT_AMOUNT, "amount of btc to buy or sell") + .withRequiredArg(); + + final OptionSpec minAmountOpt = parser.accepts(OPT_MIN_AMOUNT, "minimum amount of btc to buy or sell") + .withOptionalArg(); + + final OptionSpec mktPriceMarginPctOpt = parser.accepts(OPT_MKT_PRICE_MARGIN, "market btc price margin (%)") + .withOptionalArg() + .defaultsTo("0.00"); + + final OptionSpec fixedPriceOpt = parser.accepts(OPT_FIXED_PRICE, "fixed btc price") + .withOptionalArg() + .defaultsTo("0"); + + final OptionSpec securityDepositPctOpt = parser.accepts(OPT_SECURITY_DEPOSIT, "maker security deposit (%)") + .withRequiredArg(); + + public CreateOfferOptionParser(String[] args) { + super(args); + } + + @Override + public CreateOfferOptionParser 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"); + + if (!options.has(amountOpt) || options.valueOf(amountOpt).isEmpty()) + throw new IllegalArgumentException("no btc amount specified"); + + if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty()) + throw new IllegalArgumentException("no payment account id specified"); + + if (!options.has(mktPriceMarginPctOpt) && !options.has(fixedPriceOpt)) + throw new IllegalArgumentException("no market price margin or fixed price specified"); + + if (options.has(mktPriceMarginPctOpt)) { + var mktPriceMarginPctString = options.valueOf(mktPriceMarginPctOpt); + if (mktPriceMarginPctString.isEmpty()) + throw new IllegalArgumentException("no market price margin specified"); + else + verifyStringIsValidDouble(mktPriceMarginPctString); + } + + if (options.has(fixedPriceOpt) && options.valueOf(fixedPriceOpt).isEmpty()) + throw new IllegalArgumentException("no fixed price specified"); + + if (!options.has(securityDepositPctOpt) || options.valueOf(securityDepositPctOpt).isEmpty()) + throw new IllegalArgumentException("no security deposit specified"); + else + verifyStringIsValidDouble(options.valueOf(securityDepositPctOpt)); + + 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(mktPriceMarginPctOpt); + } + + public double getMktPriceMarginPct() { + return isUsingMktPriceMargin() ? Double.parseDouble(options.valueOf(mktPriceMarginPctOpt)) : 0.00d; + } + + public String getFixedPrice() { + return options.has(fixedPriceOpt) ? options.valueOf(fixedPriceOpt) : "0.00"; + } + + public double getSecurityDepositPct() { + return Double.valueOf(options.valueOf(securityDepositPctOpt)); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/CreatePaymentAcctOptionParser.java b/bot/src/main/java/haveno/cli/opts/CreatePaymentAcctOptionParser.java new file mode 100644 index 00000000..8070a834 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/CreatePaymentAcctOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import static haveno.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_FORM; +import static java.lang.String.format; + +public class CreatePaymentAcctOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec 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)); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/GetAddressBalanceOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetAddressBalanceOptionParser.java new file mode 100644 index 00000000..f0f9ff1c --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/GetAddressBalanceOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_ADDRESS; + +public class GetAddressBalanceOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec 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); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/GetBTCMarketPriceOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetBTCMarketPriceOptionParser.java new file mode 100644 index 00000000..8efc94be --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/GetBTCMarketPriceOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_CURRENCY_CODE; + +public class GetBTCMarketPriceOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec 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); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/GetBalanceOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetBalanceOptionParser.java new file mode 100644 index 00000000..9fa008c3 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/GetBalanceOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_CURRENCY_CODE; +import static joptsimple.internal.Strings.EMPTY; + +public class GetBalanceOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "wallet currency code (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) : ""; + } +} diff --git a/bot/src/main/java/haveno/cli/opts/GetOfferOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetOfferOptionParser.java new file mode 100644 index 00000000..961674de --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/GetOfferOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_OFFER_ID; + +public class GetOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec 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); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/GetOffersOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetOffersOptionParser.java new file mode 100644 index 00000000..a0665255 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/GetOffersOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_CURRENCY_CODE; +import static haveno.cli.opts.OptLabel.OPT_DIRECTION; + +public class GetOffersOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec directionOpt = parser.accepts(OPT_DIRECTION, "offer direction (buy|sell)") + .withRequiredArg(); + + final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency code (xmr|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); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/GetPaymentAcctFormOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetPaymentAcctFormOptionParser.java new file mode 100644 index 00000000..101bca8e --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/GetPaymentAcctFormOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_PAYMENT_METHOD_ID; + +public class GetPaymentAcctFormOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec 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); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/GetTradeOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetTradeOptionParser.java new file mode 100644 index 00000000..372bf3fb --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/GetTradeOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_SHOW_CONTRACT; +import static haveno.cli.opts.OptLabel.OPT_TRADE_ID; + +public class GetTradeOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec tradeIdOpt = parser.accepts(OPT_TRADE_ID, "id of trade") + .withRequiredArg(); + + final OptionSpec 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; + } +} diff --git a/bot/src/main/java/haveno/cli/opts/GetTradesOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetTradesOptionParser.java new file mode 100644 index 00000000..9d7ca04b --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/GetTradesOptionParser.java @@ -0,0 +1,83 @@ +/* + * 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 . + */ + +package haveno.cli.opts; + + +import haveno.proto.grpc.GetTradesRequest; +import joptsimple.OptionSpec; + +import java.util.function.Predicate; + +import static haveno.cli.opts.OptLabel.OPT_CATEGORY; +import static haveno.proto.grpc.GetTradesRequest.Category.CLOSED; +import static haveno.proto.grpc.GetTradesRequest.Category.FAILED; +import static haveno.proto.grpc.GetTradesRequest.Category.OPEN; +import static java.util.Arrays.stream; + +public class GetTradesOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + // Map valid CLI option values to gRPC request parameters. + private enum CATEGORY { + // Lower case enum fits CLI method and parameter style. + open(OPEN), + closed(CLOSED), + failed(FAILED); + + private final GetTradesRequest.Category grpcRequestCategory; + + CATEGORY(GetTradesRequest.Category grpcRequestCategory) { + this.grpcRequestCategory = grpcRequestCategory; + } + } + + final OptionSpec categoryOpt = parser.accepts(OPT_CATEGORY, + "category of trades (open|closed|failed)") + .withRequiredArg() + .defaultsTo(CATEGORY.open.name()); + + private final Predicate isValidCategory = (c) -> + stream(CATEGORY.values()).anyMatch(v -> v.name().equalsIgnoreCase(c)); + + public GetTradesOptionParser(String[] args) { + super(args); + } + + public GetTradesOptionParser parse() { + super.parse(); + + // Short circuit opt validation if user just wants help. + if (options.has(helpOpt)) + return this; + + if (options.has(categoryOpt)) { + String category = options.valueOf(categoryOpt); + if (category.isEmpty()) + throw new IllegalArgumentException("no category (open|closed|failed) specified"); + + if (!isValidCategory.test(category)) + throw new IllegalArgumentException("category must be open|closed|failed"); + } + + return this; + } + + public GetTradesRequest.Category getCategory() { + String categoryOpt = options.valueOf(this.categoryOpt).toLowerCase(); + return CATEGORY.valueOf(categoryOpt).grpcRequestCategory; + } +} diff --git a/bot/src/main/java/haveno/cli/opts/GetTransactionOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetTransactionOptionParser.java new file mode 100644 index 00000000..32bbf0f8 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/GetTransactionOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_TRANSACTION_ID; + +public class GetTransactionOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec 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); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/MethodOpts.java b/bot/src/main/java/haveno/cli/opts/MethodOpts.java new file mode 100644 index 00000000..167cec87 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/MethodOpts.java @@ -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 . + */ + +package haveno.cli.opts; + +public interface MethodOpts { + + MethodOpts parse(); + + boolean isForHelp(); +} diff --git a/bot/src/main/java/haveno/cli/opts/OfferIdOptionParser.java b/bot/src/main/java/haveno/cli/opts/OfferIdOptionParser.java new file mode 100644 index 00000000..4161e5e0 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/OfferIdOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_OFFER_ID; + +/** + * Superclass for option parsers requiring an offer-id. Avoids a small amount of + * duplicated boilerplate. + */ +public class OfferIdOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer") + .withRequiredArg(); + + public OfferIdOptionParser(String[] args) { + this(args, false); + } + + public OfferIdOptionParser(String[] args, boolean allowsUnrecognizedOptions) { + super(args); + if (allowsUnrecognizedOptions) + this.parser.allowsUnrecognizedOptions(); + } + + public OfferIdOptionParser 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); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/OptLabel.java b/bot/src/main/java/haveno/cli/opts/OptLabel.java new file mode 100644 index 00000000..79ad2ae1 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/OptLabel.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ + +package haveno.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_CATEGORY = "category"; + 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_ENABLE = "enable"; + 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_ID = "payment-account-id"; + 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_TRIGGER_PRICE = "trigger-price"; + 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"; +} diff --git a/bot/src/main/java/haveno/cli/opts/RegisterDisputeAgentOptionParser.java b/bot/src/main/java/haveno/cli/opts/RegisterDisputeAgentOptionParser.java new file mode 100644 index 00000000..c9c22926 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/RegisterDisputeAgentOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_DISPUTE_AGENT_TYPE; +import static haveno.cli.opts.OptLabel.OPT_REGISTRATION_KEY; + +public class RegisterDisputeAgentOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec disputeAgentTypeOpt = parser.accepts(OPT_DISPUTE_AGENT_TYPE, "dispute agent type") + .withRequiredArg(); + + final OptionSpec 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); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/RemoveWalletPasswordOptionParser.java b/bot/src/main/java/haveno/cli/opts/RemoveWalletPasswordOptionParser.java new file mode 100644 index 00000000..4483af94 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/RemoveWalletPasswordOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_WALLET_PASSWORD; + +public class RemoveWalletPasswordOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "haveno 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); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/SendBtcOptionParser.java b/bot/src/main/java/haveno/cli/opts/SendBtcOptionParser.java new file mode 100644 index 00000000..12eec3bc --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/SendBtcOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_ADDRESS; +import static haveno.cli.opts.OptLabel.OPT_AMOUNT; +import static haveno.cli.opts.OptLabel.OPT_MEMO; +import static haveno.cli.opts.OptLabel.OPT_TX_FEE_RATE; +import static joptsimple.internal.Strings.EMPTY; + +public class SendBtcOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "destination btc address") + .withRequiredArg(); + + final OptionSpec amountOpt = parser.accepts(OPT_AMOUNT, "amount of btc to send") + .withRequiredArg(); + + final OptionSpec feeRateOpt = parser.accepts(OPT_TX_FEE_RATE, "optional tx fee rate (sats/byte)") + .withOptionalArg() + .defaultsTo(EMPTY); + + final OptionSpec 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) : ""; + } +} diff --git a/bot/src/main/java/haveno/cli/opts/SetTxFeeRateOptionParser.java b/bot/src/main/java/haveno/cli/opts/SetTxFeeRateOptionParser.java new file mode 100644 index 00000000..3fbd9ab3 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/SetTxFeeRateOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_TX_FEE_RATE; + +public class SetTxFeeRateOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec 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); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/SetWalletPasswordOptionParser.java b/bot/src/main/java/haveno/cli/opts/SetWalletPasswordOptionParser.java new file mode 100644 index 00000000..4b180fe3 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/SetWalletPasswordOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_NEW_WALLET_PASSWORD; +import static haveno.cli.opts.OptLabel.OPT_WALLET_PASSWORD; +import static joptsimple.internal.Strings.EMPTY; + +public class SetWalletPasswordOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "haveno wallet password") + .withRequiredArg(); + + final OptionSpec newPasswordOpt = parser.accepts(OPT_NEW_WALLET_PASSWORD, "new haveno 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) : ""; + } +} diff --git a/bot/src/main/java/haveno/cli/opts/SimpleMethodOptionParser.java b/bot/src/main/java/haveno/cli/opts/SimpleMethodOptionParser.java new file mode 100644 index 00000000..93ee2ce7 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/SimpleMethodOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +public class SimpleMethodOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + public SimpleMethodOptionParser(String[] args) { + super(args); + } + + public SimpleMethodOptionParser parse() { + return (SimpleMethodOptionParser) super.parse(); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/TakeOfferOptionParser.java b/bot/src/main/java/haveno/cli/opts/TakeOfferOptionParser.java new file mode 100644 index 00000000..e45ea776 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/TakeOfferOptionParser.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_ID; + +public class TakeOfferOptionParser extends OfferIdOptionParser implements MethodOpts { + + final OptionSpec paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT_ID, "id of payment account used for trade") + .withRequiredArg(); + + public TakeOfferOptionParser(String[] args) { + super(args, true); + } + + public TakeOfferOptionParser parse() { + super.parse(); + + // Super class will short-circuit parsing if help option is present. + + if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty()) + throw new IllegalArgumentException("no payment account id specified"); + + return this; + } + + public String getPaymentAccountId() { + return options.valueOf(paymentAccountIdOpt); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/UnlockWalletOptionParser.java b/bot/src/main/java/haveno/cli/opts/UnlockWalletOptionParser.java new file mode 100644 index 00000000..dad3f826 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/UnlockWalletOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_TIMEOUT; +import static haveno.cli.opts.OptLabel.OPT_WALLET_PASSWORD; + +public class UnlockWalletOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "haveno wallet password") + .withRequiredArg(); + + final OptionSpec 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); + } +} diff --git a/bot/src/main/java/haveno/cli/opts/WithdrawFundsOptionParser.java b/bot/src/main/java/haveno/cli/opts/WithdrawFundsOptionParser.java new file mode 100644 index 00000000..96702994 --- /dev/null +++ b/bot/src/main/java/haveno/cli/opts/WithdrawFundsOptionParser.java @@ -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 . + */ + +package haveno.cli.opts; + + +import joptsimple.OptionSpec; + +import static haveno.cli.opts.OptLabel.OPT_ADDRESS; +import static haveno.cli.opts.OptLabel.OPT_MEMO; +import static haveno.cli.opts.OptLabel.OPT_TRADE_ID; +import static joptsimple.internal.Strings.EMPTY; + +public class WithdrawFundsOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + final OptionSpec tradeIdOpt = parser.accepts(OPT_TRADE_ID, "id of trade") + .withRequiredArg(); + + final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "destination btc address") + .withRequiredArg(); + + final OptionSpec 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) : ""; + } +} diff --git a/bot/src/main/java/haveno/cli/request/OffersServiceRequest.java b/bot/src/main/java/haveno/cli/request/OffersServiceRequest.java new file mode 100644 index 00000000..2fcb3426 --- /dev/null +++ b/bot/src/main/java/haveno/cli/request/OffersServiceRequest.java @@ -0,0 +1,163 @@ +/* + * 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 . + */ + +package haveno.cli.request; + +import haveno.cli.GrpcStubs; +import haveno.proto.grpc.CancelOfferRequest; +import haveno.proto.grpc.GetMyOfferRequest; +import haveno.proto.grpc.GetMyOffersRequest; +import haveno.proto.grpc.GetOfferRequest; +import haveno.proto.grpc.GetOffersRequest; +import haveno.proto.grpc.OfferInfo; +import haveno.proto.grpc.PostOfferRequest; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.toList; +import static protobuf.OfferDirection.BUY; +import static protobuf.OfferDirection.SELL; + +public class OffersServiceRequest { + + private final GrpcStubs grpcStubs; + + public OffersServiceRequest(GrpcStubs grpcStubs) { + this.grpcStubs = grpcStubs; + } + + @SuppressWarnings("unused") + public OfferInfo createFixedPricedOffer(String direction, + String currencyCode, + long amount, + long minAmount, + String fixedPrice, + double securityDepositPct, + String paymentAcctId, + String makerFeeCurrencyCode) { + return createOffer(direction, + currencyCode, + amount, + minAmount, + false, + fixedPrice, + 0.00, + securityDepositPct, + paymentAcctId, + "0" /* no trigger price */); + } + + public OfferInfo createOffer(String direction, + String currencyCode, + long amount, + long minAmount, + boolean useMarketBasedPrice, + String fixedPrice, + double marketPriceMarginPct, + double securityDepositPct, + String paymentAcctId, + String triggerPrice) { + var request = PostOfferRequest.newBuilder() + .setDirection(direction) + .setCurrencyCode(currencyCode) + .setAmount(amount) + .setMinAmount(minAmount) + .setUseMarketBasedPrice(useMarketBasedPrice) + .setPrice(fixedPrice) + .setMarketPriceMarginPct(marketPriceMarginPct) + .setSecurityDepositPct(securityDepositPct) + .setPaymentAccountId(paymentAcctId) + .setTriggerPrice(triggerPrice) + .build(); + return grpcStubs.offersService.postOffer(request).getOffer(); + } + + public void cancelOffer(String offerId) { + var request = CancelOfferRequest.newBuilder() + .setId(offerId) + .build(); + //noinspection ResultOfMethodCallIgnored + 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 getOffers(String direction, String currencyCode) { + var request = GetOffersRequest.newBuilder() + .setDirection(direction) + .setCurrencyCode(currencyCode) + .build(); + return grpcStubs.offersService.getOffers(request).getOffersList(); + } + + public List getOffersSortedByDate(String currencyCode) { + ArrayList offers = new ArrayList<>(); + offers.addAll(getOffers(BUY.name(), currencyCode)); + offers.addAll(getOffers(SELL.name(), currencyCode)); + return offers.isEmpty() ? offers : sortOffersByDate(offers); + } + + public List getOffersSortedByDate(String direction, String currencyCode) { + var offers = getOffers(direction, currencyCode); + return offers.isEmpty() ? offers : sortOffersByDate(offers); + } + + public List getMyOffers(String direction, String currencyCode) { + var request = GetMyOffersRequest.newBuilder() + .setDirection(direction) + .setCurrencyCode(currencyCode) + .build(); + return grpcStubs.offersService.getMyOffers(request).getOffersList(); + } + + public List getMyOffersSortedByDate(String currencyCode) { + ArrayList offers = new ArrayList<>(); + offers.addAll(getMyOffers(BUY.name(), currencyCode)); + offers.addAll(getMyOffers(SELL.name(), currencyCode)); + return offers.isEmpty() ? offers : sortOffersByDate(offers); + } + + public List getMyOffersSortedByDate(String direction, String currencyCode) { + var offers = getMyOffers(direction, currencyCode); + return offers.isEmpty() ? offers : sortOffersByDate(offers); + } + + public OfferInfo getMostRecentOffer(String direction, String currencyCode) { + List offers = getOffersSortedByDate(direction, currencyCode); + return offers.isEmpty() ? null : offers.get(offers.size() - 1); + } + + public List sortOffersByDate(List offerInfoList) { + return offerInfoList.stream() + .sorted(comparing(OfferInfo::getDate)) + .collect(toList()); + } +} diff --git a/bot/src/main/java/haveno/cli/request/PaymentAccountsServiceRequest.java b/bot/src/main/java/haveno/cli/request/PaymentAccountsServiceRequest.java new file mode 100644 index 00000000..4aaa53eb --- /dev/null +++ b/bot/src/main/java/haveno/cli/request/PaymentAccountsServiceRequest.java @@ -0,0 +1,99 @@ +/* + * 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 . + */ + +package haveno.cli.request; + +import haveno.cli.GrpcStubs; +import haveno.proto.grpc.CreateCryptoCurrencyPaymentAccountRequest; +import haveno.proto.grpc.CreatePaymentAccountRequest; +import haveno.proto.grpc.GetCryptoCurrencyPaymentMethodsRequest; +import haveno.proto.grpc.GetPaymentAccountFormAsJsonRequest; +import haveno.proto.grpc.GetPaymentAccountsRequest; +import haveno.proto.grpc.GetPaymentMethodsRequest; +import protobuf.PaymentAccount; +import protobuf.PaymentMethod; + +import java.util.List; + +import static java.lang.String.format; + +public class PaymentAccountsServiceRequest { + + private final GrpcStubs grpcStubs; + + public PaymentAccountsServiceRequest(GrpcStubs grpcStubs) { + this.grpcStubs = grpcStubs; + } + + public List getPaymentMethods() { + var request = GetPaymentMethodsRequest.newBuilder().build(); + return grpcStubs.paymentAccountsService.getPaymentMethods(request).getPaymentMethodsList(); + } + + public String getPaymentAcctFormAsJson(String paymentMethodId) { + var request = GetPaymentAccountFormAsJsonRequest.newBuilder() + .setPaymentMethodId(paymentMethodId) + .build(); + return grpcStubs.paymentAccountsService.getPaymentAccountFormAsJson(request).getPaymentAccountFormAsJson(); + } + + public PaymentAccount createPaymentAccount(String json) { + var request = CreatePaymentAccountRequest.newBuilder() + .setPaymentAccountFormAsJson(json) + .build(); + return grpcStubs.paymentAccountsService.createPaymentAccount(request).getPaymentAccount(); + } + + public List getPaymentAccounts() { + var request = GetPaymentAccountsRequest.newBuilder().build(); + return grpcStubs.paymentAccountsService.getPaymentAccounts(request).getPaymentAccountsList(); + } + + /** + * Returns the first PaymentAccount found with the given name, or throws an + * IllegalArgumentException if not found. This method should be used with care; + * it will only return one PaymentAccount, and the account name must be an exact + * match on the name argument. + * @param accountName the name of the stored PaymentAccount to retrieve + * @return PaymentAccount with given name + */ + public PaymentAccount getPaymentAccount(String accountName) { + return getPaymentAccounts().stream() + .filter(a -> a.getAccountName().equals(accountName)).findFirst() + .orElseThrow(() -> + new IllegalArgumentException(format("payment account with name '%s' not found", + accountName))); + } + + 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 getCryptoPaymentMethods() { + var request = GetCryptoCurrencyPaymentMethodsRequest.newBuilder().build(); + return grpcStubs.paymentAccountsService.getCryptoCurrencyPaymentMethods(request).getPaymentMethodsList(); + } +} diff --git a/bot/src/main/java/haveno/cli/request/TradesServiceRequest.java b/bot/src/main/java/haveno/cli/request/TradesServiceRequest.java new file mode 100644 index 00000000..e4826771 --- /dev/null +++ b/bot/src/main/java/haveno/cli/request/TradesServiceRequest.java @@ -0,0 +1,107 @@ +/* + * 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 . + */ + +package haveno.cli.request; + +import haveno.cli.GrpcStubs; +import haveno.proto.grpc.ConfirmPaymentReceivedRequest; +import haveno.proto.grpc.ConfirmPaymentSentRequest; +import haveno.proto.grpc.GetTradeRequest; +import haveno.proto.grpc.GetTradesRequest; +import haveno.proto.grpc.TakeOfferReply; +import haveno.proto.grpc.TakeOfferRequest; +import haveno.proto.grpc.TradeInfo; +import haveno.proto.grpc.WithdrawFundsRequest; + +import java.util.List; + +import static haveno.proto.grpc.GetTradesRequest.Category.CLOSED; +import static haveno.proto.grpc.GetTradesRequest.Category.FAILED; + +public class TradesServiceRequest { + + private final GrpcStubs grpcStubs; + + public TradesServiceRequest(GrpcStubs grpcStubs) { + this.grpcStubs = grpcStubs; + } + + public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId) { + var request = TakeOfferRequest.newBuilder() + .setOfferId(offerId) + .setPaymentAccountId(paymentAccountId) + .build(); + return grpcStubs.tradesService.takeOffer(request); + } + + public TradeInfo takeOffer(String offerId, String paymentAccountId) { + var reply = getTakeOfferReply(offerId, paymentAccountId); + 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 List getOpenTrades() { + var request = GetTradesRequest.newBuilder() + .build(); + return grpcStubs.tradesService.getTrades(request).getTradesList(); + } + + public List getTradeHistory(GetTradesRequest.Category category) { + if (!category.equals(CLOSED) && !category.equals(FAILED)) + throw new IllegalStateException("unrecognized gettrades category parameter " + category.name()); + + var request = GetTradesRequest.newBuilder() + .setCategory(category) + .build(); + return grpcStubs.tradesService.getTrades(request).getTradesList(); + } + + public void confirmPaymentSent(String tradeId) { + var request = ConfirmPaymentSentRequest.newBuilder() + .setTradeId(tradeId) + .build(); + //noinspection ResultOfMethodCallIgnored + grpcStubs.tradesService.confirmPaymentSent(request); + } + + public void confirmPaymentReceived(String tradeId) { + var request = ConfirmPaymentReceivedRequest.newBuilder() + .setTradeId(tradeId) + .build(); + //noinspection ResultOfMethodCallIgnored + grpcStubs.tradesService.confirmPaymentReceived(request); + } + + public void withdrawFunds(String tradeId, String address, String memo) { + var request = WithdrawFundsRequest.newBuilder() + .setTradeId(tradeId) + .setAddress(address) + .setMemo(memo) + .build(); + //noinspection ResultOfMethodCallIgnored + grpcStubs.tradesService.withdrawFunds(request); + } +} diff --git a/bot/src/main/java/haveno/cli/request/WalletsServiceRequest.java b/bot/src/main/java/haveno/cli/request/WalletsServiceRequest.java new file mode 100644 index 00000000..dde9ce25 --- /dev/null +++ b/bot/src/main/java/haveno/cli/request/WalletsServiceRequest.java @@ -0,0 +1,123 @@ +/* + * 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 . + */ + +package haveno.cli.request; + +import haveno.cli.GrpcStubs; +import haveno.proto.grpc.AddressBalanceInfo; +import haveno.proto.grpc.BalancesInfo; +import haveno.proto.grpc.BtcBalanceInfo; +import haveno.proto.grpc.GetAddressBalanceRequest; +import haveno.proto.grpc.GetBalancesRequest; +import haveno.proto.grpc.GetFundingAddressesRequest; +import haveno.proto.grpc.LockWalletRequest; +import haveno.proto.grpc.MarketPriceRequest; +import haveno.proto.grpc.RemoveWalletPasswordRequest; +import haveno.proto.grpc.SetWalletPasswordRequest; +import haveno.proto.grpc.UnlockWalletRequest; + +import java.util.List; + +public class WalletsServiceRequest { + + private final GrpcStubs grpcStubs; + + public WalletsServiceRequest(GrpcStubs grpcStubs) { + this.grpcStubs = grpcStubs; + } + + public BalancesInfo getBalances() { + return getBalances(""); + } + + 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 getFundingAddresses() { + var request = GetFundingAddressesRequest.newBuilder().build(); + return grpcStubs.walletsService.getFundingAddresses(request).getAddressBalanceInfoList(); + } + + 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 void lockWallet() { + var request = LockWalletRequest.newBuilder().build(); + //noinspection ResultOfMethodCallIgnored + grpcStubs.walletsService.lockWallet(request); + } + + public void unlockWallet(String walletPassword, long timeout) { + var request = UnlockWalletRequest.newBuilder() + .setPassword(walletPassword) + .setTimeout(timeout).build(); + //noinspection ResultOfMethodCallIgnored + grpcStubs.walletsService.unlockWallet(request); + } + + public void removeWalletPassword(String walletPassword) { + var request = RemoveWalletPasswordRequest.newBuilder() + .setPassword(walletPassword).build(); + //noinspection ResultOfMethodCallIgnored + grpcStubs.walletsService.removeWalletPassword(request); + } + + public void setWalletPassword(String walletPassword) { + var request = SetWalletPasswordRequest.newBuilder() + .setPassword(walletPassword).build(); + //noinspection ResultOfMethodCallIgnored + grpcStubs.walletsService.setWalletPassword(request); + } + + public void setWalletPassword(String oldWalletPassword, String newWalletPassword) { + var request = SetWalletPasswordRequest.newBuilder() + .setPassword(oldWalletPassword) + .setNewPassword(newWalletPassword).build(); + //noinspection ResultOfMethodCallIgnored + grpcStubs.walletsService.setWalletPassword(request); + } +} diff --git a/bot/src/main/java/haveno/cli/table/Table.java b/bot/src/main/java/haveno/cli/table/Table.java new file mode 100644 index 00000000..66a14c40 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/Table.java @@ -0,0 +1,152 @@ +/* + * 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 . + */ + +package haveno.cli.table; + +import haveno.cli.table.column.Column; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.stream.IntStream; + +import static com.google.common.base.Strings.padStart; +import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * A simple table of formatted data for the CLI's output console. A table must be + * created with at least one populated column, and each column passed to the constructor + * must contain the same number of rows. Null checking is omitted because tables are + * populated by protobuf message fields which cannot be null. + * + * All data in a column has the same type: long, string, etc., but a table + * may contain an arbitrary number of columns of any type. For output formatting + * purposes, numeric and date columns should be transformed to a StringColumn type with + * formatted and justified string values before being passed to the constructor. + * + * This is not a relational, rdbms table. + */ +public class Table { + + public final Column[] columns; + public final int rowCount; + + // Each printed column is delimited by two spaces. + private final int columnDelimiterLength = 2; + + /** + * Default constructor. Takes populated Columns. + * + * @param columns containing the same number of rows + */ + public Table(Column... columns) { + this.columns = columns; + this.rowCount = columns.length > 0 ? columns[0].rowCount() : 0; + validateStructure(); + } + + /** + * Print table data to a PrintStream. + * + * @param printStream the target output stream + */ + public void print(PrintStream printStream) { + printColumnNames(printStream); + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) { + printRow(printStream, rowIndex); + } + } + + /** + * Print table column names to a PrintStream. + * + * @param printStream the target output stream + */ + private void printColumnNames(PrintStream printStream) { + IntStream.range(0, columns.length).forEachOrdered(colIndex -> { + var c = columns[colIndex]; + var justifiedName = c.getJustification().equals(RIGHT) + ? padStart(c.getName(), c.getWidth(), ' ') + : c.getName(); + var paddedWidth = colIndex == columns.length - 1 + ? c.getName().length() + : c.getWidth() + columnDelimiterLength; + printStream.printf("%-" + paddedWidth + "s", justifiedName); + }); + printStream.println(); + } + + /** + * Print a table row to a PrintStream. + * + * @param printStream the target output stream + */ + private void printRow(PrintStream printStream, int rowIndex) { + IntStream.range(0, columns.length).forEachOrdered(colIndex -> { + var c = columns[colIndex]; + var paddedWidth = colIndex == columns.length - 1 + ? c.getWidth() + : c.getWidth() + columnDelimiterLength; + printStream.printf("%-" + paddedWidth + "s", c.getRow(rowIndex)); + if (colIndex == columns.length - 1) + printStream.println(); + }); + } + + /** + * Returns the table's formatted output as a String. + * @return String + */ + @Override + public String toString() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (PrintStream ps = new PrintStream(baos, true, UTF_8)) { + print(ps); + } + return baos.toString(); + } + + /** + * Verifies the table has columns, and each column has the same number of rows. + */ + private void validateStructure() { + if (columns.length == 0) + throw new IllegalArgumentException("Table has no columns."); + + if (columns[0].isEmpty()) + throw new IllegalArgumentException( + format("Table's 1st column (%s) has no data.", + columns[0].getName())); + + IntStream.range(1, columns.length).forEachOrdered(colIndex -> { + var c = columns[colIndex]; + + if (c.isEmpty()) + throw new IllegalStateException( + format("Table column # %d (%s) does not have any data.", + colIndex + 1, + c.getName())); + + if (this.rowCount != c.rowCount()) + throw new IllegalStateException( + format("Table column # %d (%s) does not have same number of rows as 1st column.", + colIndex + 1, + c.getName())); + }); + } +} diff --git a/bot/src/main/java/haveno/cli/table/builder/AbstractTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/AbstractTableBuilder.java new file mode 100644 index 00000000..17cf2f71 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/AbstractTableBuilder.java @@ -0,0 +1,44 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.Table; +import haveno.proto.grpc.OfferInfo; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Abstract superclass for TableBuilder implementations. + */ +abstract class AbstractTableBuilder { + + protected final Predicate isTraditionalOffer = (o) -> o.getBaseCurrencyCode().equals("XMR"); + + protected final TableType tableType; + protected final List protos; + + AbstractTableBuilder(TableType tableType, List protos) { + this.tableType = tableType; + this.protos = protos; + if (protos.isEmpty()) + throw new IllegalArgumentException("cannot build a table without rows"); + } + + public abstract Table build(); +} diff --git a/bot/src/main/java/haveno/cli/table/builder/AbstractTradeListBuilder.java b/bot/src/main/java/haveno/cli/table/builder/AbstractTradeListBuilder.java new file mode 100644 index 00000000..b279f68b --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/AbstractTradeListBuilder.java @@ -0,0 +1,241 @@ +/* + * 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 . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.column.Column; +import haveno.cli.table.column.MixedTradeFeeColumn; +import haveno.proto.grpc.ContractInfo; +import haveno.proto.grpc.TradeInfo; + +import javax.annotation.Nullable; +import java.math.BigDecimal; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static haveno.cli.CurrencyFormat.formatSatoshis; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_BUYER_DEPOSIT; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_SELLER_DEPOSIT; +import static haveno.cli.table.builder.TableType.TRADE_DETAIL_TBL; +import static java.lang.String.format; +import static protobuf.OfferDirection.SELL; + +abstract class AbstractTradeListBuilder extends AbstractTableBuilder { + + protected final List trades; + + protected final TradeTableColumnSupplier colSupplier; + + protected final Column colTradeId; + @Nullable + protected final Column colCreateDate; + @Nullable + protected final Column colMarket; + protected final Column colPrice; + @Nullable + protected final Column colPriceDeviation; + @Nullable + protected final Column colCurrency; + @Nullable + protected final Column colAmount; + @Nullable + protected final Column colMixedAmount; + @Nullable + protected final MixedTradeFeeColumn colMixedTradeFee; + @Nullable + protected final Column colBuyerDeposit; + @Nullable + protected final Column colSellerDeposit; + @Nullable + protected final Column colPaymentMethod; + @Nullable + protected final Column colRole; + @Nullable + protected final Column colOfferType; + @Nullable + protected final Column colClosingStatus; + + // Trade detail tbl specific columns + + @Nullable + protected final Column colIsDepositPublished; + @Nullable + protected final Column colIsDepositConfirmed; + @Nullable + protected final Column colIsPayoutPublished; + @Nullable + protected final Column colIsCompleted; + @Nullable + protected final Column colHavenoTradeFee; + @Nullable + protected final Column colTradeCost; + @Nullable + protected final Column colIsPaymentSentMessageSent; + @Nullable + protected final Column colIsPaymentReceivedMessageSent; + @Nullable + protected final Column colCryptoReceiveAddressColumn; + + AbstractTradeListBuilder(TableType tableType, List protos) { + super(tableType, protos); + validate(); + + this.trades = protos.stream().map(p -> (TradeInfo) p).collect(Collectors.toList()); + this.colSupplier = new TradeTableColumnSupplier(tableType, trades); + + this.colTradeId = colSupplier.tradeIdColumn.get(); + this.colCreateDate = colSupplier.createDateColumn.get(); + this.colMarket = colSupplier.marketColumn.get(); + this.colPrice = colSupplier.priceColumn.get(); + this.colPriceDeviation = colSupplier.priceDeviationColumn.get(); + this.colCurrency = colSupplier.currencyColumn.get(); + this.colAmount = colSupplier.amountColumn.get(); + this.colMixedAmount = colSupplier.mixedAmountColumn.get(); + this.colMixedTradeFee = colSupplier.mixedTradeFeeColumn.get(); + this.colBuyerDeposit = colSupplier.toSecurityDepositColumn.apply(COL_HEADER_BUYER_DEPOSIT); + this.colSellerDeposit = colSupplier.toSecurityDepositColumn.apply(COL_HEADER_SELLER_DEPOSIT); + this.colPaymentMethod = colSupplier.paymentMethodColumn.get(); + this.colRole = colSupplier.roleColumn.get(); + this.colOfferType = colSupplier.offerTypeColumn.get(); + this.colClosingStatus = colSupplier.statusDescriptionColumn.get(); + + // Trade detail specific columns, some in common with BSQ swap trades detail. + + this.colIsDepositPublished = colSupplier.depositPublishedColumn.get(); + this.colIsDepositConfirmed = colSupplier.depositConfirmedColumn.get(); + this.colIsPayoutPublished = colSupplier.payoutPublishedColumn.get(); + this.colIsCompleted = colSupplier.fundsWithdrawnColumn.get(); + this.colHavenoTradeFee = colSupplier.havenoTradeDetailFeeColumn.get(); + this.colTradeCost = colSupplier.tradeCostColumn.get(); + this.colIsPaymentSentMessageSent = colSupplier.paymentSentMessageSentColumn.get(); + this.colIsPaymentReceivedMessageSent = colSupplier.paymentReceivedMessageSentColumn.get(); + //noinspection ConstantConditions + this.colCryptoReceiveAddressColumn = colSupplier.cryptoReceiveAddressColumn.get(); + } + + protected void validate() { + if (isTradeDetailTblBuilder.get()) { + if (protos.size() != 1) + throw new IllegalArgumentException("trade detail tbl can have only one row"); + } else if (protos.isEmpty()) { + throw new IllegalArgumentException("trade tbl has no rows"); + } + } + + // Helper Functions + + private final Supplier isTradeDetailTblBuilder = () -> tableType.equals(TRADE_DETAIL_TBL); + protected final Predicate isTraditionalTrade = (t) -> isTraditionalOffer.test(t.getOffer()); + protected final Predicate isMyOffer = (t) -> t.getOffer().getIsMyOffer(); + protected final Predicate isTaker = (t) -> t.getRole().toLowerCase().contains("taker"); + protected final Predicate isSellOffer = (t) -> t.getOffer().getDirection().equals(SELL.name()); + protected final Predicate isBtcSeller = (t) -> (isMyOffer.test(t) && isSellOffer.test(t)) + || (!isMyOffer.test(t) && !isSellOffer.test(t)); + + + // Column Value Functions + + // Crypto volumes from server are string representations of decimals. + // Converting them to longs ("sats") requires shifting the decimal points + // to left: 2 for BSQ, 8 for other cryptos. + protected final Function toCryptoTradeVolumeAsLong = (t) -> new BigDecimal(t.getTradeVolume()).movePointRight(8).longValue(); + + protected final Function toTradeVolumeAsString = (t) -> + isTraditionalTrade.test(t) + ? t.getTradeVolume() + : formatSatoshis(t.getAmount()); + + protected final Function toTradeVolumeAsLong = (t) -> + isTraditionalTrade.test(t) + ? Long.parseLong(t.getTradeVolume()) + : toCryptoTradeVolumeAsLong.apply(t); + + protected final Function toTradeAmount = (t) -> + isTraditionalTrade.test(t) + ? t.getAmount() + : toTradeVolumeAsLong.apply(t); + + protected final Function toMarket = (t) -> + t.getOffer().getBaseCurrencyCode() + "/" + + t.getOffer().getCounterCurrencyCode(); + + protected final Function toPaymentCurrencyCode = (t) -> + isTraditionalTrade.test(t) + ? t.getOffer().getCounterCurrencyCode() + : t.getOffer().getBaseCurrencyCode(); + + protected final Function toPriceDeviation = (t) -> + t.getOffer().getUseMarketBasedPrice() + ? format("%.2f%s", t.getOffer().getMarketPriceMarginPct(), "%") + : "N/A"; + + protected final Function toTradeFeeBtc = (t) -> { + var isMyOffer = t.getOffer().getIsMyOffer(); + if (isMyOffer) { + return t.getMakerFee(); + } else { + return t.getTakerFee(); + } + }; + + protected final Function toMyMakerOrTakerFee = (t) -> { + return isTaker.test(t) + ? t.getTakerFee() + : t.getMakerFee(); + }; + + protected final Function toOfferType = (t) -> { + if (isTraditionalTrade.test(t)) { + return t.getOffer().getDirection() + " " + t.getOffer().getBaseCurrencyCode(); + } else { + if (t.getOffer().getDirection().equals("BUY")) { + return "SELL " + t.getOffer().getBaseCurrencyCode(); + } else { + return "BUY " + t.getOffer().getBaseCurrencyCode(); + } + } + }; + + protected final Predicate showCryptoBuyerAddress = (t) -> { + if (isTraditionalTrade.test(t)) { + return false; + } else { + ContractInfo contract = t.getContract(); + boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker(); + if (isTaker.test(t)) { + return !isBuyerMakerAndSellerTaker; + } else { + return isBuyerMakerAndSellerTaker; + } + } + }; + + protected final Function toCryptoReceiveAddress = (t) -> { + if (showCryptoBuyerAddress.test(t)) { + ContractInfo contract = t.getContract(); + boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker(); + return isBuyerMakerAndSellerTaker // (is BTC buyer / maker) + ? contract.getTakerPaymentAccountPayload().getCryptoCurrencyAccountPayload().getAddress() + : contract.getMakerPaymentAccountPayload().getCryptoCurrencyAccountPayload().getAddress(); + } else { + return ""; + } + }; +} diff --git a/bot/src/main/java/haveno/cli/table/builder/AddressBalanceTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/AddressBalanceTableBuilder.java new file mode 100644 index 00000000..4c26d30c --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/AddressBalanceTableBuilder.java @@ -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 . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.Table; +import haveno.cli.table.column.BooleanColumn; +import haveno.cli.table.column.Column; +import haveno.cli.table.column.LongColumn; +import haveno.cli.table.column.SatoshiColumn; +import haveno.cli.table.column.StringColumn; +import haveno.proto.grpc.AddressBalanceInfo; + +import java.util.List; +import java.util.stream.Collectors; + +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_ADDRESS; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_AVAILABLE_BALANCE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_CONFIRMATIONS; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_IS_USED_ADDRESS; +import static haveno.cli.table.builder.TableType.ADDRESS_BALANCE_TBL; +import static java.lang.String.format; + +/** + * Builds a {@code haveno.cli.table.Table} from a List of + * {@code haveno.proto.grpc.AddressBalanceInfo} objects. + */ +class AddressBalanceTableBuilder extends AbstractTableBuilder { + + // Default columns not dynamically generated with address info. + private final Column colAddress; + private final Column colAvailableBalance; + private final Column colConfirmations; + private final Column colIsUsed; + + AddressBalanceTableBuilder(List protos) { + super(ADDRESS_BALANCE_TBL, protos); + colAddress = new StringColumn(format(COL_HEADER_ADDRESS, "BTC")); + this.colAvailableBalance = new SatoshiColumn(COL_HEADER_AVAILABLE_BALANCE); + this.colConfirmations = new LongColumn(COL_HEADER_CONFIRMATIONS); + this.colIsUsed = new BooleanColumn(COL_HEADER_IS_USED_ADDRESS); + } + + public Table build() { + List addresses = protos.stream() + .map(a -> (AddressBalanceInfo) a) + .collect(Collectors.toList()); + + // Populate columns with address info. + //noinspection SimplifyStreamApiCallChains + addresses.stream().forEachOrdered(a -> { + colAddress.addRow(a.getAddress()); + colAvailableBalance.addRow(a.getBalance()); + colConfirmations.addRow(a.getNumConfirmations()); + colIsUsed.addRow(!a.getIsAddressUnused()); + }); + + // Define and return the table instance with populated columns. + return new Table(colAddress, + colAvailableBalance.asStringColumn(), + colConfirmations.asStringColumn(), + colIsUsed.asStringColumn()); + } +} diff --git a/bot/src/main/java/haveno/cli/table/builder/BtcBalanceTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/BtcBalanceTableBuilder.java new file mode 100644 index 00000000..41f3a18f --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/BtcBalanceTableBuilder.java @@ -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 . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.Table; +import haveno.cli.table.column.Column; +import haveno.cli.table.column.SatoshiColumn; +import haveno.proto.grpc.BtcBalanceInfo; + +import java.util.List; + +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_AVAILABLE_BALANCE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_LOCKED_BALANCE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_RESERVED_BALANCE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TOTAL_AVAILABLE_BALANCE; +import static haveno.cli.table.builder.TableType.BTC_BALANCE_TBL; + +/** + * Builds a {@code haveno.cli.table.Table} from a + * {@code haveno.proto.grpc.BtcBalanceInfo} object. + */ +class BtcBalanceTableBuilder extends AbstractTableBuilder { + + // Default columns not dynamically generated with btc balance info. + private final Column colAvailableBalance; + private final Column colReservedBalance; + private final Column colTotalAvailableBalance; + private final Column colLockedBalance; + + BtcBalanceTableBuilder(List protos) { + super(BTC_BALANCE_TBL, protos); + this.colAvailableBalance = new SatoshiColumn(COL_HEADER_AVAILABLE_BALANCE); + this.colReservedBalance = new SatoshiColumn(COL_HEADER_RESERVED_BALANCE); + this.colTotalAvailableBalance = new SatoshiColumn(COL_HEADER_TOTAL_AVAILABLE_BALANCE); + this.colLockedBalance = new SatoshiColumn(COL_HEADER_LOCKED_BALANCE); + } + + @Override + public Table build() { + BtcBalanceInfo balance = (BtcBalanceInfo) protos.get(0); + + // Populate columns with btc balance info. + + colAvailableBalance.addRow(balance.getAvailableBalance()); + colReservedBalance.addRow(balance.getReservedBalance()); + colTotalAvailableBalance.addRow(balance.getTotalAvailableBalance()); + colLockedBalance.addRow(balance.getLockedBalance()); + + // Define and return the table instance with populated columns. + + return new Table(colAvailableBalance.asStringColumn(), + colReservedBalance.asStringColumn(), + colTotalAvailableBalance.asStringColumn(), + colLockedBalance.asStringColumn()); + } +} diff --git a/bot/src/main/java/haveno/cli/table/builder/ClosedTradeTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/ClosedTradeTableBuilder.java new file mode 100644 index 00000000..c57baf63 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/ClosedTradeTableBuilder.java @@ -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 . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.Table; + +import java.util.List; + +import static haveno.cli.table.builder.TableType.CLOSED_TRADES_TBL; + +@SuppressWarnings("ConstantConditions") +class ClosedTradeTableBuilder extends AbstractTradeListBuilder { + + ClosedTradeTableBuilder(List protos) { + super(CLOSED_TRADES_TBL, protos); + } + + @Override + public Table build() { + populateColumns(); + return new Table(colTradeId, + colCreateDate.asStringColumn(), + colMarket, + colPrice.justify(), + colPriceDeviation.justify(), + colAmount.asStringColumn(), + colMixedAmount.justify(), + colCurrency, + colMixedTradeFee.asStringColumn(), + colBuyerDeposit.asStringColumn(), + colSellerDeposit.asStringColumn(), + colOfferType, + colClosingStatus); + } + + private void populateColumns() { + trades.forEach(t -> { + colTradeId.addRow(t.getTradeId()); + colCreateDate.addRow(t.getDate()); + colMarket.addRow(toMarket.apply(t)); + colPrice.addRow(t.getPrice()); + colPriceDeviation.addRow(toPriceDeviation.apply(t)); + colAmount.addRow(t.getAmount()); + colMixedAmount.addRow(t.getTradeVolume()); + colCurrency.addRow(toPaymentCurrencyCode.apply(t)); + + colMixedTradeFee.addRow(toTradeFeeBtc.apply(t), false); + + colBuyerDeposit.addRow(t.getOffer().getBuyerSecurityDepositPct()); + colSellerDeposit.addRow(t.getOffer().getSellerSecurityDepositPct()); + colOfferType.addRow(toOfferType.apply(t)); + }); + } +} diff --git a/bot/src/main/java/haveno/cli/table/builder/FailedTradeTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/FailedTradeTableBuilder.java new file mode 100644 index 00000000..9f30cfe8 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/FailedTradeTableBuilder.java @@ -0,0 +1,64 @@ +/* + * 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 . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.Table; + +import java.util.List; + +import static haveno.cli.table.builder.TableType.FAILED_TRADES_TBL; + +/** + * Builds a {@code haveno.cli.table.Table} from a list of {@code haveno.proto.grpc.TradeInfo} objects. + */ +@SuppressWarnings("ConstantConditions") +class FailedTradeTableBuilder extends AbstractTradeListBuilder { + + FailedTradeTableBuilder(List protos) { + super(FAILED_TRADES_TBL, protos); + } + + public Table build() { + populateColumns(); + return new Table(colTradeId, + colCreateDate.asStringColumn(), + colMarket, + colPrice.justify(), + colAmount.asStringColumn(), + colMixedAmount.justify(), + colCurrency, + colOfferType, + colRole, + colClosingStatus); + } + + private void populateColumns() { + trades.forEach(t -> { + colTradeId.addRow(t.getTradeId()); + colCreateDate.addRow(t.getDate()); + colMarket.addRow(toMarket.apply(t)); + colPrice.addRow(t.getPrice()); + colAmount.addRow(t.getAmount()); + colMixedAmount.addRow(t.getTradeVolume()); + colCurrency.addRow(toPaymentCurrencyCode.apply(t)); + colOfferType.addRow(toOfferType.apply(t)); + colRole.addRow(t.getRole()); + colClosingStatus.addRow("Failed"); + }); + } +} diff --git a/bot/src/main/java/haveno/cli/table/builder/OfferTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/OfferTableBuilder.java new file mode 100644 index 00000000..23b93ca6 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/OfferTableBuilder.java @@ -0,0 +1,272 @@ +/* + * 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 . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.Table; +import haveno.cli.table.column.Column; +import haveno.cli.table.column.Iso8601DateTimeColumn; +import haveno.cli.table.column.SatoshiColumn; +import haveno.cli.table.column.StringColumn; +import haveno.cli.table.column.ZippedStringColumns; +import haveno.proto.grpc.OfferInfo; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_AMOUNT_RANGE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_CREATION_DATE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DETAILED_PRICE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DETAILED_PRICE_OF_CRYPTO; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DIRECTION; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_ENABLED; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_PAYMENT_METHOD; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRIGGER_PRICE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_UUID; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_VOLUME_RANGE; +import static haveno.cli.table.builder.TableType.OFFER_TBL; +import static haveno.cli.table.column.Column.JUSTIFICATION.LEFT; +import static haveno.cli.table.column.Column.JUSTIFICATION.NONE; +import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; +import static haveno.cli.table.column.ZippedStringColumns.DUPLICATION_MODE.EXCLUDE_DUPLICATES; +import static java.lang.String.format; +import static protobuf.OfferDirection.BUY; +import static protobuf.OfferDirection.SELL; + +/** + * Builds a {@code haveno.cli.table.Table} from a List of + * {@code haveno.proto.grpc.OfferInfo} objects. + */ +class OfferTableBuilder extends AbstractTableBuilder { + + // Columns common to both traditional and cryptocurrency offers. + private final Column colOfferId = new StringColumn(COL_HEADER_UUID, LEFT); + private final Column colDirection = new StringColumn(COL_HEADER_DIRECTION, LEFT); + private final Column colAmount = new SatoshiColumn("Temp Amount", NONE); + private final Column colMinAmount = new SatoshiColumn("Temp Min Amount", NONE); + private final Column colPaymentMethod = new StringColumn(COL_HEADER_PAYMENT_METHOD, LEFT); + private final Column colCreateDate = new Iso8601DateTimeColumn(COL_HEADER_CREATION_DATE); + + OfferTableBuilder(List protos) { + super(OFFER_TBL, protos); + } + + @Override + public Table build() { + List offers = protos.stream().map(p -> (OfferInfo) p).collect(Collectors.toList()); + return isShowingTraditionalOffers.get() + ? buildTraditionalOfferTable(offers) + : buildCryptoCurrencyOfferTable(offers); + } + + @SuppressWarnings("ConstantConditions") + public Table buildTraditionalOfferTable(List offers) { + @Nullable + Column colEnabled = enabledColumn.get(); // Not boolean: "YES", "NO", or "PENDING" + Column colTraditionalPrice = new StringColumn(format(COL_HEADER_DETAILED_PRICE, traditionalTradeCurrency.get()), RIGHT); + Column colVolume = new StringColumn(format("Temp Volume (%s)", traditionalTradeCurrency.get()), NONE); + Column colMinVolume = new StringColumn(format("Temp Min Volume (%s)", traditionalTradeCurrency.get()), NONE); + @Nullable + Column colTriggerPrice = traditionalTriggerPriceColumn.get(); + + // Populate columns with offer info. + + offers.forEach(o -> { + if (colEnabled != null) + colEnabled.addRow(toEnabled.apply(o)); + + colDirection.addRow(o.getDirection()); + colTraditionalPrice.addRow(o.getPrice()); + colMinAmount.addRow(o.getMinAmount()); + colAmount.addRow(o.getAmount()); + colVolume.addRow(o.getVolume()); + colMinVolume.addRow(o.getMinVolume()); + + if (colTriggerPrice != null) + colTriggerPrice.addRow(toBlankOrNonZeroValue.apply(o.getTriggerPrice())); + + colPaymentMethod.addRow(o.getPaymentMethodShortName()); + colCreateDate.addRow(o.getDate()); + colOfferId.addRow(o.getId()); + }); + + ZippedStringColumns amountRange = zippedAmountRangeColumns.get(); + ZippedStringColumns volumeRange = + new ZippedStringColumns(format(COL_HEADER_VOLUME_RANGE, traditionalTradeCurrency.get()), + RIGHT, + " - ", + colMinVolume.asStringColumn(), + colVolume.asStringColumn()); + + // Define and return the table instance with populated columns. + + if (isShowingMyOffers.get()) { + return new Table(colEnabled.asStringColumn(), + colDirection, + colTraditionalPrice.justify(), + amountRange.asStringColumn(EXCLUDE_DUPLICATES), + volumeRange.asStringColumn(EXCLUDE_DUPLICATES), + colTriggerPrice.justify(), + colPaymentMethod, + colCreateDate.asStringColumn(), + colOfferId); + } else { + return new Table(colDirection, + colTraditionalPrice.justify(), + amountRange.asStringColumn(EXCLUDE_DUPLICATES), + volumeRange.asStringColumn(EXCLUDE_DUPLICATES), + colPaymentMethod, + colCreateDate.asStringColumn(), + colOfferId); + } + } + + @SuppressWarnings("ConstantConditions") + public Table buildCryptoCurrencyOfferTable(List offers) { + @Nullable + Column colEnabled = enabledColumn.get(); // Not boolean: YES, NO, or PENDING + Column colBtcPrice = new StringColumn(format(COL_HEADER_DETAILED_PRICE_OF_CRYPTO, cryptoTradeCurrency.get()), RIGHT); + Column colVolume = new StringColumn(format("Temp Volume (%s)", cryptoTradeCurrency.get()), NONE); + Column colMinVolume = new StringColumn(format("Temp Min Volume (%s)", cryptoTradeCurrency.get()), NONE); + @Nullable + Column colTriggerPrice = cryptoTriggerPriceColumn.get(); + + // Populate columns with offer info. + + offers.forEach(o -> { + if (colEnabled != null) + colEnabled.addRow(toEnabled.apply(o)); + + colDirection.addRow(directionFormat.apply(o)); + colBtcPrice.addRow(o.getPrice()); + colAmount.addRow(o.getAmount()); + colMinAmount.addRow(o.getMinAmount()); + colVolume.addRow(o.getVolume()); + colMinVolume.addRow(o.getMinVolume()); + + if (colTriggerPrice != null) + colTriggerPrice.addRow(toBlankOrNonZeroValue.apply(o.getTriggerPrice())); + + colPaymentMethod.addRow(o.getPaymentMethodShortName()); + colCreateDate.addRow(o.getDate()); + colOfferId.addRow(o.getId()); + }); + + ZippedStringColumns amountRange = zippedAmountRangeColumns.get(); + ZippedStringColumns volumeRange = + new ZippedStringColumns(format(COL_HEADER_VOLUME_RANGE, cryptoTradeCurrency.get()), + RIGHT, + " - ", + colMinVolume.asStringColumn(), + colVolume.asStringColumn()); + + // Define and return the table instance with populated columns. + + if (isShowingMyOffers.get()) { + if (isShowingBsqOffers.get()) { + return new Table(colEnabled.asStringColumn(), + colDirection, + colBtcPrice.justify(), + amountRange.asStringColumn(EXCLUDE_DUPLICATES), + volumeRange.asStringColumn(EXCLUDE_DUPLICATES), + colPaymentMethod, + colCreateDate.asStringColumn(), + colOfferId); + } else { + return new Table(colEnabled.asStringColumn(), + colDirection, + colBtcPrice.justify(), + amountRange.asStringColumn(EXCLUDE_DUPLICATES), + volumeRange.asStringColumn(EXCLUDE_DUPLICATES), + colTriggerPrice.justify(), + colPaymentMethod, + colCreateDate.asStringColumn(), + colOfferId); + } + } else { + return new Table(colDirection, + colBtcPrice.justify(), + amountRange.asStringColumn(EXCLUDE_DUPLICATES), + volumeRange.asStringColumn(EXCLUDE_DUPLICATES), + colPaymentMethod, + colCreateDate.asStringColumn(), + colOfferId); + } + } + + private final Function toBlankOrNonZeroValue = (s) -> s.trim().equals("0") ? "" : s; + private final Supplier firstOfferInList = () -> (OfferInfo) protos.get(0); + private final Supplier isShowingMyOffers = () -> firstOfferInList.get().getIsMyOffer(); + private final Supplier isShowingTraditionalOffers = () -> isTraditionalOffer.test(firstOfferInList.get()); + private final Supplier traditionalTradeCurrency = () -> firstOfferInList.get().getCounterCurrencyCode(); + private final Supplier cryptoTradeCurrency = () -> firstOfferInList.get().getBaseCurrencyCode(); + private final Supplier isShowingBsqOffers = () -> + !isTraditionalOffer.test(firstOfferInList.get()) && cryptoTradeCurrency.get().equals("BSQ"); + + @Nullable // Not a boolean column: YES, NO, or PENDING. + private final Supplier enabledColumn = () -> + isShowingMyOffers.get() + ? new StringColumn(COL_HEADER_ENABLED, LEFT) + : null; + @Nullable + private final Supplier traditionalTriggerPriceColumn = () -> + isShowingMyOffers.get() + ? new StringColumn(format(COL_HEADER_TRIGGER_PRICE, traditionalTradeCurrency.get()), RIGHT) + : null; + @Nullable + private final Supplier cryptoTriggerPriceColumn = () -> + isShowingMyOffers.get() && !isShowingBsqOffers.get() + ? new StringColumn(format(COL_HEADER_TRIGGER_PRICE, cryptoTradeCurrency.get()), RIGHT) + : null; + + private final Function toEnabled = (o) -> { + return o.getIsActivated() ? "YES" : "NO"; + }; + + private final Function toMirroredDirection = (d) -> + d.equalsIgnoreCase(BUY.name()) ? SELL.name() : BUY.name(); + + private final Function directionFormat = (o) -> { + if (isTraditionalOffer.test(o)) { + return o.getBaseCurrencyCode(); + } else { + // Return "Sell BSQ (Buy BTC)", or "Buy BSQ (Sell BTC)". + String direction = o.getDirection(); + String mirroredDirection = toMirroredDirection.apply(direction); + Function mixedCase = (word) -> word.charAt(0) + word.substring(1).toLowerCase(); + return format("%s %s (%s %s)", + mixedCase.apply(mirroredDirection), + o.getBaseCurrencyCode(), + mixedCase.apply(direction), + o.getCounterCurrencyCode()); + } + }; + + private final Supplier zippedAmountRangeColumns = () -> { + if (colMinAmount.isEmpty() || colAmount.isEmpty()) + throw new IllegalStateException("amount columns must have data"); + + return new ZippedStringColumns(COL_HEADER_AMOUNT_RANGE, + RIGHT, + " - ", + colMinAmount.asStringColumn(), + colAmount.asStringColumn()); + }; +} diff --git a/bot/src/main/java/haveno/cli/table/builder/OpenTradeTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/OpenTradeTableBuilder.java new file mode 100644 index 00000000..dab9e09a --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/OpenTradeTableBuilder.java @@ -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 . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.Table; + +import java.util.List; + +import static haveno.cli.table.builder.TableType.OPEN_TRADES_TBL; + +/** + * Builds a {@code haveno.cli.table.Table} from a list of {@code haveno.proto.grpc.TradeInfo} objects. + */ +@SuppressWarnings("ConstantConditions") +class OpenTradeTableBuilder extends AbstractTradeListBuilder { + + OpenTradeTableBuilder(List protos) { + super(OPEN_TRADES_TBL, protos); + } + + public Table build() { + populateColumns(); + return new Table(colTradeId, + colCreateDate.asStringColumn(), + colMarket, + colPrice.justify(), + colAmount.asStringColumn(), + colMixedAmount.justify(), + colCurrency, + colPaymentMethod, + colRole); + } + + private void populateColumns() { + trades.forEach(t -> { + colTradeId.addRow(t.getTradeId()); + colCreateDate.addRow(t.getDate()); + colMarket.addRow(toMarket.apply(t)); + colPrice.addRow(t.getPrice()); + colAmount.addRow(t.getAmount()); + colMixedAmount.addRow(t.getTradeVolume()); + colCurrency.addRow(toPaymentCurrencyCode.apply(t)); + colPaymentMethod.addRow(t.getOffer().getPaymentMethodShortName()); + colRole.addRow(t.getRole()); + }); + } +} diff --git a/bot/src/main/java/haveno/cli/table/builder/PaymentAccountTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/PaymentAccountTableBuilder.java new file mode 100644 index 00000000..3cfbfcaa --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/PaymentAccountTableBuilder.java @@ -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 . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.Table; +import haveno.cli.table.column.Column; +import haveno.cli.table.column.StringColumn; +import protobuf.PaymentAccount; + +import java.util.List; +import java.util.stream.Collectors; + +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_CURRENCY; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_NAME; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_PAYMENT_METHOD; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_UUID; +import static haveno.cli.table.builder.TableType.PAYMENT_ACCOUNT_TBL; + +/** + * Builds a {@code haveno.cli.table.Table} from a List of + * {@code protobuf.PaymentAccount} objects. + */ +class PaymentAccountTableBuilder extends AbstractTableBuilder { + + // Default columns not dynamically generated with payment account info. + private final Column colName; + private final Column colCurrency; + private final Column colPaymentMethod; + private final Column colId; + + PaymentAccountTableBuilder(List protos) { + super(PAYMENT_ACCOUNT_TBL, protos); + this.colName = new StringColumn(COL_HEADER_NAME); + this.colCurrency = new StringColumn(COL_HEADER_CURRENCY); + this.colPaymentMethod = new StringColumn(COL_HEADER_PAYMENT_METHOD); + this.colId = new StringColumn(COL_HEADER_UUID); + } + + public Table build() { + List paymentAccounts = protos.stream() + .map(a -> (PaymentAccount) a) + .collect(Collectors.toList()); + + // Populate columns with payment account info. + //noinspection SimplifyStreamApiCallChains + paymentAccounts.stream().forEachOrdered(a -> { + colName.addRow(a.getAccountName()); + colCurrency.addRow(a.getSelectedTradeCurrency().getCode()); + colPaymentMethod.addRow(a.getPaymentMethod().getId()); + colId.addRow(a.getId()); + }); + + // Define and return the table instance with populated columns. + return new Table(colName, colCurrency, colPaymentMethod, colId); + } +} diff --git a/bot/src/main/java/haveno/cli/table/builder/TableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/TableBuilder.java new file mode 100644 index 00000000..cff4dde6 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/TableBuilder.java @@ -0,0 +1,67 @@ +/* + * 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 . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.Table; + +import java.util.List; + +import static java.util.Collections.singletonList; + +/** + * Table builder factory. It is not conventionally named TableBuilderFactory because + * it has no static factory methods. The number of static fields and methods in the + * {@code haveno.cli.table} are kept to a minimum in an effort o reduce class load time + * in the session-less CLI. + */ +public class TableBuilder extends AbstractTableBuilder { + + public TableBuilder(TableType tableType, Object proto) { + this(tableType, singletonList(proto)); + } + + public TableBuilder(TableType tableType, List protos) { + super(tableType, protos); + } + + @Override + public Table build() { + switch (tableType) { + case ADDRESS_BALANCE_TBL: + return new AddressBalanceTableBuilder(protos).build(); + case BTC_BALANCE_TBL: + return new BtcBalanceTableBuilder(protos).build(); + case CLOSED_TRADES_TBL: + return new ClosedTradeTableBuilder(protos).build(); + case FAILED_TRADES_TBL: + return new FailedTradeTableBuilder(protos).build(); + case OFFER_TBL: + return new OfferTableBuilder(protos).build(); + case OPEN_TRADES_TBL: + return new OpenTradeTableBuilder(protos).build(); + case PAYMENT_ACCOUNT_TBL: + return new PaymentAccountTableBuilder(protos).build(); + case TRADE_DETAIL_TBL: + return new TradeDetailTableBuilder(protos).build(); + case TRANSACTION_TBL: + return new TransactionTableBuilder(protos).build(); + default: + throw new IllegalArgumentException("invalid cli table type " + tableType.name()); + } + } +} diff --git a/bot/src/main/java/haveno/cli/table/builder/TableBuilderConstants.java b/bot/src/main/java/haveno/cli/table/builder/TableBuilderConstants.java new file mode 100644 index 00000000..893f9f8b --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/TableBuilderConstants.java @@ -0,0 +1,82 @@ +/* + * 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 . + */ + +package haveno.cli.table.builder; + +/** + * Table column name constants. + */ +class TableBuilderConstants { + static final String COL_HEADER_ADDRESS = "%-3s Address"; + static final String COL_HEADER_AMOUNT = "Amount"; + static final String COL_HEADER_AMOUNT_IN_BTC = "Amount in BTC"; + static final String COL_HEADER_AMOUNT_RANGE = "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_BSQ_SWAP_TRADE_ROLE = "My BSQ Swap Role"; + static final String COL_HEADER_BUYER_DEPOSIT = "Buyer Deposit (BTC)"; + static final String COL_HEADER_SELLER_DEPOSIT = "Seller Deposit (BTC)"; + static final String COL_HEADER_CONFIRMATIONS = "Confirmations"; + static final String COL_HEADER_DEVIATION = "Deviation"; + static final String COL_HEADER_IS_USED_ADDRESS = "Is Used"; + static final String COL_HEADER_CREATION_DATE = "Creation Date (UTC)"; + static final String COL_HEADER_CURRENCY = "Currency"; + static final String COL_HEADER_DATE_TIME = "Date/Time (UTC)"; + static final String COL_HEADER_DETAILED_AMOUNT = "Amount(%-3s)"; + static final String COL_HEADER_DETAILED_PRICE = "Price in %-3s for 1 BTC"; + static final String COL_HEADER_DETAILED_PRICE_OF_CRYPTO = "Price in BTC for 1 %-3s"; + static final String COL_HEADER_DIRECTION = "Buy/Sell"; + static final String COL_HEADER_ENABLED = "Enabled"; + static final String COL_HEADER_MARKET = "Market"; + static final String COL_HEADER_NAME = "Name"; + static final String COL_HEADER_OFFER_TYPE = "Offer Type"; + static final String COL_HEADER_PAYMENT_METHOD = "Payment Method"; + static final String COL_HEADER_PRICE = "Price"; + static final String COL_HEADER_STATUS = "Status"; + static final String COL_HEADER_TRADE_CRYPTO_BUYER_ADDRESS = "%-3s Buyer Address"; + static final String COL_HEADER_TRADE_BUYER_COST = "Buyer Cost(%-3s)"; + 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 = "%-3s Sent"; + static final String COL_HEADER_TRADE_PAYMENT_RECEIVED = "%-3s Received"; + static final String COL_HEADER_TRADE_PAYOUT_PUBLISHED = "Payout Published"; + static final String COL_HEADER_TRADE_WITHDRAWN = "Withdrawn"; + static final String COL_HEADER_TRADE_ID = "Trade ID"; + static final String COL_HEADER_TRADE_ROLE = "My Role"; + static final String COL_HEADER_TRADE_SHORT_ID = "ID"; + static final String COL_HEADER_TRADE_MAKER_FEE = "Maker Fee(%-3s)"; + static final String COL_HEADER_TRADE_TAKER_FEE = "Taker Fee(%-3s)"; + static final String COL_HEADER_TRADE_FEE = "Trade Fee"; + static final String COL_HEADER_TRIGGER_PRICE = "Trigger Price(%-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_RANGE = "%-3s(min - max)"; + static final String COL_HEADER_UUID = "ID"; +} diff --git a/bot/src/main/java/haveno/cli/table/builder/TableType.java b/bot/src/main/java/haveno/cli/table/builder/TableType.java new file mode 100644 index 00000000..871240ab --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/TableType.java @@ -0,0 +1,34 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package haveno.cli.table.builder; + +/** + * Used as param in TableBuilder constructor instead of inspecting + * protos to find out what kind of CLI output table should be built. + */ +public enum TableType { + ADDRESS_BALANCE_TBL, + BTC_BALANCE_TBL, + CLOSED_TRADES_TBL, + FAILED_TRADES_TBL, + OFFER_TBL, + OPEN_TRADES_TBL, + PAYMENT_ACCOUNT_TBL, + TRADE_DETAIL_TBL, + TRANSACTION_TBL +} diff --git a/bot/src/main/java/haveno/cli/table/builder/TradeDetailTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/TradeDetailTableBuilder.java new file mode 100644 index 00000000..bf8a0c2b --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/TradeDetailTableBuilder.java @@ -0,0 +1,98 @@ +/* + * 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 . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.Table; +import haveno.cli.table.column.Column; +import haveno.proto.grpc.TradeInfo; + +import java.util.ArrayList; +import java.util.List; + +import static haveno.cli.table.builder.TableType.TRADE_DETAIL_TBL; + +/** + * Builds a {@code haveno.cli.table.Table} from a {@code haveno.proto.grpc.TradeInfo} object. + */ +@SuppressWarnings("ConstantConditions") +class TradeDetailTableBuilder extends AbstractTradeListBuilder { + + TradeDetailTableBuilder(List protos) { + super(TRADE_DETAIL_TBL, protos); + } + + /** + * Build a single row trade detail table. + * @return Table containing one row + */ + @Override + public Table build() { + // A trade detail table only has one row. + var trade = trades.get(0); + populateColumns(trade); + List> columns = defineColumnList(trade); + return new Table(columns.toArray(new Column[0])); + } + + private void populateColumns(TradeInfo trade) { + populateHavenoV1TradeColumns(trade); + } + + private void populateHavenoV1TradeColumns(TradeInfo trade) { + colTradeId.addRow(trade.getShortId()); + colRole.addRow(trade.getRole()); + colPrice.addRow(trade.getPrice()); + colAmount.addRow(toTradeAmount.apply(trade)); + colHavenoTradeFee.addRow(toMyMakerOrTakerFee.apply(trade)); + colIsDepositPublished.addRow(trade.getIsDepositsPublished()); + colIsDepositConfirmed.addRow(trade.getIsDepositsUnlocked()); + colTradeCost.addRow(toTradeVolumeAsString.apply(trade)); + colIsPaymentSentMessageSent.addRow(trade.getIsPaymentSent()); + colIsPaymentReceivedMessageSent.addRow(trade.getIsPaymentReceived()); + colIsPayoutPublished.addRow(trade.getIsPayoutPublished()); + colIsCompleted.addRow(trade.getIsCompleted()); + if (colCryptoReceiveAddressColumn != null) + colCryptoReceiveAddressColumn.addRow(toCryptoReceiveAddress.apply(trade)); + } + + private List> defineColumnList(TradeInfo trade) { + return getHavenoV1TradeColumnList(); + } + + private List> getHavenoV1TradeColumnList() { + List> columns = new ArrayList<>() {{ + add(colTradeId); + add(colRole); + add(colPrice.justify()); + add(colAmount.asStringColumn()); + add(colHavenoTradeFee.asStringColumn()); + add(colIsDepositPublished.asStringColumn()); + add(colIsDepositConfirmed.asStringColumn()); + add(colTradeCost.justify()); + add(colIsPaymentSentMessageSent.asStringColumn()); + add(colIsPaymentReceivedMessageSent.asStringColumn()); + add(colIsPayoutPublished.asStringColumn()); + add(colIsCompleted.asStringColumn()); + }}; + + if (colCryptoReceiveAddressColumn != null) + columns.add(colCryptoReceiveAddressColumn); + + return columns; + } +} diff --git a/bot/src/main/java/haveno/cli/table/builder/TradeTableColumnSupplier.java b/bot/src/main/java/haveno/cli/table/builder/TradeTableColumnSupplier.java new file mode 100644 index 00000000..d1974344 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/TradeTableColumnSupplier.java @@ -0,0 +1,290 @@ +/* + * 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 . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.column.CryptoVolumeColumn; +import haveno.cli.table.column.DoubleColumn; +import haveno.cli.table.column.BooleanColumn; +import haveno.cli.table.column.BtcColumn; +import haveno.cli.table.column.Column; +import haveno.cli.table.column.Iso8601DateTimeColumn; +import haveno.cli.table.column.MixedTradeFeeColumn; +import haveno.cli.table.column.SatoshiColumn; +import haveno.cli.table.column.StringColumn; +import haveno.proto.grpc.ContractInfo; +import haveno.proto.grpc.OfferInfo; +import haveno.proto.grpc.TradeInfo; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_AMOUNT; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_AMOUNT_IN_BTC; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_CURRENCY; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DATE_TIME; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DETAILED_AMOUNT; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DETAILED_PRICE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DETAILED_PRICE_OF_CRYPTO; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DEVIATION; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_MARKET; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_OFFER_TYPE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_PAYMENT_METHOD; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_PRICE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_STATUS; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_CRYPTO_BUYER_ADDRESS; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_BUYER_COST; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_DEPOSIT_CONFIRMED; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_DEPOSIT_PUBLISHED; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_FEE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_ID; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_MAKER_FEE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_PAYMENT_RECEIVED; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_PAYMENT_SENT; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_PAYOUT_PUBLISHED; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_ROLE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_SHORT_ID; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_TAKER_FEE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_WITHDRAWN; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_FEE; +import static haveno.cli.table.builder.TableType.CLOSED_TRADES_TBL; +import static haveno.cli.table.builder.TableType.FAILED_TRADES_TBL; +import static haveno.cli.table.builder.TableType.OPEN_TRADES_TBL; +import static haveno.cli.table.builder.TableType.TRADE_DETAIL_TBL; +import static haveno.cli.table.column.CryptoVolumeColumn.DISPLAY_MODE.CRYPTO_VOLUME; +import static haveno.cli.table.column.CryptoVolumeColumn.DISPLAY_MODE.BSQ_VOLUME; +import static haveno.cli.table.column.Column.JUSTIFICATION.LEFT; +import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; +import static java.lang.String.format; + +/** + * Convenience for supplying column definitions to + * open/closed/failed/detail trade table builders. + */ +@Slf4j +class TradeTableColumnSupplier { + + @Getter + private final TableType tableType; + @Getter + private final List trades; + + public TradeTableColumnSupplier(TableType tableType, List trades) { + this.tableType = tableType; + this.trades = trades; + } + + private final Supplier isTradeDetailTblBuilder = () -> getTableType().equals(TRADE_DETAIL_TBL); + private final Supplier isOpenTradeTblBuilder = () -> getTableType().equals(OPEN_TRADES_TBL); + private final Supplier isClosedTradeTblBuilder = () -> getTableType().equals(CLOSED_TRADES_TBL); + private final Supplier isFailedTradeTblBuilder = () -> getTableType().equals(FAILED_TRADES_TBL); + private final Supplier firstRow = () -> getTrades().get(0); + private final Predicate isTraditionalOffer = (o) -> o.getBaseCurrencyCode().equals("XMR"); + private final Predicate isTraditionalTrade = (t) -> isTraditionalOffer.test(t.getOffer()); + private final Predicate isTaker = (t) -> t.getRole().toLowerCase().contains("taker"); + + final Supplier tradeIdColumn = () -> isTradeDetailTblBuilder.get() + ? new StringColumn(COL_HEADER_TRADE_SHORT_ID) + : new StringColumn(COL_HEADER_TRADE_ID); + + final Supplier createDateColumn = () -> isTradeDetailTblBuilder.get() + ? null + : new Iso8601DateTimeColumn(COL_HEADER_DATE_TIME); + + final Supplier marketColumn = () -> isTradeDetailTblBuilder.get() + ? null + : new StringColumn(COL_HEADER_MARKET); + + private final Function> toDetailedPriceColumn = (t) -> { + String colHeader = isTraditionalTrade.test(t) + ? format(COL_HEADER_DETAILED_PRICE, t.getOffer().getCounterCurrencyCode()) + : format(COL_HEADER_DETAILED_PRICE_OF_CRYPTO, t.getOffer().getBaseCurrencyCode()); + return new StringColumn(colHeader, RIGHT); + }; + + final Supplier> priceColumn = () -> isTradeDetailTblBuilder.get() + ? toDetailedPriceColumn.apply(firstRow.get()) + : new StringColumn(COL_HEADER_PRICE, RIGHT); + + final Supplier> priceDeviationColumn = () -> isTradeDetailTblBuilder.get() + ? null + : new StringColumn(COL_HEADER_DEVIATION, RIGHT); + + final Supplier currencyColumn = () -> isTradeDetailTblBuilder.get() + ? null + : new StringColumn(COL_HEADER_CURRENCY); + + private final Function> toDetailedAmountColumn = (t) -> { + String headerCurrencyCode = t.getOffer().getBaseCurrencyCode(); + String colHeader = format(COL_HEADER_DETAILED_AMOUNT, headerCurrencyCode); + CryptoVolumeColumn.DISPLAY_MODE displayMode = headerCurrencyCode.equals("BSQ") ? BSQ_VOLUME : CRYPTO_VOLUME; + return isTraditionalTrade.test(t) + ? new SatoshiColumn(colHeader) + : new CryptoVolumeColumn(colHeader, displayMode); + }; + + // Can be tradional or crypto amount represented as longs. Placing the decimal + // in the displayed string representation is done in the Column implementation. + final Supplier> amountColumn = () -> isTradeDetailTblBuilder.get() + ? toDetailedAmountColumn.apply(firstRow.get()) + : new BtcColumn(COL_HEADER_AMOUNT_IN_BTC); + + final Supplier mixedAmountColumn = () -> isTradeDetailTblBuilder.get() + ? null + : new StringColumn(COL_HEADER_AMOUNT, RIGHT); + + final Supplier> minerTxFeeColumn = () -> isTradeDetailTblBuilder.get() || isClosedTradeTblBuilder.get() + ? new SatoshiColumn(COL_HEADER_TX_FEE) + : null; + + final Supplier mixedTradeFeeColumn = () -> isTradeDetailTblBuilder.get() + ? null + : new MixedTradeFeeColumn(COL_HEADER_TRADE_FEE); + + final Supplier paymentMethodColumn = () -> isTradeDetailTblBuilder.get() || isClosedTradeTblBuilder.get() + ? null + : new StringColumn(COL_HEADER_PAYMENT_METHOD, LEFT); + + final Supplier roleColumn = () -> { + return isTradeDetailTblBuilder.get() || isOpenTradeTblBuilder.get() || isFailedTradeTblBuilder.get() + ? new StringColumn(COL_HEADER_TRADE_ROLE) + : null; + }; + + final Function> toSecurityDepositColumn = (name) -> isClosedTradeTblBuilder.get() + ? new DoubleColumn(name) + : null; + + final Supplier offerTypeColumn = () -> isTradeDetailTblBuilder.get() + ? null + : new StringColumn(COL_HEADER_OFFER_TYPE); + + final Supplier statusDescriptionColumn = () -> isTradeDetailTblBuilder.get() + ? null + : new StringColumn(COL_HEADER_STATUS); + + private final Function> toBooleanColumn = BooleanColumn::new; + + final Supplier> depositPublishedColumn = () -> { + return isTradeDetailTblBuilder.get() + ? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_PUBLISHED) + : null; + }; + + final Supplier> depositConfirmedColumn = () -> { + return isTradeDetailTblBuilder.get() + ? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_CONFIRMED) + : null; + + }; + + final Supplier> payoutPublishedColumn = () -> { + return isTradeDetailTblBuilder.get() + ? toBooleanColumn.apply(COL_HEADER_TRADE_PAYOUT_PUBLISHED) + : null; + }; + + final Supplier> fundsWithdrawnColumn = () -> { + return isTradeDetailTblBuilder.get() + ? toBooleanColumn.apply(COL_HEADER_TRADE_WITHDRAWN) + : null; + }; + + final Supplier> havenoTradeDetailFeeColumn = () -> { + if (isTradeDetailTblBuilder.get()) { + TradeInfo t = firstRow.get(); + String headerCurrencyCode = "XMR"; + String colHeader = isTaker.test(t) + ? format(COL_HEADER_TRADE_TAKER_FEE, headerCurrencyCode) + : format(COL_HEADER_TRADE_MAKER_FEE, headerCurrencyCode); + return new SatoshiColumn(colHeader, false); + } else { + return null; + } + }; + + final Function toPaymentCurrencyCode = (t) -> + isTraditionalTrade.test(t) + ? t.getOffer().getCounterCurrencyCode() + : t.getOffer().getBaseCurrencyCode(); + + final Supplier> paymentSentMessageSentColumn = () -> { + if (isTradeDetailTblBuilder.get()) { + String headerCurrencyCode = toPaymentCurrencyCode.apply(firstRow.get()); + String colHeader = format(COL_HEADER_TRADE_PAYMENT_SENT, headerCurrencyCode); + return new BooleanColumn(colHeader); + } else { + return null; + } + }; + + final Supplier> paymentReceivedMessageSentColumn = () -> { + if (isTradeDetailTblBuilder.get()) { + String headerCurrencyCode = toPaymentCurrencyCode.apply(firstRow.get()); + String colHeader = format(COL_HEADER_TRADE_PAYMENT_RECEIVED, headerCurrencyCode); + return new BooleanColumn(colHeader); + } else { + return null; + } + }; + + final Supplier> tradeCostColumn = () -> { + if (isTradeDetailTblBuilder.get()) { + TradeInfo t = firstRow.get(); + String headerCurrencyCode = t.getOffer().getCounterCurrencyCode(); + String colHeader = format(COL_HEADER_TRADE_BUYER_COST, headerCurrencyCode); + return new StringColumn(colHeader, RIGHT); + } else { + return null; + } + }; + + final Predicate showCryptoBuyerAddress = (t) -> { + if (isTraditionalTrade.test(t)) { + return false; + } else { + ContractInfo contract = t.getContract(); + boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker(); + if (isTaker.test(t)) { + return !isBuyerMakerAndSellerTaker; + } else { + return isBuyerMakerAndSellerTaker; + } + } + }; + + @Nullable + final Supplier> cryptoReceiveAddressColumn = () -> { + if (isTradeDetailTblBuilder.get()) { + TradeInfo t = firstRow.get(); + if (showCryptoBuyerAddress.test(t)) { + String headerCurrencyCode = toPaymentCurrencyCode.apply(t); + String colHeader = format(COL_HEADER_TRADE_CRYPTO_BUYER_ADDRESS, headerCurrencyCode); + return new StringColumn(colHeader); + } else { + return null; + } + } else { + return null; + } + }; +} diff --git a/bot/src/main/java/haveno/cli/table/builder/TransactionTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/TransactionTableBuilder.java new file mode 100644 index 00000000..a07b135c --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/builder/TransactionTableBuilder.java @@ -0,0 +1,100 @@ +/* + * 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 . + */ + +package haveno.cli.table.builder; + +import haveno.cli.table.Table; +import haveno.cli.table.column.BooleanColumn; +import haveno.cli.table.column.Column; +import haveno.cli.table.column.LongColumn; +import haveno.cli.table.column.SatoshiColumn; +import haveno.cli.table.column.StringColumn; + +import javax.annotation.Nullable; +import java.util.List; + +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_FEE; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_ID; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_INPUT_SUM; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_IS_CONFIRMED; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_OUTPUT_SUM; +import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_SIZE; +import static haveno.cli.table.builder.TableType.TRANSACTION_TBL; + +/** + * Builds a {@code haveno.cli.table.Table} from a {@code haveno.proto.grpc.TxInfo} object. + */ +class TransactionTableBuilder extends AbstractTableBuilder { + + // Default columns not dynamically generated with tx info. + private final Column colTxId; + private final Column colIsConfirmed; + private final Column colInputSum; + private final Column colOutputSum; + private final Column colTxFee; + private final Column colTxSize; + + TransactionTableBuilder(List protos) { + super(TRANSACTION_TBL, protos); + this.colTxId = new StringColumn(COL_HEADER_TX_ID); + this.colIsConfirmed = new BooleanColumn(COL_HEADER_TX_IS_CONFIRMED); + this.colInputSum = new SatoshiColumn(COL_HEADER_TX_INPUT_SUM); + this.colOutputSum = new SatoshiColumn(COL_HEADER_TX_OUTPUT_SUM); + this.colTxFee = new SatoshiColumn(COL_HEADER_TX_FEE); + this.colTxSize = new LongColumn(COL_HEADER_TX_SIZE); + } + + public Table build() { + // TODO Add 'gettransactions' api method & show multiple tx in the console. + // For now, a tx tbl is only one row. + + // Declare the columns derived from tx info. + + @Nullable + Column colMemo = null; + + // Populate columns with tx info. + + colTxId.addRow(null); + colIsConfirmed.addRow(null); + colInputSum.addRow(null); + colOutputSum.addRow(null); + colTxFee.addRow(null); + colTxSize.addRow(null); + if (colMemo != null) + colMemo.addRow(null); + + // Define and return the table instance with populated columns. + + if (colMemo != null) { + return new Table(colTxId, + colIsConfirmed.asStringColumn(), + colInputSum.asStringColumn(), + colOutputSum.asStringColumn(), + colTxFee.asStringColumn(), + colTxSize.asStringColumn(), + colMemo); + } else { + return new Table(colTxId, + colIsConfirmed.asStringColumn(), + colInputSum.asStringColumn(), + colOutputSum.asStringColumn(), + colTxFee.asStringColumn(), + colTxSize.asStringColumn()); + } + } +} diff --git a/bot/src/main/java/haveno/cli/table/column/AbstractColumn.java b/bot/src/main/java/haveno/cli/table/column/AbstractColumn.java new file mode 100644 index 00000000..b9f3b074 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/AbstractColumn.java @@ -0,0 +1,86 @@ +/* + * 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 . + */ + +package haveno.cli.table.column; + +import static com.google.common.base.Strings.padEnd; +import static com.google.common.base.Strings.padStart; +import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; + +/** + * Partial implementation of the {@link Column} interface. + */ +abstract class AbstractColumn, T> implements Column { + + // We create an encapsulated StringColumn up front to populate with formatted + // strings in each this.addRow(Long value) call. But we will not know how + // to justify the cached, formatted string until the column is fully populated. + protected final StringColumn stringColumn; + + // The name field is not final, so it can be re-set for column alignment. + protected String name; + protected final JUSTIFICATION justification; + // The max width is not known until after column is fully populated. + protected int maxWidth; + + public AbstractColumn(String name, JUSTIFICATION justification) { + this.name = name; + this.justification = justification; + this.stringColumn = this instanceof StringColumn ? null : new StringColumn(name, justification); + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public int getWidth() { + return maxWidth; + } + + @Override + public JUSTIFICATION getJustification() { + return this.justification; + } + + @Override + public Column justify() { + if (this instanceof StringColumn && this.justification.equals(RIGHT)) + return this.justify(); + else + return this; // no-op + } + + protected final String toJustifiedString(String s) { + switch (justification) { + case LEFT: + return padEnd(s, maxWidth, ' '); + case RIGHT: + return padStart(s, maxWidth, ' '); + case NONE: + default: + return s; + } + } +} + diff --git a/bot/src/main/java/haveno/cli/table/column/BooleanColumn.java b/bot/src/main/java/haveno/cli/table/column/BooleanColumn.java new file mode 100644 index 00000000..4a34e098 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/BooleanColumn.java @@ -0,0 +1,131 @@ +/* + * 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 . + */ + +package haveno.cli.table.column; + + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.IntStream; + +import static haveno.cli.table.column.Column.JUSTIFICATION.LEFT; + +/** + * For displaying boolean values as YES, NO, or user's choice for 'true' and 'false'. + */ +public class BooleanColumn extends AbstractColumn { + + private static final String DEFAULT_TRUE_AS_STRING = "YES"; + private static final String DEFAULT_FALSE_AS_STRING = "NO"; + + private final List rows = new ArrayList<>(); + + private final Predicate isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth; + + private final String trueAsString; + private final String falseAsString; + + // The default BooleanColumn JUSTIFICATION is LEFT. + // The default BooleanColumn True AsString value is YES. + // The default BooleanColumn False AsString value is NO. + public BooleanColumn(String name) { + this(name, LEFT, DEFAULT_TRUE_AS_STRING, DEFAULT_FALSE_AS_STRING); + } + + // Use this constructor to override default LEFT justification. + @SuppressWarnings("unused") + public BooleanColumn(String name, JUSTIFICATION justification) { + this(name, justification, DEFAULT_TRUE_AS_STRING, DEFAULT_FALSE_AS_STRING); + } + + // Use this constructor to override default true/false as string defaults. + public BooleanColumn(String name, String trueAsString, String falseAsString) { + this(name, LEFT, trueAsString, falseAsString); + } + + // Use this constructor to override default LEFT justification. + public BooleanColumn(String name, + JUSTIFICATION justification, + String trueAsString, + String falseAsString) { + super(name, justification); + this.trueAsString = trueAsString; + this.falseAsString = falseAsString; + this.maxWidth = name.length(); + } + + @Override + public void addRow(Boolean value) { + rows.add(value); + + // We do not know how much padding each StringColumn value needs until it has all the values. + String s = asString(value); + stringColumn.addRow(s); + + if (isNewMaxWidth.test(s)) + maxWidth = s.length(); + } + + @Override + public List getRows() { + return rows; + } + + @Override + public int rowCount() { + return rows.size(); + } + + @Override + public boolean isEmpty() { + return rows.isEmpty(); + } + + @Override + public Boolean getRow(int rowIndex) { + return rows.get(rowIndex); + } + + @Override + public void updateRow(int rowIndex, Boolean newValue) { + rows.set(rowIndex, newValue); + } + + @Override + public String getRowAsFormattedString(int rowIndex) { + return getRow(rowIndex) + ? trueAsString + : falseAsString; + } + + @Override + public StringColumn asStringColumn() { + // We cached the formatted satoshi strings, but we did + // not know how much padding each string needed until now. + IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> { + String unjustified = stringColumn.getRow(rowIndex); + String justified = stringColumn.toJustifiedString(unjustified); + stringColumn.updateRow(rowIndex, justified); + }); + return stringColumn; + } + + private String asString(boolean value) { + return value ? trueAsString : falseAsString; + } +} diff --git a/bot/src/main/java/haveno/cli/table/column/BtcColumn.java b/bot/src/main/java/haveno/cli/table/column/BtcColumn.java new file mode 100644 index 00000000..f0b6f779 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/BtcColumn.java @@ -0,0 +1,48 @@ +package haveno.cli.table.column; + +import java.util.stream.IntStream; + +import static com.google.common.base.Strings.padEnd; +import static haveno.cli.CurrencyFormat.formatBtc; +import static java.util.Comparator.comparingInt; + +public class BtcColumn extends SatoshiColumn { + + public BtcColumn(String name) { + super(name); + } + + @Override + public void addRow(Long value) { + rows.add(value); + + String s = formatBtc(value); + stringColumn.addRow(s); + + if (isNewMaxWidth.test(s)) + maxWidth = s.length(); + } + + @Override + public String getRowAsFormattedString(int rowIndex) { + return formatBtc(getRow(rowIndex)); + } + + @Override + public StringColumn asStringColumn() { + // We cached the formatted satoshi strings, but we did + // not know how much zero padding each string needed until now. + int maxColumnValueWidth = stringColumn.getRows().stream() + .max(comparingInt(String::length)) + .get() + .length(); + IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> { + String btcString = stringColumn.getRow(rowIndex); + if (btcString.length() < maxColumnValueWidth) { + String paddedBtcString = padEnd(btcString, maxColumnValueWidth, '0'); + stringColumn.updateRow(rowIndex, paddedBtcString); + } + }); + return stringColumn.justify(); + } +} diff --git a/bot/src/main/java/haveno/cli/table/column/Column.java b/bot/src/main/java/haveno/cli/table/column/Column.java new file mode 100644 index 00000000..70787306 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/Column.java @@ -0,0 +1,122 @@ +/* + * 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 . + */ + +package haveno.cli.table.column; + +import java.util.List; + +public interface Column { + + enum JUSTIFICATION { + LEFT, + RIGHT, + NONE + } + + /** + * Returns the column's name. + * + * @return name as String + */ + String getName(); + + /** + * Sets the column name. + * + * @param name of the column + */ + void setName(String name); + + /** + * Add column value. + * + * @param value added to column's data (row) + */ + void addRow(T value); + + /** + * Returns the column data. + * + * @return rows as List + */ + List getRows(); + + /** + * Returns the maximum width of the column name, or longest, + * formatted string value -- whichever is greater. + * + * @return width of the populated column as int + */ + int getWidth(); + + /** + * Returns the number of rows in the column. + * + * @return number of rows in the column as int. + */ + int rowCount(); + + /** + * Returns true if the column has no data. + * + * @return true if empty, false if not + */ + boolean isEmpty(); + + /** + * Returns the column value (data) at given row index. + * + * @return value object + */ + T getRow(int rowIndex); + + /** + * Update an existing value at the given row index to a new value. + * + * @param rowIndex row index of value to be updated + * @param newValue new value + */ + void updateRow(int rowIndex, T newValue); + + /** + * Returns the row value as a formatted String. + * + * @return a row value as formatted String + */ + String getRowAsFormattedString(int rowIndex); + + /** + * Return the column with all of its data as a StringColumn with all of its + * formatted string data. + * + * @return StringColumn + */ + StringColumn asStringColumn(); + + /** + * Convenience for justifying populated StringColumns before being displayed. + * Is only useful for StringColumn instances. + */ + Column justify(); + + /** + * Returns JUSTIFICATION value (RIGHT|LEFT|NONE) for the column. + * + * @return column JUSTIFICATION + */ + JUSTIFICATION getJustification(); +} diff --git a/bot/src/main/java/haveno/cli/table/column/CryptoVolumeColumn.java b/bot/src/main/java/haveno/cli/table/column/CryptoVolumeColumn.java new file mode 100644 index 00000000..153e2735 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/CryptoVolumeColumn.java @@ -0,0 +1,88 @@ +/* + * 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 . + */ + +package haveno.cli.table.column; + +import java.math.BigDecimal; +import java.util.function.BiFunction; +import java.util.stream.IntStream; + +import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; + +/** + * For displaying crypto volume with appropriate precision. + */ +public class CryptoVolumeColumn extends LongColumn { + + public enum DISPLAY_MODE { + CRYPTO_VOLUME, + BSQ_VOLUME, + } + + private final DISPLAY_MODE displayMode; + + // The default CryptoVolumeColumn JUSTIFICATION is RIGHT. + public CryptoVolumeColumn(String name, DISPLAY_MODE displayMode) { + this(name, RIGHT, displayMode); + } + + public CryptoVolumeColumn(String name, + JUSTIFICATION justification, + DISPLAY_MODE displayMode) { + super(name, justification); + this.displayMode = displayMode; + } + + @Override + public void addRow(Long value) { + rows.add(value); + + String s = toFormattedString.apply(value, displayMode); + stringColumn.addRow(s); + + if (isNewMaxWidth.test(s)) + maxWidth = s.length(); + } + + @Override + public String getRowAsFormattedString(int rowIndex) { + return toFormattedString.apply(getRow(rowIndex), displayMode); + } + + @Override + public StringColumn asStringColumn() { + // We cached the formatted crypto value strings, but we did + // not know how much padding each string needed until now. + IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> { + String unjustified = stringColumn.getRow(rowIndex); + String justified = stringColumn.toJustifiedString(unjustified); + stringColumn.updateRow(rowIndex, justified); + }); + return this.stringColumn; + } + + private final BiFunction toFormattedString = (value, displayMode) -> { + switch (displayMode) { + case CRYPTO_VOLUME: + return value > 0 ? new BigDecimal(value).movePointLeft(8).toString() : ""; + case BSQ_VOLUME: + return value > 0 ? new BigDecimal(value).movePointLeft(2).toString() : ""; + default: + throw new IllegalStateException("invalid display mode: " + displayMode); + } + }; +} diff --git a/bot/src/main/java/haveno/cli/table/column/DoubleColumn.java b/bot/src/main/java/haveno/cli/table/column/DoubleColumn.java new file mode 100644 index 00000000..812f421c --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/DoubleColumn.java @@ -0,0 +1,93 @@ +/* + * 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 . + */ + +package haveno.cli.table.column; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.IntStream; + +import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; + +/** + * For displaying Double values. + */ +public class DoubleColumn extends NumberColumn { + + protected final List rows = new ArrayList<>(); + + protected final Predicate isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth; + + // The default DoubleColumn JUSTIFICATION is RIGHT. + public DoubleColumn(String name) { + this(name, RIGHT); + } + + public DoubleColumn(String name, JUSTIFICATION justification) { + super(name, justification); + this.maxWidth = name.length(); + } + + @Override + public void addRow(Double value) { + rows.add(value); + + String s = String.valueOf(value); + if (isNewMaxWidth.test(s)) + maxWidth = s.length(); + } + + @Override + public List getRows() { + return rows; + } + + @Override + public int rowCount() { + return rows.size(); + } + + @Override + public boolean isEmpty() { + return rows.isEmpty(); + } + + @Override + public Double getRow(int rowIndex) { + return rows.get(rowIndex); + } + + @Override + public void updateRow(int rowIndex, Double newValue) { + rows.set(rowIndex, newValue); + } + + @Override + public String getRowAsFormattedString(int rowIndex) { + String s = String.valueOf(getRow(rowIndex)); + return toJustifiedString(s); + } + + @Override + public StringColumn asStringColumn() { + IntStream.range(0, rows.size()).forEachOrdered(rowIndex -> + stringColumn.addRow(getRowAsFormattedString(rowIndex))); + + return stringColumn; + } +} diff --git a/bot/src/main/java/haveno/cli/table/column/IntegerColumn.java b/bot/src/main/java/haveno/cli/table/column/IntegerColumn.java new file mode 100644 index 00000000..5d9538e5 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/IntegerColumn.java @@ -0,0 +1,93 @@ +/* + * 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 . + */ + +package haveno.cli.table.column; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.IntStream; + +import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; + +/** + * For displaying Integer values. + */ +public class IntegerColumn extends NumberColumn { + + protected final List rows = new ArrayList<>(); + + protected final Predicate isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth; + + // The default IntegerColumn JUSTIFICATION is RIGHT. + public IntegerColumn(String name) { + this(name, RIGHT); + } + + public IntegerColumn(String name, JUSTIFICATION justification) { + super(name, justification); + this.maxWidth = name.length(); + } + + @Override + public void addRow(Integer value) { + rows.add(value); + + String s = String.valueOf(value); + if (isNewMaxWidth.test(s)) + maxWidth = s.length(); + } + + @Override + public List getRows() { + return rows; + } + + @Override + public int rowCount() { + return rows.size(); + } + + @Override + public boolean isEmpty() { + return rows.isEmpty(); + } + + @Override + public Integer getRow(int rowIndex) { + return rows.get(rowIndex); + } + + @Override + public void updateRow(int rowIndex, Integer newValue) { + rows.set(rowIndex, newValue); + } + + @Override + public String getRowAsFormattedString(int rowIndex) { + String s = String.valueOf(getRow(rowIndex)); + return toJustifiedString(s); + } + + @Override + public StringColumn asStringColumn() { + IntStream.range(0, rows.size()).forEachOrdered(rowIndex -> + stringColumn.addRow(getRowAsFormattedString(rowIndex))); + + return stringColumn; + } +} diff --git a/bot/src/main/java/haveno/cli/table/column/Iso8601DateTimeColumn.java b/bot/src/main/java/haveno/cli/table/column/Iso8601DateTimeColumn.java new file mode 100644 index 00000000..df87a500 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/Iso8601DateTimeColumn.java @@ -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 . + */ + +package haveno.cli.table.column; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.stream.IntStream; + +import static com.google.common.base.Strings.padEnd; +import static com.google.common.base.Strings.padStart; +import static haveno.cli.table.column.Column.JUSTIFICATION.LEFT; +import static java.lang.System.currentTimeMillis; +import static java.util.TimeZone.getTimeZone; + +/** + * For displaying (long) timestamp values as ISO-8601 dates in UTC time zone. + */ +public class Iso8601DateTimeColumn extends LongColumn { + + protected final SimpleDateFormat iso8601DateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + + // The default Iso8601DateTimeColumn JUSTIFICATION is LEFT. + public Iso8601DateTimeColumn(String name) { + this(name, LEFT); + } + + public Iso8601DateTimeColumn(String name, JUSTIFICATION justification) { + super(name, justification); + iso8601DateFormat.setTimeZone(getTimeZone("UTC")); + this.maxWidth = Math.max(name.length(), String.valueOf(currentTimeMillis()).length()); + } + + @Override + public String getRowAsFormattedString(int rowIndex) { + long time = getRow(rowIndex); + return justification.equals(LEFT) + ? padEnd(iso8601DateFormat.format(new Date(time)), maxWidth, ' ') + : padStart(iso8601DateFormat.format(new Date(time)), maxWidth, ' '); + } + + @Override + public StringColumn asStringColumn() { + IntStream.range(0, rows.size()).forEachOrdered(rowIndex -> + stringColumn.addRow(getRowAsFormattedString(rowIndex))); + + return stringColumn; + } +} diff --git a/bot/src/main/java/haveno/cli/table/column/LongColumn.java b/bot/src/main/java/haveno/cli/table/column/LongColumn.java new file mode 100644 index 00000000..1499b0cb --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/LongColumn.java @@ -0,0 +1,93 @@ +/* + * 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 . + */ + +package haveno.cli.table.column; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.IntStream; + +import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; + +/** + * For displaying Long values. + */ +public class LongColumn extends NumberColumn { + + protected final List rows = new ArrayList<>(); + + protected final Predicate isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth; + + // The default LongColumn JUSTIFICATION is RIGHT. + public LongColumn(String name) { + this(name, RIGHT); + } + + public LongColumn(String name, JUSTIFICATION justification) { + super(name, justification); + this.maxWidth = name.length(); + } + + @Override + public void addRow(Long value) { + rows.add(value); + + String s = String.valueOf(value); + if (isNewMaxWidth.test(s)) + maxWidth = s.length(); + } + + @Override + public List getRows() { + return rows; + } + + @Override + public int rowCount() { + return rows.size(); + } + + @Override + public boolean isEmpty() { + return rows.isEmpty(); + } + + @Override + public Long getRow(int rowIndex) { + return rows.get(rowIndex); + } + + @Override + public void updateRow(int rowIndex, Long newValue) { + rows.set(rowIndex, newValue); + } + + @Override + public String getRowAsFormattedString(int rowIndex) { + String s = String.valueOf(getRow(rowIndex)); + return toJustifiedString(s); + } + + @Override + public StringColumn asStringColumn() { + IntStream.range(0, rows.size()).forEachOrdered(rowIndex -> + stringColumn.addRow(getRowAsFormattedString(rowIndex))); + + return stringColumn; + } +} diff --git a/bot/src/main/java/haveno/cli/table/column/MixedTradeFeeColumn.java b/bot/src/main/java/haveno/cli/table/column/MixedTradeFeeColumn.java new file mode 100644 index 00000000..4e54759e --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/MixedTradeFeeColumn.java @@ -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 . + */ + +package haveno.cli.table.column; + +import static haveno.cli.CurrencyFormat.formatBsq; +import static haveno.cli.CurrencyFormat.formatSatoshis; +import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; + +/** + * For displaying a mix of BSQ and BTC trade fees with appropriate precision. + */ +public class MixedTradeFeeColumn extends LongColumn { + + public MixedTradeFeeColumn(String name) { + super(name, RIGHT); + } + + @Override + public void addRow(Long value) { + throw new UnsupportedOperationException("use public void addRow(Long value, boolean isBsq) instead"); + } + + public void addRow(Long value, boolean isBsq) { + rows.add(value); + + String s = isBsq + ? formatBsq(value) + " BSQ" + : formatSatoshis(value) + " BTC"; + stringColumn.addRow(s); + + if (isNewMaxWidth.test(s)) + maxWidth = s.length(); + } + + @Override + public String getRowAsFormattedString(int rowIndex) { + return getRow(rowIndex).toString(); + } + + @Override + public StringColumn asStringColumn() { + return stringColumn.justify(); + } +} diff --git a/bot/src/main/java/haveno/cli/table/column/NumberColumn.java b/bot/src/main/java/haveno/cli/table/column/NumberColumn.java new file mode 100644 index 00000000..69c67844 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/NumberColumn.java @@ -0,0 +1,32 @@ +/* + * 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 . + */ + +package haveno.cli.table.column; + +/** + * Abstract superclass for numeric Columns. + * + * @param the subclass column's type (LongColumn, IntegerColumn, ...) + * @param the subclass column's numeric Java type (Long, Integer, ...) + */ +abstract class NumberColumn, + T extends Number> extends AbstractColumn implements Column { + + public NumberColumn(String name, JUSTIFICATION justification) { + super(name, justification); + } +} diff --git a/bot/src/main/java/haveno/cli/table/column/SatoshiColumn.java b/bot/src/main/java/haveno/cli/table/column/SatoshiColumn.java new file mode 100644 index 00000000..767e878d --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/SatoshiColumn.java @@ -0,0 +1,72 @@ +/* + * 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 . + */ + +package haveno.cli.table.column; + +import static haveno.cli.CurrencyFormat.formatBsq; +import static haveno.cli.CurrencyFormat.formatSatoshis; +import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; + +/** + * For displaying BTC or BSQ satoshi values with appropriate precision. + */ +public class SatoshiColumn extends LongColumn { + + protected final boolean isBsqSatoshis; + + // The default SatoshiColumn JUSTIFICATION is RIGHT. + public SatoshiColumn(String name) { + this(name, RIGHT, false); + } + + public SatoshiColumn(String name, boolean isBsqSatoshis) { + this(name, RIGHT, isBsqSatoshis); + } + + public SatoshiColumn(String name, JUSTIFICATION justification) { + this(name, justification, false); + } + + public SatoshiColumn(String name, JUSTIFICATION justification, boolean isBsqSatoshis) { + super(name, justification); + this.isBsqSatoshis = isBsqSatoshis; + } + + @Override + public void addRow(Long value) { + rows.add(value); + + // We do not know how much padding each StringColumn value needs until it has all the values. + String s = isBsqSatoshis ? formatBsq(value) : formatSatoshis(value); + stringColumn.addRow(s); + + if (isNewMaxWidth.test(s)) + maxWidth = s.length(); + } + + @Override + public String getRowAsFormattedString(int rowIndex) { + return isBsqSatoshis + ? formatBsq(getRow(rowIndex)) + : formatSatoshis(getRow(rowIndex)); + } + + @Override + public StringColumn asStringColumn() { + return stringColumn.justify(); + } +} diff --git a/bot/src/main/java/haveno/cli/table/column/StringColumn.java b/bot/src/main/java/haveno/cli/table/column/StringColumn.java new file mode 100644 index 00000000..67622287 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/StringColumn.java @@ -0,0 +1,102 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package haveno.cli.table.column; + + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.IntStream; + +import static haveno.cli.table.column.Column.JUSTIFICATION.LEFT; +import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; + +/** + * For displaying justified string values. + */ +public class StringColumn extends AbstractColumn { + + private final List rows = new ArrayList<>(); + + private final Predicate isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth; + + // The default StringColumn JUSTIFICATION is LEFT. + public StringColumn(String name) { + this(name, LEFT); + } + + // Use this constructor to override default LEFT justification. + public StringColumn(String name, JUSTIFICATION justification) { + super(name, justification); + this.maxWidth = name.length(); + } + + @Override + public void addRow(String value) { + rows.add(value); + if (isNewMaxWidth.test(value)) + maxWidth = value.length(); + } + + @Override + public List getRows() { + return rows; + } + + @Override + public int rowCount() { + return rows.size(); + } + + @Override + public boolean isEmpty() { + return rows.isEmpty(); + } + + @Override + public String getRow(int rowIndex) { + return rows.get(rowIndex); + } + + @Override + public void updateRow(int rowIndex, String newValue) { + rows.set(rowIndex, newValue); + } + + @Override + public String getRowAsFormattedString(int rowIndex) { + return getRow(rowIndex); + } + + @Override + public StringColumn asStringColumn() { + return this; + } + + @Override + public StringColumn justify() { + if (justification.equals(RIGHT)) { + IntStream.range(0, getRows().size()).forEach(rowIndex -> { + String unjustified = getRow(rowIndex); + String justified = toJustifiedString(unjustified); + updateRow(rowIndex, justified); + }); + } + return this; + } +} diff --git a/bot/src/main/java/haveno/cli/table/column/ZippedStringColumns.java b/bot/src/main/java/haveno/cli/table/column/ZippedStringColumns.java new file mode 100644 index 00000000..d55468d8 --- /dev/null +++ b/bot/src/main/java/haveno/cli/table/column/ZippedStringColumns.java @@ -0,0 +1,127 @@ +/* + * 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 . + */ + +package haveno.cli.table.column; + +import haveno.cli.table.column.Column.JUSTIFICATION; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.IntStream; + +import static haveno.cli.table.column.ZippedStringColumns.DUPLICATION_MODE.EXCLUDE_DUPLICATES; +import static haveno.cli.table.column.ZippedStringColumns.DUPLICATION_MODE.INCLUDE_DUPLICATES; + +/** + * For zipping multiple StringColumns into a single StringColumn. + * Useful for displaying amount and volume range values. + */ +public class ZippedStringColumns { + + public enum DUPLICATION_MODE { + EXCLUDE_DUPLICATES, + INCLUDE_DUPLICATES + } + + private final String name; + private final JUSTIFICATION justification; + private final String delimiter; + private final StringColumn[] columns; + + public ZippedStringColumns(String name, + JUSTIFICATION justification, + String delimiter, + StringColumn... columns) { + this.name = name; + this.justification = justification; + this.delimiter = delimiter; + this.columns = columns; + validateColumnData(); + } + + public StringColumn asStringColumn(DUPLICATION_MODE duplicationMode) { + StringColumn stringColumn = new StringColumn(name, justification); + + buildRows(stringColumn, duplicationMode); + + // Re-set the column name field to its justified value, in case any of the column + // values are longer than the name passed to this constructor. + stringColumn.setName(stringColumn.toJustifiedString(name)); + + return stringColumn; + } + + private void buildRows(StringColumn stringColumn, DUPLICATION_MODE duplicationMode) { + // Populate the StringColumn with unjustified zipped values; we cannot justify + // the zipped values until stringColumn knows its final maxWidth. + IntStream.range(0, columns[0].getRows().size()).forEach(rowIndex -> { + String row = buildRow(rowIndex, duplicationMode); + stringColumn.addRow(row); + }); + + formatRows(stringColumn); + } + + private String buildRow(int rowIndex, DUPLICATION_MODE duplicationMode) { + StringBuilder rowBuilder = new StringBuilder(); + @Nullable + List processedValues = duplicationMode.equals(EXCLUDE_DUPLICATES) + ? new ArrayList<>() + : null; + IntStream.range(0, columns.length).forEachOrdered(colIndex -> { + // For each column @ rowIndex ... + var value = columns[colIndex].getRows().get(rowIndex); + if (duplicationMode.equals(INCLUDE_DUPLICATES)) { + if (rowBuilder.length() > 0) + rowBuilder.append(delimiter); + + rowBuilder.append(value); + } else if (!processedValues.contains(value)) { + if (rowBuilder.length() > 0) + rowBuilder.append(delimiter); + + rowBuilder.append(value); + processedValues.add(value); + } + }); + return rowBuilder.toString(); + } + + private void formatRows(StringColumn stringColumn) { + // Now we can justify the zipped string values in the new StringColumn. + IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> { + String unjustified = stringColumn.getRow(rowIndex); + String justified = stringColumn.toJustifiedString(unjustified); + stringColumn.updateRow(rowIndex, justified); + }); + } + + private void validateColumnData() { + if (columns.length == 0) + throw new IllegalStateException("cannot zip columns because they do not have any data"); + + StringColumn firstColumn = columns[0]; + if (firstColumn.getRows().isEmpty()) + throw new IllegalStateException("1st column has no data"); + + IntStream.range(1, columns.length).forEach(colIndex -> { + if (columns[colIndex].getRows().size() != firstColumn.getRows().size()) + throw new IllegalStateException("columns do not have same number of rows"); + }); + } +} diff --git a/bot/src/main/resources/logback.xml b/bot/src/main/resources/logback.xml new file mode 100644 index 00000000..bc8edf02 --- /dev/null +++ b/bot/src/main/resources/logback.xml @@ -0,0 +1,15 @@ + + + + + %highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30}: %msg %xEx%n) + + + + + + + + + + diff --git a/bot/src/test/java/haveno/cli/AbstractCliTest.java b/bot/src/test/java/haveno/cli/AbstractCliTest.java new file mode 100644 index 00000000..a11059fe --- /dev/null +++ b/bot/src/test/java/haveno/cli/AbstractCliTest.java @@ -0,0 +1,262 @@ +package haveno.cli; + +import haveno.cli.opts.ArgumentList; +import haveno.proto.grpc.OfferInfo; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import lombok.extern.slf4j.Slf4j; +import org.bitbucket.cowwoc.diffmatchpatch.DiffMatchPatch; + +import java.math.BigDecimal; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static haveno.cli.opts.OptLabel.OPT_HOST; +import static haveno.cli.opts.OptLabel.OPT_PASSWORD; +import static haveno.cli.opts.OptLabel.OPT_PORT; +import static java.lang.System.out; +import static java.math.RoundingMode.HALF_UP; +import static java.util.Arrays.stream; +import static org.bitbucket.cowwoc.diffmatchpatch.DiffMatchPatch.Operation.DELETE; +import static org.bitbucket.cowwoc.diffmatchpatch.DiffMatchPatch.Operation.INSERT; + +/** + * Parent class for CLI smoke tests. Useful for examining the format of the console + * output, and checking for diffs while making changes to console output formatters. + * + * Tests that create offers or trades should not be run on mainnet. + */ +@Slf4j +public abstract class AbstractCliTest { + + static final String PASSWORD_OPT = "--password=xyz"; // Both daemons' password. + static final String ALICE_PORT_OPT = "--port=" + 9998; // Alice's daemon port. + static final String BOB_PORT_OPT = "--port=" + 9999; // Bob's daemon port. + static final String[] BASE_ALICE_CLIENT_OPTS = new String[]{PASSWORD_OPT, ALICE_PORT_OPT}; + static final String[] BASE_BOB_CLIENT_OPTS = new String[]{PASSWORD_OPT, BOB_PORT_OPT}; + + protected final BiFunction> randomMarginBasedPrices = (min, max) -> + IntStream.range(min, max).asDoubleStream() + .boxed() + .map(d -> d / 100) + .map(Object::toString) + .collect(Collectors.toList()); + + protected final BiFunction randomFixedCryptoPrice = (min, max) -> { + String random = Double.valueOf(ThreadLocalRandom.current().nextDouble(min, max)).toString(); + BigDecimal bd = new BigDecimal(random).setScale(8, HALF_UP); + return bd.toPlainString(); + }; + + protected final GrpcClient aliceClient; + protected final GrpcClient bobClient; + + public AbstractCliTest() { + this.aliceClient = getGrpcClient(BASE_ALICE_CLIENT_OPTS); + this.bobClient = getGrpcClient(BASE_BOB_CLIENT_OPTS); + } + + protected GrpcClient getGrpcClient(String[] args) { + var parser = new OptionParser(); + 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(); + + OptionSet options = parser.parse(new ArgumentList(args).getCLIArguments()); + 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"); + + return new GrpcClient(host, port, password); + } + + protected void checkDiffsIgnoreWhitespace(String oldOutput, String newOutput) { + Predicate isInsertOrDelete = (operation) -> + operation.equals(INSERT) || operation.equals(DELETE); + Predicate isWhitespace = (text) -> text.trim().isEmpty(); + boolean hasNonWhitespaceDiffs = false; + if (!oldOutput.equals(newOutput)) { + DiffMatchPatch dmp = new DiffMatchPatch(); + LinkedList diff = dmp.diffMain(oldOutput, newOutput, true); + for (DiffMatchPatch.Diff d : diff) { + if (isInsertOrDelete.test(d.operation) && !isWhitespace.test(d.text)) { + hasNonWhitespaceDiffs = true; + log.error(">>> DIFF {}", d); + } + } + } + + if (hasNonWhitespaceDiffs) + log.error("FAIL: There were diffs"); + else + log.info("PASS: No diffs"); + } + + protected void printOldTbl(String tbl) { + log.info("OLD Console OUT:\n{}", tbl); + } + + protected void printNewTbl(String tbl) { + log.info("NEW Console OUT:\n{}", tbl); + } + + protected List getMyCryptoOffers(String currencyCode) { + String[] args = getMyOffersCommand("buy", currencyCode); + out.print(">>>>> haveno-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + + args = getMyOffersCommand("sell", currencyCode); + out.print(">>>>> haveno-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + + return aliceClient.getMyOffersSortedByDate(currencyCode); + } + + protected String[] getMyOffersCommand(String direction, String currencyCode) { + return new String[]{ + PASSWORD_OPT, + ALICE_PORT_OPT, + "getmyoffers", + "--direction=" + direction, + "--currency-code=" + currencyCode + }; + } + + protected String[] getAvailableOffersCommand(String direction, String currencyCode) { + return new String[]{ + PASSWORD_OPT, + BOB_PORT_OPT, + "getoffers", + "--direction=" + direction, + "--currency-code=" + currencyCode + }; + } + + + protected void editOfferPriceMargin(OfferInfo offer, String priceMargin, boolean enable) { + String[] args = new String[]{ + PASSWORD_OPT, + ALICE_PORT_OPT, + "editoffer", + "--offer-id=" + offer.getId(), + "--market-price-margin=" + priceMargin, + "--enable=" + enable + }; + out.print(">>>>> haveno-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + } + + protected void editOfferTriggerPrice(OfferInfo offer, String triggerPrice, boolean enable) { + String[] args = new String[]{ + PASSWORD_OPT, + ALICE_PORT_OPT, + "editoffer", + "--offer-id=" + offer.getId(), + "--trigger-price=" + triggerPrice, + "--enable=" + enable + }; + out.print(">>>>> haveno-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + } + + protected void editOfferPriceMarginAndTriggerPrice(OfferInfo offer, + String priceMargin, + String triggerPrice, + boolean enable) { + String[] args = new String[]{ + PASSWORD_OPT, + ALICE_PORT_OPT, + "editoffer", + "--offer-id=" + offer.getId(), + "--market-price-margin=" + priceMargin, + "--trigger-price=" + triggerPrice, + "--enable=" + enable + }; + out.print(">>>>> haveno-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + } + + protected void editOfferFixedPrice(OfferInfo offer, String fixedPrice, boolean enable) { + String[] args = new String[]{ + PASSWORD_OPT, + ALICE_PORT_OPT, + "editoffer", + "--offer-id=" + offer.getId(), + "--fixed-price=" + fixedPrice, + "--enable=" + enable + }; + out.print(">>>>> haveno-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + } + + protected void disableOffers(List offers) { + out.println("Disable Offers"); + for (OfferInfo offer : offers) { + editOfferEnable(offer, false); + sleep(5); + } + } + + protected void enableOffers(List offers) { + out.println("Enable Offers"); + for (OfferInfo offer : offers) { + editOfferEnable(offer, true); + sleep(5); + } + } + + protected void editOfferEnable(OfferInfo offer, boolean enable) { + String[] args = new String[]{ + PASSWORD_OPT, + ALICE_PORT_OPT, + "editoffer", + "--offer-id=" + offer.getId(), + "--enable=" + enable + }; + out.print(">>>>> haveno-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + } + + protected void sleep(long seconds) { + try { + TimeUnit.SECONDS.sleep(seconds); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/bot/src/test/java/haveno/cli/CreateOfferSmokeTest.java b/bot/src/test/java/haveno/cli/CreateOfferSmokeTest.java new file mode 100644 index 00000000..ea19c2ec --- /dev/null +++ b/bot/src/test/java/haveno/cli/CreateOfferSmokeTest.java @@ -0,0 +1,66 @@ +package haveno.cli; + +import static java.lang.System.out; +import static java.util.Arrays.stream; + +/** + Smoke tests for createoffer method. Useful for testing CLI command and examining the + format of its console output. + + Prerequisites: + + - Run `./haveno-apitest --apiPassword=xyz --supportingApps=bitcoind,seednode,arbdaemon,alicedaemon,bobdaemon --shutdownAfterTests=false --enableHavenoDebugging=false` + + Note: Test harness will not automatically generate BTC blocks to confirm transactions. + + Never run on mainnet! + */ +@SuppressWarnings({"CommentedOutCode", "unused"}) +public class CreateOfferSmokeTest extends AbstractCliTest { + + public static void main(String[] args) { + CreateOfferSmokeTest test = new CreateOfferSmokeTest(); + test.createBsqSwapOffer("buy"); + test.createBsqSwapOffer("sell"); + } + + private void createBsqSwapOffer(String direction) { + String[] args = createBsqSwapOfferCommand(direction, "0.01", "0.005", "0.00005"); + out.print(">>>>> haveno-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + + args = getMyOffersCommand(direction, "bsq"); + out.print(">>>>> haveno-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + + args = getAvailableOffersCommand(direction, "bsq"); + out.print(">>>>> haveno-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + } + + private String[] createBsqSwapOfferCommand(String direction, + String amount, + String minAmount, + String fixedPrice) { + return new String[]{ + PASSWORD_OPT, + ALICE_PORT_OPT, + "createoffer", + "--swap=true", + "--direction=" + direction, + "--currency-code=bsq", + "--amount=" + amount, + "--min-amount=" + minAmount, + "--fixed-price=" + fixedPrice + }; + } +} diff --git a/bot/src/test/java/haveno/cli/EditXmrOffersSmokeTest.java b/bot/src/test/java/haveno/cli/EditXmrOffersSmokeTest.java new file mode 100644 index 00000000..6e5f4e36 --- /dev/null +++ b/bot/src/test/java/haveno/cli/EditXmrOffersSmokeTest.java @@ -0,0 +1,100 @@ +package haveno.cli; + +import haveno.proto.grpc.OfferInfo; + +import java.util.List; +import java.util.Random; + +import static java.lang.System.out; +import static protobuf.OfferDirection.BUY; + +/** + Smoke tests for the editoffer method. + + Prerequisites: + + - Run `./haveno-apitest --apiPassword=xyz --supportingApps=bitcoind,seednode,arbdaemon,alicedaemon,bobdesktop --shutdownAfterTests=false --enableHavenoDebugging=false` + + - Create some XMR offers with Alice's UI or CLI. + + - Watch Alice's offers being edited in Bob's UI. + + Never run on mainnet. + */ +public class EditXmrOffersSmokeTest extends AbstractCliTest { + + public static void main(String[] args) { + var test = new EditXmrOffersSmokeTest(); + + test.doOfferPriceEdits(); + + List offers = test.getMyCryptoOffers("xmr"); + test.disableOffers(offers); + + test.sleep(6); + + offers = test.getMyCryptoOffers("xmr"); + test.enableOffers(offers); + + // A final look after last edit. + test.getMyCryptoOffers("xmr"); + } + + private void doOfferPriceEdits() { + editPriceMargin(); + editTriggerPrice(); + editPriceMarginAndTriggerPrice(); + editFixedPrice(); + } + + private void editPriceMargin() { + var offers = getMyCryptoOffers("xmr"); + out.println("Edit XMR offers' price margin"); + var margins = randomMarginBasedPrices.apply(-301, 300); + for (int i = 0; i < offers.size(); i++) { + String randomMargin = margins.get(new Random().nextInt(margins.size())); + editOfferPriceMargin(offers.get(i), randomMargin, new Random().nextBoolean()); + sleep(5); + } + } + + private void editTriggerPrice() { + var offers = getMyCryptoOffers("xmr"); + out.println("Edit XMR offers' trigger price"); + for (int i = 0; i < offers.size(); i++) { + var offer = offers.get(i); + if (offer.getUseMarketBasedPrice()) { + // Trigger price is hardcode to be a bit above or below xmr mkt price at runtime. + // It could be looked up and calculated instead. + var newTriggerPrice = offer.getDirection().equals(BUY.name()) ? "0.0039" : "0.005"; + editOfferTriggerPrice(offer, newTriggerPrice, true); + sleep(5); + } + } + } + + private void editPriceMarginAndTriggerPrice() { + var offers = getMyCryptoOffers("xmr"); + out.println("Edit XMR offers' price margin and trigger price"); + for (int i = 0; i < offers.size(); i++) { + var offer = offers.get(i); + if (offer.getUseMarketBasedPrice()) { + // Trigger price is hardcode to be a bit above or below xmr mkt price at runtime. + // It could be looked up and calculated instead. + var newTriggerPrice = offer.getDirection().equals(BUY.name()) ? "0.0038" : "0.0051"; + editOfferPriceMarginAndTriggerPrice(offer, "0.05", newTriggerPrice, true); + sleep(5); + } + } + } + + private void editFixedPrice() { + var offers = getMyCryptoOffers("xmr"); + out.println("Edit XMR offers' fixed price"); + for (int i = 0; i < offers.size(); i++) { + String randomFixedPrice = randomFixedCryptoPrice.apply(0.004, 0.0075); + editOfferFixedPrice(offers.get(i), randomFixedPrice, new Random().nextBoolean()); + sleep(5); + } + } +} diff --git a/bot/src/test/java/haveno/cli/GetOffersSmokeTest.java b/bot/src/test/java/haveno/cli/GetOffersSmokeTest.java new file mode 100644 index 00000000..2e99581b --- /dev/null +++ b/bot/src/test/java/haveno/cli/GetOffersSmokeTest.java @@ -0,0 +1,72 @@ +package haveno.cli; + +import static java.lang.System.out; + +/** + Smoke tests for getoffers method. Useful for examining the format of the console output. + + Prerequisites: + + - Run `./haveno-daemon --apiPassword=xyz --appDataDir=$TESTDIR` + + This can be run on mainnet. + */ +@SuppressWarnings({"CommentedOutCode", "unused"}) +public class GetOffersSmokeTest extends AbstractCliTest { + + // TODO use the static password and port opt definitions in superclass + + public static void main(String[] args) { + getMyBsqOffers(); + // getAvailableBsqOffers(); + // getMyUsdOffers(); + // getAvailableUsdOffers(); + } + + private static void getMyBsqOffers() { + out.println(">>> getmyoffers buy bsq"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffers", "--direction=buy", "--currency-code=bsq"}); + out.println(">>> getmyoffers sell bsq"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffers", "--direction=sell", "--currency-code=bsq"}); + out.println(">>> getmyoffer --offer-id=KRONTTMO-11cef1a9-c636-4dc7-b3f2-1616e4960c28-175"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffer", "--offer-id=KRONTTMO-11cef1a9-c636-4dc7-b3f2-1616e4960c28-175"}); + } + + private static void getAvailableBsqOffers() { + out.println(">>> getoffers buy bsq"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getoffers", "--direction=buy", "--currency-code=bsq"}); + out.println(">>> getoffers sell bsq"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getoffers", "--direction=sell", "--currency-code=bsq"}); + } + + private static void getMyUsdOffers() { + out.println(">>> getmyoffers buy usd"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffers", "--direction=buy", "--currency-code=usd"}); + out.println(">>> getmyoffers sell usd"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffers", "--direction=sell", "--currency-code=usd"}); + } + + private static void getAvailableUsdOffers() { + out.println(">>> getoffers buy usd"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getoffers", "--direction=buy", "--currency-code=usd"}); + out.println(">>> getoffers sell usd"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getoffers", "--direction=sell", "--currency-code=usd"}); + } + + private static void TODO() { + 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"}); + } +} diff --git a/bot/src/test/java/haveno/cli/GetTradesSmokeTest.java b/bot/src/test/java/haveno/cli/GetTradesSmokeTest.java new file mode 100644 index 00000000..ae8b4b1a --- /dev/null +++ b/bot/src/test/java/haveno/cli/GetTradesSmokeTest.java @@ -0,0 +1,49 @@ +package haveno.cli; + +import haveno.cli.table.builder.TableBuilder; +import haveno.proto.grpc.TradeInfo; + +import java.util.List; + +import static haveno.cli.table.builder.TableType.TRADE_DETAIL_TBL; +import static haveno.proto.grpc.GetTradesRequest.Category.CLOSED; +import static java.lang.System.out; + +@SuppressWarnings("unused") +public class GetTradesSmokeTest extends AbstractCliTest { + + public static void main(String[] args) { + GetTradesSmokeTest test = new GetTradesSmokeTest(); + test.printAlicesTrades(); + test.printBobsTrades(); + } + + private final List openTrades; + private final List closedTrades; + + public GetTradesSmokeTest() { + super(); + this.openTrades = aliceClient.getOpenTrades(); + this.closedTrades = aliceClient.getTradeHistory(CLOSED); + } + + private void printAlicesTrades() { + out.println("ALICE'S OPEN TRADES"); + openTrades.stream().forEachOrdered(t -> printTrade(aliceClient, t.getTradeId())); + out.println("ALICE'S CLOSED TRADES"); + closedTrades.stream().forEachOrdered(t -> printTrade(aliceClient, t.getTradeId())); + } + + private void printBobsTrades() { + out.println("BOB'S OPEN TRADES"); + openTrades.stream().forEachOrdered(t -> printTrade(bobClient, t.getTradeId())); + out.println("BOB'S CLOSED TRADES"); + closedTrades.stream().forEachOrdered(t -> printTrade(bobClient, t.getTradeId())); + } + + private void printTrade(GrpcClient client, String tradeId) { + var trade = client.getTrade(tradeId); + var tbl = new TableBuilder(TRADE_DETAIL_TBL, trade).build().toString(); + out.println(tbl); + } +} diff --git a/bot/src/test/java/haveno/cli/opts/OptionParsersTest.java b/bot/src/test/java/haveno/cli/opts/OptionParsersTest.java new file mode 100644 index 00000000..9df6b012 --- /dev/null +++ b/bot/src/test/java/haveno/cli/opts/OptionParsersTest.java @@ -0,0 +1,248 @@ +package haveno.cli.opts; + +import org.junit.jupiter.api.Test; + +import static haveno.cli.Method.canceloffer; +import static haveno.cli.Method.createcryptopaymentacct; +import static haveno.cli.Method.createoffer; +import static haveno.cli.Method.createpaymentacct; +import static haveno.cli.opts.OptLabel.OPT_ACCOUNT_NAME; +import static haveno.cli.opts.OptLabel.OPT_ADDRESS; +import static haveno.cli.opts.OptLabel.OPT_AMOUNT; +import static haveno.cli.opts.OptLabel.OPT_CURRENCY_CODE; +import static haveno.cli.opts.OptLabel.OPT_DIRECTION; +import static haveno.cli.opts.OptLabel.OPT_MKT_PRICE_MARGIN; +import static haveno.cli.opts.OptLabel.OPT_OFFER_ID; +import static haveno.cli.opts.OptLabel.OPT_PASSWORD; +import static haveno.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_FORM; +import static haveno.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_ID; +import static haveno.cli.opts.OptLabel.OPT_SECURITY_DEPOSIT; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + + +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 (v1) opt parser tests + + @Test + public void testCreateOfferWithMissingPaymentAccountIdOptShouldThrowException() { + String[] args = new String[]{ + PASSWORD_OPT, + createoffer.name(), + "--" + OPT_DIRECTION + "=" + "SELL", + "--" + OPT_CURRENCY_CODE + "=" + "JPY", + "--" + OPT_AMOUNT + "=" + "0.1" + }; + Throwable exception = assertThrows(RuntimeException.class, () -> + new CreateOfferOptionParser(args).parse()); + assertEquals("no payment account id specified", exception.getMessage()); + } + + @Test + public void testCreateOfferWithEmptyPaymentAccountIdOptShouldThrowException() { + String[] args = new String[]{ + PASSWORD_OPT, + createoffer.name(), + "--" + OPT_PAYMENT_ACCOUNT_ID + }; + Throwable exception = assertThrows(RuntimeException.class, () -> + new CreateOfferOptionParser(args).parse()); + assertEquals("payment-account-id requires an argument", exception.getMessage()); + } + + @Test + public void testCreateOfferWithMissingDirectionOptShouldThrowException() { + String[] args = new String[]{ + PASSWORD_OPT, + createoffer.name(), + "--" + OPT_PAYMENT_ACCOUNT_ID + "=" + "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 testCreateOfferWithMissingDirectionOptValueShouldThrowException() { + String[] args = new String[]{ + PASSWORD_OPT, + createoffer.name(), + "--" + OPT_PAYMENT_ACCOUNT_ID + "=" + "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_ID + "=" + "abc-payment-acct-id-123", + "--" + OPT_DIRECTION + "=" + "BUY", + "--" + OPT_CURRENCY_CODE + "=" + "EUR", + "--" + OPT_AMOUNT + "=" + "0.125", + "--" + OPT_MKT_PRICE_MARGIN + "=" + "3.15", + "--" + 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(3.15d, parser.getMktPriceMarginPct()); + assertEquals(25.0, parser.getSecurityDepositPct()); + } + + // createpaymentacct opt parser tests + + @Test + public void testCreatePaymentAcctWithMissingPaymentFormOptShouldThrowException() { + 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 testCreatePaymentAcctWithMissingPaymentFormOptValueShouldThrowException() { + 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 testCreatePaymentAcctWithInvalidPaymentFormOptValueShouldThrowException() { + 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 testCreateCryptoCurrencyPaymentAcctWithMissingAcctNameOptShouldThrowException() { + 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 testCreateCryptoCurrencyPaymentAcctWithEmptyAcctNameOptShouldThrowException() { + 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 testCreateCryptoCurrencyPaymentAcctWithInvalidCurrencyCodeOptShouldThrowException() { + 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("api does not support bsq payment accounts", exception.getMessage()); + } + + @Test + public void testCreateBchPaymentAcct() { + var acctName = "bch payment account"; + var currencyCode = "bch"; + var address = "B1nXyZ46XXX"; // 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()); + } +} diff --git a/bot/src/test/java/haveno/cli/table/AddressCliOutputDiffTest.java b/bot/src/test/java/haveno/cli/table/AddressCliOutputDiffTest.java new file mode 100644 index 00000000..038b4c3e --- /dev/null +++ b/bot/src/test/java/haveno/cli/table/AddressCliOutputDiffTest.java @@ -0,0 +1,61 @@ +package haveno.cli.table; + +import haveno.cli.AbstractCliTest; +import haveno.cli.table.builder.TableBuilder; +import haveno.proto.grpc.AddressBalanceInfo; + +import java.util.List; + +import static haveno.cli.table.builder.TableType.ADDRESS_BALANCE_TBL; +import static java.lang.System.err; +import static java.util.Collections.singletonList; + +@SuppressWarnings("unused") +public class AddressCliOutputDiffTest extends AbstractCliTest { + + public static void main(String[] args) { + AddressCliOutputDiffTest test = new AddressCliOutputDiffTest(); + test.getFundingAddresses(); + test.getAddressBalance(); + } + + public AddressCliOutputDiffTest() { + super(); + } + + private void getFundingAddresses() { + var fundingAddresses = aliceClient.getFundingAddresses(); + if (fundingAddresses.size() > 0) { + // TableFormat class had been deprecated, then deleted on 17-Feb-2022, but + // these diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = TableFormat.formatAddressBalanceTbl(fundingAddresses); + var newTbl = new TableBuilder(ADDRESS_BALANCE_TBL, fundingAddresses).build().toString(); + // printOldTbl(oldTbl); + printNewTbl(newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); + } else { + err.println("no funding addresses found"); + } + } + + private void getAddressBalance() { + List addresses = aliceClient.getFundingAddresses(); + int numAddresses = addresses.size(); + // Check output for last 2 addresses. + for (int i = numAddresses - 2; i < addresses.size(); i++) { + var addressBalanceInfo = addresses.get(i); + getAddressBalance(addressBalanceInfo.getAddress()); + } + } + + private void getAddressBalance(String address) { + var addressBalance = singletonList(aliceClient.getAddressBalance(address)); + // TableFormat class had been deprecated, then deleted on 17-Feb-2022, but these + // diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = TableFormat.formatAddressBalanceTbl(addressBalance); + var newTbl = new TableBuilder(ADDRESS_BALANCE_TBL, addressBalance).build().toString(); + // printOldTbl(oldTbl); + printNewTbl(newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); + } +} diff --git a/bot/src/test/java/haveno/cli/table/GetBalanceCliOutputDiffTest.java b/bot/src/test/java/haveno/cli/table/GetBalanceCliOutputDiffTest.java new file mode 100644 index 00000000..0a5dbb52 --- /dev/null +++ b/bot/src/test/java/haveno/cli/table/GetBalanceCliOutputDiffTest.java @@ -0,0 +1,30 @@ +package haveno.cli.table; + +import haveno.cli.AbstractCliTest; +import haveno.cli.table.builder.TableBuilder; + +import static haveno.cli.table.builder.TableType.BTC_BALANCE_TBL; + +@SuppressWarnings("unused") +public class GetBalanceCliOutputDiffTest extends AbstractCliTest { + + public static void main(String[] args) { + GetBalanceCliOutputDiffTest test = new GetBalanceCliOutputDiffTest(); + test.getBtcBalance(); + } + + public GetBalanceCliOutputDiffTest() { + super(); + } + + private void getBtcBalance() { + var balance = aliceClient.getBtcBalances(); + // TableFormat class had been deprecated, then deleted on 17-Feb-2022, but these + // diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = TableFormat.formatBtcBalanceInfoTbl(balance); + var newTbl = new TableBuilder(BTC_BALANCE_TBL, balance).build().toString(); + // printOldTbl(oldTbl); + printNewTbl(newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); + } +} diff --git a/bot/src/test/java/haveno/cli/table/GetOffersCliOutputDiffTest.java b/bot/src/test/java/haveno/cli/table/GetOffersCliOutputDiffTest.java new file mode 100644 index 00000000..76944fbd --- /dev/null +++ b/bot/src/test/java/haveno/cli/table/GetOffersCliOutputDiffTest.java @@ -0,0 +1,123 @@ +package haveno.cli.table; + +import haveno.cli.AbstractCliTest; +import haveno.cli.table.builder.TableBuilder; +import haveno.proto.grpc.OfferInfo; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +import static haveno.cli.table.builder.TableType.OFFER_TBL; +import static protobuf.OfferDirection.BUY; +import static protobuf.OfferDirection.SELL; + +@SuppressWarnings("unused") +@Slf4j +public class GetOffersCliOutputDiffTest extends AbstractCliTest { + + // "My" offers are always Alice's offers. + // "Available" offers are always Alice's offers available to Bob. + + public static void main(String[] args) { + GetOffersCliOutputDiffTest test = new GetOffersCliOutputDiffTest(); + + test.getMyBuyUsdOffers(); + test.getMySellUsdOffers(); + test.getAvailableBuyUsdOffers(); + test.getAvailableSellUsdOffers(); + + /* + // TODO Uncomment when XMR support is added. + test.getMyBuyXmrOffers(); + test.getMySellXmrOffers(); + test.getAvailableBuyXmrOffers(); + test.getAvailableSellXmrOffers(); + */ + + test.getMyBuyBsqOffers(); + test.getMySellBsqOffers(); + test.getAvailableBuyBsqOffers(); + test.getAvailableSellBsqOffers(); + } + + public GetOffersCliOutputDiffTest() { + super(); + } + + private void getMyBuyUsdOffers() { + var myOffers = aliceClient.getMyOffers(BUY.name(), "USD"); + printAndCheckDiffs(myOffers, BUY.name(), "USD"); + } + + private void getMySellUsdOffers() { + var myOffers = aliceClient.getMyOffers(SELL.name(), "USD"); + printAndCheckDiffs(myOffers, SELL.name(), "USD"); + } + + private void getAvailableBuyUsdOffers() { + var offers = bobClient.getOffers(BUY.name(), "USD"); + printAndCheckDiffs(offers, BUY.name(), "USD"); + } + + private void getAvailableSellUsdOffers() { + var offers = bobClient.getOffers(SELL.name(), "USD"); + printAndCheckDiffs(offers, SELL.name(), "USD"); + } + + private void getMyBuyXmrOffers() { + var myOffers = aliceClient.getMyOffers(BUY.name(), "XMR"); + printAndCheckDiffs(myOffers, BUY.name(), "XMR"); + } + + private void getMySellXmrOffers() { + var myOffers = aliceClient.getMyOffers(SELL.name(), "XMR"); + printAndCheckDiffs(myOffers, SELL.name(), "XMR"); + } + + private void getAvailableBuyXmrOffers() { + var offers = bobClient.getOffers(BUY.name(), "XMR"); + printAndCheckDiffs(offers, BUY.name(), "XMR"); + } + + private void getAvailableSellXmrOffers() { + var offers = bobClient.getOffers(SELL.name(), "XMR"); + printAndCheckDiffs(offers, SELL.name(), "XMR"); + } + + private void getMyBuyBsqOffers() { + var myOffers = aliceClient.getMyOffers(BUY.name(), "BSQ"); + printAndCheckDiffs(myOffers, BUY.name(), "BSQ"); + } + + private void getMySellBsqOffers() { + var myOffers = aliceClient.getMyOffers(SELL.name(), "BSQ"); + printAndCheckDiffs(myOffers, SELL.name(), "BSQ"); + } + + private void getAvailableBuyBsqOffers() { + var offers = bobClient.getOffers(BUY.name(), "BSQ"); + printAndCheckDiffs(offers, BUY.name(), "BSQ"); + } + + private void getAvailableSellBsqOffers() { + var offers = bobClient.getOffers(SELL.name(), "BSQ"); + printAndCheckDiffs(offers, SELL.name(), "BSQ"); + } + + private void printAndCheckDiffs(List offers, + String direction, + String currencyCode) { + if (offers.isEmpty()) { + log.warn("No {} {} offers to print.", direction, currencyCode); + } else { + log.info("Checking for diffs in {} {} offers.", direction, currencyCode); + // OfferFormat class had been deprecated, then deleted on 17-Feb-2022, but + // these diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = OfferFormat.formatOfferTable(offers, currencyCode); + var newTbl = new TableBuilder(OFFER_TBL, offers).build().toString(); + // printOldTbl(oldTbl); + printNewTbl(newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); + } + } +} diff --git a/bot/src/test/java/haveno/cli/table/GetTradeCliOutputDiffTest.java b/bot/src/test/java/haveno/cli/table/GetTradeCliOutputDiffTest.java new file mode 100644 index 00000000..9255cb5d --- /dev/null +++ b/bot/src/test/java/haveno/cli/table/GetTradeCliOutputDiffTest.java @@ -0,0 +1,50 @@ +package haveno.cli.table; + +import haveno.cli.AbstractCliTest; +import haveno.cli.GrpcClient; +import haveno.cli.table.builder.TableBuilder; +import lombok.extern.slf4j.Slf4j; + +import static haveno.cli.table.builder.TableType.TRADE_DETAIL_TBL; +import static java.lang.System.out; + +@SuppressWarnings("unused") +@Slf4j +public class GetTradeCliOutputDiffTest extends AbstractCliTest { + + public static void main(String[] args) { + if (args.length == 0) + throw new IllegalStateException("Need a single trade-id program argument."); + + GetTradeCliOutputDiffTest test = new GetTradeCliOutputDiffTest(args[0]); + test.getAlicesTrade(); + out.println(); + test.getBobsTrade(); + } + + private final String tradeId; + + public GetTradeCliOutputDiffTest(String tradeId) { + super(); + this.tradeId = tradeId; + } + + private void getAlicesTrade() { + getTrade(aliceClient); + } + + private void getBobsTrade() { + getTrade(bobClient); + } + + private void getTrade(GrpcClient client) { + var trade = client.getTrade(tradeId); + // TradeFormat class had been deprecated, then deleted on 17-Feb-2022, but these + // diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = TradeFormat.format(trade); + var newTbl = new TableBuilder(TRADE_DETAIL_TBL, trade).build().toString(); + // printOldTbl(oldTbl); + printNewTbl(newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); + } +} diff --git a/bot/src/test/java/haveno/cli/table/GetTransactionCliOutputDiffTest.java b/bot/src/test/java/haveno/cli/table/GetTransactionCliOutputDiffTest.java new file mode 100644 index 00000000..f5d1af73 --- /dev/null +++ b/bot/src/test/java/haveno/cli/table/GetTransactionCliOutputDiffTest.java @@ -0,0 +1,23 @@ +package haveno.cli.table; + +import haveno.cli.AbstractCliTest; +import lombok.extern.slf4j.Slf4j; + +@SuppressWarnings("unused") +@Slf4j +public class GetTransactionCliOutputDiffTest extends AbstractCliTest { + + public static void main(String[] args) { + if (args.length == 0) + throw new IllegalStateException("Need a single transaction-id program argument."); + + GetTransactionCliOutputDiffTest test = new GetTransactionCliOutputDiffTest(args[0]); + } + + private final String transactionId; + + public GetTransactionCliOutputDiffTest(String transactionId) { + super(); + this.transactionId = transactionId; + } +} diff --git a/bot/src/test/java/haveno/cli/table/PaymentAccountsCliOutputDiffTest.java b/bot/src/test/java/haveno/cli/table/PaymentAccountsCliOutputDiffTest.java new file mode 100644 index 00000000..b30a9911 --- /dev/null +++ b/bot/src/test/java/haveno/cli/table/PaymentAccountsCliOutputDiffTest.java @@ -0,0 +1,37 @@ +package haveno.cli.table; + +import haveno.cli.AbstractCliTest; +import haveno.cli.table.builder.TableBuilder; +import lombok.extern.slf4j.Slf4j; + +import static haveno.cli.table.builder.TableType.PAYMENT_ACCOUNT_TBL; + +@SuppressWarnings("unused") +@Slf4j +public class PaymentAccountsCliOutputDiffTest extends AbstractCliTest { + + public static void main(String[] args) { + PaymentAccountsCliOutputDiffTest test = new PaymentAccountsCliOutputDiffTest(); + test.getPaymentAccounts(); + } + + public PaymentAccountsCliOutputDiffTest() { + super(); + } + + private void getPaymentAccounts() { + var paymentAccounts = aliceClient.getPaymentAccounts(); + if (paymentAccounts.size() > 0) { + // The formatPaymentAcctTbl method had been deprecated, then deleted on 17-Feb-2022, + // but these diff tests can be useful for testing changes to the current tbl formatting api. + // var oldTbl = formatPaymentAcctTbl(paymentAccounts); + var newTbl = new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccounts).build().toString(); + // printOldTbl(oldTbl); + printNewTbl(newTbl); + // checkDiffsIgnoreWhitespace(oldTbl, newTbl); + } else { + log.warn("no payment accounts found"); + } + } + +} From 01db30156af6e8b4559539024bcd81717d122136 Mon Sep 17 00:00:00 2001 From: KewbitXMR Date: Mon, 9 Jun 2025 15:45:05 +0700 Subject: [PATCH 2/2] remove bot --- bot/src/main/java/haveno/cli/CliMain.java | 783 ------------------ .../haveno/cli/ColumnHeaderConstants.java | 81 -- .../java/haveno/cli/CryptoCurrencyUtil.java | 35 - .../main/java/haveno/cli/CurrencyFormat.java | 115 --- .../main/java/haveno/cli/DirectionFormat.java | 60 -- bot/src/main/java/haveno/cli/GrpcClient.java | 290 ------- bot/src/main/java/haveno/cli/GrpcStubs.java | 68 -- bot/src/main/java/haveno/cli/Method.java | 63 -- .../haveno/cli/PasswordCallCredentials.java | 62 -- .../java/haveno/cli/TransactionFormat.java | 26 - .../cli/opts/AbstractMethodOptionParser.java | 90 -- .../java/haveno/cli/opts/ArgumentList.java | 124 --- .../cli/opts/CancelOfferOptionParser.java | 38 - ...CryptoCurrencyPaymentAcctOptionParser.java | 90 -- .../cli/opts/CreateOfferOptionParser.java | 144 ---- .../opts/CreatePaymentAcctOptionParser.java | 61 -- .../opts/GetAddressBalanceOptionParser.java | 50 -- .../opts/GetBTCMarketPriceOptionParser.java | 50 -- .../cli/opts/GetBalanceOptionParser.java | 43 - .../haveno/cli/opts/GetOfferOptionParser.java | 50 -- .../cli/opts/GetOffersOptionParser.java | 61 -- .../opts/GetPaymentAcctFormOptionParser.java | 51 -- .../haveno/cli/opts/GetTradeOptionParser.java | 60 -- .../cli/opts/GetTradesOptionParser.java | 83 -- .../cli/opts/GetTransactionOptionParser.java | 50 -- .../main/java/haveno/cli/opts/MethodOpts.java | 25 - .../haveno/cli/opts/OfferIdOptionParser.java | 60 -- .../main/java/haveno/cli/opts/OptLabel.java | 56 -- .../RegisterDisputeAgentOptionParser.java | 61 -- .../RemoveWalletPasswordOptionParser.java | 50 -- .../haveno/cli/opts/SendBtcOptionParser.java | 80 -- .../cli/opts/SetTxFeeRateOptionParser.java | 51 -- .../opts/SetWalletPasswordOptionParser.java | 60 -- .../cli/opts/SimpleMethodOptionParser.java | 30 - .../cli/opts/TakeOfferOptionParser.java | 48 -- .../cli/opts/UnlockWalletOptionParser.java | 63 -- .../cli/opts/WithdrawFundsOptionParser.java | 71 -- .../cli/request/OffersServiceRequest.java | 163 ---- .../PaymentAccountsServiceRequest.java | 99 --- .../cli/request/TradesServiceRequest.java | 107 --- .../cli/request/WalletsServiceRequest.java | 123 --- bot/src/main/java/haveno/cli/table/Table.java | 152 ---- .../table/builder/AbstractTableBuilder.java | 44 - .../builder/AbstractTradeListBuilder.java | 241 ------ .../builder/AddressBalanceTableBuilder.java | 78 -- .../table/builder/BtcBalanceTableBuilder.java | 71 -- .../builder/ClosedTradeTableBuilder.java | 69 -- .../builder/FailedTradeTableBuilder.java | 64 -- .../cli/table/builder/OfferTableBuilder.java | 272 ------ .../table/builder/OpenTradeTableBuilder.java | 62 -- .../builder/PaymentAccountTableBuilder.java | 71 -- .../cli/table/builder/TableBuilder.java | 67 -- .../table/builder/TableBuilderConstants.java | 82 -- .../haveno/cli/table/builder/TableType.java | 34 - .../builder/TradeDetailTableBuilder.java | 98 --- .../builder/TradeTableColumnSupplier.java | 290 ------- .../builder/TransactionTableBuilder.java | 100 --- .../cli/table/column/AbstractColumn.java | 86 -- .../cli/table/column/BooleanColumn.java | 131 --- .../haveno/cli/table/column/BtcColumn.java | 48 -- .../java/haveno/cli/table/column/Column.java | 122 --- .../cli/table/column/CryptoVolumeColumn.java | 88 -- .../haveno/cli/table/column/DoubleColumn.java | 93 --- .../cli/table/column/IntegerColumn.java | 93 --- .../table/column/Iso8601DateTimeColumn.java | 63 -- .../haveno/cli/table/column/LongColumn.java | 93 --- .../cli/table/column/MixedTradeFeeColumn.java | 59 -- .../haveno/cli/table/column/NumberColumn.java | 32 - .../cli/table/column/SatoshiColumn.java | 72 -- .../haveno/cli/table/column/StringColumn.java | 102 --- .../cli/table/column/ZippedStringColumns.java | 127 --- bot/src/main/resources/logback.xml | 15 - .../test/java/haveno/cli/AbstractCliTest.java | 262 ------ .../java/haveno/cli/CreateOfferSmokeTest.java | 66 -- .../haveno/cli/EditXmrOffersSmokeTest.java | 100 --- .../java/haveno/cli/GetOffersSmokeTest.java | 72 -- .../java/haveno/cli/GetTradesSmokeTest.java | 49 -- .../haveno/cli/opts/OptionParsersTest.java | 248 ------ .../cli/table/AddressCliOutputDiffTest.java | 61 -- .../table/GetBalanceCliOutputDiffTest.java | 30 - .../cli/table/GetOffersCliOutputDiffTest.java | 123 --- .../cli/table/GetTradeCliOutputDiffTest.java | 50 -- .../GetTransactionCliOutputDiffTest.java | 23 - .../PaymentAccountsCliOutputDiffTest.java | 37 - 84 files changed, 7915 deletions(-) delete mode 100644 bot/src/main/java/haveno/cli/CliMain.java delete mode 100644 bot/src/main/java/haveno/cli/ColumnHeaderConstants.java delete mode 100644 bot/src/main/java/haveno/cli/CryptoCurrencyUtil.java delete mode 100644 bot/src/main/java/haveno/cli/CurrencyFormat.java delete mode 100644 bot/src/main/java/haveno/cli/DirectionFormat.java delete mode 100644 bot/src/main/java/haveno/cli/GrpcClient.java delete mode 100644 bot/src/main/java/haveno/cli/GrpcStubs.java delete mode 100644 bot/src/main/java/haveno/cli/Method.java delete mode 100644 bot/src/main/java/haveno/cli/PasswordCallCredentials.java delete mode 100644 bot/src/main/java/haveno/cli/TransactionFormat.java delete mode 100644 bot/src/main/java/haveno/cli/opts/AbstractMethodOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/ArgumentList.java delete mode 100644 bot/src/main/java/haveno/cli/opts/CancelOfferOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/CreateOfferOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/CreatePaymentAcctOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/GetAddressBalanceOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/GetBTCMarketPriceOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/GetBalanceOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/GetOfferOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/GetOffersOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/GetPaymentAcctFormOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/GetTradeOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/GetTradesOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/GetTransactionOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/MethodOpts.java delete mode 100644 bot/src/main/java/haveno/cli/opts/OfferIdOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/OptLabel.java delete mode 100644 bot/src/main/java/haveno/cli/opts/RegisterDisputeAgentOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/RemoveWalletPasswordOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/SendBtcOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/SetTxFeeRateOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/SetWalletPasswordOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/SimpleMethodOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/TakeOfferOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/UnlockWalletOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/opts/WithdrawFundsOptionParser.java delete mode 100644 bot/src/main/java/haveno/cli/request/OffersServiceRequest.java delete mode 100644 bot/src/main/java/haveno/cli/request/PaymentAccountsServiceRequest.java delete mode 100644 bot/src/main/java/haveno/cli/request/TradesServiceRequest.java delete mode 100644 bot/src/main/java/haveno/cli/request/WalletsServiceRequest.java delete mode 100644 bot/src/main/java/haveno/cli/table/Table.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/AbstractTableBuilder.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/AbstractTradeListBuilder.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/AddressBalanceTableBuilder.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/BtcBalanceTableBuilder.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/ClosedTradeTableBuilder.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/FailedTradeTableBuilder.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/OfferTableBuilder.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/OpenTradeTableBuilder.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/PaymentAccountTableBuilder.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/TableBuilder.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/TableBuilderConstants.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/TableType.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/TradeDetailTableBuilder.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/TradeTableColumnSupplier.java delete mode 100644 bot/src/main/java/haveno/cli/table/builder/TransactionTableBuilder.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/AbstractColumn.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/BooleanColumn.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/BtcColumn.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/Column.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/CryptoVolumeColumn.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/DoubleColumn.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/IntegerColumn.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/Iso8601DateTimeColumn.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/LongColumn.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/MixedTradeFeeColumn.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/NumberColumn.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/SatoshiColumn.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/StringColumn.java delete mode 100644 bot/src/main/java/haveno/cli/table/column/ZippedStringColumns.java delete mode 100644 bot/src/main/resources/logback.xml delete mode 100644 bot/src/test/java/haveno/cli/AbstractCliTest.java delete mode 100644 bot/src/test/java/haveno/cli/CreateOfferSmokeTest.java delete mode 100644 bot/src/test/java/haveno/cli/EditXmrOffersSmokeTest.java delete mode 100644 bot/src/test/java/haveno/cli/GetOffersSmokeTest.java delete mode 100644 bot/src/test/java/haveno/cli/GetTradesSmokeTest.java delete mode 100644 bot/src/test/java/haveno/cli/opts/OptionParsersTest.java delete mode 100644 bot/src/test/java/haveno/cli/table/AddressCliOutputDiffTest.java delete mode 100644 bot/src/test/java/haveno/cli/table/GetBalanceCliOutputDiffTest.java delete mode 100644 bot/src/test/java/haveno/cli/table/GetOffersCliOutputDiffTest.java delete mode 100644 bot/src/test/java/haveno/cli/table/GetTradeCliOutputDiffTest.java delete mode 100644 bot/src/test/java/haveno/cli/table/GetTransactionCliOutputDiffTest.java delete mode 100644 bot/src/test/java/haveno/cli/table/PaymentAccountsCliOutputDiffTest.java diff --git a/bot/src/main/java/haveno/cli/CliMain.java b/bot/src/main/java/haveno/cli/CliMain.java deleted file mode 100644 index 58dcce46..00000000 --- a/bot/src/main/java/haveno/cli/CliMain.java +++ /dev/null @@ -1,783 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli; - -import haveno.cli.opts.ArgumentList; -import haveno.cli.opts.CancelOfferOptionParser; -import haveno.cli.opts.CreateCryptoCurrencyPaymentAcctOptionParser; -import haveno.cli.opts.CreateOfferOptionParser; -import haveno.cli.opts.CreatePaymentAcctOptionParser; -import haveno.cli.opts.GetAddressBalanceOptionParser; -import haveno.cli.opts.GetBTCMarketPriceOptionParser; -import haveno.cli.opts.GetBalanceOptionParser; -import haveno.cli.opts.GetOffersOptionParser; -import haveno.cli.opts.GetPaymentAcctFormOptionParser; -import haveno.cli.opts.GetTradeOptionParser; -import haveno.cli.opts.GetTradesOptionParser; -import haveno.cli.opts.OfferIdOptionParser; -import haveno.cli.opts.RegisterDisputeAgentOptionParser; -import haveno.cli.opts.RemoveWalletPasswordOptionParser; -import haveno.cli.opts.SendBtcOptionParser; -import haveno.cli.opts.SetWalletPasswordOptionParser; -import haveno.cli.opts.SimpleMethodOptionParser; -import haveno.cli.opts.TakeOfferOptionParser; -import haveno.cli.opts.UnlockWalletOptionParser; -import haveno.cli.opts.WithdrawFundsOptionParser; -import haveno.cli.table.builder.TableBuilder; -import haveno.proto.grpc.OfferInfo; -import io.grpc.StatusRuntimeException; -import joptsimple.OptionParser; -import joptsimple.OptionSet; -import lombok.extern.slf4j.Slf4j; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.PrintStream; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Date; -import java.util.List; - -import static haveno.cli.CurrencyFormat.formatInternalFiatPrice; -import static haveno.cli.CurrencyFormat.toSatoshis; -import static haveno.cli.Method.canceloffer; -import static haveno.cli.Method.closetrade; -import static haveno.cli.Method.confirmpaymentreceived; -import static haveno.cli.Method.confirmpaymentsent; -import static haveno.cli.Method.createcryptopaymentacct; -import static haveno.cli.Method.createoffer; -import static haveno.cli.Method.createpaymentacct; -import static haveno.cli.Method.editoffer; -import static haveno.cli.Method.failtrade; -import static haveno.cli.Method.getaddressbalance; -import static haveno.cli.Method.getbalance; -import static haveno.cli.Method.getbtcprice; -import static haveno.cli.Method.getfundingaddresses; -import static haveno.cli.Method.getmyoffer; -import static haveno.cli.Method.getmyoffers; -import static haveno.cli.Method.getoffer; -import static haveno.cli.Method.getoffers; -import static haveno.cli.Method.getpaymentacctform; -import static haveno.cli.Method.getpaymentaccts; -import static haveno.cli.Method.getpaymentmethods; -import static haveno.cli.Method.gettrade; -import static haveno.cli.Method.gettrades; -import static haveno.cli.Method.gettransaction; -import static haveno.cli.Method.gettxfeerate; -import static haveno.cli.Method.getunusedbsqaddress; -import static haveno.cli.Method.getversion; -import static haveno.cli.Method.lockwallet; -import static haveno.cli.Method.sendbtc; -import static haveno.cli.Method.settxfeerate; -import static haveno.cli.Method.setwalletpassword; -import static haveno.cli.Method.stop; -import static haveno.cli.Method.takeoffer; -import static haveno.cli.Method.unfailtrade; -import static haveno.cli.Method.unlockwallet; -import static haveno.cli.Method.unsettxfeerate; -import static haveno.cli.Method.withdrawfunds; -import static haveno.cli.opts.OptLabel.OPT_AMOUNT; -import static haveno.cli.opts.OptLabel.OPT_HELP; -import static haveno.cli.opts.OptLabel.OPT_HOST; -import static haveno.cli.opts.OptLabel.OPT_PASSWORD; -import static haveno.cli.opts.OptLabel.OPT_PORT; -import static haveno.cli.opts.OptLabel.OPT_TX_FEE_RATE; -import static haveno.cli.table.builder.TableType.ADDRESS_BALANCE_TBL; -import static haveno.cli.table.builder.TableType.BTC_BALANCE_TBL; -import static haveno.cli.table.builder.TableType.CLOSED_TRADES_TBL; -import static haveno.cli.table.builder.TableType.FAILED_TRADES_TBL; -import static haveno.cli.table.builder.TableType.OFFER_TBL; -import static haveno.cli.table.builder.TableType.OPEN_TRADES_TBL; -import static haveno.cli.table.builder.TableType.PAYMENT_ACCOUNT_TBL; -import static haveno.cli.table.builder.TableType.TRADE_DETAIL_TBL; -import static haveno.proto.grpc.GetTradesRequest.Category.CLOSED; -import static haveno.proto.grpc.GetTradesRequest.Category.OPEN; -import static java.lang.String.format; -import static java.lang.System.err; -import static java.lang.System.exit; -import static java.lang.System.out; - -/** - * A command-line client for the Haveno 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) 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 "BTC": - new TableBuilder(BTC_BALANCE_TBL, balances.getBtc()).build().print(out); - break; - case "": - default: { - out.println("BTC"); - new TableBuilder(BTC_BALANCE_TBL, balances.getBtc()).build().print(out); - 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); - new TableBuilder(ADDRESS_BALANCE_TBL, addressBalance).build().print(out); - 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(formatInternalFiatPrice(price)); - return; - } - case getfundingaddresses: { - if (new SimpleMethodOptionParser(args).parse().isForHelp()) { - out.println(client.getMethodHelp(method)); - return; - } - var fundingAddresses = client.getFundingAddresses(); - new TableBuilder(ADDRESS_BALANCE_TBL, fundingAddresses).build().print(out); - 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(); - - throw new RuntimeException("Send BTC not implemented"); - } - 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 marketPriceMarginPct = opts.getMktPriceMarginPct(); - var securityDepositPct = opts.getSecurityDepositPct(); - var triggerPrice = "0"; // Cannot be defined until the new offer is added to book. - OfferInfo offer; - offer = client.createOffer(direction, - currencyCode, - amount, - minAmount, - useMarketBasedPrice, - fixedPrice, - marketPriceMarginPct, - securityDepositPct, - paymentAcctId, - triggerPrice); - new TableBuilder(OFFER_TBL, offer).build().print(out); - 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 OfferIdOptionParser(args).parse(); - if (opts.isForHelp()) { - out.println(client.getMethodHelp(method)); - return; - } - var offerId = opts.getOfferId(); - var offer = client.getOffer(offerId); - new TableBuilder(OFFER_TBL, offer).build().print(out); - return; - } - case getmyoffer: { - var opts = new OfferIdOptionParser(args).parse(); - if (opts.isForHelp()) { - out.println(client.getMethodHelp(method)); - return; - } - var offerId = opts.getOfferId(); - var offer = client.getMyOffer(offerId); - new TableBuilder(OFFER_TBL, offer).build().print(out); - 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 offers = client.getOffers(direction, currencyCode); - if (offers.isEmpty()) - out.printf("no %s %s offers found%n", direction, currencyCode); - else - new TableBuilder(OFFER_TBL, offers).build().print(out); - - 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 offers = client.getMyOffers(direction, currencyCode); - if (offers.isEmpty()) - out.printf("no %s %s offers found%n", direction, currencyCode); - else - new TableBuilder(OFFER_TBL, offers).build().print(out); - - 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 trade = client.takeOffer(offerId, paymentAccountId); - 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 - new TableBuilder(TRADE_DETAIL_TBL, trade).build().print(out); - - return; - } - case gettrades: { - var opts = new GetTradesOptionParser(args).parse(); - if (opts.isForHelp()) { - out.println(client.getMethodHelp(method)); - return; - } - var category = opts.getCategory(); - var trades = category.equals(OPEN) - ? client.getOpenTrades() - : client.getTradeHistory(category); - if (trades.isEmpty()) { - out.printf("no %s trades found%n", category.name().toLowerCase()); - } else { - var tableType = category.equals(OPEN) - ? OPEN_TRADES_TBL - : category.equals(CLOSED) ? CLOSED_TRADES_TBL : FAILED_TRADES_TBL; - new TableBuilder(tableType, trades).build().print(out); - } - return; - } - case confirmpaymentsent: { - var opts = new GetTradeOptionParser(args).parse(); - if (opts.isForHelp()) { - out.println(client.getMethodHelp(method)); - return; - } - var tradeId = opts.getTradeId(); - client.confirmPaymentSent(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 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"); - new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccount).build().print(out); - 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"); - new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccount).build().print(out); - return; - } - case getpaymentaccts: { - if (new SimpleMethodOptionParser(args).parse().isForHelp()) { - out.println(client.getMethodHelp(method)); - return; - } - var paymentAccounts = client.getPaymentAccounts(); - if (paymentAccounts.size() > 0) - new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccounts).build().print(out); - 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., INVALID_ARGUMENT, - // NOT_FOUND, ..., UNKNOWN from the exception 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("Haveno RPC Client"); - stream.println(); - stream.println("Usage: haveno-cli [options] [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=]", "Get server wallet balances"); - stream.println(); - stream.format(rowFormat, getaddressbalance.name(), "--address=", "Get server wallet address balance"); - stream.println(); - stream.format(rowFormat, getbtcprice.name(), "--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, "", "[--tx-fee-rate=]", ""); - stream.println(); - stream.format(rowFormat, sendbtc.name(), "--address= --amount= \\", "Send BTC"); - stream.format(rowFormat, "", "[--tx-fee-rate=]", ""); - stream.format(rowFormat, "", "[--memo=<\"memo\">]", ""); - 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=", "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=", "Get transaction with id"); - stream.println(); - stream.format(rowFormat, createoffer.name(), "--payment-account= \\", "Create and place an offer"); - stream.format(rowFormat, "", "--direction= \\", ""); - stream.format(rowFormat, "", "--currency-code= \\", ""); - stream.format(rowFormat, "", "--amount= \\", ""); - stream.format(rowFormat, "", "[--min-amount=] \\", ""); - stream.format(rowFormat, "", "--fixed-price= | --market-price-margin= \\", ""); - stream.format(rowFormat, "", "--security-deposit= \\", ""); - stream.format(rowFormat, "", "[--fee-currency=]", ""); - stream.format(rowFormat, "", "[--trigger-price=]", ""); - stream.format(rowFormat, "", "[--swap=]", ""); - stream.println(); - stream.format(rowFormat, editoffer.name(), "--offer-id= \\", "Edit offer with id"); - stream.format(rowFormat, "", "[--fixed-price=] \\", ""); - stream.format(rowFormat, "", "[--market-price-margin=] \\", ""); - stream.format(rowFormat, "", "[--trigger-price=] \\", ""); - stream.format(rowFormat, "", "[--enabled=]", ""); - stream.println(); - stream.format(rowFormat, canceloffer.name(), "--offer-id=", "Cancel offer with id"); - stream.println(); - stream.format(rowFormat, getoffer.name(), "--offer-id=", "Get current offer with id"); - stream.println(); - stream.format(rowFormat, getmyoffer.name(), "--offer-id=", "Get my current offer with id"); - stream.println(); - stream.format(rowFormat, getoffers.name(), "--direction= \\", "Get current offers"); - stream.format(rowFormat, "", "--currency-code=", ""); - stream.println(); - stream.format(rowFormat, getmyoffers.name(), "--direction= \\", "Get my current offers"); - stream.format(rowFormat, "", "--currency-code=", ""); - stream.println(); - stream.format(rowFormat, takeoffer.name(), "--offer-id= \\", "Take offer with id"); - stream.format(rowFormat, "", "[--payment-account=]", ""); - stream.format(rowFormat, "", "[--fee-currency=]", ""); - stream.println(); - stream.format(rowFormat, gettrade.name(), "--trade-id= \\", "Get trade summary or full contract"); - stream.format(rowFormat, "", "[--show-contract=]", ""); - stream.println(); - stream.format(rowFormat, gettrades.name(), "[--category=]", "Get open (default), closed, or failed trades"); - stream.println(); - stream.format(rowFormat, confirmpaymentsent.name(), "--trade-id=", "Confirm payment started"); - stream.println(); - stream.format(rowFormat, confirmpaymentreceived.name(), "--trade-id=", "Confirm payment received"); - stream.println(); - stream.format(rowFormat, closetrade.name(), "--trade-id=", "Close completed trade"); - stream.println(); - stream.format(rowFormat, withdrawfunds.name(), "--trade-id= --address= \\", - "Withdraw received trade funds to external wallet address"); - stream.format(rowFormat, "", "[--memo=<\"memo\">]", ""); - stream.println(); - stream.format(rowFormat, failtrade.name(), "--trade-id=", "Change open trade to failed trade"); - stream.println(); - stream.format(rowFormat, unfailtrade.name(), "--trade-id=", "Change failed trade to open trade"); - 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=", "Get a new payment account form"); - stream.println(); - stream.format(rowFormat, createpaymentacct.name(), "--payment-account-form=", "Create a new payment account"); - stream.println(); - stream.format(rowFormat, createcryptopaymentacct.name(), "--account-name= \\", "Create a new cryptocurrency payment account"); - stream.format(rowFormat, "", "--currency-code= \\", ""); - stream.format(rowFormat, "", "--address=", ""); - stream.format(rowFormat, "", "--trade-instant=", ""); - 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= --timeout=", - "Store wallet password in memory for timeout seconds"); - stream.println(); - stream.format(rowFormat, setwalletpassword.name(), "--wallet-password= \\", - "Encrypt wallet with password, or set new password on encrypted wallet"); - stream.format(rowFormat, "", "[--new-wallet-password=]", ""); - stream.println(); - stream.format(rowFormat, stop.name(), "", "Shut down the server"); - stream.println(); - stream.println("Method Help Usage: haveno-cli [options] --help"); - stream.println(); - } catch (IOException ex) { - ex.printStackTrace(stream); - } - } -} diff --git a/bot/src/main/java/haveno/cli/ColumnHeaderConstants.java b/bot/src/main/java/haveno/cli/ColumnHeaderConstants.java deleted file mode 100644 index 5f1ef39e..00000000 --- a/bot/src/main/java/haveno/cli/ColumnHeaderConstants.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 . - */ - -package haveno.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_BALANCE = "Balance"; - 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_RESERVED_OFFER_BALANCE = "Reserved Offer Balance"; - static final String COL_HEADER_RESERVED_TRADE_BALANCE = "Reserved Trade 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_CRYPTO = "Price in BTC for 1 %-3s"; - static final String COL_HEADER_TRADE_AMOUNT = padStart("Amount(%-3s)", 12, ' '); - 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, ' '); -} diff --git a/bot/src/main/java/haveno/cli/CryptoCurrencyUtil.java b/bot/src/main/java/haveno/cli/CryptoCurrencyUtil.java deleted file mode 100644 index 572130db..00000000 --- a/bot/src/main/java/haveno/cli/CryptoCurrencyUtil.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli; - -import java.util.ArrayList; -import java.util.List; - -public class CryptoCurrencyUtil { - - public static boolean apiDoesSupportCryptoCurrency(String currencyCode) { - return getSupportedCryptoCurrencies().contains(currencyCode.toUpperCase()); - } - - public static List getSupportedCryptoCurrencies() { - final List result = new ArrayList<>(); - result.add("BCH"); - result.sort(String::compareTo); - return result; - } -} diff --git a/bot/src/main/java/haveno/cli/CurrencyFormat.java b/bot/src/main/java/haveno/cli/CurrencyFormat.java deleted file mode 100644 index c88b8c62..00000000 --- a/bot/src/main/java/haveno/cli/CurrencyFormat.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli; - -import com.google.common.annotations.VisibleForTesting; - -import java.math.BigDecimal; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.text.NumberFormat; -import java.util.Locale; - -import static java.lang.String.format; -import static java.math.RoundingMode.HALF_UP; -import static java.math.RoundingMode.UNNECESSARY; - -/** - * Utility for formatting amounts, volumes and fees; there is no i18n support in the CLI. - */ -@VisibleForTesting -public class CurrencyFormat { - - // Use the US locale as a base for all DecimalFormats, but commas should be omitted from number strings. - private static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = DecimalFormatSymbols.getInstance(Locale.US); - - // Use the US locale as a base for all NumberFormats, but commas should be omitted from number strings. - private static final NumberFormat US_LOCALE_NUMBER_FORMAT = NumberFormat.getInstance(Locale.US); - - // Formats numbers for internal use, i.e., grpc request parameters. - private static final DecimalFormat INTERNAL_FIAT_DECIMAL_FORMAT = new DecimalFormat("##############0.0000"); - - static final BigDecimal SATOSHI_DIVISOR = new BigDecimal(100_000_000); - static final DecimalFormat SATOSHI_FORMAT = new DecimalFormat("###,##0.00000000", DECIMAL_FORMAT_SYMBOLS); - static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.########", DECIMAL_FORMAT_SYMBOLS); - static final DecimalFormat BTC_TX_FEE_FORMAT = new DecimalFormat("###,###,##0", DECIMAL_FORMAT_SYMBOLS); - - static final BigDecimal BSQ_SATOSHI_DIVISOR = new BigDecimal(100); - static final DecimalFormat BSQ_FORMAT = new DecimalFormat("###,###,###,##0.00", DECIMAL_FORMAT_SYMBOLS); - - public static String formatSatoshis(String sats) { - //noinspection BigDecimalMethodWithoutRoundingCalled - return SATOSHI_FORMAT.format(new BigDecimal(sats).divide(SATOSHI_DIVISOR)); - } - - @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") - public static String formatSatoshis(long sats) { - return SATOSHI_FORMAT.format(new BigDecimal(sats).divide(SATOSHI_DIVISOR)); - } - - @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") - public static String formatBtc(long sats) { - return BTC_FORMAT.format(new BigDecimal(sats).divide(SATOSHI_DIVISOR)); - } - - @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") - public static String formatBsq(long sats) { - return BSQ_FORMAT.format(new BigDecimal(sats).divide(BSQ_SATOSHI_DIVISOR)); - } - - public static String formatInternalFiatPrice(BigDecimal price) { - INTERNAL_FIAT_DECIMAL_FORMAT.setMinimumFractionDigits(4); - INTERNAL_FIAT_DECIMAL_FORMAT.setMaximumFractionDigits(4); - return INTERNAL_FIAT_DECIMAL_FORMAT.format(price); - } - - public static String formatInternalFiatPrice(double price) { - US_LOCALE_NUMBER_FORMAT.setMinimumFractionDigits(4); - US_LOCALE_NUMBER_FORMAT.setMaximumFractionDigits(4); - return US_LOCALE_NUMBER_FORMAT.format(price); - } - - public static String formatPrice(long price) { - US_LOCALE_NUMBER_FORMAT.setMinimumFractionDigits(4); - US_LOCALE_NUMBER_FORMAT.setMaximumFractionDigits(4); - US_LOCALE_NUMBER_FORMAT.setRoundingMode(UNNECESSARY); - return US_LOCALE_NUMBER_FORMAT.format((double) price / 10_000); - } - - public static String formatFiatVolume(long volume) { - US_LOCALE_NUMBER_FORMAT.setMinimumFractionDigits(0); - US_LOCALE_NUMBER_FORMAT.setMaximumFractionDigits(0); - US_LOCALE_NUMBER_FORMAT.setRoundingMode(HALF_UP); - return US_LOCALE_NUMBER_FORMAT.format((double) volume / 10_000); - } - - 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 String formatFeeSatoshis(long sats) { - return BTC_TX_FEE_FORMAT.format(BigDecimal.valueOf(sats)); - } -} diff --git a/bot/src/main/java/haveno/cli/DirectionFormat.java b/bot/src/main/java/haveno/cli/DirectionFormat.java deleted file mode 100644 index 28a30f17..00000000 --- a/bot/src/main/java/haveno/cli/DirectionFormat.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli; - -import haveno.proto.grpc.OfferInfo; - -import java.util.List; -import java.util.function.Function; - -import static haveno.cli.ColumnHeaderConstants.COL_HEADER_DIRECTION; -import static java.lang.String.format; -import static protobuf.OfferDirection.BUY; -import static protobuf.OfferDirection.SELL; - -class DirectionFormat { - - static int getLongestDirectionColWidth(List offers) { - if (offers.isEmpty() || offers.get(0).getBaseCurrencyCode().equals("XMR")) - return COL_HEADER_DIRECTION.length(); - else - return 18; // .e.g., "Sell BSQ (Buy XMR)".length() - } - - static final Function directionFormat = (offer) -> { - String baseCurrencyCode = offer.getBaseCurrencyCode(); - boolean isCryptoCurrencyOffer = !baseCurrencyCode.equals("XMR"); - if (!isCryptoCurrencyOffer) { - return baseCurrencyCode; - } else { - // Return "Sell BSQ (Buy XMR)", or "Buy BSQ (Sell XMR)". - String direction = offer.getDirection(); - String mirroredDirection = getMirroredDirection(direction); - Function 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(); - } -} diff --git a/bot/src/main/java/haveno/cli/GrpcClient.java b/bot/src/main/java/haveno/cli/GrpcClient.java deleted file mode 100644 index 229104cc..00000000 --- a/bot/src/main/java/haveno/cli/GrpcClient.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli; - -import haveno.cli.request.OffersServiceRequest; -import haveno.cli.request.PaymentAccountsServiceRequest; -import haveno.cli.request.TradesServiceRequest; -import haveno.cli.request.WalletsServiceRequest; -import haveno.proto.grpc.AddressBalanceInfo; -import haveno.proto.grpc.BalancesInfo; -import haveno.proto.grpc.BtcBalanceInfo; -import haveno.proto.grpc.GetMethodHelpRequest; -import haveno.proto.grpc.GetTradesRequest; -import haveno.proto.grpc.GetVersionRequest; -import haveno.proto.grpc.OfferInfo; -import haveno.proto.grpc.RegisterDisputeAgentRequest; -import haveno.proto.grpc.StopRequest; -import haveno.proto.grpc.TradeInfo; -import lombok.extern.slf4j.Slf4j; -import protobuf.PaymentAccount; -import protobuf.PaymentMethod; - -import java.util.List; - - -@SuppressWarnings("ResultOfMethodCallIgnored") -@Slf4j -public final class GrpcClient { - - private final GrpcStubs grpcStubs; - private final OffersServiceRequest offersServiceRequest; - private final TradesServiceRequest tradesServiceRequest; - private final WalletsServiceRequest walletsServiceRequest; - private final PaymentAccountsServiceRequest paymentAccountsServiceRequest; - - public GrpcClient(String apiHost, - int apiPort, - String apiPassword) { - this.grpcStubs = new GrpcStubs(apiHost, apiPort, apiPassword); - this.offersServiceRequest = new OffersServiceRequest(grpcStubs); - this.tradesServiceRequest = new TradesServiceRequest(grpcStubs); - this.walletsServiceRequest = new WalletsServiceRequest(grpcStubs); - this.paymentAccountsServiceRequest = new PaymentAccountsServiceRequest(grpcStubs); - } - - public String getVersion() { - var request = GetVersionRequest.newBuilder().build(); - return grpcStubs.versionService.getVersion(request).getVersion(); - } - - public BalancesInfo getBalances() { - return walletsServiceRequest.getBalances(); - } - - public BtcBalanceInfo getBtcBalances() { - return walletsServiceRequest.getBtcBalances(); - } - - public BalancesInfo getBalances(String currencyCode) { - return walletsServiceRequest.getBalances(currencyCode); - } - - public AddressBalanceInfo getAddressBalance(String address) { - return walletsServiceRequest.getAddressBalance(address); - } - - public double getBtcPrice(String currencyCode) { - return walletsServiceRequest.getBtcPrice(currencyCode); - } - - public List getFundingAddresses() { - return walletsServiceRequest.getFundingAddresses(); - } - - public String getUnusedBtcAddress() { - return walletsServiceRequest.getUnusedBtcAddress(); - } - - public OfferInfo createFixedPricedOffer(String direction, - String currencyCode, - long amount, - long minAmount, - String fixedPrice, - double securityDepositPct, - String paymentAcctId) { - return offersServiceRequest.createOffer(direction, - currencyCode, - amount, - minAmount, - false, - fixedPrice, - 0.00, - securityDepositPct, - paymentAcctId, - "0" /* no trigger price */); - } - - public OfferInfo createMarketBasedPricedOffer(String direction, - String currencyCode, - long amount, - long minAmount, - double marketPriceMarginPct, - double securityDepositPct, - String paymentAcctId, - String triggerPrice) { - return offersServiceRequest.createOffer(direction, - currencyCode, - amount, - minAmount, - true, - "0", - marketPriceMarginPct, - securityDepositPct, - paymentAcctId, - triggerPrice); - } - - public OfferInfo createOffer(String direction, - String currencyCode, - long amount, - long minAmount, - boolean useMarketBasedPrice, - String fixedPrice, - double marketPriceMarginPct, - double securityDepositPct, - String paymentAcctId, - String triggerPrice) { - return offersServiceRequest.createOffer(direction, - currencyCode, - amount, - minAmount, - useMarketBasedPrice, - fixedPrice, - marketPriceMarginPct, - securityDepositPct, - paymentAcctId, - triggerPrice); - } - - public void cancelOffer(String offerId) { - offersServiceRequest.cancelOffer(offerId); - } - - public OfferInfo getOffer(String offerId) { - return offersServiceRequest.getOffer(offerId); - } - - @Deprecated // Since 5-Dec-2021. - // Endpoint to be removed from future version. Use getOffer service method instead. - public OfferInfo getMyOffer(String offerId) { - return offersServiceRequest.getMyOffer(offerId); - } - - public List getOffers(String direction, String currencyCode) { - return offersServiceRequest.getOffers(direction, currencyCode); - } - - public List getOffersSortedByDate(String currencyCode) { - return offersServiceRequest.getOffersSortedByDate(currencyCode); - } - - public List getOffersSortedByDate(String direction, String currencyCode) { - return offersServiceRequest.getOffersSortedByDate(direction, currencyCode); - } - - public List getMyOffers(String direction, String currencyCode) { - return offersServiceRequest.getMyOffers(direction, currencyCode); - } - - public List getMyOffersSortedByDate(String currencyCode) { - return offersServiceRequest.getMyOffersSortedByDate(currencyCode); - } - - public List getMyOffersSortedByDate(String direction, String currencyCode) { - return offersServiceRequest.getMyOffersSortedByDate(direction, currencyCode); - } - - public TradeInfo takeOffer(String offerId, String paymentAccountId) { - return tradesServiceRequest.takeOffer(offerId, paymentAccountId); - } - - public TradeInfo getTrade(String tradeId) { - return tradesServiceRequest.getTrade(tradeId); - } - - public List getOpenTrades() { - return tradesServiceRequest.getOpenTrades(); - } - - public List getTradeHistory(GetTradesRequest.Category category) { - return tradesServiceRequest.getTradeHistory(category); - } - - public void confirmPaymentSent(String tradeId) { - tradesServiceRequest.confirmPaymentSent(tradeId); - } - - public void confirmPaymentReceived(String tradeId) { - tradesServiceRequest.confirmPaymentReceived(tradeId); - } - - public void withdrawFunds(String tradeId, String address, String memo) { - tradesServiceRequest.withdrawFunds(tradeId, address, memo); - } - - public List getPaymentMethods() { - return paymentAccountsServiceRequest.getPaymentMethods(); - } - - public String getPaymentAcctFormAsJson(String paymentMethodId) { - return paymentAccountsServiceRequest.getPaymentAcctFormAsJson(paymentMethodId); - } - - public PaymentAccount createPaymentAccount(String json) { - return paymentAccountsServiceRequest.createPaymentAccount(json); - } - - public List getPaymentAccounts() { - return paymentAccountsServiceRequest.getPaymentAccounts(); - } - - public PaymentAccount getPaymentAccount(String accountName) { - return paymentAccountsServiceRequest.getPaymentAccount(accountName); - } - - public PaymentAccount createCryptoCurrencyPaymentAccount(String accountName, - String currencyCode, - String address, - boolean tradeInstant) { - return paymentAccountsServiceRequest.createCryptoCurrencyPaymentAccount(accountName, - currencyCode, - address, - tradeInstant); - } - - public List getCryptoPaymentMethods() { - return paymentAccountsServiceRequest.getCryptoPaymentMethods(); - } - - public void lockWallet() { - walletsServiceRequest.lockWallet(); - } - - public void unlockWallet(String walletPassword, long timeout) { - walletsServiceRequest.unlockWallet(walletPassword, timeout); - } - - public void removeWalletPassword(String walletPassword) { - walletsServiceRequest.removeWalletPassword(walletPassword); - } - - public void setWalletPassword(String walletPassword) { - walletsServiceRequest.setWalletPassword(walletPassword); - } - - public void setWalletPassword(String oldWalletPassword, String newWalletPassword) { - walletsServiceRequest.setWalletPassword(oldWalletPassword, newWalletPassword); - } - - 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(); - } -} diff --git a/bot/src/main/java/haveno/cli/GrpcStubs.java b/bot/src/main/java/haveno/cli/GrpcStubs.java deleted file mode 100644 index 41749617..00000000 --- a/bot/src/main/java/haveno/cli/GrpcStubs.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli; - -import haveno.proto.grpc.DisputeAgentsGrpc; -import haveno.proto.grpc.GetVersionGrpc; -import haveno.proto.grpc.HelpGrpc; -import haveno.proto.grpc.OffersGrpc; -import haveno.proto.grpc.PaymentAccountsGrpc; -import haveno.proto.grpc.PriceGrpc; -import haveno.proto.grpc.ShutdownServerGrpc; -import haveno.proto.grpc.TradesGrpc; -import haveno.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); - } -} diff --git a/bot/src/main/java/haveno/cli/Method.java b/bot/src/main/java/haveno/cli/Method.java deleted file mode 100644 index f83416c3..00000000 --- a/bot/src/main/java/haveno/cli/Method.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli; - -/** - * Currently supported api methods. - */ -public enum Method { - canceloffer, - closetrade, - confirmpaymentreceived, - confirmpaymentsent, - createoffer, - editoffer, - createpaymentacct, - createcryptopaymentacct, - getaddressbalance, - getbalance, - getbtcprice, - getfundingaddresses, - @Deprecated // Since 27-Dec-2021. - getmyoffer, // Endpoint to be removed from future version. Use getoffer instead. - getmyoffers, - getoffer, - getoffers, - getpaymentacctform, - getpaymentaccts, - getpaymentmethods, - gettrade, - gettrades, - failtrade, - unfailtrade, - gettransaction, - gettxfeerate, - getunusedbsqaddress, - getversion, - lockwallet, - registerdisputeagent, - removewalletpassword, - sendbtc, - settxfeerate, - setwalletpassword, - takeoffer, - unlockwallet, - unsettxfeerate, - withdrawfunds, - stop -} diff --git a/bot/src/main/java/haveno/cli/PasswordCallCredentials.java b/bot/src/main/java/haveno/cli/PasswordCallCredentials.java deleted file mode 100644 index f7809d1a..00000000 --- a/bot/src/main/java/haveno/cli/PasswordCallCredentials.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 . - */ - -package haveno.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() { - } -} diff --git a/bot/src/main/java/haveno/cli/TransactionFormat.java b/bot/src/main/java/haveno/cli/TransactionFormat.java deleted file mode 100644 index fc0b5ab9..00000000 --- a/bot/src/main/java/haveno/cli/TransactionFormat.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli; - -import com.google.common.annotations.VisibleForTesting; - - -@VisibleForTesting -public class TransactionFormat { - -} diff --git a/bot/src/main/java/haveno/cli/opts/AbstractMethodOptionParser.java b/bot/src/main/java/haveno/cli/opts/AbstractMethodOptionParser.java deleted file mode 100644 index 9ad9a374..00000000 --- a/bot/src/main/java/haveno/cli/opts/AbstractMethodOptionParser.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - -import joptsimple.OptionException; -import joptsimple.OptionParser; -import joptsimple.OptionSet; -import joptsimple.OptionSpec; -import lombok.Getter; - -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; - -import static haveno.cli.opts.OptLabel.OPT_HELP; -import static java.lang.String.format; - -@SuppressWarnings("unchecked") -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 helpOpt = parser.accepts(OPT_HELP, "Print method help").forHelp(); - - @Getter - protected OptionSet options; - @Getter - protected List nonOptionArguments; - - protected AbstractMethodOptionParser(String[] args) { - this.args = args; - } - - public AbstractMethodOptionParser parse() { - try { - options = parser.parse(new ArgumentList(args).getMethodArguments()); - nonOptionArguments = (List) options.nonOptionArguments(); - return this; - } catch (OptionException ex) { - throw new IllegalArgumentException(cliExceptionMessageStyle.apply(ex), ex); - } - } - - public boolean isForHelp() { - return options.has(helpOpt); - } - - protected void verifyStringIsValidDouble(String string) { - try { - Double.valueOf(string); - } catch (NumberFormatException ex) { - throw new IllegalArgumentException(format("%s is not a number", string)); - } - } - - protected final Predicate> valueNotSpecified = (opt) -> - !options.hasArgument(opt) || options.valueOf(opt).isEmpty(); - - private final Function 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; - }; -} diff --git a/bot/src/main/java/haveno/cli/opts/ArgumentList.java b/bot/src/main/java/haveno/cli/opts/ArgumentList.java deleted file mode 100644 index cf8ff977..00000000 --- a/bot/src/main/java/haveno/cli/opts/ArgumentList.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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 . - */ - -package haveno.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 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 isMethodNameOpt = (o) -> !o.startsWith("-"); - - private final Predicate 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 methodNameArgument = Optional.empty(); - Optional helpArgument = Optional.empty(); - List 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 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]; - } -} diff --git a/bot/src/main/java/haveno/cli/opts/CancelOfferOptionParser.java b/bot/src/main/java/haveno/cli/opts/CancelOfferOptionParser.java deleted file mode 100644 index b0439b5f..00000000 --- a/bot/src/main/java/haveno/cli/opts/CancelOfferOptionParser.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -public class CancelOfferOptionParser extends OfferIdOptionParser implements MethodOpts { - - public CancelOfferOptionParser(String[] args) { - super(args); - } - - public CancelOfferOptionParser parse() { - super.parse(); - - // Super class will short-circuit parsing if help option is present. - - return this; - } - - public String getOfferId() { - return options.valueOf(offerIdOpt); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java b/bot/src/main/java/haveno/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java deleted file mode 100644 index 9a65e7ca..00000000 --- a/bot/src/main/java/haveno/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.CryptoCurrencyUtil.apiDoesSupportCryptoCurrency; -import static haveno.cli.opts.OptLabel.OPT_ACCOUNT_NAME; -import static haveno.cli.opts.OptLabel.OPT_ADDRESS; -import static haveno.cli.opts.OptLabel.OPT_CURRENCY_CODE; -import static haveno.cli.opts.OptLabel.OPT_TRADE_INSTANT; -import static java.lang.String.format; - -public class CreateCryptoCurrencyPaymentAcctOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec accountNameOpt = parser.accepts(OPT_ACCOUNT_NAME, "crypto currency account name") - .withRequiredArg(); - - final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "crypto currency code (xmr)") - .withRequiredArg(); - - final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "crypto address") - .withRequiredArg(); - - final OptionSpec 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"); - - String cryptoCurrencyCode = options.valueOf(currencyCodeOpt); - if (!apiDoesSupportCryptoCurrency(cryptoCurrencyCode)) - throw new IllegalArgumentException(format("api does not support %s payment accounts", - cryptoCurrencyCode.toLowerCase())); - - if (!options.has(addressOpt) || options.valueOf(addressOpt).isEmpty()) - throw new IllegalArgumentException(format("no %s address specified", - cryptoCurrencyCode.toLowerCase())); - - 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); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/CreateOfferOptionParser.java b/bot/src/main/java/haveno/cli/opts/CreateOfferOptionParser.java deleted file mode 100644 index 610667cd..00000000 --- a/bot/src/main/java/haveno/cli/opts/CreateOfferOptionParser.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_AMOUNT; -import static haveno.cli.opts.OptLabel.OPT_CURRENCY_CODE; -import static haveno.cli.opts.OptLabel.OPT_DIRECTION; -import static haveno.cli.opts.OptLabel.OPT_FIXED_PRICE; -import static haveno.cli.opts.OptLabel.OPT_MIN_AMOUNT; -import static haveno.cli.opts.OptLabel.OPT_MKT_PRICE_MARGIN; -import static haveno.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_ID; -import static haveno.cli.opts.OptLabel.OPT_SECURITY_DEPOSIT; -import static joptsimple.internal.Strings.EMPTY; - -public class CreateOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT_ID, - "id of payment account used for offer") - .withRequiredArg() - .defaultsTo(EMPTY); - - final OptionSpec directionOpt = parser.accepts(OPT_DIRECTION, "offer direction (buy|sell)") - .withRequiredArg(); - - final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency code (xmr|eur|usd|...)") - .withRequiredArg(); - - final OptionSpec amountOpt = parser.accepts(OPT_AMOUNT, "amount of btc to buy or sell") - .withRequiredArg(); - - final OptionSpec minAmountOpt = parser.accepts(OPT_MIN_AMOUNT, "minimum amount of btc to buy or sell") - .withOptionalArg(); - - final OptionSpec mktPriceMarginPctOpt = parser.accepts(OPT_MKT_PRICE_MARGIN, "market btc price margin (%)") - .withOptionalArg() - .defaultsTo("0.00"); - - final OptionSpec fixedPriceOpt = parser.accepts(OPT_FIXED_PRICE, "fixed btc price") - .withOptionalArg() - .defaultsTo("0"); - - final OptionSpec securityDepositPctOpt = parser.accepts(OPT_SECURITY_DEPOSIT, "maker security deposit (%)") - .withRequiredArg(); - - public CreateOfferOptionParser(String[] args) { - super(args); - } - - @Override - public CreateOfferOptionParser 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"); - - if (!options.has(amountOpt) || options.valueOf(amountOpt).isEmpty()) - throw new IllegalArgumentException("no btc amount specified"); - - if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty()) - throw new IllegalArgumentException("no payment account id specified"); - - if (!options.has(mktPriceMarginPctOpt) && !options.has(fixedPriceOpt)) - throw new IllegalArgumentException("no market price margin or fixed price specified"); - - if (options.has(mktPriceMarginPctOpt)) { - var mktPriceMarginPctString = options.valueOf(mktPriceMarginPctOpt); - if (mktPriceMarginPctString.isEmpty()) - throw new IllegalArgumentException("no market price margin specified"); - else - verifyStringIsValidDouble(mktPriceMarginPctString); - } - - if (options.has(fixedPriceOpt) && options.valueOf(fixedPriceOpt).isEmpty()) - throw new IllegalArgumentException("no fixed price specified"); - - if (!options.has(securityDepositPctOpt) || options.valueOf(securityDepositPctOpt).isEmpty()) - throw new IllegalArgumentException("no security deposit specified"); - else - verifyStringIsValidDouble(options.valueOf(securityDepositPctOpt)); - - 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(mktPriceMarginPctOpt); - } - - public double getMktPriceMarginPct() { - return isUsingMktPriceMargin() ? Double.parseDouble(options.valueOf(mktPriceMarginPctOpt)) : 0.00d; - } - - public String getFixedPrice() { - return options.has(fixedPriceOpt) ? options.valueOf(fixedPriceOpt) : "0.00"; - } - - public double getSecurityDepositPct() { - return Double.valueOf(options.valueOf(securityDepositPctOpt)); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/CreatePaymentAcctOptionParser.java b/bot/src/main/java/haveno/cli/opts/CreatePaymentAcctOptionParser.java deleted file mode 100644 index 8070a834..00000000 --- a/bot/src/main/java/haveno/cli/opts/CreatePaymentAcctOptionParser.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import static haveno.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_FORM; -import static java.lang.String.format; - -public class CreatePaymentAcctOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec 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)); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/GetAddressBalanceOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetAddressBalanceOptionParser.java deleted file mode 100644 index f0f9ff1c..00000000 --- a/bot/src/main/java/haveno/cli/opts/GetAddressBalanceOptionParser.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_ADDRESS; - -public class GetAddressBalanceOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec 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); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/GetBTCMarketPriceOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetBTCMarketPriceOptionParser.java deleted file mode 100644 index 8efc94be..00000000 --- a/bot/src/main/java/haveno/cli/opts/GetBTCMarketPriceOptionParser.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_CURRENCY_CODE; - -public class GetBTCMarketPriceOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec 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); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/GetBalanceOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetBalanceOptionParser.java deleted file mode 100644 index 9fa008c3..00000000 --- a/bot/src/main/java/haveno/cli/opts/GetBalanceOptionParser.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_CURRENCY_CODE; -import static joptsimple.internal.Strings.EMPTY; - -public class GetBalanceOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "wallet currency code (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) : ""; - } -} diff --git a/bot/src/main/java/haveno/cli/opts/GetOfferOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetOfferOptionParser.java deleted file mode 100644 index 961674de..00000000 --- a/bot/src/main/java/haveno/cli/opts/GetOfferOptionParser.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_OFFER_ID; - -public class GetOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec 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); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/GetOffersOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetOffersOptionParser.java deleted file mode 100644 index a0665255..00000000 --- a/bot/src/main/java/haveno/cli/opts/GetOffersOptionParser.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_CURRENCY_CODE; -import static haveno.cli.opts.OptLabel.OPT_DIRECTION; - -public class GetOffersOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec directionOpt = parser.accepts(OPT_DIRECTION, "offer direction (buy|sell)") - .withRequiredArg(); - - final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency code (xmr|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); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/GetPaymentAcctFormOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetPaymentAcctFormOptionParser.java deleted file mode 100644 index 101bca8e..00000000 --- a/bot/src/main/java/haveno/cli/opts/GetPaymentAcctFormOptionParser.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_PAYMENT_METHOD_ID; - -public class GetPaymentAcctFormOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec 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); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/GetTradeOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetTradeOptionParser.java deleted file mode 100644 index 372bf3fb..00000000 --- a/bot/src/main/java/haveno/cli/opts/GetTradeOptionParser.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_SHOW_CONTRACT; -import static haveno.cli.opts.OptLabel.OPT_TRADE_ID; - -public class GetTradeOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec tradeIdOpt = parser.accepts(OPT_TRADE_ID, "id of trade") - .withRequiredArg(); - - final OptionSpec 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; - } -} diff --git a/bot/src/main/java/haveno/cli/opts/GetTradesOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetTradesOptionParser.java deleted file mode 100644 index 9d7ca04b..00000000 --- a/bot/src/main/java/haveno/cli/opts/GetTradesOptionParser.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import haveno.proto.grpc.GetTradesRequest; -import joptsimple.OptionSpec; - -import java.util.function.Predicate; - -import static haveno.cli.opts.OptLabel.OPT_CATEGORY; -import static haveno.proto.grpc.GetTradesRequest.Category.CLOSED; -import static haveno.proto.grpc.GetTradesRequest.Category.FAILED; -import static haveno.proto.grpc.GetTradesRequest.Category.OPEN; -import static java.util.Arrays.stream; - -public class GetTradesOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - // Map valid CLI option values to gRPC request parameters. - private enum CATEGORY { - // Lower case enum fits CLI method and parameter style. - open(OPEN), - closed(CLOSED), - failed(FAILED); - - private final GetTradesRequest.Category grpcRequestCategory; - - CATEGORY(GetTradesRequest.Category grpcRequestCategory) { - this.grpcRequestCategory = grpcRequestCategory; - } - } - - final OptionSpec categoryOpt = parser.accepts(OPT_CATEGORY, - "category of trades (open|closed|failed)") - .withRequiredArg() - .defaultsTo(CATEGORY.open.name()); - - private final Predicate isValidCategory = (c) -> - stream(CATEGORY.values()).anyMatch(v -> v.name().equalsIgnoreCase(c)); - - public GetTradesOptionParser(String[] args) { - super(args); - } - - public GetTradesOptionParser parse() { - super.parse(); - - // Short circuit opt validation if user just wants help. - if (options.has(helpOpt)) - return this; - - if (options.has(categoryOpt)) { - String category = options.valueOf(categoryOpt); - if (category.isEmpty()) - throw new IllegalArgumentException("no category (open|closed|failed) specified"); - - if (!isValidCategory.test(category)) - throw new IllegalArgumentException("category must be open|closed|failed"); - } - - return this; - } - - public GetTradesRequest.Category getCategory() { - String categoryOpt = options.valueOf(this.categoryOpt).toLowerCase(); - return CATEGORY.valueOf(categoryOpt).grpcRequestCategory; - } -} diff --git a/bot/src/main/java/haveno/cli/opts/GetTransactionOptionParser.java b/bot/src/main/java/haveno/cli/opts/GetTransactionOptionParser.java deleted file mode 100644 index 32bbf0f8..00000000 --- a/bot/src/main/java/haveno/cli/opts/GetTransactionOptionParser.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_TRANSACTION_ID; - -public class GetTransactionOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec 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); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/MethodOpts.java b/bot/src/main/java/haveno/cli/opts/MethodOpts.java deleted file mode 100644 index 167cec87..00000000 --- a/bot/src/main/java/haveno/cli/opts/MethodOpts.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - -public interface MethodOpts { - - MethodOpts parse(); - - boolean isForHelp(); -} diff --git a/bot/src/main/java/haveno/cli/opts/OfferIdOptionParser.java b/bot/src/main/java/haveno/cli/opts/OfferIdOptionParser.java deleted file mode 100644 index 4161e5e0..00000000 --- a/bot/src/main/java/haveno/cli/opts/OfferIdOptionParser.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_OFFER_ID; - -/** - * Superclass for option parsers requiring an offer-id. Avoids a small amount of - * duplicated boilerplate. - */ -public class OfferIdOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer") - .withRequiredArg(); - - public OfferIdOptionParser(String[] args) { - this(args, false); - } - - public OfferIdOptionParser(String[] args, boolean allowsUnrecognizedOptions) { - super(args); - if (allowsUnrecognizedOptions) - this.parser.allowsUnrecognizedOptions(); - } - - public OfferIdOptionParser 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); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/OptLabel.java b/bot/src/main/java/haveno/cli/opts/OptLabel.java deleted file mode 100644 index 79ad2ae1..00000000 --- a/bot/src/main/java/haveno/cli/opts/OptLabel.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 . - */ - -package haveno.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_CATEGORY = "category"; - 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_ENABLE = "enable"; - 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_ID = "payment-account-id"; - 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_TRIGGER_PRICE = "trigger-price"; - 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"; -} diff --git a/bot/src/main/java/haveno/cli/opts/RegisterDisputeAgentOptionParser.java b/bot/src/main/java/haveno/cli/opts/RegisterDisputeAgentOptionParser.java deleted file mode 100644 index c9c22926..00000000 --- a/bot/src/main/java/haveno/cli/opts/RegisterDisputeAgentOptionParser.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_DISPUTE_AGENT_TYPE; -import static haveno.cli.opts.OptLabel.OPT_REGISTRATION_KEY; - -public class RegisterDisputeAgentOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec disputeAgentTypeOpt = parser.accepts(OPT_DISPUTE_AGENT_TYPE, "dispute agent type") - .withRequiredArg(); - - final OptionSpec 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); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/RemoveWalletPasswordOptionParser.java b/bot/src/main/java/haveno/cli/opts/RemoveWalletPasswordOptionParser.java deleted file mode 100644 index 4483af94..00000000 --- a/bot/src/main/java/haveno/cli/opts/RemoveWalletPasswordOptionParser.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_WALLET_PASSWORD; - -public class RemoveWalletPasswordOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "haveno 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); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/SendBtcOptionParser.java b/bot/src/main/java/haveno/cli/opts/SendBtcOptionParser.java deleted file mode 100644 index 12eec3bc..00000000 --- a/bot/src/main/java/haveno/cli/opts/SendBtcOptionParser.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_ADDRESS; -import static haveno.cli.opts.OptLabel.OPT_AMOUNT; -import static haveno.cli.opts.OptLabel.OPT_MEMO; -import static haveno.cli.opts.OptLabel.OPT_TX_FEE_RATE; -import static joptsimple.internal.Strings.EMPTY; - -public class SendBtcOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "destination btc address") - .withRequiredArg(); - - final OptionSpec amountOpt = parser.accepts(OPT_AMOUNT, "amount of btc to send") - .withRequiredArg(); - - final OptionSpec feeRateOpt = parser.accepts(OPT_TX_FEE_RATE, "optional tx fee rate (sats/byte)") - .withOptionalArg() - .defaultsTo(EMPTY); - - final OptionSpec 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) : ""; - } -} diff --git a/bot/src/main/java/haveno/cli/opts/SetTxFeeRateOptionParser.java b/bot/src/main/java/haveno/cli/opts/SetTxFeeRateOptionParser.java deleted file mode 100644 index 3fbd9ab3..00000000 --- a/bot/src/main/java/haveno/cli/opts/SetTxFeeRateOptionParser.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_TX_FEE_RATE; - -public class SetTxFeeRateOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec 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); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/SetWalletPasswordOptionParser.java b/bot/src/main/java/haveno/cli/opts/SetWalletPasswordOptionParser.java deleted file mode 100644 index 4b180fe3..00000000 --- a/bot/src/main/java/haveno/cli/opts/SetWalletPasswordOptionParser.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_NEW_WALLET_PASSWORD; -import static haveno.cli.opts.OptLabel.OPT_WALLET_PASSWORD; -import static joptsimple.internal.Strings.EMPTY; - -public class SetWalletPasswordOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "haveno wallet password") - .withRequiredArg(); - - final OptionSpec newPasswordOpt = parser.accepts(OPT_NEW_WALLET_PASSWORD, "new haveno 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) : ""; - } -} diff --git a/bot/src/main/java/haveno/cli/opts/SimpleMethodOptionParser.java b/bot/src/main/java/haveno/cli/opts/SimpleMethodOptionParser.java deleted file mode 100644 index 93ee2ce7..00000000 --- a/bot/src/main/java/haveno/cli/opts/SimpleMethodOptionParser.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -public class SimpleMethodOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - public SimpleMethodOptionParser(String[] args) { - super(args); - } - - public SimpleMethodOptionParser parse() { - return (SimpleMethodOptionParser) super.parse(); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/TakeOfferOptionParser.java b/bot/src/main/java/haveno/cli/opts/TakeOfferOptionParser.java deleted file mode 100644 index e45ea776..00000000 --- a/bot/src/main/java/haveno/cli/opts/TakeOfferOptionParser.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_ID; - -public class TakeOfferOptionParser extends OfferIdOptionParser implements MethodOpts { - - final OptionSpec paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT_ID, "id of payment account used for trade") - .withRequiredArg(); - - public TakeOfferOptionParser(String[] args) { - super(args, true); - } - - public TakeOfferOptionParser parse() { - super.parse(); - - // Super class will short-circuit parsing if help option is present. - - if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty()) - throw new IllegalArgumentException("no payment account id specified"); - - return this; - } - - public String getPaymentAccountId() { - return options.valueOf(paymentAccountIdOpt); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/UnlockWalletOptionParser.java b/bot/src/main/java/haveno/cli/opts/UnlockWalletOptionParser.java deleted file mode 100644 index dad3f826..00000000 --- a/bot/src/main/java/haveno/cli/opts/UnlockWalletOptionParser.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_TIMEOUT; -import static haveno.cli.opts.OptLabel.OPT_WALLET_PASSWORD; - -public class UnlockWalletOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "haveno wallet password") - .withRequiredArg(); - - final OptionSpec 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); - } -} diff --git a/bot/src/main/java/haveno/cli/opts/WithdrawFundsOptionParser.java b/bot/src/main/java/haveno/cli/opts/WithdrawFundsOptionParser.java deleted file mode 100644 index 96702994..00000000 --- a/bot/src/main/java/haveno/cli/opts/WithdrawFundsOptionParser.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.opts; - - -import joptsimple.OptionSpec; - -import static haveno.cli.opts.OptLabel.OPT_ADDRESS; -import static haveno.cli.opts.OptLabel.OPT_MEMO; -import static haveno.cli.opts.OptLabel.OPT_TRADE_ID; -import static joptsimple.internal.Strings.EMPTY; - -public class WithdrawFundsOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec tradeIdOpt = parser.accepts(OPT_TRADE_ID, "id of trade") - .withRequiredArg(); - - final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "destination btc address") - .withRequiredArg(); - - final OptionSpec 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) : ""; - } -} diff --git a/bot/src/main/java/haveno/cli/request/OffersServiceRequest.java b/bot/src/main/java/haveno/cli/request/OffersServiceRequest.java deleted file mode 100644 index 2fcb3426..00000000 --- a/bot/src/main/java/haveno/cli/request/OffersServiceRequest.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.request; - -import haveno.cli.GrpcStubs; -import haveno.proto.grpc.CancelOfferRequest; -import haveno.proto.grpc.GetMyOfferRequest; -import haveno.proto.grpc.GetMyOffersRequest; -import haveno.proto.grpc.GetOfferRequest; -import haveno.proto.grpc.GetOffersRequest; -import haveno.proto.grpc.OfferInfo; -import haveno.proto.grpc.PostOfferRequest; - -import java.util.ArrayList; -import java.util.List; - -import static java.util.Comparator.comparing; -import static java.util.stream.Collectors.toList; -import static protobuf.OfferDirection.BUY; -import static protobuf.OfferDirection.SELL; - -public class OffersServiceRequest { - - private final GrpcStubs grpcStubs; - - public OffersServiceRequest(GrpcStubs grpcStubs) { - this.grpcStubs = grpcStubs; - } - - @SuppressWarnings("unused") - public OfferInfo createFixedPricedOffer(String direction, - String currencyCode, - long amount, - long minAmount, - String fixedPrice, - double securityDepositPct, - String paymentAcctId, - String makerFeeCurrencyCode) { - return createOffer(direction, - currencyCode, - amount, - minAmount, - false, - fixedPrice, - 0.00, - securityDepositPct, - paymentAcctId, - "0" /* no trigger price */); - } - - public OfferInfo createOffer(String direction, - String currencyCode, - long amount, - long minAmount, - boolean useMarketBasedPrice, - String fixedPrice, - double marketPriceMarginPct, - double securityDepositPct, - String paymentAcctId, - String triggerPrice) { - var request = PostOfferRequest.newBuilder() - .setDirection(direction) - .setCurrencyCode(currencyCode) - .setAmount(amount) - .setMinAmount(minAmount) - .setUseMarketBasedPrice(useMarketBasedPrice) - .setPrice(fixedPrice) - .setMarketPriceMarginPct(marketPriceMarginPct) - .setSecurityDepositPct(securityDepositPct) - .setPaymentAccountId(paymentAcctId) - .setTriggerPrice(triggerPrice) - .build(); - return grpcStubs.offersService.postOffer(request).getOffer(); - } - - public void cancelOffer(String offerId) { - var request = CancelOfferRequest.newBuilder() - .setId(offerId) - .build(); - //noinspection ResultOfMethodCallIgnored - 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 getOffers(String direction, String currencyCode) { - var request = GetOffersRequest.newBuilder() - .setDirection(direction) - .setCurrencyCode(currencyCode) - .build(); - return grpcStubs.offersService.getOffers(request).getOffersList(); - } - - public List getOffersSortedByDate(String currencyCode) { - ArrayList offers = new ArrayList<>(); - offers.addAll(getOffers(BUY.name(), currencyCode)); - offers.addAll(getOffers(SELL.name(), currencyCode)); - return offers.isEmpty() ? offers : sortOffersByDate(offers); - } - - public List getOffersSortedByDate(String direction, String currencyCode) { - var offers = getOffers(direction, currencyCode); - return offers.isEmpty() ? offers : sortOffersByDate(offers); - } - - public List getMyOffers(String direction, String currencyCode) { - var request = GetMyOffersRequest.newBuilder() - .setDirection(direction) - .setCurrencyCode(currencyCode) - .build(); - return grpcStubs.offersService.getMyOffers(request).getOffersList(); - } - - public List getMyOffersSortedByDate(String currencyCode) { - ArrayList offers = new ArrayList<>(); - offers.addAll(getMyOffers(BUY.name(), currencyCode)); - offers.addAll(getMyOffers(SELL.name(), currencyCode)); - return offers.isEmpty() ? offers : sortOffersByDate(offers); - } - - public List getMyOffersSortedByDate(String direction, String currencyCode) { - var offers = getMyOffers(direction, currencyCode); - return offers.isEmpty() ? offers : sortOffersByDate(offers); - } - - public OfferInfo getMostRecentOffer(String direction, String currencyCode) { - List offers = getOffersSortedByDate(direction, currencyCode); - return offers.isEmpty() ? null : offers.get(offers.size() - 1); - } - - public List sortOffersByDate(List offerInfoList) { - return offerInfoList.stream() - .sorted(comparing(OfferInfo::getDate)) - .collect(toList()); - } -} diff --git a/bot/src/main/java/haveno/cli/request/PaymentAccountsServiceRequest.java b/bot/src/main/java/haveno/cli/request/PaymentAccountsServiceRequest.java deleted file mode 100644 index 4aaa53eb..00000000 --- a/bot/src/main/java/haveno/cli/request/PaymentAccountsServiceRequest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.request; - -import haveno.cli.GrpcStubs; -import haveno.proto.grpc.CreateCryptoCurrencyPaymentAccountRequest; -import haveno.proto.grpc.CreatePaymentAccountRequest; -import haveno.proto.grpc.GetCryptoCurrencyPaymentMethodsRequest; -import haveno.proto.grpc.GetPaymentAccountFormAsJsonRequest; -import haveno.proto.grpc.GetPaymentAccountsRequest; -import haveno.proto.grpc.GetPaymentMethodsRequest; -import protobuf.PaymentAccount; -import protobuf.PaymentMethod; - -import java.util.List; - -import static java.lang.String.format; - -public class PaymentAccountsServiceRequest { - - private final GrpcStubs grpcStubs; - - public PaymentAccountsServiceRequest(GrpcStubs grpcStubs) { - this.grpcStubs = grpcStubs; - } - - public List getPaymentMethods() { - var request = GetPaymentMethodsRequest.newBuilder().build(); - return grpcStubs.paymentAccountsService.getPaymentMethods(request).getPaymentMethodsList(); - } - - public String getPaymentAcctFormAsJson(String paymentMethodId) { - var request = GetPaymentAccountFormAsJsonRequest.newBuilder() - .setPaymentMethodId(paymentMethodId) - .build(); - return grpcStubs.paymentAccountsService.getPaymentAccountFormAsJson(request).getPaymentAccountFormAsJson(); - } - - public PaymentAccount createPaymentAccount(String json) { - var request = CreatePaymentAccountRequest.newBuilder() - .setPaymentAccountFormAsJson(json) - .build(); - return grpcStubs.paymentAccountsService.createPaymentAccount(request).getPaymentAccount(); - } - - public List getPaymentAccounts() { - var request = GetPaymentAccountsRequest.newBuilder().build(); - return grpcStubs.paymentAccountsService.getPaymentAccounts(request).getPaymentAccountsList(); - } - - /** - * Returns the first PaymentAccount found with the given name, or throws an - * IllegalArgumentException if not found. This method should be used with care; - * it will only return one PaymentAccount, and the account name must be an exact - * match on the name argument. - * @param accountName the name of the stored PaymentAccount to retrieve - * @return PaymentAccount with given name - */ - public PaymentAccount getPaymentAccount(String accountName) { - return getPaymentAccounts().stream() - .filter(a -> a.getAccountName().equals(accountName)).findFirst() - .orElseThrow(() -> - new IllegalArgumentException(format("payment account with name '%s' not found", - accountName))); - } - - 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 getCryptoPaymentMethods() { - var request = GetCryptoCurrencyPaymentMethodsRequest.newBuilder().build(); - return grpcStubs.paymentAccountsService.getCryptoCurrencyPaymentMethods(request).getPaymentMethodsList(); - } -} diff --git a/bot/src/main/java/haveno/cli/request/TradesServiceRequest.java b/bot/src/main/java/haveno/cli/request/TradesServiceRequest.java deleted file mode 100644 index e4826771..00000000 --- a/bot/src/main/java/haveno/cli/request/TradesServiceRequest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.request; - -import haveno.cli.GrpcStubs; -import haveno.proto.grpc.ConfirmPaymentReceivedRequest; -import haveno.proto.grpc.ConfirmPaymentSentRequest; -import haveno.proto.grpc.GetTradeRequest; -import haveno.proto.grpc.GetTradesRequest; -import haveno.proto.grpc.TakeOfferReply; -import haveno.proto.grpc.TakeOfferRequest; -import haveno.proto.grpc.TradeInfo; -import haveno.proto.grpc.WithdrawFundsRequest; - -import java.util.List; - -import static haveno.proto.grpc.GetTradesRequest.Category.CLOSED; -import static haveno.proto.grpc.GetTradesRequest.Category.FAILED; - -public class TradesServiceRequest { - - private final GrpcStubs grpcStubs; - - public TradesServiceRequest(GrpcStubs grpcStubs) { - this.grpcStubs = grpcStubs; - } - - public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId) { - var request = TakeOfferRequest.newBuilder() - .setOfferId(offerId) - .setPaymentAccountId(paymentAccountId) - .build(); - return grpcStubs.tradesService.takeOffer(request); - } - - public TradeInfo takeOffer(String offerId, String paymentAccountId) { - var reply = getTakeOfferReply(offerId, paymentAccountId); - 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 List getOpenTrades() { - var request = GetTradesRequest.newBuilder() - .build(); - return grpcStubs.tradesService.getTrades(request).getTradesList(); - } - - public List getTradeHistory(GetTradesRequest.Category category) { - if (!category.equals(CLOSED) && !category.equals(FAILED)) - throw new IllegalStateException("unrecognized gettrades category parameter " + category.name()); - - var request = GetTradesRequest.newBuilder() - .setCategory(category) - .build(); - return grpcStubs.tradesService.getTrades(request).getTradesList(); - } - - public void confirmPaymentSent(String tradeId) { - var request = ConfirmPaymentSentRequest.newBuilder() - .setTradeId(tradeId) - .build(); - //noinspection ResultOfMethodCallIgnored - grpcStubs.tradesService.confirmPaymentSent(request); - } - - public void confirmPaymentReceived(String tradeId) { - var request = ConfirmPaymentReceivedRequest.newBuilder() - .setTradeId(tradeId) - .build(); - //noinspection ResultOfMethodCallIgnored - grpcStubs.tradesService.confirmPaymentReceived(request); - } - - public void withdrawFunds(String tradeId, String address, String memo) { - var request = WithdrawFundsRequest.newBuilder() - .setTradeId(tradeId) - .setAddress(address) - .setMemo(memo) - .build(); - //noinspection ResultOfMethodCallIgnored - grpcStubs.tradesService.withdrawFunds(request); - } -} diff --git a/bot/src/main/java/haveno/cli/request/WalletsServiceRequest.java b/bot/src/main/java/haveno/cli/request/WalletsServiceRequest.java deleted file mode 100644 index dde9ce25..00000000 --- a/bot/src/main/java/haveno/cli/request/WalletsServiceRequest.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.request; - -import haveno.cli.GrpcStubs; -import haveno.proto.grpc.AddressBalanceInfo; -import haveno.proto.grpc.BalancesInfo; -import haveno.proto.grpc.BtcBalanceInfo; -import haveno.proto.grpc.GetAddressBalanceRequest; -import haveno.proto.grpc.GetBalancesRequest; -import haveno.proto.grpc.GetFundingAddressesRequest; -import haveno.proto.grpc.LockWalletRequest; -import haveno.proto.grpc.MarketPriceRequest; -import haveno.proto.grpc.RemoveWalletPasswordRequest; -import haveno.proto.grpc.SetWalletPasswordRequest; -import haveno.proto.grpc.UnlockWalletRequest; - -import java.util.List; - -public class WalletsServiceRequest { - - private final GrpcStubs grpcStubs; - - public WalletsServiceRequest(GrpcStubs grpcStubs) { - this.grpcStubs = grpcStubs; - } - - public BalancesInfo getBalances() { - return getBalances(""); - } - - 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 getFundingAddresses() { - var request = GetFundingAddressesRequest.newBuilder().build(); - return grpcStubs.walletsService.getFundingAddresses(request).getAddressBalanceInfoList(); - } - - 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 void lockWallet() { - var request = LockWalletRequest.newBuilder().build(); - //noinspection ResultOfMethodCallIgnored - grpcStubs.walletsService.lockWallet(request); - } - - public void unlockWallet(String walletPassword, long timeout) { - var request = UnlockWalletRequest.newBuilder() - .setPassword(walletPassword) - .setTimeout(timeout).build(); - //noinspection ResultOfMethodCallIgnored - grpcStubs.walletsService.unlockWallet(request); - } - - public void removeWalletPassword(String walletPassword) { - var request = RemoveWalletPasswordRequest.newBuilder() - .setPassword(walletPassword).build(); - //noinspection ResultOfMethodCallIgnored - grpcStubs.walletsService.removeWalletPassword(request); - } - - public void setWalletPassword(String walletPassword) { - var request = SetWalletPasswordRequest.newBuilder() - .setPassword(walletPassword).build(); - //noinspection ResultOfMethodCallIgnored - grpcStubs.walletsService.setWalletPassword(request); - } - - public void setWalletPassword(String oldWalletPassword, String newWalletPassword) { - var request = SetWalletPasswordRequest.newBuilder() - .setPassword(oldWalletPassword) - .setNewPassword(newWalletPassword).build(); - //noinspection ResultOfMethodCallIgnored - grpcStubs.walletsService.setWalletPassword(request); - } -} diff --git a/bot/src/main/java/haveno/cli/table/Table.java b/bot/src/main/java/haveno/cli/table/Table.java deleted file mode 100644 index 66a14c40..00000000 --- a/bot/src/main/java/haveno/cli/table/Table.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table; - -import haveno.cli.table.column.Column; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.stream.IntStream; - -import static com.google.common.base.Strings.padStart; -import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; -import static java.lang.String.format; -import static java.nio.charset.StandardCharsets.UTF_8; - -/** - * A simple table of formatted data for the CLI's output console. A table must be - * created with at least one populated column, and each column passed to the constructor - * must contain the same number of rows. Null checking is omitted because tables are - * populated by protobuf message fields which cannot be null. - * - * All data in a column has the same type: long, string, etc., but a table - * may contain an arbitrary number of columns of any type. For output formatting - * purposes, numeric and date columns should be transformed to a StringColumn type with - * formatted and justified string values before being passed to the constructor. - * - * This is not a relational, rdbms table. - */ -public class Table { - - public final Column[] columns; - public final int rowCount; - - // Each printed column is delimited by two spaces. - private final int columnDelimiterLength = 2; - - /** - * Default constructor. Takes populated Columns. - * - * @param columns containing the same number of rows - */ - public Table(Column... columns) { - this.columns = columns; - this.rowCount = columns.length > 0 ? columns[0].rowCount() : 0; - validateStructure(); - } - - /** - * Print table data to a PrintStream. - * - * @param printStream the target output stream - */ - public void print(PrintStream printStream) { - printColumnNames(printStream); - for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) { - printRow(printStream, rowIndex); - } - } - - /** - * Print table column names to a PrintStream. - * - * @param printStream the target output stream - */ - private void printColumnNames(PrintStream printStream) { - IntStream.range(0, columns.length).forEachOrdered(colIndex -> { - var c = columns[colIndex]; - var justifiedName = c.getJustification().equals(RIGHT) - ? padStart(c.getName(), c.getWidth(), ' ') - : c.getName(); - var paddedWidth = colIndex == columns.length - 1 - ? c.getName().length() - : c.getWidth() + columnDelimiterLength; - printStream.printf("%-" + paddedWidth + "s", justifiedName); - }); - printStream.println(); - } - - /** - * Print a table row to a PrintStream. - * - * @param printStream the target output stream - */ - private void printRow(PrintStream printStream, int rowIndex) { - IntStream.range(0, columns.length).forEachOrdered(colIndex -> { - var c = columns[colIndex]; - var paddedWidth = colIndex == columns.length - 1 - ? c.getWidth() - : c.getWidth() + columnDelimiterLength; - printStream.printf("%-" + paddedWidth + "s", c.getRow(rowIndex)); - if (colIndex == columns.length - 1) - printStream.println(); - }); - } - - /** - * Returns the table's formatted output as a String. - * @return String - */ - @Override - public String toString() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (PrintStream ps = new PrintStream(baos, true, UTF_8)) { - print(ps); - } - return baos.toString(); - } - - /** - * Verifies the table has columns, and each column has the same number of rows. - */ - private void validateStructure() { - if (columns.length == 0) - throw new IllegalArgumentException("Table has no columns."); - - if (columns[0].isEmpty()) - throw new IllegalArgumentException( - format("Table's 1st column (%s) has no data.", - columns[0].getName())); - - IntStream.range(1, columns.length).forEachOrdered(colIndex -> { - var c = columns[colIndex]; - - if (c.isEmpty()) - throw new IllegalStateException( - format("Table column # %d (%s) does not have any data.", - colIndex + 1, - c.getName())); - - if (this.rowCount != c.rowCount()) - throw new IllegalStateException( - format("Table column # %d (%s) does not have same number of rows as 1st column.", - colIndex + 1, - c.getName())); - }); - } -} diff --git a/bot/src/main/java/haveno/cli/table/builder/AbstractTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/AbstractTableBuilder.java deleted file mode 100644 index 17cf2f71..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/AbstractTableBuilder.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.Table; -import haveno.proto.grpc.OfferInfo; - -import java.util.List; -import java.util.function.Predicate; - -/** - * Abstract superclass for TableBuilder implementations. - */ -abstract class AbstractTableBuilder { - - protected final Predicate isTraditionalOffer = (o) -> o.getBaseCurrencyCode().equals("XMR"); - - protected final TableType tableType; - protected final List protos; - - AbstractTableBuilder(TableType tableType, List protos) { - this.tableType = tableType; - this.protos = protos; - if (protos.isEmpty()) - throw new IllegalArgumentException("cannot build a table without rows"); - } - - public abstract Table build(); -} diff --git a/bot/src/main/java/haveno/cli/table/builder/AbstractTradeListBuilder.java b/bot/src/main/java/haveno/cli/table/builder/AbstractTradeListBuilder.java deleted file mode 100644 index b279f68b..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/AbstractTradeListBuilder.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.column.Column; -import haveno.cli.table.column.MixedTradeFeeColumn; -import haveno.proto.grpc.ContractInfo; -import haveno.proto.grpc.TradeInfo; - -import javax.annotation.Nullable; -import java.math.BigDecimal; -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import static haveno.cli.CurrencyFormat.formatSatoshis; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_BUYER_DEPOSIT; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_SELLER_DEPOSIT; -import static haveno.cli.table.builder.TableType.TRADE_DETAIL_TBL; -import static java.lang.String.format; -import static protobuf.OfferDirection.SELL; - -abstract class AbstractTradeListBuilder extends AbstractTableBuilder { - - protected final List trades; - - protected final TradeTableColumnSupplier colSupplier; - - protected final Column colTradeId; - @Nullable - protected final Column colCreateDate; - @Nullable - protected final Column colMarket; - protected final Column colPrice; - @Nullable - protected final Column colPriceDeviation; - @Nullable - protected final Column colCurrency; - @Nullable - protected final Column colAmount; - @Nullable - protected final Column colMixedAmount; - @Nullable - protected final MixedTradeFeeColumn colMixedTradeFee; - @Nullable - protected final Column colBuyerDeposit; - @Nullable - protected final Column colSellerDeposit; - @Nullable - protected final Column colPaymentMethod; - @Nullable - protected final Column colRole; - @Nullable - protected final Column colOfferType; - @Nullable - protected final Column colClosingStatus; - - // Trade detail tbl specific columns - - @Nullable - protected final Column colIsDepositPublished; - @Nullable - protected final Column colIsDepositConfirmed; - @Nullable - protected final Column colIsPayoutPublished; - @Nullable - protected final Column colIsCompleted; - @Nullable - protected final Column colHavenoTradeFee; - @Nullable - protected final Column colTradeCost; - @Nullable - protected final Column colIsPaymentSentMessageSent; - @Nullable - protected final Column colIsPaymentReceivedMessageSent; - @Nullable - protected final Column colCryptoReceiveAddressColumn; - - AbstractTradeListBuilder(TableType tableType, List protos) { - super(tableType, protos); - validate(); - - this.trades = protos.stream().map(p -> (TradeInfo) p).collect(Collectors.toList()); - this.colSupplier = new TradeTableColumnSupplier(tableType, trades); - - this.colTradeId = colSupplier.tradeIdColumn.get(); - this.colCreateDate = colSupplier.createDateColumn.get(); - this.colMarket = colSupplier.marketColumn.get(); - this.colPrice = colSupplier.priceColumn.get(); - this.colPriceDeviation = colSupplier.priceDeviationColumn.get(); - this.colCurrency = colSupplier.currencyColumn.get(); - this.colAmount = colSupplier.amountColumn.get(); - this.colMixedAmount = colSupplier.mixedAmountColumn.get(); - this.colMixedTradeFee = colSupplier.mixedTradeFeeColumn.get(); - this.colBuyerDeposit = colSupplier.toSecurityDepositColumn.apply(COL_HEADER_BUYER_DEPOSIT); - this.colSellerDeposit = colSupplier.toSecurityDepositColumn.apply(COL_HEADER_SELLER_DEPOSIT); - this.colPaymentMethod = colSupplier.paymentMethodColumn.get(); - this.colRole = colSupplier.roleColumn.get(); - this.colOfferType = colSupplier.offerTypeColumn.get(); - this.colClosingStatus = colSupplier.statusDescriptionColumn.get(); - - // Trade detail specific columns, some in common with BSQ swap trades detail. - - this.colIsDepositPublished = colSupplier.depositPublishedColumn.get(); - this.colIsDepositConfirmed = colSupplier.depositConfirmedColumn.get(); - this.colIsPayoutPublished = colSupplier.payoutPublishedColumn.get(); - this.colIsCompleted = colSupplier.fundsWithdrawnColumn.get(); - this.colHavenoTradeFee = colSupplier.havenoTradeDetailFeeColumn.get(); - this.colTradeCost = colSupplier.tradeCostColumn.get(); - this.colIsPaymentSentMessageSent = colSupplier.paymentSentMessageSentColumn.get(); - this.colIsPaymentReceivedMessageSent = colSupplier.paymentReceivedMessageSentColumn.get(); - //noinspection ConstantConditions - this.colCryptoReceiveAddressColumn = colSupplier.cryptoReceiveAddressColumn.get(); - } - - protected void validate() { - if (isTradeDetailTblBuilder.get()) { - if (protos.size() != 1) - throw new IllegalArgumentException("trade detail tbl can have only one row"); - } else if (protos.isEmpty()) { - throw new IllegalArgumentException("trade tbl has no rows"); - } - } - - // Helper Functions - - private final Supplier isTradeDetailTblBuilder = () -> tableType.equals(TRADE_DETAIL_TBL); - protected final Predicate isTraditionalTrade = (t) -> isTraditionalOffer.test(t.getOffer()); - protected final Predicate isMyOffer = (t) -> t.getOffer().getIsMyOffer(); - protected final Predicate isTaker = (t) -> t.getRole().toLowerCase().contains("taker"); - protected final Predicate isSellOffer = (t) -> t.getOffer().getDirection().equals(SELL.name()); - protected final Predicate isBtcSeller = (t) -> (isMyOffer.test(t) && isSellOffer.test(t)) - || (!isMyOffer.test(t) && !isSellOffer.test(t)); - - - // Column Value Functions - - // Crypto volumes from server are string representations of decimals. - // Converting them to longs ("sats") requires shifting the decimal points - // to left: 2 for BSQ, 8 for other cryptos. - protected final Function toCryptoTradeVolumeAsLong = (t) -> new BigDecimal(t.getTradeVolume()).movePointRight(8).longValue(); - - protected final Function toTradeVolumeAsString = (t) -> - isTraditionalTrade.test(t) - ? t.getTradeVolume() - : formatSatoshis(t.getAmount()); - - protected final Function toTradeVolumeAsLong = (t) -> - isTraditionalTrade.test(t) - ? Long.parseLong(t.getTradeVolume()) - : toCryptoTradeVolumeAsLong.apply(t); - - protected final Function toTradeAmount = (t) -> - isTraditionalTrade.test(t) - ? t.getAmount() - : toTradeVolumeAsLong.apply(t); - - protected final Function toMarket = (t) -> - t.getOffer().getBaseCurrencyCode() + "/" - + t.getOffer().getCounterCurrencyCode(); - - protected final Function toPaymentCurrencyCode = (t) -> - isTraditionalTrade.test(t) - ? t.getOffer().getCounterCurrencyCode() - : t.getOffer().getBaseCurrencyCode(); - - protected final Function toPriceDeviation = (t) -> - t.getOffer().getUseMarketBasedPrice() - ? format("%.2f%s", t.getOffer().getMarketPriceMarginPct(), "%") - : "N/A"; - - protected final Function toTradeFeeBtc = (t) -> { - var isMyOffer = t.getOffer().getIsMyOffer(); - if (isMyOffer) { - return t.getMakerFee(); - } else { - return t.getTakerFee(); - } - }; - - protected final Function toMyMakerOrTakerFee = (t) -> { - return isTaker.test(t) - ? t.getTakerFee() - : t.getMakerFee(); - }; - - protected final Function toOfferType = (t) -> { - if (isTraditionalTrade.test(t)) { - return t.getOffer().getDirection() + " " + t.getOffer().getBaseCurrencyCode(); - } else { - if (t.getOffer().getDirection().equals("BUY")) { - return "SELL " + t.getOffer().getBaseCurrencyCode(); - } else { - return "BUY " + t.getOffer().getBaseCurrencyCode(); - } - } - }; - - protected final Predicate showCryptoBuyerAddress = (t) -> { - if (isTraditionalTrade.test(t)) { - return false; - } else { - ContractInfo contract = t.getContract(); - boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker(); - if (isTaker.test(t)) { - return !isBuyerMakerAndSellerTaker; - } else { - return isBuyerMakerAndSellerTaker; - } - } - }; - - protected final Function toCryptoReceiveAddress = (t) -> { - if (showCryptoBuyerAddress.test(t)) { - ContractInfo contract = t.getContract(); - boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker(); - return isBuyerMakerAndSellerTaker // (is BTC buyer / maker) - ? contract.getTakerPaymentAccountPayload().getCryptoCurrencyAccountPayload().getAddress() - : contract.getMakerPaymentAccountPayload().getCryptoCurrencyAccountPayload().getAddress(); - } else { - return ""; - } - }; -} diff --git a/bot/src/main/java/haveno/cli/table/builder/AddressBalanceTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/AddressBalanceTableBuilder.java deleted file mode 100644 index 4c26d30c..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/AddressBalanceTableBuilder.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.Table; -import haveno.cli.table.column.BooleanColumn; -import haveno.cli.table.column.Column; -import haveno.cli.table.column.LongColumn; -import haveno.cli.table.column.SatoshiColumn; -import haveno.cli.table.column.StringColumn; -import haveno.proto.grpc.AddressBalanceInfo; - -import java.util.List; -import java.util.stream.Collectors; - -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_ADDRESS; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_AVAILABLE_BALANCE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_CONFIRMATIONS; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_IS_USED_ADDRESS; -import static haveno.cli.table.builder.TableType.ADDRESS_BALANCE_TBL; -import static java.lang.String.format; - -/** - * Builds a {@code haveno.cli.table.Table} from a List of - * {@code haveno.proto.grpc.AddressBalanceInfo} objects. - */ -class AddressBalanceTableBuilder extends AbstractTableBuilder { - - // Default columns not dynamically generated with address info. - private final Column colAddress; - private final Column colAvailableBalance; - private final Column colConfirmations; - private final Column colIsUsed; - - AddressBalanceTableBuilder(List protos) { - super(ADDRESS_BALANCE_TBL, protos); - colAddress = new StringColumn(format(COL_HEADER_ADDRESS, "BTC")); - this.colAvailableBalance = new SatoshiColumn(COL_HEADER_AVAILABLE_BALANCE); - this.colConfirmations = new LongColumn(COL_HEADER_CONFIRMATIONS); - this.colIsUsed = new BooleanColumn(COL_HEADER_IS_USED_ADDRESS); - } - - public Table build() { - List addresses = protos.stream() - .map(a -> (AddressBalanceInfo) a) - .collect(Collectors.toList()); - - // Populate columns with address info. - //noinspection SimplifyStreamApiCallChains - addresses.stream().forEachOrdered(a -> { - colAddress.addRow(a.getAddress()); - colAvailableBalance.addRow(a.getBalance()); - colConfirmations.addRow(a.getNumConfirmations()); - colIsUsed.addRow(!a.getIsAddressUnused()); - }); - - // Define and return the table instance with populated columns. - return new Table(colAddress, - colAvailableBalance.asStringColumn(), - colConfirmations.asStringColumn(), - colIsUsed.asStringColumn()); - } -} diff --git a/bot/src/main/java/haveno/cli/table/builder/BtcBalanceTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/BtcBalanceTableBuilder.java deleted file mode 100644 index 41f3a18f..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/BtcBalanceTableBuilder.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.Table; -import haveno.cli.table.column.Column; -import haveno.cli.table.column.SatoshiColumn; -import haveno.proto.grpc.BtcBalanceInfo; - -import java.util.List; - -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_AVAILABLE_BALANCE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_LOCKED_BALANCE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_RESERVED_BALANCE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TOTAL_AVAILABLE_BALANCE; -import static haveno.cli.table.builder.TableType.BTC_BALANCE_TBL; - -/** - * Builds a {@code haveno.cli.table.Table} from a - * {@code haveno.proto.grpc.BtcBalanceInfo} object. - */ -class BtcBalanceTableBuilder extends AbstractTableBuilder { - - // Default columns not dynamically generated with btc balance info. - private final Column colAvailableBalance; - private final Column colReservedBalance; - private final Column colTotalAvailableBalance; - private final Column colLockedBalance; - - BtcBalanceTableBuilder(List protos) { - super(BTC_BALANCE_TBL, protos); - this.colAvailableBalance = new SatoshiColumn(COL_HEADER_AVAILABLE_BALANCE); - this.colReservedBalance = new SatoshiColumn(COL_HEADER_RESERVED_BALANCE); - this.colTotalAvailableBalance = new SatoshiColumn(COL_HEADER_TOTAL_AVAILABLE_BALANCE); - this.colLockedBalance = new SatoshiColumn(COL_HEADER_LOCKED_BALANCE); - } - - @Override - public Table build() { - BtcBalanceInfo balance = (BtcBalanceInfo) protos.get(0); - - // Populate columns with btc balance info. - - colAvailableBalance.addRow(balance.getAvailableBalance()); - colReservedBalance.addRow(balance.getReservedBalance()); - colTotalAvailableBalance.addRow(balance.getTotalAvailableBalance()); - colLockedBalance.addRow(balance.getLockedBalance()); - - // Define and return the table instance with populated columns. - - return new Table(colAvailableBalance.asStringColumn(), - colReservedBalance.asStringColumn(), - colTotalAvailableBalance.asStringColumn(), - colLockedBalance.asStringColumn()); - } -} diff --git a/bot/src/main/java/haveno/cli/table/builder/ClosedTradeTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/ClosedTradeTableBuilder.java deleted file mode 100644 index c57baf63..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/ClosedTradeTableBuilder.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.Table; - -import java.util.List; - -import static haveno.cli.table.builder.TableType.CLOSED_TRADES_TBL; - -@SuppressWarnings("ConstantConditions") -class ClosedTradeTableBuilder extends AbstractTradeListBuilder { - - ClosedTradeTableBuilder(List protos) { - super(CLOSED_TRADES_TBL, protos); - } - - @Override - public Table build() { - populateColumns(); - return new Table(colTradeId, - colCreateDate.asStringColumn(), - colMarket, - colPrice.justify(), - colPriceDeviation.justify(), - colAmount.asStringColumn(), - colMixedAmount.justify(), - colCurrency, - colMixedTradeFee.asStringColumn(), - colBuyerDeposit.asStringColumn(), - colSellerDeposit.asStringColumn(), - colOfferType, - colClosingStatus); - } - - private void populateColumns() { - trades.forEach(t -> { - colTradeId.addRow(t.getTradeId()); - colCreateDate.addRow(t.getDate()); - colMarket.addRow(toMarket.apply(t)); - colPrice.addRow(t.getPrice()); - colPriceDeviation.addRow(toPriceDeviation.apply(t)); - colAmount.addRow(t.getAmount()); - colMixedAmount.addRow(t.getTradeVolume()); - colCurrency.addRow(toPaymentCurrencyCode.apply(t)); - - colMixedTradeFee.addRow(toTradeFeeBtc.apply(t), false); - - colBuyerDeposit.addRow(t.getOffer().getBuyerSecurityDepositPct()); - colSellerDeposit.addRow(t.getOffer().getSellerSecurityDepositPct()); - colOfferType.addRow(toOfferType.apply(t)); - }); - } -} diff --git a/bot/src/main/java/haveno/cli/table/builder/FailedTradeTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/FailedTradeTableBuilder.java deleted file mode 100644 index 9f30cfe8..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/FailedTradeTableBuilder.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.Table; - -import java.util.List; - -import static haveno.cli.table.builder.TableType.FAILED_TRADES_TBL; - -/** - * Builds a {@code haveno.cli.table.Table} from a list of {@code haveno.proto.grpc.TradeInfo} objects. - */ -@SuppressWarnings("ConstantConditions") -class FailedTradeTableBuilder extends AbstractTradeListBuilder { - - FailedTradeTableBuilder(List protos) { - super(FAILED_TRADES_TBL, protos); - } - - public Table build() { - populateColumns(); - return new Table(colTradeId, - colCreateDate.asStringColumn(), - colMarket, - colPrice.justify(), - colAmount.asStringColumn(), - colMixedAmount.justify(), - colCurrency, - colOfferType, - colRole, - colClosingStatus); - } - - private void populateColumns() { - trades.forEach(t -> { - colTradeId.addRow(t.getTradeId()); - colCreateDate.addRow(t.getDate()); - colMarket.addRow(toMarket.apply(t)); - colPrice.addRow(t.getPrice()); - colAmount.addRow(t.getAmount()); - colMixedAmount.addRow(t.getTradeVolume()); - colCurrency.addRow(toPaymentCurrencyCode.apply(t)); - colOfferType.addRow(toOfferType.apply(t)); - colRole.addRow(t.getRole()); - colClosingStatus.addRow("Failed"); - }); - } -} diff --git a/bot/src/main/java/haveno/cli/table/builder/OfferTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/OfferTableBuilder.java deleted file mode 100644 index 23b93ca6..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/OfferTableBuilder.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.Table; -import haveno.cli.table.column.Column; -import haveno.cli.table.column.Iso8601DateTimeColumn; -import haveno.cli.table.column.SatoshiColumn; -import haveno.cli.table.column.StringColumn; -import haveno.cli.table.column.ZippedStringColumns; -import haveno.proto.grpc.OfferInfo; - -import javax.annotation.Nullable; -import java.util.List; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_AMOUNT_RANGE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_CREATION_DATE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DETAILED_PRICE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DETAILED_PRICE_OF_CRYPTO; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DIRECTION; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_ENABLED; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_PAYMENT_METHOD; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRIGGER_PRICE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_UUID; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_VOLUME_RANGE; -import static haveno.cli.table.builder.TableType.OFFER_TBL; -import static haveno.cli.table.column.Column.JUSTIFICATION.LEFT; -import static haveno.cli.table.column.Column.JUSTIFICATION.NONE; -import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; -import static haveno.cli.table.column.ZippedStringColumns.DUPLICATION_MODE.EXCLUDE_DUPLICATES; -import static java.lang.String.format; -import static protobuf.OfferDirection.BUY; -import static protobuf.OfferDirection.SELL; - -/** - * Builds a {@code haveno.cli.table.Table} from a List of - * {@code haveno.proto.grpc.OfferInfo} objects. - */ -class OfferTableBuilder extends AbstractTableBuilder { - - // Columns common to both traditional and cryptocurrency offers. - private final Column colOfferId = new StringColumn(COL_HEADER_UUID, LEFT); - private final Column colDirection = new StringColumn(COL_HEADER_DIRECTION, LEFT); - private final Column colAmount = new SatoshiColumn("Temp Amount", NONE); - private final Column colMinAmount = new SatoshiColumn("Temp Min Amount", NONE); - private final Column colPaymentMethod = new StringColumn(COL_HEADER_PAYMENT_METHOD, LEFT); - private final Column colCreateDate = new Iso8601DateTimeColumn(COL_HEADER_CREATION_DATE); - - OfferTableBuilder(List protos) { - super(OFFER_TBL, protos); - } - - @Override - public Table build() { - List offers = protos.stream().map(p -> (OfferInfo) p).collect(Collectors.toList()); - return isShowingTraditionalOffers.get() - ? buildTraditionalOfferTable(offers) - : buildCryptoCurrencyOfferTable(offers); - } - - @SuppressWarnings("ConstantConditions") - public Table buildTraditionalOfferTable(List offers) { - @Nullable - Column colEnabled = enabledColumn.get(); // Not boolean: "YES", "NO", or "PENDING" - Column colTraditionalPrice = new StringColumn(format(COL_HEADER_DETAILED_PRICE, traditionalTradeCurrency.get()), RIGHT); - Column colVolume = new StringColumn(format("Temp Volume (%s)", traditionalTradeCurrency.get()), NONE); - Column colMinVolume = new StringColumn(format("Temp Min Volume (%s)", traditionalTradeCurrency.get()), NONE); - @Nullable - Column colTriggerPrice = traditionalTriggerPriceColumn.get(); - - // Populate columns with offer info. - - offers.forEach(o -> { - if (colEnabled != null) - colEnabled.addRow(toEnabled.apply(o)); - - colDirection.addRow(o.getDirection()); - colTraditionalPrice.addRow(o.getPrice()); - colMinAmount.addRow(o.getMinAmount()); - colAmount.addRow(o.getAmount()); - colVolume.addRow(o.getVolume()); - colMinVolume.addRow(o.getMinVolume()); - - if (colTriggerPrice != null) - colTriggerPrice.addRow(toBlankOrNonZeroValue.apply(o.getTriggerPrice())); - - colPaymentMethod.addRow(o.getPaymentMethodShortName()); - colCreateDate.addRow(o.getDate()); - colOfferId.addRow(o.getId()); - }); - - ZippedStringColumns amountRange = zippedAmountRangeColumns.get(); - ZippedStringColumns volumeRange = - new ZippedStringColumns(format(COL_HEADER_VOLUME_RANGE, traditionalTradeCurrency.get()), - RIGHT, - " - ", - colMinVolume.asStringColumn(), - colVolume.asStringColumn()); - - // Define and return the table instance with populated columns. - - if (isShowingMyOffers.get()) { - return new Table(colEnabled.asStringColumn(), - colDirection, - colTraditionalPrice.justify(), - amountRange.asStringColumn(EXCLUDE_DUPLICATES), - volumeRange.asStringColumn(EXCLUDE_DUPLICATES), - colTriggerPrice.justify(), - colPaymentMethod, - colCreateDate.asStringColumn(), - colOfferId); - } else { - return new Table(colDirection, - colTraditionalPrice.justify(), - amountRange.asStringColumn(EXCLUDE_DUPLICATES), - volumeRange.asStringColumn(EXCLUDE_DUPLICATES), - colPaymentMethod, - colCreateDate.asStringColumn(), - colOfferId); - } - } - - @SuppressWarnings("ConstantConditions") - public Table buildCryptoCurrencyOfferTable(List offers) { - @Nullable - Column colEnabled = enabledColumn.get(); // Not boolean: YES, NO, or PENDING - Column colBtcPrice = new StringColumn(format(COL_HEADER_DETAILED_PRICE_OF_CRYPTO, cryptoTradeCurrency.get()), RIGHT); - Column colVolume = new StringColumn(format("Temp Volume (%s)", cryptoTradeCurrency.get()), NONE); - Column colMinVolume = new StringColumn(format("Temp Min Volume (%s)", cryptoTradeCurrency.get()), NONE); - @Nullable - Column colTriggerPrice = cryptoTriggerPriceColumn.get(); - - // Populate columns with offer info. - - offers.forEach(o -> { - if (colEnabled != null) - colEnabled.addRow(toEnabled.apply(o)); - - colDirection.addRow(directionFormat.apply(o)); - colBtcPrice.addRow(o.getPrice()); - colAmount.addRow(o.getAmount()); - colMinAmount.addRow(o.getMinAmount()); - colVolume.addRow(o.getVolume()); - colMinVolume.addRow(o.getMinVolume()); - - if (colTriggerPrice != null) - colTriggerPrice.addRow(toBlankOrNonZeroValue.apply(o.getTriggerPrice())); - - colPaymentMethod.addRow(o.getPaymentMethodShortName()); - colCreateDate.addRow(o.getDate()); - colOfferId.addRow(o.getId()); - }); - - ZippedStringColumns amountRange = zippedAmountRangeColumns.get(); - ZippedStringColumns volumeRange = - new ZippedStringColumns(format(COL_HEADER_VOLUME_RANGE, cryptoTradeCurrency.get()), - RIGHT, - " - ", - colMinVolume.asStringColumn(), - colVolume.asStringColumn()); - - // Define and return the table instance with populated columns. - - if (isShowingMyOffers.get()) { - if (isShowingBsqOffers.get()) { - return new Table(colEnabled.asStringColumn(), - colDirection, - colBtcPrice.justify(), - amountRange.asStringColumn(EXCLUDE_DUPLICATES), - volumeRange.asStringColumn(EXCLUDE_DUPLICATES), - colPaymentMethod, - colCreateDate.asStringColumn(), - colOfferId); - } else { - return new Table(colEnabled.asStringColumn(), - colDirection, - colBtcPrice.justify(), - amountRange.asStringColumn(EXCLUDE_DUPLICATES), - volumeRange.asStringColumn(EXCLUDE_DUPLICATES), - colTriggerPrice.justify(), - colPaymentMethod, - colCreateDate.asStringColumn(), - colOfferId); - } - } else { - return new Table(colDirection, - colBtcPrice.justify(), - amountRange.asStringColumn(EXCLUDE_DUPLICATES), - volumeRange.asStringColumn(EXCLUDE_DUPLICATES), - colPaymentMethod, - colCreateDate.asStringColumn(), - colOfferId); - } - } - - private final Function toBlankOrNonZeroValue = (s) -> s.trim().equals("0") ? "" : s; - private final Supplier firstOfferInList = () -> (OfferInfo) protos.get(0); - private final Supplier isShowingMyOffers = () -> firstOfferInList.get().getIsMyOffer(); - private final Supplier isShowingTraditionalOffers = () -> isTraditionalOffer.test(firstOfferInList.get()); - private final Supplier traditionalTradeCurrency = () -> firstOfferInList.get().getCounterCurrencyCode(); - private final Supplier cryptoTradeCurrency = () -> firstOfferInList.get().getBaseCurrencyCode(); - private final Supplier isShowingBsqOffers = () -> - !isTraditionalOffer.test(firstOfferInList.get()) && cryptoTradeCurrency.get().equals("BSQ"); - - @Nullable // Not a boolean column: YES, NO, or PENDING. - private final Supplier enabledColumn = () -> - isShowingMyOffers.get() - ? new StringColumn(COL_HEADER_ENABLED, LEFT) - : null; - @Nullable - private final Supplier traditionalTriggerPriceColumn = () -> - isShowingMyOffers.get() - ? new StringColumn(format(COL_HEADER_TRIGGER_PRICE, traditionalTradeCurrency.get()), RIGHT) - : null; - @Nullable - private final Supplier cryptoTriggerPriceColumn = () -> - isShowingMyOffers.get() && !isShowingBsqOffers.get() - ? new StringColumn(format(COL_HEADER_TRIGGER_PRICE, cryptoTradeCurrency.get()), RIGHT) - : null; - - private final Function toEnabled = (o) -> { - return o.getIsActivated() ? "YES" : "NO"; - }; - - private final Function toMirroredDirection = (d) -> - d.equalsIgnoreCase(BUY.name()) ? SELL.name() : BUY.name(); - - private final Function directionFormat = (o) -> { - if (isTraditionalOffer.test(o)) { - return o.getBaseCurrencyCode(); - } else { - // Return "Sell BSQ (Buy BTC)", or "Buy BSQ (Sell BTC)". - String direction = o.getDirection(); - String mirroredDirection = toMirroredDirection.apply(direction); - Function mixedCase = (word) -> word.charAt(0) + word.substring(1).toLowerCase(); - return format("%s %s (%s %s)", - mixedCase.apply(mirroredDirection), - o.getBaseCurrencyCode(), - mixedCase.apply(direction), - o.getCounterCurrencyCode()); - } - }; - - private final Supplier zippedAmountRangeColumns = () -> { - if (colMinAmount.isEmpty() || colAmount.isEmpty()) - throw new IllegalStateException("amount columns must have data"); - - return new ZippedStringColumns(COL_HEADER_AMOUNT_RANGE, - RIGHT, - " - ", - colMinAmount.asStringColumn(), - colAmount.asStringColumn()); - }; -} diff --git a/bot/src/main/java/haveno/cli/table/builder/OpenTradeTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/OpenTradeTableBuilder.java deleted file mode 100644 index dab9e09a..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/OpenTradeTableBuilder.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.Table; - -import java.util.List; - -import static haveno.cli.table.builder.TableType.OPEN_TRADES_TBL; - -/** - * Builds a {@code haveno.cli.table.Table} from a list of {@code haveno.proto.grpc.TradeInfo} objects. - */ -@SuppressWarnings("ConstantConditions") -class OpenTradeTableBuilder extends AbstractTradeListBuilder { - - OpenTradeTableBuilder(List protos) { - super(OPEN_TRADES_TBL, protos); - } - - public Table build() { - populateColumns(); - return new Table(colTradeId, - colCreateDate.asStringColumn(), - colMarket, - colPrice.justify(), - colAmount.asStringColumn(), - colMixedAmount.justify(), - colCurrency, - colPaymentMethod, - colRole); - } - - private void populateColumns() { - trades.forEach(t -> { - colTradeId.addRow(t.getTradeId()); - colCreateDate.addRow(t.getDate()); - colMarket.addRow(toMarket.apply(t)); - colPrice.addRow(t.getPrice()); - colAmount.addRow(t.getAmount()); - colMixedAmount.addRow(t.getTradeVolume()); - colCurrency.addRow(toPaymentCurrencyCode.apply(t)); - colPaymentMethod.addRow(t.getOffer().getPaymentMethodShortName()); - colRole.addRow(t.getRole()); - }); - } -} diff --git a/bot/src/main/java/haveno/cli/table/builder/PaymentAccountTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/PaymentAccountTableBuilder.java deleted file mode 100644 index 3cfbfcaa..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/PaymentAccountTableBuilder.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.Table; -import haveno.cli.table.column.Column; -import haveno.cli.table.column.StringColumn; -import protobuf.PaymentAccount; - -import java.util.List; -import java.util.stream.Collectors; - -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_CURRENCY; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_NAME; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_PAYMENT_METHOD; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_UUID; -import static haveno.cli.table.builder.TableType.PAYMENT_ACCOUNT_TBL; - -/** - * Builds a {@code haveno.cli.table.Table} from a List of - * {@code protobuf.PaymentAccount} objects. - */ -class PaymentAccountTableBuilder extends AbstractTableBuilder { - - // Default columns not dynamically generated with payment account info. - private final Column colName; - private final Column colCurrency; - private final Column colPaymentMethod; - private final Column colId; - - PaymentAccountTableBuilder(List protos) { - super(PAYMENT_ACCOUNT_TBL, protos); - this.colName = new StringColumn(COL_HEADER_NAME); - this.colCurrency = new StringColumn(COL_HEADER_CURRENCY); - this.colPaymentMethod = new StringColumn(COL_HEADER_PAYMENT_METHOD); - this.colId = new StringColumn(COL_HEADER_UUID); - } - - public Table build() { - List paymentAccounts = protos.stream() - .map(a -> (PaymentAccount) a) - .collect(Collectors.toList()); - - // Populate columns with payment account info. - //noinspection SimplifyStreamApiCallChains - paymentAccounts.stream().forEachOrdered(a -> { - colName.addRow(a.getAccountName()); - colCurrency.addRow(a.getSelectedTradeCurrency().getCode()); - colPaymentMethod.addRow(a.getPaymentMethod().getId()); - colId.addRow(a.getId()); - }); - - // Define and return the table instance with populated columns. - return new Table(colName, colCurrency, colPaymentMethod, colId); - } -} diff --git a/bot/src/main/java/haveno/cli/table/builder/TableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/TableBuilder.java deleted file mode 100644 index cff4dde6..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/TableBuilder.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.Table; - -import java.util.List; - -import static java.util.Collections.singletonList; - -/** - * Table builder factory. It is not conventionally named TableBuilderFactory because - * it has no static factory methods. The number of static fields and methods in the - * {@code haveno.cli.table} are kept to a minimum in an effort o reduce class load time - * in the session-less CLI. - */ -public class TableBuilder extends AbstractTableBuilder { - - public TableBuilder(TableType tableType, Object proto) { - this(tableType, singletonList(proto)); - } - - public TableBuilder(TableType tableType, List protos) { - super(tableType, protos); - } - - @Override - public Table build() { - switch (tableType) { - case ADDRESS_BALANCE_TBL: - return new AddressBalanceTableBuilder(protos).build(); - case BTC_BALANCE_TBL: - return new BtcBalanceTableBuilder(protos).build(); - case CLOSED_TRADES_TBL: - return new ClosedTradeTableBuilder(protos).build(); - case FAILED_TRADES_TBL: - return new FailedTradeTableBuilder(protos).build(); - case OFFER_TBL: - return new OfferTableBuilder(protos).build(); - case OPEN_TRADES_TBL: - return new OpenTradeTableBuilder(protos).build(); - case PAYMENT_ACCOUNT_TBL: - return new PaymentAccountTableBuilder(protos).build(); - case TRADE_DETAIL_TBL: - return new TradeDetailTableBuilder(protos).build(); - case TRANSACTION_TBL: - return new TransactionTableBuilder(protos).build(); - default: - throw new IllegalArgumentException("invalid cli table type " + tableType.name()); - } - } -} diff --git a/bot/src/main/java/haveno/cli/table/builder/TableBuilderConstants.java b/bot/src/main/java/haveno/cli/table/builder/TableBuilderConstants.java deleted file mode 100644 index 893f9f8b..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/TableBuilderConstants.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -/** - * Table column name constants. - */ -class TableBuilderConstants { - static final String COL_HEADER_ADDRESS = "%-3s Address"; - static final String COL_HEADER_AMOUNT = "Amount"; - static final String COL_HEADER_AMOUNT_IN_BTC = "Amount in BTC"; - static final String COL_HEADER_AMOUNT_RANGE = "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_BSQ_SWAP_TRADE_ROLE = "My BSQ Swap Role"; - static final String COL_HEADER_BUYER_DEPOSIT = "Buyer Deposit (BTC)"; - static final String COL_HEADER_SELLER_DEPOSIT = "Seller Deposit (BTC)"; - static final String COL_HEADER_CONFIRMATIONS = "Confirmations"; - static final String COL_HEADER_DEVIATION = "Deviation"; - static final String COL_HEADER_IS_USED_ADDRESS = "Is Used"; - static final String COL_HEADER_CREATION_DATE = "Creation Date (UTC)"; - static final String COL_HEADER_CURRENCY = "Currency"; - static final String COL_HEADER_DATE_TIME = "Date/Time (UTC)"; - static final String COL_HEADER_DETAILED_AMOUNT = "Amount(%-3s)"; - static final String COL_HEADER_DETAILED_PRICE = "Price in %-3s for 1 BTC"; - static final String COL_HEADER_DETAILED_PRICE_OF_CRYPTO = "Price in BTC for 1 %-3s"; - static final String COL_HEADER_DIRECTION = "Buy/Sell"; - static final String COL_HEADER_ENABLED = "Enabled"; - static final String COL_HEADER_MARKET = "Market"; - static final String COL_HEADER_NAME = "Name"; - static final String COL_HEADER_OFFER_TYPE = "Offer Type"; - static final String COL_HEADER_PAYMENT_METHOD = "Payment Method"; - static final String COL_HEADER_PRICE = "Price"; - static final String COL_HEADER_STATUS = "Status"; - static final String COL_HEADER_TRADE_CRYPTO_BUYER_ADDRESS = "%-3s Buyer Address"; - static final String COL_HEADER_TRADE_BUYER_COST = "Buyer Cost(%-3s)"; - 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 = "%-3s Sent"; - static final String COL_HEADER_TRADE_PAYMENT_RECEIVED = "%-3s Received"; - static final String COL_HEADER_TRADE_PAYOUT_PUBLISHED = "Payout Published"; - static final String COL_HEADER_TRADE_WITHDRAWN = "Withdrawn"; - static final String COL_HEADER_TRADE_ID = "Trade ID"; - static final String COL_HEADER_TRADE_ROLE = "My Role"; - static final String COL_HEADER_TRADE_SHORT_ID = "ID"; - static final String COL_HEADER_TRADE_MAKER_FEE = "Maker Fee(%-3s)"; - static final String COL_HEADER_TRADE_TAKER_FEE = "Taker Fee(%-3s)"; - static final String COL_HEADER_TRADE_FEE = "Trade Fee"; - static final String COL_HEADER_TRIGGER_PRICE = "Trigger Price(%-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_RANGE = "%-3s(min - max)"; - static final String COL_HEADER_UUID = "ID"; -} diff --git a/bot/src/main/java/haveno/cli/table/builder/TableType.java b/bot/src/main/java/haveno/cli/table/builder/TableType.java deleted file mode 100644 index 871240ab..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/TableType.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -/** - * Used as param in TableBuilder constructor instead of inspecting - * protos to find out what kind of CLI output table should be built. - */ -public enum TableType { - ADDRESS_BALANCE_TBL, - BTC_BALANCE_TBL, - CLOSED_TRADES_TBL, - FAILED_TRADES_TBL, - OFFER_TBL, - OPEN_TRADES_TBL, - PAYMENT_ACCOUNT_TBL, - TRADE_DETAIL_TBL, - TRANSACTION_TBL -} diff --git a/bot/src/main/java/haveno/cli/table/builder/TradeDetailTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/TradeDetailTableBuilder.java deleted file mode 100644 index bf8a0c2b..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/TradeDetailTableBuilder.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.Table; -import haveno.cli.table.column.Column; -import haveno.proto.grpc.TradeInfo; - -import java.util.ArrayList; -import java.util.List; - -import static haveno.cli.table.builder.TableType.TRADE_DETAIL_TBL; - -/** - * Builds a {@code haveno.cli.table.Table} from a {@code haveno.proto.grpc.TradeInfo} object. - */ -@SuppressWarnings("ConstantConditions") -class TradeDetailTableBuilder extends AbstractTradeListBuilder { - - TradeDetailTableBuilder(List protos) { - super(TRADE_DETAIL_TBL, protos); - } - - /** - * Build a single row trade detail table. - * @return Table containing one row - */ - @Override - public Table build() { - // A trade detail table only has one row. - var trade = trades.get(0); - populateColumns(trade); - List> columns = defineColumnList(trade); - return new Table(columns.toArray(new Column[0])); - } - - private void populateColumns(TradeInfo trade) { - populateHavenoV1TradeColumns(trade); - } - - private void populateHavenoV1TradeColumns(TradeInfo trade) { - colTradeId.addRow(trade.getShortId()); - colRole.addRow(trade.getRole()); - colPrice.addRow(trade.getPrice()); - colAmount.addRow(toTradeAmount.apply(trade)); - colHavenoTradeFee.addRow(toMyMakerOrTakerFee.apply(trade)); - colIsDepositPublished.addRow(trade.getIsDepositsPublished()); - colIsDepositConfirmed.addRow(trade.getIsDepositsUnlocked()); - colTradeCost.addRow(toTradeVolumeAsString.apply(trade)); - colIsPaymentSentMessageSent.addRow(trade.getIsPaymentSent()); - colIsPaymentReceivedMessageSent.addRow(trade.getIsPaymentReceived()); - colIsPayoutPublished.addRow(trade.getIsPayoutPublished()); - colIsCompleted.addRow(trade.getIsCompleted()); - if (colCryptoReceiveAddressColumn != null) - colCryptoReceiveAddressColumn.addRow(toCryptoReceiveAddress.apply(trade)); - } - - private List> defineColumnList(TradeInfo trade) { - return getHavenoV1TradeColumnList(); - } - - private List> getHavenoV1TradeColumnList() { - List> columns = new ArrayList<>() {{ - add(colTradeId); - add(colRole); - add(colPrice.justify()); - add(colAmount.asStringColumn()); - add(colHavenoTradeFee.asStringColumn()); - add(colIsDepositPublished.asStringColumn()); - add(colIsDepositConfirmed.asStringColumn()); - add(colTradeCost.justify()); - add(colIsPaymentSentMessageSent.asStringColumn()); - add(colIsPaymentReceivedMessageSent.asStringColumn()); - add(colIsPayoutPublished.asStringColumn()); - add(colIsCompleted.asStringColumn()); - }}; - - if (colCryptoReceiveAddressColumn != null) - columns.add(colCryptoReceiveAddressColumn); - - return columns; - } -} diff --git a/bot/src/main/java/haveno/cli/table/builder/TradeTableColumnSupplier.java b/bot/src/main/java/haveno/cli/table/builder/TradeTableColumnSupplier.java deleted file mode 100644 index d1974344..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/TradeTableColumnSupplier.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.column.CryptoVolumeColumn; -import haveno.cli.table.column.DoubleColumn; -import haveno.cli.table.column.BooleanColumn; -import haveno.cli.table.column.BtcColumn; -import haveno.cli.table.column.Column; -import haveno.cli.table.column.Iso8601DateTimeColumn; -import haveno.cli.table.column.MixedTradeFeeColumn; -import haveno.cli.table.column.SatoshiColumn; -import haveno.cli.table.column.StringColumn; -import haveno.proto.grpc.ContractInfo; -import haveno.proto.grpc.OfferInfo; -import haveno.proto.grpc.TradeInfo; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_AMOUNT; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_AMOUNT_IN_BTC; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_CURRENCY; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DATE_TIME; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DETAILED_AMOUNT; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DETAILED_PRICE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DETAILED_PRICE_OF_CRYPTO; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_DEVIATION; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_MARKET; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_OFFER_TYPE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_PAYMENT_METHOD; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_PRICE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_STATUS; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_CRYPTO_BUYER_ADDRESS; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_BUYER_COST; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_DEPOSIT_CONFIRMED; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_DEPOSIT_PUBLISHED; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_FEE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_ID; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_MAKER_FEE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_PAYMENT_RECEIVED; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_PAYMENT_SENT; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_PAYOUT_PUBLISHED; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_ROLE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_SHORT_ID; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_TAKER_FEE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TRADE_WITHDRAWN; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_FEE; -import static haveno.cli.table.builder.TableType.CLOSED_TRADES_TBL; -import static haveno.cli.table.builder.TableType.FAILED_TRADES_TBL; -import static haveno.cli.table.builder.TableType.OPEN_TRADES_TBL; -import static haveno.cli.table.builder.TableType.TRADE_DETAIL_TBL; -import static haveno.cli.table.column.CryptoVolumeColumn.DISPLAY_MODE.CRYPTO_VOLUME; -import static haveno.cli.table.column.CryptoVolumeColumn.DISPLAY_MODE.BSQ_VOLUME; -import static haveno.cli.table.column.Column.JUSTIFICATION.LEFT; -import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; -import static java.lang.String.format; - -/** - * Convenience for supplying column definitions to - * open/closed/failed/detail trade table builders. - */ -@Slf4j -class TradeTableColumnSupplier { - - @Getter - private final TableType tableType; - @Getter - private final List trades; - - public TradeTableColumnSupplier(TableType tableType, List trades) { - this.tableType = tableType; - this.trades = trades; - } - - private final Supplier isTradeDetailTblBuilder = () -> getTableType().equals(TRADE_DETAIL_TBL); - private final Supplier isOpenTradeTblBuilder = () -> getTableType().equals(OPEN_TRADES_TBL); - private final Supplier isClosedTradeTblBuilder = () -> getTableType().equals(CLOSED_TRADES_TBL); - private final Supplier isFailedTradeTblBuilder = () -> getTableType().equals(FAILED_TRADES_TBL); - private final Supplier firstRow = () -> getTrades().get(0); - private final Predicate isTraditionalOffer = (o) -> o.getBaseCurrencyCode().equals("XMR"); - private final Predicate isTraditionalTrade = (t) -> isTraditionalOffer.test(t.getOffer()); - private final Predicate isTaker = (t) -> t.getRole().toLowerCase().contains("taker"); - - final Supplier tradeIdColumn = () -> isTradeDetailTblBuilder.get() - ? new StringColumn(COL_HEADER_TRADE_SHORT_ID) - : new StringColumn(COL_HEADER_TRADE_ID); - - final Supplier createDateColumn = () -> isTradeDetailTblBuilder.get() - ? null - : new Iso8601DateTimeColumn(COL_HEADER_DATE_TIME); - - final Supplier marketColumn = () -> isTradeDetailTblBuilder.get() - ? null - : new StringColumn(COL_HEADER_MARKET); - - private final Function> toDetailedPriceColumn = (t) -> { - String colHeader = isTraditionalTrade.test(t) - ? format(COL_HEADER_DETAILED_PRICE, t.getOffer().getCounterCurrencyCode()) - : format(COL_HEADER_DETAILED_PRICE_OF_CRYPTO, t.getOffer().getBaseCurrencyCode()); - return new StringColumn(colHeader, RIGHT); - }; - - final Supplier> priceColumn = () -> isTradeDetailTblBuilder.get() - ? toDetailedPriceColumn.apply(firstRow.get()) - : new StringColumn(COL_HEADER_PRICE, RIGHT); - - final Supplier> priceDeviationColumn = () -> isTradeDetailTblBuilder.get() - ? null - : new StringColumn(COL_HEADER_DEVIATION, RIGHT); - - final Supplier currencyColumn = () -> isTradeDetailTblBuilder.get() - ? null - : new StringColumn(COL_HEADER_CURRENCY); - - private final Function> toDetailedAmountColumn = (t) -> { - String headerCurrencyCode = t.getOffer().getBaseCurrencyCode(); - String colHeader = format(COL_HEADER_DETAILED_AMOUNT, headerCurrencyCode); - CryptoVolumeColumn.DISPLAY_MODE displayMode = headerCurrencyCode.equals("BSQ") ? BSQ_VOLUME : CRYPTO_VOLUME; - return isTraditionalTrade.test(t) - ? new SatoshiColumn(colHeader) - : new CryptoVolumeColumn(colHeader, displayMode); - }; - - // Can be tradional or crypto amount represented as longs. Placing the decimal - // in the displayed string representation is done in the Column implementation. - final Supplier> amountColumn = () -> isTradeDetailTblBuilder.get() - ? toDetailedAmountColumn.apply(firstRow.get()) - : new BtcColumn(COL_HEADER_AMOUNT_IN_BTC); - - final Supplier mixedAmountColumn = () -> isTradeDetailTblBuilder.get() - ? null - : new StringColumn(COL_HEADER_AMOUNT, RIGHT); - - final Supplier> minerTxFeeColumn = () -> isTradeDetailTblBuilder.get() || isClosedTradeTblBuilder.get() - ? new SatoshiColumn(COL_HEADER_TX_FEE) - : null; - - final Supplier mixedTradeFeeColumn = () -> isTradeDetailTblBuilder.get() - ? null - : new MixedTradeFeeColumn(COL_HEADER_TRADE_FEE); - - final Supplier paymentMethodColumn = () -> isTradeDetailTblBuilder.get() || isClosedTradeTblBuilder.get() - ? null - : new StringColumn(COL_HEADER_PAYMENT_METHOD, LEFT); - - final Supplier roleColumn = () -> { - return isTradeDetailTblBuilder.get() || isOpenTradeTblBuilder.get() || isFailedTradeTblBuilder.get() - ? new StringColumn(COL_HEADER_TRADE_ROLE) - : null; - }; - - final Function> toSecurityDepositColumn = (name) -> isClosedTradeTblBuilder.get() - ? new DoubleColumn(name) - : null; - - final Supplier offerTypeColumn = () -> isTradeDetailTblBuilder.get() - ? null - : new StringColumn(COL_HEADER_OFFER_TYPE); - - final Supplier statusDescriptionColumn = () -> isTradeDetailTblBuilder.get() - ? null - : new StringColumn(COL_HEADER_STATUS); - - private final Function> toBooleanColumn = BooleanColumn::new; - - final Supplier> depositPublishedColumn = () -> { - return isTradeDetailTblBuilder.get() - ? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_PUBLISHED) - : null; - }; - - final Supplier> depositConfirmedColumn = () -> { - return isTradeDetailTblBuilder.get() - ? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_CONFIRMED) - : null; - - }; - - final Supplier> payoutPublishedColumn = () -> { - return isTradeDetailTblBuilder.get() - ? toBooleanColumn.apply(COL_HEADER_TRADE_PAYOUT_PUBLISHED) - : null; - }; - - final Supplier> fundsWithdrawnColumn = () -> { - return isTradeDetailTblBuilder.get() - ? toBooleanColumn.apply(COL_HEADER_TRADE_WITHDRAWN) - : null; - }; - - final Supplier> havenoTradeDetailFeeColumn = () -> { - if (isTradeDetailTblBuilder.get()) { - TradeInfo t = firstRow.get(); - String headerCurrencyCode = "XMR"; - String colHeader = isTaker.test(t) - ? format(COL_HEADER_TRADE_TAKER_FEE, headerCurrencyCode) - : format(COL_HEADER_TRADE_MAKER_FEE, headerCurrencyCode); - return new SatoshiColumn(colHeader, false); - } else { - return null; - } - }; - - final Function toPaymentCurrencyCode = (t) -> - isTraditionalTrade.test(t) - ? t.getOffer().getCounterCurrencyCode() - : t.getOffer().getBaseCurrencyCode(); - - final Supplier> paymentSentMessageSentColumn = () -> { - if (isTradeDetailTblBuilder.get()) { - String headerCurrencyCode = toPaymentCurrencyCode.apply(firstRow.get()); - String colHeader = format(COL_HEADER_TRADE_PAYMENT_SENT, headerCurrencyCode); - return new BooleanColumn(colHeader); - } else { - return null; - } - }; - - final Supplier> paymentReceivedMessageSentColumn = () -> { - if (isTradeDetailTblBuilder.get()) { - String headerCurrencyCode = toPaymentCurrencyCode.apply(firstRow.get()); - String colHeader = format(COL_HEADER_TRADE_PAYMENT_RECEIVED, headerCurrencyCode); - return new BooleanColumn(colHeader); - } else { - return null; - } - }; - - final Supplier> tradeCostColumn = () -> { - if (isTradeDetailTblBuilder.get()) { - TradeInfo t = firstRow.get(); - String headerCurrencyCode = t.getOffer().getCounterCurrencyCode(); - String colHeader = format(COL_HEADER_TRADE_BUYER_COST, headerCurrencyCode); - return new StringColumn(colHeader, RIGHT); - } else { - return null; - } - }; - - final Predicate showCryptoBuyerAddress = (t) -> { - if (isTraditionalTrade.test(t)) { - return false; - } else { - ContractInfo contract = t.getContract(); - boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker(); - if (isTaker.test(t)) { - return !isBuyerMakerAndSellerTaker; - } else { - return isBuyerMakerAndSellerTaker; - } - } - }; - - @Nullable - final Supplier> cryptoReceiveAddressColumn = () -> { - if (isTradeDetailTblBuilder.get()) { - TradeInfo t = firstRow.get(); - if (showCryptoBuyerAddress.test(t)) { - String headerCurrencyCode = toPaymentCurrencyCode.apply(t); - String colHeader = format(COL_HEADER_TRADE_CRYPTO_BUYER_ADDRESS, headerCurrencyCode); - return new StringColumn(colHeader); - } else { - return null; - } - } else { - return null; - } - }; -} diff --git a/bot/src/main/java/haveno/cli/table/builder/TransactionTableBuilder.java b/bot/src/main/java/haveno/cli/table/builder/TransactionTableBuilder.java deleted file mode 100644 index a07b135c..00000000 --- a/bot/src/main/java/haveno/cli/table/builder/TransactionTableBuilder.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.builder; - -import haveno.cli.table.Table; -import haveno.cli.table.column.BooleanColumn; -import haveno.cli.table.column.Column; -import haveno.cli.table.column.LongColumn; -import haveno.cli.table.column.SatoshiColumn; -import haveno.cli.table.column.StringColumn; - -import javax.annotation.Nullable; -import java.util.List; - -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_FEE; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_ID; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_INPUT_SUM; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_IS_CONFIRMED; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_OUTPUT_SUM; -import static haveno.cli.table.builder.TableBuilderConstants.COL_HEADER_TX_SIZE; -import static haveno.cli.table.builder.TableType.TRANSACTION_TBL; - -/** - * Builds a {@code haveno.cli.table.Table} from a {@code haveno.proto.grpc.TxInfo} object. - */ -class TransactionTableBuilder extends AbstractTableBuilder { - - // Default columns not dynamically generated with tx info. - private final Column colTxId; - private final Column colIsConfirmed; - private final Column colInputSum; - private final Column colOutputSum; - private final Column colTxFee; - private final Column colTxSize; - - TransactionTableBuilder(List protos) { - super(TRANSACTION_TBL, protos); - this.colTxId = new StringColumn(COL_HEADER_TX_ID); - this.colIsConfirmed = new BooleanColumn(COL_HEADER_TX_IS_CONFIRMED); - this.colInputSum = new SatoshiColumn(COL_HEADER_TX_INPUT_SUM); - this.colOutputSum = new SatoshiColumn(COL_HEADER_TX_OUTPUT_SUM); - this.colTxFee = new SatoshiColumn(COL_HEADER_TX_FEE); - this.colTxSize = new LongColumn(COL_HEADER_TX_SIZE); - } - - public Table build() { - // TODO Add 'gettransactions' api method & show multiple tx in the console. - // For now, a tx tbl is only one row. - - // Declare the columns derived from tx info. - - @Nullable - Column colMemo = null; - - // Populate columns with tx info. - - colTxId.addRow(null); - colIsConfirmed.addRow(null); - colInputSum.addRow(null); - colOutputSum.addRow(null); - colTxFee.addRow(null); - colTxSize.addRow(null); - if (colMemo != null) - colMemo.addRow(null); - - // Define and return the table instance with populated columns. - - if (colMemo != null) { - return new Table(colTxId, - colIsConfirmed.asStringColumn(), - colInputSum.asStringColumn(), - colOutputSum.asStringColumn(), - colTxFee.asStringColumn(), - colTxSize.asStringColumn(), - colMemo); - } else { - return new Table(colTxId, - colIsConfirmed.asStringColumn(), - colInputSum.asStringColumn(), - colOutputSum.asStringColumn(), - colTxFee.asStringColumn(), - colTxSize.asStringColumn()); - } - } -} diff --git a/bot/src/main/java/haveno/cli/table/column/AbstractColumn.java b/bot/src/main/java/haveno/cli/table/column/AbstractColumn.java deleted file mode 100644 index b9f3b074..00000000 --- a/bot/src/main/java/haveno/cli/table/column/AbstractColumn.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - -import static com.google.common.base.Strings.padEnd; -import static com.google.common.base.Strings.padStart; -import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; - -/** - * Partial implementation of the {@link Column} interface. - */ -abstract class AbstractColumn, T> implements Column { - - // We create an encapsulated StringColumn up front to populate with formatted - // strings in each this.addRow(Long value) call. But we will not know how - // to justify the cached, formatted string until the column is fully populated. - protected final StringColumn stringColumn; - - // The name field is not final, so it can be re-set for column alignment. - protected String name; - protected final JUSTIFICATION justification; - // The max width is not known until after column is fully populated. - protected int maxWidth; - - public AbstractColumn(String name, JUSTIFICATION justification) { - this.name = name; - this.justification = justification; - this.stringColumn = this instanceof StringColumn ? null : new StringColumn(name, justification); - } - - @Override - public String getName() { - return this.name; - } - - @Override - public void setName(String name) { - this.name = name; - } - - @Override - public int getWidth() { - return maxWidth; - } - - @Override - public JUSTIFICATION getJustification() { - return this.justification; - } - - @Override - public Column justify() { - if (this instanceof StringColumn && this.justification.equals(RIGHT)) - return this.justify(); - else - return this; // no-op - } - - protected final String toJustifiedString(String s) { - switch (justification) { - case LEFT: - return padEnd(s, maxWidth, ' '); - case RIGHT: - return padStart(s, maxWidth, ' '); - case NONE: - default: - return s; - } - } -} - diff --git a/bot/src/main/java/haveno/cli/table/column/BooleanColumn.java b/bot/src/main/java/haveno/cli/table/column/BooleanColumn.java deleted file mode 100644 index 4a34e098..00000000 --- a/bot/src/main/java/haveno/cli/table/column/BooleanColumn.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.IntStream; - -import static haveno.cli.table.column.Column.JUSTIFICATION.LEFT; - -/** - * For displaying boolean values as YES, NO, or user's choice for 'true' and 'false'. - */ -public class BooleanColumn extends AbstractColumn { - - private static final String DEFAULT_TRUE_AS_STRING = "YES"; - private static final String DEFAULT_FALSE_AS_STRING = "NO"; - - private final List rows = new ArrayList<>(); - - private final Predicate isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth; - - private final String trueAsString; - private final String falseAsString; - - // The default BooleanColumn JUSTIFICATION is LEFT. - // The default BooleanColumn True AsString value is YES. - // The default BooleanColumn False AsString value is NO. - public BooleanColumn(String name) { - this(name, LEFT, DEFAULT_TRUE_AS_STRING, DEFAULT_FALSE_AS_STRING); - } - - // Use this constructor to override default LEFT justification. - @SuppressWarnings("unused") - public BooleanColumn(String name, JUSTIFICATION justification) { - this(name, justification, DEFAULT_TRUE_AS_STRING, DEFAULT_FALSE_AS_STRING); - } - - // Use this constructor to override default true/false as string defaults. - public BooleanColumn(String name, String trueAsString, String falseAsString) { - this(name, LEFT, trueAsString, falseAsString); - } - - // Use this constructor to override default LEFT justification. - public BooleanColumn(String name, - JUSTIFICATION justification, - String trueAsString, - String falseAsString) { - super(name, justification); - this.trueAsString = trueAsString; - this.falseAsString = falseAsString; - this.maxWidth = name.length(); - } - - @Override - public void addRow(Boolean value) { - rows.add(value); - - // We do not know how much padding each StringColumn value needs until it has all the values. - String s = asString(value); - stringColumn.addRow(s); - - if (isNewMaxWidth.test(s)) - maxWidth = s.length(); - } - - @Override - public List getRows() { - return rows; - } - - @Override - public int rowCount() { - return rows.size(); - } - - @Override - public boolean isEmpty() { - return rows.isEmpty(); - } - - @Override - public Boolean getRow(int rowIndex) { - return rows.get(rowIndex); - } - - @Override - public void updateRow(int rowIndex, Boolean newValue) { - rows.set(rowIndex, newValue); - } - - @Override - public String getRowAsFormattedString(int rowIndex) { - return getRow(rowIndex) - ? trueAsString - : falseAsString; - } - - @Override - public StringColumn asStringColumn() { - // We cached the formatted satoshi strings, but we did - // not know how much padding each string needed until now. - IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> { - String unjustified = stringColumn.getRow(rowIndex); - String justified = stringColumn.toJustifiedString(unjustified); - stringColumn.updateRow(rowIndex, justified); - }); - return stringColumn; - } - - private String asString(boolean value) { - return value ? trueAsString : falseAsString; - } -} diff --git a/bot/src/main/java/haveno/cli/table/column/BtcColumn.java b/bot/src/main/java/haveno/cli/table/column/BtcColumn.java deleted file mode 100644 index f0b6f779..00000000 --- a/bot/src/main/java/haveno/cli/table/column/BtcColumn.java +++ /dev/null @@ -1,48 +0,0 @@ -package haveno.cli.table.column; - -import java.util.stream.IntStream; - -import static com.google.common.base.Strings.padEnd; -import static haveno.cli.CurrencyFormat.formatBtc; -import static java.util.Comparator.comparingInt; - -public class BtcColumn extends SatoshiColumn { - - public BtcColumn(String name) { - super(name); - } - - @Override - public void addRow(Long value) { - rows.add(value); - - String s = formatBtc(value); - stringColumn.addRow(s); - - if (isNewMaxWidth.test(s)) - maxWidth = s.length(); - } - - @Override - public String getRowAsFormattedString(int rowIndex) { - return formatBtc(getRow(rowIndex)); - } - - @Override - public StringColumn asStringColumn() { - // We cached the formatted satoshi strings, but we did - // not know how much zero padding each string needed until now. - int maxColumnValueWidth = stringColumn.getRows().stream() - .max(comparingInt(String::length)) - .get() - .length(); - IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> { - String btcString = stringColumn.getRow(rowIndex); - if (btcString.length() < maxColumnValueWidth) { - String paddedBtcString = padEnd(btcString, maxColumnValueWidth, '0'); - stringColumn.updateRow(rowIndex, paddedBtcString); - } - }); - return stringColumn.justify(); - } -} diff --git a/bot/src/main/java/haveno/cli/table/column/Column.java b/bot/src/main/java/haveno/cli/table/column/Column.java deleted file mode 100644 index 70787306..00000000 --- a/bot/src/main/java/haveno/cli/table/column/Column.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - -import java.util.List; - -public interface Column { - - enum JUSTIFICATION { - LEFT, - RIGHT, - NONE - } - - /** - * Returns the column's name. - * - * @return name as String - */ - String getName(); - - /** - * Sets the column name. - * - * @param name of the column - */ - void setName(String name); - - /** - * Add column value. - * - * @param value added to column's data (row) - */ - void addRow(T value); - - /** - * Returns the column data. - * - * @return rows as List - */ - List getRows(); - - /** - * Returns the maximum width of the column name, or longest, - * formatted string value -- whichever is greater. - * - * @return width of the populated column as int - */ - int getWidth(); - - /** - * Returns the number of rows in the column. - * - * @return number of rows in the column as int. - */ - int rowCount(); - - /** - * Returns true if the column has no data. - * - * @return true if empty, false if not - */ - boolean isEmpty(); - - /** - * Returns the column value (data) at given row index. - * - * @return value object - */ - T getRow(int rowIndex); - - /** - * Update an existing value at the given row index to a new value. - * - * @param rowIndex row index of value to be updated - * @param newValue new value - */ - void updateRow(int rowIndex, T newValue); - - /** - * Returns the row value as a formatted String. - * - * @return a row value as formatted String - */ - String getRowAsFormattedString(int rowIndex); - - /** - * Return the column with all of its data as a StringColumn with all of its - * formatted string data. - * - * @return StringColumn - */ - StringColumn asStringColumn(); - - /** - * Convenience for justifying populated StringColumns before being displayed. - * Is only useful for StringColumn instances. - */ - Column justify(); - - /** - * Returns JUSTIFICATION value (RIGHT|LEFT|NONE) for the column. - * - * @return column JUSTIFICATION - */ - JUSTIFICATION getJustification(); -} diff --git a/bot/src/main/java/haveno/cli/table/column/CryptoVolumeColumn.java b/bot/src/main/java/haveno/cli/table/column/CryptoVolumeColumn.java deleted file mode 100644 index 153e2735..00000000 --- a/bot/src/main/java/haveno/cli/table/column/CryptoVolumeColumn.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - -import java.math.BigDecimal; -import java.util.function.BiFunction; -import java.util.stream.IntStream; - -import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; - -/** - * For displaying crypto volume with appropriate precision. - */ -public class CryptoVolumeColumn extends LongColumn { - - public enum DISPLAY_MODE { - CRYPTO_VOLUME, - BSQ_VOLUME, - } - - private final DISPLAY_MODE displayMode; - - // The default CryptoVolumeColumn JUSTIFICATION is RIGHT. - public CryptoVolumeColumn(String name, DISPLAY_MODE displayMode) { - this(name, RIGHT, displayMode); - } - - public CryptoVolumeColumn(String name, - JUSTIFICATION justification, - DISPLAY_MODE displayMode) { - super(name, justification); - this.displayMode = displayMode; - } - - @Override - public void addRow(Long value) { - rows.add(value); - - String s = toFormattedString.apply(value, displayMode); - stringColumn.addRow(s); - - if (isNewMaxWidth.test(s)) - maxWidth = s.length(); - } - - @Override - public String getRowAsFormattedString(int rowIndex) { - return toFormattedString.apply(getRow(rowIndex), displayMode); - } - - @Override - public StringColumn asStringColumn() { - // We cached the formatted crypto value strings, but we did - // not know how much padding each string needed until now. - IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> { - String unjustified = stringColumn.getRow(rowIndex); - String justified = stringColumn.toJustifiedString(unjustified); - stringColumn.updateRow(rowIndex, justified); - }); - return this.stringColumn; - } - - private final BiFunction toFormattedString = (value, displayMode) -> { - switch (displayMode) { - case CRYPTO_VOLUME: - return value > 0 ? new BigDecimal(value).movePointLeft(8).toString() : ""; - case BSQ_VOLUME: - return value > 0 ? new BigDecimal(value).movePointLeft(2).toString() : ""; - default: - throw new IllegalStateException("invalid display mode: " + displayMode); - } - }; -} diff --git a/bot/src/main/java/haveno/cli/table/column/DoubleColumn.java b/bot/src/main/java/haveno/cli/table/column/DoubleColumn.java deleted file mode 100644 index 812f421c..00000000 --- a/bot/src/main/java/haveno/cli/table/column/DoubleColumn.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.IntStream; - -import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; - -/** - * For displaying Double values. - */ -public class DoubleColumn extends NumberColumn { - - protected final List rows = new ArrayList<>(); - - protected final Predicate isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth; - - // The default DoubleColumn JUSTIFICATION is RIGHT. - public DoubleColumn(String name) { - this(name, RIGHT); - } - - public DoubleColumn(String name, JUSTIFICATION justification) { - super(name, justification); - this.maxWidth = name.length(); - } - - @Override - public void addRow(Double value) { - rows.add(value); - - String s = String.valueOf(value); - if (isNewMaxWidth.test(s)) - maxWidth = s.length(); - } - - @Override - public List getRows() { - return rows; - } - - @Override - public int rowCount() { - return rows.size(); - } - - @Override - public boolean isEmpty() { - return rows.isEmpty(); - } - - @Override - public Double getRow(int rowIndex) { - return rows.get(rowIndex); - } - - @Override - public void updateRow(int rowIndex, Double newValue) { - rows.set(rowIndex, newValue); - } - - @Override - public String getRowAsFormattedString(int rowIndex) { - String s = String.valueOf(getRow(rowIndex)); - return toJustifiedString(s); - } - - @Override - public StringColumn asStringColumn() { - IntStream.range(0, rows.size()).forEachOrdered(rowIndex -> - stringColumn.addRow(getRowAsFormattedString(rowIndex))); - - return stringColumn; - } -} diff --git a/bot/src/main/java/haveno/cli/table/column/IntegerColumn.java b/bot/src/main/java/haveno/cli/table/column/IntegerColumn.java deleted file mode 100644 index 5d9538e5..00000000 --- a/bot/src/main/java/haveno/cli/table/column/IntegerColumn.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.IntStream; - -import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; - -/** - * For displaying Integer values. - */ -public class IntegerColumn extends NumberColumn { - - protected final List rows = new ArrayList<>(); - - protected final Predicate isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth; - - // The default IntegerColumn JUSTIFICATION is RIGHT. - public IntegerColumn(String name) { - this(name, RIGHT); - } - - public IntegerColumn(String name, JUSTIFICATION justification) { - super(name, justification); - this.maxWidth = name.length(); - } - - @Override - public void addRow(Integer value) { - rows.add(value); - - String s = String.valueOf(value); - if (isNewMaxWidth.test(s)) - maxWidth = s.length(); - } - - @Override - public List getRows() { - return rows; - } - - @Override - public int rowCount() { - return rows.size(); - } - - @Override - public boolean isEmpty() { - return rows.isEmpty(); - } - - @Override - public Integer getRow(int rowIndex) { - return rows.get(rowIndex); - } - - @Override - public void updateRow(int rowIndex, Integer newValue) { - rows.set(rowIndex, newValue); - } - - @Override - public String getRowAsFormattedString(int rowIndex) { - String s = String.valueOf(getRow(rowIndex)); - return toJustifiedString(s); - } - - @Override - public StringColumn asStringColumn() { - IntStream.range(0, rows.size()).forEachOrdered(rowIndex -> - stringColumn.addRow(getRowAsFormattedString(rowIndex))); - - return stringColumn; - } -} diff --git a/bot/src/main/java/haveno/cli/table/column/Iso8601DateTimeColumn.java b/bot/src/main/java/haveno/cli/table/column/Iso8601DateTimeColumn.java deleted file mode 100644 index df87a500..00000000 --- a/bot/src/main/java/haveno/cli/table/column/Iso8601DateTimeColumn.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.stream.IntStream; - -import static com.google.common.base.Strings.padEnd; -import static com.google.common.base.Strings.padStart; -import static haveno.cli.table.column.Column.JUSTIFICATION.LEFT; -import static java.lang.System.currentTimeMillis; -import static java.util.TimeZone.getTimeZone; - -/** - * For displaying (long) timestamp values as ISO-8601 dates in UTC time zone. - */ -public class Iso8601DateTimeColumn extends LongColumn { - - protected final SimpleDateFormat iso8601DateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - - // The default Iso8601DateTimeColumn JUSTIFICATION is LEFT. - public Iso8601DateTimeColumn(String name) { - this(name, LEFT); - } - - public Iso8601DateTimeColumn(String name, JUSTIFICATION justification) { - super(name, justification); - iso8601DateFormat.setTimeZone(getTimeZone("UTC")); - this.maxWidth = Math.max(name.length(), String.valueOf(currentTimeMillis()).length()); - } - - @Override - public String getRowAsFormattedString(int rowIndex) { - long time = getRow(rowIndex); - return justification.equals(LEFT) - ? padEnd(iso8601DateFormat.format(new Date(time)), maxWidth, ' ') - : padStart(iso8601DateFormat.format(new Date(time)), maxWidth, ' '); - } - - @Override - public StringColumn asStringColumn() { - IntStream.range(0, rows.size()).forEachOrdered(rowIndex -> - stringColumn.addRow(getRowAsFormattedString(rowIndex))); - - return stringColumn; - } -} diff --git a/bot/src/main/java/haveno/cli/table/column/LongColumn.java b/bot/src/main/java/haveno/cli/table/column/LongColumn.java deleted file mode 100644 index 1499b0cb..00000000 --- a/bot/src/main/java/haveno/cli/table/column/LongColumn.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.IntStream; - -import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; - -/** - * For displaying Long values. - */ -public class LongColumn extends NumberColumn { - - protected final List rows = new ArrayList<>(); - - protected final Predicate isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth; - - // The default LongColumn JUSTIFICATION is RIGHT. - public LongColumn(String name) { - this(name, RIGHT); - } - - public LongColumn(String name, JUSTIFICATION justification) { - super(name, justification); - this.maxWidth = name.length(); - } - - @Override - public void addRow(Long value) { - rows.add(value); - - String s = String.valueOf(value); - if (isNewMaxWidth.test(s)) - maxWidth = s.length(); - } - - @Override - public List getRows() { - return rows; - } - - @Override - public int rowCount() { - return rows.size(); - } - - @Override - public boolean isEmpty() { - return rows.isEmpty(); - } - - @Override - public Long getRow(int rowIndex) { - return rows.get(rowIndex); - } - - @Override - public void updateRow(int rowIndex, Long newValue) { - rows.set(rowIndex, newValue); - } - - @Override - public String getRowAsFormattedString(int rowIndex) { - String s = String.valueOf(getRow(rowIndex)); - return toJustifiedString(s); - } - - @Override - public StringColumn asStringColumn() { - IntStream.range(0, rows.size()).forEachOrdered(rowIndex -> - stringColumn.addRow(getRowAsFormattedString(rowIndex))); - - return stringColumn; - } -} diff --git a/bot/src/main/java/haveno/cli/table/column/MixedTradeFeeColumn.java b/bot/src/main/java/haveno/cli/table/column/MixedTradeFeeColumn.java deleted file mode 100644 index 4e54759e..00000000 --- a/bot/src/main/java/haveno/cli/table/column/MixedTradeFeeColumn.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - -import static haveno.cli.CurrencyFormat.formatBsq; -import static haveno.cli.CurrencyFormat.formatSatoshis; -import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; - -/** - * For displaying a mix of BSQ and BTC trade fees with appropriate precision. - */ -public class MixedTradeFeeColumn extends LongColumn { - - public MixedTradeFeeColumn(String name) { - super(name, RIGHT); - } - - @Override - public void addRow(Long value) { - throw new UnsupportedOperationException("use public void addRow(Long value, boolean isBsq) instead"); - } - - public void addRow(Long value, boolean isBsq) { - rows.add(value); - - String s = isBsq - ? formatBsq(value) + " BSQ" - : formatSatoshis(value) + " BTC"; - stringColumn.addRow(s); - - if (isNewMaxWidth.test(s)) - maxWidth = s.length(); - } - - @Override - public String getRowAsFormattedString(int rowIndex) { - return getRow(rowIndex).toString(); - } - - @Override - public StringColumn asStringColumn() { - return stringColumn.justify(); - } -} diff --git a/bot/src/main/java/haveno/cli/table/column/NumberColumn.java b/bot/src/main/java/haveno/cli/table/column/NumberColumn.java deleted file mode 100644 index 69c67844..00000000 --- a/bot/src/main/java/haveno/cli/table/column/NumberColumn.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - -/** - * Abstract superclass for numeric Columns. - * - * @param the subclass column's type (LongColumn, IntegerColumn, ...) - * @param the subclass column's numeric Java type (Long, Integer, ...) - */ -abstract class NumberColumn, - T extends Number> extends AbstractColumn implements Column { - - public NumberColumn(String name, JUSTIFICATION justification) { - super(name, justification); - } -} diff --git a/bot/src/main/java/haveno/cli/table/column/SatoshiColumn.java b/bot/src/main/java/haveno/cli/table/column/SatoshiColumn.java deleted file mode 100644 index 767e878d..00000000 --- a/bot/src/main/java/haveno/cli/table/column/SatoshiColumn.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - -import static haveno.cli.CurrencyFormat.formatBsq; -import static haveno.cli.CurrencyFormat.formatSatoshis; -import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; - -/** - * For displaying BTC or BSQ satoshi values with appropriate precision. - */ -public class SatoshiColumn extends LongColumn { - - protected final boolean isBsqSatoshis; - - // The default SatoshiColumn JUSTIFICATION is RIGHT. - public SatoshiColumn(String name) { - this(name, RIGHT, false); - } - - public SatoshiColumn(String name, boolean isBsqSatoshis) { - this(name, RIGHT, isBsqSatoshis); - } - - public SatoshiColumn(String name, JUSTIFICATION justification) { - this(name, justification, false); - } - - public SatoshiColumn(String name, JUSTIFICATION justification, boolean isBsqSatoshis) { - super(name, justification); - this.isBsqSatoshis = isBsqSatoshis; - } - - @Override - public void addRow(Long value) { - rows.add(value); - - // We do not know how much padding each StringColumn value needs until it has all the values. - String s = isBsqSatoshis ? formatBsq(value) : formatSatoshis(value); - stringColumn.addRow(s); - - if (isNewMaxWidth.test(s)) - maxWidth = s.length(); - } - - @Override - public String getRowAsFormattedString(int rowIndex) { - return isBsqSatoshis - ? formatBsq(getRow(rowIndex)) - : formatSatoshis(getRow(rowIndex)); - } - - @Override - public StringColumn asStringColumn() { - return stringColumn.justify(); - } -} diff --git a/bot/src/main/java/haveno/cli/table/column/StringColumn.java b/bot/src/main/java/haveno/cli/table/column/StringColumn.java deleted file mode 100644 index 67622287..00000000 --- a/bot/src/main/java/haveno/cli/table/column/StringColumn.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.IntStream; - -import static haveno.cli.table.column.Column.JUSTIFICATION.LEFT; -import static haveno.cli.table.column.Column.JUSTIFICATION.RIGHT; - -/** - * For displaying justified string values. - */ -public class StringColumn extends AbstractColumn { - - private final List rows = new ArrayList<>(); - - private final Predicate isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth; - - // The default StringColumn JUSTIFICATION is LEFT. - public StringColumn(String name) { - this(name, LEFT); - } - - // Use this constructor to override default LEFT justification. - public StringColumn(String name, JUSTIFICATION justification) { - super(name, justification); - this.maxWidth = name.length(); - } - - @Override - public void addRow(String value) { - rows.add(value); - if (isNewMaxWidth.test(value)) - maxWidth = value.length(); - } - - @Override - public List getRows() { - return rows; - } - - @Override - public int rowCount() { - return rows.size(); - } - - @Override - public boolean isEmpty() { - return rows.isEmpty(); - } - - @Override - public String getRow(int rowIndex) { - return rows.get(rowIndex); - } - - @Override - public void updateRow(int rowIndex, String newValue) { - rows.set(rowIndex, newValue); - } - - @Override - public String getRowAsFormattedString(int rowIndex) { - return getRow(rowIndex); - } - - @Override - public StringColumn asStringColumn() { - return this; - } - - @Override - public StringColumn justify() { - if (justification.equals(RIGHT)) { - IntStream.range(0, getRows().size()).forEach(rowIndex -> { - String unjustified = getRow(rowIndex); - String justified = toJustifiedString(unjustified); - updateRow(rowIndex, justified); - }); - } - return this; - } -} diff --git a/bot/src/main/java/haveno/cli/table/column/ZippedStringColumns.java b/bot/src/main/java/haveno/cli/table/column/ZippedStringColumns.java deleted file mode 100644 index d55468d8..00000000 --- a/bot/src/main/java/haveno/cli/table/column/ZippedStringColumns.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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 . - */ - -package haveno.cli.table.column; - -import haveno.cli.table.column.Column.JUSTIFICATION; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.IntStream; - -import static haveno.cli.table.column.ZippedStringColumns.DUPLICATION_MODE.EXCLUDE_DUPLICATES; -import static haveno.cli.table.column.ZippedStringColumns.DUPLICATION_MODE.INCLUDE_DUPLICATES; - -/** - * For zipping multiple StringColumns into a single StringColumn. - * Useful for displaying amount and volume range values. - */ -public class ZippedStringColumns { - - public enum DUPLICATION_MODE { - EXCLUDE_DUPLICATES, - INCLUDE_DUPLICATES - } - - private final String name; - private final JUSTIFICATION justification; - private final String delimiter; - private final StringColumn[] columns; - - public ZippedStringColumns(String name, - JUSTIFICATION justification, - String delimiter, - StringColumn... columns) { - this.name = name; - this.justification = justification; - this.delimiter = delimiter; - this.columns = columns; - validateColumnData(); - } - - public StringColumn asStringColumn(DUPLICATION_MODE duplicationMode) { - StringColumn stringColumn = new StringColumn(name, justification); - - buildRows(stringColumn, duplicationMode); - - // Re-set the column name field to its justified value, in case any of the column - // values are longer than the name passed to this constructor. - stringColumn.setName(stringColumn.toJustifiedString(name)); - - return stringColumn; - } - - private void buildRows(StringColumn stringColumn, DUPLICATION_MODE duplicationMode) { - // Populate the StringColumn with unjustified zipped values; we cannot justify - // the zipped values until stringColumn knows its final maxWidth. - IntStream.range(0, columns[0].getRows().size()).forEach(rowIndex -> { - String row = buildRow(rowIndex, duplicationMode); - stringColumn.addRow(row); - }); - - formatRows(stringColumn); - } - - private String buildRow(int rowIndex, DUPLICATION_MODE duplicationMode) { - StringBuilder rowBuilder = new StringBuilder(); - @Nullable - List processedValues = duplicationMode.equals(EXCLUDE_DUPLICATES) - ? new ArrayList<>() - : null; - IntStream.range(0, columns.length).forEachOrdered(colIndex -> { - // For each column @ rowIndex ... - var value = columns[colIndex].getRows().get(rowIndex); - if (duplicationMode.equals(INCLUDE_DUPLICATES)) { - if (rowBuilder.length() > 0) - rowBuilder.append(delimiter); - - rowBuilder.append(value); - } else if (!processedValues.contains(value)) { - if (rowBuilder.length() > 0) - rowBuilder.append(delimiter); - - rowBuilder.append(value); - processedValues.add(value); - } - }); - return rowBuilder.toString(); - } - - private void formatRows(StringColumn stringColumn) { - // Now we can justify the zipped string values in the new StringColumn. - IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> { - String unjustified = stringColumn.getRow(rowIndex); - String justified = stringColumn.toJustifiedString(unjustified); - stringColumn.updateRow(rowIndex, justified); - }); - } - - private void validateColumnData() { - if (columns.length == 0) - throw new IllegalStateException("cannot zip columns because they do not have any data"); - - StringColumn firstColumn = columns[0]; - if (firstColumn.getRows().isEmpty()) - throw new IllegalStateException("1st column has no data"); - - IntStream.range(1, columns.length).forEach(colIndex -> { - if (columns[colIndex].getRows().size() != firstColumn.getRows().size()) - throw new IllegalStateException("columns do not have same number of rows"); - }); - } -} diff --git a/bot/src/main/resources/logback.xml b/bot/src/main/resources/logback.xml deleted file mode 100644 index bc8edf02..00000000 --- a/bot/src/main/resources/logback.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - %highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30}: %msg %xEx%n) - - - - - - - - - - diff --git a/bot/src/test/java/haveno/cli/AbstractCliTest.java b/bot/src/test/java/haveno/cli/AbstractCliTest.java deleted file mode 100644 index a11059fe..00000000 --- a/bot/src/test/java/haveno/cli/AbstractCliTest.java +++ /dev/null @@ -1,262 +0,0 @@ -package haveno.cli; - -import haveno.cli.opts.ArgumentList; -import haveno.proto.grpc.OfferInfo; -import joptsimple.OptionParser; -import joptsimple.OptionSet; -import lombok.extern.slf4j.Slf4j; -import org.bitbucket.cowwoc.diffmatchpatch.DiffMatchPatch; - -import java.math.BigDecimal; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; -import java.util.function.BiFunction; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static haveno.cli.opts.OptLabel.OPT_HOST; -import static haveno.cli.opts.OptLabel.OPT_PASSWORD; -import static haveno.cli.opts.OptLabel.OPT_PORT; -import static java.lang.System.out; -import static java.math.RoundingMode.HALF_UP; -import static java.util.Arrays.stream; -import static org.bitbucket.cowwoc.diffmatchpatch.DiffMatchPatch.Operation.DELETE; -import static org.bitbucket.cowwoc.diffmatchpatch.DiffMatchPatch.Operation.INSERT; - -/** - * Parent class for CLI smoke tests. Useful for examining the format of the console - * output, and checking for diffs while making changes to console output formatters. - * - * Tests that create offers or trades should not be run on mainnet. - */ -@Slf4j -public abstract class AbstractCliTest { - - static final String PASSWORD_OPT = "--password=xyz"; // Both daemons' password. - static final String ALICE_PORT_OPT = "--port=" + 9998; // Alice's daemon port. - static final String BOB_PORT_OPT = "--port=" + 9999; // Bob's daemon port. - static final String[] BASE_ALICE_CLIENT_OPTS = new String[]{PASSWORD_OPT, ALICE_PORT_OPT}; - static final String[] BASE_BOB_CLIENT_OPTS = new String[]{PASSWORD_OPT, BOB_PORT_OPT}; - - protected final BiFunction> randomMarginBasedPrices = (min, max) -> - IntStream.range(min, max).asDoubleStream() - .boxed() - .map(d -> d / 100) - .map(Object::toString) - .collect(Collectors.toList()); - - protected final BiFunction randomFixedCryptoPrice = (min, max) -> { - String random = Double.valueOf(ThreadLocalRandom.current().nextDouble(min, max)).toString(); - BigDecimal bd = new BigDecimal(random).setScale(8, HALF_UP); - return bd.toPlainString(); - }; - - protected final GrpcClient aliceClient; - protected final GrpcClient bobClient; - - public AbstractCliTest() { - this.aliceClient = getGrpcClient(BASE_ALICE_CLIENT_OPTS); - this.bobClient = getGrpcClient(BASE_BOB_CLIENT_OPTS); - } - - protected GrpcClient getGrpcClient(String[] args) { - var parser = new OptionParser(); - 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(); - - OptionSet options = parser.parse(new ArgumentList(args).getCLIArguments()); - 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"); - - return new GrpcClient(host, port, password); - } - - protected void checkDiffsIgnoreWhitespace(String oldOutput, String newOutput) { - Predicate isInsertOrDelete = (operation) -> - operation.equals(INSERT) || operation.equals(DELETE); - Predicate isWhitespace = (text) -> text.trim().isEmpty(); - boolean hasNonWhitespaceDiffs = false; - if (!oldOutput.equals(newOutput)) { - DiffMatchPatch dmp = new DiffMatchPatch(); - LinkedList diff = dmp.diffMain(oldOutput, newOutput, true); - for (DiffMatchPatch.Diff d : diff) { - if (isInsertOrDelete.test(d.operation) && !isWhitespace.test(d.text)) { - hasNonWhitespaceDiffs = true; - log.error(">>> DIFF {}", d); - } - } - } - - if (hasNonWhitespaceDiffs) - log.error("FAIL: There were diffs"); - else - log.info("PASS: No diffs"); - } - - protected void printOldTbl(String tbl) { - log.info("OLD Console OUT:\n{}", tbl); - } - - protected void printNewTbl(String tbl) { - log.info("NEW Console OUT:\n{}", tbl); - } - - protected List getMyCryptoOffers(String currencyCode) { - String[] args = getMyOffersCommand("buy", currencyCode); - out.print(">>>>> haveno-cli "); - stream(args).forEach(a -> out.print(a + " ")); - out.println(); - CliMain.main(args); - out.println("<<<<<"); - - args = getMyOffersCommand("sell", currencyCode); - out.print(">>>>> haveno-cli "); - stream(args).forEach(a -> out.print(a + " ")); - out.println(); - CliMain.main(args); - out.println("<<<<<"); - - return aliceClient.getMyOffersSortedByDate(currencyCode); - } - - protected String[] getMyOffersCommand(String direction, String currencyCode) { - return new String[]{ - PASSWORD_OPT, - ALICE_PORT_OPT, - "getmyoffers", - "--direction=" + direction, - "--currency-code=" + currencyCode - }; - } - - protected String[] getAvailableOffersCommand(String direction, String currencyCode) { - return new String[]{ - PASSWORD_OPT, - BOB_PORT_OPT, - "getoffers", - "--direction=" + direction, - "--currency-code=" + currencyCode - }; - } - - - protected void editOfferPriceMargin(OfferInfo offer, String priceMargin, boolean enable) { - String[] args = new String[]{ - PASSWORD_OPT, - ALICE_PORT_OPT, - "editoffer", - "--offer-id=" + offer.getId(), - "--market-price-margin=" + priceMargin, - "--enable=" + enable - }; - out.print(">>>>> haveno-cli "); - stream(args).forEach(a -> out.print(a + " ")); - out.println(); - CliMain.main(args); - out.println("<<<<<"); - } - - protected void editOfferTriggerPrice(OfferInfo offer, String triggerPrice, boolean enable) { - String[] args = new String[]{ - PASSWORD_OPT, - ALICE_PORT_OPT, - "editoffer", - "--offer-id=" + offer.getId(), - "--trigger-price=" + triggerPrice, - "--enable=" + enable - }; - out.print(">>>>> haveno-cli "); - stream(args).forEach(a -> out.print(a + " ")); - out.println(); - CliMain.main(args); - out.println("<<<<<"); - } - - protected void editOfferPriceMarginAndTriggerPrice(OfferInfo offer, - String priceMargin, - String triggerPrice, - boolean enable) { - String[] args = new String[]{ - PASSWORD_OPT, - ALICE_PORT_OPT, - "editoffer", - "--offer-id=" + offer.getId(), - "--market-price-margin=" + priceMargin, - "--trigger-price=" + triggerPrice, - "--enable=" + enable - }; - out.print(">>>>> haveno-cli "); - stream(args).forEach(a -> out.print(a + " ")); - out.println(); - CliMain.main(args); - out.println("<<<<<"); - } - - protected void editOfferFixedPrice(OfferInfo offer, String fixedPrice, boolean enable) { - String[] args = new String[]{ - PASSWORD_OPT, - ALICE_PORT_OPT, - "editoffer", - "--offer-id=" + offer.getId(), - "--fixed-price=" + fixedPrice, - "--enable=" + enable - }; - out.print(">>>>> haveno-cli "); - stream(args).forEach(a -> out.print(a + " ")); - out.println(); - CliMain.main(args); - out.println("<<<<<"); - } - - protected void disableOffers(List offers) { - out.println("Disable Offers"); - for (OfferInfo offer : offers) { - editOfferEnable(offer, false); - sleep(5); - } - } - - protected void enableOffers(List offers) { - out.println("Enable Offers"); - for (OfferInfo offer : offers) { - editOfferEnable(offer, true); - sleep(5); - } - } - - protected void editOfferEnable(OfferInfo offer, boolean enable) { - String[] args = new String[]{ - PASSWORD_OPT, - ALICE_PORT_OPT, - "editoffer", - "--offer-id=" + offer.getId(), - "--enable=" + enable - }; - out.print(">>>>> haveno-cli "); - stream(args).forEach(a -> out.print(a + " ")); - out.println(); - CliMain.main(args); - out.println("<<<<<"); - } - - protected void sleep(long seconds) { - try { - TimeUnit.SECONDS.sleep(seconds); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } -} diff --git a/bot/src/test/java/haveno/cli/CreateOfferSmokeTest.java b/bot/src/test/java/haveno/cli/CreateOfferSmokeTest.java deleted file mode 100644 index ea19c2ec..00000000 --- a/bot/src/test/java/haveno/cli/CreateOfferSmokeTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package haveno.cli; - -import static java.lang.System.out; -import static java.util.Arrays.stream; - -/** - Smoke tests for createoffer method. Useful for testing CLI command and examining the - format of its console output. - - Prerequisites: - - - Run `./haveno-apitest --apiPassword=xyz --supportingApps=bitcoind,seednode,arbdaemon,alicedaemon,bobdaemon --shutdownAfterTests=false --enableHavenoDebugging=false` - - Note: Test harness will not automatically generate BTC blocks to confirm transactions. - - Never run on mainnet! - */ -@SuppressWarnings({"CommentedOutCode", "unused"}) -public class CreateOfferSmokeTest extends AbstractCliTest { - - public static void main(String[] args) { - CreateOfferSmokeTest test = new CreateOfferSmokeTest(); - test.createBsqSwapOffer("buy"); - test.createBsqSwapOffer("sell"); - } - - private void createBsqSwapOffer(String direction) { - String[] args = createBsqSwapOfferCommand(direction, "0.01", "0.005", "0.00005"); - out.print(">>>>> haveno-cli "); - stream(args).forEach(a -> out.print(a + " ")); - out.println(); - CliMain.main(args); - out.println("<<<<<"); - - args = getMyOffersCommand(direction, "bsq"); - out.print(">>>>> haveno-cli "); - stream(args).forEach(a -> out.print(a + " ")); - out.println(); - CliMain.main(args); - out.println("<<<<<"); - - args = getAvailableOffersCommand(direction, "bsq"); - out.print(">>>>> haveno-cli "); - stream(args).forEach(a -> out.print(a + " ")); - out.println(); - CliMain.main(args); - out.println("<<<<<"); - } - - private String[] createBsqSwapOfferCommand(String direction, - String amount, - String minAmount, - String fixedPrice) { - return new String[]{ - PASSWORD_OPT, - ALICE_PORT_OPT, - "createoffer", - "--swap=true", - "--direction=" + direction, - "--currency-code=bsq", - "--amount=" + amount, - "--min-amount=" + minAmount, - "--fixed-price=" + fixedPrice - }; - } -} diff --git a/bot/src/test/java/haveno/cli/EditXmrOffersSmokeTest.java b/bot/src/test/java/haveno/cli/EditXmrOffersSmokeTest.java deleted file mode 100644 index 6e5f4e36..00000000 --- a/bot/src/test/java/haveno/cli/EditXmrOffersSmokeTest.java +++ /dev/null @@ -1,100 +0,0 @@ -package haveno.cli; - -import haveno.proto.grpc.OfferInfo; - -import java.util.List; -import java.util.Random; - -import static java.lang.System.out; -import static protobuf.OfferDirection.BUY; - -/** - Smoke tests for the editoffer method. - - Prerequisites: - - - Run `./haveno-apitest --apiPassword=xyz --supportingApps=bitcoind,seednode,arbdaemon,alicedaemon,bobdesktop --shutdownAfterTests=false --enableHavenoDebugging=false` - - - Create some XMR offers with Alice's UI or CLI. - - - Watch Alice's offers being edited in Bob's UI. - - Never run on mainnet. - */ -public class EditXmrOffersSmokeTest extends AbstractCliTest { - - public static void main(String[] args) { - var test = new EditXmrOffersSmokeTest(); - - test.doOfferPriceEdits(); - - List offers = test.getMyCryptoOffers("xmr"); - test.disableOffers(offers); - - test.sleep(6); - - offers = test.getMyCryptoOffers("xmr"); - test.enableOffers(offers); - - // A final look after last edit. - test.getMyCryptoOffers("xmr"); - } - - private void doOfferPriceEdits() { - editPriceMargin(); - editTriggerPrice(); - editPriceMarginAndTriggerPrice(); - editFixedPrice(); - } - - private void editPriceMargin() { - var offers = getMyCryptoOffers("xmr"); - out.println("Edit XMR offers' price margin"); - var margins = randomMarginBasedPrices.apply(-301, 300); - for (int i = 0; i < offers.size(); i++) { - String randomMargin = margins.get(new Random().nextInt(margins.size())); - editOfferPriceMargin(offers.get(i), randomMargin, new Random().nextBoolean()); - sleep(5); - } - } - - private void editTriggerPrice() { - var offers = getMyCryptoOffers("xmr"); - out.println("Edit XMR offers' trigger price"); - for (int i = 0; i < offers.size(); i++) { - var offer = offers.get(i); - if (offer.getUseMarketBasedPrice()) { - // Trigger price is hardcode to be a bit above or below xmr mkt price at runtime. - // It could be looked up and calculated instead. - var newTriggerPrice = offer.getDirection().equals(BUY.name()) ? "0.0039" : "0.005"; - editOfferTriggerPrice(offer, newTriggerPrice, true); - sleep(5); - } - } - } - - private void editPriceMarginAndTriggerPrice() { - var offers = getMyCryptoOffers("xmr"); - out.println("Edit XMR offers' price margin and trigger price"); - for (int i = 0; i < offers.size(); i++) { - var offer = offers.get(i); - if (offer.getUseMarketBasedPrice()) { - // Trigger price is hardcode to be a bit above or below xmr mkt price at runtime. - // It could be looked up and calculated instead. - var newTriggerPrice = offer.getDirection().equals(BUY.name()) ? "0.0038" : "0.0051"; - editOfferPriceMarginAndTriggerPrice(offer, "0.05", newTriggerPrice, true); - sleep(5); - } - } - } - - private void editFixedPrice() { - var offers = getMyCryptoOffers("xmr"); - out.println("Edit XMR offers' fixed price"); - for (int i = 0; i < offers.size(); i++) { - String randomFixedPrice = randomFixedCryptoPrice.apply(0.004, 0.0075); - editOfferFixedPrice(offers.get(i), randomFixedPrice, new Random().nextBoolean()); - sleep(5); - } - } -} diff --git a/bot/src/test/java/haveno/cli/GetOffersSmokeTest.java b/bot/src/test/java/haveno/cli/GetOffersSmokeTest.java deleted file mode 100644 index 2e99581b..00000000 --- a/bot/src/test/java/haveno/cli/GetOffersSmokeTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package haveno.cli; - -import static java.lang.System.out; - -/** - Smoke tests for getoffers method. Useful for examining the format of the console output. - - Prerequisites: - - - Run `./haveno-daemon --apiPassword=xyz --appDataDir=$TESTDIR` - - This can be run on mainnet. - */ -@SuppressWarnings({"CommentedOutCode", "unused"}) -public class GetOffersSmokeTest extends AbstractCliTest { - - // TODO use the static password and port opt definitions in superclass - - public static void main(String[] args) { - getMyBsqOffers(); - // getAvailableBsqOffers(); - // getMyUsdOffers(); - // getAvailableUsdOffers(); - } - - private static void getMyBsqOffers() { - out.println(">>> getmyoffers buy bsq"); - CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffers", "--direction=buy", "--currency-code=bsq"}); - out.println(">>> getmyoffers sell bsq"); - CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffers", "--direction=sell", "--currency-code=bsq"}); - out.println(">>> getmyoffer --offer-id=KRONTTMO-11cef1a9-c636-4dc7-b3f2-1616e4960c28-175"); - CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffer", "--offer-id=KRONTTMO-11cef1a9-c636-4dc7-b3f2-1616e4960c28-175"}); - } - - private static void getAvailableBsqOffers() { - out.println(">>> getoffers buy bsq"); - CliMain.main(new String[]{"--password=xyz", "--port=9998", "getoffers", "--direction=buy", "--currency-code=bsq"}); - out.println(">>> getoffers sell bsq"); - CliMain.main(new String[]{"--password=xyz", "--port=9998", "getoffers", "--direction=sell", "--currency-code=bsq"}); - } - - private static void getMyUsdOffers() { - out.println(">>> getmyoffers buy usd"); - CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffers", "--direction=buy", "--currency-code=usd"}); - out.println(">>> getmyoffers sell usd"); - CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffers", "--direction=sell", "--currency-code=usd"}); - } - - private static void getAvailableUsdOffers() { - out.println(">>> getoffers buy usd"); - CliMain.main(new String[]{"--password=xyz", "--port=9998", "getoffers", "--direction=buy", "--currency-code=usd"}); - out.println(">>> getoffers sell usd"); - CliMain.main(new String[]{"--password=xyz", "--port=9998", "getoffers", "--direction=sell", "--currency-code=usd"}); - } - - private static void TODO() { - 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"}); - } -} diff --git a/bot/src/test/java/haveno/cli/GetTradesSmokeTest.java b/bot/src/test/java/haveno/cli/GetTradesSmokeTest.java deleted file mode 100644 index ae8b4b1a..00000000 --- a/bot/src/test/java/haveno/cli/GetTradesSmokeTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package haveno.cli; - -import haveno.cli.table.builder.TableBuilder; -import haveno.proto.grpc.TradeInfo; - -import java.util.List; - -import static haveno.cli.table.builder.TableType.TRADE_DETAIL_TBL; -import static haveno.proto.grpc.GetTradesRequest.Category.CLOSED; -import static java.lang.System.out; - -@SuppressWarnings("unused") -public class GetTradesSmokeTest extends AbstractCliTest { - - public static void main(String[] args) { - GetTradesSmokeTest test = new GetTradesSmokeTest(); - test.printAlicesTrades(); - test.printBobsTrades(); - } - - private final List openTrades; - private final List closedTrades; - - public GetTradesSmokeTest() { - super(); - this.openTrades = aliceClient.getOpenTrades(); - this.closedTrades = aliceClient.getTradeHistory(CLOSED); - } - - private void printAlicesTrades() { - out.println("ALICE'S OPEN TRADES"); - openTrades.stream().forEachOrdered(t -> printTrade(aliceClient, t.getTradeId())); - out.println("ALICE'S CLOSED TRADES"); - closedTrades.stream().forEachOrdered(t -> printTrade(aliceClient, t.getTradeId())); - } - - private void printBobsTrades() { - out.println("BOB'S OPEN TRADES"); - openTrades.stream().forEachOrdered(t -> printTrade(bobClient, t.getTradeId())); - out.println("BOB'S CLOSED TRADES"); - closedTrades.stream().forEachOrdered(t -> printTrade(bobClient, t.getTradeId())); - } - - private void printTrade(GrpcClient client, String tradeId) { - var trade = client.getTrade(tradeId); - var tbl = new TableBuilder(TRADE_DETAIL_TBL, trade).build().toString(); - out.println(tbl); - } -} diff --git a/bot/src/test/java/haveno/cli/opts/OptionParsersTest.java b/bot/src/test/java/haveno/cli/opts/OptionParsersTest.java deleted file mode 100644 index 9df6b012..00000000 --- a/bot/src/test/java/haveno/cli/opts/OptionParsersTest.java +++ /dev/null @@ -1,248 +0,0 @@ -package haveno.cli.opts; - -import org.junit.jupiter.api.Test; - -import static haveno.cli.Method.canceloffer; -import static haveno.cli.Method.createcryptopaymentacct; -import static haveno.cli.Method.createoffer; -import static haveno.cli.Method.createpaymentacct; -import static haveno.cli.opts.OptLabel.OPT_ACCOUNT_NAME; -import static haveno.cli.opts.OptLabel.OPT_ADDRESS; -import static haveno.cli.opts.OptLabel.OPT_AMOUNT; -import static haveno.cli.opts.OptLabel.OPT_CURRENCY_CODE; -import static haveno.cli.opts.OptLabel.OPT_DIRECTION; -import static haveno.cli.opts.OptLabel.OPT_MKT_PRICE_MARGIN; -import static haveno.cli.opts.OptLabel.OPT_OFFER_ID; -import static haveno.cli.opts.OptLabel.OPT_PASSWORD; -import static haveno.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_FORM; -import static haveno.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_ID; -import static haveno.cli.opts.OptLabel.OPT_SECURITY_DEPOSIT; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - - -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 (v1) opt parser tests - - @Test - public void testCreateOfferWithMissingPaymentAccountIdOptShouldThrowException() { - String[] args = new String[]{ - PASSWORD_OPT, - createoffer.name(), - "--" + OPT_DIRECTION + "=" + "SELL", - "--" + OPT_CURRENCY_CODE + "=" + "JPY", - "--" + OPT_AMOUNT + "=" + "0.1" - }; - Throwable exception = assertThrows(RuntimeException.class, () -> - new CreateOfferOptionParser(args).parse()); - assertEquals("no payment account id specified", exception.getMessage()); - } - - @Test - public void testCreateOfferWithEmptyPaymentAccountIdOptShouldThrowException() { - String[] args = new String[]{ - PASSWORD_OPT, - createoffer.name(), - "--" + OPT_PAYMENT_ACCOUNT_ID - }; - Throwable exception = assertThrows(RuntimeException.class, () -> - new CreateOfferOptionParser(args).parse()); - assertEquals("payment-account-id requires an argument", exception.getMessage()); - } - - @Test - public void testCreateOfferWithMissingDirectionOptShouldThrowException() { - String[] args = new String[]{ - PASSWORD_OPT, - createoffer.name(), - "--" + OPT_PAYMENT_ACCOUNT_ID + "=" + "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 testCreateOfferWithMissingDirectionOptValueShouldThrowException() { - String[] args = new String[]{ - PASSWORD_OPT, - createoffer.name(), - "--" + OPT_PAYMENT_ACCOUNT_ID + "=" + "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_ID + "=" + "abc-payment-acct-id-123", - "--" + OPT_DIRECTION + "=" + "BUY", - "--" + OPT_CURRENCY_CODE + "=" + "EUR", - "--" + OPT_AMOUNT + "=" + "0.125", - "--" + OPT_MKT_PRICE_MARGIN + "=" + "3.15", - "--" + 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(3.15d, parser.getMktPriceMarginPct()); - assertEquals(25.0, parser.getSecurityDepositPct()); - } - - // createpaymentacct opt parser tests - - @Test - public void testCreatePaymentAcctWithMissingPaymentFormOptShouldThrowException() { - 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 testCreatePaymentAcctWithMissingPaymentFormOptValueShouldThrowException() { - 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 testCreatePaymentAcctWithInvalidPaymentFormOptValueShouldThrowException() { - 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 testCreateCryptoCurrencyPaymentAcctWithMissingAcctNameOptShouldThrowException() { - 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 testCreateCryptoCurrencyPaymentAcctWithEmptyAcctNameOptShouldThrowException() { - 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 testCreateCryptoCurrencyPaymentAcctWithInvalidCurrencyCodeOptShouldThrowException() { - 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("api does not support bsq payment accounts", exception.getMessage()); - } - - @Test - public void testCreateBchPaymentAcct() { - var acctName = "bch payment account"; - var currencyCode = "bch"; - var address = "B1nXyZ46XXX"; // 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()); - } -} diff --git a/bot/src/test/java/haveno/cli/table/AddressCliOutputDiffTest.java b/bot/src/test/java/haveno/cli/table/AddressCliOutputDiffTest.java deleted file mode 100644 index 038b4c3e..00000000 --- a/bot/src/test/java/haveno/cli/table/AddressCliOutputDiffTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package haveno.cli.table; - -import haveno.cli.AbstractCliTest; -import haveno.cli.table.builder.TableBuilder; -import haveno.proto.grpc.AddressBalanceInfo; - -import java.util.List; - -import static haveno.cli.table.builder.TableType.ADDRESS_BALANCE_TBL; -import static java.lang.System.err; -import static java.util.Collections.singletonList; - -@SuppressWarnings("unused") -public class AddressCliOutputDiffTest extends AbstractCliTest { - - public static void main(String[] args) { - AddressCliOutputDiffTest test = new AddressCliOutputDiffTest(); - test.getFundingAddresses(); - test.getAddressBalance(); - } - - public AddressCliOutputDiffTest() { - super(); - } - - private void getFundingAddresses() { - var fundingAddresses = aliceClient.getFundingAddresses(); - if (fundingAddresses.size() > 0) { - // TableFormat class had been deprecated, then deleted on 17-Feb-2022, but - // these diff tests can be useful for testing changes to the current tbl formatting api. - // var oldTbl = TableFormat.formatAddressBalanceTbl(fundingAddresses); - var newTbl = new TableBuilder(ADDRESS_BALANCE_TBL, fundingAddresses).build().toString(); - // printOldTbl(oldTbl); - printNewTbl(newTbl); - // checkDiffsIgnoreWhitespace(oldTbl, newTbl); - } else { - err.println("no funding addresses found"); - } - } - - private void getAddressBalance() { - List addresses = aliceClient.getFundingAddresses(); - int numAddresses = addresses.size(); - // Check output for last 2 addresses. - for (int i = numAddresses - 2; i < addresses.size(); i++) { - var addressBalanceInfo = addresses.get(i); - getAddressBalance(addressBalanceInfo.getAddress()); - } - } - - private void getAddressBalance(String address) { - var addressBalance = singletonList(aliceClient.getAddressBalance(address)); - // TableFormat class had been deprecated, then deleted on 17-Feb-2022, but these - // diff tests can be useful for testing changes to the current tbl formatting api. - // var oldTbl = TableFormat.formatAddressBalanceTbl(addressBalance); - var newTbl = new TableBuilder(ADDRESS_BALANCE_TBL, addressBalance).build().toString(); - // printOldTbl(oldTbl); - printNewTbl(newTbl); - // checkDiffsIgnoreWhitespace(oldTbl, newTbl); - } -} diff --git a/bot/src/test/java/haveno/cli/table/GetBalanceCliOutputDiffTest.java b/bot/src/test/java/haveno/cli/table/GetBalanceCliOutputDiffTest.java deleted file mode 100644 index 0a5dbb52..00000000 --- a/bot/src/test/java/haveno/cli/table/GetBalanceCliOutputDiffTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package haveno.cli.table; - -import haveno.cli.AbstractCliTest; -import haveno.cli.table.builder.TableBuilder; - -import static haveno.cli.table.builder.TableType.BTC_BALANCE_TBL; - -@SuppressWarnings("unused") -public class GetBalanceCliOutputDiffTest extends AbstractCliTest { - - public static void main(String[] args) { - GetBalanceCliOutputDiffTest test = new GetBalanceCliOutputDiffTest(); - test.getBtcBalance(); - } - - public GetBalanceCliOutputDiffTest() { - super(); - } - - private void getBtcBalance() { - var balance = aliceClient.getBtcBalances(); - // TableFormat class had been deprecated, then deleted on 17-Feb-2022, but these - // diff tests can be useful for testing changes to the current tbl formatting api. - // var oldTbl = TableFormat.formatBtcBalanceInfoTbl(balance); - var newTbl = new TableBuilder(BTC_BALANCE_TBL, balance).build().toString(); - // printOldTbl(oldTbl); - printNewTbl(newTbl); - // checkDiffsIgnoreWhitespace(oldTbl, newTbl); - } -} diff --git a/bot/src/test/java/haveno/cli/table/GetOffersCliOutputDiffTest.java b/bot/src/test/java/haveno/cli/table/GetOffersCliOutputDiffTest.java deleted file mode 100644 index 76944fbd..00000000 --- a/bot/src/test/java/haveno/cli/table/GetOffersCliOutputDiffTest.java +++ /dev/null @@ -1,123 +0,0 @@ -package haveno.cli.table; - -import haveno.cli.AbstractCliTest; -import haveno.cli.table.builder.TableBuilder; -import haveno.proto.grpc.OfferInfo; -import lombok.extern.slf4j.Slf4j; - -import java.util.List; - -import static haveno.cli.table.builder.TableType.OFFER_TBL; -import static protobuf.OfferDirection.BUY; -import static protobuf.OfferDirection.SELL; - -@SuppressWarnings("unused") -@Slf4j -public class GetOffersCliOutputDiffTest extends AbstractCliTest { - - // "My" offers are always Alice's offers. - // "Available" offers are always Alice's offers available to Bob. - - public static void main(String[] args) { - GetOffersCliOutputDiffTest test = new GetOffersCliOutputDiffTest(); - - test.getMyBuyUsdOffers(); - test.getMySellUsdOffers(); - test.getAvailableBuyUsdOffers(); - test.getAvailableSellUsdOffers(); - - /* - // TODO Uncomment when XMR support is added. - test.getMyBuyXmrOffers(); - test.getMySellXmrOffers(); - test.getAvailableBuyXmrOffers(); - test.getAvailableSellXmrOffers(); - */ - - test.getMyBuyBsqOffers(); - test.getMySellBsqOffers(); - test.getAvailableBuyBsqOffers(); - test.getAvailableSellBsqOffers(); - } - - public GetOffersCliOutputDiffTest() { - super(); - } - - private void getMyBuyUsdOffers() { - var myOffers = aliceClient.getMyOffers(BUY.name(), "USD"); - printAndCheckDiffs(myOffers, BUY.name(), "USD"); - } - - private void getMySellUsdOffers() { - var myOffers = aliceClient.getMyOffers(SELL.name(), "USD"); - printAndCheckDiffs(myOffers, SELL.name(), "USD"); - } - - private void getAvailableBuyUsdOffers() { - var offers = bobClient.getOffers(BUY.name(), "USD"); - printAndCheckDiffs(offers, BUY.name(), "USD"); - } - - private void getAvailableSellUsdOffers() { - var offers = bobClient.getOffers(SELL.name(), "USD"); - printAndCheckDiffs(offers, SELL.name(), "USD"); - } - - private void getMyBuyXmrOffers() { - var myOffers = aliceClient.getMyOffers(BUY.name(), "XMR"); - printAndCheckDiffs(myOffers, BUY.name(), "XMR"); - } - - private void getMySellXmrOffers() { - var myOffers = aliceClient.getMyOffers(SELL.name(), "XMR"); - printAndCheckDiffs(myOffers, SELL.name(), "XMR"); - } - - private void getAvailableBuyXmrOffers() { - var offers = bobClient.getOffers(BUY.name(), "XMR"); - printAndCheckDiffs(offers, BUY.name(), "XMR"); - } - - private void getAvailableSellXmrOffers() { - var offers = bobClient.getOffers(SELL.name(), "XMR"); - printAndCheckDiffs(offers, SELL.name(), "XMR"); - } - - private void getMyBuyBsqOffers() { - var myOffers = aliceClient.getMyOffers(BUY.name(), "BSQ"); - printAndCheckDiffs(myOffers, BUY.name(), "BSQ"); - } - - private void getMySellBsqOffers() { - var myOffers = aliceClient.getMyOffers(SELL.name(), "BSQ"); - printAndCheckDiffs(myOffers, SELL.name(), "BSQ"); - } - - private void getAvailableBuyBsqOffers() { - var offers = bobClient.getOffers(BUY.name(), "BSQ"); - printAndCheckDiffs(offers, BUY.name(), "BSQ"); - } - - private void getAvailableSellBsqOffers() { - var offers = bobClient.getOffers(SELL.name(), "BSQ"); - printAndCheckDiffs(offers, SELL.name(), "BSQ"); - } - - private void printAndCheckDiffs(List offers, - String direction, - String currencyCode) { - if (offers.isEmpty()) { - log.warn("No {} {} offers to print.", direction, currencyCode); - } else { - log.info("Checking for diffs in {} {} offers.", direction, currencyCode); - // OfferFormat class had been deprecated, then deleted on 17-Feb-2022, but - // these diff tests can be useful for testing changes to the current tbl formatting api. - // var oldTbl = OfferFormat.formatOfferTable(offers, currencyCode); - var newTbl = new TableBuilder(OFFER_TBL, offers).build().toString(); - // printOldTbl(oldTbl); - printNewTbl(newTbl); - // checkDiffsIgnoreWhitespace(oldTbl, newTbl); - } - } -} diff --git a/bot/src/test/java/haveno/cli/table/GetTradeCliOutputDiffTest.java b/bot/src/test/java/haveno/cli/table/GetTradeCliOutputDiffTest.java deleted file mode 100644 index 9255cb5d..00000000 --- a/bot/src/test/java/haveno/cli/table/GetTradeCliOutputDiffTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package haveno.cli.table; - -import haveno.cli.AbstractCliTest; -import haveno.cli.GrpcClient; -import haveno.cli.table.builder.TableBuilder; -import lombok.extern.slf4j.Slf4j; - -import static haveno.cli.table.builder.TableType.TRADE_DETAIL_TBL; -import static java.lang.System.out; - -@SuppressWarnings("unused") -@Slf4j -public class GetTradeCliOutputDiffTest extends AbstractCliTest { - - public static void main(String[] args) { - if (args.length == 0) - throw new IllegalStateException("Need a single trade-id program argument."); - - GetTradeCliOutputDiffTest test = new GetTradeCliOutputDiffTest(args[0]); - test.getAlicesTrade(); - out.println(); - test.getBobsTrade(); - } - - private final String tradeId; - - public GetTradeCliOutputDiffTest(String tradeId) { - super(); - this.tradeId = tradeId; - } - - private void getAlicesTrade() { - getTrade(aliceClient); - } - - private void getBobsTrade() { - getTrade(bobClient); - } - - private void getTrade(GrpcClient client) { - var trade = client.getTrade(tradeId); - // TradeFormat class had been deprecated, then deleted on 17-Feb-2022, but these - // diff tests can be useful for testing changes to the current tbl formatting api. - // var oldTbl = TradeFormat.format(trade); - var newTbl = new TableBuilder(TRADE_DETAIL_TBL, trade).build().toString(); - // printOldTbl(oldTbl); - printNewTbl(newTbl); - // checkDiffsIgnoreWhitespace(oldTbl, newTbl); - } -} diff --git a/bot/src/test/java/haveno/cli/table/GetTransactionCliOutputDiffTest.java b/bot/src/test/java/haveno/cli/table/GetTransactionCliOutputDiffTest.java deleted file mode 100644 index f5d1af73..00000000 --- a/bot/src/test/java/haveno/cli/table/GetTransactionCliOutputDiffTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package haveno.cli.table; - -import haveno.cli.AbstractCliTest; -import lombok.extern.slf4j.Slf4j; - -@SuppressWarnings("unused") -@Slf4j -public class GetTransactionCliOutputDiffTest extends AbstractCliTest { - - public static void main(String[] args) { - if (args.length == 0) - throw new IllegalStateException("Need a single transaction-id program argument."); - - GetTransactionCliOutputDiffTest test = new GetTransactionCliOutputDiffTest(args[0]); - } - - private final String transactionId; - - public GetTransactionCliOutputDiffTest(String transactionId) { - super(); - this.transactionId = transactionId; - } -} diff --git a/bot/src/test/java/haveno/cli/table/PaymentAccountsCliOutputDiffTest.java b/bot/src/test/java/haveno/cli/table/PaymentAccountsCliOutputDiffTest.java deleted file mode 100644 index b30a9911..00000000 --- a/bot/src/test/java/haveno/cli/table/PaymentAccountsCliOutputDiffTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package haveno.cli.table; - -import haveno.cli.AbstractCliTest; -import haveno.cli.table.builder.TableBuilder; -import lombok.extern.slf4j.Slf4j; - -import static haveno.cli.table.builder.TableType.PAYMENT_ACCOUNT_TBL; - -@SuppressWarnings("unused") -@Slf4j -public class PaymentAccountsCliOutputDiffTest extends AbstractCliTest { - - public static void main(String[] args) { - PaymentAccountsCliOutputDiffTest test = new PaymentAccountsCliOutputDiffTest(); - test.getPaymentAccounts(); - } - - public PaymentAccountsCliOutputDiffTest() { - super(); - } - - private void getPaymentAccounts() { - var paymentAccounts = aliceClient.getPaymentAccounts(); - if (paymentAccounts.size() > 0) { - // The formatPaymentAcctTbl method had been deprecated, then deleted on 17-Feb-2022, - // but these diff tests can be useful for testing changes to the current tbl formatting api. - // var oldTbl = formatPaymentAcctTbl(paymentAccounts); - var newTbl = new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccounts).build().toString(); - // printOldTbl(oldTbl); - printNewTbl(newTbl); - // checkDiffsIgnoreWhitespace(oldTbl, newTbl); - } else { - log.warn("no payment accounts found"); - } - } - -}