From 6f149b1b95fc0a216429f4b4ef1efd25dda27a0d Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 11 Sep 2021 12:12:55 -0400 Subject: [PATCH] can get offers from haveno daemon --- src/App.tsx | 14 +++- src/HavenoDaemon.test.tsx | 21 +++-- src/HavenoDaemon.tsx | 157 ++++++++++++++++++++++++++++++++++---- 3 files changed, 168 insertions(+), 24 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index a224c1b9..71bfa681 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,14 +3,17 @@ import logo from './logo.png'; import './App.css'; import {HavenoDaemon} from './HavenoDaemon'; +const HAVENO_DAEMON_URL = "http://localhost:8080"; +const HAVENO_DAEMON_PASSWORD = "apitest"; + class App extends React.Component<{}, {daemonVersion: string}> { - + daemon: HavenoDaemon; constructor(props: any) { super(props); this.state = {daemonVersion: ""}; - this.daemon = new HavenoDaemon("http://localhost:8080", undefined, "apitest"); + this.daemon = new HavenoDaemon(HAVENO_DAEMON_URL, HAVENO_DAEMON_PASSWORD); } render() { @@ -38,7 +41,12 @@ class App extends React.Component<{}, {daemonVersion: string}> { } async componentDidMount() { - this.setState({daemonVersion: await this.daemon.getVersion()}); + try { + this.setState({daemonVersion: await this.daemon.getVersion()}); + } catch (err) { + console.error(err); + this.setState({daemonVersion: " not available"}); + } } } diff --git a/src/HavenoDaemon.test.tsx b/src/HavenoDaemon.test.tsx index 023a55a3..711c4650 100644 --- a/src/HavenoDaemon.test.tsx +++ b/src/HavenoDaemon.test.tsx @@ -1,11 +1,10 @@ -import {HavenoDaemon, BalancesModel} from "./HavenoDaemon"; +import {HavenoDaemon, HavenoBalances, HavenoOffer} from "./HavenoDaemon"; const HAVENO_UI_VERSION = "1.6.2"; const HAVENO_DAEMON_URL = "http://localhost:8080"; -const HAVENO_DAEMON_USERNAME = undefined; const HAVENO_DAEMON_PASSWORD = "apitest"; -const daemon = new HavenoDaemon(HAVENO_DAEMON_URL, HAVENO_DAEMON_USERNAME, HAVENO_DAEMON_PASSWORD); +const daemon = new HavenoDaemon(HAVENO_DAEMON_URL, HAVENO_DAEMON_PASSWORD); test("Can get the version", async () => { let version = await daemon.getVersion(); @@ -13,9 +12,21 @@ test("Can get the version", async () => { }); test("Can get the user's balances", async () => { - let balances: BalancesModel = await daemon.getBalances(); + let balances: HavenoBalances = await daemon.getBalances(); expect(balances.unlockedBalance); expect(balances.lockedBalance); expect(balances.reservedOfferBalance); expect(balances.reservedTradeBalance); -}); \ No newline at end of file +}); + +test("Can get offers", async() => { + let offers: HavenoOffer[] = await daemon.getOffers("SELL", "XMR"); + for (let offer of offers) { + testOffer(offer); + } +}); + +function testOffer(offer: HavenoOffer) { + expect(offer.id).toHaveLength; + // TODO: test rest of offer +} \ No newline at end of file diff --git a/src/HavenoDaemon.tsx b/src/HavenoDaemon.tsx index 3a7662da..3d9603d3 100644 --- a/src/HavenoDaemon.tsx +++ b/src/HavenoDaemon.tsx @@ -1,10 +1,10 @@ /** - * These files are generated by protoc-gen-grpc-web (created with `sudo make install-plugin` in grpc-web) using the following command: + * These imports are generated by protoc-gen-grpc-web (created with `sudo make install-plugin` in grpc-web) using the following command: * * `protoc -I=./ *.proto --js_out=import_style=commonjs:./ --grpc-web_out=import_style=commonjs,mode=grpcwebtext:./` */ -const {GetVersionRequest, WalletsClient} = require('./grpc_grpc_web_pb.js'); -const {GetVersionClient, GetBalancesRequest} = require('./grpc_pb.js'); +const {GetVersionRequest, WalletsClient, OffersClient} = require('./grpc_grpc_web_pb.js'); +const {GetVersionClient, GetBalancesRequest, GetOffersRequest} = require('./grpc_pb.js'); /** * Haveno daemon client using gRPC. @@ -13,19 +13,19 @@ class HavenoDaemon { // instance variables _url: string; - _username?: string; _password?: string; + _getVersionClient: any; + _walletsClient?: any; + _offersClient?: any; /** * Construct a client connected to a Haveno daemon. * * @param {string} url - Haveno daemon url - * @param {string} username - Haveno daemon username if applicable * @param {string} password - Haveno daemon password if applicable */ - constructor(url: string, username?: string, password?: string) { + constructor(url: string, password?: string) { this._url = url; - this._username = username; this._password = password; } @@ -35,11 +35,11 @@ class HavenoDaemon { * @return {string} the Haveno daemon version */ async getVersion(): Promise { - let getVersionClient = new GetVersionClient(this._url); // TODO: use one instance, username, password + if (!this._getVersionClient) this._getVersionClient = new GetVersionClient(this._url); let request = new GetVersionRequest(); let that = this; return new Promise(function(resolve, reject) { - getVersionClient.getVersion(request, {password: that._password}, function(err: any, response: any) { + that._getVersionClient.getVersion(request, {password: that._password}, function(err: any, response: any) { if (err) reject(err); else resolve(response.getVersion()); }); @@ -49,16 +49,16 @@ class HavenoDaemon { /** * Get the user's balances. * - * @return {BalancesModel} the user's balances + * @return {HavenoBalances} the user's balances */ - async getBalances(): Promise { - let walletsClient = new WalletsClient(this._url); + async getBalances(): Promise { + if (!this._walletsClient) this._walletsClient = new WalletsClient(this._url); let request = new GetBalancesRequest(); let that = this; return new Promise(function(resolve, reject) { - walletsClient.getBalances(request, {password: that._password}, function(err: any, response: any) { + that._walletsClient.getBalances(request, {password: that._password}, function(err: any, response: any) { if (err) reject(err); - else resolve(new BalancesModel( + else resolve(new HavenoBalances( BigInt(response.getBalances().getXmr().getUnlockedbalance()), BigInt(response.getBalances().getXmr().getLockedbalance()), BigInt(response.getBalances().getXmr().getReservedofferbalance()), @@ -66,9 +66,61 @@ class HavenoDaemon { }); }); } + + /** + * Get available offers. + * + * @param {string} direction - one of "BUY" or "SELL" + * @param {string} currencyCode - the currency being bought or sold, e.g. "ETH" + * + * @return {HavenoOffer[]} available offers + */ + async getOffers(direction: string, currencyCode: string): Promise { + if (!this._offersClient) this._offersClient = new OffersClient(this._url); + let request = new GetOffersRequest() + .setDirection(direction) + .setCurrencycode(currencyCode); + let that = this; + return new Promise(function(resolve, reject) { + that._offersClient.getOffers(request, {password: that._password}, function(err: any, response: any) { + if (err) reject(err); + else { + let offers: HavenoOffer[] = []; + for (let offer of response.getOffersList()) { + offers.push(new HavenoOffer( + offer.getId(), + offer.getDirection(), + offer.getPrice(), + offer.getUsemarketbasedprice(), + offer.getMarketpricemargin(), + offer.getAmount(), + offer.getMinamount(), + offer.getVolume(), + offer.getMinvolume(), + offer.getBuyersecuritydeposit(), + offer.getTriggerprice(), + offer.getIscurrencyformakerfeebtc(), + offer.getPaymentaccountid(), + offer.getPaymentmethodid(), + offer.getPaymentmethodshortname(), + offer.getBasecurrencycode(), + offer.getCountercurrencycode(), + offer.getDate(), + offer.getState(), + offer.getSellersecuritydeposit(), + offer.getOfferfeepaymenttxid(), + offer.getTxfee(), + offer.getMakerfee() + )); + } + resolve(offers); + } + }); + }); + } } -class BalancesModel { +class HavenoBalances { unlockedBalance: bigint; lockedBalance: bigint; reservedOfferBalance: bigint; @@ -84,4 +136,77 @@ class BalancesModel { } } -export {HavenoDaemon, BalancesModel}; \ No newline at end of file +class HavenoOffer { + id: string; + direction: string; + price: bigint; + useMarketBasedPrice: boolean; + marketPriceMargin: number; + amount: bigint; + minAmount: bigint; + volume: bigint; + minVolume: bigint; + buyerSecurityDeposit: bigint; + triggerPrice: bigint; + isCurrencyForMakerFeeBtc: boolean; + paymentAccountId: string; + paymentMethodId: string; + paymentMethodShortName: string; + baseCurrencyCode: string; + counterCurrencyCode: string; + date: bigint; + state: string; + sellerSecurityDeposit: bigint; + offerFeePaymentTxId: string; + txFee: bigint; + makerFee: bigint; + constructor(id: string, + direction: string, + price: bigint, + useMarketBasedPrice: boolean, + marketPriceMargin: number, + amount: bigint, + minAmount: bigint, + volume: bigint, + minVolume: bigint, + buyerSecurityDeposit: bigint, + triggerPrice: bigint, + isCurrencyForMakerFeeBtc: boolean, + paymentAccountId: string, + paymentMethodId: string, + paymentMethodShortName: string, + baseCurrencyCode: string, + counterCurrencyCode: string, + date: bigint, + state: string, + sellerSecurityDeposit: bigint, + offerFeePaymentTxId: string, + txFee: bigint, + makerFee: bigint,) { + this.id = id; + this.direction = direction; + this.price = price; + this.useMarketBasedPrice = useMarketBasedPrice; + this.marketPriceMargin = marketPriceMargin; + this.amount = amount; + this.minAmount = minAmount; + this.volume = volume; + this.minVolume = minVolume; + this.buyerSecurityDeposit = buyerSecurityDeposit; + this.triggerPrice = triggerPrice; + this.isCurrencyForMakerFeeBtc = isCurrencyForMakerFeeBtc; + this.paymentAccountId = paymentAccountId; + this.paymentMethodId = paymentMethodId; + this.paymentMethodShortName = paymentMethodShortName; + this.baseCurrencyCode = baseCurrencyCode; + this.counterCurrencyCode = counterCurrencyCode; + this.date = date; + this.state = state; + this.sellerSecurityDeposit = sellerSecurityDeposit; + this.offerFeePaymentTxId = offerFeePaymentTxId; + this.txFee = txFee; + this.makerFee = makerFee; + } +} + +export {HavenoDaemon, HavenoBalances, HavenoOffer}; \ No newline at end of file