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

Integrated coinbase API for dollar exchange rate for each currency price and added bars for price #57

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion web/.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@ REACT_APP_NODE_URL=http://localhost:8545
REACT_APP_HYDRO_PROXY_ADDRESS=0x04f67E8b7C39A25e100847Cb167460D715215FEb
REACT_APP_HYDRO_TOKEN_ADDRESS=0x4C4Fa7E8EA4cFCfC93DEAE2c0Cff142a1DD3a218
REACT_APP_WETH_TOKEN_ADDRESS=0x4a817489643A89a1428b2DD441c3fbe4DBf44789
REACT_APP_NETWORK_ID=66
REACT_APP_NETWORK_ID=66
REACT_APP_DAI_PRICE_IN_DOLLAR=1.00437
REACT_APP_HOT_PRICE_IN_DOLLAR=0.00067
REACT_APP_WETH_PRICE_IN_DOLLAR=168.21835
REACT_APP_COIN_VASE_API_ADDRESS=https://rest.coinapi.io/v1
REACT_APP_COIN_BASE_API_KEY=87822C5B-06F8-4231-B362-B4CA756B6CCC
3 changes: 2 additions & 1 deletion web/src/App.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
import { loadMarkets, loadTradeHistory } from './actions/markets';
import { loadMarkets, loadExchange, loadTradeHistory } from './actions/markets';
import Header from './components/Header';
import WebsocketConnector from './components/WebsocketConnector';
import OrderBook from './components/Orderbook';
Expand Down Expand Up @@ -38,6 +38,7 @@ class App extends React.PureComponent {
componentDidMount() {
const { dispatch, currentMarket } = this.props;
dispatch(loadMarkets());
dispatch(loadExchange());
if (parseInt(env.NETWORK_ID) === 66) {
this.initTestBrowserWallet();
}
Expand Down
18 changes: 18 additions & 0 deletions web/src/actions/markets.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,24 @@ export const loadMarkets = () => {
};
};

export const loadExchange = () => {
return async (dispatch, getState) => {
try {
const res = await Promise.all([api.coinBaseGet('/exchangerate/DAI/USD'), api.coinBaseGet('/exchangerate/HOT/USD'), api.coinBaseGet('/exchangerate/ETH/USD')]);
if (res) {
const dollarExchange = {DAI: res[0]['data']['rate'], HOT: res[1]['data']['rate'], WETH: res[2]['data']['rate']}
return dispatch({
type: 'LOAD_DOLLAR_EXCHANGE_RATE',
payload: dollarExchange
});
}
} catch (error) {
console.log(error);
return;
}
}
}

// load current market trade history
export const loadTradeHistory = marketID => {
return async (dispatch, getState) => {
Expand Down
80 changes: 55 additions & 25 deletions web/src/components/Orderbook/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,28 +37,52 @@ class OrderBook extends React.Component {
this.lastUpdatedAt = new Date();
}

maxAmount(asks) {
let max = 0;
asks.forEach(element => {
const amount = element[1].toFixed(this.props.currentMarket.amountDecimals);
if (max < amount) {
max = amount;
}
});
return max;
}

calculateBarWidth(maxAmount, Amount) {
const width = ~~((Amount / maxAmount) * 20); //20 is the max percentage
return width + '%';
}

render() {
let { bids, asks, websocketConnected, currentMarket } = this.props;
const { bids, asks, websocketConnected, currentMarket, dollarExchangeRate } = this.props;
const asksArray = asks.slice(-20).reverse().toArray();
const bidsArray = bids.slice(-20).reverse().toArray();
const asksMaxAmount = this.maxAmount(asksArray);
const bidsMaxAmount = this.maxAmount(bidsArray);

return (
<div className="orderbook flex-column flex-1">
<div className="orderbook">
<div className="flex header text-secondary">
<div className="col-6 text-right">Amount</div>
<div className="col-6 text-right">Price</div>
<div className="col-4 text-center border-right font-weight-bold">Amount</div>
<div className="col-4 text-center border-right font-weight-bold">Price</div>
<div className="col-4 text-center font-weight-bold">Amount</div>
</div>
<div className="flex-column flex-1">
<div className="asks flex-column flex-column-reverse flex-1 overflow-hidden">
{asks
.slice(-20)
.reverse()
.toArray()
.map(([price, amount]) => {
<div className="asks flex-column flex-column-reverse">
{asksArray
.map(([price, amount], index) => {
const dollarValue = price * dollarExchangeRate;
return (
<div className="ask flex align-items-center" key={price.toString()}>
<div className="col-6 orderbook-amount text-right">
{amount.toFixed(currentMarket.amountDecimals)}
<div className="ask flex align-items-center" style={{backgroundColor: index % 2 ? '#f7f7f7' : '#fff' }} key={price.toString()}>
<div className="col-4 orderbook-amount text-center">
<p className="amount">{amount.toFixed(currentMarket.amountDecimals)}</p>
<div className="amount-bar-ask" style={{width: this.calculateBarWidth(asksMaxAmount, amount.toFixed(currentMarket.amountDecimals))}}></div>
</div>
<div className="col-4 text-center font-weight-bold price-container-ask">
<div className="price text-danger">{price.toFixed(currentMarket.priceDecimals)}</div>
<div className="currency">{dollarValue.toFixed(2)} USD</div>
</div>
<div className="col-6 text-danger text-right">{price.toFixed(currentMarket.priceDecimals)}</div>
<div className="col-4 text-center">-</div>
</div>
);
})}
Expand All @@ -74,17 +98,21 @@ class OrderBook extends React.Component {
</div>
)}
</div>
<div className="bids flex-column flex-1 overflow-hidden">
{bids
.slice(0, 20)
.toArray()
.map(([price, amount]) => {
<div className="bids flex-column">
{bidsArray
.map(([price, amount], index) => {
const dollarValue = price * dollarExchangeRate;
return (
<div className="bid flex align-items-center" key={price.toString()}>
<div className="col-6 orderbook-amount text-right">
{amount.toFixed(currentMarket.amountDecimals)}
<div className="ask flex align-items-center" style={{backgroundColor: index % 2 ? '#f7f7f7' : '#fff' }} key={price.toString()}>
<div className="col-4 text-center">-</div>
<div className="col-4 text-center font-weight-bold bid-price-container">
<div className="price text-primary">{price.toFixed(currentMarket.priceDecimals)}</div>
<div className="currency">{dollarValue.toFixed(2)} USD</div>
</div>
<div className="col-4 orderbook-amount text-center">
<div className="amount-bar-bid" style={{width: this.calculateBarWidth(bidsMaxAmount, amount.toFixed(currentMarket.amountDecimals))}}></div>
<p className="amount">{amount.toFixed(currentMarket.amountDecimals)}</p>
</div>
<div className="col-6 text-success text-right">{price.toFixed(currentMarket.priceDecimals)}</div>
</div>
);
})}
Expand All @@ -96,13 +124,15 @@ class OrderBook extends React.Component {
}

const mapStateToProps = state => {
const currentMarket = state.market.getIn(['markets', 'currentMarket']);
return {
asks: state.market.getIn(['orderbook', 'asks']),
bids: state.market.getIn(['orderbook', 'bids']),
loading: false,
currentMarket: state.market.getIn(['markets', 'currentMarket']),
currentMarket,
websocketConnected: state.config.get('websocketConnected'),
theme: state.config.get('theme')
theme: state.config.get('theme'),
dollarExchangeRate: state.market.getIn(['exchangeRate', 'data', currentMarket['quoteToken']]),
};
};

Expand Down
39 changes: 38 additions & 1 deletion web/src/components/Orderbook/styles.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
@import '../../styles/variables.scss';
.orderbook {
height: 508px;
overflow-y: scroll;
.header,
.status {
height: 40px;
Expand All @@ -10,6 +13,40 @@
}
.ask,
.bid {
min-height: 24px;
min-height: 30px;
}
.amount {
display: inline;
position: relative;
z-index: 1;
}
.price-container-ask {
background-color: $grey-95;
}
.bid-price-container {
background-color: $pinkLight;
}
.price {
font-size: 15px;
}
.currency {
color: $black;
font-size: 9px;
}
.amount-bar-ask {
background-color: $light;
background-position: 50% center;
height: 22px;
position: absolute;
right: 1px;
top: -1px;
}
.amount-bar-bid {
background-color: $pinkLight;
background-position: 50% center;
height: 22px;
position: absolute;
left: 1px;
top: -1px;
}
}
14 changes: 13 additions & 1 deletion web/src/lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,25 @@ const _request = (method, url, ...args) => {
return getAxiosInstance()[method](`${env.API_ADDRESS}${url}`, ...args);
};

const _coinBaseRequest = (method, url, ...args) => {
const instance = axios.create({
baseURL: env.COIN_BASE_API_ADDRESS,
headers: {
'X-CoinAPI-Key': env.COIN_BASE_API_KEY
}
});

return instance[method](url, ...args);
}

const api = {
get: (url, ...args) => _request('get', url, ...args),
delete: (url, ...args) => _request('delete', url, ...args),
head: (url, ...args) => _request('head', url, ...args),
post: (url, ...args) => _request('post', url, ...args),
put: (url, ...args) => _request('put', url, ...args),
patch: (url, ...args) => _request('patch', url, ...args)
patch: (url, ...args) => _request('patch', url, ...args),
coinBaseGet: (url, ...args) => _coinBaseRequest('get', url, ...args)
};

export default api;
7 changes: 6 additions & 1 deletion web/src/lib/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@ export default {
HYDRO_PROXY_ADDRESS: _env.REACT_APP_HYDRO_PROXY_ADDRESS,
HYDRO_TOKEN_ADDRESS: _env.REACT_APP_HYDRO_TOKEN_ADDRESS,
WETH_TOKEN_ADDRESS: _env.REACT_APP_WETH_TOKEN_ADDRESS,
NETWORK_ID: _env.REACT_APP_NETWORK_ID
NETWORK_ID: _env.REACT_APP_NETWORK_ID,
DAI_PRICE_IN_DOLLAR: _env.REACT_APP_DAI_PRICE_IN_DOLLAR,
HOT_PRICE_IN_DOLLAR: _env.REACT_APP_HOT_PRICE_IN_DOLLAR,
WETH_PRICE_IN_DOLLAR: _env.REACT_APP_WETH_PRICE_IN_DOLLAR,
COIN_BASE_API_ADDRESS: _env.REACT_APP_COIN_VASE_API_ADDRESS,
COIN_BASE_API_KEY: _env.REACT_APP_COIN_BASE_API_KEY,
};
17 changes: 16 additions & 1 deletion web/src/reducers/market.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Map, List, OrderedMap } from 'immutable';
import env from '../lib/env';

const initialOrderbook = Map({
bids: List(),
Expand Down Expand Up @@ -34,7 +35,16 @@ const initialState = Map({
tokenPrices: Map({
loading: true,
data: {}
})
}),

exchangeRate: Map({
loading: true,
data: Map({
DAI: env.DAI_PRICE_IN_DOLLAR,
HOT: env.HOT_PRICE_IN_DOLLAR,
WETH: env.WETH_PRICE_IN_DOLLAR,
})
}),
});

const reverseBigNumberComparator = (a, b) => {
Expand Down Expand Up @@ -96,6 +106,11 @@ export default (state = initialState, action) => {

state = state.setIn(['orderbook', side], state.getIn(['orderbook', side]).sort(reverseBigNumberComparator));
return state;

case 'LOAD_DOLLAR_EXCHANGE_RATE':
state = state.updateIn(['exchangeRate', 'loading'], () => false);
state = state.updateIn(['exchangeRate', 'data'], () => Map(action.payload));
return state;
default:
return state;
}
Expand Down
3 changes: 3 additions & 0 deletions web/src/styles/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ $blueHover: #5c6fac;
$red: #ff6f75;
$green: #00d99f;
$grey: #9b9b9b;
$grey-95: #f2f2f2;
$borderGrey: #e0e0e0;
$black: #505d6f;
$white: #fff;
$light: #cce3f1;
$pinkLight: #ffe6ee;