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

@ -3,4 +3,4 @@
# generate imports for haveno services and types using grpc-web
mkdir -p ./src/protobuf
cd ./src/protobuf || exit 1
protoc -I=../../../haveno/proto/src/main/proto/ ../../../haveno/proto/src/main/proto/*.proto --js_out=import_style=commonjs,binary:./ --grpc-web_out=import_style=typescript,mode=grpcwebtext:./ || exit 1
protoc -I=../../../haveno/proto/src/main/proto/ ../../../haveno/proto/src/main/proto/*.proto --js_out=import_style=commonjs,binary:./ --grpc-web_out=import_style=typescript,mode=grpcwebtext:./ || exit 1

View File

@ -1,5 +1,5 @@
// --------------------------------- IMPORTS ----------------------------------
// import haveno types
import {HavenoDaemon} from "./HavenoDaemon";
import {HavenoUtils} from "./utils/HavenoUtils";
@ -104,7 +104,19 @@ const TestConfig = {
["8086", ["10005", "7781"]],
]),
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 {
@ -562,6 +574,76 @@ test("Can get market prices", async () => {
.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 () => {
await arbitrator.registerDisputeAgent("mediator", TestConfig.devPrivilegePrivKey); // TODO: bisq mediator = haveno arbitrator
await arbitrator.registerDisputeAgent("refundagent", TestConfig.devPrivilegePrivKey); // TODO: no refund agent in haveno
@ -607,7 +689,7 @@ test("Can get payment accounts", async () => {
});
test("Can create crypto payment accounts", async () => {
// test each stagenet crypto account
for (let testAccount of TestConfig.cryptoAccounts) {
@ -648,7 +730,7 @@ test("Can create crypto payment accounts", async () => {
});
test("Can post and remove an offer", async () => {
// wait for alice to have unlocked balance to post offer
let tradeAmount: bigint = BigInt("250000000000");
await waitForUnlockedBalance(tradeAmount * BigInt("2"), alice);
@ -657,7 +739,7 @@ test("Can post and remove an offer", async () => {
let unlockedBalanceBefore: bigint = BigInt((await alice.getBalances()).getUnlockedBalance());
// post offer
let offer: OfferInfo = await postOffer(alice, "buy", BigInt("200000000000"), undefined);
let offer: OfferInfo = await postOffer(alice);
assert.equal(offer.getState(), "AVAILABLE");
// has offer
@ -688,7 +770,7 @@ test("Invalidates offers when reserved funds are spent", async () => {
// post offer
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
let reservedKeyImages = [];
@ -749,7 +831,7 @@ test("Handles unexpected errors during trade initialization", async () => {
// trader 0 posts 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());
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
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")
} catch (err) {
let errTyped = err as grpcWeb.RpcError;
@ -856,7 +938,7 @@ test("Cannot make or take offer with insufficient unlocked funds", async () => {
else {
let tradeAmount: bigint = BigInt("250000000000");
await waitForUnlockedBalance(tradeAmount * BigInt("2"), alice);
offer = await postOffer(alice, "buy", tradeAmount, undefined);
offer = await postOffer(alice, {amount: tradeAmount});
assert.equal(offer.getState(), "AVAILABLE");
await wait(TestConfig.walletSyncPeriodMs * 2);
}
@ -907,7 +989,7 @@ test("Can complete a trade", async () => {
// alice posts offer to buy xmr
console.log("Alice posting offer");
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");
console.log("Alice done posting offer");
@ -1073,7 +1155,7 @@ async function initHavenoDaemon(config?: any): Promise<HavenoDaemon> {
return havenod;
async function getAvailablePort(): Promise<number> {
return new Promise(function(resolve, reject) {
return new Promise(function(resolve) {
let srv = net.createServer();
srv.listen(0, function() {
let port = srv.address().port;
@ -1204,7 +1286,7 @@ async function waitForUnlockedBalance(amount: bigint, ...wallets: any[]) {
await startMining();
let promises: Promise<void>[] = [];
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() {
if (await wallet.getUnlockedBalance() >= amount) {
taskLooper.stop();
@ -1224,7 +1306,7 @@ async function waitForUnlockedTxs(...txHashes: string[]) {
let promises: Promise<void>[] = []
for (let txHash of txHashes) {
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);
if (tx.isConfirmed() && tx.getBlock().getHeight() <= await monerod.getHeight() - 10) {
taskLooper.stop();
@ -1286,24 +1368,24 @@ function testTx(tx: XmrTx, ctx: TxContext) {
assert(tx.getOutgoingTransfer() || tx.getIncomingTransfersList().length); // TODO (woodser): test transfers
for (let incomingTransfer of tx.getIncomingTransfersList()) testTransfer(incomingTransfer, 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.getIsConfirmed(), false);
assert.equal(tx.getIsLocked(), true);
assert(tx.getMetadata() && tx.getMetadata().length > 0);
}
function testTransfer(transfer: XmrIncomingTransfer|XmrOutgoingTransfer, ctx: TxContext) {
function testTransfer(transfer: XmrIncomingTransfer | XmrOutgoingTransfer, ctx: TxContext) {
expect(BigInt(transfer.getAmount())).toBeGreaterThanOrEqual(BigInt("0"));
assert(transfer.getAccountIndex() >= 0);
if (transfer instanceof XmrIncomingTransfer) testIncomingTransfer(transfer, ctx);
if (transfer instanceof XmrIncomingTransfer) testIncomingTransfer(transfer);
else testOutgoingTransfer(transfer, ctx);
}
function testIncomingTransfer(transfer: XmrIncomingTransfer, ctx: TxContext) {
function testIncomingTransfer(transfer: XmrIncomingTransfer) {
assert(transfer.getAddress());
assert(transfer.getSubaddressIndex() >= 0);
assert(transfer.getNumSuggestedConfirmations() > 0);
@ -1330,34 +1412,45 @@ function testDestination(destination: XmrDestination) {
expect(BigInt(destination.getAmount())).toBeGreaterThan(BigInt("0"));
}
async function createCryptoPaymentAccount(trader: HavenoDaemon): Promise<PaymentAccount> {
let testAccount = TestConfig.cryptoAccounts[0];
let paymentAccount: PaymentAccount = await trader.createCryptoPaymentAccount(
testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + GenUtils.getUUID(),
testAccount.currencyCode,
testAccount.address);
return paymentAccount;
async function createCryptoPaymentAccount(trader: HavenoDaemon, currencyCode = "eth"): Promise<PaymentAccount> {
for (let cryptoAccount of TestConfig.cryptoAccounts) {
if (cryptoAccount.currencyCode.toLowerCase() !== currencyCode.toLowerCase()) continue;
return trader.createCryptoPaymentAccount(
cryptoAccount.currencyCode + " " + cryptoAccount.address.substr(0, 8) + "... " + GenUtils.getUUID(),
cryptoAccount.currencyCode,
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
if (!paymentAccountId) paymentAccountId = (await createCryptoPaymentAccount(maker)).getId();
if (!config.paymentAccountId) config.paymentAccountId = (await createCryptoPaymentAccount(maker, config.counterCurrency)).getId();
// get unlocked balance before reserving offer
let unlockedBalanceBefore: bigint = BigInt((await maker.getBalances()).getUnlockedBalance());
// post offer
let offer: OfferInfo = await maker.postOffer("eth",
direction, // buy or sell xmr for eth
12.378981, // price TODO: price is optional? price string gets converted to long?
true, // use market price
0.02, // market price margin, e.g. within 2%
amount, // amount
BigInt("150000000000"), // min amount
0.15, // buyer security deposit, e.g. 15%
paymentAccountId, // payment account id
undefined); // trigger price // TODO: fails if there is a decimal, gets converted to long?
// TODO: re-arrange post offer parameters like this postOffer() or use config interface?
let offer: OfferInfo = await maker.postOffer(
config.counterCurrency,
config.direction,
config.price,
config.price ? false : true, // TODO: redundant with price field?
config.priceMargin,
config.amount,
config.minAmount,
config.buyerSecurityDeposit,
config.paymentAccountId,
config.triggerPrice);
testOffer(offer);
// 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");
// 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);
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!");
}
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;
}

View File

@ -2,7 +2,7 @@ import {HavenoUtils} from "./utils/HavenoUtils";
import {TaskLooper} from "./utils/TaskLooper";
import * as grpcWeb from 'grpc-web';
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';
const console = require('console');
@ -205,11 +205,11 @@ class HavenoDaemon {
getAppName(): string|undefined {
return this._appName;
}
/**
* Get the Haveno version.
*
* @return {string} the Haveno daemon version
* @return {string} the Haveno daemon version
*/
async getVersion(): Promise<string> {
let that = this;
@ -575,7 +575,7 @@ class HavenoDaemon {
});
});
}
/**
* Automatically switch to the best available connection if current connection is disconnected after being checked.
*
@ -668,7 +668,7 @@ class HavenoDaemon {
}
throw new Error("No transaction with hash " + txHash);
}
/**
* Create but do not relay a transaction to send funds from the Monero wallet.
*
@ -683,7 +683,7 @@ class HavenoDaemon {
});
});
}
/**
* Relay a previously created transaction to send funds from the Monero wallet.
*
@ -698,7 +698,7 @@ class HavenoDaemon {
});
});
}
/**
* Get the current market price per 1 XMR in the given currency.
*
@ -714,9 +714,9 @@ 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)
*/
@ -729,7 +729,22 @@ 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.
*
@ -773,10 +788,11 @@ class HavenoDaemon {
/**
* Get available offers to buy or sell XMR.
*
* @param {string} direction - one of "BUY" or "SELL" // TODO (woodser): make optional
* @return {OfferInfo[]} available offers
* @param {string|undefined} direction - "buy" or "sell" (default all)
* @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;
return new Promise(function(resolve, reject) {
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
*/
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;
return new Promise(function(resolve, reject) {
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} direction - one of "BUY" or "SELL"
* @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 {bigint} amount - amount to trade
* @param {bigint} minAmount - minimum amount to trade
@ -846,8 +863,8 @@ class HavenoDaemon {
let request = new CreateOfferRequest()
.setCurrencyCode(currencyCode)
.setDirection(direction)
.setPrice(price.toString())
.setUseMarketBasedPrice(useMarketBasedPrice)
.setPrice(useMarketBasedPrice ? "1.0" : price.toString()) // TODO: positive price required even if using market price
.setMarketPriceMargin(marketPriceMargin)
.setAmount(amount.toString())
.setMinAmount(minAmount.toString())
@ -973,7 +990,7 @@ class HavenoDaemon {
});
if (this._process) return HavenoUtils.kill(this._process);
}
// ------------------------------- HELPERS ----------------------------------
/**