Merge pull request #5081 from ghubstan/04-posix-cli-opts+method-help

Use posix-style CLI opts, provide method help
This commit is contained in:
sqrrm 2021-01-16 13:43:18 +01:00 committed by GitHub
commit 44cbea886a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 2430 additions and 388 deletions

View file

@ -27,7 +27,7 @@
run ./bisq-cli --bogus getversion
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: bogus is not a recognized option" ]
[ "$output" = "Error: missing required 'password' option" ]
}
@test "test missing required password option error" {
@ -61,7 +61,7 @@
}
@test "test setwalletpassword \"a b c\"" {
run ./bisq-cli --password=xyz setwalletpassword "a b c"
run ./bisq-cli --password=xyz setwalletpassword --wallet-password="a b c"
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet encrypted" ]
@ -76,7 +76,7 @@
}
@test "test unlockwallet without timeout arg" {
run ./bisq-cli --password=xyz unlockwallet "a b c"
run ./bisq-cli --password=xyz unlockwallet --wallet-password="a b c"
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: no unlock timeout specified" ]
@ -84,7 +84,7 @@
@test "test unlockwallet \"a b c\" 8" {
run ./bisq-cli --password=xyz unlockwallet "a b c" 8
run ./bisq-cli --password=xyz unlockwallet --wallet-password="a b c" --timeout=8
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet unlocked" ]
@ -97,7 +97,7 @@
}
@test "test unlockwallet \"a b c\" 6" {
run ./bisq-cli --password=xyz unlockwallet "a b c" 6
run ./bisq-cli --password=xyz unlockwallet --wallet-password="a b c" --timeout=6
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet unlocked" ]
@ -111,14 +111,14 @@
}
@test "test setwalletpassword incorrect old pwd error" {
run ./bisq-cli --password=xyz setwalletpassword "z z z" "d e f"
run ./bisq-cli --password=xyz setwalletpassword --wallet-password="z z z" --new-wallet-password="d e f"
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: incorrect old password" ]
}
@test "test setwalletpassword oldpwd newpwd" {
run ./bisq-cli --password=xyz setwalletpassword "a b c" "d e f"
run ./bisq-cli --password=xyz setwalletpassword --wallet-password="a b c" --new-wallet-password="d e f"
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet encrypted with new password" ]
@ -133,7 +133,7 @@
}
@test "test removewalletpassword" {
run ./bisq-cli --password=xyz removewalletpassword "d e f"
run ./bisq-cli --password=xyz removewalletpassword --wallet-password="d e f"
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet decrypted" ]
@ -163,7 +163,7 @@
}
@test "test getaddressbalance bogus address argument" {
run ./bisq-cli --password=xyz getaddressbalance bogus
run ./bisq-cli --password=xyz getaddressbalance --address=bogus
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: address bogus not found in wallet" ]
@ -183,21 +183,21 @@
run ./bisq-cli --password=xyz getoffers
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: incorrect parameter count, expecting direction (buy|sell), currency code" ]
[ "$output" = "Error: no direction (buy|sell) specified" ]
}
@test "test getoffers sell eur check return status" {
run ./bisq-cli --password=xyz getoffers sell eur
run ./bisq-cli --password=xyz getoffers --direction=sell --currency-code=eur
[ "$status" -eq 0 ]
}
@test "test getoffers buy eur check return status" {
run ./bisq-cli --password=xyz getoffers buy eur
run ./bisq-cli --password=xyz getoffers --direction=buy --currency-code=eur
[ "$status" -eq 0 ]
}
@test "test getoffers sell gbp check return status" {
run ./bisq-cli --password=xyz getoffers sell gbp
run ./bisq-cli --password=xyz getoffers --direction=sell --currency-code=gbp
[ "$status" -eq 0 ]
}

View file

@ -0,0 +1,65 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.apitest.method;
import bisq.proto.grpc.GetMethodHelpRequest;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.cli.Method.createoffer;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
@Disabled
@Slf4j
@TestMethodOrder(OrderAnnotation.class)
public class GetMethodHelpTest extends MethodTest {
@BeforeAll
public static void setUp() {
try {
setUpScaffold(alicedaemon);
} catch (Exception ex) {
fail(ex);
}
}
@Test
@Order(1)
public void testGetCreateOfferHelp() {
var help = grpcStubs(alicedaemon).helpService
.getMethodHelp(GetMethodHelpRequest.newBuilder()
.setMethodName(createoffer.name()).build())
.getMethodHelp();
assertNotNull(help);
}
@AfterAll
public static void tearDown() {
tearDownScaffold();
}
}

View file

@ -35,6 +35,7 @@ import bisq.proto.grpc.CreatePaymentAccountRequest;
import bisq.proto.grpc.GetAddressBalanceRequest;
import bisq.proto.grpc.GetBalancesRequest;
import bisq.proto.grpc.GetFundingAddressesRequest;
import bisq.proto.grpc.GetMethodHelpRequest;
import bisq.proto.grpc.GetMyOfferRequest;
import bisq.proto.grpc.GetOfferRequest;
import bisq.proto.grpc.GetPaymentAccountFormRequest;
@ -271,6 +272,10 @@ public class MethodTest extends ApiTestCase {
.build();
}
protected final GetMethodHelpRequest createGetMethodHelpRequest(String methodName) {
return GetMethodHelpRequest.newBuilder().setMethodName(methodName).build();
}
// Convenience methods for calling frequently used & thoroughly tested gRPC services.
protected final BalancesInfo getBalances(BisqAppConfig bisqAppConfig, String currencyCode) {
return grpcStubs(bisqAppConfig).walletsService.getBalances(
@ -490,6 +495,11 @@ public class MethodTest extends ApiTestCase {
return f2FAccount;
}
protected final String getMethodHelp(BisqAppConfig bisqAppConfig, String methodName) {
var req = createGetMethodHelpRequest(methodName);
return grpcStubs(bisqAppConfig).helpService.getMethodHelp(req).getMethodHelp();
}
// Static conveniences for test methods and test case fixture setups.
protected static RegisterDisputeAgentRequest createRegisterDisputeAgentRequest(String disputeAgentType) {

View file

@ -38,6 +38,7 @@ import static org.junit.jupiter.api.Assertions.fail;
import bisq.apitest.method.CallRateMeteringInterceptorTest;
import bisq.apitest.method.GetMethodHelpTest;
import bisq.apitest.method.GetVersionTest;
import bisq.apitest.method.MethodTest;
import bisq.apitest.method.RegisterDisputeAgentsTest;
@ -92,6 +93,13 @@ public class StartupTest extends MethodTest {
test.testRegisterRefundAgent();
}
@Test
@Order(4)
public void testGetCreateOfferHelp() {
GetMethodHelpTest test = new GetMethodHelpTest();
test.testGetCreateOfferHelp();
}
@AfterAll
public static void tearDown() {
tearDownScaffold();

View file

@ -25,6 +25,7 @@ import bisq.proto.grpc.CreatePaymentAccountRequest;
import bisq.proto.grpc.GetAddressBalanceRequest;
import bisq.proto.grpc.GetBalancesRequest;
import bisq.proto.grpc.GetFundingAddressesRequest;
import bisq.proto.grpc.GetMethodHelpRequest;
import bisq.proto.grpc.GetMyOfferRequest;
import bisq.proto.grpc.GetMyOffersRequest;
import bisq.proto.grpc.GetOfferRequest;
@ -69,8 +70,6 @@ import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@ -79,15 +78,43 @@ import lombok.extern.slf4j.Slf4j;
import static bisq.cli.CurrencyFormat.formatTxFeeRateInfo;
import static bisq.cli.CurrencyFormat.toSatoshis;
import static bisq.cli.CurrencyFormat.toSecurityDepositAsPct;
import static bisq.cli.NegativeNumberOptions.hasNegativeNumberOptions;
import static bisq.cli.Method.*;
import static bisq.cli.TableFormat.*;
import static bisq.cli.opts.OptLabel.OPT_HELP;
import static bisq.cli.opts.OptLabel.OPT_HOST;
import static bisq.cli.opts.OptLabel.OPT_PASSWORD;
import static bisq.cli.opts.OptLabel.OPT_PORT;
import static bisq.proto.grpc.HelpGrpc.HelpBlockingStub;
import static java.lang.String.format;
import static java.lang.System.err;
import static java.lang.System.exit;
import static java.lang.System.out;
import static java.math.BigDecimal.ZERO;
import static java.util.Collections.singletonList;
import bisq.cli.opts.ArgumentList;
import bisq.cli.opts.CancelOfferOptionParser;
import bisq.cli.opts.CreateOfferOptionParser;
import bisq.cli.opts.CreatePaymentAcctOptionParser;
import bisq.cli.opts.GetAddressBalanceOptionParser;
import bisq.cli.opts.GetBalanceOptionParser;
import bisq.cli.opts.GetOfferOptionParser;
import bisq.cli.opts.GetOffersOptionParser;
import bisq.cli.opts.GetPaymentAcctFormOptionParser;
import bisq.cli.opts.GetTradeOptionParser;
import bisq.cli.opts.GetTransactionOptionParser;
import bisq.cli.opts.RegisterDisputeAgentOptionParser;
import bisq.cli.opts.RemoveWalletPasswordOptionParser;
import bisq.cli.opts.SendBsqOptionParser;
import bisq.cli.opts.SendBtcOptionParser;
import bisq.cli.opts.SetTxFeeRateOptionParser;
import bisq.cli.opts.SetWalletPasswordOptionParser;
import bisq.cli.opts.SimpleMethodOptionParser;
import bisq.cli.opts.TakeOfferOptionParser;
import bisq.cli.opts.UnlockWalletOptionParser;
import bisq.cli.opts.WithdrawFundsOptionParser;
/**
* A command-line client for the Bisq gRPC API.
*/
@ -95,41 +122,6 @@ import static java.util.Collections.singletonList;
@Slf4j
public class CliMain {
private enum Method {
createoffer,
canceloffer,
getoffer,
getmyoffer,
getoffers,
getmyoffers,
takeoffer,
gettrade,
confirmpaymentstarted,
confirmpaymentreceived,
keepfunds,
withdrawfunds,
getpaymentmethods,
getpaymentacctform,
createpaymentacct,
getpaymentaccts,
getversion,
getbalance,
getaddressbalance,
getfundingaddresses,
getunusedbsqaddress,
sendbsq,
sendbtc,
gettxfeerate,
settxfeerate,
unsettxfeerate,
gettransaction,
lockwallet,
unlockwallet,
removewalletpassword,
setwalletpassword,
registerdisputeagent
}
public static void main(String[] args) {
try {
run(args);
@ -142,48 +134,47 @@ public class CliMain {
public static void run(String[] args) {
var parser = new OptionParser();
var helpOpt = parser.accepts("help", "Print this help text")
var helpOpt = parser.accepts(OPT_HELP, "Print this help text")
.forHelp();
var hostOpt = parser.accepts("host", "rpc server hostname or IP")
var hostOpt = parser.accepts(OPT_HOST, "rpc server hostname or ip")
.withRequiredArg()
.defaultsTo("localhost");
var portOpt = parser.accepts("port", "rpc server port")
var portOpt = parser.accepts(OPT_PORT, "rpc server port")
.withRequiredArg()
.ofType(Integer.class)
.defaultsTo(9998);
var passwordOpt = parser.accepts("password", "rpc server password")
var passwordOpt = parser.accepts(OPT_PASSWORD, "rpc server password")
.withRequiredArg();
var negativeNumberOpts = hasNegativeNumberOptions(args)
? new NegativeNumberOptions()
: null;
// Cache any negative number params that will not be accepted by the parser.
if (negativeNumberOpts != null)
args = negativeNumberOpts.removeNegativeNumberOptions(args);
// Parse the options after temporarily removing any negative number params we
// do not want the parser recognizing as invalid option arguments, e.g., -0.05.
OptionSet options = parser.parse(args);
if (options.has(helpOpt)) {
printHelp(parser, out);
return;
}
// Parse the CLI opts host, port, password, method name, and help. The help opt
// may indicate the user is asking for method level help, and will be excluded
// from the parsed options if a method opt is present in String[] args.
OptionSet options = parser.parse(new ArgumentList(args).getCLIArguments());
@SuppressWarnings("unchecked")
var nonOptionArgs = (List<String>) options.nonOptionArguments();
if (nonOptionArgs.isEmpty()) {
// 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");
}
// Restore any cached negative number params to the nonOptionArgs list.
if (negativeNumberOpts != null)
nonOptionArgs = negativeNumberOpts.restoreNegativeNumberOptions(nonOptionArgs);
// 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;
@ -193,14 +184,9 @@ public class CliMain {
throw new IllegalArgumentException(format("'%s' is not a supported method", methodName));
}
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");
GrpcStubs grpcStubs = new GrpcStubs(host, port, password);
var disputeAgentsService = grpcStubs.disputeAgentsService;
var helpService = grpcStubs.helpService;
var offersService = grpcStubs.offersService;
var paymentAccountsService = grpcStubs.paymentAccountsService;
var tradesService = grpcStubs.tradesService;
@ -210,15 +196,22 @@ public class CliMain {
try {
switch (method) {
case getversion: {
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var request = GetVersionRequest.newBuilder().build();
var version = versionService.getVersion(request).getVersion();
out.println(version);
return;
}
case getbalance: {
var currencyCode = nonOptionArgs.size() == 2
? nonOptionArgs.get(1)
: "";
var opts = new GetBalanceOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var currencyCode = opts.getCurrencyCode();
var request = GetBalancesRequest.newBuilder()
.setCurrencyCode(currencyCode)
.build();
@ -238,41 +231,50 @@ public class CliMain {
return;
}
case getaddressbalance: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("no address specified");
var opts = new GetAddressBalanceOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var address = opts.getAddress();
var request = GetAddressBalanceRequest.newBuilder()
.setAddress(nonOptionArgs.get(1)).build();
.setAddress(address).build();
var reply = walletsService.getAddressBalance(request);
out.println(formatAddressBalanceTbl(singletonList(reply.getAddressBalanceInfo())));
return;
}
case getfundingaddresses: {
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var request = GetFundingAddressesRequest.newBuilder().build();
var reply = walletsService.getFundingAddresses(request);
out.println(formatAddressBalanceTbl(reply.getAddressBalanceInfoList()));
return;
}
case getunusedbsqaddress: {
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var request = GetUnusedBsqAddressRequest.newBuilder().build();
var reply = walletsService.getUnusedBsqAddress(request);
out.println(reply.getAddress());
return;
}
case sendbsq: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("no bsq address specified");
var address = nonOptionArgs.get(1);
if (nonOptionArgs.size() < 3)
throw new IllegalArgumentException("no bsq amount specified");
var amount = nonOptionArgs.get(2);
var opts = new SendBsqOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var address = opts.getAddress();
var amount = opts.getAmount();
verifyStringIsValidDecimal(amount);
var txFeeRate = nonOptionArgs.size() == 4 ? nonOptionArgs.get(3) : "";
if (!txFeeRate.isEmpty())
var txFeeRate = opts.getFeeRate();
if (txFeeRate.isEmpty())
verifyStringIsValidLong(txFeeRate);
var request = SendBsqRequest.newBuilder()
@ -289,24 +291,20 @@ public class CliMain {
return;
}
case sendbtc: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("no btc address specified");
var address = nonOptionArgs.get(1);
if (nonOptionArgs.size() < 3)
throw new IllegalArgumentException("no btc amount specified");
var amount = nonOptionArgs.get(2);
var opts = new SendBtcOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var address = opts.getAddress();
var amount = opts.getAmount();
verifyStringIsValidDecimal(amount);
// TODO Find a better way to handle the two optional parameters.
var txFeeRate = nonOptionArgs.size() >= 4 ? nonOptionArgs.get(3) : "";
if (!txFeeRate.isEmpty())
var txFeeRate = opts.getFeeRate();
if (txFeeRate.isEmpty())
verifyStringIsValidLong(txFeeRate);
var memo = nonOptionArgs.size() == 5 ? nonOptionArgs.get(4) : "";
var memo = opts.getMemo();
var request = SendBtcRequest.newBuilder()
.setAddress(address)
.setAmount(amount)
@ -322,16 +320,22 @@ public class CliMain {
return;
}
case gettxfeerate: {
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var request = GetTxFeeRateRequest.newBuilder().build();
var reply = walletsService.getTxFeeRate(request);
out.println(formatTxFeeRateInfo(reply.getTxFeeRateInfo()));
return;
}
case settxfeerate: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("no tx fee rate specified");
var txFeeRate = toLong(nonOptionArgs.get(2));
var opts = new SetTxFeeRateOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var txFeeRate = toLong(opts.getFeeRate());
var request = SetTxFeeRatePreferenceRequest.newBuilder()
.setTxFeeRatePreference(txFeeRate)
.build();
@ -340,16 +344,22 @@ public class CliMain {
return;
}
case unsettxfeerate: {
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var request = UnsetTxFeeRatePreferenceRequest.newBuilder().build();
var reply = walletsService.unsetTxFeeRatePreference(request);
out.println(formatTxFeeRateInfo(reply.getTxFeeRateInfo()));
return;
}
case gettransaction: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("no tx id specified");
var txId = nonOptionArgs.get(1);
var opts = new GetTransactionOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var txId = opts.getTxId();
var request = GetTransactionRequest.newBuilder()
.setTxId(txId)
.build();
@ -358,30 +368,21 @@ public class CliMain {
return;
}
case createoffer: {
if (nonOptionArgs.size() < 9)
throw new IllegalArgumentException("incorrect parameter count,"
+ " expecting payment acct id, buy | sell, currency code, amount, min amount,"
+ " use-market-based-price, fixed-price | mkt-price-margin, security-deposit"
+ " [,maker-fee-currency-code = bsq|btc]");
var paymentAcctId = nonOptionArgs.get(1);
var direction = nonOptionArgs.get(2);
var currencyCode = nonOptionArgs.get(3);
var amount = toSatoshis(nonOptionArgs.get(4));
var minAmount = toSatoshis(nonOptionArgs.get(5));
var useMarketBasedPrice = Boolean.parseBoolean(nonOptionArgs.get(6));
var fixedPrice = ZERO.toString();
var marketPriceMargin = ZERO;
if (useMarketBasedPrice)
marketPriceMargin = new BigDecimal(nonOptionArgs.get(7));
else
fixedPrice = nonOptionArgs.get(7);
var securityDeposit = toSecurityDepositAsPct(nonOptionArgs.get(8));
var makerFeeCurrencyCode = nonOptionArgs.size() == 10
? nonOptionArgs.get(9)
: "btc";
var opts = new CreateOfferOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var paymentAcctId = opts.getPaymentAccountId();
var direction = opts.getDirection();
var currencyCode = opts.getCurrencyCode();
var amount = toSatoshis(opts.getAmount());
var minAmount = toSatoshis(opts.getMinAmount());
var useMarketBasedPrice = opts.isUsingMktPriceMargin();
var fixedPrice = opts.getFixedPrice();
var marketPriceMargin = opts.getMktPriceMarginAsBigDecimal();
var securityDeposit = toSecurityDepositAsPct(opts.getSecurityDeposit());
var makerFeeCurrencyCode = opts.getMakerFeeCurrencyCode();
var request = CreateOfferRequest.newBuilder()
.setDirection(direction)
.setCurrencyCode(currencyCode)
@ -399,10 +400,12 @@ public class CliMain {
return;
}
case canceloffer: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("incorrect parameter count, expecting offer id");
var offerId = nonOptionArgs.get(1);
var opts = new CancelOfferOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var offerId = opts.getOfferId();
var request = CancelOfferRequest.newBuilder()
.setId(offerId)
.build();
@ -411,10 +414,12 @@ public class CliMain {
return;
}
case getoffer: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("incorrect parameter count, expecting offer id");
var offerId = nonOptionArgs.get(1);
var opts = new GetOfferOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var offerId = opts.getOfferId();
var request = GetOfferRequest.newBuilder()
.setId(offerId)
.build();
@ -424,10 +429,12 @@ public class CliMain {
return;
}
case getmyoffer: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("incorrect parameter count, expecting offer id");
var offerId = nonOptionArgs.get(1);
var opts = new GetOfferOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var offerId = opts.getOfferId();
var request = GetMyOfferRequest.newBuilder()
.setId(offerId)
.build();
@ -437,13 +444,13 @@ public class CliMain {
return;
}
case getoffers: {
if (nonOptionArgs.size() < 3)
throw new IllegalArgumentException("incorrect parameter count,"
+ " expecting direction (buy|sell), currency code");
var direction = nonOptionArgs.get(1);
var currencyCode = nonOptionArgs.get(2);
var opts = new GetOffersOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var direction = opts.getDirection();
var currencyCode = opts.getCurrencyCode();
var request = GetOffersRequest.newBuilder()
.setDirection(direction)
.setCurrencyCode(currencyCode)
@ -459,13 +466,13 @@ public class CliMain {
return;
}
case getmyoffers: {
if (nonOptionArgs.size() < 3)
throw new IllegalArgumentException("incorrect parameter count,"
+ " expecting direction (buy|sell), currency code");
var direction = nonOptionArgs.get(1);
var currencyCode = nonOptionArgs.get(2);
var opts = new GetOffersOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var direction = opts.getDirection();
var currencyCode = opts.getCurrencyCode();
var request = GetMyOffersRequest.newBuilder()
.setDirection(direction)
.setCurrencyCode(currencyCode)
@ -481,16 +488,14 @@ public class CliMain {
return;
}
case takeoffer: {
if (nonOptionArgs.size() < 3)
throw new IllegalArgumentException("incorrect parameter count, " +
" expecting offer id, payment acct id [,taker fee currency code = bsq|btc]");
var offerId = nonOptionArgs.get(1);
var paymentAccountId = nonOptionArgs.get(2);
var takerFeeCurrencyCode = nonOptionArgs.size() == 4
? nonOptionArgs.get(3)
: "btc";
var opts = new TakeOfferOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var offerId = opts.getOfferId();
var paymentAccountId = opts.getPaymentAccountId();
var takerFeeCurrencyCode = opts.getTakerFeeCurrencyCode();
var request = TakeOfferRequest.newBuilder()
.setOfferId(offerId)
.setPaymentAccountId(paymentAccountId)
@ -502,30 +507,32 @@ public class CliMain {
}
case gettrade: {
// TODO make short-id a valid argument?
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("incorrect parameter count, "
+ " expecting trade id [,showcontract = true|false]");
var tradeId = nonOptionArgs.get(1);
var showContract = false;
if (nonOptionArgs.size() == 3)
showContract = Boolean.getBoolean(nonOptionArgs.get(2));
var opts = new GetTradeOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var tradeId = opts.getTradeId();
var showContract = opts.getShowContract();
var request = GetTradeRequest.newBuilder()
.setTradeId(tradeId)
.build();
var reply = tradesService.getTrade(request);
if (showContract)
out.println(reply.getTrade().getContractAsJson());
else
out.println(TradeFormat.format(reply.getTrade()));
return;
}
case confirmpaymentstarted: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("incorrect parameter count, expecting trade id");
var tradeId = nonOptionArgs.get(1);
var opts = new GetTradeOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var tradeId = opts.getTradeId();
var request = ConfirmPaymentStartedRequest.newBuilder()
.setTradeId(tradeId)
.build();
@ -534,10 +541,12 @@ public class CliMain {
return;
}
case confirmpaymentreceived: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("incorrect parameter count, expecting trade id");
var tradeId = nonOptionArgs.get(1);
var opts = new GetTradeOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var tradeId = opts.getTradeId();
var request = ConfirmPaymentReceivedRequest.newBuilder()
.setTradeId(tradeId)
.build();
@ -546,10 +555,12 @@ public class CliMain {
return;
}
case keepfunds: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("incorrect parameter count, expecting trade id");
var tradeId = nonOptionArgs.get(1);
var opts = new GetTradeOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var tradeId = opts.getTradeId();
var request = KeepFundsRequest.newBuilder()
.setTradeId(tradeId)
.build();
@ -558,16 +569,15 @@ public class CliMain {
return;
}
case withdrawfunds: {
if (nonOptionArgs.size() < 3)
throw new IllegalArgumentException("incorrect parameter count, "
+ " expecting trade id, bitcoin wallet address [,\"memo\"]");
var tradeId = nonOptionArgs.get(1);
var address = nonOptionArgs.get(2);
// A multi-word memo must be double quoted.
var memo = nonOptionArgs.size() == 4
? nonOptionArgs.get(3)
: "";
var opts = new WithdrawFundsOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var tradeId = opts.getTradeId();
var address = opts.getAddress();
// Multi-word memos must be double quoted.
var memo = opts.getMemo();
var request = WithdrawFundsRequest.newBuilder()
.setTradeId(tradeId)
.setAddress(address)
@ -578,16 +588,22 @@ public class CliMain {
return;
}
case getpaymentmethods: {
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var request = GetPaymentMethodsRequest.newBuilder().build();
var reply = paymentAccountsService.getPaymentMethods(request);
reply.getPaymentMethodsList().forEach(p -> out.println(p.getId()));
return;
}
case getpaymentacctform: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("incorrect parameter count, expecting payment method id");
var paymentMethodId = nonOptionArgs.get(1);
var opts = new GetPaymentAcctFormOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var paymentMethodId = opts.getPaymentMethodId();
var request = GetPaymentAccountFormRequest.newBuilder()
.setPaymentMethodId(paymentMethodId)
.build();
@ -602,22 +618,18 @@ public class CliMain {
return;
}
case createpaymentacct: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException(
"incorrect parameter count, expecting path to payment account form");
var paymentAccountFormPath = Paths.get(nonOptionArgs.get(1));
if (!paymentAccountFormPath.toFile().exists())
throw new IllegalStateException(
format("payment account form '%s' could not be found",
paymentAccountFormPath.toString()));
var opts = new CreatePaymentAcctOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var paymentAccountForm = opts.getPaymentAcctForm();
String jsonString;
try {
jsonString = new String(Files.readAllBytes(paymentAccountFormPath));
jsonString = new String(Files.readAllBytes(paymentAccountForm));
} catch (IOException e) {
throw new IllegalStateException(
format("could not read %s", paymentAccountFormPath.toString()));
format("could not read %s", paymentAccountForm.toString()));
}
var request = CreatePaymentAccountRequest.newBuilder()
@ -629,6 +641,10 @@ public class CliMain {
return;
}
case getpaymentaccts: {
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var request = GetPaymentAccountsRequest.newBuilder().build();
var reply = paymentAccountsService.getPaymentAccounts(request);
@ -641,56 +657,66 @@ public class CliMain {
return;
}
case lockwallet: {
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var request = LockWalletRequest.newBuilder().build();
walletsService.lockWallet(request);
out.println("wallet locked");
return;
}
case unlockwallet: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("no password specified");
if (nonOptionArgs.size() < 3)
throw new IllegalArgumentException("no unlock timeout specified");
var timeout = toLong(nonOptionArgs.get(2));
var opts = new UnlockWalletOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var walletPassword = opts.getPassword();
var timeout = opts.getUnlockTimeout();
var request = UnlockWalletRequest.newBuilder()
.setPassword(nonOptionArgs.get(1))
.setPassword(walletPassword)
.setTimeout(timeout).build();
walletsService.unlockWallet(request);
out.println("wallet unlocked");
return;
}
case removewalletpassword: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("no password specified");
var opts = new RemoveWalletPasswordOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var walletPassword = opts.getPassword();
var request = RemoveWalletPasswordRequest.newBuilder()
.setPassword(nonOptionArgs.get(1)).build();
.setPassword(walletPassword).build();
walletsService.removeWalletPassword(request);
out.println("wallet decrypted");
return;
}
case setwalletpassword: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("no password specified");
var opts = new SetWalletPasswordOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var walletPassword = opts.getPassword();
var newWalletPassword = opts.getNewPassword();
var requestBuilder = SetWalletPasswordRequest.newBuilder()
.setPassword(nonOptionArgs.get(1));
var hasNewPassword = nonOptionArgs.size() == 3;
if (hasNewPassword)
requestBuilder.setNewPassword(nonOptionArgs.get(2));
.setPassword(walletPassword)
.setNewPassword(newWalletPassword);
walletsService.setWalletPassword(requestBuilder.build());
out.println("wallet encrypted" + (hasNewPassword ? " with new password" : ""));
out.println("wallet encrypted" + (!newWalletPassword.isEmpty() ? " with new password" : ""));
return;
}
case registerdisputeagent: {
if (nonOptionArgs.size() < 3)
throw new IllegalArgumentException(
"incorrect parameter count, expecting dispute agent type, registration key");
var disputeAgentType = nonOptionArgs.get(1);
var registrationKey = nonOptionArgs.get(2);
var opts = new RegisterDisputeAgentOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(getMethodHelp(helpService, method));
return;
}
var disputeAgentType = opts.getDisputeAgentType();
var registrationKey = opts.getRegistrationKey();
var requestBuilder = RegisterDisputeAgentRequest.newBuilder()
.setDisputeAgentType(disputeAgentType).setRegistrationKey(registrationKey);
disputeAgentsService.registerDisputeAgent(requestBuilder.build());
@ -759,7 +785,7 @@ public class CliMain {
}
}
private static void printHelp(OptionParser parser, PrintStream stream) {
private static void printHelp(OptionParser parser, @SuppressWarnings("SameParameterValue") PrintStream stream) {
try {
stream.println("Bisq RPC Client");
stream.println();
@ -770,45 +796,93 @@ public class CliMain {
String rowFormat = "%-24s%-52s%s%n";
stream.format(rowFormat, "Method", "Params", "Description");
stream.format(rowFormat, "------", "------", "------------");
stream.format(rowFormat, "getversion", "", "Get server version");
stream.format(rowFormat, "getbalance", "[currency code = bsq|btc]", "Get server wallet balances");
stream.format(rowFormat, "getaddressbalance", "address", "Get server wallet address balance");
stream.format(rowFormat, "getfundingaddresses", "", "Get BTC funding addresses");
stream.format(rowFormat, "getunusedbsqaddress", "", "Get unused BSQ address");
stream.format(rowFormat, "sendbsq", "address, amount [,tx fee rate (sats/byte)]", "Send BSQ");
stream.format(rowFormat, "sendbtc", "address, amount [,tx fee rate (sats/byte), \"memo\"]", "Send BTC");
stream.format(rowFormat, "gettxfeerate", "", "Get current tx fee rate in sats/byte");
stream.format(rowFormat, "settxfeerate", "satoshis (per byte)", "Set custom tx fee rate in sats/byte");
stream.format(rowFormat, "unsettxfeerate", "", "Unset custom tx fee rate");
stream.format(rowFormat, "gettransaction", "transaction id", "Get transaction with id");
stream.format(rowFormat, "createoffer", "payment acct id, buy | sell, currency code, \\", "Create and place an offer");
stream.format(rowFormat, "", "amount (btc), min amount, use mkt based price, \\", "");
stream.format(rowFormat, "", "fixed price (btc) | mkt price margin (%), security deposit (%) \\", "");
stream.format(rowFormat, "", "[,maker fee currency code = bsq|btc]", "");
stream.format(rowFormat, "canceloffer", "offer id", "Cancel offer with id");
stream.format(rowFormat, "getoffer", "offer id", "Get current offer with id");
stream.format(rowFormat, "getmyoffer", "offer id", "Get my current offer with id");
stream.format(rowFormat, "getoffers", "buy | sell, currency code", "Get current offers");
stream.format(rowFormat, "getmyoffers", "buy | sell, currency code", "Get my current offers");
stream.format(rowFormat, "takeoffer", "offer id [,taker fee currency code = bsq|btc]", "Take offer with id");
stream.format(rowFormat, "gettrade", "trade id [,showcontract = true|false]", "Get trade summary or full contract");
stream.format(rowFormat, "confirmpaymentstarted", "trade id", "Confirm payment started");
stream.format(rowFormat, "confirmpaymentreceived", "trade id", "Confirm payment received");
stream.format(rowFormat, "keepfunds", "trade id", "Keep received funds in Bisq wallet");
stream.format(rowFormat, "withdrawfunds", "trade id, bitcoin wallet address [,\"memo\"]",
stream.format(rowFormat, getversion.name(), "", "Get server version");
stream.println();
stream.format(rowFormat, getbalance.name(), "[--currency-code=<bsq|btc>]", "Get server wallet balances");
stream.println();
stream.format(rowFormat, getaddressbalance.name(), "--address=<btc-address>", "Get server wallet address balance");
stream.println();
stream.format(rowFormat, getfundingaddresses.name(), "", "Get BTC funding addresses");
stream.println();
stream.format(rowFormat, getunusedbsqaddress.name(), "", "Get unused BSQ address");
stream.println();
stream.format(rowFormat, sendbsq.name(), "--address=<btc-address> --amount=<btc-amount> \\", "Send BSQ");
stream.format(rowFormat, "", "[--tx-fee-rate=<sats/byte>]", "");
stream.println();
stream.format(rowFormat, sendbtc.name(), "--address=<bsq-address> --amount=<bsq-amount> \\", "Send BTC");
stream.format(rowFormat, "", "[--tx-fee-rate=<sats/byte>]", "");
stream.println();
stream.format(rowFormat, gettxfeerate.name(), "", "Get current tx fee rate in sats/byte");
stream.println();
stream.format(rowFormat, settxfeerate.name(), "--tx-fee-rate=<sats/byte>", "Set custom tx fee rate in sats/byte");
stream.println();
stream.format(rowFormat, unsettxfeerate.name(), "", "Unset custom tx fee rate");
stream.println();
stream.format(rowFormat, gettransaction.name(), "--transaction-id=<transaction-id>", "Get transaction with id");
stream.println();
stream.format(rowFormat, createoffer.name(), "--payment-account=<payment-account-id> \\", "Create and place an offer");
stream.format(rowFormat, "", "--direction=<buy|sell> \\", "");
stream.format(rowFormat, "", "--currency-code=<currency-code> \\", "");
stream.format(rowFormat, "", "--amount=<btc-amount> \\", "");
stream.format(rowFormat, "", "[--min-amount=<min-btc-amount>] \\", "");
stream.format(rowFormat, "", "--fixed-price=<price> | --market-price=margin=<percent> \\", "");
stream.format(rowFormat, "", "--security-deposit=<percent> \\", "");
stream.format(rowFormat, "", "[--fee-currency=<bsq|btc>]", "");
stream.println();
stream.format(rowFormat, canceloffer.name(), "--offer-id=<offer-id>", "Cancel offer with id");
stream.println();
stream.format(rowFormat, getoffer.name(), "--offer-id=<offer-id>", "Get current offer with id");
stream.println();
stream.format(rowFormat, getmyoffer.name(), "--offer-id=<offer-id>", "Get my current offer with id");
stream.println();
stream.format(rowFormat, getoffers.name(), "--direction=<buy|sell> \\", "Get current offers");
stream.format(rowFormat, "", "--currency-code=<currency-code>", "");
stream.println();
stream.format(rowFormat, getmyoffers.name(), "--direction=<buy|sell> \\", "Get my current offers");
stream.format(rowFormat, "", "--currency-code=<currency-code>", "");
stream.println();
stream.format(rowFormat, takeoffer.name(), "--offer-id=<offer-id> [--fee-currency=<btc|bsq>]", "Take offer with id");
stream.println();
stream.format(rowFormat, gettrade.name(), "--trade-id=<trade-id> \\", "Get trade summary or full contract");
stream.format(rowFormat, "", "[--show-contract=<true|false>]", "");
stream.println();
stream.format(rowFormat, confirmpaymentstarted.name(), "--trade-id=<trade-id>", "Confirm payment started");
stream.println();
stream.format(rowFormat, confirmpaymentreceived.name(), "--trade-id=<trade-id>", "Confirm payment received");
stream.println();
stream.format(rowFormat, keepfunds.name(), "--trade-id=<trade-id>", "Keep received funds in Bisq wallet");
stream.println();
stream.format(rowFormat, withdrawfunds.name(), "--trade-id=<trade-id> --address=<btc-address> \\",
"Withdraw received funds to external wallet address");
stream.format(rowFormat, "getpaymentmethods", "", "Get list of supported payment account method ids");
stream.format(rowFormat, "getpaymentacctform", "payment method id", "Get a new payment account form");
stream.format(rowFormat, "createpaymentacct", "path to payment account form", "Create a new payment account");
stream.format(rowFormat, "getpaymentaccts", "", "Get user payment accounts");
stream.format(rowFormat, "lockwallet", "", "Remove wallet password from memory, locking the wallet");
stream.format(rowFormat, "unlockwallet", "password timeout",
stream.format(rowFormat, "", "[--memo=<\"memo\">]", "");
stream.println();
stream.format(rowFormat, getpaymentmethods.name(), "", "Get list of supported payment account method ids");
stream.println();
stream.format(rowFormat, getpaymentacctform.name(), "--payment-method-id=<payment-method-id>", "Get a new payment account form");
stream.println();
stream.format(rowFormat, createpaymentacct.name(), "--payment-account-form=<path>", "Create a new payment account");
stream.println();
stream.format(rowFormat, getpaymentaccts.name(), "", "Get user payment accounts");
stream.println();
stream.format(rowFormat, lockwallet.name(), "", "Remove wallet password from memory, locking the wallet");
stream.println();
stream.format(rowFormat, unlockwallet.name(), "--wallet-password=<password> --timeout=<seconds>",
"Store wallet password in memory for timeout seconds");
stream.format(rowFormat, "setwalletpassword", "password [newpassword]",
stream.println();
stream.format(rowFormat, setwalletpassword.name(), "--wallet-password=<password> \\",
"Encrypt wallet with password, or set new password on encrypted wallet");
stream.format(rowFormat, "", "[--new-wallet-password=<new-password>]", "");
stream.println();
stream.println("Method Help Usage: bisq-cli [options] <method> --help");
stream.println();
} catch (IOException ex) {
ex.printStackTrace(stream);
}
}
private static String getMethodHelp(HelpBlockingStub helpService, Method method) {
var request = GetMethodHelpRequest.newBuilder().setMethodName(method.name()).build();
var reply = helpService.getMethodHelp(request);
return reply.getMethodHelp();
}
}

View file

@ -19,6 +19,7 @@ package bisq.cli;
import bisq.proto.grpc.DisputeAgentsGrpc;
import bisq.proto.grpc.GetVersionGrpc;
import bisq.proto.grpc.HelpGrpc;
import bisq.proto.grpc.OffersGrpc;
import bisq.proto.grpc.PaymentAccountsGrpc;
import bisq.proto.grpc.PriceGrpc;
@ -33,6 +34,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
public 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;
@ -53,6 +55,7 @@ public class GrpcStubs {
}));
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);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
package bisq.cli;
/**
* Currently supported api methods.
*/
public enum Method {
createoffer,
canceloffer,
getoffer,
getmyoffer,
getoffers,
getmyoffers,
takeoffer,
gettrade,
confirmpaymentstarted,
confirmpaymentreceived,
keepfunds,
withdrawfunds,
getpaymentmethods,
getpaymentacctform,
createpaymentacct,
getpaymentaccts,
getversion,
getbalance,
getaddressbalance,
getfundingaddresses,
getunusedbsqaddress,
sendbsq,
sendbtc,
gettxfeerate,
settxfeerate,
unsettxfeerate,
gettransaction,
lockwallet,
unlockwallet,
removewalletpassword,
setwalletpassword,
registerdisputeagent
}

View file

@ -1,97 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.cli;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import static java.util.Arrays.stream;
import static java.util.stream.IntStream.range;
class NegativeNumberOptions {
private final Map<Integer, String> negativeNumberParams = new HashMap<>();
String[] removeNegativeNumberOptions(String[] args) {
// Cache any negative number params that will be rejected by the parser.
// This should be called before command line parsing.
int skipped = getIndexOfMethodInArgs(args);
for (int i = skipped; i < args.length; i++) {
if (isNegativeNumber.test(args[i])) {
String param = args[i];
negativeNumberParams.put(i - skipped, new BigDecimal(param).toString());
// Substitute a zero placeholder at the index containing the
// negative number positional option value.
args[i] = "0";
}
}
return args;
}
List<String> restoreNegativeNumberOptions(List<String> nonOptionArgs) {
// Put cached negative number params into a clone of the nonOptionArgs list.
// This should be called after command line parsing.
if (!negativeNumberParams.isEmpty()) {
List<String> nonOptionArgsClone = new ArrayList<>(nonOptionArgs);
negativeNumberParams.forEach((k, v) -> {
int idx = k;
nonOptionArgsClone.set(idx, v);
});
return Collections.unmodifiableList(nonOptionArgsClone);
} else {
// This should never happen. Instances of this class should not be created
// if there are no negative number options.
return nonOptionArgs;
}
}
static boolean hasNegativeNumberOptions(String[] args) {
return stream(args).anyMatch(isNegativeNumber);
}
private static final Predicate<String> isNegativeNumber = (param) -> {
if (param.length() > 1 && param.startsWith("-")) {
try {
new BigDecimal(param);
return true;
} catch (NumberFormatException ignored) {
// empty
}
}
return false;
};
private int getIndexOfMethodInArgs(String[] args) {
// The first argument that does not start with '-' or '--' is the method name.
// Skip over the --password=xyz [--host=s --port=n] options.
int skipped = range(0, args.length)
.filter(i -> !args[i].startsWith("-"))
.findFirst()
.orElse(-1);
if (skipped >= 0)
return skipped;
else
throw new IllegalArgumentException("required --password option not found");
}
}

View file

@ -0,0 +1,59 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import java.util.List;
import lombok.Getter;
import static bisq.cli.opts.OptLabel.OPT_HELP;
abstract class AbstractMethodOptionParser implements MethodOpts {
// The full command line args passed to CliMain.main(String[] args).
// CLI and Method level arguments are derived from args by an ArgumentList(args).
protected final String[] args;
protected final OptionParser parser = new OptionParser();
// The help option for a specific api method, e.g., takeoffer -help.
protected final OptionSpec<Void> helpOpt = parser.accepts(OPT_HELP, "Print method help").forHelp();
@Getter
protected OptionSet options;
@Getter
protected List<String> nonOptionArguments;
protected AbstractMethodOptionParser(String[] args) {
this.args = args;
}
public AbstractMethodOptionParser parse() {
options = parser.parse(new ArgumentList(args).getMethodArguments());
nonOptionArguments = (List<String>) options.nonOptionArguments();
return this;
}
public boolean isForHelp() {
return options.has(helpOpt);
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
/**
* Wrapper for an array of command line arguments.
*
* Used to extract CLI connection and authentication arguments, or method arguments
* before parsing CLI or method opts
*
*/
public class ArgumentList {
private final Predicate<String> isCliOpt = (o) ->
o.startsWith("--password") || o.startsWith("-password")
|| o.startsWith("--port") || o.startsWith("-port")
|| o.startsWith("--host") || o.startsWith("-host");
// The method name is the only positional opt in a command (easy to identify).
// If the positional argument does not match a Method, or there are more than one
// positional arguments, the joptsimple parser or CLI will fail as expected.
private final Predicate<String> isMethodNameOpt = (o) -> !o.startsWith("-");
private final Predicate<String> isHelpOpt = (o) -> o.startsWith("--help") || o.startsWith("-help");
private final String[] arguments;
private int currentIndex;
public ArgumentList(String... arguments) {
this.arguments = arguments.clone();
}
/**
* Returns only the CLI connection & authentication, and method name args
* (--password, --host, --port, --help, method name) contained in the original
* String[] args; excludes the method specific arguments.
*
* If String[] args contains both a method name (the only positional opt) and a help
* argument (--help, -help), it is assumed the user wants method help, not CLI help,
* and the help argument is not included in the returned String[].
*/
public String[] getCLIArguments() {
currentIndex = 0;
Optional<String> methodNameArgument = Optional.empty();
Optional<String> helpArgument = Optional.empty();
List<String> prunedArguments = new ArrayList<>();
while (hasMore()) {
String arg = peek();
if (isMethodNameOpt.test(arg)) {
methodNameArgument = Optional.of(arg);
prunedArguments.add(arg);
}
if (isCliOpt.test(arg))
prunedArguments.add(arg);
if (isHelpOpt.test(arg))
helpArgument = Optional.of(arg);
next();
}
// Include the saved CLI help argument if the positional method name argument
// was not found.
if (!methodNameArgument.isPresent() && helpArgument.isPresent())
prunedArguments.add(helpArgument.get());
return prunedArguments.toArray(new String[0]);
}
/**
* Returns only the method args contained in the original String[] args; excludes the
* CLI connection & authentication opts (--password, --host, --port), plus the
* positional method name arg.
*/
public String[] getMethodArguments() {
List<String> prunedArguments = new ArrayList<>();
currentIndex = 0;
while (hasMore()) {
String arg = peek();
if (!isCliOpt.test(arg) && !isMethodNameOpt.test(arg)) {
prunedArguments.add(arg);
}
next();
}
return prunedArguments.toArray(new String[0]);
}
boolean hasMore() {
return currentIndex < arguments.length;
}
String next() {
return arguments[currentIndex++];
}
String peek() {
return arguments[currentIndex];
}
}

View file

@ -0,0 +1,52 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_OFFER_ID;
import static joptsimple.internal.Strings.EMPTY;
public class CancelOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to cancel")
.withRequiredArg()
.defaultsTo(EMPTY);
public CancelOfferOptionParser(String[] args) {
super(args);
}
public CancelOfferOptionParser parse() {
super.parse();
// Short circuit opt validation if user just wants help.
if (options.has(helpOpt))
return this;
if (!options.has(offerIdOpt))
throw new IllegalArgumentException("no offer id specified");
return this;
}
public String getOfferId() {
return options.valueOf(offerIdOpt);
}
}

View file

@ -0,0 +1,140 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import java.math.BigDecimal;
import static bisq.cli.opts.OptLabel.*;
import static joptsimple.internal.Strings.EMPTY;
public class CreateOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT,
"id of payment account used for offer")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> directionOpt = parser.accepts(OPT_DIRECTION, "offer direction (buy|sell)")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency code (eur|usd|...)")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> amountOpt = parser.accepts(OPT_AMOUNT, "amount of btc to buy or sell")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> minAmountOpt = parser.accepts(OPT_MIN_AMOUNT, "minimum amount of btc to buy or sell")
.withOptionalArg()
.defaultsTo(EMPTY);
final OptionSpec<String> mktPriceMarginOpt = parser.accepts(OPT_MKT_PRICE_MARGIN, "market btc price margin (%)")
.withOptionalArg()
.defaultsTo("0.00");
final OptionSpec<String> fixedPriceOpt = parser.accepts(OPT_FIXED_PRICE, "fixed btc price")
.withOptionalArg()
.defaultsTo(EMPTY);
final OptionSpec<String> securityDepositOpt = parser.accepts(OPT_SECURITY_DEPOSIT, "maker security deposit (%)")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> makerFeeCurrencyCodeOpt = parser.accepts(OPT_FEE_CURRENCY, "maker fee currency code (bsq|btc)")
.withOptionalArg()
.defaultsTo("btc");
public CreateOfferOptionParser(String[] args) {
super(args);
}
public CreateOfferOptionParser parse() {
super.parse();
// Short circuit opt validation if user just wants help.
if (options.has(helpOpt))
return this;
if (!options.has(paymentAccountIdOpt))
throw new IllegalArgumentException("no payment account id specified");
if (!options.has(directionOpt))
throw new IllegalArgumentException("no direction (buy|sell) specified");
if (!options.has(amountOpt))
throw new IllegalArgumentException("no btc amount specified");
if (!options.has(mktPriceMarginOpt) && !options.has(fixedPriceOpt))
throw new IllegalArgumentException("no market price margin or fixed price specified");
if (!options.has(securityDepositOpt))
throw new IllegalArgumentException("no security deposit specified");
return this;
}
public String getPaymentAccountId() {
return options.valueOf(paymentAccountIdOpt);
}
public String getDirection() {
return options.valueOf(directionOpt);
}
public String getCurrencyCode() {
return options.valueOf(currencyCodeOpt);
}
public String getAmount() {
return options.valueOf(amountOpt);
}
public String getMinAmount() {
return options.has(minAmountOpt) ? options.valueOf(minAmountOpt) : getAmount();
}
public boolean isUsingMktPriceMargin() {
return options.has(mktPriceMarginOpt);
}
@SuppressWarnings("unused")
public String getMktPriceMargin() {
return isUsingMktPriceMargin() ? options.valueOf(mktPriceMarginOpt) : "0.00";
}
public BigDecimal getMktPriceMarginAsBigDecimal() {
return isUsingMktPriceMargin() ? new BigDecimal(options.valueOf(mktPriceMarginOpt)) : BigDecimal.ZERO;
}
public String getFixedPrice() {
return options.has(fixedPriceOpt) ? options.valueOf(fixedPriceOpt) : "0.00";
}
public String getSecurityDeposit() {
return options.valueOf(securityDepositOpt);
}
public String getMakerFeeCurrencyCode() {
return options.has(makerFeeCurrencyCodeOpt) ? options.valueOf(makerFeeCurrencyCodeOpt) : "btc";
}
}

View file

@ -0,0 +1,63 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import java.nio.file.Path;
import java.nio.file.Paths;
import static bisq.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_FORM;
import static java.lang.String.format;
import static joptsimple.internal.Strings.EMPTY;
public class CreatePaymentAcctOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> paymentAcctFormPathOpt = parser.accepts(OPT_PAYMENT_ACCOUNT_FORM,
"path to json payment account form")
.withRequiredArg()
.defaultsTo(EMPTY);
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))
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.toString()));
return this;
}
public Path getPaymentAcctForm() {
return Paths.get(options.valueOf(paymentAcctFormPathOpt));
}
}

View file

@ -0,0 +1,52 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
import static joptsimple.internal.Strings.EMPTY;
public class GetAddressBalanceOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> addressOpt = parser.accepts(OPT_ADDRESS, "wallet btc address")
.withRequiredArg()
.defaultsTo(EMPTY);
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))
throw new IllegalArgumentException("no address specified");
return this;
}
public String getAddress() {
return options.valueOf(addressOpt);
}
}

View file

@ -0,0 +1,43 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_CURRENCY_CODE;
import static joptsimple.internal.Strings.EMPTY;
public class GetBalanceOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "wallet currency code (bsq|btc)")
.withOptionalArg()
.defaultsTo(EMPTY);
public GetBalanceOptionParser(String[] args) {
super(args);
}
public GetBalanceOptionParser parse() {
return (GetBalanceOptionParser) super.parse();
}
public String getCurrencyCode() {
return options.has(currencyCodeOpt) ? options.valueOf(currencyCodeOpt) : "";
}
}

View file

@ -0,0 +1,52 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_OFFER_ID;
import static joptsimple.internal.Strings.EMPTY;
public class GetOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to get")
.withRequiredArg()
.defaultsTo(EMPTY);
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))
throw new IllegalArgumentException("no offer id specified");
return this;
}
public String getOfferId() {
return options.valueOf(offerIdOpt);
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_CURRENCY_CODE;
import static bisq.cli.opts.OptLabel.OPT_DIRECTION;
import static joptsimple.internal.Strings.EMPTY;
public class GetOffersOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> directionOpt = parser.accepts(OPT_DIRECTION, "offer direction (buy|sell)")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency code (eur|usd|...)")
.withRequiredArg()
.defaultsTo(EMPTY);
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))
throw new IllegalArgumentException("no direction (buy|sell) specified");
if (!options.has(currencyCodeOpt))
throw new IllegalArgumentException("no currency code specified");
return this;
}
public String getDirection() {
return options.valueOf(directionOpt);
}
public String getCurrencyCode() {
return options.valueOf(currencyCodeOpt);
}
}

