Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build out 'createoffer' API method #4559

Merged
merged 52 commits into from Oct 3, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
9132722
Replace hardcoded version with Version.java value
ghubstan Sep 22, 2020
34cfe95
Remove comment
ghubstan Sep 22, 2020
8896372
Move test dispute agent type constants to core
ghubstan Sep 22, 2020
1f307c8
Fix indentation
ghubstan Sep 22, 2020
6a50402
Add simple create payment acct test
ghubstan Sep 22, 2020
1d88d27
Remove final modifiers
ghubstan Sep 22, 2020
c4dd041
Don't use static boilerplate helpers if not necessary
ghubstan Sep 22, 2020
e63a6c5
Remove comment
ghubstan Sep 22, 2020
92f36ed
Add get default payment acct helper
ghubstan Sep 23, 2020
2b68e57
Stub out createoffer method in CLI
ghubstan Sep 23, 2020
3c0c443
Change API's createoffer return value from bool to Offer
ghubstan Sep 23, 2020
70e3be0
Add API CreateOfferTest case
ghubstan Sep 23, 2020
fc1f0ba
Fix imports
ghubstan Sep 23, 2020
d190d09
Fix unnecessary use of fully qualified name
ghubstan Sep 23, 2020
adb175c
Add options helper for handling negative number CLI params
ghubstan Sep 23, 2020
1431a07
Add license comment
ghubstan Sep 23, 2020
d5b8800
Add license comment and btc-string to satoshi converter
ghubstan Sep 23, 2020
c8a7fe4
Print createoffer's reply in the CLI's console
ghubstan Sep 23, 2020
a6048a4
Add comment to empty catch block for codacy
ghubstan Sep 23, 2020
ec9c1b0
Uppercase direction & ccy-code CLI arguments in core
ghubstan Sep 24, 2020
9999c95
Change 'createoffer' argument order
ghubstan Sep 24, 2020
942a6f2
Scale & convert (double) fixed price input to long
ghubstan Sep 24, 2020
6cdbc13
Move 'createoffer' price arg transform to server & test it
ghubstan Sep 24, 2020
96278b9
Push currencyCode.toUpperCase conversion below CoreApi
ghubstan Sep 25, 2020
995af0d
Convert mktPriceMargin to %, make createAndPlaceOffer private
ghubstan Sep 25, 2020
6cf9bbb
Minor createoffer test changes
ghubstan Sep 25, 2020
3b51824
Do not reassign currencyCode parameter
ghubstan Sep 25, 2020
82ce864
Delete trailing spaces from blank line for codacy
ghubstan Sep 25, 2020
2f3e3a3
Add simple mkt-price service & test calculated offer prices
ghubstan Sep 25, 2020
18df1e2
Fix abs(dbl) comparison
ghubstan Sep 25, 2020
92042d7
Remove unused import
ghubstan Sep 25, 2020
de3105a
Add license comment
ghubstan Sep 25, 2020
96abda4
Tidy up create offer using mkt price margin % test
ghubstan Sep 26, 2020
35a77be
Redefine DisputeAgentType REFUNDAGENT as REFUND_AGENT
ghubstan Sep 27, 2020
7053169
Fix asserts
ghubstan Sep 27, 2020
82b7b79
Factor out duplicated OfferInfo wrapping
ghubstan Sep 27, 2020
50d4b9f
Fix 'switch statements should have a default label' codacy problem
ghubstan Sep 27, 2020
d9ece9f
Revert "Fix 'switch statements should have a default label' codacy pr…
ghubstan Sep 27, 2020
f376153
Codacy requires default label in switch
ghubstan Sep 28, 2020
94996a5
Fix tx result handling in GrpcOffersService
ghubstan Sep 28, 2020
fa5c21c
Fix BitcoinCli wrapper create bug
ghubstan Sep 28, 2020
fc94b97
Throw exception to CLI if attempted offer placement fails
ghubstan Sep 28, 2020
cfe22c3
Make task handler's error msg CLI friendly (needs review)
ghubstan Sep 28, 2020
23a677d
Use list.set, not list.remove, list.add
ghubstan Oct 2, 2020
e09b821
Explain use of args clone index (i-1)
ghubstan Oct 2, 2020
628c557
Revert "Make task handler's error msg CLI friendly (needs review)"
ghubstan Oct 2, 2020
631c3f4
Log provenance of Task error on server, but pass only exception msg t…
ghubstan Oct 2, 2020
0332711
Ignore codacy complaint about (dead) default switch labels
ghubstan Oct 2, 2020
f1693a6
Add switch statement break to make codacy happy
ghubstan Oct 2, 2020
c71ad84
Change 'break' to 'return' at end of switch.
ghubstan Oct 2, 2020
723fc8f
Skip over method name in args loop, start at i=1
ghubstan Oct 2, 2020
d3d6d98
Revert all changes since commit d55114e
ghubstan Oct 3, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions apitest/src/test/java/bisq/apitest/method/MethodTest.java
Expand Up @@ -22,6 +22,7 @@
import bisq.proto.grpc.GetFundingAddressesRequest;
import bisq.proto.grpc.GetPaymentAccountsRequest;
import bisq.proto.grpc.LockWalletRequest;
import bisq.proto.grpc.MarketPriceRequest;
import bisq.proto.grpc.RegisterDisputeAgentRequest;
import bisq.proto.grpc.RemoveWalletPasswordRequest;
import bisq.proto.grpc.SetWalletPasswordRequest;
Expand Down Expand Up @@ -75,6 +76,10 @@ protected final GetFundingAddressesRequest createGetFundingAddressesRequest() {
return GetFundingAddressesRequest.newBuilder().build();
}

