Skip to content

Commit

Permalink
Merge branch 'develop' into parity-signer-erc681
Browse files Browse the repository at this point in the history
  • Loading branch information
dternyak authored Jul 6, 2018
2 parents 2129286 + fbe792a commit b8fe3bd
Show file tree
Hide file tree
Showing 38 changed files with 539 additions and 331 deletions.
125 changes: 125 additions & 0 deletions common/api/shapeshift.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import shapeshift, { SHAPESHIFT_BASE_URL } from './shapeshift';

describe('ShapeShift service', () => {
beforeEach(() => {
(global as any).fetch = jest.fn().mockImplementation(
(url: string) =>
new Promise(resolve => {
const returnValues = {
[`${SHAPESHIFT_BASE_URL}/marketinfo`]: {
status: 200,
json: () => [
{
limit: 1,
maxLimit: 2,
min: 1,
minerFee: 2,
pair: 'BTC_ETH',
rate: '1.0'
},
{
limit: 1,
maxLimit: 2,
min: 1,
minerFee: 2,
pair: 'ETH_BTC',
rate: '1.0'
}
]
},
[`${SHAPESHIFT_BASE_URL}/getcoins`]: {
status: 200,
json: () => ({
BTC: {
name: 'Bitcoin',
symbol: 'BTC',
image: '',
imageSmall: '',
status: 'available',
minerFee: 1
},
ETH: {
name: 'Ethereum',
symbol: 'ETH',
image: '',
imageSmall: '',
status: 'available',
minerFee: 1
},
XMR: {
name: 'Monero',
symbol: 'XMR',
image: '',
imageSmall: '',
status: 'unavailable',
minerFee: 1
}
})
}
};

resolve(returnValues[url]);
})
);
});
it('provides a collection of all available and unavailable coins and tokens', async done => {
const rates = await shapeshift.getAllRates();

expect(rates).toEqual({
BTCETH: {
id: 'BTCETH',
rate: '1.0',
limit: 1,
min: 1,
options: [
{
id: 'BTC',
image: '',
name: 'Bitcoin',
status: 'available'
},
{
id: 'ETH',
image: '',
name: 'Ethereum',
status: 'available'
}
]
},
ETHBTC: {
id: 'ETHBTC',
rate: '1.0',
limit: 1,
min: 1,
options: [
{
id: 'ETH',
image: '',
name: 'Ethereum',
status: 'available'
},
{
id: 'BTC',
image: '',
name: 'Bitcoin',
status: 'available'
}
]
},
__XMR: {
id: '__XMR',
limit: 0,
min: 0,
options: [
{
id: 'XMR',
image: '',
name: 'Monero',
status: 'unavailable'
}
]
}
});
done();
});
});
109 changes: 100 additions & 9 deletions common/api/shapeshift.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import flatten from 'lodash/flatten';
import uniqBy from 'lodash/uniqBy';

import { checkHttpStatus, parseJSON } from 'api/utils';

const SHAPESHIFT_API_KEY =
export const SHAPESHIFT_API_KEY =
'8abde0f70ca69d5851702d57b10305705d7333e93263124cc2a2649dab7ff9cf86401fc8de7677e8edcd0e7f1eed5270b1b49be8806937ef95d64839e319e6d9';

const SHAPESHIFT_BASE_URL = 'https://shapeshift.io';
export const SHAPESHIFT_BASE_URL = 'https://shapeshift.io';

