diff --git a/common/Root.tsx b/common/Root.tsx index 86f694166fc..6b4dae8bbaf 100644 --- a/common/Root.tsx +++ b/common/Root.tsx @@ -152,9 +152,14 @@ class RootClass extends Component { const LegacyRoutes = withRouter(props => { const { history } = props; - const { pathname } = props.location; + const { pathname, search } = props.location; let { hash } = props.location; + if (search.includes('redirectToSignMessage')) { + history.push('/sign-and-verify-message'); + return null; + } + if (pathname === '/') { hash = hash.split('?')[0]; switch (hash) { diff --git a/common/api/shapeshift.ts b/common/api/shapeshift.ts index 1f1d040cd09..7555f7b820e 100644 --- a/common/api/shapeshift.ts +++ b/common/api/shapeshift.ts @@ -1,13 +1,11 @@ +import axios from 'axios'; import flatten from 'lodash/flatten'; import uniqBy from 'lodash/uniqBy'; +import queryString from 'query-string'; import { checkHttpStatus, parseJSON } from 'api/utils'; -export const SHAPESHIFT_API_KEY = - '8abde0f70ca69d5851702d57b10305705d7333e93263124cc2a2649dab7ff9cf86401fc8de7677e8edcd0e7f1eed5270b1b49be8806937ef95d64839e319e6d9'; - export const SHAPESHIFT_BASE_URL = 'https://shapeshift.io'; - export const SHAPESHIFT_TOKEN_WHITELIST = [ 'OMG', 'REP', @@ -30,6 +28,10 @@ export const SHAPESHIFT_TOKEN_WHITELIST = [ 'GUP' ]; export const SHAPESHIFT_WHITELIST = [...SHAPESHIFT_TOKEN_WHITELIST, 'ETH', 'ETC', 'BTC', 'XMR']; +export const SHAPESHIFT_ACCESS_TOKEN = 'c640aa85-dd01-4db1-a6f2-ed57e6fd6c54'; +export const SHAPESHIFT_API_URL = 'https://auth.shapeshift.io/oauth/authorize'; +export const SHAPESHIFT_CLIENT_ID = 'fcbbb372-4221-4436-b345-024d91384652'; +export const SHAPESHIFT_REDIRECT_URI = 'https://mycrypto.com/swap'; interface IPairData { limit: number; @@ -95,12 +97,21 @@ interface ShapeshiftOptionMap { 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; + private token: string | null = null; + + public constructor() { + this.retrieveAccessTokenFromStorage(); + } + + public hasToken() { + return !!window.localStorage.getItem(SHAPESHIFT_ACCESS_TOKEN); + } + + public urlHasCodeParam() { + return !!queryString.parse(window.location.search).code; + } public checkStatus(address: string) { return fetch(`${this.url}/txStat/${address}`) @@ -121,10 +132,9 @@ class ShapeshiftService { body: JSON.stringify({ amount: destinationAmount, pair, - apiKey: this.apiKey, withdrawal }), - headers: new Headers(this.postHeaders) + headers: new Headers(this.getPostHeaders()) }) .then(checkHttpStatus) .then(parseJSON) @@ -154,6 +164,29 @@ class ShapeshiftService { return allRates; }; + public sendUserToAuthorize = () => { + const query = queryString.stringify({ + client_id: SHAPESHIFT_CLIENT_ID, + scope: 'users:read', + response_type: 'code', + redirect_uri: SHAPESHIFT_REDIRECT_URI + }); + const url = `${SHAPESHIFT_API_URL}?${query}`; + + window.open(url, '_blank', 'width=800, height=600, menubar=yes'); + }; + + public requestAccessToken = async () => { + const { code } = queryString.parse(window.location.search); + const { data: { access_token: token } } = await axios.post( + 'https://proxy.mycryptoapi.com/request-shapeshift-token', + { code, grant_type: 'authorization_code' } + ); + + this.token = token; + this.saveAccessTokenToStorage(token); + }; + public addUnavailableCoinsAndTokens = (availableCoinsAndTokens: TokenMap) => { if (this.fetchedSupportedCoinsAndTokens) { /** @desc Create a hash for efficiently checking which tokens are currently available. */ @@ -211,6 +244,11 @@ class ShapeshiftService { return availableCoinsAndTokens; }; + private getPostHeaders = () => ({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.token}` + }); + private filterPairs(marketInfo: ShapeshiftMarketInfo[]) { return marketInfo.filter(obj => { const { pair } = obj; @@ -300,6 +338,19 @@ class ShapeshiftService { }); return tokenMap; } + + private saveAccessTokenToStorage = (token: string) => { + if (window && window.localStorage) { + window.localStorage.setItem(SHAPESHIFT_ACCESS_TOKEN, token); + } + }; + + private retrieveAccessTokenFromStorage = () => { + if (window && window.localStorage) { + const token = window.localStorage.getItem(SHAPESHIFT_ACCESS_TOKEN); + this.token = token; + } + }; } const shapeshift = new ShapeshiftService(); diff --git a/common/assets/images/wallets/trust.svg b/common/assets/images/wallets/trust.svg new file mode 100644 index 00000000000..dd4eb931752 --- /dev/null +++ b/common/assets/images/wallets/trust.svg @@ -0,0 +1,16 @@ + + + + trust + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/common/components/AddressField.tsx b/common/components/AddressField.tsx index c9464dc5041..fde31d51ec2 100644 --- a/common/components/AddressField.tsx +++ b/common/components/AddressField.tsx @@ -59,11 +59,10 @@ const AddressField: React.SFC = ({ className={`input-group-input ${!isValid && !isLabelEntry ? 'invalid' : ''}`} isValid={isValid} type="text" - value={ - value != null - ? value - : isCheckSummed ? toChecksumAddress(currentTo.raw) : currentTo.raw - } + value={(value != null + ? value + : isCheckSummed ? toChecksumAddress(currentTo.raw) : currentTo.raw + ).trim()} placeholder={placeholder} readOnly={!!(isReadOnly || readOnly)} spellCheck={false} diff --git a/common/components/BalanceSidebar/TokenBalances/index.tsx b/common/components/BalanceSidebar/TokenBalances/index.tsx index 0dd30027635..798dbba0124 100644 --- a/common/components/BalanceSidebar/TokenBalances/index.tsx +++ b/common/components/BalanceSidebar/TokenBalances/index.tsx @@ -23,7 +23,7 @@ interface StateProps { isOffline: AppState['config']['meta']['offline']; } interface ActionProps { - addCustomToken: customTokensActions.TAddCustomToken; + attemptAddCustomToken: customTokensActions.TAttemptAddCustomToken; removeCustomToken: customTokensActions.TRemoveCustomToken; scanWalletForTokens: walletActions.TScanWalletForTokens; setWalletTokens: walletActions.TSetWalletTokens; @@ -85,7 +85,7 @@ class TokenBalances extends React.Component { hasSavedWalletTokens={hasSavedWalletTokens} scanWalletForTokens={this.scanWalletForTokens} setWalletTokens={this.props.setWalletTokens} - onAddCustomToken={this.props.addCustomToken} + onAddCustomToken={this.props.attemptAddCustomToken} onRemoveCustomToken={this.props.removeCustomToken} /> ); @@ -121,7 +121,7 @@ function mapStateToProps(state: AppState): StateProps { } export default connect(mapStateToProps, { - addCustomToken: customTokensActions.addCustomToken, + attemptAddCustomToken: customTokensActions.attemptAddCustomToken, removeCustomToken: customTokensActions.removeCustomToken, scanWalletForTokens: walletActions.scanWalletForTokens, setWalletTokens: walletActions.setWalletTokens, diff --git a/common/components/Footer/NewFooter/components/DonateAndSubscribe.scss b/common/components/Footer/NewFooter/components/DonateAndSubscribe.scss index 178992296fd..da8f520ae1f 100644 --- a/common/components/Footer/NewFooter/components/DonateAndSubscribe.scss +++ b/common/components/Footer/NewFooter/components/DonateAndSubscribe.scss @@ -149,6 +149,9 @@ $input-border-radius: 3px; background: #142c46; border: 1px solid #4d5f74; border-radius: $input-border-radius; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right: none; color: #fff; } } @@ -158,6 +161,8 @@ $input-border-radius: 3px; background: #027796; border: none; border-radius: $input-border-radius; + border-top-left-radius: 0; + border-bottom-left-radius: 0; color: #fff; } } diff --git a/common/components/Header/NewHeader/components/MobileHeader.tsx b/common/components/Header/NewHeader/components/MobileHeader.tsx index 10e6d97ff37..56059d2b8eb 100644 --- a/common/components/Header/NewHeader/components/MobileHeader.tsx +++ b/common/components/Header/NewHeader/components/MobileHeader.tsx @@ -120,8 +120,10 @@ class MobileHeader extends Component { )} -
  • - {translateRaw('NEW_HEADER_TEXT_6')} +
  • + + {translateRaw('NEW_HEADER_TEXT_6')} +