View file

@ -0,0 +1,53 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_PAYMENT_METHOD_ID;
import static joptsimple.internal.Strings.EMPTY;
public class GetPaymentAcctFormOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> paymentMethodIdOpt = parser.accepts(OPT_PAYMENT_METHOD_ID,
"id of payment method type used by a payment account")
.withRequiredArg()
.defaultsTo(EMPTY);
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))
throw new IllegalArgumentException("no payment method id specified");
return this;
}
public String getPaymentMethodId() {
return options.valueOf(paymentMethodIdOpt);
}
}

View file

@ -0,0 +1,62 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_SHOW_CONTRACT;
import static bisq.cli.opts.OptLabel.OPT_TRADE_ID;
import static joptsimple.internal.Strings.EMPTY;
public class GetTradeOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> tradeIdOpt = parser.accepts(OPT_TRADE_ID, "id of trade")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<Boolean> showContractOpt = parser.accepts(OPT_SHOW_CONTRACT, "show trade's json contract")
.withOptionalArg()
.ofType(boolean.class)
.defaultsTo(Boolean.FALSE);
public GetTradeOptionParser(String[] args) {
super(args);
}
public GetTradeOptionParser parse() {
super.parse();
// Short circuit opt validation if user just wants help.
if (options.has(helpOpt))
return this;
if (!options.has(tradeIdOpt))
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;
}
}