export const SHAPESHIFT_TOKEN_WHITELIST = [
'OMG',
Expand All @@ -26,7 +29,7 @@ export const SHAPESHIFT_TOKEN_WHITELIST = [
'TRST',
'GUP'
];
export const SHAPESHIFT_WHITELIST = [...SHAPESHIFT_TOKEN_WHITELIST, 'ETH', 'ETC', 'BTC'];
export const SHAPESHIFT_WHITELIST = [...SHAPESHIFT_TOKEN_WHITELIST, 'ETH', 'ETC', 'BTC', 'XMR'];

interface IPairData {
limit: number;
Expand Down Expand Up @@ -66,13 +69,38 @@ interface TokenMap {
};
}

interface ShapeshiftCoinInfo {
image: string;
imageSmall: string;
minerFee: number;
name: string;
status: string;
symbol: string;
}

interface ShapeshiftCoinInfoMap {
[id: string]: ShapeshiftCoinInfo;
}

interface ShapeshiftOption {
id?: string;
status?: string;
image?: string;
}

interface ShapeshiftOptionMap {
[symbol: string]: ShapeshiftOption;
}

class ShapeshiftService {
public whitelist = SHAPESHIFT_WHITELIST;
private url = SHAPESHIFT_BASE_URL;
private apiKey = SHAPESHIFT_API_KEY;
private postHeaders = {
'Content-Type': 'application/json'
};
private supportedCoinsAndTokens: ShapeshiftCoinInfoMap = {};
private fetchedSupportedCoinsAndTokens = false;

public checkStatus(address: string) {
return fetch(`${this.url}/txStat/${address}`)
Expand Down Expand Up @@ -118,19 +146,76 @@ class ShapeshiftService {

public getAllRates = async () => {
const marketInfo = await this.getMarketInfo();
const pairRates = await this.filterPairs(marketInfo);
const pairRates = this.filterPairs(marketInfo);
const checkAvl = await this.checkAvl(pairRates);
const mappedRates = this.mapMarketInfo(checkAvl);
return mappedRates;
const allRates = this.addUnavailableCoinsAndTokens(mappedRates);

return allRates;
};

public addUnavailableCoinsAndTokens = (availableCoinsAndTokens: TokenMap) => {
if (this.fetchedSupportedCoinsAndTokens) {
/** @desc Create a hash for efficiently checking which tokens are currently available. */
const allOptions = flatten(
Object.values(availableCoinsAndTokens).map(({ options }) => options)
);
const availableOptions: ShapeshiftOptionMap = uniqBy(allOptions, 'id').reduce(
(prev: ShapeshiftOptionMap, next) => {
prev[next.id] = next;
return prev;
},
{}
);

const unavailableCoinsAndTokens = this.whitelist
.map(token => {
/** @desc ShapeShift claims support for the token and it is available. */
const availableCoinOrToken = availableOptions[token];

if (availableCoinOrToken) {
return null;
}

/** @desc ShapeShift claims support for the token, but it is unavailable. */
const supportedCoinOrToken = this.supportedCoinsAndTokens[token];

if (supportedCoinOrToken) {
const { symbol: id, image, name, status } = supportedCoinOrToken;

return {
/** @desc Preface the false id with '__' to differentiate from actual pairs. */
id: `__${id}`,
limit: 0,
min: 0,
options: [{ id, image, name, status }]
};
}

/** @desc We claim support for the coin or token, but ShapeShift doesn't. */
return null;
})
.reduce((prev: ShapeshiftOptionMap, next) => {
if (next) {
prev[next.id] = next;

return prev;
}

return prev;
}, {});

return { ...availableCoinsAndTokens, ...unavailableCoinsAndTokens };
}

return availableCoinsAndTokens;
};

private filterPairs(marketInfo: ShapeshiftMarketInfo[]) {
return marketInfo.filter(obj => {
const { pair } = obj;
const pairArr = pair.split('_');
return this.whitelist.includes(pairArr[0]) && this.whitelist.includes(pairArr[1])
? true
: false;
return this.whitelist.includes(pairArr[0]) && this.whitelist.includes(pairArr[1]);
});
}

Expand Down Expand Up @@ -165,7 +250,13 @@ class ShapeshiftService {
private getAvlCoins() {
return fetch(`${this.url}/getcoins`)
.then(checkHttpStatus)
.then(parseJSON);
.then(parseJSON)
.then(supportedCoinsAndTokens => {
this.supportedCoinsAndTokens = supportedCoinsAndTokens;
this.fetchedSupportedCoinsAndTokens = true;

return supportedCoinsAndTokens;
});
}

private getMarketInfo() {
Expand Down
53 changes: 0 additions & 53 deletions common/components/AppAlphaNotice/AlphaNotice.scss

This file was deleted.

Loading

0 comments on commit b8fe3bd

Please sign in to comment.