Merge branch 'master' into shapeshiftTx

This commit is contained in:
Frankie 2016-08-18 11:06:32 -07:00
commit efa61f2cf8
24 changed files with 401 additions and 15 deletions

View File

@ -1 +1,2 @@
app/scripts/lib/extension-instance.js
ui/app/conversion-util.js

View File

@ -2,6 +2,10 @@
## Current Master
- Added feature to reflect current conversion rates of current vault balance.
## 2.8.0 2016-08-15
- Integrate ShapeShift
- Add a for for Coinbase to specify amount to buy
- Fix various typos.

1
app/currencies.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
{
"name": "MetaMask",
"short_name": "Metamask",
"version": "2.7.3",
"version": "2.8.0",
"manifest_version": 2,
"description": "Ethereum Browser Extension",
"icons": {

View File

@ -1,6 +1,7 @@
const Migrator = require('pojo-migrator')
const MetamaskConfig = require('../config.js')
const migrations = require('./migrations')
const rp = require('request-promise')
const TESTNET_RPC = MetamaskConfig.network.testnet
const MAINNET_RPC = MetamaskConfig.network.mainnet
@ -270,6 +271,53 @@ ConfigManager.prototype.getConfirmed = function () {
return ('isConfirmed' in data) && data.isConfirmed
}
ConfigManager.prototype.setCurrentFiat = function (currency) {
var data = this.getData()
data.fiatCurrency = currency
this.setData(data)
}
ConfigManager.prototype.getCurrentFiat = function () {
var data = this.getData()
return ('fiatCurrency' in data) && data.fiatCurrency
}
ConfigManager.prototype.updateConversionRate = function () {
var data = this.getData()
return rp(`https://www.cryptonator.com/api/ticker/eth-${data.fiatCurrency}`)
.then((response) => {
const parsedResponse = JSON.parse(response)
this.setConversionPrice(parsedResponse.ticker.price)
this.setConversionDate(parsedResponse.timestamp)
}).catch((err) => {
console.error('Error in conversion.', err)
this.setConversionPrice(0)
this.setConversionDate('N/A')
})
}
ConfigManager.prototype.setConversionPrice = function(price) {
var data = this.getData()
data.conversionRate = Number(price)
this.setData(data)
}
ConfigManager.prototype.setConversionDate = function (datestring) {
var data = this.getData()
data.conversionDate = datestring
this.setData(data)
}
ConfigManager.prototype.getConversionRate = function () {
var data = this.getData()
return (('conversionRate' in data) && data.conversionRate) || 0
}
ConfigManager.prototype.getConversionDate = function () {
var data = this.getData()
return (('conversionDate' in data) && data.conversionDate) || 'N/A'
}
ConfigManager.prototype.setShouldntShowWarning = function () {
var data = this.getData()
if (data.isEthConfirmed) {

View File

@ -101,6 +101,9 @@ IdentityStore.prototype.getState = function () {
messages: messageManager.getMsgList(),
selectedAddress: configManager.getSelectedAccount(),
shapeShiftTxList: configManager.getShapeShiftTxList(),
currentFiat: configManager.getCurrentFiat(),
conversionRate: configManager.getConversionRate(),
conversionDate: configManager.getConversionDate(),
}))
}

View File

@ -21,6 +21,9 @@ module.exports = class MetamaskController {
this.idStore.setStore(this.ethStore)
this.messageManager = messageManager
this.publicConfigStore = this.initPublicConfigStore()
this.configManager.setCurrentFiat('USD')
this.configManager.updateConversionRate()
this.scheduleConversionInterval()
}
getState () {
@ -40,7 +43,9 @@ module.exports = class MetamaskController {
setProviderType: this.setProviderType.bind(this),
useEtherscanProvider: this.useEtherscanProvider.bind(this),
agreeToDisclaimer: this.agreeToDisclaimer.bind(this),
setCurrentFiat: this.setCurrentFiat.bind(this),
agreeToEthWarning: this.agreeToEthWarning.bind(this),
// forward directly to idStore
createNewVault: idStore.createNewVault.bind(idStore),
recoverFromSeed: idStore.recoverFromSeed.bind(idStore),
@ -247,6 +252,31 @@ module.exports = class MetamaskController {
}
}
setCurrentFiat (fiat, cb) {
try {
this.configManager.setCurrentFiat(fiat)
this.configManager.updateConversionRate()
this.scheduleConversionInterval()
const data = {
conversionRate: this.configManager.getConversionRate(),
currentFiat: this.configManager.getCurrentFiat(),
conversionDate: this.configManager.getConversionDate(),
}
cb(data)
} catch (e) {
cb(null, e)
}
}
scheduleConversionInterval () {
if (this.conversionInterval) {
clearInterval(this.conversionInterval)
}
this.conversionInterval = setInterval(() => {
this.configManager.updateConversionRate()
}, 300000)
}
agreeToEthWarning (cb) {
try {
this.configManager.setShouldntShowWarning()

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,8 @@
{
"metamask": {
"currentFiat": "USD",
"conversionRate": 11.06608791,
"conversionDate": 1470421024,
"isInitialized": true,
"isUnlocked": true,
"currentDomain": "example.com",
@ -72,7 +75,7 @@
"txParams": {
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"to": "0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761",
"value": "0x99966c8104aa57038000",
"value": "0x0",
"origin": "thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com",
"metamaskId": 1467923203344608,
"metamaskNetworkId": "2"

View File

@ -1,5 +1,8 @@
{
"metamask": {
"currentFiat": "USD",
"conversionRate": 11.06608791,
"conversionDate": 1470421024,
"isInitialized": true,
"isUnlocked": true,
"currentDomain": "example.com",
@ -81,4 +84,4 @@
"warning": null
},
"identities": {}
}
}

View File

@ -1,5 +1,8 @@
{
"metamask": {
"currentFiat": "USD",
"conversionRate": 11.06608791,
"conversionDate": 1470421024,
"isInitialized": true,
"isUnlocked": true,
"currentDomain": "example.com",
@ -68,7 +71,7 @@
"appState": {
"menuOpen": false,
"currentView": {
"name": "accounts"
"name": "config"
},
"accountDetail": {
"subview": "transactions",
@ -82,4 +85,4 @@
"scrollToBottom": true
},
"identities": {}
}
}

View File

@ -67,6 +67,7 @@
"redux": "^3.0.5",
"redux-logger": "^2.3.1",
"redux-thunk": "^1.0.2",
"request-promise": "^4.1.1",
"sandwich-expando": "^1.0.5",
"textarea-caret": "^3.0.1",
"three.js": "^0.73.2",

View File

@ -3,6 +3,7 @@ const extend = require('xtend')
const STORAGE_KEY = 'metamask-persistance-key'
var configManagerGen = require('../lib/mock-config-manager')
var configManager
const rp = require('request-promise')
describe('config-manager', function() {
@ -11,6 +12,81 @@ describe('config-manager', function() {
configManager = configManagerGen()
})
describe('currency conversions', function() {
describe('#getCurrentFiat', function() {
it('should return false if no previous key exists', function() {
var result = configManager.getCurrentFiat()
assert.ok(!result)
})
})
describe('#setCurrentFiat', function() {
it('should make getCurrentFiat return true once set', function() {
assert.equal(configManager.getCurrentFiat(), false)
configManager.setCurrentFiat('USD')
var result = configManager.getCurrentFiat()
assert.equal(result, 'USD')
})
it('should work with other currencies as well', function() {
assert.equal(configManager.getCurrentFiat(), false)
configManager.setCurrentFiat('JPY')
var result = configManager.getCurrentFiat()
assert.equal(result, 'JPY')
})
})
describe('#getConversionRate', function() {
it('should return false if non-existent', function() {
var result = configManager.getConversionRate()
assert.ok(!result)
})
})
describe('#updateConversionRate', function() {
it('should retrieve an update for ETH to USD and set it in memory', function(done) {
this.timeout(15000)
assert.equal(configManager.getConversionRate(), false)
var promise = new Promise(
function (resolve, reject) {
configManager.setCurrentFiat('USD')
configManager.updateConversionRate().then(function() {
resolve()
})
})
promise.then(function() {
var result = configManager.getConversionRate()
assert.equal(typeof result, 'number')
done()
}).catch(function(err) {
console.log(err)
})
})
it('should work for JPY as well.', function() {
this.timeout(15000)
assert.equal(configManager.getConversionRate(), false)
var promise = new Promise(
function (resolve, reject) {
configManager.setCurrentFiat('JPY')
configManager.updateConversionRate().then(function() {
resolve()
})
})
promise.then(function() {
var result = configManager.getConversionRate()
assert.equal(typeof result, 'number')
}).catch(function(err) {
console.log(err)
})
})
})
})
describe('confirmation', function() {
describe('#getConfirmed', function() {
@ -215,4 +291,3 @@ describe('config-manager', function() {
})
})
})

View File

@ -9,7 +9,7 @@ const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const valuesFor = require('./util').valuesFor
const Identicon = require('./components/identicon')
const EtherBalance = require('./components/eth-balance')
const AccountEtherBalance = require('./components/account-eth-balance')
const TransactionList = require('./components/transaction-list')
const ExportAccountView = require('./components/account-export')
const ethUtil = require('ethereumjs-util')
@ -20,6 +20,7 @@ module.exports = connect(mapStateToProps)(AccountDetailScreen)
function mapStateToProps (state) {
return {
metamask: state.metamask,
identities: state.metamask.identities,
accounts: state.metamask.accounts,
address: state.metamask.selectedAccount,
@ -163,9 +164,8 @@ AccountDetailScreen.prototype.render = function () {
},
}, [
h(EtherBalance, {
h(AccountEtherBalance, {
value: account && account.balance,
mainBalance: true,
style: {
lineHeight: '7px',
marginTop: '10px',
@ -255,6 +255,7 @@ AccountDetailScreen.prototype.requestAccountExport = function () {
this.props.dispatch(actions.requestExportAccount())
}
AccountDetailScreen.prototype.buyButtonDeligator = function () {
var props = this.props

View File

@ -3,7 +3,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const EtherBalance = require('../components/eth-balance')
const AccountEtherBalance = require('../components/account-eth-balance')
const CopyButton = require('../components/copyButton')
const Identicon = require('../components/identicon')
@ -50,8 +50,12 @@ NewComponent.prototype.render = function () {
textOverflow: 'ellipsis',
},
}, ethUtil.toChecksumAddress(identity.address)),
h(EtherBalance, {
h(AccountEtherBalance, {
value: account.balance,
style: {
lineHeight: '7px',
marginTop: '10px',
},
}),
]),

View File

@ -55,6 +55,8 @@ var actions = {
SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE',
REVEAL_ACCOUNT: 'REVEAL_ACCOUNT',
revealAccount: revealAccount,
SET_CURRENT_FIAT: 'SET_CURRENT_FIAT',
setCurrentFiat: setCurrentFiat,
// account detail screen
SHOW_SEND_PAGE: 'SHOW_SEND_PAGE',
showSendPage: showSendPage,
@ -236,6 +238,23 @@ function revealAccount () {
}
}
function setCurrentFiat (fiat) {
return (dispatch) => {
dispatch(this.showLoadingIndication())
_accountManager.setCurrentFiat(fiat, (data, err) => {
dispatch(this.hideLoadingIndication())
dispatch({
type: this.SET_CURRENT_FIAT,
value: {
currentFiat: data.currentFiat,
conversionRate: data.conversionRate,
conversionDate: data.conversionDate,
},
})
})
}
}
function signMsg (msgData) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())

View File

@ -0,0 +1,139 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const formatBalance = require('../util').formatBalance
const generateBalanceObject = require('../util').generateBalanceObject
const Tooltip = require('./tooltip.js')
module.exports = connect(mapStateToProps)(EthBalanceComponent)
function mapStateToProps (state) {
return {
conversionRate: state.metamask.conversionRate,
conversionDate: state.metamask.conversionDate,
currentFiat: state.metamask.currentFiat,
}
}
inherits(EthBalanceComponent, Component)
function EthBalanceComponent () {
Component.call(this)
}
EthBalanceComponent.prototype.render = function () {
var state = this.props
var style = state.style
const value = formatBalance(state.value, 6)
var width = state.width
return (
h('.ether-balance', {
style: style,
}, [
h('.ether-balance-amount', {
style: {
display: 'inline',
width: width,
},
}, this.renderBalance(value, state)),
])
)
}
EthBalanceComponent.prototype.renderBalance = function (value, state) {
if (value === 'None') return value
var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3)
var balance, fiatNumber
var splitBalance = value.split(' ')
var ethNumber = splitBalance[0]
var ethSuffix = splitBalance[1]
if (state.conversionRate !== 0) {
fiatNumber = (Number(splitBalance[0]) * state.conversionRate).toFixed(2)
} else {
fiatNumber = 'N/A'
}
var fiatSuffix = state.currentFiat
if (state.shorten) {
balance = balanceObj.shortBalance
} else {
balance = balanceObj.balance
}
var label = balanceObj.label
return (
h('.flex-column', [
h(Tooltip, {
position: 'bottom',
title: `${ethNumber} ${ethSuffix}`,
}, [
h('.flex-row', {
style: {
alignItems: 'flex-end',
lineHeight: '13px',
fontFamily: 'Montserrat Light',
textRendering: 'geometricPrecision',
marginBottom: '5px',
},
}, [
h('div', {
style: {
width: '100%',
textAlign: 'right',
},
}, balance),
h('div', {
style: {
color: '#AEAEAE',
marginLeft: '5px',
},
}, label),
]),
]),
h(Tooltip, {
position: 'bottom',
title: `${fiatNumber} ${fiatSuffix}`,
}, [
fiatDisplay(fiatNumber, fiatSuffix),
]),
])
)
}
function fiatDisplay (fiatNumber, fiatSuffix) {
if (fiatNumber !== 'N/A') {
return h('.flex-row', {
style: {
alignItems: 'flex-end',
lineHeight: '13px',
fontFamily: 'Montserrat Light',
textRendering: 'geometricPrecision',
},
}, [
h('div', {
style: {
width: '100%',
textAlign: 'right',
fontSize: '12px',
color: '#333333',
},
}, fiatNumber),
h('div', {
style: {
color: '#AEAEAE',
marginLeft: '5px',
fontSize: '12px',
},
}, fiatSuffix),
])
} else {
return h('div')
}
}

View File

@ -110,6 +110,9 @@ BuyButtonSubview.prototype.formVersionSubview = function () {
h('h3.text-transform-uppercase', 'or:'),
this.props.network === '2' ? h('button.text-transform-uppercase', {
onClick: () => this.props.dispatch(actions.buyEth()),
style: {
marginTop: '15px',
},
}, 'Go To Test Faucet') : null,
])
}

View File

@ -4,6 +4,7 @@ const inherits = require('util').inherits
const formatBalance = require('../util').formatBalance
const generateBalanceObject = require('../util').generateBalanceObject
const Tooltip = require('./tooltip.js')
module.exports = EthBalanceComponent
inherits(EthBalanceComponent, Component)
@ -37,6 +38,9 @@ EthBalanceComponent.prototype.renderBalance = function (value, state) {
if (value === 'None') return value
var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3)
var balance
var splitBalance = value.split(' ')
var ethNumber = splitBalance[0]
var ethSuffix = splitBalance[1]
if (state.shorten) {
balance = balanceObj.shortBalance
@ -49,7 +53,7 @@ EthBalanceComponent.prototype.renderBalance = function (value, state) {
return (
h(Tooltip, {
position: 'bottom',
title: value.split(' ')[0],
title: `${ethNumber} ${ethSuffix}`,
}, [
h('.flex-column', {
style: {

View File

@ -52,7 +52,11 @@ QrCodeView.prototype.render = function () {
},
}),
h('.flex-row', [
h('h3.ellip-address', Qr.data),
h('h3.ellip-address', {
style: {
width: '247px',
},
}, Qr.data),
h(CopyButton, {
value: Qr.data,
}),

View File

@ -3,7 +3,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('./actions')
const currencies = require('./conversion-util').availableCurrencies.rows
module.exports = connect(mapStateToProps)(ConfigScreen)
function mapStateToProps (state) {
@ -74,6 +74,8 @@ ConfigScreen.prototype.render = function () {
}, 'Save'),
]),
h('hr.horizontal-line'),
currentConversionInformation(metamaskState, state),
h('hr.horizontal-line'),
h('div', {
style: {
@ -97,6 +99,27 @@ ConfigScreen.prototype.render = function () {
)
}
function currentConversionInformation (metamaskState, state) {
var currentFiat = metamaskState.currentFiat
var conversionDate = metamaskState.conversionDate
return h('div', [
h('span', {style: { fontWeight: 'bold', paddingRight: '10px'}}, 'Current Conversion'),
h('span', {style: { fontWeight: 'bold', paddingRight: '10px', fontSize: '13px'}}, `Updated ${Date(conversionDate)}`),
h('select#currentFiat', {
onChange (event) {
event.preventDefault()
var element = document.getElementById('currentFiat')
var newFiat = element.value
state.dispatch(actions.setCurrentFiat(newFiat))
},
defaultValue: currentFiat,
}, currencies.map((currency) => {
return h('option', {key: currency.code, value: currency.code}, `${currency.code} - ${currency.name}`)
})
),
])
}
function currentProviderDisplay (metamaskState) {
var provider = metamaskState.provider
var title, value

File diff suppressed because one or more lines are too long

View File

@ -15,6 +15,9 @@ function reduceMetamask (state, action) {
rpcTarget: 'https://rawtestrpc.metamask.io/',
identities: {},
unconfTxs: {},
currentFiat: 'USD',
conversionRate: 0,
conversionDate: 'N/A',
}, state.metamask)
switch (action.type) {
@ -115,6 +118,13 @@ function reduceMetamask (state, action) {
var identities = extend(metamaskState.identities, id)
return extend(metamaskState, { identities })
case actions.SET_CURRENT_FIAT:
return extend(metamaskState, {
currentFiat: action.value.currentFiat,
conversionRate: action.value.conversionRate,
conversionDate: action.value.conversionDate,
})
default:
return metamaskState

View File

@ -145,6 +145,8 @@ function shortenBalance (balance, decimalsToKeep = 1) {
} else if (convertedBalance > 1000) {
truncatedValue = (balance / 1000).toFixed(decimalsToKeep)
return `>${truncatedValue}k`
} else if (convertedBalance === 0) {
return '0'
} else if (convertedBalance < 1) {
var exponent = balance.match(/\.0*/)[0].length
truncatedValue = (convertedBalance * Math.pow(10, exponent)).toFixed(decimalsToKeep)