View file

@ -0,0 +1,52 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_TRANSACTION_ID;
import static joptsimple.internal.Strings.EMPTY;
public class GetTransactionOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> txIdOpt = parser.accepts(OPT_TRANSACTION_ID, "id of transaction")
.withRequiredArg()
.defaultsTo(EMPTY);
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))
throw new IllegalArgumentException("no tx id specified");
return this;
}
public String getTxId() {
return options.valueOf(txIdOpt);
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
public interface MethodOpts {
MethodOpts parse();
boolean isForHelp();
}

View file

@ -0,0 +1,51 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
/**
* CLI opt label definitions.
*/
public class OptLabel {
public final static String OPT_ADDRESS = "address";
public final static String OPT_AMOUNT = "amount";
public final static String OPT_CURRENCY_CODE = "currency-code";
public final static String OPT_DIRECTION = "direction";
public final static String OPT_DISPUTE_AGENT_TYPE = "dispute-agent-type";
public final static String OPT_FEE_CURRENCY = "fee-currency";
public final static String OPT_FIXED_PRICE = "fixed-price";
public final static String OPT_HELP = "help";
public final static String OPT_HOST = "host";
public final static String OPT_MEMO = "memo";
public final static String OPT_MKT_PRICE_MARGIN = "market-price-margin";
public final static String OPT_MIN_AMOUNT = "min-amount";
public final static String OPT_OFFER_ID = "offer-id";
public final static String OPT_PASSWORD = "password";
public final static String OPT_PAYMENT_ACCOUNT = "payment-account";
public final static String OPT_PAYMENT_ACCOUNT_FORM = "payment-account-form";
public final static String OPT_PAYMENT_METHOD_ID = "payment-method-id";
public final static String OPT_PORT = "port";
public final static String OPT_REGISTRATION_KEY = "registration-key";
public final static String OPT_SECURITY_DEPOSIT = "security-deposit";
public final static String OPT_SHOW_CONTRACT = "show-contract";
public final static String OPT_TRADE_ID = "trade-id";
public final static String OPT_TIMEOUT = "timeout";
public final static String OPT_TRANSACTION_ID = "transaction-id";
public final static String OPT_TX_FEE_RATE = "tx-fee-rate";
public final static String OPT_WALLET_PASSWORD = "wallet-password";
public final static String OPT_NEW_WALLET_PASSWORD = "new-wallet-password";
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_DISPUTE_AGENT_TYPE;
import static bisq.cli.opts.OptLabel.OPT_REGISTRATION_KEY;
import static joptsimple.internal.Strings.EMPTY;
public class RegisterDisputeAgentOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> disputeAgentTypeOpt = parser.accepts(OPT_DISPUTE_AGENT_TYPE, "dispute agent type")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> registrationKeyOpt = parser.accepts(OPT_REGISTRATION_KEY, "registration key")
.withRequiredArg()
.defaultsTo(EMPTY);
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))
throw new IllegalArgumentException("no dispute agent type specified");
if (!options.has(registrationKeyOpt))
throw new IllegalArgumentException("no registration key specified");
return this;
}
public String getDisputeAgentType() {
return options.valueOf(disputeAgentTypeOpt);
}
public String getRegistrationKey() {
return options.valueOf(registrationKeyOpt);
}
}

