mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
ShapeShift Integration
This commit is contained in:
parent
5aaa2d679b
commit
5cbbb476b3
@ -3,6 +3,7 @@ import classnames from 'classnames'
|
|||||||
import {connect} from 'react-redux'
|
import {connect} from 'react-redux'
|
||||||
import {qrcode} from 'qrcode-npm'
|
import {qrcode} from 'qrcode-npm'
|
||||||
import copyToClipboard from 'copy-to-clipboard'
|
import copyToClipboard from 'copy-to-clipboard'
|
||||||
|
import ShapeShiftForm from '../shapeshift-form'
|
||||||
import Identicon from '../../../../ui/app/components/identicon'
|
import Identicon from '../../../../ui/app/components/identicon'
|
||||||
import {buyEth, showAccountDetail} from '../../../../ui/app/actions'
|
import {buyEth, showAccountDetail} from '../../../../ui/app/actions'
|
||||||
|
|
||||||
@ -79,12 +80,6 @@ class BuyEtherScreen extends Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderShapeShiftLogo () {
|
|
||||||
return (
|
|
||||||
<div className='shapeshift-logo' />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderCoinbaseForm () {
|
renderCoinbaseForm () {
|
||||||
const {goToCoinbase, address} = this.props
|
const {goToCoinbase, address} = this.props
|
||||||
|
|
||||||
@ -119,81 +114,11 @@ class BuyEtherScreen extends Component {
|
|||||||
case OPTION_VALUES.SHAPESHIFT:
|
case OPTION_VALUES.SHAPESHIFT:
|
||||||
return (
|
return (
|
||||||
<div className='buy-ether__action-content-wrapper'>
|
<div className='buy-ether__action-content-wrapper'>
|
||||||
<div>{this.renderShapeShiftLogo()}</div>
|
<div className='shapeshift-logo' />
|
||||||
<div className='buy-ether__body-text'>
|
<div className='buy-ether__body-text'>
|
||||||
Trade any leading blockchain asset for any other. Protection by Design. No Account Needed.
|
Trade any leading blockchain asset for any other. Protection by Design. No Account Needed.
|
||||||
</div>
|
</div>
|
||||||
<div className='shapeshift-form'>
|
<ShapeShiftForm btnClass='first-time-flow__button' />
|
||||||
<div className='shapeshift-form__selectors'>
|
|
||||||
<div className='shapeshift-form__selector'>
|
|
||||||
<div className='shapeshift-form__selector-label'>
|
|
||||||
Deposit
|
|
||||||
</div>
|
|
||||||
<select className='shapeshift-form__selector-input'>
|
|
||||||
<option value='btc'>BTC</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='icon shapeshift-form__caret'
|
|
||||||
style={{ backgroundImage: 'url(images/caret-right.svg)'}}
|
|
||||||
/>
|
|
||||||
<div className='shapeshift-form__selector'>
|
|
||||||
<div className='shapeshift-form__selector-label'>
|
|
||||||
Receive
|
|
||||||
</div>
|
|
||||||
<select className='shapeshift-form__selector-input'>
|
|
||||||
<option value='btc'>BTC</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='shapeshift-form__address-input-wrapper'>
|
|
||||||
<div className='shapeshift-form__address-input-label'>
|
|
||||||
Your Refund Address
|
|
||||||
</div>
|
|
||||||
<input type='text' className='shapeshift-form__address-input' />
|
|
||||||
</div>
|
|
||||||
<div className='shapeshift-form__metadata'>
|
|
||||||
<div className='shapeshift-form__metadata-wrapper'>
|
|
||||||
<div className='shapeshift-form__metadata-label'>
|
|
||||||
Status:
|
|
||||||
</div>
|
|
||||||
<div className='shapeshift-form__metadata-value'>
|
|
||||||
Available
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='shapeshift-form__metadata-wrapper'>
|
|
||||||
<div className='shapeshift-form__metadata-label'>
|
|
||||||
Limit:
|
|
||||||
</div>
|
|
||||||
<div className='shapeshift-form__metadata-value'>
|
|
||||||
2.06856464
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='shapeshift-form__metadata-wrapper'>
|
|
||||||
<div className='shapeshift-form__metadata-label'>
|
|
||||||
Exchange Rate:
|
|
||||||
</div>
|
|
||||||
<div className='shapeshift-form__metadata-value'>
|
|
||||||
12.21840214
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='shapeshift-form__metadata-wrapper'>
|
|
||||||
<div className='shapeshift-form__metadata-label'>
|
|
||||||
Minimum:
|
|
||||||
</div>
|
|
||||||
<div className='shapeshift-form__metadata-value'>
|
|
||||||
0.000163
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='buy-ether__buttons'>
|
|
||||||
<button
|
|
||||||
className='first-time-flow__button'
|
|
||||||
>
|
|
||||||
Buy
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
case OPTION_VALUES.QR_CODE:
|
case OPTION_VALUES.QR_CODE:
|
||||||
|
@ -578,7 +578,8 @@ button.first-time-flow__button--tertiary:hover {
|
|||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shapeshift-form__selector-label {
|
.shapeshift-form__selector-label,
|
||||||
|
.shapeshift-form__deposit-instruction {
|
||||||
color: #757575;
|
color: #757575;
|
||||||
color: rgba(0, 0, 0, 0.45);
|
color: rgba(0, 0, 0, 0.45);
|
||||||
font-family: Montserrat Light;
|
font-family: Montserrat Light;
|
||||||
@ -597,10 +598,8 @@ button.first-time-flow__button--tertiary:hover {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
}
|
line-height: 44px;
|
||||||
|
font-family: Montserrat Light;
|
||||||
.shapeshift-form__address-input-wrapper {
|
|
||||||
padding-bottom: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shapeshift-form__address-input-label {
|
.shapeshift-form__address-input-label {
|
||||||
@ -622,6 +621,18 @@ button.first-time-flow__button--tertiary:hover {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shapeshift-form__address-input-wrapper--error .shapeshift-form__address-input {
|
||||||
|
border-color: #FF001F;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shapeshift-form__address-input-error-message {
|
||||||
|
color: #FF001F;
|
||||||
|
font-family: Montserrat Light;
|
||||||
|
font-size: 12px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.shapeshift-form__metadata {
|
.shapeshift-form__metadata {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
@ -640,11 +651,11 @@ button.first-time-flow__button--tertiary:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.shapeshift-form__metadata-wrapper:nth-child(odd) {
|
.shapeshift-form__metadata-wrapper:nth-child(odd) {
|
||||||
padding-right: 24px;
|
padding-right: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shapeshift-form__metadata-label {
|
.shapeshift-form__metadata-label {
|
||||||
flex: 1 0 65%;
|
flex: 1 0 60%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shapeshift-form__metadata-value {
|
.shapeshift-form__metadata-value {
|
||||||
@ -654,3 +665,9 @@ button.first-time-flow__button--tertiary:hover {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shapeshift-form__qr-code {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
217
mascara/src/app/shapeshift-form/index.js
Normal file
217
mascara/src/app/shapeshift-form/index.js
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
import React, {Component, PropTypes} from 'react'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
import {qrcode} from 'qrcode-npm'
|
||||||
|
import {connect} from 'react-redux'
|
||||||
|
import {shapeShiftSubview, pairUpdate, buyWithShapeShift} from '../../../../ui/app/actions'
|
||||||
|
import {isValidAddress} from '../../../../ui/app/util'
|
||||||
|
|
||||||
|
export class ShapeShiftForm extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
selectedAddress: PropTypes.string.isRequired,
|
||||||
|
btnClass: PropTypes.string.isRequired,
|
||||||
|
tokenExchangeRates: PropTypes.object.isRequired,
|
||||||
|
coinOptions: PropTypes.object.isRequired,
|
||||||
|
shapeShiftSubview: PropTypes.func.isRequired,
|
||||||
|
pairUpdate: PropTypes.func.isRequired,
|
||||||
|
buyWithShapeShift: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
depositCoin: 'btc',
|
||||||
|
refundAddress: '',
|
||||||
|
showQrCode: false,
|
||||||
|
depositAddress: '',
|
||||||
|
errorMessage: '',
|
||||||
|
isLoading: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentWillMount () {
|
||||||
|
this.props.shapeShiftSubview()
|
||||||
|
}
|
||||||
|
|
||||||
|
onCoinChange = e => {
|
||||||
|
const coin = e.target.value
|
||||||
|
this.setState({
|
||||||
|
depositCoin: coin,
|
||||||
|
errorMessage: '',
|
||||||
|
})
|
||||||
|
this.props.pairUpdate(coin)
|
||||||
|
}
|
||||||
|
|
||||||
|
onBuyWithShapeShift = () => {
|
||||||
|
this.setState({
|
||||||
|
isLoading: true,
|
||||||
|
showQrCode: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
buyWithShapeShift,
|
||||||
|
selectedAddress: withdrawal,
|
||||||
|
} = this.props
|
||||||
|
const {
|
||||||
|
refundAddress: returnAddress,
|
||||||
|
depositCoin,
|
||||||
|
} = this.state
|
||||||
|
const pair = `${depositCoin}_eth`
|
||||||
|
const data = {
|
||||||
|
withdrawal,
|
||||||
|
pair,
|
||||||
|
returnAddress,
|
||||||
|
// Public api key
|
||||||
|
'apiKey': '803d1f5df2ed1b1476e4b9e6bcd089e34d8874595dda6a23b67d93c56ea9cc2445e98a6748b219b2b6ad654d9f075f1f1db139abfa93158c04e825db122c14b6',
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValidAddress(withdrawal)) {
|
||||||
|
buyWithShapeShift(data)
|
||||||
|
.then(d => this.setState({
|
||||||
|
showQrCode: true,
|
||||||
|
depositAddress: d.deposit,
|
||||||
|
isLoading: false,
|
||||||
|
}))
|
||||||
|
.catch(() => this.setState({
|
||||||
|
showQrCode: false,
|
||||||
|
errorMessage: 'Invalid Request',
|
||||||
|
isLoading: false,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMetadata (label, value) {
|
||||||
|
return (
|
||||||
|
<div className='shapeshift-form__metadata-wrapper'>
|
||||||
|
<div className='shapeshift-form__metadata-label'>
|
||||||
|
{label}:
|
||||||
|
</div>
|
||||||
|
<div className='shapeshift-form__metadata-value'>
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMarketInfo () {
|
||||||
|
const { depositCoin } = this.state
|
||||||
|
const coinPair = `${depositCoin}_eth`
|
||||||
|
const { tokenExchangeRates } = this.props
|
||||||
|
const {
|
||||||
|
limit,
|
||||||
|
rate,
|
||||||
|
minimum,
|
||||||
|
} = tokenExchangeRates[coinPair] || {}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='shapeshift-form__metadata'>
|
||||||
|
{this.renderMetadata('Status', limit ? 'Available' : 'Unavailable')}
|
||||||
|
{this.renderMetadata('Limit', limit)}
|
||||||
|
{this.renderMetadata('Exchange Rate', rate)}
|
||||||
|
{this.renderMetadata('Minimum', minimum)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderQrCode () {
|
||||||
|
const { depositAddress, isLoading } = this.state
|
||||||
|
const qrImage = qrcode(4, 'M')
|
||||||
|
qrImage.addData(depositAddress)
|
||||||
|
qrImage.make()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='shapeshift-form'>
|
||||||
|
<div className='shapeshift-form__deposit-instruction'>
|
||||||
|
Deposit your BTC to the address bellow:
|
||||||
|
</div>
|
||||||
|
<div className='shapeshift-form__qr-code'>
|
||||||
|
{isLoading
|
||||||
|
? <img src='images/loading.svg' style={{ width: '60px' }} />
|
||||||
|
: <div dangerouslySetInnerHTML={{ __html: qrImage.createTableTag(4) }} />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
{this.renderMarketInfo()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { coinOptions, btnClass } = this.props
|
||||||
|
const { depositCoin, errorMessage, showQrCode } = this.state
|
||||||
|
const coinPair = `${depositCoin}_eth`
|
||||||
|
const { tokenExchangeRates } = this.props
|
||||||
|
const token = tokenExchangeRates[coinPair]
|
||||||
|
|
||||||
|
return showQrCode ? this.renderQrCode() : (
|
||||||
|
<div>
|
||||||
|
<div className='shapeshift-form'>
|
||||||
|
<div className='shapeshift-form__selectors'>
|
||||||
|
<div className='shapeshift-form__selector'>
|
||||||
|
<div className='shapeshift-form__selector-label'>
|
||||||
|
Deposit
|
||||||
|
</div>
|
||||||
|
<select
|
||||||
|
className='shapeshift-form__selector-input'
|
||||||
|
value={this.state.depositCoin}
|
||||||
|
onChange={this.onCoinChange}
|
||||||
|
>
|
||||||
|
{Object.entries(coinOptions).map(([coin]) => (
|
||||||
|
<option key={coin} value={coin.toLowerCase()}>
|
||||||
|
{coin}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='icon shapeshift-form__caret'
|
||||||
|
style={{ backgroundImage: 'url(images/caret-right.svg)'}}
|
||||||
|
/>
|
||||||
|
<div className='shapeshift-form__selector'>
|
||||||
|
<div className='shapeshift-form__selector-label'>
|
||||||
|
Receive
|
||||||
|
</div>
|
||||||
|
<div className='shapeshift-form__selector-input'>
|
||||||
|
ETH
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={classnames('shapeshift-form__address-input-wrapper', {
|
||||||
|
'shapeshift-form__address-input-wrapper--error': errorMessage,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div className='shapeshift-form__address-input-label'>
|
||||||
|
Your Refund Address
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
className='shapeshift-form__address-input'
|
||||||
|
onChange={e => this.setState({
|
||||||
|
refundAddress: e.target.value,
|
||||||
|
errorMessage: '',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<div className='shapeshift-form__address-input-error-message'>
|
||||||
|
{errorMessage}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{this.renderMarketInfo()}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className={btnClass}
|
||||||
|
disabled={!token}
|
||||||
|
onClick={this.onBuyWithShapeShift}
|
||||||
|
>
|
||||||
|
Buy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
({ metamask: { coinOptions, tokenExchangeRates, selectedAddress } }) => ({
|
||||||
|
coinOptions, tokenExchangeRates, selectedAddress,
|
||||||
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
shapeShiftSubview: () => dispatch(shapeShiftSubview()),
|
||||||
|
pairUpdate: coin => dispatch(pairUpdate(coin)),
|
||||||
|
buyWithShapeShift: data => dispatch(buyWithShapeShift(data)),
|
||||||
|
})
|
||||||
|
)(ShapeShiftForm)
|
@ -138,6 +138,7 @@ var actions = {
|
|||||||
BUY_ETH: 'BUY_ETH',
|
BUY_ETH: 'BUY_ETH',
|
||||||
buyEth: buyEth,
|
buyEth: buyEth,
|
||||||
buyEthView: buyEthView,
|
buyEthView: buyEthView,
|
||||||
|
buyWithShapeShift,
|
||||||
BUY_ETH_VIEW: 'BUY_ETH_VIEW',
|
BUY_ETH_VIEW: 'BUY_ETH_VIEW',
|
||||||
COINBASE_SUBVIEW: 'COINBASE_SUBVIEW',
|
COINBASE_SUBVIEW: 'COINBASE_SUBVIEW',
|
||||||
coinBaseSubview: coinBaseSubview,
|
coinBaseSubview: coinBaseSubview,
|
||||||
@ -977,6 +978,18 @@ function coinShiftRquest (data, marketData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buyWithShapeShift (data) {
|
||||||
|
return dispatch => new Promise((resolve, reject) => {
|
||||||
|
shapeShiftRequest('shift', { method: 'POST', data}, (response) => {
|
||||||
|
if (response.error) {
|
||||||
|
return reject(response.error)
|
||||||
|
}
|
||||||
|
background.createShapeShiftTx(response.deposit, response.depositType)
|
||||||
|
return resolve(response)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function showQrView (data, message) {
|
function showQrView (data, message) {
|
||||||
return {
|
return {
|
||||||
type: actions.SHOW_QR_VIEW,
|
type: actions.SHOW_QR_VIEW,
|
||||||
@ -1010,9 +1023,14 @@ function shapeShiftRequest (query, options, cb) {
|
|||||||
options.method ? method = options.method : method = 'GET'
|
options.method ? method = options.method : method = 'GET'
|
||||||
|
|
||||||
var requestListner = function (request) {
|
var requestListner = function (request) {
|
||||||
|
try {
|
||||||
queryResponse = JSON.parse(this.responseText)
|
queryResponse = JSON.parse(this.responseText)
|
||||||
cb ? cb(queryResponse) : null
|
cb ? cb(queryResponse) : null
|
||||||
return queryResponse
|
return queryResponse
|
||||||
|
} catch (e) {
|
||||||
|
cb ? cb({error: e}) : null
|
||||||
|
return e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var shapShiftReq = new XMLHttpRequest()
|
var shapShiftReq = new XMLHttpRequest()
|
||||||
|
@ -494,7 +494,6 @@ function reduceApp (state, action) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
case actions.ONBOARDING_BUY_ETH_VIEW:
|
case actions.ONBOARDING_BUY_ETH_VIEW:
|
||||||
return extend(appState, {
|
return extend(appState, {
|
||||||
transForward: true,
|
transForward: true,
|
||||||
|
@ -19,6 +19,8 @@ function reduceMetamask (state, action) {
|
|||||||
lastUnreadNotice: undefined,
|
lastUnreadNotice: undefined,
|
||||||
frequentRpcList: [],
|
frequentRpcList: [],
|
||||||
addressBook: [],
|
addressBook: [],
|
||||||
|
tokenExchangeRates: {},
|
||||||
|
coinOptions: {},
|
||||||
}, state.metamask)
|
}, state.metamask)
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@ -132,6 +134,25 @@ function reduceMetamask (state, action) {
|
|||||||
conversionDate: action.value.conversionDate,
|
conversionDate: action.value.conversionDate,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case actions.PAIR_UPDATE:
|
||||||
|
const { value: { marketinfo: pairMarketInfo } } = action
|
||||||
|
return extend(metamaskState, {
|
||||||
|
tokenExchangeRates: {
|
||||||
|
...metamaskState.tokenExchangeRates,
|
||||||
|
[pairMarketInfo.pair]: pairMarketInfo,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
case actions.SHAPESHIFT_SUBVIEW:
|
||||||
|
const { value: { marketinfo, coinOptions } } = action
|
||||||
|
return extend(metamaskState, {
|
||||||
|
tokenExchangeRates: {
|
||||||
|
...metamaskState.tokenExchangeRates,
|
||||||
|
[marketinfo.pair]: marketinfo,
|
||||||
|
},
|
||||||
|
coinOptions,
|
||||||
|
})
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return metamaskState
|
return metamaskState
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user