protected final MarketPriceRequest createMarketPriceRequest(String currencyCode) {
return MarketPriceRequest.newBuilder().setCurrencyCode(currencyCode).build();
}

// Convenience methods for calling frequently used & thoroughly tested gRPC services.

protected final long getBalance(BisqAppConfig bisqAppConfig) {
Expand Down Expand Up @@ -115,9 +120,9 @@ protected final CreatePaymentAccountRequest createCreatePerfectMoneyPaymentAccou
}

protected final PaymentAccount getDefaultPerfectDummyPaymentAccount(BisqAppConfig bisqAppConfig) {
var getPaymentAccountsRequest = GetPaymentAccountsRequest.newBuilder().build();
var req = GetPaymentAccountsRequest.newBuilder().build();
var paymentAccountsService = grpcStubs(bisqAppConfig).paymentAccountsService;
PaymentAccount paymentAccount = paymentAccountsService.getPaymentAccounts(getPaymentAccountsRequest)
PaymentAccount paymentAccount = paymentAccountsService.getPaymentAccounts(req)
.getPaymentAccountsList()
.stream()
.sorted(comparing(PaymentAccount::getCreationDate))
Expand All @@ -126,6 +131,11 @@ protected final PaymentAccount getDefaultPerfectDummyPaymentAccount(BisqAppConfi
return paymentAccount;
}

protected final double getMarketPrice(BisqAppConfig bisqAppConfig, String currencyCode) {
var req = createMarketPriceRequest(currencyCode);
return grpcStubs(bisqAppConfig).priceService.getMarketPrice(req).getPrice();
}

// Static conveniences for test methods and test case fixture setups.

protected static RegisterDisputeAgentRequest createRegisterDisputeAgentRequest(String disputeAgentType) {
Expand Down
Expand Up @@ -45,7 +45,7 @@
@Slf4j
abstract class AbstractCreateOfferTest extends MethodTest {

protected static GrpcStubs alice;
protected static GrpcStubs aliceStubs;

@BeforeAll
public static void setUp() {
Expand All @@ -56,7 +56,7 @@ static void startSupportingApps() {
try {
// setUpScaffold(new String[]{"--supportingApps", "bitcoind,seednode,alicedaemon", "--enableBisqDebugging", "true"});
setUpScaffold(bitcoind, seednode, alicedaemon);
alice = grpcStubs(alicedaemon);
aliceStubs = grpcStubs(alicedaemon);

// Generate 1 regtest block for alice's wallet to show 10 BTC balance,
// and give alicedaemon time to parse the new block.
Expand All @@ -79,7 +79,7 @@ protected final List<OfferInfo> getOffersSortedByDate(String direction, String c
var req = GetOffersRequest.newBuilder()
.setDirection(direction)
.setCurrencyCode(currencyCode).build();
var reply = alice.offersService.getOffers(req);
var reply = aliceStubs.offersService.getOffers(req);
return sortOffersByDate(reply.getOffersList());
}

Expand All @@ -89,6 +89,10 @@ protected final List<OfferInfo> sortOffersByDate(List<OfferInfo> offerInfoList)
.collect(Collectors.toList());
}

protected final double getPrice(String currencyCode) {
return getMarketPrice(alicedaemon, currencyCode);
}

@AfterAll
public static void tearDown() {
tearDownScaffold();
Expand Down
Expand Up @@ -53,7 +53,7 @@ public void testCreateUSDBTCBuyOfferUsingFixedPrice10000() {
.setPrice("10000")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.build();
var newOffer = alice.offersService.createOffer(req).getOffer();
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();
String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
assertEquals("BUY", newOffer.getDirection());
Expand Down Expand Up @@ -94,7 +94,7 @@ public void testCreateUSDBTCBuyOfferUsingFixedPrice100001234() {
.setPrice("10000.1234")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.build();
var newOffer = alice.offersService.createOffer(req).getOffer();
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();
String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
assertEquals("BUY", newOffer.getDirection());
Expand Down
Expand Up @@ -18,10 +18,16 @@
package bisq.apitest.method.offer;

import bisq.core.btc.wallet.Restrictions;
import bisq.core.monetary.Altcoin;

import bisq.proto.grpc.CreateOfferRequest;
import bisq.proto.grpc.OfferInfo;

import org.bitcoinj.utils.Fiat;

import java.math.BigDecimal;
import java.math.RoundingMode;

import lombok.extern.slf4j.Slf4j;

import org.junit.jupiter.api.MethodOrderer;
Expand All @@ -30,9 +36,15 @@
import org.junit.jupiter.api.TestMethodOrder;

import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.common.util.MathUtils.roundDouble;
import static bisq.common.util.MathUtils.scaleDownByPowerOf10;
import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static protobuf.OfferPayload.Direction.BUY;

@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
Expand All @@ -42,19 +54,19 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractCreateOfferTe
@Order(1)
public void testCreateUSDBTCBuyOffer5PctPriceMargin() {
var paymentAccount = getDefaultPerfectDummyPaymentAccount(alicedaemon);
double priceMarginPctInput = 5.00;
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(paymentAccount.getId())
.setDirection("buy")
.setCurrencyCode("usd")
.setAmount(10000000)
.setMinAmount(10000000)
.setUseMarketBasedPrice(true)
.setMarketPriceMargin(5.00)
.setMarketPriceMargin(priceMarginPctInput)
.setPrice("0")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.build();
var newOffer = alice.offersService.createOffer(req).getOffer();
log.info(newOffer.toString());
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();

String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
Expand All @@ -77,25 +89,27 @@ public void testCreateUSDBTCBuyOffer5PctPriceMargin() {
assertEquals("", offer.getPaymentAccountId());
assertEquals("BTC", offer.getBaseCurrencyCode());
assertEquals("USD", offer.getCounterCurrencyCode());

assertMarketBasedPriceDiff(offer, priceMarginPctInput);
}

@Test
@Order(2)
public void testCreateNZDBTCBuyOfferMinus2PctPriceMargin() {
var paymentAccount = getDefaultPerfectDummyPaymentAccount(alicedaemon);
double priceMarginPctInput = -2.00;
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(paymentAccount.getId())
.setDirection("buy")
.setCurrencyCode("nzd")
.setAmount(10000000)
.setMinAmount(10000000)
.setUseMarketBasedPrice(true)
.setMarketPriceMargin(-2.00)
.setMarketPriceMargin(priceMarginPctInput)
.setPrice("0")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.build();
var newOffer = alice.offersService.createOffer(req).getOffer();
log.info(newOffer.toString());
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();

String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
Expand All @@ -118,5 +132,134 @@ public void testCreateNZDBTCBuyOfferMinus2PctPriceMargin() {
assertEquals("", offer.getPaymentAccountId());
assertEquals("BTC", offer.getBaseCurrencyCode());
assertEquals("NZD", offer.getCounterCurrencyCode());

assertMarketBasedPriceDiff(offer, priceMarginPctInput);
}

@Test
@Order(3)
public void testCreateGBPBTCSellOfferMinus1Point5PctPriceMargin() {
var paymentAccount = getDefaultPerfectDummyPaymentAccount(alicedaemon);
double priceMarginPctInput = -1.5;
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(paymentAccount.getId())
.setDirection("sell")
.setCurrencyCode("gbp")
.setAmount(10000000)
.setMinAmount(10000000)
.setUseMarketBasedPrice(true)
.setMarketPriceMargin(priceMarginPctInput)
.setPrice("0")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.build();
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();
log.info(newOffer.toString());

String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
assertEquals("SELL", newOffer.getDirection());
assertTrue(newOffer.getUseMarketBasedPrice());
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(paymentAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("GBP", newOffer.getCounterCurrencyCode());

OfferInfo offer = getMostRecentOffer("sell", "gbp");
assertEquals(newOfferId, offer.getId());
assertEquals("SELL", offer.getDirection());
assertTrue(offer.getUseMarketBasedPrice());
assertEquals(10000000, offer.getAmount());
assertEquals(10000000, offer.getMinAmount());
assertEquals(1500000, offer.getBuyerSecurityDeposit());
assertEquals("", offer.getPaymentAccountId());
assertEquals("BTC", offer.getBaseCurrencyCode());
assertEquals("GBP", offer.getCounterCurrencyCode());

assertMarketBasedPriceDiff(offer, priceMarginPctInput);
}

@Test
@Order(4)
public void testCreateBRLBTCSellOffer6Point55PctPriceMargin() {
var paymentAccount = getDefaultPerfectDummyPaymentAccount(alicedaemon);
double priceMarginPctInput = 6.55;
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(paymentAccount.getId())
.setDirection("sell")
.setCurrencyCode("brl")
.setAmount(10000000)
.setMinAmount(10000000)
.setUseMarketBasedPrice(true)
.setMarketPriceMargin(priceMarginPctInput)
.setPrice("0")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.build();
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();
log.info(newOffer.toString());

String newOfferId = newOffer.getId();
assertNotEquals("", newOfferId);
assertEquals("SELL", newOffer.getDirection());
assertTrue(newOffer.getUseMarketBasedPrice());
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(paymentAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("BRL", newOffer.getCounterCurrencyCode());

OfferInfo offer = getMostRecentOffer("sell", "brl");
assertEquals(newOfferId, offer.getId());
assertEquals("SELL", offer.getDirection());
assertTrue(offer.getUseMarketBasedPrice());
assertEquals(10000000, offer.getAmount());
assertEquals(10000000, offer.getMinAmount());
assertEquals(1500000, offer.getBuyerSecurityDeposit());
assertEquals("", offer.getPaymentAccountId());
assertEquals("BTC", offer.getBaseCurrencyCode());
assertEquals("BRL", offer.getCounterCurrencyCode());

assertMarketBasedPriceDiff(offer, priceMarginPctInput);
}

private void assertMarketBasedPriceDiff(OfferInfo offer, double priceMarginPctInput) {
// Assert the mkt price margin difference ( %) is < 1% from the expected difference.
String counterCurrencyCode = offer.getCounterCurrencyCode();
double lastPrice = getPrice(counterCurrencyCode);
int precision = isCryptoCurrency(counterCurrencyCode) ? Altcoin.SMALLEST_UNIT_EXPONENT : Fiat.SMALLEST_UNIT_EXPONENT;
double scaledOfferPrice = scaleDownByPowerOf10(offer.getPrice(), precision);
assertTrue(() -> {
double expectedPriceMarginPct = scaleDownByPowerOf10(priceMarginPctInput, 2);
double actualPriceMarginPct = offer.getDirection().equals(BUY.name())
? getPercentageDifference(scaledOfferPrice, lastPrice)
: getPercentageDifference(lastPrice, scaledOfferPrice);
double diff = expectedPriceMarginPct - actualPriceMarginPct;
if (diff > 0.0001) {
String priceCalculationWarning = format("The calculated price was %.2f%s off"
+ " mkt price, not the expected %.2f%s off mkt price.%n"
+ "Offer %s",
scaleUpByPowerOf10(actualPriceMarginPct, 2), "%",
priceMarginPctInput, "%",
offer);
double onePercent = 0.01;
if (diff > Math.abs(onePercent)) {
log.error(priceCalculationWarning);
return false;
} else {
log.warn(priceCalculationWarning);
return true;
}
} else {
return true;
}
});
}

private double getPercentageDifference(double price1, double price2) {
return new BigDecimal(roundDouble((1 - (price1 / price2)), 5))
.setScale(4, RoundingMode.HALF_UP)
.doubleValue();
}
}
3 changes: 3 additions & 0 deletions cli/src/main/java/bisq/cli/GrpcStubs.java
Expand Up @@ -21,6 +21,7 @@
import bisq.proto.grpc.GetVersionGrpc;
import bisq.proto.grpc.OffersGrpc;
import bisq.proto.grpc.PaymentAccountsGrpc;
import bisq.proto.grpc.PriceGrpc;
import bisq.proto.grpc.WalletsGrpc;

import io.grpc.CallCredentials;
Expand All @@ -34,6 +35,7 @@ public class GrpcStubs {
public final GetVersionGrpc.GetVersionBlockingStub versionService;
public final OffersGrpc.OffersBlockingStub offersService;
public final PaymentAccountsGrpc.PaymentAccountsBlockingStub paymentAccountsService;
public final PriceGrpc.PriceBlockingStub priceService;
public final WalletsGrpc.WalletsBlockingStub walletsService;

public GrpcStubs(String apiHost, int apiPort, String apiPassword) {
Expand All @@ -52,6 +54,7 @@ public GrpcStubs(String apiHost, int apiPort, String apiPassword) {
this.versionService = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(credentials);
this.offersService = OffersGrpc.newBlockingStub(channel).withCallCredentials(credentials);
this.paymentAccountsService = PaymentAccountsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
this.priceService = PriceGrpc.newBlockingStub(channel).withCallCredentials(credentials);
this.walletsService = WalletsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
}
}
12 changes: 12 additions & 0 deletions core/src/main/java/bisq/core/api/CoreApi.java
Expand Up @@ -22,6 +22,7 @@
import bisq.core.offer.Offer;
import bisq.core.offer.OfferPayload;
import bisq.core.payment.PaymentAccount;
import bisq.core.provider.price.MarketPrice;
import bisq.core.trade.handlers.TransactionResultHandler;
import bisq.core.trade.statistics.TradeStatistics2;
import bisq.core.trade.statistics.TradeStatisticsManager;
Expand Down Expand Up @@ -50,17 +51,20 @@ public class CoreApi {
private final CoreDisputeAgentsService coreDisputeAgentsService;
private final CoreOffersService coreOffersService;
private final CorePaymentAccountsService paymentAccountsService;
private final CorePriceService corePriceService;
private final CoreWalletsService walletsService;
private final TradeStatisticsManager tradeStatisticsManager;

@Inject
public CoreApi(CoreDisputeAgentsService coreDisputeAgentsService,
CoreOffersService coreOffersService,
CorePaymentAccountsService paymentAccountsService,
CorePriceService corePriceService,
CoreWalletsService walletsService,
TradeStatisticsManager tradeStatisticsManager) {
this.coreDisputeAgentsService = coreDisputeAgentsService;
this.coreOffersService = coreOffersService;
this.corePriceService = corePriceService;
this.paymentAccountsService = paymentAccountsService;
this.walletsService = walletsService;
this.tradeStatisticsManager = tradeStatisticsManager;
Expand Down Expand Up @@ -153,6 +157,14 @@ public Set<PaymentAccount> getPaymentAccounts() {
return paymentAccountsService.getPaymentAccounts();
}

///////////////////////////////////////////////////////////////////////////////////////////
// Prices
///////////////////////////////////////////////////////////////////////////////////////////

public double getMarketPrice(String currencyCode) {
return corePriceService.getMarketPrice(currencyCode);
}

///////////////////////////////////////////////////////////////////////////////////////////
// Wallets
///////////////////////////////////////////////////////////////////////////////////////////
Expand Down