View file

@ -0,0 +1,52 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_WALLET_PASSWORD;
import static joptsimple.internal.Strings.EMPTY;
public class RemoveWalletPasswordOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "bisq wallet password")
.withRequiredArg()
.defaultsTo(EMPTY);
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))
throw new IllegalArgumentException("no password specified");
return this;
}
public String getPassword() {
return options.valueOf(passwordOpt);
}
}

View file

@ -0,0 +1,73 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
import static bisq.cli.opts.OptLabel.OPT_AMOUNT;
import static bisq.cli.opts.OptLabel.OPT_TX_FEE_RATE;
import static joptsimple.internal.Strings.EMPTY;
public class SendBsqOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> addressOpt = parser.accepts(OPT_ADDRESS, "destination bsq address")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> amountOpt = parser.accepts(OPT_AMOUNT, "amount of bsq to send")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> feeRateOpt = parser.accepts(OPT_TX_FEE_RATE, "optional tx fee rate (sats/byte)")
.withOptionalArg()
.defaultsTo(EMPTY);
public SendBsqOptionParser(String[] args) {
super(args);
}
public SendBsqOptionParser parse() {
super.parse();
// Short circuit opt validation if user just wants help.
if (options.has(helpOpt))
return this;
if (!options.has(addressOpt))
throw new IllegalArgumentException("no bsq address specified");
if (!options.has(amountOpt))
throw new IllegalArgumentException("no bsq amount specified");
return this;
}
public String getAddress() {
return options.valueOf(addressOpt);
}
public String getAmount() {
return options.valueOf(amountOpt);
}
public String getFeeRate() {
return options.has(feeRateOpt) ? options.valueOf(feeRateOpt) : "";
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
import static bisq.cli.opts.OptLabel.OPT_AMOUNT;
import static bisq.cli.opts.OptLabel.OPT_MEMO;
import static bisq.cli.opts.OptLabel.OPT_TX_FEE_RATE;
import static joptsimple.internal.Strings.EMPTY;
public class SendBtcOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> addressOpt = parser.accepts(OPT_ADDRESS, "destination btc address")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> amountOpt = parser.accepts(OPT_AMOUNT, "amount of btc to send")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> feeRateOpt = parser.accepts(OPT_TX_FEE_RATE, "optional tx fee rate (sats/byte)")
.withOptionalArg()
.defaultsTo(EMPTY);
final OptionSpec<String> memoOpt = parser.accepts(OPT_MEMO, "optional tx memo")
.withOptionalArg()
.defaultsTo(EMPTY);
public SendBtcOptionParser(String[] args) {
super(args);
}
public SendBtcOptionParser parse() {
super.parse();
// Short circuit opt validation if user just wants help.
if (options.has(helpOpt))
return this;
if (!options.has(addressOpt))
throw new IllegalArgumentException("no btc address specified");
if (!options.has(amountOpt))
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) : "";
}
}

