Merge pull request #4329 from ghubstan/6-getoffers

Add rpc method 'getoffers'
This commit is contained in:
sqrrm 2020-06-25 20:41:34 +02:00 committed by GitHub
commit a54eeeabcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1009 additions and 235 deletions

View File

@ -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"));
}
}

View 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);
}
}

View File

@ -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;

View 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));
}
}

View 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"});
}
}

View File

@ -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 ]

View File

@ -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();
}
}

View 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);
}
}

View File

@ -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.

View File

@ -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())

View 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);
}
}

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View 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);
}
}
}

View File

@ -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;
}
///////////////////////////////////////////////////////////////////////////////////////////