1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-22 17:33:23 +01:00

ShapeShift Integration

This commit is contained in:
Chi Kei Chan 2017-09-25 14:51:49 -07:00
parent 5aaa2d679b
commit 5cbbb476b3
6 changed files with 287 additions and 90 deletions

View File

@ -3,6 +3,7 @@ import classnames from 'classnames'
import {connect} from 'react-redux'
import {qrcode} from 'qrcode-npm'
import copyToClipboard from 'copy-to-clipboard'
import ShapeShiftForm from '../shapeshift-form'
import Identicon from '../../../../ui/app/components/identicon'
import {buyEth, showAccountDetail} from '../../../../ui/app/actions'
@ -79,12 +80,6 @@ class BuyEtherScreen extends Component {
)
}
renderShapeShiftLogo () {
return (
<div className='shapeshift-logo' />
)
}
renderCoinbaseForm () {
const {goToCoinbase, address} = this.props
@ -119,83 +114,13 @@ class BuyEtherScreen extends Component {
case OPTION_VALUES.SHAPESHIFT:
return (
<div className='buy-ether__action-content-wrapper'>
<div>{this.renderShapeShiftLogo()}</div>
<div className='shapeshift-logo' />
<div className='buy-ether__body-text'>
Trade any leading blockchain asset for any other. Protection by Design. No Account Needed.
</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'>
<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>
<ShapeShiftForm btnClass='first-time-flow__button' />
</div>
)
)
case OPTION_VALUES.QR_CODE:
return (
<div className='buy-ether__action-content-wrapper'>

View File

@ -578,7 +578,8 @@ button.first-time-flow__button--tertiary:hover {
flex: 1 0 auto;
}
.shapeshift-form__selector-label {
.shapeshift-form__selector-label,
.shapeshift-form__deposit-instruction {
color: #757575;
color: rgba(0, 0, 0, 0.45);
font-family: Montserrat Light;
@ -597,10 +598,8 @@ button.first-time-flow__button--tertiary:hover {
text-align: center;
width: 100%;
height: 45px;
}
.shapeshift-form__address-input-wrapper {
padding-bottom: 24px;
line-height: 44px;
font-family: Montserrat Light;
}
.shapeshift-form__address-input-label {
@ -622,6 +621,18 @@ button.first-time-flow__button--tertiary:hover {
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 {
display: flex;
flex-flow: row wrap;
@ -640,11 +651,11 @@ button.first-time-flow__button--tertiary:hover {
}
.shapeshift-form__metadata-wrapper:nth-child(odd) {
padding-right: 24px;
padding-right: 14px;
}
.shapeshift-form__metadata-label {
flex: 1 0 65%;
flex: 1 0 60%;
}
.shapeshift-form__metadata-value {
@ -654,3 +665,9 @@ button.first-time-flow__button--tertiary:hover {
text-overflow: ellipsis;
white-space: nowrap;
}
.shapeshift-form__qr-code {
display: flex;
flex-flow: row nowrap;
justify-content: center;
}

View 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)

View File

@ -138,6 +138,7 @@ var actions = {
BUY_ETH: 'BUY_ETH',
buyEth: buyEth,
buyEthView: buyEthView,
buyWithShapeShift,
BUY_ETH_VIEW: 'BUY_ETH_VIEW',
COINBASE_SUBVIEW: 'COINBASE_SUBVIEW',
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) {
return {
type: actions.SHOW_QR_VIEW,
@ -1010,9 +1023,14 @@ function shapeShiftRequest (query, options, cb) {
options.method ? method = options.method : method = 'GET'
var requestListner = function (request) {
queryResponse = JSON.parse(this.responseText)
cb ? cb(queryResponse) : null
return queryResponse
try {
queryResponse = JSON.parse(this.responseText)
cb ? cb(queryResponse) : null
return queryResponse
} catch (e) {
cb ? cb({error: e}) : null
return e
}
}
var shapShiftReq = new XMLHttpRequest()

View File

@ -494,7 +494,6 @@ function reduceApp (state, action) {
},
})
case actions.ONBOARDING_BUY_ETH_VIEW:
return extend(appState, {
transForward: true,

View File

@ -19,6 +19,8 @@ function reduceMetamask (state, action) {
lastUnreadNotice: undefined,
frequentRpcList: [],
addressBook: [],
tokenExchangeRates: {},
coinOptions: {},
}, state.metamask)
switch (action.type) {
@ -132,6 +134,25 @@ function reduceMetamask (state, action) {
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:
return metamaskState