View file

@ -0,0 +1,53 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_TX_FEE_RATE;
import static joptsimple.internal.Strings.EMPTY;
public class SetTxFeeRateOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> feeRateOpt = parser.accepts(OPT_TX_FEE_RATE,
"tx fee rate preference (sats/byte)")
.withRequiredArg()
.defaultsTo(EMPTY);
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))
throw new IllegalArgumentException("no tx fee rate specified");
return this;
}
public String getFeeRate() {
return options.valueOf(feeRateOpt);
}
}

View file

@ -0,0 +1,61 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_NEW_WALLET_PASSWORD;
import static bisq.cli.opts.OptLabel.OPT_WALLET_PASSWORD;
import static joptsimple.internal.Strings.EMPTY;
public class SetWalletPasswordOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "bisq wallet password")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> newPasswordOpt = parser.accepts(OPT_NEW_WALLET_PASSWORD, "new bisq wallet password")
.withOptionalArg()
.defaultsTo(EMPTY);
public SetWalletPasswordOptionParser(String[] args) {
super(args);
}
public SetWalletPasswordOptionParser parse() {
super.parse();
// Short circuit opt validation if user just wants help.
if (options.has(helpOpt))
return this;
if (!options.has(passwordOpt))
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) : "";
}
}

View file

@ -0,0 +1,30 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
public class SimpleMethodOptionParser extends AbstractMethodOptionParser implements MethodOpts {
public SimpleMethodOptionParser(String[] args) {
super(args);
}
public SimpleMethodOptionParser parse() {
return (SimpleMethodOptionParser) super.parse();
}
}

