Add market depth API call (#47)

This commit is contained in:
Randall B 2022-02-11 17:13:56 -06:00 committed by GitHub
parent 0df355faa7
commit f89ed9d3bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 171 additions and 61 deletions

View File

@ -104,7 +104,19 @@ const TestConfig = {
["8086", ["10005", "7781"]], ["8086", ["10005", "7781"]],
]), ]),
devPrivilegePrivKey: "6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a", // from DEV_PRIVILEGE_PRIV_KEY devPrivilegePrivKey: "6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a", // from DEV_PRIVILEGE_PRIV_KEY
timeout: 900000 // timeout in ms for all tests to complete (15 minutes) timeout: 900000, // timeout in ms for all tests to complete (15 minutes),
postOffer: {
direction: "buy", // buy or sell xmr
amount: BigInt("200000000000"),
counterCurrency: "eth",
price: undefined, // use market price if undefined // TODO: converted to long on backend
paymentAcountId: undefined,
priceMargin: 0.0,
minAmount: BigInt("150000000000"), // TODO: disable by default
buyerSecurityDeposit: 0.15,
awaitUnlockedBalance: false,
triggerPrice: undefined // TODO: fails if there is a decimal, converted to long on backend
}
}; };
interface TxContext { interface TxContext {
@ -562,6 +574,76 @@ test("Can get market prices", async () => {
.toThrow('Currency not found: INVALID_CURRENCY'); .toThrow('Currency not found: INVALID_CURRENCY');
}); });
test("Can get market depth", async () => {
let counterCurrency = "eth";
// clear offers
await clearOffers(alice, counterCurrency);
await clearOffers(bob, counterCurrency);
async function clearOffers(havenod: HavenoDaemon, counterCurrency: string) {
for (let offer of await havenod.getMyOffers()) {
if (offer.getBaseCurrencyCode().toLowerCase() === counterCurrency.toLowerCase()) { // TODO (woodser): offer base currency and counter currency are switched
await havenod.removeOffer(offer.getId());
}
}
}
// market depth has no data
await wait(TestConfig.maxTimePeerNoticeMs);
let marketDepth = await alice.getMarketDepth(counterCurrency);
expect(marketDepth.getBuyPricesList().length).toEqual(0);
expect(marketDepth.getBuyDepthList().length).toEqual(0);
expect(marketDepth.getSellPricesList().length).toEqual(0);
expect(marketDepth.getSellDepthList().length).toEqual(0);
// post offers to buy and sell
await postOffer(alice, {direction: "buy", amount: BigInt("150000000000"), counterCurrency: counterCurrency, priceMargin: 0.00, awaitUnlockedBalance: true, price: 17.0}); // TODO: offer price is reversed. fix everywhere
await postOffer(alice, {direction: "buy", amount: BigInt("150000000000"), counterCurrency: counterCurrency, priceMargin: 0.02, awaitUnlockedBalance: true, price: 17.2});
await postOffer(alice, {direction: "buy", amount: BigInt("200000000000"), counterCurrency: counterCurrency, priceMargin: 0.05, awaitUnlockedBalance: true, price: 17.3});
await postOffer(alice, {direction: "buy", amount: BigInt("150000000000"), counterCurrency: counterCurrency, priceMargin: 0.02, awaitUnlockedBalance: true, price: 17.3});
await postOffer(alice, {direction: "sell", amount: BigInt("300000000000"), counterCurrency: counterCurrency, priceMargin: 0.00, awaitUnlockedBalance: true});
await postOffer(alice, {direction: "sell", amount: BigInt("300000000000"), counterCurrency: counterCurrency, priceMargin: 0.02, awaitUnlockedBalance: true});
await postOffer(alice, {direction: "sell", amount: BigInt("400000000000"), counterCurrency: counterCurrency, priceMargin: 0.05, awaitUnlockedBalance: true});
// get bob's market depth
await wait(TestConfig.maxTimePeerNoticeMs);
marketDepth = await alice.getMarketDepth(counterCurrency);
// each unique price has a depth
expect(marketDepth.getBuyPricesList().length).toEqual(3);
expect(marketDepth.getSellPricesList().length).toEqual(3);
expect(marketDepth.getBuyPricesList().length).toEqual(marketDepth.getBuyDepthList().length);
expect(marketDepth.getSellPricesList().length).toEqual(marketDepth.getSellDepthList().length);
// test buy prices and depths
const priceDivisor = 100000000; // TODO: offer price = price * 100000000
let buyOffers = (await alice.getOffers("buy")).concat(await alice.getMyOffers("buy")).sort(function(a, b) { return a.getPrice() - b.getPrice() });
expect(marketDepth.getBuyPricesList()[0]).toEqual(1 / (buyOffers[0].getPrice() / priceDivisor)); // TODO: price when posting offer is reversed. this assumes crypto counter currency
expect(marketDepth.getBuyPricesList()[1]).toEqual(1 / (buyOffers[1].getPrice() / priceDivisor));
expect(marketDepth.getBuyPricesList()[2]).toEqual(1 / (buyOffers[2].getPrice() / priceDivisor));
expect(marketDepth.getBuyDepthList()[0]).toEqual(0.15);
expect(marketDepth.getBuyDepthList()[1]).toEqual(0.30);
expect(marketDepth.getBuyDepthList()[2]).toEqual(0.65);
// test sell prices and depths
let sellOffers = (await alice.getOffers("sell")).concat(await alice.getMyOffers("sell")).sort(function(a, b) { return b.getPrice() - a.getPrice() });
expect(marketDepth.getSellPricesList()[0]).toEqual(1 / (sellOffers[0].getPrice() / priceDivisor));
expect(marketDepth.getSellPricesList()[1]).toEqual(1 / (sellOffers[1].getPrice() / priceDivisor));
expect(marketDepth.getSellPricesList()[2]).toEqual(1 / (sellOffers[2].getPrice() / priceDivisor));
expect(marketDepth.getSellDepthList()[0]).toEqual(0.3);
expect(marketDepth.getSellDepthList()[1]).toEqual(0.6);
expect(marketDepth.getSellDepthList()[2]).toEqual(1);
// clear offers
await clearOffers(alice, counterCurrency);
await clearOffers(bob, counterCurrency);
// test invalid currency
await expect(async () => {await alice.getMarketDepth("INVALID_CURRENCY")})
.rejects
.toThrow('Currency not found: INVALID_CURRENCY');
});
test("Can register as dispute agents", async () => { test("Can register as dispute agents", async () => {
await arbitrator.registerDisputeAgent("mediator", TestConfig.devPrivilegePrivKey); // TODO: bisq mediator = haveno arbitrator await arbitrator.registerDisputeAgent("mediator", TestConfig.devPrivilegePrivKey); // TODO: bisq mediator = haveno arbitrator
await arbitrator.registerDisputeAgent("refundagent", TestConfig.devPrivilegePrivKey); // TODO: no refund agent in haveno await arbitrator.registerDisputeAgent("refundagent", TestConfig.devPrivilegePrivKey); // TODO: no refund agent in haveno
@ -657,7 +739,7 @@ test("Can post and remove an offer", async () => {
let unlockedBalanceBefore: bigint = BigInt((await alice.getBalances()).getUnlockedBalance()); let unlockedBalanceBefore: bigint = BigInt((await alice.getBalances()).getUnlockedBalance());
// post offer // post offer
let offer: OfferInfo = await postOffer(alice, "buy", BigInt("200000000000"), undefined); let offer: OfferInfo = await postOffer(alice);
assert.equal(offer.getState(), "AVAILABLE"); assert.equal(offer.getState(), "AVAILABLE");
// has offer // has offer
@ -688,7 +770,7 @@ test("Invalidates offers when reserved funds are spent", async () => {
// post offer // post offer
await wait(1000); await wait(1000);
let offer: OfferInfo = await postOffer(alice, "buy", tradeAmount, undefined); let offer: OfferInfo = await postOffer(alice, {amount: tradeAmount});
// get key images reserved by offer // get key images reserved by offer
let reservedKeyImages = []; let reservedKeyImages = [];
@ -749,7 +831,7 @@ test("Handles unexpected errors during trade initialization", async () => {
// trader 0 posts offer // trader 0 posts offer
console.log("Posting offer"); console.log("Posting offer");
let offer = await postOffer(traders[0], "buy", tradeAmount, undefined); let offer = await postOffer(traders[0], {amount: tradeAmount});
offer = await traders[0].getMyOffer(offer.getId()); offer = await traders[0].getMyOffer(offer.getId());
assert.equal(offer.getState(), "AVAILABLE"); assert.equal(offer.getState(), "AVAILABLE");
@ -841,7 +923,7 @@ test("Cannot make or take offer with insufficient unlocked funds", async () => {
// charlie cannot make offer with insufficient funds // charlie cannot make offer with insufficient funds
try { try {
await postOffer(charlie, "buy", BigInt("200000000000"), paymentAccount.getId()); await postOffer(charlie, {paymentAccountId: paymentAccount.getId()});
throw new Error("Should have failed making offer with insufficient funds") throw new Error("Should have failed making offer with insufficient funds")
} catch (err) { } catch (err) {
let errTyped = err as grpcWeb.RpcError; let errTyped = err as grpcWeb.RpcError;
@ -856,7 +938,7 @@ test("Cannot make or take offer with insufficient unlocked funds", async () => {
else { else {
let tradeAmount: bigint = BigInt("250000000000"); let tradeAmount: bigint = BigInt("250000000000");
await waitForUnlockedBalance(tradeAmount * BigInt("2"), alice); await waitForUnlockedBalance(tradeAmount * BigInt("2"), alice);
offer = await postOffer(alice, "buy", tradeAmount, undefined); offer = await postOffer(alice, {amount: tradeAmount});
assert.equal(offer.getState(), "AVAILABLE"); assert.equal(offer.getState(), "AVAILABLE");
await wait(TestConfig.walletSyncPeriodMs * 2); await wait(TestConfig.walletSyncPeriodMs * 2);
} }
@ -907,7 +989,7 @@ test("Can complete a trade", async () => {
// alice posts offer to buy xmr // alice posts offer to buy xmr
console.log("Alice posting offer"); console.log("Alice posting offer");
let direction = "buy"; let direction = "buy";
let offer: OfferInfo = await postOffer(alice, direction, tradeAmount, undefined); let offer: OfferInfo = await postOffer(alice, {direction: direction, amount: tradeAmount});
expect(offer.getState()).toEqual("AVAILABLE"); expect(offer.getState()).toEqual("AVAILABLE");
console.log("Alice done posting offer"); console.log("Alice done posting offer");
@ -1073,7 +1155,7 @@ async function initHavenoDaemon(config?: any): Promise<HavenoDaemon> {
return havenod; return havenod;
async function getAvailablePort(): Promise<number> { async function getAvailablePort(): Promise<number> {
return new Promise(function(resolve, reject) { return new Promise(function(resolve) {
let srv = net.createServer(); let srv = net.createServer();
srv.listen(0, function() { srv.listen(0, function() {
let port = srv.address().port; let port = srv.address().port;
@ -1204,7 +1286,7 @@ async function waitForUnlockedBalance(amount: bigint, ...wallets: any[]) {
await startMining(); await startMining();
let promises: Promise<void>[] = []; let promises: Promise<void>[] = [];
for (let wallet of wallets) { for (let wallet of wallets) {
promises.push(new Promise(async function(resolve, reject) { promises.push(new Promise(async function(resolve) {
let taskLooper: any = new TaskLooper(async function() { let taskLooper: any = new TaskLooper(async function() {
if (await wallet.getUnlockedBalance() >= amount) { if (await wallet.getUnlockedBalance() >= amount) {
taskLooper.stop(); taskLooper.stop();
@ -1224,7 +1306,7 @@ async function waitForUnlockedTxs(...txHashes: string[]) {
let promises: Promise<void>[] = [] let promises: Promise<void>[] = []
for (let txHash of txHashes) { for (let txHash of txHashes) {
promises.push(new Promise(async function(resolve, reject) { promises.push(new Promise(async function(resolve, reject) {
let taskLooper: any = new TaskLooper(async function() { let taskLooper = new TaskLooper(async function() {
let tx = await monerod.getTx(txHash); let tx = await monerod.getTx(txHash);
if (tx.isConfirmed() && tx.getBlock().getHeight() <= await monerod.getHeight() - 10) { if (tx.isConfirmed() && tx.getBlock().getHeight() <= await monerod.getHeight() - 10) {
taskLooper.stop(); taskLooper.stop();
@ -1286,10 +1368,10 @@ function testTx(tx: XmrTx, ctx: TxContext) {
assert(tx.getOutgoingTransfer() || tx.getIncomingTransfersList().length); // TODO (woodser): test transfers assert(tx.getOutgoingTransfer() || tx.getIncomingTransfersList().length); // TODO (woodser): test transfers
for (let incomingTransfer of tx.getIncomingTransfersList()) testTransfer(incomingTransfer, ctx); for (let incomingTransfer of tx.getIncomingTransfersList()) testTransfer(incomingTransfer, ctx);
if (tx.getOutgoingTransfer()) testTransfer(tx.getOutgoingTransfer()!, ctx); if (tx.getOutgoingTransfer()) testTransfer(tx.getOutgoingTransfer()!, ctx);
if (ctx.isCreatedTx) testCreatedTx(tx, ctx); if (ctx.isCreatedTx) testCreatedTx(tx);
} }
function testCreatedTx(tx: XmrTx, ctx: TxContext) { function testCreatedTx(tx: XmrTx) {
assert.equal(tx.getTimestamp(), 0); assert.equal(tx.getTimestamp(), 0);
assert.equal(tx.getIsConfirmed(), false); assert.equal(tx.getIsConfirmed(), false);
assert.equal(tx.getIsLocked(), true); assert.equal(tx.getIsLocked(), true);
@ -1299,11 +1381,11 @@ function testCreatedTx(tx: XmrTx, ctx: TxContext) {
function testTransfer(transfer: XmrIncomingTransfer | XmrOutgoingTransfer, ctx: TxContext) { function testTransfer(transfer: XmrIncomingTransfer | XmrOutgoingTransfer, ctx: TxContext) {
expect(BigInt(transfer.getAmount())).toBeGreaterThanOrEqual(BigInt("0")); expect(BigInt(transfer.getAmount())).toBeGreaterThanOrEqual(BigInt("0"));
assert(transfer.getAccountIndex() >= 0); assert(transfer.getAccountIndex() >= 0);
if (transfer instanceof XmrIncomingTransfer) testIncomingTransfer(transfer, ctx); if (transfer instanceof XmrIncomingTransfer) testIncomingTransfer(transfer);
else testOutgoingTransfer(transfer, ctx); else testOutgoingTransfer(transfer, ctx);
} }
function testIncomingTransfer(transfer: XmrIncomingTransfer, ctx: TxContext) { function testIncomingTransfer(transfer: XmrIncomingTransfer) {
assert(transfer.getAddress()); assert(transfer.getAddress());
assert(transfer.getSubaddressIndex() >= 0); assert(transfer.getSubaddressIndex() >= 0);
assert(transfer.getNumSuggestedConfirmations() > 0); assert(transfer.getNumSuggestedConfirmations() > 0);
@ -1330,34 +1412,45 @@ function testDestination(destination: XmrDestination) {
expect(BigInt(destination.getAmount())).toBeGreaterThan(BigInt("0")); expect(BigInt(destination.getAmount())).toBeGreaterThan(BigInt("0"));
} }
async function createCryptoPaymentAccount(trader: HavenoDaemon): Promise<PaymentAccount> { async function createCryptoPaymentAccount(trader: HavenoDaemon, currencyCode = "eth"): Promise<PaymentAccount> {
let testAccount = TestConfig.cryptoAccounts[0]; for (let cryptoAccount of TestConfig.cryptoAccounts) {
let paymentAccount: PaymentAccount = await trader.createCryptoPaymentAccount( if (cryptoAccount.currencyCode.toLowerCase() !== currencyCode.toLowerCase()) continue;
testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + GenUtils.getUUID(), return trader.createCryptoPaymentAccount(
testAccount.currencyCode, cryptoAccount.currencyCode + " " + cryptoAccount.address.substr(0, 8) + "... " + GenUtils.getUUID(),
testAccount.address); cryptoAccount.currencyCode,
return paymentAccount; cryptoAccount.address);
}
throw new Error("No test config for crypto: " + currencyCode);
} }
async function postOffer(maker: HavenoDaemon, direction: string, amount: bigint, paymentAccountId: string|undefined) { // TODO: specify counter currency code
async function postOffer(maker: HavenoDaemon, config?: any) {
// assign default options
config = Object.assign({}, TestConfig.postOffer, config);
// wait for unlocked balance
if (config.awaitUnlockedBalance) await waitForUnlockedBalance(config.amount * BigInt("2"), maker);
// create payment account if not given // create payment account if not given
if (!paymentAccountId) paymentAccountId = (await createCryptoPaymentAccount(maker)).getId(); if (!config.paymentAccountId) config.paymentAccountId = (await createCryptoPaymentAccount(maker, config.counterCurrency)).getId();
// get unlocked balance before reserving offer // get unlocked balance before reserving offer
let unlockedBalanceBefore: bigint = BigInt((await maker.getBalances()).getUnlockedBalance()); let unlockedBalanceBefore: bigint = BigInt((await maker.getBalances()).getUnlockedBalance());
// post offer // post offer
let offer: OfferInfo = await maker.postOffer("eth", // TODO: re-arrange post offer parameters like this postOffer() or use config interface?
direction, // buy or sell xmr for eth let offer: OfferInfo = await maker.postOffer(
12.378981, // price TODO: price is optional? price string gets converted to long? config.counterCurrency,
true, // use market price config.direction,
0.02, // market price margin, e.g. within 2% config.price,
amount, // amount config.price ? false : true, // TODO: redundant with price field?
BigInt("150000000000"), // min amount config.priceMargin,
0.15, // buyer security deposit, e.g. 15% config.amount,
paymentAccountId, // payment account id config.minAmount,
undefined); // trigger price // TODO: fails if there is a decimal, gets converted to long? config.buyerSecurityDeposit,
config.paymentAccountId,
config.triggerPrice);
testOffer(offer); testOffer(offer);
// unlocked balance has decreased // unlocked balance has decreased
@ -1365,12 +1458,12 @@ async function postOffer(maker: HavenoDaemon, direction: string, amount: bigint,
if (unlockedBalanceAfter === unlockedBalanceBefore) throw new Error("unlocked balance did not change after posting offer"); if (unlockedBalanceAfter === unlockedBalanceBefore) throw new Error("unlocked balance did not change after posting offer");
// offer is included in my offers only // offer is included in my offers only
if (!getOffer(await maker.getMyOffers(direction), offer.getId())) { if (!getOffer(await maker.getMyOffers(config.amountdirection), offer.getId())) {
await wait(10000); await wait(10000);
if (!getOffer(await maker.getMyOffers(direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in my offers"); if (!getOffer(await maker.getMyOffers(config.amountdirection), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in my offers");
else console.log("The offer finally posted!"); else console.log("The offer finally posted!");
} }
if (getOffer(await maker.getOffers(direction), offer.getId())) throw new Error("My offer " + offer.getId() + " should not appear in available offers"); if (getOffer(await maker.getOffers(config.amountdirection), offer.getId())) throw new Error("My offer " + offer.getId() + " should not appear in available offers");
return offer; return offer;
} }

View File

@ -2,7 +2,7 @@ import {HavenoUtils} from "./utils/HavenoUtils";
import {TaskLooper} from "./utils/TaskLooper"; import {TaskLooper} from "./utils/TaskLooper";
import * as grpcWeb from 'grpc-web'; import * as grpcWeb from 'grpc-web';
import {GetVersionClient, AccountClient, MoneroConnectionsClient, DisputeAgentsClient, NotificationsClient, WalletsClient, PriceClient, OffersClient, PaymentAccountsClient, TradesClient, ShutdownServerClient} from './protobuf/GrpcServiceClientPb'; import {GetVersionClient, AccountClient, MoneroConnectionsClient, DisputeAgentsClient, NotificationsClient, WalletsClient, PriceClient, OffersClient, PaymentAccountsClient, TradesClient, ShutdownServerClient} from './protobuf/GrpcServiceClientPb';
import {GetVersionRequest, GetVersionReply, IsAppInitializedRequest, IsAppInitializedReply, RegisterDisputeAgentRequest, MarketPriceRequest, MarketPriceReply, MarketPricesRequest, MarketPricesReply, MarketPriceInfo, GetBalancesRequest, GetBalancesReply, XmrBalanceInfo, GetOffersRequest, GetOffersReply, OfferInfo, GetPaymentAccountsRequest, GetPaymentAccountsReply, CreateCryptoCurrencyPaymentAccountRequest, CreateCryptoCurrencyPaymentAccountReply, CreateOfferRequest, CreateOfferReply, CancelOfferRequest, TakeOfferRequest, TakeOfferReply, TradeInfo, GetTradeRequest, GetTradeReply, GetTradesRequest, GetTradesReply, GetNewDepositSubaddressRequest, GetNewDepositSubaddressReply, ConfirmPaymentStartedRequest, ConfirmPaymentReceivedRequest, XmrTx, GetXmrTxsRequest, GetXmrTxsReply, XmrDestination, CreateXmrTxRequest, CreateXmrTxReply, RelayXmrTxRequest, RelayXmrTxReply, CreateAccountRequest, AccountExistsRequest, AccountExistsReply, DeleteAccountRequest, OpenAccountRequest, IsAccountOpenRequest, IsAccountOpenReply, CloseAccountRequest, ChangePasswordRequest, BackupAccountRequest, BackupAccountReply, RestoreAccountRequest, StopRequest, NotificationMessage, RegisterNotificationListenerRequest, SendNotificationRequest, UrlConnection, AddConnectionRequest, RemoveConnectionRequest, GetConnectionRequest, GetConnectionsRequest, SetConnectionRequest, CheckConnectionRequest, CheckConnectionsReply, CheckConnectionsRequest, StartCheckingConnectionsRequest, StopCheckingConnectionsRequest, GetBestAvailableConnectionRequest, SetAutoSwitchRequest, CheckConnectionReply, GetConnectionsReply, GetConnectionReply, GetBestAvailableConnectionReply} from './protobuf/grpc_pb'; import {GetVersionRequest, GetVersionReply, IsAppInitializedRequest, IsAppInitializedReply, RegisterDisputeAgentRequest, MarketPriceRequest, MarketPriceReply, MarketPricesRequest, MarketPricesReply, MarketPriceInfo, MarketDepthRequest, MarketDepthReply, MarketDepthInfo, GetBalancesRequest, GetBalancesReply, XmrBalanceInfo, GetOffersRequest, GetOffersReply, OfferInfo, GetPaymentAccountsRequest, GetPaymentAccountsReply, CreateCryptoCurrencyPaymentAccountRequest, CreateCryptoCurrencyPaymentAccountReply, CreateOfferRequest, CreateOfferReply, CancelOfferRequest, TakeOfferRequest, TakeOfferReply, TradeInfo, GetTradeRequest, GetTradeReply, GetTradesRequest, GetTradesReply, GetNewDepositSubaddressRequest, GetNewDepositSubaddressReply, ConfirmPaymentStartedRequest, ConfirmPaymentReceivedRequest, XmrTx, GetXmrTxsRequest, GetXmrTxsReply, XmrDestination, CreateXmrTxRequest, CreateXmrTxReply, RelayXmrTxRequest, RelayXmrTxReply, CreateAccountRequest, AccountExistsRequest, AccountExistsReply, DeleteAccountRequest, OpenAccountRequest, IsAccountOpenRequest, IsAccountOpenReply, CloseAccountRequest, ChangePasswordRequest, BackupAccountRequest, BackupAccountReply, RestoreAccountRequest, StopRequest, NotificationMessage, RegisterNotificationListenerRequest, SendNotificationRequest, UrlConnection, AddConnectionRequest, RemoveConnectionRequest, GetConnectionRequest, GetConnectionsRequest, SetConnectionRequest, CheckConnectionRequest, CheckConnectionsReply, CheckConnectionsRequest, StartCheckingConnectionsRequest, StopCheckingConnectionsRequest, GetBestAvailableConnectionRequest, SetAutoSwitchRequest, CheckConnectionReply, GetConnectionsReply, GetConnectionReply, GetBestAvailableConnectionReply} from './protobuf/grpc_pb';
import {PaymentAccount, AvailabilityResult} from './protobuf/pb_pb'; import {PaymentAccount, AvailabilityResult} from './protobuf/pb_pb';
const console = require('console'); const console = require('console');
@ -716,7 +716,7 @@ class HavenoDaemon {
} }
/** /**
* Get the current market prices of all the currencies. * Get the current market prices of all currencies.
* *
* @return {MarketPrice[]} price per 1 XMR in all supported currencies (fiat & crypto) * @return {MarketPrice[]} price per 1 XMR in all supported currencies (fiat & crypto)
*/ */
@ -730,6 +730,21 @@ class HavenoDaemon {
}); });
} }
/**
* Get the market depth of a currency.
*
* @return {MarketDepthInfo} market depth of the given currency
*/
async getMarketDepth(currencyCode: string): Promise<MarketDepthInfo> {
let that = this;
return new Promise(function(resolve, reject) {
that._priceClient.getMarketDepth(new MarketDepthRequest().setCurrencyCode(currencyCode), {password: that._password}, function(err: grpcWeb.RpcError, response: MarketDepthReply) {
if (err) reject(err);
else resolve(response.getMarketDepth());
});
});
}
/** /**
* Get payment accounts. * Get payment accounts.
* *
@ -773,10 +788,11 @@ class HavenoDaemon {
/** /**
* Get available offers to buy or sell XMR. * Get available offers to buy or sell XMR.
* *
* @param {string} direction - one of "BUY" or "SELL" // TODO (woodser): make optional * @param {string|undefined} direction - "buy" or "sell" (default all)
* @return {OfferInfo[]} available offers * @return {OfferInfo[]} the available offers
*/ */
async getOffers(direction: string): Promise<OfferInfo[]> { async getOffers(direction?: string): Promise<OfferInfo[]> {
if (!direction) return (await this.getOffers("buy")).concat(await this.getOffers("sell")); // TODO: implement in backend
let that = this; let that = this;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
that._offersClient.getOffers(new GetOffersRequest().setDirection(direction).setCurrencyCode("XMR"), {password: that._password}, function(err: grpcWeb.RpcError, response: GetOffersReply) { that._offersClient.getOffers(new GetOffersRequest().setDirection(direction).setCurrencyCode("XMR"), {password: that._password}, function(err: grpcWeb.RpcError, response: GetOffersReply) {
@ -787,12 +803,13 @@ class HavenoDaemon {
} }
/** /**
* Get user's created offers to buy or sell XMR. * Get the user's posted offers to buy or sell XMR.
* *
* @param {string} direction - one of "BUY" or "SELL" // TODO (woodser): make optional * @param {string|undefined} direction - "buy" or "sell" (default all)
* @return {OfferInfo[]} the user's created offers * @return {OfferInfo[]} the user's created offers
*/ */
async getMyOffers(direction: string): Promise<OfferInfo[]> { async getMyOffers(direction?: string): Promise<OfferInfo[]> {
if (!direction) return (await this.getMyOffers("buy")).concat(await this.getMyOffers("sell")); // TODO: implement in backend
let that = this; let that = this;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
that._offersClient.getMyOffers(new GetOffersRequest().setDirection(direction).setCurrencyCode("XMR"), {password: that._password}, function(err: grpcWeb.RpcError, response: GetOffersReply) { that._offersClient.getMyOffers(new GetOffersRequest().setDirection(direction).setCurrencyCode("XMR"), {password: that._password}, function(err: grpcWeb.RpcError, response: GetOffersReply) {
@ -823,7 +840,7 @@ class HavenoDaemon {
* @param {string} currencyCode - currency code of traded pair * @param {string} currencyCode - currency code of traded pair
* @param {string} direction - one of "BUY" or "SELL" * @param {string} direction - one of "BUY" or "SELL"
* @param {number} price - trade price * @param {number} price - trade price
* @param {bool} useMarketBasedPrice - base trade on market price * @param {bool} useMarketBasedPrice - base trade on market price // TODO: this field redundant with price
* @param {number} marketPriceMargin - % from market price to tolerate * @param {number} marketPriceMargin - % from market price to tolerate
* @param {bigint} amount - amount to trade * @param {bigint} amount - amount to trade
* @param {bigint} minAmount - minimum amount to trade * @param {bigint} minAmount - minimum amount to trade
@ -846,8 +863,8 @@ class HavenoDaemon {
let request = new CreateOfferRequest() let request = new CreateOfferRequest()
.setCurrencyCode(currencyCode) .setCurrencyCode(currencyCode)
.setDirection(direction) .setDirection(direction)
.setPrice(price.toString())
.setUseMarketBasedPrice(useMarketBasedPrice) .setUseMarketBasedPrice(useMarketBasedPrice)
.setPrice(useMarketBasedPrice ? "1.0" : price.toString()) // TODO: positive price required even if using market price
.setMarketPriceMargin(marketPriceMargin) .setMarketPriceMargin(marketPriceMargin)
.setAmount(amount.toString()) .setAmount(amount.toString())
.setMinAmount(minAmount.toString()) .setMinAmount(minAmount.toString())