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<string> {
-    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<BalancesModel> {
-    let walletsClient = new WalletsClient(this._url);
+  async getBalances(): Promise<HavenoBalances> {
+    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<HavenoOffer[]> {
+    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