mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Merge pull request #4329 from ghubstan/6-getoffers
Add rpc method 'getoffers'
This commit is contained in:
commit
a54eeeabcc
@ -17,15 +17,16 @@
|
||||
|
||||
package bisq.cli;
|
||||
|
||||
import bisq.proto.grpc.AddressBalanceInfo;
|
||||
import bisq.proto.grpc.CreatePaymentAccountRequest;
|
||||
import bisq.proto.grpc.GetAddressBalanceRequest;
|
||||
import bisq.proto.grpc.GetBalanceRequest;
|
||||
import bisq.proto.grpc.GetFundingAddressesRequest;
|
||||
import bisq.proto.grpc.GetOffersRequest;
|
||||
import bisq.proto.grpc.GetPaymentAccountsRequest;
|
||||
import bisq.proto.grpc.GetVersionGrpc;
|
||||
import bisq.proto.grpc.GetVersionRequest;
|
||||
import bisq.proto.grpc.LockWalletRequest;
|
||||
import bisq.proto.grpc.OffersGrpc;
|
||||
import bisq.proto.grpc.PaymentAccountsGrpc;
|
||||
import bisq.proto.grpc.RemoveWalletPasswordRequest;
|
||||
import bisq.proto.grpc.SetWalletPasswordRequest;
|
||||
@ -38,20 +39,18 @@ import io.grpc.StatusRuntimeException;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.formatSatoshis;
|
||||
import static bisq.cli.TableFormat.formatAddressBalanceTbl;
|
||||
import static bisq.cli.TableFormat.formatOfferTable;
|
||||
import static bisq.cli.TableFormat.formatPaymentAcctTbl;
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.err;
|
||||
import static java.lang.System.exit;
|
||||
@ -65,13 +64,8 @@ import static java.util.Collections.singletonList;
|
||||
@Slf4j
|
||||
public class CliMain {
|
||||
|
||||
private static final BigDecimal SATOSHI_DIVISOR = new BigDecimal(100000000);
|
||||
private static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.00000000");
|
||||
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
|
||||
private static final Function<Long, String> formatSatoshis = (sats) ->
|
||||
BTC_FORMAT.format(BigDecimal.valueOf(sats).divide(SATOSHI_DIVISOR));
|
||||
|
||||
private enum Method {
|
||||
getoffers,
|
||||
createpaymentacct,
|
||||
getpaymentaccts,
|
||||
getversion,
|
||||
@ -151,6 +145,7 @@ public class CliMain {
|
||||
}));
|
||||
|
||||
var versionService = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
var offersService = OffersGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
var paymentAccountsService = PaymentAccountsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
var walletsService = WalletsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
|
||||
@ -165,7 +160,7 @@ public class CliMain {
|
||||
case getbalance: {
|
||||
var request = GetBalanceRequest.newBuilder().build();
|
||||
var reply = walletsService.getBalance(request);
|
||||
var btcBalance = formatSatoshis.apply(reply.getBalance());
|
||||
var btcBalance = formatSatoshis(reply.getBalance());
|
||||
out.println(btcBalance);
|
||||
return;
|
||||
}
|
||||
@ -176,30 +171,38 @@ public class CliMain {
|
||||
var request = GetAddressBalanceRequest.newBuilder()
|
||||
.setAddress(nonOptionArgs.get(1)).build();
|
||||
var reply = walletsService.getAddressBalance(request);
|
||||
out.println(formatTable(singletonList(reply.getAddressBalanceInfo())));
|
||||
out.println(formatAddressBalanceTbl(singletonList(reply.getAddressBalanceInfo())));
|
||||
return;
|
||||
}
|
||||
case getfundingaddresses: {
|
||||
var request = GetFundingAddressesRequest.newBuilder().build();
|
||||
var reply = walletsService.getFundingAddresses(request);
|
||||
out.println(formatTable(reply.getAddressBalanceInfoList()));
|
||||
out.println(formatAddressBalanceTbl(reply.getAddressBalanceInfoList()));
|
||||
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 fiatCurrency = nonOptionArgs.get(2);
|
||||
|
||||
var request = GetOffersRequest.newBuilder()
|
||||
.setDirection(direction)
|
||||
.setFiatCurrencyCode(fiatCurrency)
|
||||
.build();
|
||||
var reply = offersService.getOffers(request);
|
||||
out.println(formatOfferTable(reply.getOffersList(), fiatCurrency));
|
||||
return;
|
||||
}
|
||||
case createpaymentacct: {
|
||||
if (nonOptionArgs.size() < 2)
|
||||
throw new IllegalArgumentException("no account name specified");
|
||||
if (nonOptionArgs.size() < 4)
|
||||
throw new IllegalArgumentException(
|
||||
"incorrect parameter count, expecting account name, account number, currency code");
|
||||
|
||||
var accountName = nonOptionArgs.get(1);
|
||||
|
||||
if (nonOptionArgs.size() < 3)
|
||||
throw new IllegalArgumentException("no account number specified");
|
||||
|
||||
var accountNumber = nonOptionArgs.get(2);
|
||||
|
||||
if (nonOptionArgs.size() < 4)
|
||||
throw new IllegalArgumentException("no fiat currency specified");
|
||||
|
||||
var fiatCurrencyCode = nonOptionArgs.get(3).toUpperCase();
|
||||
var fiatCurrencyCode = nonOptionArgs.get(3);
|
||||
|
||||
var request = CreatePaymentAccountRequest.newBuilder()
|
||||
.setAccountName(accountName)
|
||||
@ -212,15 +215,7 @@ public class CliMain {
|
||||
case getpaymentaccts: {
|
||||
var request = GetPaymentAccountsRequest.newBuilder().build();
|
||||
var reply = paymentAccountsService.getPaymentAccounts(request);
|
||||
var columnFormatSpec = "%-41s %-25s %-14s %s";
|
||||
out.println(format(columnFormatSpec, "ID", "Name", "Currency", "Payment Method"));
|
||||
out.println(reply.getPaymentAccountsList().stream()
|
||||
.map(a -> format(columnFormatSpec,
|
||||
a.getId(),
|
||||
a.getAccountName(),
|
||||
a.getSelectedTradeCurrency().getCode(),
|
||||
a.getPaymentMethod().getId()))
|
||||
.collect(Collectors.joining("\n")));
|
||||
out.println(formatPaymentAcctTbl(reply.getPaymentAccountsList()));
|
||||
return;
|
||||
}
|
||||
case lockwallet: {
|
||||
@ -295,6 +290,7 @@ public class CliMain {
|
||||
stream.format("%-22s%-50s%s%n", "getbalance", "", "Get server wallet balance");
|
||||
stream.format("%-22s%-50s%s%n", "getaddressbalance", "address", "Get server wallet address balance");
|
||||
stream.format("%-22s%-50s%s%n", "getfundingaddresses", "", "Get BTC funding addresses");
|
||||
stream.format("%-22s%-50s%s%n", "getoffers", "buy | sell, fiat currency code", "Get current offers");
|
||||
stream.format("%-22s%-50s%s%n", "createpaymentacct", "account name, account number, currency code", "Create PerfectMoney dummy account");
|
||||
stream.format("%-22s%-50s%s%n", "getpaymentaccts", "", "Get user payment accounts");
|
||||
stream.format("%-22s%-50s%s%n", "lockwallet", "", "Remove wallet password from memory, locking the wallet");
|
||||
@ -307,14 +303,4 @@ public class CliMain {
|
||||
ex.printStackTrace(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatTable(List<AddressBalanceInfo> addressBalanceInfo) {
|
||||
return format("%-35s %13s %s%n", "Address", "Balance", "Confirmations")
|
||||
+ addressBalanceInfo.stream()
|
||||
.map(info -> format("%-35s %13s %14d",
|
||||
info.getAddress(),
|
||||
formatSatoshis.apply(info.getBalance()),
|
||||
info.getNumConfirmations()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
}
|
||||
|
47
cli/src/main/java/bisq/cli/CurrencyFormat.java
Normal file
47
cli/src/main/java/bisq/cli/CurrencyFormat.java
Normal file
@ -0,0 +1,47 @@
|
||||
package bisq.cli;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
class CurrencyFormat {
|
||||
|
||||
private static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
|
||||
static final BigDecimal SATOSHI_DIVISOR = new BigDecimal(100000000);
|
||||
static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.00000000");
|
||||
|
||||
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
|
||||
static final String formatSatoshis(long sats) {
|
||||
return BTC_FORMAT.format(BigDecimal.valueOf(sats).divide(SATOSHI_DIVISOR));
|
||||
}
|
||||
|
||||
static String formatAmountRange(long minAmount, long amount) {
|
||||
return minAmount != amount
|
||||
? formatSatoshis(minAmount) + " - " + formatSatoshis(amount)
|
||||
: formatSatoshis(amount);
|
||||
}
|
||||
|
||||
static String formatVolumeRange(long minVolume, long volume) {
|
||||
return minVolume != volume
|
||||
? formatOfferVolume(minVolume) + " - " + formatOfferVolume(volume)
|
||||
: formatOfferVolume(volume);
|
||||
}
|
||||
|
||||
static String formatOfferPrice(long price) {
|
||||
NUMBER_FORMAT.setMaximumFractionDigits(4);
|
||||
NUMBER_FORMAT.setMinimumFractionDigits(4);
|
||||
NUMBER_FORMAT.setRoundingMode(RoundingMode.UNNECESSARY);
|
||||
return NUMBER_FORMAT.format((double) price / 10000);
|
||||
}
|
||||
|
||||
static String formatOfferVolume(long volume) {
|
||||
NUMBER_FORMAT.setMaximumFractionDigits(0);
|
||||
NUMBER_FORMAT.setRoundingMode(RoundingMode.UNNECESSARY);
|
||||
return NUMBER_FORMAT.format((double) volume / 10000);
|
||||
}
|
||||
}
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.cli;
|
||||
|
||||
import io.grpc.CallCredentials;
|
||||
|
144
cli/src/main/java/bisq/cli/TableFormat.java
Normal file
144
cli/src/main/java/bisq/cli/TableFormat.java
Normal file
@ -0,0 +1,144 @@
|
||||
package bisq.cli;
|
||||
|
||||
import bisq.proto.grpc.AddressBalanceInfo;
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import protobuf.PaymentAccount;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.formatAmountRange;
|
||||
import static bisq.cli.CurrencyFormat.formatOfferPrice;
|
||||
import static bisq.cli.CurrencyFormat.formatSatoshis;
|
||||
import static bisq.cli.CurrencyFormat.formatVolumeRange;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Collections.max;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.TimeZone.getTimeZone;
|
||||
|
||||
class TableFormat {
|
||||
|
||||
private static final TimeZone TZ_UTC = getTimeZone("UTC");
|
||||
private static final SimpleDateFormat DATE_FORMAT_ISO_8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
|
||||
// For inserting 2 spaces between column headers.
|
||||
private static final String COL_HEADER_DELIMITER = " ";
|
||||
|
||||
// Table column header format specs, right padded with two spaces. In some cases
|
||||
// such as COL_HEADER_CREATION_DATE, COL_HEADER_VOLUME and COL_HEADER_UUID, the
|
||||
// expected max data string length is accounted for. In others, the column header length
|
||||
// are expected to be greater than any column value length.
|
||||
private static final String COL_HEADER_ADDRESS = padEnd("Address", 34, ' ');
|
||||
private static final String COL_HEADER_AMOUNT = padEnd("BTC(min - max)", 24, ' ');
|
||||
private static final String COL_HEADER_BALANCE = padStart("Balance", 12, ' ');
|
||||
private static final String COL_HEADER_CONFIRMATIONS = "Confirmations";
|
||||
private static final String COL_HEADER_CREATION_DATE = padEnd("Creation Date (UTC)", 20, ' ');
|
||||
private static final String COL_HEADER_CURRENCY = "Currency";
|
||||
private static final String COL_HEADER_DIRECTION = "Buy/Sell"; // TODO "Take Offer to
|
||||
private static final String COL_HEADER_NAME = "Name";
|
||||
private static final String COL_HEADER_PAYMENT_METHOD = "Payment Method";
|
||||
private static final String COL_HEADER_PRICE = "Price in %-3s for 1 BTC";
|
||||
private static final String COL_HEADER_VOLUME = padEnd("%-3s(min - max)", 15, ' ');
|
||||
private static final String COL_HEADER_UUID = padEnd("ID", 52, ' ');
|
||||
|
||||
static String formatAddressBalanceTbl(List<AddressBalanceInfo> addressBalanceInfo) {
|
||||
String headerLine = (COL_HEADER_ADDRESS + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_BALANCE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_CONFIRMATIONS + COL_HEADER_DELIMITER + "\n");
|
||||
String colDataFormat = "%-" + COL_HEADER_ADDRESS.length() + "s" // left justify
|
||||
+ " %" + COL_HEADER_BALANCE.length() + "s" // right justify
|
||||
+ " %" + COL_HEADER_CONFIRMATIONS.length() + "d"; // right justify
|
||||
return headerLine
|
||||
+ addressBalanceInfo.stream()
|
||||
.map(info -> format(colDataFormat,
|
||||
info.getAddress(),
|
||||
formatSatoshis(info.getBalance()),
|
||||
info.getNumConfirmations()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
static String formatOfferTable(List<OfferInfo> offerInfo, String fiatCurrency) {
|
||||
|
||||
// Some column values might be longer than header, so we need to calculated them.
|
||||
int paymentMethodColWidth = getLengthOfLongestColumn(
|
||||
COL_HEADER_PAYMENT_METHOD.length(),
|
||||
offerInfo.stream()
|
||||
.map(OfferInfo::getPaymentMethodShortName)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
String headersFormat = COL_HEADER_DIRECTION + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_PRICE + COL_HEADER_DELIMITER // includes %s -> fiatCurrency
|
||||
+ COL_HEADER_AMOUNT + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_VOLUME + COL_HEADER_DELIMITER // includes %s -> fiatCurrency
|
||||
+ padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_CREATION_DATE + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_UUID.trim() + "%n";
|
||||
String headerLine = format(headersFormat, fiatCurrency, fiatCurrency);
|
||||
|
||||
String colDataFormat = "%-" + (COL_HEADER_DIRECTION.length() + COL_HEADER_DELIMITER.length()) + "s" // left
|
||||
+ "%" + (COL_HEADER_PRICE.length() - 1) + "s" // rt justify to end of hdr
|
||||
+ " %-" + (COL_HEADER_AMOUNT.length() - 1) + "s" // left justify
|
||||
+ " %" + COL_HEADER_VOLUME.length() + "s" // right justify
|
||||
+ " %-" + paymentMethodColWidth + "s" // left justify
|
||||
+ " %-" + (COL_HEADER_CREATION_DATE.length()) + "s" // left justify
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s";
|
||||
return headerLine
|
||||
+ offerInfo.stream()
|
||||
.map(o -> format(colDataFormat,
|
||||
o.getDirection(),
|
||||
formatOfferPrice(o.getPrice()),
|
||||
formatAmountRange(o.getMinAmount(), o.getAmount()),
|
||||
formatVolumeRange(o.getMinVolume(), o.getVolume()),
|
||||
o.getPaymentMethodShortName(),
|
||||
formatTimestamp(o.getDate()),
|
||||
o.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
static String formatPaymentAcctTbl(List<PaymentAccount> paymentAccounts) {
|
||||
// Some column values might be longer than header, so we need to calculated them.
|
||||
int nameColWidth = getLengthOfLongestColumn(
|
||||
COL_HEADER_NAME.length(),
|
||||
paymentAccounts.stream().map(PaymentAccount::getAccountName)
|
||||
.collect(Collectors.toList()));
|
||||
int paymentMethodColWidth = getLengthOfLongestColumn(
|
||||
COL_HEADER_PAYMENT_METHOD.length(),
|
||||
paymentAccounts.stream().map(a -> a.getPaymentMethod().getId())
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
String headerLine = padEnd(COL_HEADER_NAME, nameColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_CURRENCY + COL_HEADER_DELIMITER
|
||||
+ padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER
|
||||
+ COL_HEADER_UUID + COL_HEADER_DELIMITER + "\n";
|
||||
String colDataFormat = "%-" + nameColWidth + "s" // left justify
|
||||
+ " %" + COL_HEADER_CURRENCY.length() + "s" // right justify
|
||||
+ " %-" + paymentMethodColWidth + "s" // left justify
|
||||
+ " %-" + COL_HEADER_UUID.length() + "s"; // left justify
|
||||
return headerLine
|
||||
+ paymentAccounts.stream()
|
||||
.map(a -> format(colDataFormat,
|
||||
a.getAccountName(),
|
||||
a.getSelectedTradeCurrency().getCode(),
|
||||
a.getPaymentMethod().getId(),
|
||||
a.getId()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
// Return length of the longest string value, or the header.len, whichever is greater.
|
||||
private static int getLengthOfLongestColumn(int headerLength, List<String> strings) {
|
||||
int longest = max(strings, comparing(String::length)).length();
|
||||
return Math.max(longest, headerLength);
|
||||
}
|
||||
|
||||
private static String formatTimestamp(long timestamp) {
|
||||
DATE_FORMAT_ISO_8601.setTimeZone(TZ_UTC);
|
||||
return DATE_FORMAT_ISO_8601.format(new Date(timestamp));
|
||||
}
|
||||
}
|
39
cli/src/test/java/bisq/cli/GetOffersSmokeTest.java
Normal file
39
cli/src/test/java/bisq/cli/GetOffersSmokeTest.java
Normal file
@ -0,0 +1,39 @@
|
||||
package bisq.cli;
|
||||
|
||||
import static java.lang.System.out;
|
||||
|
||||
/**
|
||||
Smoke tests for getoffers method. Useful for examining the format of the console output.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Run `./bisq-daemon --apiPassword=xyz --appDataDir=$TESTDIR`
|
||||
|
||||
This can be run on mainnet.
|
||||
*/
|
||||
public class GetOffersSmokeTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
out.println(">>> getoffers buy usd");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "buy", "usd"});
|
||||
out.println(">>> getoffers sell usd");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "sell", "usd"});
|
||||
|
||||
out.println(">>> getoffers buy eur");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "buy", "eur"});
|
||||
out.println(">>> getoffers sell eur");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "sell", "eur"});
|
||||
|
||||
out.println(">>> getoffers buy gbp");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "buy", "gbp"});
|
||||
out.println(">>> getoffers sell gbp");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "sell", "gbp"});
|
||||
|
||||
out.println(">>> getoffers buy brl");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "buy", "brl"});
|
||||
out.println(">>> getoffers sell brl");
|
||||
CliMain.main(new String[]{"--password=xyz", "getoffers", "sell", "brl"});
|
||||
}
|
||||
|
||||
}
|
29
cli/test.sh
29
cli/test.sh
@ -166,6 +166,13 @@
|
||||
[ "$output" = "Error: address bogus not found in wallet" ]
|
||||
}
|
||||
|
||||
@test "test createpaymentacct PerfectMoneyDummy (missing nbr, ccy params)" {
|
||||
run ./bisq-cli --password=xyz createpaymentacct PerfectMoneyDummy
|
||||
[ "$status" -eq 1 ]
|
||||
echo "actual output: $output" >&2
|
||||
[ "$output" = "Error: incorrect parameter count, expecting account name, account number, currency code" ]
|
||||
}
|
||||
|
||||
@test "test createpaymentacct PerfectMoneyDummy 0123456789 USD" {
|
||||
run ./bisq-cli --password=xyz createpaymentacct PerfectMoneyDummy 0123456789 USD
|
||||
[ "$status" -eq 0 ]
|
||||
@ -176,6 +183,28 @@
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "test getoffers missing direction argument" {
|
||||
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" ]
|
||||
}
|
||||
|
||||
@test "test getoffers buy eur check return status" {
|
||||
run ./bisq-cli --password=xyz getoffers buy eur
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "test getoffers buy eur check return status" {
|
||||
run ./bisq-cli --password=xyz getoffers buy eur
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "test getoffers sell gbp check return status" {
|
||||
run ./bisq-cli --password=xyz getoffers sell gbp
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "test help displayed on stderr if no options or arguments" {
|
||||
run ./bisq-cli
|
||||
[ "$status" -eq 1 ]
|
||||
|
@ -19,16 +19,12 @@ package bisq.core.grpc;
|
||||
|
||||
import bisq.core.grpc.model.AddressBalanceInfo;
|
||||
import bisq.core.monetary.Price;
|
||||
import bisq.core.offer.CreateOfferService;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.offer.OfferBookService;
|
||||
import bisq.core.offer.OfferPayload;
|
||||
import bisq.core.offer.OpenOfferManager;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.trade.handlers.TransactionResultHandler;
|
||||
import bisq.core.trade.statistics.TradeStatistics2;
|
||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
import bisq.core.user.User;
|
||||
|
||||
import bisq.common.app.Version;
|
||||
|
||||
@ -48,35 +44,95 @@ import lombok.extern.slf4j.Slf4j;
|
||||
*/
|
||||
@Slf4j
|
||||
public class CoreApi {
|
||||
|
||||
private final CoreOffersService coreOffersService;
|
||||
private final CorePaymentAccountsService paymentAccountsService;
|
||||
private final CoreWalletsService walletsService;
|
||||
private final OfferBookService offerBookService;
|
||||
private final TradeStatisticsManager tradeStatisticsManager;
|
||||
private final CreateOfferService createOfferService;
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final User user;
|
||||
|
||||
@Inject
|
||||
public CoreApi(CorePaymentAccountsService paymentAccountsService,
|
||||
public CoreApi(CoreOffersService coreOffersService,
|
||||
CorePaymentAccountsService paymentAccountsService,
|
||||
CoreWalletsService walletsService,
|
||||
OfferBookService offerBookService,
|
||||
TradeStatisticsManager tradeStatisticsManager,
|
||||
CreateOfferService createOfferService,
|
||||
OpenOfferManager openOfferManager,
|
||||
User user) {
|
||||
TradeStatisticsManager tradeStatisticsManager) {
|
||||
this.coreOffersService = coreOffersService;
|
||||
this.paymentAccountsService = paymentAccountsService;
|
||||
this.walletsService = walletsService;
|
||||
this.offerBookService = offerBookService;
|
||||
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||
this.createOfferService = createOfferService;
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return Version.VERSION;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Offers
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public List<Offer> getOffers(String direction, String fiatCurrencyCode) {
|
||||
return coreOffersService.getOffers(direction, fiatCurrencyCode);
|
||||
}
|
||||
|
||||
public void createOffer(String currencyCode,
|
||||
String directionAsString,
|
||||
long priceAsLong,
|
||||
boolean useMarketBasedPrice,
|
||||
double marketPriceMargin,
|
||||
long amountAsLong,
|
||||
long minAmountAsLong,
|
||||
double buyerSecurityDeposit,
|
||||
String paymentAccountId,
|
||||
TransactionResultHandler resultHandler) {
|
||||
coreOffersService.createOffer(currencyCode,
|
||||
directionAsString,
|
||||
priceAsLong,
|
||||
useMarketBasedPrice,
|
||||
marketPriceMargin,
|
||||
amountAsLong,
|
||||
minAmountAsLong,
|
||||
buyerSecurityDeposit,
|
||||
paymentAccountId,
|
||||
resultHandler);
|
||||
}
|
||||
|
||||
public void createOffer(String offerId,
|
||||
String currencyCode,
|
||||
OfferPayload.Direction direction,
|
||||
Price price,
|
||||
boolean useMarketBasedPrice,
|
||||
double marketPriceMargin,
|
||||
Coin amount,
|
||||
Coin minAmount,
|
||||
double buyerSecurityDeposit,
|
||||
PaymentAccount paymentAccount,
|
||||
boolean useSavingsWallet,
|
||||
TransactionResultHandler resultHandler) {
|
||||
coreOffersService.createOffer(offerId,
|
||||
currencyCode,
|
||||
direction,
|
||||
price,
|
||||
useMarketBasedPrice,
|
||||
marketPriceMargin,
|
||||
amount,
|
||||
minAmount,
|
||||
buyerSecurityDeposit,
|
||||
paymentAccount,
|
||||
useSavingsWallet,
|
||||
resultHandler);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PaymentAccounts
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void createPaymentAccount(String accountName, String accountNumber, String fiatCurrencyCode) {
|
||||
paymentAccountsService.createPaymentAccount(accountName, accountNumber, fiatCurrencyCode);
|
||||
}
|
||||
|
||||
public Set<PaymentAccount> getPaymentAccounts() {
|
||||
return paymentAccountsService.getPaymentAccounts();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Wallets
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -120,83 +176,4 @@ public class CoreApi {
|
||||
public int getNumConfirmationsForMostRecentTransaction(String addressString) {
|
||||
return walletsService.getNumConfirmationsForMostRecentTransaction(addressString);
|
||||
}
|
||||
|
||||
public List<Offer> getOffers() {
|
||||
return offerBookService.getOffers();
|
||||
}
|
||||
|
||||
public void placeOffer(String currencyCode,
|
||||
String directionAsString,
|
||||
long priceAsLong,
|
||||
boolean useMarketBasedPrice,
|
||||
double marketPriceMargin,
|
||||
long amountAsLong,
|
||||
long minAmountAsLong,
|
||||
double buyerSecurityDeposit,
|
||||
String paymentAccountId,
|
||||
TransactionResultHandler resultHandler) {
|
||||
String offerId = createOfferService.getRandomOfferId();
|
||||
OfferPayload.Direction direction = OfferPayload.Direction.valueOf(directionAsString);
|
||||
Price price = Price.valueOf(currencyCode, priceAsLong);
|
||||
Coin amount = Coin.valueOf(amountAsLong);
|
||||
Coin minAmount = Coin.valueOf(minAmountAsLong);
|
||||
PaymentAccount paymentAccount = user.getPaymentAccount(paymentAccountId);
|
||||
// We don't support atm funding from external wallet to keep it simple
|
||||
boolean useSavingsWallet = true;
|
||||
|
||||
placeOffer(offerId,
|
||||
currencyCode,
|
||||
direction,
|
||||
price,
|
||||
useMarketBasedPrice,
|
||||
marketPriceMargin,
|
||||
amount,
|
||||
minAmount,
|
||||
buyerSecurityDeposit,
|
||||
paymentAccount,
|
||||
useSavingsWallet,
|
||||
resultHandler);
|
||||
}
|
||||
|
||||
public void placeOffer(String offerId,
|
||||
String currencyCode,
|
||||
OfferPayload.Direction direction,
|
||||
Price price,
|
||||
boolean useMarketBasedPrice,
|
||||
double marketPriceMargin,
|
||||
Coin amount,
|
||||
Coin minAmount,
|
||||
double buyerSecurityDeposit,
|
||||
PaymentAccount paymentAccount,
|
||||
boolean useSavingsWallet,
|
||||
TransactionResultHandler resultHandler) {
|
||||
Offer offer = createOfferService.createAndGetOffer(offerId,
|
||||
direction,
|
||||
currencyCode,
|
||||
amount,
|
||||
minAmount,
|
||||
price,
|
||||
useMarketBasedPrice,
|
||||
marketPriceMargin,
|
||||
buyerSecurityDeposit,
|
||||
paymentAccount);
|
||||
|
||||
openOfferManager.placeOffer(offer,
|
||||
buyerSecurityDeposit,
|
||||
useSavingsWallet,
|
||||
resultHandler,
|
||||
log::error);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PaymentAccounts
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void createPaymentAccount(String accountName, String accountNumber, String fiatCurrencyCode) {
|
||||
paymentAccountsService.createPaymentAccount(accountName, accountNumber, fiatCurrencyCode);
|
||||
}
|
||||
|
||||
public Set<PaymentAccount> getPaymentAccounts() {
|
||||
return paymentAccountsService.getPaymentAccounts();
|
||||
}
|
||||
}
|
||||
|
143
core/src/main/java/bisq/core/grpc/CoreOffersService.java
Normal file
143
core/src/main/java/bisq/core/grpc/CoreOffersService.java
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.grpc;
|
||||
|
||||
import bisq.core.monetary.Price;
|
||||
import bisq.core.offer.CreateOfferService;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.offer.OfferBookService;
|
||||
import bisq.core.offer.OfferPayload;
|
||||
import bisq.core.offer.OpenOfferManager;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.trade.handlers.TransactionResultHandler;
|
||||
import bisq.core.user.User;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.core.offer.OfferPayload.Direction.BUY;
|
||||
|
||||
@Slf4j
|
||||
public class CoreOffersService {
|
||||
|
||||
private final CreateOfferService createOfferService;
|
||||
private final OfferBookService offerBookService;
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final User user;
|
||||
|
||||
@Inject
|
||||
public CoreOffersService(CreateOfferService createOfferService,
|
||||
OfferBookService offerBookService,
|
||||
OpenOfferManager openOfferManager,
|
||||
User user) {
|
||||
this.createOfferService = createOfferService;
|
||||
this.offerBookService = offerBookService;
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public List<Offer> getOffers(String direction, String fiatCurrencyCode) {
|
||||
List<Offer> offers = offerBookService.getOffers().stream()
|
||||
.filter(o -> {
|
||||
var offerOfWantedDirection = o.getDirection().name().equalsIgnoreCase(direction);
|
||||
var offerInWantedCurrency = o.getOfferPayload().getCounterCurrencyCode().equalsIgnoreCase(fiatCurrencyCode);
|
||||
return offerOfWantedDirection && offerInWantedCurrency;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// A buyer probably wants to see sell orders in price ascending order.
|
||||
// A seller probably wants to see buy orders in price descending order.
|
||||
if (direction.equalsIgnoreCase(BUY.name()))
|
||||
offers.sort(Comparator.comparing(Offer::getPrice).reversed());
|
||||
else
|
||||
offers.sort(Comparator.comparing(Offer::getPrice));
|
||||
|
||||
return offers;
|
||||
}
|
||||
|
||||
public void createOffer(String currencyCode,
|
||||
String directionAsString,
|
||||
long priceAsLong,
|
||||
boolean useMarketBasedPrice,
|
||||
double marketPriceMargin,
|
||||
long amountAsLong,
|
||||
long minAmountAsLong,
|
||||
double buyerSecurityDeposit,
|
||||
String paymentAccountId,
|
||||
TransactionResultHandler resultHandler) {
|
||||
String offerId = createOfferService.getRandomOfferId();
|
||||
OfferPayload.Direction direction = OfferPayload.Direction.valueOf(directionAsString);
|
||||
Price price = Price.valueOf(currencyCode, priceAsLong);
|
||||
Coin amount = Coin.valueOf(amountAsLong);
|
||||
Coin minAmount = Coin.valueOf(minAmountAsLong);
|
||||
PaymentAccount paymentAccount = user.getPaymentAccount(paymentAccountId);
|
||||
// We don't support atm funding from external wallet to keep it simple
|
||||
boolean useSavingsWallet = true;
|
||||
|
||||
//noinspection ConstantConditions
|
||||
createOffer(offerId,
|
||||
currencyCode,
|
||||
direction,
|
||||
price,
|
||||
useMarketBasedPrice,
|
||||
marketPriceMargin,
|
||||
amount,
|
||||
minAmount,
|
||||
buyerSecurityDeposit,
|
||||
paymentAccount,
|
||||
useSavingsWallet,
|
||||
resultHandler);
|
||||
}
|
||||
|
||||
public void createOffer(String offerId,
|
||||
String currencyCode,
|
||||
OfferPayload.Direction direction,
|
||||
Price price,
|
||||
boolean useMarketBasedPrice,
|
||||
double marketPriceMargin,
|
||||
Coin amount,
|
||||
Coin minAmount,
|
||||
double buyerSecurityDeposit,
|
||||
PaymentAccount paymentAccount,
|
||||
boolean useSavingsWallet,
|
||||
TransactionResultHandler resultHandler) {
|
||||
Offer offer = createOfferService.createAndGetOffer(offerId,
|
||||
direction,
|
||||
currencyCode,
|
||||
amount,
|
||||
minAmount,
|
||||
price,
|
||||
useMarketBasedPrice,
|
||||
marketPriceMargin,
|
||||
buyerSecurityDeposit,
|
||||
paymentAccount);
|
||||
|
||||
openOfferManager.placeOffer(offer,
|
||||
buyerSecurityDeposit,
|
||||
useSavingsWallet,
|
||||
resultHandler,
|
||||
log::error);
|
||||
}
|
||||
}
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* 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.grpc;
|
||||
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
@ -41,7 +58,7 @@ public class CorePaymentAccountsService {
|
||||
paymentAccount.init();
|
||||
paymentAccount.setAccountName(accountName);
|
||||
((PerfectMoneyAccount) paymentAccount).setAccountNr(accountNumber);
|
||||
paymentAccount.setSingleTradeCurrency(new FiatCurrency(fiatCurrencyCode));
|
||||
paymentAccount.setSingleTradeCurrency(new FiatCurrency(fiatCurrencyCode.toUpperCase()));
|
||||
user.addPaymentAccount(paymentAccount);
|
||||
|
||||
// Don't do this on mainnet until thoroughly tested.
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* 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.grpc;
|
||||
|
||||
import bisq.core.btc.Balances;
|
||||
@ -55,9 +72,7 @@ class CoreWalletsService {
|
||||
}
|
||||
|
||||
public long getAvailableBalance() {
|
||||
if (!walletsManager.areWalletsAvailable())
|
||||
throw new IllegalStateException("wallet is not yet available");
|
||||
|
||||
verifyWalletsAreAvailable();
|
||||
verifyEncryptedWalletIsUnlocked();
|
||||
|
||||
var balance = balances.getAvailableBalance().get();
|
||||
@ -79,9 +94,7 @@ class CoreWalletsService {
|
||||
}
|
||||
|
||||
public List<AddressBalanceInfo> getFundingAddresses() {
|
||||
if (!walletsManager.areWalletsAvailable())
|
||||
throw new IllegalStateException("wallet is not yet available");
|
||||
|
||||
verifyWalletsAreAvailable();
|
||||
verifyEncryptedWalletIsUnlocked();
|
||||
|
||||
// Create a new funding address if none exists.
|
||||
@ -123,8 +136,7 @@ class CoreWalletsService {
|
||||
}
|
||||
|
||||
public void setWalletPassword(String password, String newPassword) {
|
||||
if (!walletsManager.areWalletsAvailable())
|
||||
throw new IllegalStateException("wallet is not yet available");
|
||||
verifyWalletsAreAvailable();
|
||||
|
||||
KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt();
|
||||
|
||||
@ -213,6 +225,12 @@ class CoreWalletsService {
|
||||
walletsManager.backupWallets();
|
||||
}
|
||||
|
||||
// Throws a RuntimeException if wallets are not available (encrypted or not).
|
||||
private void verifyWalletsAreAvailable() {
|
||||
if (!walletsManager.areWalletsAvailable())
|
||||
throw new IllegalStateException("wallet is not yet available");
|
||||
}
|
||||
|
||||
// Throws a RuntimeException if wallets are not available or not encrypted.
|
||||
private void verifyWalletIsAvailableAndEncrypted() {
|
||||
if (!walletsManager.areWalletsAvailable())
|
||||
|
105
core/src/main/java/bisq/core/grpc/GrpcOffersService.java
Normal file
105
core/src/main/java/bisq/core/grpc/GrpcOffersService.java
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.grpc;
|
||||
|
||||
import bisq.core.grpc.model.OfferInfo;
|
||||
import bisq.core.trade.handlers.TransactionResultHandler;
|
||||
|
||||
import bisq.proto.grpc.CreateOfferReply;
|
||||
import bisq.proto.grpc.CreateOfferRequest;
|
||||
import bisq.proto.grpc.GetOffersReply;
|
||||
import bisq.proto.grpc.GetOffersRequest;
|
||||
import bisq.proto.grpc.OffersGrpc;
|
||||
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
class GrpcOffersService extends OffersGrpc.OffersImplBase {
|
||||
|
||||
private final CoreApi coreApi;
|
||||
|
||||
@Inject
|
||||
public GrpcOffersService(CoreApi coreApi) {
|
||||
this.coreApi = coreApi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOffers(GetOffersRequest req,
|
||||
StreamObserver<GetOffersReply> responseObserver) {
|
||||
// The client cannot see bisq.core.Offer or its fromProto method.
|
||||
// We use the lighter weight OfferInfo proto wrapper instead, containing just
|
||||
// enough fields to view and create offers.
|
||||
List<OfferInfo> result = coreApi.getOffers(req.getDirection(), req.getFiatCurrencyCode())
|
||||
.stream().map(offer -> new OfferInfo.OfferInfoBuilder()
|
||||
.withId(offer.getId())
|
||||
.withDirection(offer.getDirection().name())
|
||||
.withPrice(offer.getPrice().getValue())
|
||||
.withUseMarketBasedPrice(offer.isUseMarketBasedPrice())
|
||||
.withMarketPriceMargin(offer.getMarketPriceMargin())
|
||||
.withAmount(offer.getAmount().value)
|
||||
.withMinAmount(offer.getMinAmount().value)
|
||||
.withVolume(offer.getVolume().getValue())
|
||||
.withMinVolume(offer.getMinVolume().getValue())
|
||||
.withBuyerSecurityDeposit(offer.getBuyerSecurityDeposit().value)
|
||||
.withPaymentAccountId("") // only used when creating offer (?)
|
||||
.withPaymentMethodId(offer.getPaymentMethod().getId())
|
||||
.withPaymentMethodShortName(offer.getPaymentMethod().getShortName())
|
||||
.withBaseCurrencyCode(offer.getOfferPayload().getBaseCurrencyCode())
|
||||
.withCounterCurrencyCode(offer.getOfferPayload().getCounterCurrencyCode())
|
||||
.withDate(offer.getDate().getTime())
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
var reply = GetOffersReply.newBuilder()
|
||||
.addAllOffers(
|
||||
result.stream()
|
||||
.map(OfferInfo::toProtoMessage)
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createOffer(CreateOfferRequest req,
|
||||
StreamObserver<CreateOfferReply> responseObserver) {
|
||||
TransactionResultHandler resultHandler = transaction -> {
|
||||
CreateOfferReply reply = CreateOfferReply.newBuilder().setResult(true).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
};
|
||||
coreApi.createOffer(
|
||||
req.getCurrencyCode(),
|
||||
req.getDirection(),
|
||||
req.getPrice(),
|
||||
req.getUseMarketBasedPrice(),
|
||||
req.getMarketPriceMargin(),
|
||||
req.getAmount(),
|
||||
req.getMinAmount(),
|
||||
req.getBuyerSecurityDeposit(),
|
||||
req.getPaymentAccountId(),
|
||||
resultHandler);
|
||||
}
|
||||
}
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* 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.grpc;
|
||||
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
|
@ -17,24 +17,16 @@
|
||||
|
||||
package bisq.core.grpc;
|
||||
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.trade.handlers.TransactionResultHandler;
|
||||
import bisq.core.trade.statistics.TradeStatistics2;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
|
||||
import bisq.proto.grpc.GetOffersGrpc;
|
||||
import bisq.proto.grpc.GetOffersReply;
|
||||
import bisq.proto.grpc.GetOffersRequest;
|
||||
import bisq.proto.grpc.GetTradeStatisticsGrpc;
|
||||
import bisq.proto.grpc.GetTradeStatisticsReply;
|
||||
import bisq.proto.grpc.GetTradeStatisticsRequest;
|
||||
import bisq.proto.grpc.GetVersionGrpc;
|
||||
import bisq.proto.grpc.GetVersionReply;
|
||||
import bisq.proto.grpc.GetVersionRequest;
|
||||
import bisq.proto.grpc.PlaceOfferGrpc;
|
||||
import bisq.proto.grpc.PlaceOfferReply;
|
||||
import bisq.proto.grpc.PlaceOfferRequest;
|
||||
|
||||
import io.grpc.Server;
|
||||
import io.grpc.ServerBuilder;
|
||||
@ -58,16 +50,16 @@ public class GrpcServer {
|
||||
@Inject
|
||||
public GrpcServer(Config config,
|
||||
CoreApi coreApi,
|
||||
GrpcOffersService offersService,
|
||||
GrpcPaymentAccountsService paymentAccountsService,
|
||||
GrpcWalletsService walletService) {
|
||||
GrpcWalletsService walletsService) {
|
||||
this.coreApi = coreApi;
|
||||
this.server = ServerBuilder.forPort(config.apiPort)
|
||||
.addService(new GetVersionService())
|
||||
.addService(new GetTradeStatisticsService())
|
||||
.addService(new GetOffersService())
|
||||
.addService(new PlaceOfferService())
|
||||
.addService(offersService)
|
||||
.addService(paymentAccountsService)
|
||||
.addService(walletService)
|
||||
.addService(walletsService)
|
||||
.intercept(new PasswordAuthInterceptor(config.apiPassword))
|
||||
.build();
|
||||
}
|
||||
@ -94,7 +86,6 @@ public class GrpcServer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class GetTradeStatisticsService extends GetTradeStatisticsGrpc.GetTradeStatisticsImplBase {
|
||||
@Override
|
||||
public void getTradeStatistics(GetTradeStatisticsRequest req,
|
||||
@ -109,40 +100,4 @@ public class GrpcServer {
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
class GetOffersService extends GetOffersGrpc.GetOffersImplBase {
|
||||
@Override
|
||||
public void getOffers(GetOffersRequest req, StreamObserver<GetOffersReply> responseObserver) {
|
||||
|
||||
var tradeStatistics = coreApi.getOffers().stream()
|
||||
.map(Offer::toProtoMessage)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
var reply = GetOffersReply.newBuilder().addAllOffers(tradeStatistics).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
class PlaceOfferService extends PlaceOfferGrpc.PlaceOfferImplBase {
|
||||
@Override
|
||||
public void placeOffer(PlaceOfferRequest req, StreamObserver<PlaceOfferReply> responseObserver) {
|
||||
TransactionResultHandler resultHandler = transaction -> {
|
||||
PlaceOfferReply reply = PlaceOfferReply.newBuilder().setResult(true).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
};
|
||||
coreApi.placeOffer(
|
||||
req.getCurrencyCode(),
|
||||
req.getDirection(),
|
||||
req.getPrice(),
|
||||
req.getUseMarketBasedPrice(),
|
||||
req.getMarketPriceMargin(),
|
||||
req.getAmount(),
|
||||
req.getMinAmount(),
|
||||
req.getBuyerSecurityDeposit(),
|
||||
req.getPaymentAccountId(),
|
||||
resultHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* 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.grpc;
|
||||
|
||||
import bisq.core.grpc.model.AddressBalanceInfo;
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* 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.grpc;
|
||||
|
||||
import io.grpc.Metadata;
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
* 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.grpc.model;
|
||||
|
||||
import bisq.common.Payload;
|
||||
|
215
core/src/main/java/bisq/core/grpc/model/OfferInfo.java
Normal file
215
core/src/main/java/bisq/core/grpc/model/OfferInfo.java
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.grpc.model;
|
||||
|
||||
import bisq.common.Payload;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
@Getter
|
||||
public class OfferInfo implements Payload {
|
||||
|
||||
private final String id;
|
||||
private final String direction;
|
||||
private final long price;
|
||||
private final boolean useMarketBasedPrice;
|
||||
private final double marketPriceMargin;
|
||||
private final long amount;
|
||||
private final long minAmount;
|
||||
private final long volume;
|
||||
private final long minVolume;
|
||||
private final long buyerSecurityDeposit;
|
||||
private final String paymentAccountId; // only used when creating offer
|
||||
private final String paymentMethodId;
|
||||
private final String paymentMethodShortName;
|
||||
// For fiat offer the baseCurrencyCode is BTC and the counterCurrencyCode is the fiat currency
|
||||
// For altcoin offers it is the opposite. baseCurrencyCode is the altcoin and the counterCurrencyCode is BTC.
|
||||
private final String baseCurrencyCode;
|
||||
private final String counterCurrencyCode;
|
||||
private final long date;
|
||||
|
||||
public OfferInfo(OfferInfoBuilder builder) {
|
||||
this.id = builder.id;
|
||||
this.direction = builder.direction;
|
||||
this.price = builder.price;
|
||||
this.useMarketBasedPrice = builder.useMarketBasedPrice;
|
||||
this.marketPriceMargin = builder.marketPriceMargin;
|
||||
this.amount = builder.amount;
|
||||
this.minAmount = builder.minAmount;
|
||||
this.volume = builder.volume;
|
||||
this.minVolume = builder.minVolume;
|
||||
this.buyerSecurityDeposit = builder.buyerSecurityDeposit;
|
||||
this.paymentAccountId = builder.paymentAccountId;
|
||||
this.paymentMethodId = builder.paymentMethodId;
|
||||
this.paymentMethodShortName = builder.paymentMethodShortName;
|
||||
this.baseCurrencyCode = builder.baseCurrencyCode;
|
||||
this.counterCurrencyCode = builder.counterCurrencyCode;
|
||||
this.date = builder.date;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PROTO BUFFER
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public bisq.proto.grpc.OfferInfo toProtoMessage() {
|
||||
return bisq.proto.grpc.OfferInfo.newBuilder()
|
||||
.setId(id)
|
||||
.setDirection(direction)
|
||||
.setPrice(price)
|
||||
.setUseMarketBasedPrice(useMarketBasedPrice)
|
||||
.setMarketPriceMargin(marketPriceMargin)
|
||||
.setAmount(amount)
|
||||
.setMinAmount(minAmount)
|
||||
.setVolume(volume)
|
||||
.setMinVolume(minVolume)
|
||||
.setBuyerSecurityDeposit(buyerSecurityDeposit)
|
||||
.setPaymentAccountId(paymentAccountId)
|
||||
.setPaymentMethodId(paymentMethodId)
|
||||
.setPaymentMethodShortName(paymentMethodShortName)
|
||||
.setBaseCurrencyCode(baseCurrencyCode)
|
||||
.setCounterCurrencyCode(counterCurrencyCode)
|
||||
.setDate(date)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static OfferInfo fromProto(bisq.proto.grpc.OfferInfo proto) {
|
||||
/*
|
||||
TODO (will be needed by the createoffer method)
|
||||
return new OfferInfo(proto.getOfferPayload().getId(),
|
||||
proto.getOfferPayload().getDate());
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* OfferInfoBuilder helps avoid bungling use of a large OfferInfo constructor
|
||||
* argument list. If consecutive argument values of the same type are not
|
||||
* ordered correctly, the compiler won't complain but the resulting bugs could
|
||||
* be hard to find and fix.
|
||||
*/
|
||||
public static class OfferInfoBuilder {
|
||||
private String id;
|
||||
private String direction;
|
||||
private long price;
|
||||
private boolean useMarketBasedPrice;
|
||||
private double marketPriceMargin;
|
||||
private long amount;
|
||||
private long minAmount;
|
||||
private long volume;
|
||||
private long minVolume;
|
||||
private long buyerSecurityDeposit;
|
||||
private String paymentAccountId;
|
||||
private String paymentMethodId;
|
||||
private String paymentMethodShortName;
|
||||
private String baseCurrencyCode;
|
||||
private String counterCurrencyCode;
|
||||
private long date;
|
||||
|
||||
public OfferInfoBuilder() {
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withDirection(String direction) {
|
||||
this.direction = direction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withPrice(long price) {
|
||||
this.price = price;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withUseMarketBasedPrice(boolean useMarketBasedPrice) {
|
||||
this.useMarketBasedPrice = useMarketBasedPrice;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withMarketPriceMargin(double useMarketBasedPrice) {
|
||||
this.marketPriceMargin = useMarketBasedPrice;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withAmount(long amount) {
|
||||
this.amount = amount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withMinAmount(long minAmount) {
|
||||
this.minAmount = minAmount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withVolume(long volume) {
|
||||
this.volume = volume;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withMinVolume(long minVolume) {
|
||||
this.minVolume = minVolume;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withBuyerSecurityDeposit(long buyerSecurityDeposit) {
|
||||
this.buyerSecurityDeposit = buyerSecurityDeposit;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withPaymentAccountId(String paymentAccountId) {
|
||||
this.paymentAccountId = paymentAccountId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withPaymentMethodId(String paymentMethodId) {
|
||||
this.paymentMethodId = paymentMethodId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withPaymentMethodShortName(String paymentMethodShortName) {
|
||||
this.paymentMethodShortName = paymentMethodShortName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withBaseCurrencyCode(String baseCurrencyCode) {
|
||||
this.baseCurrencyCode = baseCurrencyCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withCounterCurrencyCode(String counterCurrencyCode) {
|
||||
this.counterCurrencyCode = counterCurrencyCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfoBuilder withDate(long date) {
|
||||
this.date = date;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OfferInfo build() {
|
||||
return new OfferInfo(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -40,35 +40,58 @@ message GetVersionReply {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// TradeStatistics
|
||||
// Offers
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
service GetTradeStatistics {
|
||||
rpc GetTradeStatistics (GetTradeStatisticsRequest) returns (GetTradeStatisticsReply) {
|
||||
}
|
||||
}
|
||||
|
||||
message GetTradeStatisticsRequest {
|
||||
}
|
||||
|
||||
message GetTradeStatisticsReply {
|
||||
repeated TradeStatistics2 TradeStatistics = 1;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Offer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
service GetOffers {
|
||||
service Offers {
|
||||
rpc GetOffers (GetOffersRequest) returns (GetOffersReply) {
|
||||
}
|
||||
rpc CreateOffer (CreateOfferRequest) returns (CreateOfferReply) {
|
||||
}
|
||||
}
|
||||
|
||||
message GetOffersRequest {
|
||||
string direction = 1;
|
||||
string fiatCurrencyCode = 2;
|
||||
}
|
||||
|
||||
message GetOffersReply {
|
||||
repeated Offer offers = 1;
|
||||
repeated OfferInfo offers = 1;
|
||||
}
|
||||
|
||||
message CreateOfferRequest {
|
||||
string currencyCode = 1; // TODO switch order w/ direction field in next PR
|
||||
string direction = 2;
|
||||
uint64 price = 3;
|
||||
bool useMarketBasedPrice = 4;
|
||||
double marketPriceMargin = 5;
|
||||
uint64 amount = 6;
|
||||
uint64 minAmount = 7;
|
||||
double buyerSecurityDeposit = 8;
|
||||
string paymentAccountId = 9;
|
||||
}
|
||||
|
||||
message CreateOfferReply {
|
||||
bool result = 1;
|
||||
}
|
||||
|
||||
message OfferInfo {
|
||||
string id = 1;
|
||||
string direction = 2;
|
||||
uint64 price = 3;
|
||||
bool useMarketBasedPrice = 4;
|
||||
double marketPriceMargin = 5;
|
||||
uint64 amount = 6;
|
||||
uint64 minAmount = 7;
|
||||
uint64 volume = 8;
|
||||
uint64 minVolume = 9;
|
||||
uint64 buyerSecurityDeposit = 10;
|
||||
string paymentAccountId = 11; // only used when creating offer
|
||||
string paymentMethodId = 12;
|
||||
string paymentMethodShortName = 13;
|
||||
string baseCurrencyCode = 14;
|
||||
string counterCurrencyCode = 15;
|
||||
uint64 date = 16;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -99,28 +122,19 @@ message GetPaymentAccountsReply {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PlaceOffer
|
||||
// TradeStatistics
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
service PlaceOffer {
|
||||
rpc PlaceOffer (PlaceOfferRequest) returns (PlaceOfferReply) {
|
||||
service GetTradeStatistics {
|
||||
rpc GetTradeStatistics (GetTradeStatisticsRequest) returns (GetTradeStatisticsReply) {
|
||||
}
|
||||
}
|
||||
|
||||
message PlaceOfferRequest {
|
||||
string currencyCode = 1;
|
||||
string direction = 2;
|
||||
uint64 price = 3;
|
||||
bool useMarketBasedPrice = 4;
|
||||
double marketPriceMargin = 5;
|
||||
uint64 amount = 6;
|
||||
uint64 minAmount = 7;
|
||||
double buyerSecurityDeposit = 8;
|
||||
string paymentAccountId = 9;
|
||||
message GetTradeStatisticsRequest {
|
||||
}
|
||||
|
||||
message PlaceOfferReply {
|
||||
bool result = 1;
|
||||
message GetTradeStatisticsReply {
|
||||
repeated TradeStatistics2 TradeStatistics = 1;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
Reference in New Issue
Block a user