View file

@ -0,0 +1,73 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_FEE_CURRENCY;
import static bisq.cli.opts.OptLabel.OPT_OFFER_ID;
import static bisq.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT;
import static joptsimple.internal.Strings.EMPTY;
public class TakeOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to take")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT, "id of payment account used for trade")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> takerFeeCurrencyCodeOpt = parser.accepts(OPT_FEE_CURRENCY, "taker fee currency code (bsq|btc)")
.withOptionalArg()
.defaultsTo("btc");
public TakeOfferOptionParser(String[] args) {
super(args);
}
public TakeOfferOptionParser parse() {
super.parse();
// Short circuit opt validation if user just wants help.
if (options.has(helpOpt))
return this;
if (!options.has(offerIdOpt))
throw new IllegalArgumentException("no offer id specified");
if (!options.has(paymentAccountIdOpt))
throw new IllegalArgumentException("no payment account id specified");
return this;
}
public String getOfferId() {
return options.valueOf(offerIdOpt);
}
public String getPaymentAccountId() {
return options.valueOf(paymentAccountIdOpt);
}
public String getTakerFeeCurrencyCode() {
return options.has(takerFeeCurrencyCodeOpt) ? options.valueOf(takerFeeCurrencyCodeOpt) : "btc";
}
}

View file

@ -0,0 +1,65 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_TIMEOUT;
import static bisq.cli.opts.OptLabel.OPT_WALLET_PASSWORD;
import static joptsimple.internal.Strings.EMPTY;
public class UnlockWalletOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "bisq wallet password")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<Long> unlockTimeoutOpt = parser.accepts(OPT_TIMEOUT, "wallet unlock timeout (s)")
.withRequiredArg()
.ofType(long.class)
.defaultsTo(0L);
public UnlockWalletOptionParser(String[] args) {
super(args);
}
public UnlockWalletOptionParser parse() {
super.parse();
// Short circuit opt validation if user just wants help.
if (options.has(helpOpt))
return this;
if (!options.has(passwordOpt))
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);
}
}

View file

@ -0,0 +1,70 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.cli.opts;
import joptsimple.OptionSpec;
import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
import static bisq.cli.opts.OptLabel.OPT_MEMO;
import static bisq.cli.opts.OptLabel.OPT_TRADE_ID;
import static joptsimple.internal.Strings.EMPTY;
public class WithdrawFundsOptionParser extends AbstractMethodOptionParser implements MethodOpts {
final OptionSpec<String> tradeIdOpt = parser.accepts(OPT_TRADE_ID, "id of trade to get")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> addressOpt = parser.accepts(OPT_ADDRESS, "destination btc address")
.withRequiredArg()
.defaultsTo(EMPTY);
final OptionSpec<String> memoOpt = parser.accepts(OPT_MEMO, "optional tx memo")
.withOptionalArg()
.defaultsTo(EMPTY);
public WithdrawFundsOptionParser(String[] args) {
super(args);
}
public WithdrawFundsOptionParser parse() {
super.parse();
// Short circuit opt validation if user just wants help.
if (options.has(helpOpt))
return this;
if (!options.has(tradeIdOpt))
throw new IllegalArgumentException("no trade id 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) : "";
}
}

View file

@ -62,6 +62,7 @@ public class CoreApi {
@Getter
private final Config config;
private final CoreDisputeAgentsService coreDisputeAgentsService;
private final CoreHelpService coreHelpService;
private final CoreOffersService coreOffersService;
private final CorePaymentAccountsService paymentAccountsService;
private final CorePriceService corePriceService;
@ -72,7 +73,7 @@ public class CoreApi {
@Inject
public CoreApi(Config config,
CoreDisputeAgentsService coreDisputeAgentsService,
CoreOffersService coreOffersService,
CoreHelpService coreHelpService, CoreOffersService coreOffersService,
CorePaymentAccountsService paymentAccountsService,
CorePriceService corePriceService,
CoreTradesService coreTradesService,
@ -80,6 +81,7 @@ public class CoreApi {
TradeStatisticsManager tradeStatisticsManager) {
this.config = config;
this.coreDisputeAgentsService = coreDisputeAgentsService;
this.coreHelpService = coreHelpService;
this.coreOffersService = coreOffersService;
this.paymentAccountsService = paymentAccountsService;
this.coreTradesService = coreTradesService;
@ -101,6 +103,15 @@ public class CoreApi {
coreDisputeAgentsService.registerDisputeAgent(disputeAgentType, registrationKey);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Help
///////////////////////////////////////////////////////////////////////////////////////////
public String getMethodHelp(String methodName) {
return coreHelpService.getMethodHelp(methodName);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Offers
///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,87 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.api;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import lombok.extern.slf4j.Slf4j;
import static java.io.File.separator;
import static java.lang.String.format;
import static java.lang.System.out;
@Singleton
@Slf4j
class CoreHelpService {
@Inject
public CoreHelpService() {
}
public String getMethodHelp(String methodName) {
String resourceFile = "/help" + separator + methodName + "-" + "help.txt";
try {
return readHelpFile(resourceFile);
} catch (NullPointerException ex) {
log.error("", ex);
throw new IllegalStateException(format("no help found for api method %s", methodName));
} catch (IOException ex) {
log.error("", ex);
throw new IllegalStateException(format("could not read %s help doc", methodName));
}
}
private String readHelpFile(String resourceFile) throws NullPointerException, IOException {
// The deployed text file is in the core.jar file, so use
// Class.getResourceAsStream to read it.
InputStream is = getClass().getResourceAsStream(resourceFile);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
StringBuilder builder = new StringBuilder();
while ((line = br.readLine()) != null)
builder.append(line).append("\n");
return builder.toString();
}
// Main method for devs to view help text without running the server.
@SuppressWarnings("CommentedOutCode")
public static void main(String[] args) {
CoreHelpService coreHelpService = new CoreHelpService();
out.println(coreHelpService.getMethodHelp("getversion"));
// out.println(coreHelpService.getMethodHelp("getfundingaddresses"));
// out.println(coreHelpService.getMethodHelp("getfundingaddresses"));
// out.println(coreHelpService.getMethodHelp("getunusedbsqaddress"));
// out.println(coreHelpService.getMethodHelp("unsettxfeerate"));
// out.println(coreHelpService.getMethodHelp("getpaymentmethods"));
// out.println(coreHelpService.getMethodHelp("getpaymentaccts"));
// out.println(coreHelpService.getMethodHelp("lockwallet"));
// out.println(coreHelpService.getMethodHelp("gettxfeerate"));
// out.println(coreHelpService.getMethodHelp("createoffer"));
// out.println(coreHelpService.getMethodHelp("takeoffer"));
// out.println(coreHelpService.getMethodHelp("garbage"));
// out.println(coreHelpService.getMethodHelp(""));
// out.println(coreHelpService.getMethodHelp(null));
}
}

View file

@ -0,0 +1,64 @@
createoffer
NAME
----
createoffer - create offer to buy or sell BTC
SYNOPSIS
--------
createoffer
--payment-account=<payment-acct-id>
--direction=<buy|sell>
--currency-code=<eur|usd>
--market-price-margin=<percent> | --fixed-price=<btc-price>
--amount=<btc-amount>
--min-amount=<btc-amount>
--security-deposit=<percent>
[--fee-currency=<bsq|btc>]
DESCRIPTION
-----------
Create and place an offer to buy or sell BTC using a fiat account.
OPTIONS
-------
--payment-account
The ID of the fiat payment account used to send or receive funds during the trade.
--direction
The direction of the trade (BUY or SELL).
--currency-code
The three letter code for the fiat used to buy or sell BTC, e.g., EUR, USD, BRL, ...
--market-price-margin
The % above or below market BTC price, e.g., 1.00 (1%).
If --market-price-margin is not present, --fixed-price must be.
--fixed-price
The fixed BTC price in fiat used to buy or sell BTC, e.g., 34000 (USD).
If --fixed-price is not present, --market-price-margin must be.
--amount
The amount of BTC to buy or sell, e.g., 0.125.
--min-amount
The minimum amount of BTC to buy or sell, e.g., 0.006.
If --min-amount is not present, it defaults to the --amount value.
--security-deposit
The percentage of the BTC amount being traded for the security deposit, e.g., 60.0 (60%).
--fee-currency
The wallet currency used to pay the Bisq trade maker fee (BSQ|BTC). Default is BTC
EXAMPLES
--------
To create a BUY 0.125 BTC with EUR offer
at the current market price,
using a payment account with ID 7413d263-225a-4f1b-837a-1e3094dc0d77,
putting up a 30 percent security deposit,
and paying the Bisq maker trading fee in BSQ:
$ ./bisq-cli --password=xyz --port=9998 createoffer --payment-account=7413d263-225a-4f1b-837a-1e3094dc0d77 --direction=buy --currency-code=eur --amount=0.125 --market-price-margin=0.00 --security-deposit=30.0 --fee-currency=bsq
(TODO another 3 examples: selling @ mkt price, buying a fixed price, selling at fixed price...)

View file

@ -0,0 +1,17 @@
getfundingaddresses
NAME
----
getfundingaddresses - list BTC receiving address
SYNOPSIS
--------
getfundingaddresses
DESCRIPTION
-----------
Returns a list of receiving BTC addresses.
EXAMPLES
--------
$ ./bisq-cli --password=xyz --port=9998 getfundingaddresses

View file

@ -0,0 +1,17 @@
getpaymentaccts
NAME
----
getpaymentaccts - list user payment accounts
SYNOPSIS
--------
getpaymentaccts
DESCRIPTION
-----------
Returns the list of user payment accounts.
EXAMPLES
--------
$ ./bisq-cli --password=xyz --port=9998 getpaymentaccts

View file

@ -0,0 +1,17 @@
getpaymentmethods
NAME
----
getpaymentmethods - list fiat payment methods
SYNOPSIS
--------
getpaymentmethods
DESCRIPTION
-----------
Returns a list of currently supported fiat payment method IDs.
EXAMPLES
--------
$ ./bisq-cli --password=xyz --port=9998 getpaymentmethods

View file

@ -0,0 +1,17 @@
gettxfeerate
NAME
----
gettxfeerate - get transaction fee rate
SYNOPSIS
--------
gettxfeerate
DESCRIPTION
-----------
Returns the most recent bitcoin network transaction fee the Bisq server could find.
EXAMPLES
--------
$ ./bisq-cli --password=xyz --port=9998 gettxfeerate

View file

@ -0,0 +1,17 @@
getunusedbsqaddress
NAME
----
getunusedbsqaddress - get BSQ receiving address
SYNOPSIS
--------
getunusedbsqaddress
DESCRIPTION
-----------
Returns an unused BSQ receiving address.
EXAMPLES
--------
$ ./bisq-cli --password=xyz --port=9998 getunusedbsqaddress

View file

@ -0,0 +1,17 @@
getversion
NAME
----
getversion - get server version
SYNOPSIS
--------
getversion
DESCRIPTION
-----------
Returns the Bisq server version.
EXAMPLES
--------
$ ./bisq-cli --password=xyz --port=9998 getversion

View file

@ -0,0 +1,17 @@
lockwallet
NAME
----
lockwallet - lock Bisq wallet
SYNOPSIS
--------
lockwallet
DESCRIPTION
-----------
Locks an unlocked wallet before an unlockwallet timeout expires.
EXAMPLES
--------
$ ./bisq-cli --password=xyz --port=9998 lockwallet

View file

@ -0,0 +1,35 @@
takeoffer
NAME
----
takeoffer - take an offer to buy or sell BTC
SYNOPSIS
--------
takeoffer
--offer-id=<offer-id>
--payment-account=<payment-acct-id>
--fee-currency=<eur|usd>
DESCRIPTION
-----------
Take an existing offer using a matching payment method. The Bisq trade fee can be paid in BSQ or BTC.
OPTIONS
-------
--offer-id
The ID of the buy or sell offer to take.
--payment-account
The ID of the fiat payment account used to send or receive funds during the trade.
The payment account's payment method must match that of the offer.
--fee-currency
The wallet currency used to pay the Bisq trade taker fee (BSQ|BTC). Default is BTC
EXAMPLES
--------
To take an offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea
using a payment account with ID fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e,
and paying the Bisq trading fee in BSQ:
$ ./bisq-cli --password=xyz --port=9998 takeoffer -offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea -payment-account=fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e -fee-currency=bsq

View file

@ -0,0 +1,17 @@
unsettxfeerate
NAME
----
unsettxfeerate - unset transaction fee rate preference
SYNOPSIS
--------
unsettxfeerate
DESCRIPTION
-----------
Unsets (removes) the transaction fee rate user preference.
EXAMPLES
--------
$ ./bisq-cli --password=xyz --port=9998 unsettxfeerate

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
package bisq.daemon.grpc;
import bisq.core.api.CoreApi;
import bisq.proto.grpc.GetMethodHelpReply;
import bisq.proto.grpc.GetMethodHelpRequest;
import bisq.proto.grpc.HelpGrpc;
import io.grpc.stub.StreamObserver;
import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
@Slf4j
class GrpcHelpService extends HelpGrpc.HelpImplBase {
private final CoreApi coreApi;
private final GrpcExceptionHandler exceptionHandler;
@Inject
public GrpcHelpService(CoreApi coreApi, GrpcExceptionHandler exceptionHandler) {
this.coreApi = coreApi;
this.exceptionHandler = exceptionHandler;
}
@Override
public void getMethodHelp(GetMethodHelpRequest req,
StreamObserver<GetMethodHelpReply> responseObserver) {
try {
String helpText = coreApi.getMethodHelp(req.getMethodName());
var reply = GetMethodHelpReply.newBuilder().setMethodHelp(helpText).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
} catch (Throwable cause) {
exceptionHandler.handleException(cause, responseObserver);
}
}
}

View file

@ -50,6 +50,7 @@ public class GrpcServer {
Config config,
PasswordAuthInterceptor passwordAuthInterceptor,
GrpcDisputeAgentsService disputeAgentsService,
GrpcHelpService helpService,
GrpcOffersService offersService,
GrpcPaymentAccountsService paymentAccountsService,
GrpcPriceService priceService,
@ -60,6 +61,7 @@ public class GrpcServer {
this.server = ServerBuilder.forPort(config.apiPort)
.executor(UserThread.getExecutor())
.addService(disputeAgentsService)
.addService(helpService)
.addService(offersService)
.addService(paymentAccountsService)
.addService(priceService)

View file

@ -40,6 +40,23 @@ message RegisterDisputeAgentRequest {
message RegisterDisputeAgentReply {
}
///////////////////////////////////////////////////////////////////////////////////////////
// Help
///////////////////////////////////////////////////////////////////////////////////////////
service Help {
rpc GetMethodHelp (GetMethodHelpRequest) returns (GetMethodHelpReply) {
}
}
message GetMethodHelpRequest {
string methodName = 1;
}
message GetMethodHelpReply {
string methodHelp = 1;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Offers
///////////////////////////////////////////////////////////////////////////////////////////